Skip to content

Commit

Permalink
Grade exams manually (#1253)
Browse files Browse the repository at this point in the history
* All exam submissions -page

* Grading page for exam submission

* Added justification and hidden to TeacherGradingDesicions

* New structs, add new teacher grading decision and little improvements

* Translations for grading exams manually

* Student can view published grading results

* Test, publish grades, refactoring

* Test fix

* Test fix and added css to viewing exam feedback

* Fixed typo and removed console logs

* Refactoring

* Teachers only see ended exam submissions in grading page

* Added ended_at time to exam_enrollments and student button for students to end exam

* Little fix for ending exam

* Changes to test

* Removed unused pagination

* updated snapshots

* Process ended exam enrollments

* Fix to processing ended exam enrollments

* Changes to process ended exam enrollments

* Changes to query

* Test update

* Test fix

* Checkbox for choosing between grading exam manually or automatically

* Updated snapshots

* Changes to view automatically graded exam submissions

* No automatic completions for manually graded exams

* Test fix

* Small fix

---------

Co-authored-by: Maija Y <[email protected]>
Co-authored-by: Henrik Nygren <[email protected]>
  • Loading branch information
3 people authored Aug 23, 2024
1 parent f1b7c01 commit 1bb2cf9
Show file tree
Hide file tree
Showing 76 changed files with 3,564 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import LayoutContext from "../../../contexts/LayoutContext"
import PageContext, { CoursePageDispatch, getDefaultPageState } from "../../../contexts/PageContext"
import useTime from "../../../hooks/useTime"
import pageStateReducer from "../../../reducers/pageStateReducer"
import { Block, enrollInExam, fetchExam } from "../../../services/backend"

import { Block, endExamTime, enrollInExam, fetchExam } from "@/services/backend"
import Button from "@/shared-module/common/components/Button"
import BreakFromCentered from "@/shared-module/common/components/Centering/BreakFromCentered"
import ErrorBanner from "@/shared-module/common/components/ErrorBanner"
import Spinner from "@/shared-module/common/components/Spinner"
import HideTextInSystemTests from "@/shared-module/common/components/system-tests/HideTextInSystemTests"
import { withSignedIn } from "@/shared-module/common/contexts/LoginStateContext"
import { baseTheme } from "@/shared-module/common/styles"
import useToastMutation from "@/shared-module/common/hooks/useToastMutation"
import { baseTheme, headingFont } from "@/shared-module/common/styles"
import { respondToOrLarger } from "@/shared-module/common/styles/respond"
import dontRenderUntilQueryParametersReady, {
SimplifiedUrlQuery,
Expand Down Expand Up @@ -88,6 +90,20 @@ const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
await handleRefresh()
}, [handleRefresh])

const handleEndExam = () => {
endExamMutation.mutate({ id: examId })
}

const endExamMutation = useToastMutation(
({ id }: { id: string }) => endExamTime(id),
{ notify: true, method: "POST" },
{
onSuccess: async () => {
await handleRefresh()
},
},
)

if (exam.isPending) {
return <Spinner variant="medium" />
}
Expand Down Expand Up @@ -229,6 +245,56 @@ const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
)
}

if (exam.data.enrollment_data.tag === "StudentCanViewGrading") {
return (
<>
{examInfo}
{exam.data.enrollment_data.gradings.map(
(grade) =>
!grade[0].hidden && (
<div
key={grade[0].id}
className={css`
display: flex;
flex-direction: column;
background: #f5f6f7;
font-family: ${headingFont};
font-size: 18px;
padding: 8px;
margin: 10px;
`}
>
<div>
{t("label-name")}: {grade[1].name}
</div>
<div>
{t("points")}: {grade[0].score_given} / {grade[1].score_maximum}
</div>
<div
className={css`
color: #535a66;
font-size: 16px;
padding-top: 1rem;
`}
>
{t("label-feedback")}:
<div
className={css`
background: #ffffff;
color: #535a66;
padding: 10px;
`}
>
{grade[0].justification}
</div>
</div>
</div>
),
)}
</>
)
}

const endsAt = exam.data.ends_at
? min([
addMinutes(exam.data.enrollment_data.enrollment.started_at, exam.data.time_minutes),
Expand Down Expand Up @@ -266,6 +332,18 @@ const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
</div>
)}
<Page onRefresh={handleRefresh} organizationSlug={query.organizationSlug} />
<Button
variant={"primary"}
size={"small"}
onClick={() => {
const confirmation = confirm(t("message-do-you-want-to-end-the-exam"))
if (confirmation) {
handleEndExam()
}
}}
>
{t("button-end-exam")}
</Button>
</PageContext.Provider>
</CoursePageDispatch.Provider>
)
Expand Down
18 changes: 18 additions & 0 deletions services/course-material/src/services/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
CustomViewExerciseSubmissions,
ExamData,
ExamEnrollment,
ExerciseSlideSubmissionAndUserExerciseStateList,
IsChapterFrontPage,
MaterialReference,
NewFeedback,
Expand Down Expand Up @@ -62,6 +63,7 @@ import {
isCoursePageWithUserData,
isCustomViewExerciseSubmissions,
isExamData,
isExerciseSlideSubmissionAndUserExerciseStateList,
isIsChapterFrontPage,
isMaterialReference,
isOEmbedResponse,
Expand Down Expand Up @@ -655,6 +657,22 @@ export const getAllCourseModuleCompletionsForUserAndCourseInstance = async (
return validateResponse(response, isArray(isCourseModuleCompletion))
}

export const fetchExerciseSubmissions = async (
exerciseId: string,
page: number,
limit: number,
): Promise<ExerciseSlideSubmissionAndUserExerciseStateList> => {
const response = await courseMaterialClient.get(
`/exams/${exerciseId}/submissions?page=${page}&limit=${limit}`,
)
return validateResponse(response, isExerciseSlideSubmissionAndUserExerciseStateList)
}

export const endExamTime = async (examId: string): Promise<void> => {
const response = await courseMaterialClient.post(`/exams/${examId}/end-exam-time`)
return response.data
}

export const getChatbotCurrentConversationInfo = async (
chatBotConfigurationId: string,
): Promise<ChatbotConversationInfo> => {
Expand Down
Loading

0 comments on commit 1bb2cf9

Please sign in to comment.