headless_lms_server/controllers/main_frontend/
exercise_slide_submissions.rs1use crate::{domain::models_requests, prelude::*};
2use headless_lms_models::exercise_slide_submissions::ExerciseSlideSubmissionInfo;
3use models::{
4 exercises::get_exercise_by_id,
5 teacher_grading_decisions::{
6 NewTeacherGradingDecision, TeacherDecisionType, TeacherGradingDecision,
7 },
8 user_exercise_states::UserExerciseState,
9};
10
11#[instrument(skip(pool))]
15async fn get_submission_info(
16 submission_id: web::Path<Uuid>,
17 pool: web::Data<PgPool>,
18 user: AuthUser,
19) -> ControllerResult<web::Json<ExerciseSlideSubmissionInfo>> {
20 let mut conn = pool.acquire().await?;
21 let token = authorize(
22 &mut conn,
23 Act::Teach,
24 Some(user.id),
25 Res::ExerciseSlideSubmission(*submission_id),
26 )
27 .await?;
28
29 let res = models::exercise_slide_submissions::get_exercise_slide_submission_info(
30 &mut conn,
31 submission_id.into_inner(),
32 user.id,
33 models_requests::fetch_service_info,
34 )
35 .await?;
36
37 token.authorized_ok(web::Json(res))
38}
39
40#[derive(Debug, Deserialize)]
41#[cfg_attr(feature = "ts_rs", derive(TS))]
42pub struct ExerciseStateIds {
43 exercise_id: Uuid,
44 user_id: Uuid,
45}
46#[instrument(skip(pool))]
50async fn get_user_exercise_state_info(
51 exam_id: web::Path<Uuid>,
52 pool: web::Data<PgPool>,
53 query_ids: web::Query<ExerciseStateIds>,
54 user: AuthUser,
55) -> ControllerResult<web::Json<UserExerciseState>> {
56 let mut conn = pool.acquire().await?;
57 let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Exam(*exam_id)).await?;
58
59 let res = models::user_exercise_states::get_or_create_user_exercise_state(
60 &mut conn,
61 query_ids.user_id,
62 query_ids.exercise_id,
63 None,
64 Some(*exam_id),
65 )
66 .await?;
67 token.authorized_ok(web::Json(res))
68}
69
70#[instrument(skip(pool))]
74async fn add_teacher_grading(
75 payload: web::Json<NewTeacherGradingDecision>,
76 pool: web::Data<PgPool>,
77 user: AuthUser,
78) -> ControllerResult<web::Json<TeacherGradingDecision>> {
79 let action = &payload.action;
80 let exercise_id = payload.exercise_id;
81 let user_exercise_state_id = payload.user_exercise_state_id;
82 let manual_points = payload.manual_points;
83 let justification = &payload.justification;
84 let mut conn = pool.acquire().await?;
85 let token = authorize(
86 &mut conn,
87 Act::Edit,
88 Some(user.id),
89 Res::Exercise(exercise_id),
90 )
91 .await?;
92
93 let points_given;
94 if *action == TeacherDecisionType::CustomPoints {
95 let exercise = get_exercise_by_id(&mut conn, exercise_id).await?;
96 let max_points = exercise.score_maximum as f32;
97
98 points_given = manual_points.unwrap_or(0.0);
99
100 if max_points < points_given {
101 return Err(ControllerError::new(
102 ControllerErrorType::BadRequest,
103 "Cannot give more points than maximum score".to_string(),
104 None,
105 ));
106 }
107 } else {
108 return Err(ControllerError::new(
109 ControllerErrorType::BadRequest,
110 "Invalid query".to_string(),
111 None,
112 ));
113 }
114
115 info!(
116 "Teacher took the following action: {:?}. Points given: {:?}.",
117 &action, points_given
118 );
119
120 let mut tx = conn.begin().await?;
121
122 let _res = models::teacher_grading_decisions::add_teacher_grading_decision(
123 &mut tx,
124 user_exercise_state_id,
125 *action,
126 points_given,
127 Some(user.id),
128 justification.clone(),
129 true,
130 )
131 .await?;
132
133 tx.commit().await?;
134
135 token.authorized_ok(web::Json(_res))
136}
137
138pub fn _add_routes(cfg: &mut ServiceConfig) {
139 cfg.route("/{submission_id}/info", web::get().to(get_submission_info))
140 .route(
141 "/{exam_id}/user-exercise-state-info",
142 web::get().to(get_user_exercise_state_info),
143 )
144 .route(
145 "/add-teacher-grading-for-exam-submission",
146 web::put().to(add_teacher_grading),
147 );
148}