headless_lms_server/controllers/main_frontend/
exercise_slide_submissions.rs

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