Skip to content

Commit

Permalink
Fix instructions for exams that have not been started yet.
Browse files Browse the repository at this point in the history
  • Loading branch information
nygrenh committed Nov 9, 2023
1 parent 4d3b658 commit a6655b5
Show file tree
Hide file tree
Showing 12 changed files with 66 additions and 21 deletions.
18 changes: 14 additions & 4 deletions services/course-material/src/components/exams/ExamStartBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ import { css } from "@emotion/css"
import React, { useState } from "react"
import { useTranslation } from "react-i18next"

import { ExamEnrollmentData } from "../../shared-module/bindings"
import Button from "../../shared-module/components/Button"
import { baseTheme } from "../../shared-module/styles"

export interface ExamInstructionsProps {
onStart: () => Promise<void>
canStartExam: boolean
examHasStarted: boolean
examHasEnded: boolean
timeMinutes: number
examEnrollmentData: ExamEnrollmentData
}

const ExamStartBanner: React.FC<React.PropsWithChildren<ExamInstructionsProps>> = ({
onStart,
canStartExam,
examEnrollmentData,
examHasStarted,
examHasEnded,
timeMinutes,
Expand Down Expand Up @@ -69,15 +70,24 @@ const ExamStartBanner: React.FC<React.PropsWithChildren<ExamInstructionsProps>>
>
{children}
</p>
{!canStartExam && <p>{t("you-are-not-eligible-for-taking-this-exam")}</p>}
{examEnrollmentData.tag === "NotEnrolled" && !examEnrollmentData.can_enroll && (
<p>{t("message-you-have-not-met-the-requirements-for-taking-this-exam")}</p>
)}
{!examHasStarted && !examHasEnded && <p>{t("message-the-exam-has-not-started-yet")}</p>}
<div
className={css`
text-align: center;
margin-top: 1rem;
`}
>
<Button
onClick={handleStart}
disabled={!examHasStarted || examHasEnded || !canStartExam || disabled}
disabled={
!examHasStarted ||
examHasEnded ||
(examEnrollmentData.tag === "NotEnrolled" && !examEnrollmentData.can_enroll) ||
disabled
}
variant="primary"
size="medium"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface ExamProps {
}

const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
const { t } = useTranslation()
const { t, i18n } = useTranslation()
const examId = query.id
const [pageState, pageStateDispatch] = useReducer(
pageStateReducer,
Expand Down Expand Up @@ -65,6 +65,15 @@ const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
}
}, [exam.isError, exam.isSuccess, exam.data, exam.error])

useEffect(() => {
if (!exam.data) {
return
}
if (i18n.language !== exam.data.language) {
i18n.changeLanguage(exam.data.language)
}
})

const layoutContext = useContext(LayoutContext)
useEffect(() => {
layoutContext.setOrganizationSlug(query.organizationSlug)
Expand Down Expand Up @@ -172,9 +181,6 @@ const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
</BreakFromCentered>
)

const canStartExam =
exam.data.enrollment_data.tag === "NotEnrolled" ? exam.data.enrollment_data.can_enroll : false

if (
exam.data.enrollment_data.tag === "NotEnrolled" ||
exam.data.enrollment_data.tag === "NotYetStarted"
Expand All @@ -188,7 +194,7 @@ const Exam: React.FC<React.PropsWithChildren<ExamProps>> = ({ query }) => {
await enrollInExam(examId)
exam.refetch()
}}
canStartExam={canStartExam}
examEnrollmentData={exam.data.enrollment_data}
examHasStarted={exam.data.starts_at ? isPast(exam.data.starts_at) : false}
examHasEnded={exam.data.ends_at ? isPast(exam.data.ends_at) : false}
timeMinutes={exam.data.time_minutes}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion services/headless-lms/models/src/exams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ pub struct Exam {
pub id: Uuid,
pub name: String,
pub instructions: serde_json::Value,
// TODO: page_id is not in the exams table, prevents from using select * with query_as!
pub page_id: Uuid,
pub courses: Vec<Course>,
pub starts_at: Option<DateTime<Utc>>,
pub ends_at: Option<DateTime<Utc>>,
pub time_minutes: i32,
pub minimum_points_treshold: i32,
pub language: String,
}

impl Exam {
Expand Down Expand Up @@ -60,7 +62,8 @@ SELECT exams.id,
exams.starts_at,
exams.ends_at,
exams.time_minutes,
exams.minimum_points_treshold
exams.minimum_points_treshold,
exams.language
FROM exams
JOIN pages ON pages.exam_id = exams.id
WHERE exams.id = $1
Expand Down Expand Up @@ -109,6 +112,7 @@ WHERE course_exams.exam_id = $1
time_minutes: exam.time_minutes,
courses,
minimum_points_treshold: exam.minimum_points_treshold,
language: exam.language.unwrap_or("en-US".to_string()),
})
}

Expand Down
1 change: 1 addition & 0 deletions services/headless-lms/models/src/library/copying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ WHERE id = $2;
time_minutes: copied_exam.time_minutes,
page_id: get_page_id.page_id,
minimum_points_treshold: copied_exam.minimum_points_treshold,
language: copied_exam.language.unwrap_or("en-US".to_string()),
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,24 @@ pub struct ExamData {
pub ended: bool,
pub time_minutes: i32,
pub enrollment_data: ExamEnrollmentData,
pub language: String,
}

#[derive(Debug, Serialize)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
#[serde(tag = "tag")]
pub enum ExamEnrollmentData {
/// The student has enrolled to the exam and started it.
EnrolledAndStarted {
page_id: Uuid,
page: Box<Page>,
enrollment: ExamEnrollment,
},
NotEnrolled {
can_enroll: bool,
},
/// The student has not enrolled to the exam yet. However, the the exam is open.
NotEnrolled { can_enroll: bool },
/// The exam's start time is in the future, no one can enroll yet.
NotYetStarted,
/// The exam is still open but the student has run out of time.
StudentTimeUp,
}

Expand Down Expand Up @@ -145,6 +148,7 @@ pub async fn fetch_exam_for_user(
ended,
time_minutes: exam.time_minutes,
enrollment_data: ExamEnrollmentData::NotYetStarted,
language: exam.language,
}));
}

Expand All @@ -166,6 +170,7 @@ pub async fn fetch_exam_for_user(
ended,
time_minutes: exam.time_minutes,
enrollment_data: ExamEnrollmentData::StudentTimeUp,
language: exam.language,
}));
}
enrollment
Expand All @@ -183,6 +188,7 @@ pub async fn fetch_exam_for_user(
ended,
time_minutes: exam.time_minutes,
enrollment_data: ExamEnrollmentData::NotEnrolled { can_enroll },
language: exam.language,
}));
};

Expand All @@ -202,6 +208,7 @@ pub async fn fetch_exam_for_user(
page: Box::new(page),
enrollment,
},
language: exam.language,
}))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ fn controllers() {
ends_at,
ended: false,
time_minutes: 120,
enrollment_data: ExamEnrollmentData::NotEnrolled { can_enroll: true }
enrollment_data: ExamEnrollmentData::NotEnrolled { can_enroll: true },
language: "en-US".to_string()
});
doc!(ExerciseSubmissions {
data,
Expand Down Expand Up @@ -1059,6 +1060,7 @@ fn models() {
ends_at,
time_minutes: 120,
minimum_points_treshold: 24,
language: "en-US".to_string()
});
doc!(
T,
Expand Down
6 changes: 4 additions & 2 deletions shared-module/src/bindings.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,8 @@ export function isExam(obj: unknown): obj is Exam {
(typedObj["starts_at"] === null || typedObj["starts_at"] instanceof Date) &&
(typedObj["ends_at"] === null || typedObj["ends_at"] instanceof Date) &&
typeof typedObj["time_minutes"] === "number" &&
typeof typedObj["minimum_points_treshold"] === "number"
typeof typedObj["minimum_points_treshold"] === "number" &&
typeof typedObj["language"] === "string"
)
}

Expand Down Expand Up @@ -3115,7 +3116,8 @@ export function isExamData(obj: unknown): obj is ExamData {
typedObj["ends_at"] instanceof Date &&
typeof typedObj["ended"] === "boolean" &&
typeof typedObj["time_minutes"] === "number" &&
(isExamEnrollmentData(typedObj["enrollment_data"]) as boolean)
(isExamEnrollmentData(typedObj["enrollment_data"]) as boolean) &&
typeof typedObj["language"] === "string"
)
}

Expand Down
2 changes: 2 additions & 0 deletions shared-module/src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ export interface Exam {
ends_at: Date | null
time_minutes: number
minimum_points_treshold: number
language: string
}

export interface ExamEnrollment {
Expand Down Expand Up @@ -1775,6 +1776,7 @@ export interface ExamData {
ended: boolean
time_minutes: number
enrollment_data: ExamEnrollmentData
language: string
}

export type ExamEnrollmentData =
Expand Down
4 changes: 4 additions & 0 deletions shared-module/src/components/LanguageSelection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const LanguageSelection: React.FC<LanguageSelectionProps> = ({
document.cookie = `${LANGUAGE_COOKIE_KEY}=${selectedLanguage[0]}; path=/; SameSite=Strict; max-age=31536000;`
}

const noLanguagesToChange = (languages ?? DEFAULT_LANGUAGES).length <= 1

return (
<>
<OutsideClickHandler onOutsideClick={() => setVisible(false)}>
Expand All @@ -66,6 +68,8 @@ const LanguageSelection: React.FC<LanguageSelectionProps> = ({
:hover {
cursor: pointer;
}
${noLanguagesToChange && `cursor: not-allowed !important;`}
`}
ref={setReferenceElement}
onClick={(e) => {
Expand Down
3 changes: 2 additions & 1 deletion shared-module/src/locales/en/course-material.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
"max-points": "Max points",
"max-score-n-marks": "Max score: <2>{{marks}} marks</2>",
"message-already-on-different-language-version": "You're already on a different language version of this course. Before answering any exercises, please return to <1>{{name}}</1> or change your active language in the course settings.",
"message-the-exam-has-not-started-yet": "You cannot start the exam yet. Please come back later.",
"message-you-have-not-met-the-requirements-for-taking-this-exam": "You have not met the requirements for taking this exam.",
"n-characters-left": "{{n}} characters left",
"n-characters-over-limit": "{{n}} characters over the limit",
"no-comments-yet": "No comments yet",
Expand Down Expand Up @@ -172,7 +174,6 @@
"waiting-for-peer-reviews-explanation": "Other students are peer reviewing your answer. Please come back later to see the results.",
"write-your-feedback-here": "Write your feedback here",
"written-feedback": "Written feedback",
"you-are-not-eligible-for-taking-this-exam": "The exam is not open yet. Use this button to access the exam once the exam starts.",
"you-have-completed-the-course-to-receive-certificate-use-following-links": "You have successfully completed the course! You can use the following links to generate your certificate.",
"you-have-completed-the-course-to-receive-credits-or-certificate-use-following-links": "You have successfully completed the course! To receive ECTS credits for your completions, you can use the following links to register your completion and receive a certificate.",
"youve-made-changes": "You've made changes",
Expand Down
3 changes: 2 additions & 1 deletion shared-module/src/locales/fi/course-material.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
"max-points": "Maksimipisteet",
"max-score-n-marks": "Maksimi pisteet: <2>{{marks}}</2>",
"message-already-on-different-language-version": "Olet tekemässä kurssia jo toisella kielellä. Ennen kuin vastaat mihinkään tehtävään, palaa kieliversioon <1>{{name}}</1> tai vaihda käytössä oleva kieli kurssin asetuksista.",
"message-the-exam-has-not-started-yet": "Et voi vielä aloittaa koetta. Ole hyvä ja palaa myöhemmin.",
"message-you-have-not-met-the-requirements-for-taking-this-exam": "Et ole täyttänyt esivaatimuksia tämän kokeen suorittamiseen.",
"n-characters-left": "{{n}} merkkiä jäljellä",
"n-characters-over-limit": "{{n}} Merkkiä yli rajan",
"no-comments-yet": "Ei kommentteja vielä",
Expand Down Expand Up @@ -167,7 +169,6 @@
"waiting-for-peer-reviews-explanation": "Kurssin muut opiskelijat ovat vertaisarvioimassa vastaustasi. Tule takaisin myöhemmin nähdäksesi tulokset.",
"write-your-feedback-here": "Kirjoita palaute tähän",
"written-feedback": "Kirjallinen palaute",
"you-are-not-eligible-for-taking-this-exam": "Et ole täyttänyt esivaatimuksia tämän kokeen suorittamiseen.",
"you-have-completed-the-course-to-receive-certificate-use-following-links": "Olet suorittanut kurssin onnistuneesti! Voit käyttää oheisia linkkejä sertifikaatin luomiseen.",
"you-have-completed-the-course-to-receive-credits-or-certificate-use-following-links": "Olet suorittanut kurssin onnistuneesti! Voit käyttää oheisia linkkejä opintopisteiden saamiseksi tai sertifikaatin luomiseen.",
"youve-made-changes": "Olet tehnyt muutoksia",
Expand Down

0 comments on commit a6655b5

Please sign in to comment.