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