From cbcf35747c25429017c387cda1ec4cf6803e8d23 Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Sun, 25 May 2025 21:53:15 +0200 Subject: [PATCH 1/5] Add prep exercise --- Week2/prep_exercise/package-lock.json | 143 +++++++++++++++++ Week2/prep_exercise/package.json | 16 ++ Week2/prep_exercise/recipes_db.sql | 216 ++++++++++++++++++++++++++ Week2/prep_exercise/server.js | 63 ++++++++ 4 files changed, 438 insertions(+) create mode 100644 Week2/prep_exercise/package-lock.json create mode 100644 Week2/prep_exercise/package.json create mode 100644 Week2/prep_exercise/recipes_db.sql create mode 100644 Week2/prep_exercise/server.js diff --git a/Week2/prep_exercise/package-lock.json b/Week2/prep_exercise/package-lock.json new file mode 100644 index 000000000..80d3d7bdb --- /dev/null +++ b/Week2/prep_exercise/package-lock.json @@ -0,0 +1,143 @@ +{ + "name": "prep_exercise", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "prep_exercise", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "mysql2": "^3.14.1" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/mysql2": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.1.tgz", + "integrity": "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + } + } +} diff --git a/Week2/prep_exercise/package.json b/Week2/prep_exercise/package.json new file mode 100644 index 000000000..012be0fed --- /dev/null +++ b/Week2/prep_exercise/package.json @@ -0,0 +1,16 @@ +{ + "name": "prep_exercise", + "version": "1.0.0", + "main": "server.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "mysql2": "^3.14.1" + } +} diff --git a/Week2/prep_exercise/recipes_db.sql b/Week2/prep_exercise/recipes_db.sql new file mode 100644 index 000000000..b020fc876 --- /dev/null +++ b/Week2/prep_exercise/recipes_db.sql @@ -0,0 +1,216 @@ +drop database if exists recipes_db; +create database recipes_db; +use recipes_db; + +create table recipes( +recipeid int auto_increment primary key, +name varchar (100) + +); + + +create table ingredients( +ingredientID int auto_increment primary key, +name varchar (100) +); + +create table categories( +categoryid int auto_increment primary key, +name varchar (100) + +); + + + +create table steps( +stepID int auto_increment primary key, +name varchar (100) + +); +-- those are the junction tables + + +create table recipe_ingredients( +recipeID int ,ingredientID int ,primary key (recipeID, ingredientID), +foreign key (recipeID) references recipes (recipeID) on delete cascade, +foreign key (ingredientID) references ingredients (ingredientID) on delete cascade + +); + + +create table recipe_categories( +recipeID int ,categoryID int, primary key (recipeID,categoryID), +foreign key(recipeID) references recipes (recipeID) on delete cascade, +foreign key(categoryid) references categories(categoryid) on delete cascade + +); + + create table recipe_steps( + recipeid int , stepID int , primary key (recipeid,stepID), + foreign key(recipeid) references recipes (recipeid) on delete cascade, + foreign key (stepID) references steps (stepID) on delete cascade + ); + +-- -- -- inserting + +use recipes_db; +SET SQL_SAFE_UPDATES = 0; +delete from recipes; +delete from categories; +delete from ingredients; +delete from recipe_categories; +delete from recipe_ingredients; +delete from recipe_steps; +delete from steps; +ALTER TABLE recipes AUTO_INCREMENT = 1; +ALTER TABLE ingredients AUTO_INCREMENT = 1; +ALTER TABLE categories AUTO_INCREMENT = 1; +ALTER TABLE steps AUTO_INCREMENT = 1; + + + + +insert into recipes (name) values +('cheesecake'),('roasted brussels sprouts'), +('mac&cheese'),('Tamagoyaki Japanese Omelette'); + +insert into categories (name) values +('cake'), +('no-bake'), +('vegetarian'), +('Vegan'), +('Gluten-Free'), +('Japanese'); + + +insert into ingredients (name) values +('condensed milk'), +('cream cheese'), +('lemon juice'), +('pie crust'), +('cherry jam'), +('Brussels Sprouts'), +('Sesame seeds'), +('Pepper'), +('Salt'), +('Olive oil'), +('Macaroni'), +('Butter'), +('Flour'), +('Shredded Cheddar cheese'), +('Eggs'), +('Soy sauce'), +('Sugar'), +('potatoes'); + +insert into steps (name) values +('beat cream cheese'), +('add condensed milk and blend'), +('add lemon juice and blend'), +('add the mix to pe crust'), +('spread the cherry jam'), +('place in refrigerator for 3h'), +('Preheat the oven'), +('Mix the ingredients in a bowl'), +('Spread the mix on baking sheet'), +('Bake for 30'), +('Cook Macaroni for 8'), +('Melt butter in a saucepan'), +('Add flour, salt, pepper and mix'), +('Add Milk and mix'), +('Cook until mix is smooth'), +('Add cheddar cheese'), +('Add the macaroni'), +('Beat the eggs'), +('Add soy sauce, sugar and salt'), +('Add oil to a sauce pan'), +('Bring to medium heat'), +('Add some mix to the sauce pan'), +('Let it cook for 1'), +('Remove pan from fire'), +('Add oil to a saucepan again'), +('Add some mix to the saucepan again'), +('Let it cook for 1 minute again'); + +-- insert into the junctions tables. + +insert into recipe_categories (recipeid,categoryid) +values +(1, 1), -- No-Bake Cheesecake: Cake +(1, 2), -- No-Bake Cheesecake: No-Bake +(1, 3), -- No-Bake Cheesecake: Vegetarian + +(2, 4), -- Roasted Brussels Sprouts: Vegan +(2, 5), -- Roasted Brussels Sprouts: Gluten-Free + +(3, 3), -- Mac & Cheese: Vegetarian + +(4, 3), -- Tamagoyaki Japanese Omelette: Vegetarian +(4, 6); -- Tamagoyaki Japanese Omelette: Japanese + + +insert into recipe_ingredients (recipeid,ingredientid) +values +(1, 1), -- No-Bake Cheesecake: Condensed Milk +(1, 2), -- No-Bake Cheesecake: Cream Cheese +(1, 3), -- No-Bake Cheesecake: Lemon Juice +(1, 4), -- No-Bake Cheesecake: Pie Crust +(1, 5), -- No-Bake Cheesecake: Cherry Jam + +(2, 6), -- Roasted Brussels Sprouts: Brussels Sprouts +(2, 3), -- Roasted Brussels Sprouts: Lemon Juice +(2, 7), -- Roasted Brussels Sprouts: Sesame Seeds +(2, 8), -- Roasted Brussels Sprouts: Pepper +(2, 9), -- Roasted Brussels Sprouts: Salt +(2, 10), -- Roasted Brussels Sprouts: Olive Oil + +(3, 11), -- Mac & Cheese: Macaroni +(3, 12), -- Mac & Cheese: Butter +(3, 13), -- Mac & Cheese: Flour +(3, 9), -- Mac & Cheese: Salt +(3, 8), -- Mac & Cheese: Pepper +(3, 14), -- Mac & Cheese: Milk +(3, 15), -- Mac & Cheese: Shredded Cheddar Cheese +(3,18), + + +(4, 15), -- Tamagoyaki Japanese Omelette: Eggs +(4, 16), -- Tamagoyaki Japanese Omelette: Soy Sauce +(4,17), -- Tamagoyaki Japanese Omelette: Sugr +(4, 9), -- Tamagoyaki Japanese Omelette: Salt +(4, 10); -- Tamagoyaki Japanese Omelette: Olive Oil + + +insert into recipe_steps (recipeid,stepID) +values +(1, 1), -- No-Bake Cheesecake: Beat Cream Cheese +(1, 2), -- No-Bake Cheesecake: Add Condensed Milk and Blend +(1, 3), -- No-Bake Cheesecake: Add Lemon Juice and Blend +(1, 4), -- No-Bake Cheesecake: Add the Mix to the Pie Crust +(1, 5), -- No-Bake Cheesecake: Spread the Cherry Jam +(1, 6), -- No-Bake Cheesecake: Place in Refrigerator for 3h + +(2, 7), -- Roasted Brussels Sprouts: Preheat the Oven +(2, 8), -- Roasted Brussels Sprouts: Mix the Ingredients in a Bowl +(2, 9), -- Roasted Brussels Sprouts: Spread the Mix on Baking Sheet +(2, 10), -- Roasted Brussels Sprouts: Bake for 30 minutes + +(3, 11), -- Mac & Cheese: Cook Macaroni for 8 minutes +(3, 12), -- Mac & Cheese: Melt Butter in a Saucepan +(3, 13), -- Mac & Cheese: Add Flour, Salt, Pepper, and Mix +(3, 14), -- Mac & Cheese: Add Milk and Mix +(3, 15), -- Mac & Cheese: Cook Until Mix is Smooth +(3, 16), -- Mac & Cheese: Add Cheddar Cheese +(3, 17), -- Mac & Cheese: Add the Macaroni + +(4, 18), -- Tamagoyaki Japanese Omelette: Beat the Eggs +(4, 19), -- Tamagoyaki Japanese Omelette: Add Soy Sauce, Sugar, and Salt +(4, 20), -- Tamagoyaki Japanese Omelette: Add Oil to a Saucepan +(4, 21), -- Tamagoyaki Japanese Omelette: Bring to Medium Heat +(4, 22), -- Tamagoyaki Japanese Omelette: Add Some Mix to the Saucepan +(4, 23), -- Tamagoyaki Japanese Omelette: Let It Cook for 1 minute +(4, 24), -- Tamagoyaki Japanese Omelette: Add Oil to a Saucepan Again +(4, 25), -- Tamagoyaki Japanese Omelette: Add Some Mix to the Saucepan Again +(4, 26), -- Tamagoyaki Japanese Omelette: Let It Cook for 1 minute Again +(4, 27); -- Tamagoyaki Japanese Omelette: Remove Pan from Fire + diff --git a/Week2/prep_exercise/server.js b/Week2/prep_exercise/server.js new file mode 100644 index 000000000..110c5e0a1 --- /dev/null +++ b/Week2/prep_exercise/server.js @@ -0,0 +1,63 @@ +import mysql from "mysql2"; + +const connection = mysql.createConnection({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "recipes_db", +}); + +connection.connect((err) => { + if (err) throw err; + console.log("Connected to MySQL database"); + + runQueries(connection); +}); + +// Function to run all queries + +const runQueries = (connection) => { + const vegetarianWithPotatoes = ` + SELECT recipes.name AS recipe + FROM recipes + JOIN recipe_ingredients ON recipes.recipeid = recipe_ingredients.recipeid + JOIN ingredients ON recipe_ingredients.ingredientid = ingredients.ingredientid + JOIN recipe_categories ON recipes.recipeid = recipe_categories.recipeid + JOIN categories ON recipe_categories.categoryid = categories.categoryid + WHERE categories.name = 'vegetarian' AND ingredients.name = 'potatoes'; + `; + + connection.query(vegetarianWithPotatoes, (err, results) => { + if (err) throw err; + console.log("Vegetarian Recipes with Potatoes:", results); + }); + + const cakesNoBaking = ` + SELECT recipes.name AS recipe + FROM recipes + JOIN recipe_categories AS rc1 ON recipes.recipeid = rc1.recipeid + JOIN categories AS c1 ON rc1.categoryid = c1.categoryid + JOIN recipe_categories AS rc2 ON recipes.recipeid = rc2.recipeid + JOIN categories AS c2 ON rc2.categoryid = c2.categoryid + WHERE c1.name = 'Cake' + AND c2.name = 'No-Bake'; + `; + + connection.query(cakesNoBaking, (err, results) => { + if (err) throw err; + console.log("Cakes that do not need baking:", results); + }); + + const veganAndJapanese = ` + SELECT recipes.name as recipe, categories.name as category + FROM recipes + JOIN recipe_categories ON recipes.recipeid = recipe_categories.recipeid + JOIN categories ON recipe_categories.categoryid = categories.categoryid + WHERE categories.name IN ('Vegan', 'Japanese'); + `; + + connection.query(veganAndJapanese, (err, results) => { + if (err) throw err; + console.log("Vegan and Japanese Recipes:", results); + }); +}; From d16d2d61aaf5e7f1bc715c73ff4300600f1f367b Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Sun, 1 Jun 2025 22:50:58 +0200 Subject: [PATCH 2/5] w3-prep-exercise --- Week3/prep-exercise/readme.md | 25 ++++ Week3/prep-exercise/schema.sql | 233 +++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 Week3/prep-exercise/readme.md create mode 100644 Week3/prep-exercise/schema.sql diff --git a/Week3/prep-exercise/readme.md b/Week3/prep-exercise/readme.md new file mode 100644 index 000000000..a4f5a2d9d --- /dev/null +++ b/Week3/prep-exercise/readme.md @@ -0,0 +1,25 @@ +# Week 3 Prep – Normalization of Food Recipes Database + +## Was your database already in 2NF / 3NF? + +Yes. My database was already designed to follow 1NF, 2NF, and 3NF principles. + +1 Each table contains atomic values (1NF) +2 No column depends on only part of a composite key (2NF) +3 No column depends on another non-key column (3NF) + +--- + +## What changes did you make to normalize your database? + +In the unnormalized version: +1 Recipes had repeating product columns like `ingredient1`, `ingredient2` +2 Steps were stored across multiple columns +3 Categories were duplicated directly in the recipes table + +After normalization: +1 I created separate tables for `ingredients`, `steps`, and `categories` +2 I used junction tables: `recipe_ingredients`, `recipe_categories`, and `recipe_steps` +3 Each relationship is handled cleanly through foreign keys + +See `schema.sql` for the full structure. diff --git a/Week3/prep-exercise/schema.sql b/Week3/prep-exercise/schema.sql new file mode 100644 index 000000000..27e3a298d --- /dev/null +++ b/Week3/prep-exercise/schema.sql @@ -0,0 +1,233 @@ +drop database if exists recipes_db; +create database recipes_db; +use recipes_db; + +create table recipes( +recipeid int auto_increment primary key, +name varchar (100) + +); + + +create table ingredients( +ingredientID int auto_increment primary key, +name varchar (100) +); + +create table categories( +categoryid int auto_increment primary key, +name varchar (100) + +); + + + +create table steps( +stepID int auto_increment primary key, +name varchar (100) + +); +-- those are the junction tables + + +create table recipe_ingredients( +recipeID int ,ingredientID int ,primary key (recipeID, ingredientID), +foreign key (recipeID) references recipes (recipeID) on delete cascade, +foreign key (ingredientID) references ingredients (ingredientID) on delete cascade + +); + + +create table recipe_categories( +recipeID int ,categoryID int, primary key (recipeID,categoryID), +foreign key(recipeID) references recipes (recipeID) on delete cascade, +foreign key(categoryid) references categories(categoryid) on delete cascade + +); + + create table recipe_steps( + recipeid int , stepID int , primary key (recipeid,stepID), + foreign key(recipeid) references recipes (recipeid) on delete cascade, + foreign key (stepID) references steps (stepID) on delete cascade + ); + + + +-- +/* +use recipes_db; +SET SQL_SAFE_UPDATES = 0; +delete from recipes; +delete from categories; +delete from ingredients; +delete from recipe_categories; +delete from recipe_ingredients; +delete from recipe_steps; +delete from steps; +ALTER TABLE recipes AUTO_INCREMENT = 1; +ALTER TABLE ingredients AUTO_INCREMENT = 1; +ALTER TABLE categories AUTO_INCREMENT = 1; +ALTER TABLE steps AUTO_INCREMENT = 1; + + + + +insert into recipes (name) values +('cheesecake'),('roasted brussels sprouts'), +('mac&cheese'),('Tamagoyaki Japanese Omelette'); + +insert into categories (name) values +('cake'), +('no-bake'), +('vegetarian'), +('Vegan'), +('Gluten-Free'), +('Japanese'); + + +insert into ingredients (name) values +('condensed milk'), +('cream cheese'), +('lemon juice'), +('pie crust'), +('cherry jam'), +('Brussels Sprouts'), +('Sesame seeds'), +('Pepper'), +('Salt'), +('Olive oil'), +('Macaroni'), +('Butter'), +('Flour'), +('Shredded Cheddar cheese'), +('Eggs'), +('Soy sauce'), +('Sugar'), +('potatoes'); + +insert into steps (name) values +('beat cream cheese'), +('add condensed milk and blend'), +('add lemon juice and blend'), +('add the mix to pe crust'), +('spread the cherry jam'), +('place in refrigerator for 3h'), +('Preheat the oven'), +('Mix the ingredients in a bowl'), +('Spread the mix on baking sheet'), +('Bake for 30'), +('Cook Macaroni for 8'), +('Melt butter in a saucepan'), +('Add flour, salt, pepper and mix'), +('Add Milk and mix'), +('Cook until mix is smooth'), +('Add cheddar cheese'), +('Add the macaroni'), +('Beat the eggs'), +('Add soy sauce, sugar and salt'), +('Add oil to a sauce pan'), +('Bring to medium heat'), +('Add some mix to the sauce pan'), +('Let it cook for 1'), +('Remove pan from fire'), +('Add oil to a saucepan again'), +('Add some mix to the saucepan again'), +('Let it cook for 1 minute again'); + +-- insert into the junctions tables. + +insert into recipe_categories (recipeid,categoryid) +values +(1, 1), -- No-Bake Cheesecake: Cake +(1, 2), -- No-Bake Cheesecake: No-Bake +(1, 3), -- No-Bake Cheesecake: Vegetarian + +(2, 4), -- Roasted Brussels Sprouts: Vegan +(2, 5), -- Roasted Brussels Sprouts: Gluten-Free + +(3, 3), -- Mac & Cheese: Vegetarian + +(4, 3), -- Tamagoyaki Japanese Omelette: Vegetarian +(4, 6); -- Tamagoyaki Japanese Omelette: Japanese + + +insert into recipe_ingredients (recipeid,ingredientid) +values +(1, 1), -- No-Bake Cheesecake: Condensed Milk +(1, 2), -- No-Bake Cheesecake: Cream Cheese +(1, 3), -- No-Bake Cheesecake: Lemon Juice +(1, 4), -- No-Bake Cheesecake: Pie Crust +(1, 5), -- No-Bake Cheesecake: Cherry Jam + +(2, 6), -- Roasted Brussels Sprouts: Brussels Sprouts +(2, 3), -- Roasted Brussels Sprouts: Lemon Juice +(2, 7), -- Roasted Brussels Sprouts: Sesame Seeds +(2, 8), -- Roasted Brussels Sprouts: Pepper +(2, 9), -- Roasted Brussels Sprouts: Salt +(2, 10), -- Roasted Brussels Sprouts: Olive Oil + +(3, 11), -- Mac & Cheese: Macaroni +(3, 12), -- Mac & Cheese: Butter +(3, 13), -- Mac & Cheese: Flour +(3, 9), -- Mac & Cheese: Salt +(3, 8), -- Mac & Cheese: Pepper +(3, 14), -- Mac & Cheese: Milk +(3, 15), -- Mac & Cheese: Shredded Cheddar Cheese +(3,18), + + +(4, 15), -- Tamagoyaki Japanese Omelette: Eggs +(4, 16), -- Tamagoyaki Japanese Omelette: Soy Sauce +(4,17), -- Tamagoyaki Japanese Omelette: Sugr +(4, 9), -- Tamagoyaki Japanese Omelette: Salt +(4, 10); -- Tamagoyaki Japanese Omelette: Olive Oil + + +insert into recipe_steps (recipeid,stepID) +values +(1, 1), -- No-Bake Cheesecake: Beat Cream Cheese +(1, 2), -- No-Bake Cheesecake: Add Condensed Milk and Blend +(1, 3), -- No-Bake Cheesecake: Add Lemon Juice and Blend +(1, 4), -- No-Bake Cheesecake: Add the Mix to the Pie Crust +(1, 5), -- No-Bake Cheesecake: Spread the Cherry Jam +(1, 6), -- No-Bake Cheesecake: Place in Refrigerator for 3h + +(2, 7), -- Roasted Brussels Sprouts: Preheat the Oven +(2, 8), -- Roasted Brussels Sprouts: Mix the Ingredients in a Bowl +(2, 9), -- Roasted Brussels Sprouts: Spread the Mix on Baking Sheet +(2, 10), -- Roasted Brussels Sprouts: Bake for 30 minutes + +(3, 11), -- Mac & Cheese: Cook Macaroni for 8 minutes +(3, 12), -- Mac & Cheese: Melt Butter in a Saucepan +(3, 13), -- Mac & Cheese: Add Flour, Salt, Pepper, and Mix +(3, 14), -- Mac & Cheese: Add Milk and Mix +(3, 15), -- Mac & Cheese: Cook Until Mix is Smooth +(3, 16), -- Mac & Cheese: Add Cheddar Cheese +(3, 17), -- Mac & Cheese: Add the Macaroni + +(4, 18), -- Tamagoyaki Japanese Omelette: Beat the Eggs +(4, 19), -- Tamagoyaki Japanese Omelette: Add Soy Sauce, Sugar, and Salt +(4, 20), -- Tamagoyaki Japanese Omelette: Add Oil to a Saucepan +(4, 21), -- Tamagoyaki Japanese Omelette: Bring to Medium Heat +(4, 22), -- Tamagoyaki Japanese Omelette: Add Some Mix to the Saucepan +(4, 23), -- Tamagoyaki Japanese Omelette: Let It Cook for 1 minute +(4, 24), -- Tamagoyaki Japanese Omelette: Add Oil to a Saucepan Again +(4, 25), -- Tamagoyaki Japanese Omelette: Add Some Mix to the Saucepan Again +(4, 26), -- Tamagoyaki Japanese Omelette: Let It Cook for 1 minute Again +(4, 27); -- Tamagoyaki Japanese Omelette: Remove Pan from Fire +*/ + + +/* +select section + +use recipes_db; +select*from recipes; +select*from ingredients; +select*from categories; +select*from steps; +select*from recipe_categories; +SELECT ingredientid, name FROM ingredients; +select*from recipe_ingredients; +select*from recipe_steps; +*/ \ No newline at end of file From 0cb735c7369d2808c2b0c006c7f12ea9849c121c Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Wed, 11 Jun 2025 14:48:30 +0200 Subject: [PATCH 3/5] Add Exercise 1 and Exercise 2 projects: Aggregation and Transactions --- Week4/homework/ex1-aggregation/.gitignore | 1 + .../ex1-aggregation/aggregationFunctions.js | 49 +++++ Week4/homework/ex1-aggregation/index.js | 16 ++ .../ex1-aggregation/package-lock.json | 176 ++++++++++++++++++ Week4/homework/ex1-aggregation/package.json | 17 ++ Week4/homework/ex2-transactions/.gitignore | 1 + Week4/homework/ex2-transactions/index.js | 14 ++ .../ex2-transactions/package-lock.json | 176 ++++++++++++++++++ Week4/homework/ex2-transactions/package.json | 17 ++ Week4/homework/ex2-transactions/setup.js | 55 ++++++ Week4/homework/ex2-transactions/transfer.js | 66 +++++++ 11 files changed, 588 insertions(+) create mode 100644 Week4/homework/ex1-aggregation/.gitignore create mode 100644 Week4/homework/ex1-aggregation/aggregationFunctions.js create mode 100644 Week4/homework/ex1-aggregation/index.js create mode 100644 Week4/homework/ex1-aggregation/package-lock.json create mode 100644 Week4/homework/ex1-aggregation/package.json create mode 100644 Week4/homework/ex2-transactions/.gitignore create mode 100644 Week4/homework/ex2-transactions/index.js create mode 100644 Week4/homework/ex2-transactions/package-lock.json create mode 100644 Week4/homework/ex2-transactions/package.json create mode 100644 Week4/homework/ex2-transactions/setup.js create mode 100644 Week4/homework/ex2-transactions/transfer.js diff --git a/Week4/homework/ex1-aggregation/.gitignore b/Week4/homework/ex1-aggregation/.gitignore new file mode 100644 index 000000000..2eea525d8 --- /dev/null +++ b/Week4/homework/ex1-aggregation/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/Week4/homework/ex1-aggregation/aggregationFunctions.js b/Week4/homework/ex1-aggregation/aggregationFunctions.js new file mode 100644 index 000000000..867b0e1c2 --- /dev/null +++ b/Week4/homework/ex1-aggregation/aggregationFunctions.js @@ -0,0 +1,49 @@ +import { MongoClient } from "mongodb"; +import dotenv from "dotenv"; + +dotenv.config(); + +const client = new MongoClient(process.env.MONGO_URI); +const dbName = "populationData"; + +export async function getPopulationByCountry(countryName) { + await client.connect(); + const db = client.db(dbName); + const collection = db.collection("population"); + + const result = await collection + .aggregate([ + { $match: { Country: countryName } }, + { + $group: { + _id: "$Year", + countPopulation: { $sum: { $add: ["$M", "$F"] } }, + }, + }, + { $sort: { _id: 1 } }, + ]) + .toArray(); + + await client.close(); + return result; +} + +export async function getAgeDataWithTotal(year, age) { + await client.connect(); + const db = client.db(dbName); + const collection = db.collection("population"); + + const result = await collection + .aggregate([ + { $match: { Year: year, Age: age } }, + { + $addFields: { + TotalPopulation: { $add: ["$M", "$F"] }, + }, + }, + ]) + .toArray(); + + await client.close(); + return result; +} diff --git a/Week4/homework/ex1-aggregation/index.js b/Week4/homework/ex1-aggregation/index.js new file mode 100644 index 000000000..23f6e6f11 --- /dev/null +++ b/Week4/homework/ex1-aggregation/index.js @@ -0,0 +1,16 @@ +import { + getPopulationByCountry, + getAgeDataWithTotal, +} from "./aggregationFunctions.js"; + +const run = async () => { + console.log("Population totals for Netherlands by year:"); + const netherlands = await getPopulationByCountry("Netherlands"); + console.log(netherlands); + + console.log("All continents with Age '100+' in 2020:"); + const ageGroup = await getAgeDataWithTotal(2020, "100+"); + console.log(ageGroup); +}; + +run(); diff --git a/Week4/homework/ex1-aggregation/package-lock.json b/Week4/homework/ex1-aggregation/package-lock.json new file mode 100644 index 000000000..3b17b84d8 --- /dev/null +++ b/Week4/homework/ex1-aggregation/package-lock.json @@ -0,0 +1,176 @@ +{ + "name": "ex1-aggregation", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ex1-aggregation", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.5.0", + "mongodb": "^6.17.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", + "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz", + "integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/Week4/homework/ex1-aggregation/package.json b/Week4/homework/ex1-aggregation/package.json new file mode 100644 index 000000000..c5152d476 --- /dev/null +++ b/Week4/homework/ex1-aggregation/package.json @@ -0,0 +1,17 @@ +{ + "name": "ex1-aggregation", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "dotenv": "^16.5.0", + "mongodb": "^6.17.0" + } +} diff --git a/Week4/homework/ex2-transactions/.gitignore b/Week4/homework/ex2-transactions/.gitignore new file mode 100644 index 000000000..2eea525d8 --- /dev/null +++ b/Week4/homework/ex2-transactions/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/Week4/homework/ex2-transactions/index.js b/Week4/homework/ex2-transactions/index.js new file mode 100644 index 000000000..8a49b74ac --- /dev/null +++ b/Week4/homework/ex2-transactions/index.js @@ -0,0 +1,14 @@ +import { setupAccounts } from "./setup.js"; +import { transfer } from "./transfer.js"; + +const run = async () => { + console.log("🔄 Setting up accounts..."); + await setupAccounts(); + + console.log("💸 Transferring 1000 from 101 to 102..."); + await transfer(101, 102, 1000, "Test payment"); + + console.log("Done."); +}; + +run(); diff --git a/Week4/homework/ex2-transactions/package-lock.json b/Week4/homework/ex2-transactions/package-lock.json new file mode 100644 index 000000000..233aa41fb --- /dev/null +++ b/Week4/homework/ex2-transactions/package-lock.json @@ -0,0 +1,176 @@ +{ + "name": "ex2-transactions", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ex2-transactions", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.5.0", + "mongodb": "^6.17.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", + "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz", + "integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/Week4/homework/ex2-transactions/package.json b/Week4/homework/ex2-transactions/package.json new file mode 100644 index 000000000..b6b1637a1 --- /dev/null +++ b/Week4/homework/ex2-transactions/package.json @@ -0,0 +1,17 @@ +{ + "name": "ex2-transactions", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "dotenv": "^16.5.0", + "mongodb": "^6.17.0" + } +} diff --git a/Week4/homework/ex2-transactions/setup.js b/Week4/homework/ex2-transactions/setup.js new file mode 100644 index 000000000..48c78b255 --- /dev/null +++ b/Week4/homework/ex2-transactions/setup.js @@ -0,0 +1,55 @@ +import { MongoClient } from "mongodb"; +import dotenv from "dotenv"; + +dotenv.config(); + +const client = new MongoClient(process.env.MONGO_URI); +const dbName = "transactionsDB"; + +export async function setupAccounts() { + try { + await client.connect(); + const db = client.db(dbName); + const accounts = db.collection("accounts"); + + // Delete existing accounts + await accounts.deleteMany({}); + + // Insert sample accounts + const initialAccounts = [ + { + account_number: 101, + balance: 5000, + account_changes: [ + { + change_number: 1, + amount: 5000, + changed_date: new Date(), + remark: "Initial deposit", + }, + ], + }, + { + account_number: 102, + balance: 3000, + account_changes: [ + { + change_number: 1, + amount: 3000, + changed_date: new Date(), + remark: "Initial deposit", + }, + ], + }, + ]; + + await accounts.insertMany(initialAccounts); + console.log("Accounts setup complete."); + } catch (err) { + console.error("Error setting up accounts:", err); + } finally { + await client.close(); + } +} + +setupAccounts(); diff --git a/Week4/homework/ex2-transactions/transfer.js b/Week4/homework/ex2-transactions/transfer.js new file mode 100644 index 000000000..7a712568b --- /dev/null +++ b/Week4/homework/ex2-transactions/transfer.js @@ -0,0 +1,66 @@ +import { MongoClient } from "mongodb"; +import dotenv from "dotenv"; + +dotenv.config(); + +const client = new MongoClient(process.env.MONGO_URI); +const dbName = "transactionsDB"; + +export async function transfer(fromAcc, toAcc, amount, remark) { + try { + await client.connect(); + const db = client.db(dbName); + const accounts = db.collection("accounts"); + + // Fetch both accounts + const sender = await accounts.findOne({ account_number: fromAcc }); + const receiver = await accounts.findOne({ account_number: toAcc }); + + if (!sender || !receiver) { + throw new Error("One or both accounts not found."); + } + + if (sender.balance < amount) { + throw new Error("Insufficient funds."); + } + + const nextSenderChange = sender.account_changes.length + 1; + const nextReceiverChange = receiver.account_changes.length + 1; + + await accounts.updateOne( + { account_number: fromAcc }, + { + $inc: { balance: -amount }, + $push: { + account_changes: { + change_number: nextSenderChange, + amount: -amount, + changed_date: new Date(), + remark: `Sent to ${toAcc}: ${remark}`, + }, + }, + } + ); + + await accounts.updateOne( + { account_number: toAcc }, + { + $inc: { balance: amount }, + $push: { + account_changes: { + change_number: nextReceiverChange, + amount: amount, + changed_date: new Date(), + remark: `Received from ${fromAcc}: ${remark}`, + }, + }, + } + ); + + console.log(`Transfer of ${amount} from ${fromAcc} to ${toAcc} completed.`); + } catch (err) { + console.error("Transfer failed:", err.message); + } finally { + await client.close(); + } +} From 83e160bb2ee1cf0940492f1985e193415f02f1a6 Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Mon, 16 Jun 2025 18:44:29 +0200 Subject: [PATCH 4/5] fixed ex1&ex2 --- Week4/homework/ex2-transactions/index.js | 25 ++++-- Week4/homework/ex2-transactions/setup.js | 16 +--- Week4/homework/ex2-transactions/transfer.js | 91 ++++++++++----------- 3 files changed, 64 insertions(+), 68 deletions(-) diff --git a/Week4/homework/ex2-transactions/index.js b/Week4/homework/ex2-transactions/index.js index 8a49b74ac..bea1d827a 100644 --- a/Week4/homework/ex2-transactions/index.js +++ b/Week4/homework/ex2-transactions/index.js @@ -1,14 +1,29 @@ import { setupAccounts } from "./setup.js"; import { transfer } from "./transfer.js"; +import dotenv from "dotenv"; +import { MongoClient } from "mongodb"; +dotenv.config(); + +const client = new MongoClient(process.env.MONGO_URI); +const dbName = "transactionsDB"; const run = async () => { - console.log("🔄 Setting up accounts..."); - await setupAccounts(); + try { + await client.connect(); + const db = client.db(dbName); + + console.log("🔄 Setting up accounts..."); + await setupAccounts(db); - console.log("💸 Transferring 1000 from 101 to 102..."); - await transfer(101, 102, 1000, "Test payment"); + console.log("💸 Transferring 1000 from 101 to 102..."); + await transfer(db, 101, 102, 1000, "Test payment"); - console.log("Done."); + console.log("Done."); + } catch (err) { + console.error("Error in main run:", err.message); + } finally { + await client.close(); + } }; run(); diff --git a/Week4/homework/ex2-transactions/setup.js b/Week4/homework/ex2-transactions/setup.js index 48c78b255..9fe9e4986 100644 --- a/Week4/homework/ex2-transactions/setup.js +++ b/Week4/homework/ex2-transactions/setup.js @@ -1,15 +1,5 @@ -import { MongoClient } from "mongodb"; -import dotenv from "dotenv"; - -dotenv.config(); - -const client = new MongoClient(process.env.MONGO_URI); -const dbName = "transactionsDB"; - -export async function setupAccounts() { +export async function setupAccounts(db) { try { - await client.connect(); - const db = client.db(dbName); const accounts = db.collection("accounts"); // Delete existing accounts @@ -47,9 +37,5 @@ export async function setupAccounts() { console.log("Accounts setup complete."); } catch (err) { console.error("Error setting up accounts:", err); - } finally { - await client.close(); } } - -setupAccounts(); diff --git a/Week4/homework/ex2-transactions/transfer.js b/Week4/homework/ex2-transactions/transfer.js index 7a712568b..52dc6576d 100644 --- a/Week4/homework/ex2-transactions/transfer.js +++ b/Week4/homework/ex2-transactions/transfer.js @@ -1,66 +1,61 @@ -import { MongoClient } from "mongodb"; -import dotenv from "dotenv"; - -dotenv.config(); - -const client = new MongoClient(process.env.MONGO_URI); -const dbName = "transactionsDB"; - -export async function transfer(fromAcc, toAcc, amount, remark) { +export async function transfer(db, fromAcc, toAcc, amount, remark) { + const session = db.client.startSession(); try { - await client.connect(); - const db = client.db(dbName); const accounts = db.collection("accounts"); // Fetch both accounts - const sender = await accounts.findOne({ account_number: fromAcc }); - const receiver = await accounts.findOne({ account_number: toAcc }); + await session.withTransaction(async () => { + const sender = await accounts.findOne({ account_number: fromAcc }); + const receiver = await accounts.findOne({ account_number: toAcc }); - if (!sender || !receiver) { - throw new Error("One or both accounts not found."); - } - - if (sender.balance < amount) { - throw new Error("Insufficient funds."); - } + if (!sender || !receiver) { + throw new Error("One or both accounts not found."); + } - const nextSenderChange = sender.account_changes.length + 1; - const nextReceiverChange = receiver.account_changes.length + 1; + if (sender.balance < amount) { + throw new Error("Insufficient funds."); + } - await accounts.updateOne( - { account_number: fromAcc }, - { - $inc: { balance: -amount }, - $push: { - account_changes: { - change_number: nextSenderChange, - amount: -amount, - changed_date: new Date(), - remark: `Sent to ${toAcc}: ${remark}`, + const nextSenderChange = sender.account_changes.length + 1; + const nextReceiverChange = receiver.account_changes.length + 1; + + await accounts.updateOne( + { account_number: fromAcc }, + { + $inc: { balance: -amount }, + $push: { + account_changes: { + change_number: nextSenderChange, + amount: -amount, + changed_date: new Date(), + remark: `Sent to ${toAcc}: ${remark}`, + }, }, }, - } - ); - - await accounts.updateOne( - { account_number: toAcc }, - { - $inc: { balance: amount }, - $push: { - account_changes: { - change_number: nextReceiverChange, - amount: amount, - changed_date: new Date(), - remark: `Received from ${fromAcc}: ${remark}`, + { session } + ); + + await accounts.updateOne( + { account_number: toAcc }, + { + $inc: { balance: amount }, + $push: { + account_changes: { + change_number: nextReceiverChange, + amount: amount, + changed_date: new Date(), + remark: `Received from ${fromAcc}: ${remark}`, + }, }, }, - } - ); + { session } + ); + }); console.log(`Transfer of ${amount} from ${fromAcc} to ${toAcc} completed.`); } catch (err) { console.error("Transfer failed:", err.message); } finally { - await client.close(); + await session.endSession(); } } From b77fa73d22d3c121bcabb1434efc2e97bb480daf Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Mon, 16 Jun 2025 18:49:49 +0200 Subject: [PATCH 5/5] filtered the content and the countries in the database and reinsert it --- .../homework/ex1-aggregation/insertCleaned.js | 51 +++++++++++++++++++ .../ex1-aggregation/package-lock.json | 13 +++++ Week4/homework/ex1-aggregation/package.json | 1 + 3 files changed, 65 insertions(+) create mode 100644 Week4/homework/ex1-aggregation/insertCleaned.js diff --git a/Week4/homework/ex1-aggregation/insertCleaned.js b/Week4/homework/ex1-aggregation/insertCleaned.js new file mode 100644 index 000000000..8a5e4c064 --- /dev/null +++ b/Week4/homework/ex1-aggregation/insertCleaned.js @@ -0,0 +1,51 @@ +import fs from "fs"; +import csv from "csv-parser"; +import { MongoClient } from "mongodb"; +import dotenv from "dotenv"; + +dotenv.config(); + +const client = new MongoClient(process.env.MONGO_URI); +const dbName = "populationData"; +const collectionName = "population"; + +const continents = [ + "AFRICA", + "ASIA", + "EUROPE", + "LATIN AMERICA AND THE CARIBBEAN", + "NORTHERN AMERICA", + "OCEANIA", +]; + +const data = []; + +fs.createReadStream("population_pyramid_1950-2022.csv") + .pipe(csv()) + .on("data", (row) => { + if (!continents.includes(row.Country)) { + data.push({ + Country: row.Country, + Year: parseInt(row.Year), + Age: row.Age, + M: parseInt(row.M), + F: parseInt(row.F), + }); + } + }) + .on("end", async () => { + try { + await client.connect(); + const db = client.db(dbName); + const collection = db.collection(collectionName); + await collection.deleteMany({}); + await collection.insertMany(data); + console.log( + `✅ Inserted ${data.length} country records (no continents).` + ); + } catch (err) { + console.error("Error inserting data:", err); + } finally { + await client.close(); + } + }); diff --git a/Week4/homework/ex1-aggregation/package-lock.json b/Week4/homework/ex1-aggregation/package-lock.json index 3b17b84d8..c483197ad 100644 --- a/Week4/homework/ex1-aggregation/package-lock.json +++ b/Week4/homework/ex1-aggregation/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "csv-parser": "^3.2.0", "dotenv": "^16.5.0", "mongodb": "^6.17.0" } @@ -46,6 +47,18 @@ "node": ">=16.20.1" } }, + "node_modules/csv-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.2.0.tgz", + "integrity": "sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==", + "license": "MIT", + "bin": { + "csv-parser": "bin/csv-parser" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/dotenv": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", diff --git a/Week4/homework/ex1-aggregation/package.json b/Week4/homework/ex1-aggregation/package.json index c5152d476..5206a95e3 100644 --- a/Week4/homework/ex1-aggregation/package.json +++ b/Week4/homework/ex1-aggregation/package.json @@ -11,6 +11,7 @@ "license": "ISC", "description": "", "dependencies": { + "csv-parser": "^3.2.0", "dotenv": "^16.5.0", "mongodb": "^6.17.0" }