Skip to content

Commit

Permalink
New structs, add new teacher grading decision and little improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Maija Y committed Mar 19, 2024
1 parent ca9467a commit 425efda
Show file tree
Hide file tree
Showing 12 changed files with 397 additions and 95 deletions.
47 changes: 42 additions & 5 deletions services/headless-lms/models/src/exercise_slide_submissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use crate::{
exercise_tasks::CourseMaterialExerciseTask,
exercises::{Exercise, GradingProgress},
prelude::*,
user_exercise_states::CourseInstanceOrExamId,
teacher_grading_decisions::{self, TeacherGradingDecision},
user_exercise_states::{self, CourseInstanceOrExamId, UserExerciseState},
CourseOrExamId,
};

Expand Down Expand Up @@ -113,6 +114,21 @@ pub struct ExerciseSlideSubmissionInfo {
pub exercise_slide_submission: ExerciseSlideSubmission,
}

#[derive(Debug, Serialize)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct ExerciseSlideSubmissionAndUserExerciseState {
pub exercise_slide_submission: ExerciseSlideSubmission,
pub user_exercise_state: UserExerciseState,
pub teacher_grading_decision: Option<TeacherGradingDecision>,
}

#[derive(Debug, Serialize)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct ExerciseSlideSubmissionAndUserExerciseStateList {
pub data: Vec<ExerciseSlideSubmissionAndUserExerciseState>,
pub total_pages: u32,
}

pub async fn insert_exercise_slide_submission(
conn: &mut PgConnection,
exercise_slide_submission: NewExerciseSlideSubmission,
Expand Down Expand Up @@ -469,11 +485,11 @@ AND deleted_at IS NULL
Ok(count.count.unwrap_or(0).try_into()?)
}

pub async fn exercise_slide_submissions_with_exam_id(
pub async fn exercise_slide_submissions_and_user_exercise_state_list_with_exam_id(
conn: &mut PgConnection,
exam_id: Uuid,
pagination: Pagination,
) -> ModelResult<Vec<ExerciseSlideSubmission>> {
) -> ModelResult<Vec<ExerciseSlideSubmissionAndUserExerciseState>> {
let submissions = sqlx::query_as!(
ExerciseSlideSubmission,
r#"
Expand All @@ -498,9 +514,30 @@ pub async fn exercise_slide_submissions_with_exam_id(
pagination.limit(),
pagination.offset(),
)
.fetch_all(conn)
.fetch_all(&mut *conn)
.await?;
Ok(submissions)

let mut list: Vec<ExerciseSlideSubmissionAndUserExerciseState> = Vec::new();
for sub in submissions {
let res = user_exercise_states::get_or_create_user_exercise_state(
conn,
sub.user_id,
sub.exercise_id,
None,
Some(exam_id),
)
.await?;

let decision = teacher_grading_decisions::try_to_get_latest_grading_decision_by_user_exercise_state_id(conn, res.id).await?;
let data = ExerciseSlideSubmissionAndUserExerciseState {
exercise_slide_submission: sub,
user_exercise_state: res,
teacher_grading_decision: decision,
};
list.push(data);
}

Ok(list)
}

pub async fn get_course_daily_slide_submission_counts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ pub async fn remove_from_queue_and_give_full_points(
// Giver is none because the system made the decision
None,
None,
Some(false),
false,
)
.await?;
user_exercise_state_updater::update_user_exercise_state(&mut tx, user_exercise_state.id)
Expand Down
4 changes: 2 additions & 2 deletions services/headless-lms/models/src/teacher_grading_decisions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub struct NewTeacherGradingDecision {
pub action: TeacherDecisionType,
pub manual_points: Option<f32>,
pub justification: Option<String>,
pub hidden: Option<bool>,
pub hidden: bool,
}

pub async fn add_teacher_grading_decision(
Expand All @@ -42,7 +42,7 @@ pub async fn add_teacher_grading_decision(
score_given: f32,
decision_maker_user_id: Option<Uuid>,
justification: Option<String>,
hidden: Option<bool>,
hidden: bool,
) -> ModelResult<TeacherGradingDecision> {
let res = sqlx::query_as!(
TeacherGradingDecision,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use chrono::Utc;
use models::{
course_exams,
exams::{self, Exam, NewExam},
exercise_slide_submissions::ExerciseSlideSubmissionAndUserExerciseStateList,
};

use crate::{
Expand All @@ -14,8 +15,6 @@ use crate::{
prelude::*,
};

use super::exercises::ExerciseSubmissions;

/**
GET `/api/v0/main-frontend/exams/:id
*/
Expand Down Expand Up @@ -165,15 +164,15 @@ async fn duplicate_exam(
}

/**
GET `/api/v0/main-frontend/exercises/:exercise_id/submissions-with-exam-id` - Returns all exams exercise submissions.
GET `/api/v0/main-frontend/exam/:exercise_id/submissions-with-exam-id` - Returns all exams exercise submissions.
*/
#[instrument(skip(pool))]
async fn get_exercise_submissions_with_exam_id(
pool: web::Data<PgPool>,
exam_id: web::Path<Uuid>,
pagination: web::Query<Pagination>,
user: AuthUser,
) -> ControllerResult<web::Json<ExerciseSubmissions>> {
) -> ControllerResult<web::Json<ExerciseSlideSubmissionAndUserExerciseStateList>> {
let mut conn = pool.acquire().await?;

let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Exam(*exam_id)).await?;
Expand All @@ -183,16 +182,15 @@ async fn get_exercise_submissions_with_exam_id(
&mut conn, *exam_id,
);
let mut conn = pool.acquire().await?;
let submissions = models::exercise_slide_submissions::exercise_slide_submissions_with_exam_id(
let submissions = models::exercise_slide_submissions::exercise_slide_submissions_and_user_exercise_state_list_with_exam_id(
&mut conn,
*exam_id,
*pagination,
);
let (submission_count, submissions) = future::try_join(submission_count, submissions).await?;

let total_pages = pagination.total_pages(submission_count);

token.authorized_ok(web::Json(ExerciseSubmissions {
token.authorized_ok(web::Json(ExerciseSlideSubmissionAndUserExerciseStateList {
data: submissions,
total_pages,
}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use headless_lms_models::exercise_slide_submissions::ExerciseSlideSubmissionInfo
use models::{
exercises::get_exercise_by_id,
library::user_exercise_state_updater,
teacher_grading_decisions::{NewTeacherGradingDecision, TeacherDecisionType},
teacher_grading_decisions::{
NewTeacherGradingDecision, TeacherDecisionType, TeacherGradingDecision,
},
user_exercise_states::UserExerciseState,
};

Expand Down Expand Up @@ -115,10 +117,114 @@ async fn update_answer_requiring_attention(
token.authorized_ok(web::Json(new_user_exercise_state))
}

#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct ExerciseStateIds {
exercise_id: Uuid,
user_id: Uuid,
}
/**
GET `/api/v0/main-frontend/exercise-slide-submissions/{exam_id}/{exercise_id}/{user_id}/user-exercise-state-info`-
*/
#[instrument(skip(pool))]
async fn get_user_exercise_state_info(
exam_id: web::Path<Uuid>,
pool: web::Data<PgPool>,
query_ids: web::Query<ExerciseStateIds>,
user: AuthUser,
) -> ControllerResult<web::Json<UserExerciseState>> {
let mut conn = pool.acquire().await?;
let token = authorize(&mut conn, Act::Teach, Some(user.id), Res::Exam(*exam_id)).await?;

let res = models::user_exercise_states::get_or_create_user_exercise_state(
&mut conn,
query_ids.user_id,
query_ids.exercise_id,
None,
Some(*exam_id),
)
.await?;
token.authorized_ok(web::Json(res))
}

/**
PUT `/api/v0/main-frontend/exercise-slide-submissions/add_teacher_grading"` - Given a teacher grading decision, updates an answer by giving it a manual score given.
*/
#[instrument(skip(pool))]
async fn add_teacher_grading(
payload: web::Json<NewTeacherGradingDecision>,
pool: web::Data<PgPool>,
user: AuthUser,
) -> ControllerResult<web::Json<TeacherGradingDecision>> {
let action = &payload.action;
let exercise_id = payload.exercise_id;
let user_exercise_state_id = payload.user_exercise_state_id;
let manual_points = payload.manual_points;
let justification = &payload.justification;
let hidden = payload.hidden;
let mut conn = pool.acquire().await?;
let token = authorize(
&mut conn,
Act::Edit,
Some(user.id),
Res::Exercise(exercise_id),
)
.await?;
let points_given;
if *action == TeacherDecisionType::FullPoints {
let exercise = get_exercise_by_id(&mut conn, exercise_id).await?;
points_given = exercise.score_maximum as f32;
} else if *action == TeacherDecisionType::ZeroPoints {
points_given = 0.0;
} else if *action == TeacherDecisionType::CustomPoints {
points_given = manual_points.unwrap_or(0.0);
} else if *action == TeacherDecisionType::SuspectedPlagiarism {
points_given = 0.0;
} else {
return Err(ControllerError::new(
ControllerErrorType::BadRequest,
"Invalid query".to_string(),
None,
));
}

info!(
"Teacher took the following action: {:?}. Points given: {:?}.",
&action, points_given
);

let mut tx = conn.begin().await?;

let _res = models::teacher_grading_decisions::add_teacher_grading_decision(
&mut tx,
user_exercise_state_id,
*action,
points_given,
Some(user.id),
justification.clone(),
hidden,
)
.await?;

if !hidden {
user_exercise_state_updater::update_user_exercise_state(&mut tx, user_exercise_state_id)
.await?;
}

tx.commit().await?;

token.authorized_ok(web::Json(_res))
}

pub fn _add_routes(cfg: &mut ServiceConfig) {
cfg.route("/{submission_id}/info", web::get().to(get_submission_info))
.route(
"/update-answer-requiring-attention",
web::put().to(update_answer_requiring_attention),
);
)
.route(
"/{exam_id}/user-exercise-state-info",
web::get().to(get_user_exercise_state_info),
)
.route("/add-teacher-grading", web::put().to(add_teacher_grading));
}
2 changes: 2 additions & 0 deletions services/headless-lms/server/src/ts_binding_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ fn models(target: &mut File) {
exercise_slide_submissions::ExerciseSlideSubmissionCountByExercise,
exercise_slide_submissions::ExerciseSlideSubmissionCountByWeekAndHour,
exercise_slide_submissions::ExerciseSlideSubmissionInfo,
exercise_slide_submissions::ExerciseSlideSubmissionAndUserExerciseState,
exercise_slide_submissions::ExerciseSlideSubmissionAndUserExerciseStateList,
exercise_task_submissions::PeerReviewsRecieved,
exercise_slides::CourseMaterialExerciseSlide,
exercise_slides::ExerciseSlide,
Expand Down
32 changes: 25 additions & 7 deletions services/main-frontend/src/pages/manage/exams/[id]/submissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React from "react"
import { useTranslation } from "react-i18next"

import { fetchExerciseSubmissionsWithExamId } from "../../../../services/backend/exams"
import Accordion from "../../../../shared-module/components/Accordion"
import ErrorBanner from "../../../../shared-module/components/ErrorBanner"
import Pagination from "../../../../shared-module/components/Pagination"
import Spinner from "../../../../shared-module/components/Spinner"
Expand Down Expand Up @@ -110,16 +111,16 @@ const GradingPage: React.FC<React.PropsWithChildren<SubmissionPageProps>> = ({ q
>
{getSubmissions.data.data.map((submission) => (
<tr
key={submission.id}
key={submission.exercise_slide_submission.id}
className={css`
font-family: ${headingFont};
font-weight: ${fontWeights.medium};
font-size: ${baseTheme.fontSizes[16]};
line-height: 19px;
`}
>
<td>{submission.user_id}</td>
<td>-</td>
<td>{submission.exercise_slide_submission.user_id}</td>
<td>{submission.teacher_grading_decision ? "Graded" : "Not graded"}</td>
<td
className={css`
font-size: 20px;
Expand All @@ -129,16 +130,33 @@ const GradingPage: React.FC<React.PropsWithChildren<SubmissionPageProps>> = ({ q
<Link
href={{
pathname: "/submissions/[id]/grading/",
query: { id: submission.id },
query: { id: submission.exercise_slide_submission.id },
}}
>
{t("link")}
</Link>
</td>
<td>{submission.created_at.toLocaleString()}</td>
<td>-</td>
<td>-</td>
<td>{submission.exercise_slide_submission.created_at.toLocaleString()}</td>
<td>
<Accordion variant={"simple"}>
<details>
<summary
className={css`
border-width: 0px !important;
`}
>
{t("label-feedback")}
</summary>
<div>{submission.teacher_grading_decision?.justification}</div>
</details>
</Accordion>
</td>
<td>-</td>
<td>
{submission.teacher_grading_decision
? submission.teacher_grading_decision.score_given
: 0}
</td>
</tr>
))}
</tbody>
Expand Down
Loading

0 comments on commit 425efda

Please sign in to comment.