diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 6c70856..c5f5d68 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -10,6 +10,11 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Setup Node.js environment + uses: actions/setup-node@v4.1.0 + with: + node-version: 18 + - name: Get branch name id: branch-name uses: tj-actions/branch-names@v7 diff --git a/src/routes/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/+page.server.ts b/src/routes/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/+page.server.ts index 6baa4eb..01d4dc1 100644 --- a/src/routes/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/+page.server.ts +++ b/src/routes/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/+page.server.ts @@ -1,295 +1,311 @@ -import { getPersonByEmail } from "$lib/server/util/person/getPersonByEmail.js"; -import { getPersonFromUser } from "$lib/server/util/person/getPersonFromUser"; -import prisma from "$lib/server/util/prisma.js"; -import { QuizType } from "@prisma/client"; -import { error, redirect } from "@sveltejs/kit"; -import type { PageServerLoad } from "./$types.js"; -import { QuestionType, type Question, type ServerQuestion } from "$lib/util/lab-certification/quizzes.js"; +import { getPersonByEmail } from '$lib/server/util/person/getPersonByEmail.js'; +import { getPersonFromUser } from '$lib/server/util/person/getPersonFromUser'; +import prisma from '$lib/server/util/prisma.js'; +import { QuizType } from '@prisma/client'; +import { error, redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types.js'; +import { + QuestionType, + type Question, + type ServerQuestion +} from '$lib/util/lab-certification/quizzes.js'; export const _questions: ServerQuestion[] = [ - { - id: "what-do-you-do", - readableName: "What do you do in the event of an emergency?", - type: QuestionType.SINGLE_SELECT, - answers: { - "call-911": "Call 911", - "run-away": "Run away screaming", - "notify-mentor": "Notify the acting mentor immediately", - "nothing": "Nothing", - }, - correctAnswer: "notify-mentor", - }, - { - id: "where-fire-extinguishers", - readableName: "Where are the Fire Extinguishers?", - type: QuestionType.SINGLE_SELECT, - answers: { - "door-husky": "By the door and next to the Husky", - "cnc-front": "Next to the CNC and at the front of the room", - "lab": "In the engineering lab", - }, - correctAnswer: "cnc-front", - }, - { - id: "close-calls", - readableName: "Should you report any injuries or \"close calls\" with tool use?", - type: QuestionType.SINGLE_SELECT, - answers: { - "yes": "Yes", - "no": "No", - }, - correctAnswer: "yes", - }, - { - id: "tool-location", - readableName: "Where are tools located and who do they belong to?", - type: QuestionType.MULTI_SELECT, - answers: { - "school-tools": "School tools are in the cabinets and wall and are limited to class use", - "everyone": "The tools are open to everyone", - "wherever": "Tools go wherever you feel like", - "robotics": "Highlander Robotics tools are located in the Husky and tables and are limited to robotics use", - }, - correctAnswer: [ - "school-tools", - "robotics" - ], - }, - { - id: "first-aid-kit", - readableName: "Where is the first-aid kit located?", - type: QuestionType.SINGLE_SELECT, - answers: { - "backpack-husky": "In the red backpack or on top of the of the Husky", - "fire-extinguisher": "Next to the fire extinguisher", - "tables": "In one of the tables", - }, - correctAnswer: "backpack-husky", - }, - { - id: "earthquake-fire", - readableName: "What should you do in the event of an earthquake or fire?", - type: QuestionType.SINGLE_SELECT, - answers: { - "alert-mentor": "Alert a mentor", - "alert-mentor-exit": "Alert a mentor then exit the building", - "continue": "If it's safe, continue working", - }, - correctAnswer: "alert-mentor-exit", - }, - { - id: "put-away-tools", - readableName: "How do you put away tools?", - type: QuestionType.SINGLE_SELECT, - answers: { - "quickly": "Quickly", - "wherever": "Wherever you think it should go", - "proper-location": "In the proper location, asking if you don't know", - "where-found": "Back where you got it", - }, - correctAnswer: "proper-location", - }, - { - id: "leaving-lab", - readableName: "What are the most important things to do before leaving the lab?", - type: QuestionType.MULTI_SELECT, - answers: { - "finishing-project": "Finishing your project", - "cleaning-checklist": "Making sure the cleaning checklist is completed and the lab is in proper condition", - "doors-locked": "Making sure the doors are locked", - "leave-project-out": "Leaving your project out so you can work on it next time you come in", - }, - correctAnswer: [ - "cleaning-checklist", - "doors-locked", - ], - }, - { - id: "cabinet-locked", - readableName: "If a cabinet is locked, what should you do?", - type: QuestionType.SINGLE_SELECT, - answers: { - "steal-key": "Steal a key and unlock it", - "force": "Try and force it open", - "ask": "Ask a supervisor/mentor or lead to unlock it", - }, - correctAnswer: "ask", - }, - { - id: "what-do-you-need", - readableName: "What do you need to be able to work in the lab?", - type: QuestionType.SINGLE_SELECT, - answers: { - "certified-adult": "A certified adult to act as supervision", - "friends": "Some friends", - "food": "Food", - }, - correctAnswer: "certified-adult", - } + { + id: 'what-do-you-do', + readableName: 'What do you do in the event of an emergency?', + type: QuestionType.MULTI_SELECT, + answers: { + 'call-911': 'Call 911 if needed', + 'run-away': 'Run away screaming', + 'notify-mentor': 'Notify the acting mentor immediately', + nothing: 'Nothing' + }, + correctAnswer: ['call-911', 'notify-mentor'] + }, + { + id: 'where-fire-extinguishers', + readableName: 'Where are the Fire Extinguishers?', + type: QuestionType.MULTI_SELECT, + answers: { + 'door-husky': 'By the door', + cnc: 'Next to the CNC', + husky: 'Next to the Husky', + laser: 'Next to the laser (laser only!)' + }, + correctAnswer: ['door-husky', 'husky', 'laser'] + }, + { + id: 'close-calls', + readableName: 'Should you report any injuries or "close calls" with tool use?', + type: QuestionType.SINGLE_SELECT, + answers: { + yes: 'Yes', + no: 'No' + }, + correctAnswer: 'yes' + }, + { + id: 'tool-location', + readableName: 'Where are tools located and who do they belong to?', + type: QuestionType.MULTI_SELECT, + answers: { + 'school-tools': 'School tools are in the cabinets and wall and are limited to class use', + everyone: 'The tools are open to everyone', + wherever: 'Tools go wherever you feel like', + robotics: + 'Highlander Robotics tools are located in the team Husky and shed and are limited to robotics use' + }, + correctAnswer: ['school-tools', 'robotics'] + }, + { + id: 'first-aid-kit', + readableName: 'Where is the first-aid kit located?', + type: QuestionType.SINGLE_SELECT, + answers: { + 'backpack-husky': 'In the red backpack, in the Husky, and by the sink', + 'fire-extinguisher': 'Next to the fire extinguisher', + tables: 'In one of the tables and in the shed' + }, + correctAnswer: 'backpack-husky' + }, + { + id: 'earthquake-fire', + readableName: 'What should you do in the event of an earthquake or fire?', + type: QuestionType.SINGLE_SELECT, + answers: { + 'alert-mentor': 'Alert a mentor', + 'alert-mentor-exit': 'Alert a mentor and exit the building', + continue: "If it's safe, continue working" + }, + correctAnswer: 'alert-mentor-exit' + }, + { + id: 'put-away-tools', + readableName: 'How do you put away tools?', + type: QuestionType.SINGLE_SELECT, + answers: { + quickly: 'Quickly', + wherever: 'Wherever you think it should go', + 'proper-location': "In the proper location, asking if you don't know", + 'where-found': 'Back where you got it' + }, + correctAnswer: 'proper-location' + }, + { + id: 'leaving-lab', + readableName: 'What are the most important things to do before leaving the lab?', + type: QuestionType.MULTI_SELECT, + answers: { + 'finishing-project': 'Finishing your project', + 'cleaning-checklist': + 'Making sure the cleaning checklist is completed and the lab is in proper condition', + 'doors-locked': 'Making sure the doors are locked', + 'leave-project-out': 'Leaving your project out so you can work on it next time you come in', + 'machinery-off': 'Ensure all machinery is off' + }, + correctAnswer: ['cleaning-checklist', 'doors-locked', 'machinery-off'] + }, + { + id: 'cabinet-locked', + readableName: 'If a cabinet is locked, what should you do?', + type: QuestionType.SINGLE_SELECT, + answers: { + 'steal-key': 'Steal a key and unlock it', + force: 'Try and force it open', + ask: 'Ask a supervisor/mentor or lead to unlock it' + }, + correctAnswer: 'ask' + }, + { + id: 'what-do-you-need', + readableName: 'What do you need to be able to work in the lab?', + type: QuestionType.SINGLE_SELECT, + answers: { + 'certified-adult': 'A certified adult to act as supervision', + friends: 'Some friends', + food: 'Food' + }, + correctAnswer: 'certified-adult' + } ]; export const _clientQuestions: Question[] = _questions.map((q) => ({ - id: q.id, - readableName: q.readableName, - type: q.type, - answers: q.answers, + id: q.id, + readableName: q.readableName, + type: q.type, + answers: q.answers })); export const load = (async () => { - return { - questions: _clientQuestions, - }; + return { + questions: _clientQuestions + }; }) satisfies PageServerLoad; -const correctAnswers: Record = Object.fromEntries(_questions.map((q) => [q.id, q.correctAnswer])); +const correctAnswers: Record = Object.fromEntries( + _questions.map((q) => [q.id, q.correctAnswer]) +); export const actions = { - default: async (event) => { - const session = await event.locals.getSession(); - - const data = await event.request.formData(); + default: async (event) => { + const session = await event.locals.getSession(); - let person = null; - if (session?.user) { - person = await getPersonFromUser(session?.user); - } else { - const email = data.get("email")?.toString(); + const data = await event.request.formData(); - if (!email) { - throw error(400, "No email provided"); - } + let person = null; + if (session?.user) { + person = await getPersonFromUser(session?.user); + } else { + const email = data.get('email')?.toString(); - person = await getPersonByEmail(email); - } + if (!email) { + throw error(400, 'No email provided'); + } - - const answers: Record = {}; - for (const [key, value] of data.entries()) { - if (!key.includes("__")) { - if (!correctAnswers[key]) { - // It's the email or something else we don't care about - continue; - } - answers[key] = value.toString(); - } else { - const [question, answer] = key.split("__"); + person = await getPersonByEmail(email); + } - if (!correctAnswers[question]) { - // It's the email or something else we don't care about - continue; - } + const answers: Record = {}; + for (const [key, value] of data.entries()) { + if (!key.includes('__')) { + if (!correctAnswers[key]) { + // It's the email or something else we don't care about + continue; + } + answers[key] = value.toString(); + } else { + const [question, answer] = key.split('__'); - if (!answers[question]) { - answers[question] = []; - } + if (!correctAnswers[question]) { + // It's the email or something else we don't care about + continue; + } - if (value.toString() === "on") { - (answers[question] as string[]).push(answer); - } - } - } + if (!answers[question]) { + answers[question] = []; + } - let score = 0; - let total = 0; - const incorrectQuestions: string[] = []; + if (value.toString() === 'on') { + (answers[question] as string[]).push(answer); + } + } + } - for (const [question, correctAnswer] of Object.entries(correctAnswers)) { - if (Array.isArray(correctAnswer)) { - total++ - let correct = true; - for (const a of correctAnswer) { - if (!answers[question].includes(a)) { - correct = false; - } - } - for (const a of answers[question]) { - if (!correctAnswer.includes(a)) { - correct = false; - } - } - if (correct) { - score++; - } else { - incorrectQuestions.push(question); - } - } else { - total++; - if (answers[question] === correctAnswer) { - score++; - } else { - incorrectQuestions.push(question); - } - } - } + let score = 0; + let total = 0; + const incorrectQuestions: string[] = []; - if (!person && !data.get("email")) { - throw error(400, "No email provided"); - } + for (const [question, correctAnswer] of Object.entries(correctAnswers)) { + if (Array.isArray(correctAnswer)) { + total++; + let correct = true; + for (const a of correctAnswer) { + if (!answers[question].includes(a)) { + correct = false; + } + } + for (const a of answers[question]) { + if (!correctAnswer.includes(a)) { + correct = false; + } + } + if (correct) { + score++; + } else { + incorrectQuestions.push(question); + } + } else { + total++; + if (answers[question] === correctAnswer) { + score++; + } else { + incorrectQuestions.push(question); + } + } + } - await prisma.quizSubmission.create({ - data: { - quizType: QuizType.LAB_LAYOUT_EMERGENCY_PREPAREDNESS, - submitterId: person?.id, - submitterEmail: !person ? data.get("email")?.toString() : undefined, - answers: JSON.stringify(answers), - score, - maxScore: total, - } - }); + if (!person && !data.get('email')) { + throw error(400, 'No email provided'); + } - const percent = Math.round(score / total * 100); + await prisma.quizSubmission.create({ + data: { + quizType: QuizType.LAB_LAYOUT_EMERGENCY_PREPAREDNESS, + submitterId: person?.id, + submitterEmail: !person ? data.get('email')?.toString() : undefined, + answers: JSON.stringify(answers), + score, + maxScore: total + } + }); - if (percent >= 90) { - if (person) { - await prisma.person.update({ - where: { - id: person.id, - }, - include: { - labCertification: true, - }, - data: { - labCertification: { - upsert: { - create: { - labLayoutEmergencyPreparedness: true, - }, - update: { - labLayoutEmergencyPreparedness: true, - } - } - } - } - }); - } + const percent = Math.round((score / total) * 100); - throw redirect(303, `/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/pass?score=${encodeURIComponent(score)}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent(JSON.stringify(incorrectQuestions))}`); - } else { - if (person) { - await prisma.person.update({ - where: { - id: person.id, - }, - include: { - labCertification: true, - }, - data: { - labCertification: { - upsert: { - create: { - labLayoutEmergencyPreparedness: false, - }, - update: { - labLayoutEmergencyPreparedness: false, - } - } - } - } - }); - } + if (percent >= 90) { + if (person) { + await prisma.person.update({ + where: { + id: person.id + }, + include: { + labCertification: true + }, + data: { + labCertification: { + upsert: { + create: { + labLayoutEmergencyPreparedness: true + }, + update: { + labLayoutEmergencyPreparedness: true + } + } + } + } + }); + } - throw redirect(303, `/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/fail?score=${encodeURIComponent(score)}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent(JSON.stringify(incorrectQuestions))}`); - } - }, -} + throw redirect( + 303, + `/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/pass?score=${encodeURIComponent( + score + )}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent( + JSON.stringify(incorrectQuestions) + )}` + ); + } else { + if (person) { + await prisma.person.update({ + where: { + id: person.id + }, + include: { + labCertification: true + }, + data: { + labCertification: { + upsert: { + create: { + labLayoutEmergencyPreparedness: false + }, + update: { + labLayoutEmergencyPreparedness: false + } + } + } + } + }); + } + throw redirect( + 303, + `/tools/lab-certification/quizzes/lab-layout-emergency-preparedness/fail?score=${encodeURIComponent( + score + )}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent( + JSON.stringify(incorrectQuestions) + )}` + ); + } + } +}; diff --git a/src/routes/tools/lab-certification/quizzes/safety-quiz/+page.server.ts b/src/routes/tools/lab-certification/quizzes/safety-quiz/+page.server.ts index fef7708..7085ee7 100644 --- a/src/routes/tools/lab-certification/quizzes/safety-quiz/+page.server.ts +++ b/src/routes/tools/lab-certification/quizzes/safety-quiz/+page.server.ts @@ -1,354 +1,355 @@ -import { getPersonByEmail } from "$lib/server/util/person/getPersonByEmail.js"; -import { getPersonFromUser } from "$lib/server/util/person/getPersonFromUser"; -import prisma from "$lib/server/util/prisma.js"; -import { QuizType } from "@prisma/client"; -import { error, redirect } from "@sveltejs/kit"; -import type { PageServerLoad } from "./$types.js"; -import { QuestionType, type Question, type ServerQuestion } from "$lib/util/lab-certification/quizzes.js"; +import { getPersonByEmail } from '$lib/server/util/person/getPersonByEmail.js'; +import { getPersonFromUser } from '$lib/server/util/person/getPersonFromUser'; +import prisma from '$lib/server/util/prisma.js'; +import { QuizType } from '@prisma/client'; +import { error, redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types.js'; +import { + QuestionType, + type Question, + type ServerQuestion +} from '$lib/util/lab-certification/quizzes.js'; export const _questions: ServerQuestion[] = [ - { - id: "safety-glasses", - readableName: "When should you be wearing safety glasses?", - type: QuestionType.SINGLE_SELECT, - answers: { - "working-on-robot": "When working on the robot.", - "all-times": "At all times in the shop.", - "advanced-machine-tool": "When operating an advanced machine tool.", - "feel-unsafe": "Only when you feel unsafe.", - }, - correctAnswer: "all-times", - }, - { - id: "tool-without-test", - readableName: "Can you use a shop tool without the safety test if you're careful?", - type: QuestionType.SINGLE_SELECT, - answers: { - "basic-tools": "Only for basic tools.", - "never": "Never.", - "student-supervision": "With the supervision of another student.", - }, - correctAnswer: "never", - }, - { - id: "unacceptable-attire", - readableName: "What is unacceptable attire for the shop?", - type: QuestionType.MULTI_SELECT, - answers: { - "shorts": "Shorts", - "long-hair": "Long hair not tied back", - "close-toed-shoes": "Close Toed Shoes", - "baggy-sweaters": "Baggy sweaters", - "safety-glasses": "Safety Glasses", - "reading-glasses": "Reading Glasses with protective safety glasses", - "long-pants": "Long Pants", - }, - correctAnswer: [ - "shorts", - "long-hair", - "baggy-sweaters", - ], - }, - { - id: "when-finished", - readableName: "What needs to be done after you finish with a tool or machine?", - type: QuestionType.MULTI_SELECT, - answers: { - "leave-it": "Leave it for someone else.", - "put-away": "Put it await in the correct location.", - "clean-up": "Clean up your space.", - "find-someone-else": "Find someone else who needs it.", - }, - correctAnswer: [ - "put-away", - "clean-up", - ], - }, - { - id: "when-clean-up", - readableName: "When should you clean up your mess?", - type: QuestionType.SINGLE_SELECT, - answers: { - "end-of-day": "At the end of the work day.", - "too-messy": "When you feel the space is too messy to work.", - "finish-using-tool": "After you finish using the tool.", - }, - correctAnswer: "finish-using-tool", - }, - { - id: "can-work-when-alone", - readableName: "Can you work in the shop when everybody is getting food and you're alone?", - type: QuestionType.SINGLE_SELECT, - answers: { - "yes": "Yes.", - "no": "No.", - }, - correctAnswer: "no", - }, - { - id: "bad-etiquette", - readableName: "What are some examples of bad shop etiquette?", - type: QuestionType.MULTI_SELECT, - answers: { - "too-many-questions": "Asking too many questions.", - "leaving-tools-out": "Leaving tools out for someone else.", - "talking-while-machining": "Talking to someone as they are machining.", - "eating-when-working": "Eating food when working with tools", - "working-when-tired": "Working when tired or distracted if the part needs to get done.", - "turning-off-machine": "Turning off your machine before walking away.", - }, - correctAnswer: [ - "leaving-tools-out", - "talking-while-machining", - "eating-when-working", - "working-when-tired", - ], - }, - { - id: "feel-uncomfortable", - readableName: "If you don't feel comfortable with a tool you should...", - type: QuestionType.SINGLE_SELECT, - answers: { - "carefully-slowly": "Try to use it carefully and slowly.", - "teach-you-now": "Have someone teach you then and there.", - "wikihow": "Read a WikiHow page on it.", - "re-certified": "Refer back to the machine training and get re-certified.", - }, - correctAnswer: "re-certified", - }, - { - id: "can-be-messy", - readableName: "Floors, tables, and machines can be messy if...", - type: QuestionType.SINGLE_SELECT, - answers: { - "looks-good": "It makes the shop look like work is getting done.", - "should-not-be": "They should not be.", - "need-to-finish": "We're back logged and need to finish.", - }, - correctAnswer: "should-not-be", - }, - { - id: "emergency", - readableName: "In case of an emergency you should...", - type: QuestionType.SINGLE_SELECT, - answers: { - "help": "Take immediate action and help.", - "leave": "Leave them and don't create further trouble.", - "notify": "Notify the parent or mentor supervising to take care of the emergency.", - "call-911": "Immediately call 911.", - }, - correctAnswer: "notify", - }, - { - id: "about-to-use", - readableName: "If you're about to use a tool or machine you should...", - type: QuestionType.MULTI_SELECT, - answers: { - "have-fun": "Use it and have fun.", - "avoid-mistakes": "Assess what can go wrong and avoid mistakes.", - "take-photo": "Have someone take a photo of you.", - "aware-safety-glasses": "Make sure others are aware and are wearing their safety glasses", - }, - correctAnswer: [ - "avoid-mistakes", - "aware-safety-glasses", - ], - }, - { - id: "gloves", - readableName: "When should you wear gloves?", - type: QuestionType.MULTI_SELECT, - answers: { - "welding": "While welding, in which case you use specific gloves.", - "hot": "When handling something hot.", - "rotating-machine": "When using any rotating machine.", - "drive-base": "Working on the drive base, grease is dirty.", - "sharps": "When working with sharp objects or splintered wood.", - }, - correctAnswer: [ - "welding", - "hot", - "drive-base", - "sharps", - ], - }, - { - id: "defective-tools", - readableName: "If tools become defective you should...", - type: QuestionType.SINGLE_SELECT, - answers: { - "discard": "Toss it.", - "consult": "Talk to a mentor or student leader and see what to do.", - "repair": "Attempt to repair it.", - "take-home": "Take it home.", - "put-back": "Put it back in the toolbox.", - }, - correctAnswer: "consult", - }, + { + id: 'safety-glasses', + readableName: 'When should you be wearing safety glasses?', + type: QuestionType.SINGLE_SELECT, + answers: { + 'working-on-robot': 'When working on the robot.', + 'all-times': 'At all times in the shop.', + 'advanced-machine-tool': 'When operating an advanced machine tool.', + 'feel-unsafe': 'Only when you feel unsafe.' + }, + correctAnswer: 'all-times' + }, + { + id: 'tool-without-test', + readableName: "Can you use a shop tool without the safety test if you're careful?", + type: QuestionType.SINGLE_SELECT, + answers: { + 'basic-tools': 'Only for basic tools.', + never: 'Never.', + 'student-supervision': 'With the supervision of another student.' + }, + correctAnswer: 'never' + }, + { + id: 'requierd-attire', + readableName: 'What attire is required for the shop?', + type: QuestionType.MULTI_SELECT, + answers: { + 'long-pants': 'Long pants', + 'long-hair-tied': 'Long hair tied back', + 'close-toed-shoes': 'Close Toed Shoes', + 'baggy-sweaters': 'Baggy sweaters', + 'short-sleeves': 'Short sleeves or sleeves rolled above the elbow', + 'safety-glasses': 'Safety Glasses', + 'reading-glasses': 'Reading Glasses with protective safety glasses' + }, + correctAnswer: ['long-pants', 'long-hair-tied', 'short-sleeves', 'safety-glasses'] + }, + { + id: 'when-finished', + readableName: 'What needs to be done after you finish with a tool or machine?', + type: QuestionType.MULTI_SELECT, + answers: { + 'leave-it': 'Leave it for someone else.', + 'put-away': 'Put it away in the correct location.', + 'clean-up': 'Clean up your space.', + 'find-someone-else': 'Find someone else who needs it.' + }, + correctAnswer: ['put-away', 'clean-up'] + }, + { + id: 'when-clean-up', + readableName: 'When should you clean up your mess?', + type: QuestionType.SINGLE_SELECT, + answers: { + 'end-of-day': 'At the end of the work day.', + 'too-messy': 'When you feel the space is too messy to work.', + 'finish-using-tool': 'After you finish using the tool.' + }, + correctAnswer: 'finish-using-tool' + }, + { + id: 'can-work-when-alone', + readableName: "Can you work in the shop when everybody is getting food and you're alone?", + type: QuestionType.SINGLE_SELECT, + answers: { + yes: 'Yes.', + no: 'No.' + }, + correctAnswer: 'no' + }, + { + id: 'bad-etiquette', + readableName: 'What are some examples of bad shop etiquette?', + type: QuestionType.MULTI_SELECT, + answers: { + 'too-many-questions': 'Asking too many questions.', + 'leaving-tools-out': 'Leaving tools out for someone else.', + 'talking-while-machining': 'Talking to someone as they are machining.', + 'eating-when-working': 'Eating food when working with tools', + 'working-when-tired': 'Working when tired or distracted if the part needs to get done.', + 'turning-off-machine': 'Turning off your machine before walking away.' + }, + correctAnswer: [ + 'leaving-tools-out', + 'talking-while-machining', + 'eating-when-working', + 'working-when-tired' + ] + }, + { + id: 'feel-uncomfortable', + readableName: "If you don't feel comfortable with a tool you should...", + type: QuestionType.SINGLE_SELECT, + answers: { + 'carefully-slowly': 'Try to use it carefully and slowly.', + wikihow: 'Read a WikiHow page on it.', + support: 'Ask a mentor to support you.' + }, + correctAnswer: 'support' + }, + { + id: 'can-be-messy', + readableName: 'Floors, tables, and machines can be messy if...', + type: QuestionType.SINGLE_SELECT, + answers: { + 'looks-good': 'It makes the shop look like work is getting done.', + 'should-not-be': 'They should not be.', + 'need-to-finish': "We're back logged and need to finish." + }, + correctAnswer: 'should-not-be' + }, + { + id: 'emergency', + readableName: 'In case of an emergency you should...', + type: QuestionType.SINGLE_SELECT, + answers: { + help: 'Take immediate action and help.', + leave: "Leave them and don't create further trouble.", + notify: 'Notify the parent or mentor supervising.', + 'call-911': 'Immediately call 911.' + }, + correctAnswer: 'notify' + }, + { + id: 'about-to-use', + readableName: "If you're about to use a tool or machine you should...", + type: QuestionType.MULTI_SELECT, + answers: { + 'have-fun': 'Use it and have fun.', + 'avoid-mistakes': 'Assess what can go wrong and avoid mistakes.', + 'take-photo': 'Have someone take a photo of you.', + 'aware-safety-glasses': 'Make sure others are aware and are wearing their safety glasses' + }, + correctAnswer: ['avoid-mistakes', 'aware-safety-glasses'] + }, + { + id: 'gloves', + readableName: "When shouldn't you wear gloves?", + type: QuestionType.SINGLE_SELECT, + answers: { + welding: 'While welding', + hot: 'When handling something hot.', + 'powered-tool': 'When using any powered tool.', + sharps: 'When working with sharp objects or splintered wood.' + }, + correctAnswer: 'powered-tool' + }, + { + id: 'defective-tools', + readableName: 'If tools become defective you should...', + type: QuestionType.SINGLE_SELECT, + answers: { + discard: 'Toss it.', + consult: 'Talk to a mentor or student leader and see what to do.', + repair: 'Attempt to repair it.', + 'take-home': 'Take it home.', + 'put-back': 'Put it back in the toolbox.' + }, + correctAnswer: 'consult' + } ]; export const _clientQuestions: Question[] = _questions.map((q) => ({ - id: q.id, - readableName: q.readableName, - type: q.type, - answers: q.answers, + id: q.id, + readableName: q.readableName, + type: q.type, + answers: q.answers })); export const load = (async () => { - return { - questions: _clientQuestions, - }; + return { + questions: _clientQuestions + }; }) satisfies PageServerLoad; -const correctAnswers: Record = Object.fromEntries(_questions.map((q) => [q.id, q.correctAnswer])); +const correctAnswers: Record = Object.fromEntries( + _questions.map((q) => [q.id, q.correctAnswer]) +); export const actions = { - default: async (event) => { - const session = await event.locals.getSession(); - - const data = await event.request.formData(); + default: async (event) => { + const session = await event.locals.getSession(); - let person = null; - if (session?.user) { - person = await getPersonFromUser(session?.user); - } else { - const email = data.get("email")?.toString(); + const data = await event.request.formData(); - if (!email) { - throw error(400, "No email provided"); - } + let person = null; + if (session?.user) { + person = await getPersonFromUser(session?.user); + } else { + const email = data.get('email')?.toString(); - person = await getPersonByEmail(email); - } + if (!email) { + throw error(400, 'No email provided'); + } - - const answers: Record = {}; - for (const [key, value] of data.entries()) { - if (!key.includes("__")) { - if (!correctAnswers[key]) { - // It's the email or something else we don't care about - continue; - } - answers[key] = value.toString(); - } else { - const [question, answer] = key.split("__"); + person = await getPersonByEmail(email); + } - if (!correctAnswers[question]) { - // It's the email or something else we don't care about - continue; - } + const answers: Record = {}; + for (const [key, value] of data.entries()) { + if (!key.includes('__')) { + if (!correctAnswers[key]) { + // It's the email or something else we don't care about + continue; + } + answers[key] = value.toString(); + } else { + const [question, answer] = key.split('__'); - if (!answers[question]) { - answers[question] = []; - } + if (!correctAnswers[question]) { + // It's the email or something else we don't care about + continue; + } - if (value.toString() === "on") { - (answers[question] as string[]).push(answer); - } - } - } + if (!answers[question]) { + answers[question] = []; + } - let score = 0; - let total = 0; - const incorrectQuestions: string[] = []; + if (value.toString() === 'on') { + (answers[question] as string[]).push(answer); + } + } + } - for (const [question, correctAnswer] of Object.entries(correctAnswers)) { - if (Array.isArray(correctAnswer)) { - total++ - let correct = true; - for (const a of correctAnswer) { - if (!answers[question].includes(a)) { - correct = false; - } - } - for (const a of answers[question]) { - if (!correctAnswer.includes(a)) { - correct = false; - } - } - if (correct) { - score++; - } else { - incorrectQuestions.push(question); - } - } else { - total++; - if (answers[question] === correctAnswer) { - score++; - } else { - incorrectQuestions.push(question); - } - } - } + let score = 0; + let total = 0; + const incorrectQuestions: string[] = []; - if (!person && !data.get("email")) { - throw error(400, "No email provided"); - } + for (const [question, correctAnswer] of Object.entries(correctAnswers)) { + if (Array.isArray(correctAnswer)) { + total++; + let correct = true; + for (const a of correctAnswer) { + if (!answers[question].includes(a)) { + correct = false; + } + } + for (const a of answers[question]) { + if (!correctAnswer.includes(a)) { + correct = false; + } + } + if (correct) { + score++; + } else { + incorrectQuestions.push(question); + } + } else { + total++; + if (answers[question] === correctAnswer) { + score++; + } else { + incorrectQuestions.push(question); + } + } + } - await prisma.quizSubmission.create({ - data: { - quizType: QuizType.SAFETY_QUIZ, - submitterId: person?.id, - submitterEmail: !person ? data.get("email")?.toString() : undefined, - answers: JSON.stringify(answers), - score, - maxScore: total, - } - }); + if (!person && !data.get('email')) { + throw error(400, 'No email provided'); + } - const percent = Math.round(score / total * 100); + await prisma.quizSubmission.create({ + data: { + quizType: QuizType.SAFETY_QUIZ, + submitterId: person?.id, + submitterEmail: !person ? data.get('email')?.toString() : undefined, + answers: JSON.stringify(answers), + score, + maxScore: total + } + }); - if (percent >= 80) { - if (person) { - await prisma.person.update({ - where: { - id: person.id, - }, - include: { - labCertification: true, - }, - data: { - labCertification: { - upsert: { - create: { - safetyQuiz: true, - }, - update: { - safetyQuiz: true, - } - } - } - } - }); - } + const percent = Math.round((score / total) * 100); - throw redirect(303, `/tools/lab-certification/quizzes/safety-quiz/pass?score=${encodeURIComponent(score)}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent(JSON.stringify(incorrectQuestions))}`); - } else { - if (person) { - await prisma.person.update({ - where: { - id: person.id, - }, - include: { - labCertification: true, - }, - data: { - labCertification: { - upsert: { - create: { - safetyQuiz: false, - }, - update: { - safetyQuiz: false, - } - } - } - } - }); - } + if (percent >= 80) { + if (person) { + await prisma.person.update({ + where: { + id: person.id + }, + include: { + labCertification: true + }, + data: { + labCertification: { + upsert: { + create: { + safetyQuiz: true + }, + update: { + safetyQuiz: true + } + } + } + } + }); + } - throw redirect(303, `/tools/lab-certification/quizzes/safety-quiz/fail?score=${encodeURIComponent(score)}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent(JSON.stringify(incorrectQuestions))}`); - } - }, -} + throw redirect( + 303, + `/tools/lab-certification/quizzes/safety-quiz/pass?score=${encodeURIComponent( + score + )}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent( + JSON.stringify(incorrectQuestions) + )}` + ); + } else { + if (person) { + await prisma.person.update({ + where: { + id: person.id + }, + include: { + labCertification: true + }, + data: { + labCertification: { + upsert: { + create: { + safetyQuiz: false + }, + update: { + safetyQuiz: false + } + } + } + } + }); + } + throw redirect( + 303, + `/tools/lab-certification/quizzes/safety-quiz/fail?score=${encodeURIComponent( + score + )}&total=${encodeURIComponent(total)}&incorrect=${encodeURIComponent( + JSON.stringify(incorrectQuestions) + )}` + ); + } + } +};