From 65044ee861f75cb78d9418a41a21adc881993fe6 Mon Sep 17 00:00:00 2001 From: Shadi Date: Tue, 24 Mar 2026 19:19:09 +0100 Subject: [PATCH 1/4] Implement Time class with methods for hours, minutes, seconds. --- task-1/Time.js | 36 ++++++++++++++++++++++++++++++++++-- task-1/index.js | 5 +++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/task-1/Time.js b/task-1/Time.js index 97ca7e2..13457af 100644 --- a/task-1/Time.js +++ b/task-1/Time.js @@ -1,3 +1,35 @@ export class Time { - // Your code here -} \ No newline at end of file + #secondsFromMidnight + + constructor(hours, minutes , seconds) { + + this.#secondsFromMidnight = hours * 3600 + minutes * 60 + seconds + } + getHours() { + return Math.floor(this.#secondsFromMidnight / 3600) + } + getMinutes() { + return Math.floor((this.#secondsFromMidnight % 3600) / 60) + } + getSeconds() { + return this.#secondsFromMidnight % 60 + } + + addSeconds(seconds) { + this.#secondsFromMidnight += seconds; + this.#secondsFromMidnight = ((this.#secondsFromMidnight % 86400) + 86400) % 86400; + } + addMinutes(minutes) { + this.addSeconds(minutes * 60) + } + addHours(hours) { + this.addSeconds(hours * 3600) + } + toString() { + const hours = String(this.getHours()).padStart(2, '0'); + const minutes = String(this.getMinutes()).padStart(2, '0'); + const seconds = String(this.getSeconds()).padStart(2, '0'); + return `${hours}:${minutes}:${seconds}`; + } +} + diff --git a/task-1/index.js b/task-1/index.js index cfa7c93..d937149 100644 --- a/task-1/index.js +++ b/task-1/index.js @@ -2,3 +2,8 @@ import { Time } from './Time.js'; const time = new Time(13, 37, 0); console.log(time.toString()); // Output: "13:37:00" +console.log(time.getHours()); // Output: 13 +time.addMinutes(10); +console.log(time.toString()); +time.addSeconds(8000); +console.log(time.toString()); From cd1541ec9d6b0994c98964bf4ce3a30f0e119b65 Mon Sep 17 00:00:00 2001 From: Shadi Date: Wed, 25 Mar 2026 19:37:57 +0100 Subject: [PATCH 2/4] Add quiz functionality with dynamic question generation and answer validation --- task-2/answer.js | 7 +++ task-2/index.js | 72 +++++++++++++++++++++++++++++- task-2/package-lock.json | 94 ++++++++++++++++++++++++++++++++++++++++ task-2/package.json | 10 ++++- task-2/question.js | 13 ++++++ task-2/quiz.js | 32 ++++++++++++++ 6 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 task-2/answer.js create mode 100644 task-2/package-lock.json create mode 100644 task-2/question.js create mode 100644 task-2/quiz.js diff --git a/task-2/answer.js b/task-2/answer.js new file mode 100644 index 0000000..7b1f725 --- /dev/null +++ b/task-2/answer.js @@ -0,0 +1,7 @@ +export class Answer { + constructor(answerText, is_Correct) { + this.answerText = answerText; + this.isCorrect = is_Correct; + } + +} diff --git a/task-2/index.js b/task-2/index.js index 02acffa..e41b8b9 100644 --- a/task-2/index.js +++ b/task-2/index.js @@ -1 +1,71 @@ -// Write your code here. You may create as many files as you like. +import { fetchQuestions } from "./quiz.js"; +import promptSync from "prompt-sync"; +import chalk from "chalk"; +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +console.log(chalk.magenta("Welcome to the Quiz!")); +const prompt = promptSync(); +const category = prompt("Select category to start the quiz: "); + +if (!category) { + console.log( + chalk.red( + "Category cannot be empty. Please restart the quiz and enter a valid category.", + ), + ); + process.exit(1); +} +const questions = await fetchQuestions(category); + +let score = 0; + +for (let i = 0; i < questions.length; i++) { + const question = questions[i]; + + try { + console.log(chalk.magenta(`Loading question ${i + 1}...\n`)); + await sleep(1000); + console.log(); + console.log(chalk.yellow(question.questionText)); + if (question.answers.length > 0) { + question.answers.forEach((answer, index) => { + console.log(`${index + 1}- ${answer.answerText}`); + }); + } + + const userAnswer = prompt("Your answer is :"); + if ( + userAnswer.toLowerCase() === "exit" || + userAnswer.toLowerCase() === "q" + ) { + console.log( + chalk.blue( + `Quiz exited! Your final score is: ${score}/${questions.length}`, + ), + ); + process.exit(0); + } + + if (question.isCorrectAnswer(parseInt(userAnswer))) { + console.log(chalk.green("Correct!")); + score++; + } else { + console.log( + chalk.red( + `Wrong! The correct answer is: ${question.answers.findIndex((answer) => answer.isCorrect) + 1} `, + ), + ); + } + } catch (error) { + console.log(chalk.red.bold(`Error: ${error.message}`)); + i--; + } +} + +console.log( + chalk.blue( + `Quiz completed! Your final score is: ${score}/${questions.length}`, + ), +); diff --git a/task-2/package-lock.json b/task-2/package-lock.json new file mode 100644 index 0000000..e95b834 --- /dev/null +++ b/task-2/package-lock.json @@ -0,0 +1,94 @@ +{ + "name": "task-2", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "task-2", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "chalk": "^5.6.2", + "dotenv": "^17.3.1", + "openai": "^6.32.0", + "prompt-sync": "^4.2.0" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/openai": { + "version": "6.32.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.32.0.tgz", + "integrity": "sha512-j3k+BjydAf8yQlcOI7WUQMQTbbF5GEIMAE2iZYCOzwwB3S2pCheaWYp+XZRNAch4jWVc52PMDGRRjutao3lLCg==", + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "license": "MIT", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/task-2/package.json b/task-2/package.json index a992f45..9610386 100644 --- a/task-2/package.json +++ b/task-2/package.json @@ -9,5 +9,11 @@ "keywords": [], "author": "", "license": "ISC", - "type": "module" -} \ No newline at end of file + "type": "module", + "dependencies": { + "chalk": "^5.6.2", + "dotenv": "^17.3.1", + "openai": "^6.32.0", + "prompt-sync": "^4.2.0" + } +} diff --git a/task-2/question.js b/task-2/question.js new file mode 100644 index 0000000..9b22a8e --- /dev/null +++ b/task-2/question.js @@ -0,0 +1,13 @@ +export class Question { + constructor(questionText, answers) { + this.questionText = questionText; + this.answers = answers; + } + + isCorrectAnswer(answerIndex) { + if (!answerIndex || answerIndex < 1 || answerIndex > 4) { + throw new Error("Answer index must be between 1 and 4"); + } + return this.answers[answerIndex - 1].isCorrect; + } +} diff --git a/task-2/quiz.js b/task-2/quiz.js new file mode 100644 index 0000000..712a895 --- /dev/null +++ b/task-2/quiz.js @@ -0,0 +1,32 @@ +import OpenAI from "openai"; +import { Question } from "./question.js"; +import { Answer } from "./answer.js"; +import dotenv from "dotenv"; +dotenv.config(); +const openai = new OpenAI({ + baseURL: "https://models.github.ai/inference/", + apiKey: `${process.env.OPENAI_API_KEY}`, +}); + +export async function fetchQuestions(category) { + const prompt = `Generate 10 questions , and each questions should include only 4 answer which is only one of them is correct in the ${category} category for each questions from start till end the difficulty of the questions should increase . Reply in a valid and parsable JSON with the following structure: + +{ "questions": "string", "answer_list": [{ "answer": "answer1":is_correct, true/false},{ "answer": "answer2":is_correct, true/false},{ "answer": "answer3":is_correct, true/false},...], } + +do not return anything else besides the JSON and make sure the JSON is valid and parsable dont be markdown.`; + const response = await openai.chat.completions.create({ + model: "openai/gpt-4o-mini", + messages: [{ role: "user", content: prompt }], + }); + + const responseContent = response.choices[0].message.content; + + const quiz = JSON.parse(responseContent); + const questions = quiz.questions.map((item) => { + const answers = item.answer_list.map( + (answerItem) => new Answer(answerItem.answer, answerItem.is_correct), + ); + return new Question(item.question, answers); + }); + return questions; +} From 1cdfdf7dfe5c0da2ce8cdbe59fb8109e969ea6d1 Mon Sep 17 00:00:00 2001 From: Shadi Date: Wed, 25 Mar 2026 19:38:18 +0100 Subject: [PATCH 3/4] Add .env to .gitignore to exclude environment variable files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2b76d7c..6f40893 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env # System files .DS_Store Thumbs.db From 2052ad964eb53ddf10fb7d5ea91231ef6ccb9a74 Mon Sep 17 00:00:00 2001 From: Shadi Date: Thu, 2 Apr 2026 15:52:06 +0200 Subject: [PATCH 4/4] Enhance error handling for question fetching and update JSON structure in fetchQuestions function --- task-2/index.js | 8 ++++++++ task-2/quiz.js | 9 +++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/task-2/index.js b/task-2/index.js index e41b8b9..5f8c96e 100644 --- a/task-2/index.js +++ b/task-2/index.js @@ -18,6 +18,14 @@ if (!category) { process.exit(1); } const questions = await fetchQuestions(category); +if (questions.length === 0) { + console.log( + chalk.red( + "Failed to fetch questions. Please check your API key and try again.", + ), + ); + process.exit(1); +} let score = 0; diff --git a/task-2/quiz.js b/task-2/quiz.js index 712a895..8d5a3a1 100644 --- a/task-2/quiz.js +++ b/task-2/quiz.js @@ -11,10 +11,11 @@ const openai = new OpenAI({ export async function fetchQuestions(category) { const prompt = `Generate 10 questions , and each questions should include only 4 answer which is only one of them is correct in the ${category} category for each questions from start till end the difficulty of the questions should increase . Reply in a valid and parsable JSON with the following structure: -{ "questions": "string", "answer_list": [{ "answer": "answer1":is_correct, true/false},{ "answer": "answer2":is_correct, true/false},{ "answer": "answer3":is_correct, true/false},...], } +[{ "questions": "string", "answer_list": [{ "answer": "answer1":is_correct, true/false},{ "answer": "answer2":is_correct, true/false},{ "answer": "answer3":is_correct, true/false},...], }] do not return anything else besides the JSON and make sure the JSON is valid and parsable dont be markdown.`; - const response = await openai.chat.completions.create({ + try { + const response = await openai.chat.completions.create({ model: "openai/gpt-4o-mini", messages: [{ role: "user", content: prompt }], }); @@ -29,4 +30,8 @@ do not return anything else besides the JSON and make sure the JSON is valid an return new Question(item.question, answers); }); return questions; + } catch (error) { + console.error("Error fetching questions:", error); + return []; + } }