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};
8
9#[instrument(skip(pool))]
13async fn create_teacher_grading_decision(
14 payload: web::Json<NewTeacherGradingDecision>,
15 pool: web::Data<PgPool>,
16 user: AuthUser,
17) -> ControllerResult<web::Json<Option<UserExerciseState>>> {
18 let action = &payload.action;
19 let exercise_id = payload.exercise_id;
20 let user_exercise_state_id = payload.user_exercise_state_id;
21 let manual_points = payload.manual_points;
22 let justification = &payload.justification;
23 let hidden = payload.hidden;
24 let mut conn = pool.acquire().await?;
25 let token = authorize(
26 &mut conn,
27 Act::Edit,
28 Some(user.id),
29 Res::Exercise(exercise_id),
30 )
31 .await?;
32 let points_given;
33 if *action == TeacherDecisionType::FullPoints {
34 let exercise = get_exercise_by_id(&mut conn, exercise_id).await?;
35 points_given = exercise.score_maximum as f32;
36 } else if *action == TeacherDecisionType::ZeroPoints {
37 points_given = 0.0;
38 } else if *action == TeacherDecisionType::CustomPoints {
39 points_given = manual_points.unwrap_or(0.0);
40 } else if *action == TeacherDecisionType::SuspectedPlagiarism {
41 points_given = 0.0;
42 } else if *action == TeacherDecisionType::RejectAndReset {
43 points_given = 0.0;
44
45 let mut tx = conn.begin().await?;
46
47 let _res = models::teacher_grading_decisions::add_teacher_grading_decision(
48 &mut tx,
49 user_exercise_state_id,
50 *action,
51 points_given,
52 Some(user.id),
53 justification.clone(),
54 hidden,
55 )
56 .await?;
57
58 let student_state =
59 models::user_exercise_states::get_by_id(&mut tx, user_exercise_state_id).await?;
60 let users_and_exercises = vec![(student_state.user_id, vec![exercise_id])];
61
62 let course_id = student_state.course_id.ok_or_else(|| {
63 ControllerError::new(
64 ControllerErrorType::BadRequest,
65 "RejectAndReset requires course_id".to_string(),
66 None,
67 )
68 })?;
69
70 let _reset = models::exercises::reset_exercises_for_selected_users(
71 &mut tx,
72 &users_and_exercises,
73 Some(user.id),
74 course_id,
75 Some("reset-by-staff".to_string()),
76 )
77 .await?;
78
79 tx.commit().await?;
80 info!("Teacher took the following action: RejectAndReset.",);
81
82 return token.authorized_ok(web::Json(None));
83 } else {
84 return Err(ControllerError::new(
85 ControllerErrorType::BadRequest,
86 "Invalid query".to_string(),
87 None,
88 ));
89 }
90
91 info!(
92 "Teacher took the following action: {:?}. Points given: {:?}.",
93 &action, points_given
94 );
95
96 let mut tx = conn.begin().await?;
97
98 let _res = models::teacher_grading_decisions::add_teacher_grading_decision(
99 &mut tx,
100 user_exercise_state_id,
101 *action,
102 points_given,
103 Some(user.id),
104 justification.clone(),
105 hidden,
106 )
107 .await?;
108
109 let new_user_exercise_state =
110 user_exercise_state_updater::update_user_exercise_state(&mut tx, user_exercise_state_id)
111 .await?;
112
113 if let Some(course_id) = new_user_exercise_state.course_id {
114 models::peer_review_queue_entries::remove_queue_entries_for_unusual_reason(
116 &mut tx,
117 new_user_exercise_state.user_id,
118 new_user_exercise_state.exercise_id,
119 course_id,
120 )
121 .await?;
122 }
123
124 tx.commit().await?;
125
126 token.authorized_ok(web::Json(Some(new_user_exercise_state)))
127}
128
129pub fn _add_routes(cfg: &mut ServiceConfig) {
130 cfg.route("", web::post().to(create_teacher_grading_decision));
131}