headless_lms_server/controllers/course_material/
course_modules.rs1use 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::authorize_access_to_course_material, 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#[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 chapter = models::chapters::get_chapter(&mut conn, *chapter_id).await?;
40 let token =
41 authorize_access_to_course_material(&mut conn, Some(user.id), chapter.course_id).await?;
42 let module_id = chapter.course_module_id;
43 token.authorized_ok(web::Json(module_id))
44}
45
46#[utoipa::path(
52 get,
53 path = "/course/{course_id}",
54 operation_id = "getCourseMaterialDefaultModuleIdByCourseId",
55 tag = "course-material-course-modules",
56 params(
57 ("course_id" = Uuid, Path, description = "Course id")
58 ),
59 responses(
60 (status = 200, description = "Default course module id", body = Uuid)
61 )
62)]
63#[instrument(skip(pool))]
64async fn get_default_course_module_id_by_course_id(
65 course_id: web::Path<Uuid>,
66 pool: web::Data<PgPool>,
67 user: AuthUser,
68) -> ControllerResult<web::Json<Uuid>> {
69 let mut conn = pool.acquire().await?;
70 let token = authorize_access_to_course_material(&mut conn, Some(user.id), *course_id).await?;
71 let module = course_modules::get_default_by_course_id(&mut conn, *course_id).await?;
72 let module_id = module.id;
73 token.authorized_ok(web::Json(module_id))
74}
75
76#[utoipa::path(
82 get,
83 path = "/{course_module_id}/exercise-tasks/{exercise_type}/{course_instance_id}",
84 operation_id = "getCourseMaterialExerciseTasksByModuleAndType",
85 tag = "course-material-course-modules",
86 params(
87 ("course_module_id" = Uuid, Path, description = "Course module id"),
88 ("exercise_type" = String, Path, description = "Exercise type"),
89 ("course_instance_id" = Uuid, Path, description = "Course instance id")
90 ),
91 responses(
92 (
93 status = 200,
94 description = "Exercise tasks, exercises, and user variables",
95 body = CustomViewExerciseSubmissions
96 )
97 )
98)]
99#[instrument(skip(pool))]
100async fn get_user_course_module_exercises_by_exercise_type(
101 path: web::Path<(Uuid, String, Uuid)>,
102 pool: web::Data<PgPool>,
103 user: AuthUser,
104) -> ControllerResult<web::Json<CustomViewExerciseSubmissions>> {
105 let mut conn = pool.acquire().await?;
106 let (course_module_id, exercise_type, course_instance_id) = path.into_inner();
107 let course_instance =
108 course_instances::get_course_instance(&mut conn, course_instance_id).await?;
109 let module = course_modules::get_by_id(&mut conn, course_module_id).await?;
110 if module.course_id != course_instance.course_id {
111 return Err(controller_err!(
112 Forbidden,
113 "Course module does not belong to the requested course instance".to_string()
114 ));
115 }
116 let token =
117 authorize_access_to_course_material(&mut conn, Some(user.id), module.course_id).await?;
118 let course_id = course_instance.course_id;
119 let exercise_tasks = models::exercise_task_submissions::get_user_custom_view_exercise_tasks_by_module_and_exercise_type(
120 &mut conn,
121 &exercise_type,
122 course_module_id,
123 user.id,
124 course_id)
125 .await?;
126
127 let exercises = models::exercises::get_exercises_by_module_containing_exercise_type(
128 &mut conn,
129 &exercise_type,
130 course_module_id,
131 )
132 .await?;
133 let user_variables =
134 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?;
135 let res = CustomViewExerciseSubmissions {
136 exercise_tasks,
137 exercises,
138 user_variables,
139 };
140
141 token.authorized_ok(web::Json(res))
142}
143
144pub fn _add_routes(cfg: &mut ServiceConfig) {
152 cfg.route(
153 "/chapter/{chapter_id}",
154 web::get().to(get_course_module_id_by_chapter_id),
155 )
156 .route(
157 "/course/{course_id}",
158 web::get().to(get_default_course_module_id_by_course_id),
159 )
160 .route(
161 "/{course_module_id}/exercise-tasks/{exercise_type}/{course_instance_id}",
162 web::get().to(get_user_course_module_exercises_by_exercise_type),
163 );
164}