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