diff --git a/api/__mocks__/exam-environment-exam.ts b/api/__mocks__/exam-environment-exam.ts index bbb1576a6c0..b9bb5a5fa5d 100644 --- a/api/__mocks__/exam-environment-exam.ts +++ b/api/__mocks__/exam-environment-exam.ts @@ -18,7 +18,6 @@ export const oid = () => new ObjectId().toString(); export const examId = oid(); export const config = { - totalTimeInMS: 2 * 60 * 60 * 1000, totalTimeInS: 2 * 60 * 60, tags: [], name: 'Test Exam', @@ -47,7 +46,6 @@ export const config = { numberOfIncorrectAnswers: 1 } ], - retakeTimeInMS: 24 * 60 * 60 * 1000, retakeTimeInS: 24 * 60 * 60 } satisfies ExamEnvironmentConfig; @@ -264,7 +262,6 @@ export const examAttempt: ExamEnvironmentExamAttempt = { { id: generatedExam.questionSets[0]!.questions[0]!.id, answers: [generatedExam.questionSets[0]!.questions[0]!.answers[0]!], - submissionTimeInMS: Date.now(), submissionTime: new Date() } ] @@ -275,7 +272,6 @@ export const examAttempt: ExamEnvironmentExamAttempt = { { id: generatedExam.questionSets[1]!.questions[0]!.id, answers: [generatedExam.questionSets[1]!.questions[0]!.answers[1]!], - submissionTimeInMS: Date.now(), submissionTime: new Date() } ] @@ -286,19 +282,16 @@ export const examAttempt: ExamEnvironmentExamAttempt = { { id: generatedExam.questionSets[2]!.questions[0]!.id, answers: [generatedExam.questionSets[2]!.questions[0]!.answers[1]!], - submissionTimeInMS: Date.now(), submissionTime: new Date() }, { id: generatedExam.questionSets[2]!.questions[1]!.id, answers: [generatedExam.questionSets[2]!.questions[1]!.answers[0]!], - submissionTimeInMS: Date.now(), submissionTime: new Date() } ] } ], - startTimeInMS: Date.now(), startTime: new Date(), userId: defaultUserId, version: 2 diff --git a/api/prisma/exam-creator.prisma b/api/prisma/exam-creator.prisma index 52eb3e58b44..c575e91eb0b 100644 --- a/api/prisma/exam-creator.prisma +++ b/api/prisma/exam-creator.prisma @@ -14,8 +14,7 @@ model ExamCreatorExam { deprecated Boolean /// Version of the record /// The default must be incremented by 1, if anything in the schema changes - version Int @default(2) - + version Int @default(3) } /// Exam Creator application collection to store authZ users. @@ -30,10 +29,8 @@ model ExamCreatorUser { github_id Int? name String picture String? - /// TODO: After migration, remove optionality - settings ExamCreatorUserSettings? - version Int @default(1) - + settings ExamCreatorUserSettings + version Int @default(2) ExamCreatorSession ExamCreatorSession[] } @@ -55,8 +52,7 @@ model ExamCreatorSession { /// Expiration date for record. expires_at DateTime - version Int @default(1) - + version Int @default(1) ExamCreatorUser ExamCreatorUser @relation(fields: [user_id], references: [id]) } diff --git a/api/prisma/exam-environment.prisma b/api/prisma/exam-environment.prisma index 2037e223fec..adad76a56f9 100644 --- a/api/prisma/exam-environment.prisma +++ b/api/prisma/exam-environment.prisma @@ -12,7 +12,7 @@ model ExamEnvironmentExam { deprecated Boolean /// Version of the record /// The default must be incremented by 1, if anything in the schema changes - version Int @default(2) + version Int @default(3) // Relations generatedExams ExamEnvironmentGeneratedExam[] @@ -83,16 +83,12 @@ type ExamEnvironmentConfig { note String /// Category configuration for question selection tags ExamEnvironmentTagConfig[] - /// Deprecated: use `totalTimeInS` instead - totalTimeInMS Int /// Total time allocated for exam in seconds - totalTimeInS Int? + totalTimeInS Int /// Configuration for sets of questions questionSets ExamEnvironmentQuestionSetConfig[] - /// Deprecated: use `retakeTimeInS` instead - retakeTimeInMS Int /// Duration after exam completion before a retake is allowed in seconds - retakeTimeInS Int? + retakeTimeInS Int /// Passing percent for the exam passingPercent Float } @@ -130,14 +126,12 @@ model ExamEnvironmentExamAttempt { /// Foreign key to generated exam id generatedExamId String @db.ObjectId - questionSets ExamEnvironmentQuestionSetAttempt[] - /// Deprecated: Use `startTime` instead - startTimeInMS Int + questionSets ExamEnvironmentQuestionSetAttempt[] /// Time exam was started - startTime DateTime? + startTime DateTime /// Version of the record /// The default must be incremented by 1, if anything in the schema changes - version Int @default(2) + version Int @default(3) // Relations user user @relation(fields: [userId], references: [id], onDelete: Cascade) @@ -153,15 +147,13 @@ type ExamEnvironmentQuestionSetAttempt { type ExamEnvironmentMultipleChoiceQuestionAttempt { /// Foreign key to question - id String @db.ObjectId + id String @db.ObjectId /// An array of foreign keys to answers - answers String[] @db.ObjectId - /// Deprecated: Use `submissionTime` instead - submissionTimeInMS Int + answers String[] @db.ObjectId /// Time answers to question were submitted /// /// If the question is later revisited, this field is updated - submissionTime DateTime? + submissionTime DateTime } /// A generated exam for the Exam Environment App diff --git a/api/src/exam-environment/routes/exam-environment.test.ts b/api/src/exam-environment/routes/exam-environment.test.ts index 809342f7827..06ea6857e12 100644 --- a/api/src/exam-environment/routes/exam-environment.test.ts +++ b/api/src/exam-environment/routes/exam-environment.test.ts @@ -102,7 +102,6 @@ describe('/exam-environment/', () => { data: { examId, generatedExamId: mock.oid(), - startTimeInMS: Date.now(), startTime: new Date(), userId: defaultUserId } @@ -134,7 +133,6 @@ describe('/exam-environment/', () => { data: { examId: mock.examId, generatedExamId: mock.oid(), - startTimeInMS: Date.now() - (1000 * 60 * 60 * 2 + 1000), startTime: new Date(Date.now() - (1000 * 60 * 60 * 2 + 1000)), userId: defaultUserId } @@ -166,7 +164,6 @@ describe('/exam-environment/', () => { data: { examId: mock.examId, generatedExamId: mock.oid(), - startTimeInMS: Date.now(), startTime: new Date(), userId: defaultUserId } @@ -238,7 +235,6 @@ describe('/exam-environment/', () => { userId: defaultUserId, examId: mock.examId, generatedExamId: mock.generatedExam.id, - startTimeInMS: Date.now(), startTime: new Date(), questionSets: [] } @@ -342,7 +338,6 @@ describe('/exam-environment/', () => { const recentExamAttempt = { ...mock.examAttempt, // Set start time such that exam has just expired - startTimeInMS: Date.now() - examTotalTimeInMS, startTime: new Date(Date.now() - examTotalTimeInMS) }; await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({ @@ -375,8 +370,6 @@ describe('/exam-environment/', () => { }, data: { // Set start time such that exam has expired, but retake time -1s has passed - startTimeInMS: - Date.now() - (examTotalTimeInMS + (examRetakeTimeInMS - 1000)), startTime: new Date( Date.now() - (examTotalTimeInMS + (examRetakeTimeInMS - 1000)) ) @@ -408,8 +401,6 @@ describe('/exam-environment/', () => { // Set start time such that exam has expired, but 24 hours + 1s has passed const examTotalTimeInMS = mock.exam.config.totalTimeInS * 1000; - recentExamAttempt.startTimeInMS = - Date.now() - examTotalTimeInMS + (24 * 60 * 60 * 1000 + 1000); recentExamAttempt.startTime = new Date( Date.now() - (examTotalTimeInMS + (24 * 60 * 60 * 1000 + 1000)) ); @@ -501,8 +492,8 @@ describe('/exam-environment/', () => { data: { startTime: new Date( Date.now() - - mock.exam.config.totalTimeInMS - - mock.exam.config.retakeTimeInMS + mock.exam.config.totalTimeInS * 1000 - + mock.exam.config.retakeTimeInS * 1000 ) } }); @@ -537,8 +528,8 @@ describe('/exam-environment/', () => { data: { startTime: new Date( Date.now() - - mock.exam.config.totalTimeInMS - - mock.exam.config.retakeTimeInMS + mock.exam.config.totalTimeInS * 1000 - + mock.exam.config.retakeTimeInS * 1000 ) } }); @@ -603,8 +594,6 @@ describe('/exam-environment/', () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment startTime: expect.any(Date), // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - startTimeInMS: expect.any(Number), - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment version: expect.any(Number) }); }); @@ -721,9 +710,7 @@ describe('/exam-environment/', () => { note: mock.exam.config.note, passingPercent: mock.exam.config.passingPercent, totalTimeInS: mock.exam.config.totalTimeInS, - totalTimeInMS: mock.exam.config.totalTimeInMS, - retakeTimeInS: mock.exam.config.retakeTimeInS, - retakeTimeInMS: mock.exam.config.retakeTimeInMS + retakeTimeInS: mock.exam.config.retakeTimeInS }, id: mock.examId } @@ -798,9 +785,7 @@ describe('/exam-environment/', () => { note: mock.exam.config.note, passingPercent: mock.exam.config.passingPercent, totalTimeInS: mock.exam.config.totalTimeInS, - totalTimeInMS: mock.exam.config.totalTimeInMS, - retakeTimeInS: mock.exam.config.retakeTimeInS, - retakeTimeInMS: mock.exam.config.retakeTimeInMS + retakeTimeInS: mock.exam.config.retakeTimeInS }, id: mock.examId } @@ -816,7 +801,6 @@ describe('/exam-environment/', () => { const recentExamAttempt = { ...mock.examAttempt, userId: defaultUserId, - startTimeInMS: Date.now() - examTotalTimeInMS, startTime: new Date(Date.now() - examTotalTimeInMS) }; await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({ @@ -837,8 +821,6 @@ describe('/exam-environment/', () => { await fastifyTestInstance.prisma.examEnvironmentExamAttempt.update({ where: { id: recentExamAttempt.id }, data: { - startTimeInMS: - Date.now() - (examTotalTimeInMS + examRetakeTimeInMS + 1000), startTime: new Date( Date.now() - (examTotalTimeInMS + examRetakeTimeInMS + 1000) ) @@ -862,8 +844,6 @@ describe('/exam-environment/', () => { const examAttempt = { ...mock.examAttempt, userId: defaultUserId, - startTimeInMS: - Date.now() - (examTotalTimeInMS + examRetakeTimeInMS + 1000), startTime: new Date( Date.now() - (examTotalTimeInMS + examRetakeTimeInMS + 1000) ) @@ -962,7 +942,6 @@ describe('/exam-environment/', () => { examId: mock.exam.id, result: null, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets }; @@ -1003,7 +982,6 @@ describe('/exam-environment/', () => { examId: mock.exam.id, result: null, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets }; @@ -1015,7 +993,6 @@ describe('/exam-environment/', () => { const examAttempt = structuredClone(mock.examAttempt); const examTotalTimeInMS = mock.exam.config.totalTimeInS * 1000; - examAttempt.startTimeInMS = Date.now() - examTotalTimeInMS; examAttempt.startTime = new Date(Date.now() - examTotalTimeInMS); const attempt = await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({ @@ -1044,7 +1021,6 @@ describe('/exam-environment/', () => { passingPercent: 80 }, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets }; @@ -1100,7 +1076,6 @@ describe('/exam-environment/', () => { examId: mock.exam.id, result: null, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets }; @@ -1131,7 +1106,6 @@ describe('/exam-environment/', () => { examId: mock.exam.id, result: null, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets }; @@ -1143,7 +1117,6 @@ describe('/exam-environment/', () => { const examAttempt = structuredClone(mock.examAttempt); const examTotalTimeInMS = mock.exam.config.totalTimeInS * 1000; - examAttempt.startTimeInMS = Date.now() - examTotalTimeInMS; examAttempt.startTime = new Date(Date.now() - examTotalTimeInMS); const attempt = await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({ @@ -1170,7 +1143,6 @@ describe('/exam-environment/', () => { passingPercent: 80 }, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets }; @@ -1221,7 +1193,6 @@ describe('/exam-environment/', () => { examId: mock.exam.id, result: null, startTime: attempt.startTime, - startTimeInMS: attempt.startTimeInMS, questionSets: attempt.questionSets, // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment version: expect.any(Number) diff --git a/api/src/exam-environment/routes/exam-environment.ts b/api/src/exam-environment/routes/exam-environment.ts index e22da6660ef..403d8aa930d 100644 --- a/api/src/exam-environment/routes/exam-environment.ts +++ b/api/src/exam-environment/routes/exam-environment.ts @@ -260,10 +260,8 @@ async function postExamGeneratedExamHandler( const lastAttempt = examAttempts.length ? examAttempts.reduce((latest, current) => { - const latestStartTime = - latest.startTime?.getTime() ?? latest.startTimeInMS; - const currentStartTime = - current.startTime?.getTime() ?? current.startTimeInMS; + const latestStartTime = latest.startTime; + const currentStartTime = current.startTime; return latestStartTime > currentStartTime ? latest : current; }) : null; @@ -304,17 +302,12 @@ async function postExamGeneratedExamHandler( ); } - const lastAttemptStartTime = - lastAttempt.startTime?.getTime() ?? lastAttempt.startTimeInMS; - const examTotalTimeInMS = exam.config.totalTimeInS - ? exam.config.totalTimeInS * 1000 - : exam.config.totalTimeInMS; + const lastAttemptStartTime = lastAttempt.startTime.getTime(); + const examTotalTimeInMS = exam.config.totalTimeInS * 1000; const examExpirationTime = lastAttemptStartTime + examTotalTimeInMS; if (examExpirationTime < Date.now()) { - const examRetakeTimeInMS = exam.config.retakeTimeInS - ? exam.config.retakeTimeInS * 1000 - : exam.config.retakeTimeInMS; + const examRetakeTimeInMS = exam.config.retakeTimeInS * 1000; const retakeAllowed = examExpirationTime + examRetakeTimeInMS < Date.now(); @@ -466,7 +459,6 @@ async function postExamGeneratedExamHandler( userId: user.id, examId: exam.id, generatedExamId: generatedExam.id, - startTimeInMS: Date.now(), startTime: new Date(), questionSets: [] } @@ -565,9 +557,8 @@ async function postExamAttemptHandler( } const latestAttempt = attempts.reduce((latest, current) => { - const latestStartTime = latest.startTime?.getTime() ?? latest.startTimeInMS; - const currentStartTime = - current.startTime?.getTime() ?? current.startTimeInMS; + const latestStartTime = latest.startTime; + const currentStartTime = current.startTime; return latestStartTime > currentStartTime ? latest : current; }); @@ -600,11 +591,8 @@ async function postExamAttemptHandler( ); } - const latestAttemptStartTime = - latestAttempt.startTime?.getTime() ?? latestAttempt.startTimeInMS; - const examTotalTimeInMS = exam.config.totalTimeInS - ? exam.config.totalTimeInS * 1000 - : exam.config.totalTimeInMS; + const latestAttemptStartTime = latestAttempt.startTime.getTime(); + const examTotalTimeInMS = exam.config.totalTimeInS * 1000; const isAttemptExpired = latestAttemptStartTime + examTotalTimeInMS < Date.now(); @@ -747,7 +735,6 @@ async function getExams( select: { id: true, examId: true, - startTimeInMS: true, startTime: true } }) @@ -772,9 +759,7 @@ async function getExams( config: { name: exam.config.name, note: exam.config.note, - totalTimeInMS: exam.config.totalTimeInMS, totalTimeInS: exam.config.totalTimeInS, - retakeTimeInMS: exam.config.retakeTimeInMS, retakeTimeInS: exam.config.retakeTimeInS, passingPercent: exam.config.passingPercent }, @@ -798,10 +783,8 @@ async function getExams( const lastAttempt = attemptsForExam.length ? attemptsForExam.reduce((latest, current) => { - const latestStartTime = - latest.startTime?.getTime() ?? latest.startTimeInMS; - const currentStartTime = - current.startTime?.getTime() ?? current.startTimeInMS; + const latestStartTime = latest.startTime; + const currentStartTime = current.startTime; return latestStartTime > currentStartTime ? latest : current; }) : null; @@ -813,14 +796,9 @@ async function getExams( continue; } - const lastAttemptStartTime = - lastAttempt.startTime?.getTime() ?? lastAttempt.startTimeInMS; - const examTotalTimeInMS = exam.config.totalTimeInS - ? exam.config.totalTimeInS * 1000 - : exam.config.totalTimeInMS; - const examRetakeTimeInMS = exam.config.retakeTimeInS - ? exam.config.retakeTimeInS * 1000 - : exam.config.retakeTimeInMS; + const lastAttemptStartTime = lastAttempt.startTime.getTime(); + const examTotalTimeInMS = exam.config.totalTimeInS * 1000; + const examRetakeTimeInMS = exam.config.retakeTimeInS * 1000; const retakeDateInMS = lastAttemptStartTime + examTotalTimeInMS + examRetakeTimeInMS; diff --git a/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts b/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts index c2a07aa87c7..f891254dfdc 100644 --- a/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts +++ b/api/src/exam-environment/schemas/exam-environment-exam-attempt.ts @@ -29,7 +29,6 @@ export const examEnvironmentPostExamAttempt = { const examEnvAttempt = Type.Object({ id: Type.String(), examId: Type.String(), - startTimeInMS: Type.Number(), startTime: Type.String({ format: 'date-time' }), questionSets: Type.Array( Type.Object({ @@ -38,7 +37,6 @@ const examEnvAttempt = Type.Object({ Type.Object({ id: Type.String(), answers: Type.Array(Type.String()), - submissionTimeInMS: Type.Number(), submissionTime: Type.String({ format: 'date-time' }) }) ) diff --git a/api/src/exam-environment/schemas/exam-environment-exams.ts b/api/src/exam-environment/schemas/exam-environment-exams.ts index ac2239eaf32..10b9c991d29 100644 --- a/api/src/exam-environment/schemas/exam-environment-exams.ts +++ b/api/src/exam-environment/schemas/exam-environment-exams.ts @@ -11,9 +11,7 @@ export const examEnvironmentExams = { config: Type.Object({ name: Type.String(), note: Type.String(), - totalTimeInMS: Type.Number(), totalTimeInS: Type.Number(), - retakeTimeInMS: Type.Number(), retakeTimeInS: Type.Number(), passingPercent: Type.Number() }), diff --git a/api/src/exam-environment/utils/exam-environment.test.ts b/api/src/exam-environment/utils/exam-environment.test.ts index 11cf458e798..8b1f8871d40 100644 --- a/api/src/exam-environment/utils/exam-environment.test.ts +++ b/api/src/exam-environment/utils/exam-environment.test.ts @@ -446,10 +446,8 @@ describe('Exam Environment Schema', () => { note: '', passingPercent: 0.0, questionSets: configQuestionSets, - retakeTimeInMS: 0, retakeTimeInS: 0, tags, - totalTimeInMS: 0, totalTimeInS: 0 }; @@ -527,13 +525,11 @@ describe('Exam Environment Schema', () => { { answers: [oid()], id: oid(), - submissionTime: new Date(), - submissionTimeInMS: Date.now() + submissionTime: new Date() } ] } ], - startTimeInMS: Date.now(), startTime: new Date(), userId: oid() } diff --git a/api/src/exam-environment/utils/exam-environment.ts b/api/src/exam-environment/utils/exam-environment.ts index b72db4fab91..66af49e0bf5 100644 --- a/api/src/exam-environment/utils/exam-environment.ts +++ b/api/src/exam-environment/utils/exam-environment.ts @@ -111,11 +111,9 @@ export function constructUserExam( }); const config = { - totalTimeInMS: exam.config.totalTimeInMS, totalTimeInS: exam.config.totalTimeInS, name: exam.config.name, note: exam.config.note, - retakeTimeInMS: exam.config.retakeTimeInMS, retakeTimeInS: exam.config.retakeTimeInS, passingPercent: exam.config.passingPercent }; @@ -245,8 +243,7 @@ export function userAttemptToDatabaseAttemptQuestionSets( questions: questionSet.questions.map(q => { return { ...q, - submissionTime: new Date(), - submissionTimeInMS: Date.now() + submissionTime: new Date() }; }) }); @@ -262,8 +259,7 @@ export function userAttemptToDatabaseAttemptQuestionSets( if (!latestQuestion) { return { ...q, - submissionTime: new Date(), - submissionTimeInMS: Date.now() + submissionTime: new Date() }; } @@ -273,8 +269,7 @@ export function userAttemptToDatabaseAttemptQuestionSets( ) { return { ...q, - submissionTime: new Date(), - submissionTimeInMS: Date.now() + submissionTime: new Date() }; } @@ -824,11 +819,8 @@ export async function constructEnvExamAttempt( } // If attempt is still in progress, return without result - const attemptStartTimeInMS = - attempt.startTime?.getTime() ?? attempt.startTimeInMS; - const examTotalTimeInMS = exam.config.totalTimeInS - ? exam.config.totalTimeInS * 1000 - : exam.config.totalTimeInMS; + const attemptStartTimeInMS = attempt.startTime.getTime(); + const examTotalTimeInMS = exam.config.totalTimeInS * 1000; const isAttemptExpired = attemptStartTimeInMS + examTotalTimeInMS < Date.now(); if (!isAttemptExpired) { diff --git a/client/src/templates/Challenges/exam-download/attempts.tsx b/client/src/templates/Challenges/exam-download/attempts.tsx index 557af7dcd9c..ebedcdf1ced 100644 --- a/client/src/templates/Challenges/exam-download/attempts.tsx +++ b/client/src/templates/Challenges/exam-download/attempts.tsx @@ -59,8 +59,8 @@ export function Attempts({ id }: AttemptsProps) {
{attempts.map(attempt => ( -