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, file_store, app_conf))]
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 file_store: web::Data<dyn FileStore>,
105 app_conf: web::Data<ApplicationConfiguration>,
106) -> ControllerResult<web::Json<CustomViewExerciseSubmissions>> {
107 let mut conn = pool.acquire().await?;
108 let (course_module_id, exercise_type, course_instance_id) = path.into_inner();
109 let course_instance =
110 course_instances::get_course_instance(&mut conn, course_instance_id).await?;
111 let module = course_modules::get_by_id(&mut conn, course_module_id).await?;
112 if module.course_id != course_instance.course_id {
113 return Err(controller_err!(
114 Forbidden,
115 "Course module does not belong to the requested course instance".to_string()
116 ));
117 }
118 let token =
119 authorize_access_to_course_material(&mut conn, Some(user.id), module.course_id).await?;
120 let course_id = course_instance.course_id;
121 let exercise_tasks = models::exercise_task_submissions::get_user_custom_view_exercise_tasks_by_module_and_exercise_type(
122 &mut conn,
123 &exercise_type,
124 course_module_id,
125 user.id,
126 course_id, file_store.as_ref(), app_conf.as_ref())
127 .await?;
128
129 let exercises = models::exercises::get_exercises_by_module_containing_exercise_type(
130 &mut conn,
131 &exercise_type,
132 course_module_id,
133 )
134 .await?;
135 let user_variables =
136 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?;
137 let res = CustomViewExerciseSubmissions {
138 exercise_tasks,
139 exercises,
140 user_variables,
141 };
142
143 token.authorized_ok(web::Json(res))
144}
145
146pub fn _add_routes(cfg: &mut ServiceConfig) {
154 cfg.route(
155 "/chapter/{chapter_id}",
156 web::get().to(get_course_module_id_by_chapter_id),
157 )
158 .route(
159 "/course/{course_id}",
160 web::get().to(get_default_course_module_id_by_course_id),
161 )
162 .route(
163 "/{course_module_id}/exercise-tasks/{exercise_type}/{course_instance_id}",
164 web::get().to(get_user_course_module_exercises_by_exercise_type),
165 );
166}