diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..df09c77
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "c55-core-week-12",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {}
+}
diff --git a/task-1/queries.sql b/task-1/queries.sql
index fecf7ad..ab57176 100644
--- a/task-1/queries.sql
+++ b/task-1/queries.sql
@@ -1,21 +1,50 @@
---- Queries
-- **Question 1** — List the title and published year of every book in the `'Science Fiction'` genre, ordered by published year (oldest first).
+SELECT title, published_year
+FROM books
+WHERE genre = 'Science Fiction'
+ORDER BY published_year ASC;
-- **Question 2** — Show every book published before 1950. Display the title and year only.
+SELECT title, published_year
+FROM books
+WHERE published_year < 1950;
-- **Question 3** — Show every book in the database along with its author's full name. Combine `first_name` and `last_name` into a single column called `author`. _(Hint: you will need a JOIN.)_
+SELECT books.title, authors.first_name || ' ' || authors.last_name AS author
+FROM books
+JOIN authors ON books.author_id = authors.id;
-- **Question 4** — List all books written by Stephen King. Show the title and published year, ordered by year. _(Hint: JOIN the two tables and filter on the author's name.)_
+SELECT books.title, books.published_year
+FROM books
+JOIN authors ON books.author_id = authors.id
+WHERE authors.first_name = 'Stephen'
+AND authors.last_name = 'King'
+ORDER BY books.published_year;
-- **Question 5** — Add yourself as a new author. Use your real name, or make one up. Pick any nationality and birth year.
+INSERT INTO authors (first_name, last_name, nationality, birth_year)
+VALUES ('Baraah', 'Alshiaani', 'Yemeni', 2000);
-- **Question 6** — Add one book for the author you just inserted. It can be a real book or a made-up one.
+INSERT INTO books (title, published_year, genre, author_id)
+VALUES ('BLife Book', 2026, 'Fiction', (SELECT id FROM authors WHERE first_name='Baraah' AND last_name='Alshiaani'));
-- **Question 7** — The genre for "The Dark Tower: The Gunslinger" was entered incorrectly as `'Fantasy'`. It should be `'Horror'`. Write an UPDATE to fix it, then verify the change with a SELECT.
+UPDATE books
+SET genre = 'Horror'
+WHERE title = 'The Dark Tower: The Gunslinger';
--- **Question 8** — Delete the book you added in Question 6. Make sure your query targets only that specific row.
+-- verify
+SELECT title, genre
+FROM books
+WHERE title = 'The Dark Tower: The Gunslinger';
+-- **Question 8** — Delete the book you added in Question 6. Make sure your query targets only that specific row.
+DELETE FROM books
+WHERE title = 'BLife Book';
---
-- ### Bonus questions _(optional)_
@@ -23,5 +52,13 @@
-- These cover topics slightly beyond the core material. Have a go if you finish early.
-- **Bonus A** — How many books are there per genre? Show the genre name and the count, ordered from most to fewest books.
+SELECT genre, COUNT(*) AS book_count
+FROM books
+GROUP BY genre
+ORDER BY book_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 authors.first_name, authors.last_name
+FROM authors
+LEFT JOIN books ON authors.id = books.author_id
+WHERE books.id IS NULL;
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
new file mode 100644
index 0000000..e69de29
diff --git a/task-2/flashcards.db b/task-2/flashcards.db
new file mode 100644
index 0000000..148e958
Binary files /dev/null and b/task-2/flashcards.db differ
diff --git a/task-2/migrate.js b/task-2/migrate.js
new file mode 100644
index 0000000..5c32543
--- /dev/null
+++ b/task-2/migrate.js
@@ -0,0 +1,29 @@
+import fs from 'fs';
+import Database from 'better-sqlite3';
+
+const db = new Database('flashcards.db');
+
+const data = JSON.parse(fs.readFileSync('data/data.json', 'utf-8'));
+
+const insertDeck = db.prepare(`
+ INSERT INTO decks (name, description)
+ VALUES (?, ?)
+`);
+
+const insertCard = db.prepare(`
+ INSERT INTO cards (question, answer, learned, deck_id)
+ VALUES (?, ?, ?, ?)
+`);
+
+data.decks.forEach(deck => {
+ insertDeck.run(deck.name, deck.description);
+});
+
+data.cards.forEach(card => {
+ insertCard.run(
+ card.question,
+ card.answer,
+ card.learned ? 1 : 0,
+ card.deckId
+ );
+});
\ No newline at end of file
diff --git a/task-2/package-lock.json b/task-2/package-lock.json
new file mode 100644
index 0000000..72fc35b
--- /dev/null
+++ b/task-2/package-lock.json
@@ -0,0 +1,467 @@
+{
+ "name": "flashcard-app",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "flashcard-app",
+ "version": "1.0.0",
+ "dependencies": {
+ "better-sqlite3": "^12.8.0"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "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/better-sqlite3": {
+ "version": "12.8.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.8.0.tgz",
+ "integrity": "sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ },
+ "engines": {
+ "node": "20.x || 22.x || 23.x || 24.x || 25.x"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "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": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "license": "ISC"
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "license": "(MIT OR WTFPL)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "license": "MIT"
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "license": "MIT"
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "license": "MIT"
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "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",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "license": "ISC"
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "license": "MIT"
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "license": "MIT"
+ },
+ "node_modules/node-abi": {
+ "version": "3.89.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz",
+ "integrity": "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+ "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
+ "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",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "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",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "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",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "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",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "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",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "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",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
+ "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",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "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",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ }
+ }
+}
diff --git a/task-2/setup.sql b/task-2/setup.sql
new file mode 100644
index 0000000..7cc423a
--- /dev/null
+++ b/task-2/setup.sql
@@ -0,0 +1,16 @@
+-- decks table
+CREATE TABLE decks (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ description TEXT
+);
+
+-- cards table
+CREATE TABLE 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)
+);
\ No newline at end of file
diff --git a/task-2/src/database.js b/task-2/src/database.js
index a737fcb..8718096 100644
--- a/task-2/src/database.js
+++ b/task-2/src/database.js
@@ -11,7 +11,7 @@ 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 DB_FILE = join(__dirname, '../flashcards.db');
const db = new Database(DB_FILE);
@@ -21,23 +21,29 @@ 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');
+return db.prepare('SELECT * FROM decks WHERE id = ?').get(id) ?? 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 result = db.prepare('INSERT INTO decks (name, description) VALUES (?, ?)').run(name, description);
+ return {
+ id: result.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 result = db.prepare('DELETE FROM decks WHERE id = ?').run(deckId);
+ return result.changes > 0;
}
// ----------------------------------------------------------------
@@ -46,22 +52,44 @@ 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 result = db.prepare(`
+ INSERT INTO cards (question, answer, deck_id)
+ VALUES (?, ?, ?)
+ `).run(question, answer, deckId);
+
+ return {
+ id: result.lastInsertRowid,
+ question,
+ answer,
+ deckId,
+ learned: 0
+ };
}
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);
+
+ return db.prepare(`
+ SELECT id, question, answer, learned, deck_id AS deckId
+ FROM cards
+ WHERE id = ?
+ `).get(cardId) ?? 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 result = db.prepare('DELETE FROM cards WHERE id = ?').run(cardId);
+ return result.changes > 0;
}
diff --git a/task-2/src/storage.js b/task-2/src/storage.js
deleted file mode 100644
index 4e59b53..0000000
--- a/task-2/src/storage.js
+++ /dev/null
@@ -1,90 +0,0 @@
-// storage.js
-// This file handles all reading and writing of data.
-// Currently it uses a JSON file on disk.
-// In the assignment you will replace each function here with a SQLite query.
-
-import { readFileSync, writeFileSync } from 'fs';
-import { fileURLToPath } from 'url';
-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
-// ----------------------------------------------------------------
-
-export function getAllDecks() {
- const data = readData();
- return data.decks;
-}
-
-export function getDeckById(id) {
- const data = readData();
- return data.decks.find(d => d.id === id) ?? null;
-}
-
-export function addDeck(name, description) {
- const data = readData();
- const newId = Math.max(0, ...data.decks.map(d => d.id)) + 1;
- const deck = { id: newId, name, description };
- data.decks.push(deck);
- writeData(data);
- return deck;
-}
-
-export function deleteDeck(deckId) {
- const data = readData();
- const index = data.decks.findIndex(d => d.id === deckId);
- if (index === -1) return false;
- data.decks.splice(index, 1);
- writeData(data);
- return true;
-}
-
-// ----------------------------------------------------------------
-// Cards
-// ----------------------------------------------------------------
-
-export function getAllCardsForDeck(deckId) {
- const data = readData();
- return data.cards.filter(c => c.deckId === deckId);
-}
-
-export function addCard(question, answer, deckId) {
- const data = readData();
- const newId = Math.max(0, ...data.cards.map(c => c.id)) + 1;
- const card = { id: newId, question, answer, learned: false, deckId };
- data.cards.push(card);
- writeData(data);
- return card;
-}
-
-export function markCardLearned(cardId) {
- const data = readData();
- const card = data.cards.find(c => c.id === cardId);
- if (!card) return null;
- card.learned = true;
- writeData(data);
- return card;
-}
-
-export function deleteCard(cardId) {
- const data = readData();
- const index = data.cards.findIndex(c => c.id === cardId);
- if (index === -1) return false;
- data.cards.splice(index, 1);
- writeData(data);
- return true;
-}