From faed834f22a1dbb036898193ed9a356ef272b3a9 Mon Sep 17 00:00:00 2001 From: Yusup Rozimemet Date: Wed, 1 Apr 2026 20:49:25 +0200 Subject: [PATCH 1/2] update --- task-1/books_library.db | Bin 24576 -> 24576 bytes task-1/queries.sql | 49 +++-- task-2/data/flashcards.db | Bin 0 -> 16384 bytes task-2/package-lock.json | 389 ++++++++++++++++++++++++++++++++++++++ task-2/package.json | 2 +- task-2/setup.sql | 18 ++ task-2/src/database.js | 50 +++-- task-2/src/migrate.js | 42 ++++ 8 files changed, 518 insertions(+), 32 deletions(-) create mode 100644 task-2/data/flashcards.db create mode 100644 task-2/package-lock.json create mode 100644 task-2/setup.sql create mode 100644 task-2/src/migrate.js diff --git a/task-1/books_library.db b/task-1/books_library.db index 88b0418c6cf5291e897c14742bfcd1b1868c352a..e6768383b02850a725616b0af941cec4bf84ac32 100644 GIT binary patch delta 315 zcmZoTz}Rqrae_1>$3z)tRt^TeE}@Mn3;3BOd6_4(3m7u-GH+}=$s;K)$;KcmEX(AT zpONR1pX!*KT9lcbn8$u>vyALDA-+^@MrLWo#L|+C{GwvXO$Pt@IgEH27#NH=*fz5} zSTVBe#;`GXGD%MMa8%^+$S*3&FJfh7U|^fNxzw?rFT;p~i;?dm-+q33z8QQ?d})04 ze0;nQdAIVm@&@zD@;u|&$Wz4=&SS_U$o-!CDEA!hGVUO5J+9wed$~Hfvbh|&l(^V9 z-*E2YoXlCs>B1?@@qyzg$2yLBju;Me4le%7{2Tcv@~864ZET#!KKYD8DYA3eAkNub z;3&j4dAWPMqI@bFgEo^iqi>~xV{T$qW?s62Q+|Fn`wO?s1LKeG`aEe|CmWCj2u0xbbTY-M3Km>>Y5g-CY zfCvx)B0vO)01>#C1m0w_g|)Rc_s1}0{g{VLMcQ6+&92`Fd1{b1W~HD0#^crv#T@$md)Nz9_%ytZ}N_4jrcjb7$dp~@ueI@X)kaIqHwj)w_2zcF{^uOUsn_nGaB2$&b&}UkN8nQWO{qx=6pp6Zi zz24SD#F3B#xa55pvjNxDOwQS?^}ASgeWXNcnneu%A27`Ue?}<5^`@J1 z9@qBSl=bXVAGWNUW}-MOm7<7{k5g^d{~7O3$NLK}bPxd|Km>>Y5g-CYfCvx)B0vO) z01+Sp?>T{DCcEa^ERAx}#YdZ2P%!KN)zZ4-{pyXq=jFf4zm$h%DE(IYskHv#AMg2) zNj?!E0z`la5CI}U1c(3;AOin?0{i8>^Y~)LJsRSkW}$&$%yl#pX}tkjvauc}DI{kw zOC}1!A(I$n1sGO!inyu*lM#%V8VH$f7W2**-(9&(Bt4K_r zLzL8;inG)f_2VQw-;DW`$JQV95{HLOVj<7< zVz6T+P{$0;`OHvZGQlA#uxaU;Rw3^^<;6KlEA40(^W%-MmWWdfX@g2GH3*5CO$d5s z;@U_O1KkF@j4(`{$jojY)rHIImUaf9x``Cn8+Q&MEO92#KW<3}&cdOwKQ}*DwEaQe zsU6)~>iKQI8NkFi#Xj{ePcaTYUBt%WOeG_fauX-Nwvxkfg)2onMeMjvYqi>`1wJ%+ z|JJ`oQ(+32=+7|ae+C;<7Ng2WXB*Plm{*=;aiZ0Wb1P<|;h>dbvwA~<{miH-7TV4> z+g0yp^3IooJAM(8PETRPnXGT~FlNer?I}~jY`Bk~lW8pmxc_0x#W9>L Zd51+G1ZuVbL`&1e;M6#yZ@A3$`~z*G=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "license": "MIT" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.89.0", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + } + } +} diff --git a/task-2/package.json b/task-2/package.json index 8fb2060..20ca4de 100644 --- a/task-2/package.json +++ b/task-2/package.json @@ -8,6 +8,6 @@ "start": "node app.js" }, "dependencies": { - "better-sqlite3": "^12.8.0" + "better-sqlite3": "^11.8.1" } } diff --git a/task-2/setup.sql b/task-2/setup.sql new file mode 100644 index 0000000..8ad3409 --- /dev/null +++ b/task-2/setup.sql @@ -0,0 +1,18 @@ +-- Create the flashcards database schema + +-- Decks table +CREATE TABLE IF NOT EXISTS decks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + description TEXT +); + +-- Cards table +CREATE TABLE IF NOT EXISTS cards ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + question TEXT NOT NULL, + answer TEXT NOT NULL, + learned INTEGER NOT NULL DEFAULT 0, + deck_id INTEGER NOT NULL, + FOREIGN KEY (deck_id) REFERENCES decks(id) +); diff --git a/task-2/src/database.js b/task-2/src/database.js index a737fcb..efc2fb8 100644 --- a/task-2/src/database.js +++ b/task-2/src/database.js @@ -20,24 +20,30 @@ const db = new Database(DB_FILE); // ---------------------------------------------------------------- export function getAllDecks() { - // TODO: return all rows from the decks table - throw new Error('Not implemented'); + return db.prepare('SELECT * FROM decks').all(); } export function getDeckById(id) { - // TODO: return the deck row with the given id, or null if not found - throw new Error('Not implemented'); + const aSingleDeck = db.prepare('SELECT * FROM decks WHERE id = ?').get(id); + if (aSingleDeck) { + return aSingleDeck; + } else { + return null; + } } export function addDeck(name, description) { - // TODO: insert a new deck and return the new row (including its id) - throw new Error('Not implemented'); + const insertResult = db.prepare('INSERT INTO decks (name, description) VALUES (?, ?)').run(name, description); + return { id: insertResult.lastInsertRowid, name, description }; } export function deleteDeck(deckId) { - // TODO: delete the deck with the given id - // return true if a row was deleted, false otherwise - throw new Error('Not implemented'); + const deleteResult = db.prepare('DELETE FROM decks WHERE id = ?').run(deckId); + if (deleteResult.changes > 0) { + return true; + } else { + return false; + } } // ---------------------------------------------------------------- @@ -45,23 +51,29 @@ export function deleteDeck(deckId) { // ---------------------------------------------------------------- export function getAllCardsForDeck(deckId) { - // TODO: return all card rows whose deckId matches - throw new Error('Not implemented'); + return db.prepare('SELECT id, question, answer, learned, deck_id AS deckId FROM cards WHERE deck_id = ?').all(deckId); } export function addCard(question, answer, deckId) { - // TODO: insert a new card and return the new row (including its id) - throw new Error('Not implemented'); + const insertResult = db.prepare('INSERT INTO cards (question, answer, deck_id) VALUES (?, ?, ?)').run(question, answer, deckId); + return { id: insertResult.lastInsertRowid, question, answer, learned: false, deckId }; } export function markCardLearned(cardId) { - // TODO: set learned = 1 for the card with the given id - // return the updated row, or null if not found - throw new Error('Not implemented'); + db.prepare('UPDATE cards SET learned = 1 WHERE id = ?').run(cardId); + const aSingleCard = db.prepare('SELECT id, question, answer, learned, deck_id AS deckId FROM cards WHERE id = ?').get(cardId); + if (aSingleCard) { + return aSingleCard; + } else { + return null; + } } export function deleteCard(cardId) { - // TODO: delete the card with the given id - // return true if a row was deleted, false otherwise - throw new Error('Not implemented'); + const deleteResult = db.prepare('DELETE FROM cards WHERE id = ?').run(cardId); + if (deleteResult.changes > 0) { + return true; + } else { + return false; + } } diff --git a/task-2/src/migrate.js b/task-2/src/migrate.js new file mode 100644 index 0000000..934b608 --- /dev/null +++ b/task-2/src/migrate.js @@ -0,0 +1,42 @@ +import Database from 'better-sqlite3'; +import { readFileSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const DB_FILE = join(__dirname, '../data/flashcards.db'); +const DATA_FILE = join(__dirname, '../data/data.json'); + +// Read the JSON file +const rawJsonData = readFileSync(DATA_FILE, 'utf-8'); +const parsedJsonData = JSON.parse(rawJsonData); + +// Open the database +const db = new Database(DB_FILE); + +// Prepare the SQL statements +const insertDeckStatement = db.prepare('INSERT INTO decks (id, name, description) VALUES (?, ?, ?)'); +const insertCardStatement = db.prepare('INSERT INTO cards (id, question, answer, learned, deck_id) VALUES (?, ?, ?, ?, ?)'); + +// Insert all decks from JSON into database +const allDecksFromJson = parsedJsonData.decks; +for (const eachDeck of allDecksFromJson) { + insertDeckStatement.run(eachDeck.id, eachDeck.name, eachDeck.description); +} + +// Insert all cards from JSON into database +const allCardsFromJson = parsedJsonData.cards; +for (const eachCard of allCardsFromJson) { + let learnedValue; + if (eachCard.learned === true) { + learnedValue = 1; + } else { + learnedValue = 0; + } + insertCardStatement.run(eachCard.id, eachCard.question, eachCard.answer, learnedValue, eachCard.deckId); +} + +console.log(`Migrated ${allDecksFromJson.length} decks and ${allCardsFromJson.length} cards`); + +// Close the database connection +db.close(); From 241ee9c6aa806737638b774a894af56468c35760 Mon Sep 17 00:00:00 2001 From: Yusup Rozimemet Date: Wed, 1 Apr 2026 20:50:15 +0200 Subject: [PATCH 2/2] update --- task-1/books_library.db | Bin 24576 -> 24576 bytes task-1/queries.sql | 12 ++++- task-2/data/data.json | 91 -------------------------------------- task-2/data/flashcards.db | Bin 16384 -> 16384 bytes task-2/src/storage.js | 11 +---- 5 files changed, 11 insertions(+), 103 deletions(-) delete mode 100644 task-2/data/data.json diff --git a/task-1/books_library.db b/task-1/books_library.db index e6768383b02850a725616b0af941cec4bf84ac32..40e6fe2188efaf0bde3e5888ddc5a9328c726034 100644 GIT binary patch delta 38 ucmZoTz}Rqrae_1>=R_H2M$U~13;kL6$3z)tMvjdM3;kIb7#NH;vj#}=v&g5iF-%s9zXt%(bP5*$ diff --git a/task-1/queries.sql b/task-1/queries.sql index 25ed6bc..77d187b 100644 --- a/task-1/queries.sql +++ b/task-1/queries.sql @@ -48,5 +48,13 @@ WHERE id = 101; -- ### Bonus questions (optional) -- **Bonus A** — How many books are there per genre? Show the genre name and the count, ordered from most to fewest books. - --- **Bonus B** — Find any authors in the database who have no books at all. (Hint: you will need a LEFT JOIN and check for NULL.) \ No newline at end of file +SELECT genre, COUNT(*) AS count +FROM books +GROUP BY genre +ORDER BY count DESC; + +-- **Bonus B** — Find any authors in the database who have no books at all. (Hint: you will need a LEFT JOIN and check for NULL.) +SELECT a.id, a.first_name, a.last_name +FROM authors a +LEFT JOIN books b ON b.author_id = a.id +WHERE b.id IS NULL; \ No newline at end of file diff --git a/task-2/data/data.json b/task-2/data/data.json deleted file mode 100644 index 42a5b06..0000000 --- a/task-2/data/data.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "decks": [ - { - "id": 1, - "name": "JavaScript Basics", - "description": "Core JavaScript concepts" - }, - { - "id": 2, - "name": "SQL Fundamentals", - "description": "Writing database queries" - }, - { - "id": 3, - "name": "HTML & CSS", - "description": "Building web pages" - } - ], - "cards": [ - { - "id": 1, - "question": "What is a variable?", - "answer": "A named container for storing data values", - "learned": true, - "deckId": 1 - }, - { - "id": 2, - "question": "What does `const` mean?", - "answer": "Declares a variable that cannot be reassigned", - "learned": false, - "deckId": 1 - }, - { - "id": 3, - "question": "What is an array?", - "answer": "An ordered list of values", - "learned": true, - "deckId": 1 - }, - { - "id": 4, - "question": "What does `===` do?", - "answer": "Checks for strict equality (value AND type)", - "learned": false, - "deckId": 1 - }, - { - "id": 5, - "question": "What is SELECT used for?", - "answer": "Retrieving data from a database table", - "learned": false, - "deckId": 2 - }, - { - "id": 6, - "question": "What is a PRIMARY KEY?", - "answer": "A unique identifier for each row in a table", - "learned": false, - "deckId": 2 - }, - { - "id": 7, - "question": "What does WHERE do?", - "answer": "Filters rows in a query based on a condition", - "learned": true, - "deckId": 2 - }, - { - "id": 8, - "question": "What is a FOREIGN KEY?", - "answer": "A column that links to the primary key of another table", - "learned": false, - "deckId": 2 - }, - { - "id": 9, - "question": "What does a
do?", - "answer": "Creates a block-level container element", - "learned": false, - "deckId": 3 - }, - { - "id": 10, - "question": "What is the CSS box model?", - "answer": "Every element is a box with content, padding, border, and margin", - "learned": false, - "deckId": 3 - } - ] -} \ No newline at end of file diff --git a/task-2/data/flashcards.db b/task-2/data/flashcards.db index d49b66ec405feb8c7d07483d7bdac173d7292135..8bd7991376c94cdbe50ef8c5974668e828e9025e 100644 GIT binary patch delta 56 zcmZo@U~Fh$oFL68JyFJ)QF>#-FF8iG&C&8R1$cNl8JWcyQ&N+&i`h0AT;pNnoqXNK L6DX@_d!GpazS$4t delta 56 zcmZo@U~Fh$oFL68KT*b+QGR2>FF8i`&C&8R1$g*48JWcyQ&N+&i`h3BT;pNnn|$5I L6DX@_d!Gpaz@iWF diff --git a/task-2/src/storage.js b/task-2/src/storage.js index 4e59b53..81eddc9 100644 --- a/task-2/src/storage.js +++ b/task-2/src/storage.js @@ -10,16 +10,7 @@ import { dirname, join } from 'path'; const __dirname = dirname(fileURLToPath(import.meta.url)); const DATA_FILE = join(__dirname, '../data/data.json'); -// --- helpers (you will remove these when you switch to SQLite) --- -function readData() { - const raw = readFileSync(DATA_FILE, 'utf-8'); - return JSON.parse(raw); -} - -function writeData(data) { - writeFileSync(DATA_FILE, JSON.stringify(data, null, 2), 'utf-8'); -} // ---------------------------------------------------------------- // Decks @@ -87,4 +78,4 @@ export function deleteCard(cardId) { data.cards.splice(index, 1); writeData(data); return true; -} +} \ No newline at end of file