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 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/**
48GET `/api/v0/main-frontend/exercise-slide-submissions/{exam_id}/{exercise_id}/{user_id}/user-exercise-state-info`-
49*/
50#[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/**
72PUT `/api/v0/main-frontend/exercise-slide-submissions/add_teacher_grading"` - Adds a new teacher grading decision, without updating user exercise state
73*/
74#[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}