headless_lms_server/controllers/course_material/
course_modules.rs

1use headless_lms_models::{course_instances, user_course_exercise_service_variables};
2use models::{course_modules, library::custom_view_exercises::CustomViewExerciseSubmissions};
3use utoipa::OpenApi;
4
5use crate::{domain::authorization::skip_authorize, prelude::*};
6
7#[derive(OpenApi)]
8#[openapi(paths(
9    get_course_module_id_by_chapter_id,
10    get_default_course_module_id_by_course_id,
11    get_user_course_module_exercises_by_exercise_type
12))]
13pub(crate) struct CourseMaterialCourseModulesApiDoc;
14
15/**
16GET `/api/v0/course-material/course-modules/chapter/:chapter_id`
17
18Returns course module id based on chapter.
19*/
20#[utoipa::path(
21    get,
22    path = "/chapter/{chapter_id}",
23    operation_id = "getCourseMaterialModuleIdByChapterId",
24    tag = "course-material-course-modules",
25    params(
26        ("chapter_id" = Uuid, Path, description = "Chapter id")
27    ),
28    responses(
29        (status = 200, description = "Course module id", body = Uuid)
30    )
31)]
32#[instrument(skip(pool))]
33async fn get_course_module_id_by_chapter_id(
34    chapter_id: web::Path<Uuid>,
35    pool: web::Data<PgPool>,
36    user: AuthUser,
37) -> ControllerResult<web::Json<Uuid>> {
38    let mut conn = pool.acquire().await?;
39    let module_id = course_modules::get_course_module_id_by_chapter(&mut conn, *chapter_id).await?;
40    let token = skip_authorize();
41    token.authorized_ok(web::Json(module_id))
42}
43
44/**
45GET `/api/v0/course-material/course-modules/course/:course_instance_id`
46
47Returns course module id based on chapter.
48*/
49#[utoipa::path(
50    get,
51    path = "/course/{course_id}",
52    operation_id = "getCourseMaterialDefaultModuleIdByCourseId",
53    tag = "course-material-course-modules",
54    params(
55        ("course_id" = Uuid, Path, description = "Course id")
56    ),
57    responses(
58        (status = 200, description = "Default course module id", body = Uuid)
59    )
60)]
61#[instrument(skip(pool))]
62async fn get_default_course_module_id_by_course_id(
63    course_id: web::Path<Uuid>,
64    pool: web::Data<PgPool>,
65    user: AuthUser,
66) -> ControllerResult<web::Json<Uuid>> {
67    let mut conn = pool.acquire().await?;
68    let module = course_modules::get_default_by_course_id(&mut conn, *course_id).await?;
69    let module_id = module.id;
70    let token = skip_authorize();
71    token.authorized_ok(web::Json(module_id))
72}
73
74/**
75GET `/api/v0/course-material/course-modules/:course_module_id/exercise-tasks/:exercise_type/:course_instance_id`
76
77Returns exercise submissions for user to be used in en exercise service Custom view.
78*/
79#[utoipa::path(
80    get,
81    path = "/{course_module_id}/exercise-tasks/{exercise_type}/{course_instance_id}",
82    operation_id = "getCourseMaterialExerciseTasksByModuleAndType",
83    tag = "course-material-course-modules",
84    params(
85        ("course_module_id" = Uuid, Path, description = "Course module id"),
86        ("exercise_type" = String, Path, description = "Exercise type"),
87        ("course_instance_id" = Uuid, Path, description = "Course instance id")
88    ),
89    responses(
90        (
91            status = 200,
92            description = "Exercise tasks, exercises, and user variables",
93            body = CustomViewExerciseSubmissions
94        )
95    )
96)]
97#[instrument(skip(pool))]
98async fn get_user_course_module_exercises_by_exercise_type(
99    path: web::Path<(Uuid, String, Uuid)>,
100    pool: web::Data<PgPool>,
101    user: AuthUser,
102) -> ControllerResult<web::Json<CustomViewExerciseSubmissions>> {
103    let mut conn = pool.acquire().await?;
104    let (course_module_id, exercise_type, course_instance_id) = path.into_inner();
105    let course_id = course_instances::get_course_instance(&mut conn, course_instance_id)
106        .await?
107        .course_id;
108    let exercise_tasks = models::exercise_task_submissions::get_user_custom_view_exercise_tasks_by_module_and_exercise_type(
109        &mut conn,
110        &exercise_type,
111        course_module_id,
112        user.id,
113    course_id)
114        .await?;
115
116    let exercises = models::exercises::get_exercises_by_module_containing_exercise_type(
117        &mut conn,
118        &exercise_type,
119        course_module_id,
120    )
121    .await?;
122    let user_variables =
123    user_course_exercise_service_variables::get_all_user_variables_for_user_and_course_and_exercise_type(&mut conn, user.id, course_id, &exercise_type).await?;
124    let token = skip_authorize();
125    let res = CustomViewExerciseSubmissions {
126        exercise_tasks,
127        exercises,
128        user_variables,
129    };
130
131    token.authorized_ok(web::Json(res))
132}
133
134/**
135Add a route for each controller in this module.
136
137The name starts with an underline in order to appear before other functions in the module documentation.
138
139We add the routes by calling the route method instead of using the route annotations because this method preserves the function signatures for documentation.
140*/
141pub fn _add_routes(cfg: &mut ServiceConfig) {
142    cfg.route(
143        "/chapter/{chapter_id}",
144        web::get().to(get_course_module_id_by_chapter_id),
145    )
146    .route(
147        "/course/{course_id}",
148        web::get().to(get_default_course_module_id_by_course_id),
149    )
150    .route(
151        "/{course_module_id}/exercise-tasks/{exercise_type}/{course_instance_id}",
152        web::get().to(get_user_course_module_exercises_by_exercise_type),
153    );
154}