headless_lms_server/controllers/main_frontend/
teacher_grading_decisions.rs1use crate::prelude::*;
2use headless_lms_models::{
3 exercises::get_exercise_by_id,
4 library::user_exercise_state_updater,
5 teacher_grading_decisions::{NewTeacherGradingDecision, TeacherDecisionType},
6 user_exercise_states::UserExerciseState,
7};
8use utoipa::OpenApi;
9
10#[derive(OpenApi)]
11#[openapi(paths(create_teacher_grading_decision))]
12pub(crate) struct MainFrontendTeacherGradingDecisionsApiDoc;
13
14#[utoipa::path(
18 post,
19 path = "",
20 operation_id = "createTeacherGradingDecision",
21 tag = "teacher_grading_decisions",
22 request_body = NewTeacherGradingDecision,
23 responses(
24 (status = 200, description = "Teacher grading decision created", body = Option<UserExerciseState>)
25 )
26)]
27#[instrument(skip(pool))]
28async fn create_teacher_grading_decision(
29 payload: web::Json<NewTeacherGradingDecision>,
30 pool: web::Data<PgPool>,
31 user: AuthUser,
32) -> ControllerResult<web::Json<Option<UserExerciseState>>> {
33 let action = &payload.action;
34 let exercise_id = payload.exercise_id;
35 let user_exercise_state_id = payload.user_exercise_state_id;
36 let manual_points = payload.manual_points;
37 let justification = &payload.justification;
38 let hidden = payload.hidden;
39 let mut conn = pool.acquire().await?;
40 let token = authorize(
41 &mut conn,
42 Act::Edit,
43 Some(user.id),
44 Res::Exercise(exercise_id),
45 )
46 .await?;
47 let points_given;
48 if *action == TeacherDecisionType::FullPoints {
49 let exercise = get_exercise_by_id(&mut conn, exercise_id).await?;
50 points_given = exercise.score_maximum as f32;
51 } else if *action == TeacherDecisionType::ZeroPoints {
52 points_given = 0.0;
53 } else if *action == TeacherDecisionType::CustomPoints {
54 points_given = manual_points.unwrap_or(0.0);
55 } else if *action == TeacherDecisionType::SuspectedPlagiarism {
56 points_given = 0.0;
57 } else if *action == TeacherDecisionType::RejectAndReset {
58 points_given = 0.0;
59
60 let mut tx = conn.begin().await?;
61
62 let _res = models::teacher_grading_decisions::add_teacher_grading_decision(
63 &mut tx,
64 user_exercise_state_id,
65 *action,
66 points_given,
67 Some(user.id),
68 justification.clone(),
69 hidden,
70 )
71 .await?;
72
73 let student_state =
74 models::user_exercise_states::get_by_id(&mut tx, user_exercise_state_id).await?;
75 let users_and_exercises = vec![(student_state.user_id, vec![exercise_id])];
76
77 let course_id = student_state.course_id.ok_or_else(|| {
78 ControllerError::new(
79 ControllerErrorType::BadRequest,
80 "RejectAndReset requires course_id".to_string(),
81 None,
82 )
83 })?;
84
85 let _reset = models::exercises::reset_exercises_for_selected_users(
86 &mut tx,
87 &users_and_exercises,
88 Some(user.id),
89 course_id,
90 Some("reset-by-staff".to_string()),
91 )
92 .await?;
93
94 tx.commit().await?;
95 info!("Teacher took the following action: RejectAndReset.",);
96
97 return token.authorized_ok(web::Json(None));
98 } else {
99 return Err(ControllerError::new(
100 ControllerErrorType::BadRequest,
101 "Invalid query".to_string(),
102 None,
103 ));
104 }
105
106 info!(
107 "Teacher took the following action: {:?}. Points given: {:?}.",
108 &action, points_given
109 );
110
111 let mut tx = conn.begin().await?;
112
113 let _res = models::teacher_grading_decisions::add_teacher_grading_decision(
114 &mut tx,
115 user_exercise_state_id,
116 *action,
117 points_given,
118 Some(user.id),
119 justification.clone(),
120 hidden,
121 )
122 .await?;
123
124 let new_user_exercise_state =
125 user_exercise_state_updater::update_user_exercise_state(&mut tx, user_exercise_state_id)
126 .await?;
127
128 if let Some(course_id) = new_user_exercise_state.course_id {
129 models::peer_review_queue_entries::remove_queue_entries_for_unusual_reason(
131 &mut tx,
132 new_user_exercise_state.user_id,
133 new_user_exercise_state.exercise_id,
134 course_id,
135 )
136 .await?;
137 }
138
139 tx.commit().await?;
140
141 token.authorized_ok(web::Json(Some(new_user_exercise_state)))
142}
143
144pub fn _add_routes(cfg: &mut ServiceConfig) {
145 cfg.route("", web::post().to(create_teacher_grading_decision));
146}