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 ee05f91b769c1a893fc40f181aaa048bc7c4caf0 Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Wed, 4 Jun 2025 20:32:29 +0200 Subject: [PATCH 3/5] assignenment --- .../3.1/sql_normalization_exercise_3.1.md | 39 ++++ Week3/assingnment/3.2/package-lock.json | 143 +++++++++++++ Week3/assingnment/3.2/package.json | 16 ++ Week3/assingnment/3.2/transaction.js | 41 ++++ .../3.2/transactions-create-tables.js | 30 +++ .../3.2/transactions-insert-values.js | 39 ++++ Week3/assingnment/3.3/sql-injection.js | 15 ++ Week3/assingnment/3.4/.gitignore | 2 + Week3/assingnment/3.4/data.json | 14 ++ Week3/assingnment/3.4/db.js | 8 + Week3/assingnment/3.4/index.js | 44 ++++ Week3/assingnment/3.4/package-lock.json | 192 ++++++++++++++++++ Week3/assingnment/3.4/package.json | 18 ++ Week3/assingnment/3.4/seedDatabase.js | 23 +++ 14 files changed, 624 insertions(+) create mode 100644 Week3/assingnment/3.1/sql_normalization_exercise_3.1.md create mode 100644 Week3/assingnment/3.2/package-lock.json create mode 100644 Week3/assingnment/3.2/package.json create mode 100644 Week3/assingnment/3.2/transaction.js create mode 100644 Week3/assingnment/3.2/transactions-create-tables.js create mode 100644 Week3/assingnment/3.2/transactions-insert-values.js create mode 100644 Week3/assingnment/3.3/sql-injection.js create mode 100644 Week3/assingnment/3.4/.gitignore create mode 100644 Week3/assingnment/3.4/data.json create mode 100644 Week3/assingnment/3.4/db.js create mode 100644 Week3/assingnment/3.4/index.js create mode 100644 Week3/assingnment/3.4/package-lock.json create mode 100644 Week3/assingnment/3.4/package.json create mode 100644 Week3/assingnment/3.4/seedDatabase.js diff --git a/Week3/assingnment/3.1/sql_normalization_exercise_3.1.md b/Week3/assingnment/3.1/sql_normalization_exercise_3.1.md new file mode 100644 index 000000000..44122b418 --- /dev/null +++ b/Week3/assingnment/3.1/sql_normalization_exercise_3.1.md @@ -0,0 +1,39 @@ +## Columns that violate 1NF? + +`food_code` and `food_desc` contain multiple values in a single cell (comma-separated), which breaks the rule of atomic values. + +## What entities do you recognize that could be extracted? + +- Members: identified by member_id, with member_name and member_address +- Dinners: identified by dinner_id and dinner_date +- Venues: identified by venue_code and venue_description +- FoodItems: identified by food_code and food_desc +- Dinner_Food (junction): links each dinner_id to one or more food_code values + +## Name all the tables and columns that would make a 3NF compliant solution. + +3NF-Compliant Tables and Columns: + +1.Members +-member_id (PK) +-member_name +-member_address + +2.Venues +-venue_code (PK) +-venue_description + +3.FoodItems +-food_code (PK) +-food_desc + +4.Dinners +-dinner_id (PK) +-member_id (FK → Members.member_id) +-dinner_date +-venue_code (FK → Venues.venue_code) + +5.Dinner_Food (junction table). +-dinner_id (FK → Dinners.dinner_id). +-food_code (FK → FoodItems.food_code). +(PK = composite key of dinner_id + food_code). diff --git a/Week3/assingnment/3.2/package-lock.json b/Week3/assingnment/3.2/package-lock.json new file mode 100644 index 000000000..6f72be365 --- /dev/null +++ b/Week3/assingnment/3.2/package-lock.json @@ -0,0 +1,143 @@ +{ + "name": "3.2", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "3.2", + "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/Week3/assingnment/3.2/package.json b/Week3/assingnment/3.2/package.json new file mode 100644 index 000000000..3422d88a8 --- /dev/null +++ b/Week3/assingnment/3.2/package.json @@ -0,0 +1,16 @@ +{ + "name": "3.2", + "version": "1.0.0", + "main": "transaction.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/Week3/assingnment/3.2/transaction.js b/Week3/assingnment/3.2/transaction.js new file mode 100644 index 000000000..3ad54f2b8 --- /dev/null +++ b/Week3/assingnment/3.2/transaction.js @@ -0,0 +1,41 @@ +import mysql from "mysql2/promise"; + +const connection = await mysql.createConnection({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "dinner_club", +}); + +try { + await connection.beginTransaction(); + + const amount = 1000; + const fromAccount = 1; + const toAccount = 2; + + await connection.execute( + ` + update account set balance =balance -? where account_no =?`, + [amount, fromAccount] + ); + await connection.execute( + ` + insert into account_changes (account_no,amount,change_date,remark) + values (?,?,now(),?)`, + [fromAccount, -amount, `Transfer to account ${toAccount}`] + ); + + await connection.execute( + ` + update account set balance = balance + ? where account_no =?`, + [amount, toAccount] + ); + await connection.commit(); + console.log("transaction complete"); +} catch (err) { + await connection.rollback(); + console.error("Transaction faild:", err.message); +} finally { + await connection.end(); +} diff --git a/Week3/assingnment/3.2/transactions-create-tables.js b/Week3/assingnment/3.2/transactions-create-tables.js new file mode 100644 index 000000000..8c49421f3 --- /dev/null +++ b/Week3/assingnment/3.2/transactions-create-tables.js @@ -0,0 +1,30 @@ +import mysql from "mysql2/promise"; + +const connection = await mysql.createConnection({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "dinner_club", +}); + +await connection.execute(` + create table if not exists account ( + account_no int auto_increment primary key, + balance decimal (10,2) + + ); + + `); + +await connection.execute(` + create table if not exists account_changes ( + change_no int auto_increment primary key, + account_no int, + amount decimal (10,2), + change_date datetime, + remark varchar (250), + foreign key (account_no) references account (account_no) + ); + + `); +await connection.end(); diff --git a/Week3/assingnment/3.2/transactions-insert-values.js b/Week3/assingnment/3.2/transactions-insert-values.js new file mode 100644 index 000000000..a2b2ea108 --- /dev/null +++ b/Week3/assingnment/3.2/transactions-insert-values.js @@ -0,0 +1,39 @@ +import mysql from "mysql2/promise"; +const connection = await mysql.createConnection({ + host: "localhost", + user: "hyfuser", + password: "hyfpassword", + database: "dinner_club", +}); + +await connection.execute(`DELETE FROM account_changes`); +await connection.execute(`DELETE FROM account`); +await connection.execute(`alter table account auto_increment =1`); +await connection.execute(`alter table account_changes auto_increment =1`); + +const [resualt1] = await connection.execute( + ` + + insert into account (balance) + values(?)`, + [5000.0] +); +const accountId1 = resualt1.insertId; + +const [resualt2] = await connection.execute( + ` + + insert into account (balance) + values (?)`, + [1000.0] +); +const accountId2 = resualt2.insertId; + +await connection.execute( + ` + + insert into account_changes (account_no,amount, change_date, remark) + values (?,?, NOW(), ?)`, + [accountId2, 400.0, "Initial deposit"] +); +await connection.end(); diff --git a/Week3/assingnment/3.3/sql-injection.js b/Week3/assingnment/3.3/sql-injection.js new file mode 100644 index 000000000..0f0a32a58 --- /dev/null +++ b/Week3/assingnment/3.3/sql-injection.js @@ -0,0 +1,15 @@ +function getPopulation(conn, table, name, code, cb) { + // assuming that connection to the database is established and stored as conn + + const allowedTbles = ["city"]; + if (!allowedTbles.includes(table)) { + return cb(new Error("invalid table name")); + } + + const sql = `select Population from ${table} where name =? and CountryCode = ?`; + conn.query(sql, [name, code], function (err, result) { + if (err) return cb(err); + if (result.length === 0) return cb(new Error("Not found")); + cb(null, result[0].Population); + }); +} diff --git a/Week3/assingnment/3.4/.gitignore b/Week3/assingnment/3.4/.gitignore new file mode 100644 index 000000000..47caa6930 --- /dev/null +++ b/Week3/assingnment/3.4/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules/ diff --git a/Week3/assingnment/3.4/data.json b/Week3/assingnment/3.4/data.json new file mode 100644 index 000000000..0ebe8fa71 --- /dev/null +++ b/Week3/assingnment/3.4/data.json @@ -0,0 +1,14 @@ +[ + { + "title": "Mountain Retreat", + "elements": ["mountain", "lake", "trees"] + }, + { + "title": "Winter Harmony", + "elements": ["snow", "cabin", "pine trees"] + }, + { + "title": "Seaside Sunset", + "elements": ["ocean", "sunset", "beach"] + } +] diff --git a/Week3/assingnment/3.4/db.js b/Week3/assingnment/3.4/db.js new file mode 100644 index 000000000..6937a93fd --- /dev/null +++ b/Week3/assingnment/3.4/db.js @@ -0,0 +1,8 @@ +import { MongoClient } from "mongodb"; +import dotenv from "dotenv"; + +dotenv.config(); +const uri = process.env.MONGO_URL; +const client = new MongoClient(process.env.MONGO_URL); + +export default client; diff --git a/Week3/assingnment/3.4/index.js b/Week3/assingnment/3.4/index.js new file mode 100644 index 000000000..e75c5fcde --- /dev/null +++ b/Week3/assingnment/3.4/index.js @@ -0,0 +1,44 @@ +import client from "./db.js"; + +async function main() { + try { + await client.connect(); + const db = client.db("databaseWeek3"); + const collection = db.collection("bob_ross_episodes"); + + // Fetch all episodes + const allEpisodes = await collection.find({}).toArray(); + console.log("All episodes:", allEpisodes); + + // Find an episode by title + const episode = await collection.findOne({ title: "Mountain Retreat" }); + console.log("Single episode:", episode); + + // Insert a new episode + const newEpisode = { + title: "Peaceful Sunset", + season: 99, + episode: 99, + painted_elements: ["sunset", "lake", "mountain"], + }; + await collection.insertOne(newEpisode); + console.log("New episode inserted"); + + // Update an episode + await collection.updateOne( + { title: "Peaceful Sunset" }, + { $set: { season: 100 } } + ); + console.log("Episode updated"); + + // Delete an episode + await collection.deleteOne({ title: "Peaceful Sunset" }); + console.log("Episode deleted"); + } catch (err) { + console.error("Error:", err.message); + } finally { + await client.close(); + } +} + +main(); diff --git a/Week3/assingnment/3.4/package-lock.json b/Week3/assingnment/3.4/package-lock.json new file mode 100644 index 000000000..065285cd5 --- /dev/null +++ b/Week3/assingnment/3.4/package-lock.json @@ -0,0 +1,192 @@ +{ + "name": "3.4", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "3.4", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.5.0", + "mongodb": "^6.17.0", + "promise": "^8.3.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/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "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/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, + "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/Week3/assingnment/3.4/package.json b/Week3/assingnment/3.4/package.json new file mode 100644 index 000000000..6654b2d4e --- /dev/null +++ b/Week3/assingnment/3.4/package.json @@ -0,0 +1,18 @@ +{ + "name": "3.4", + "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", + "promise": "^8.3.0" + } +} diff --git a/Week3/assingnment/3.4/seedDatabase.js b/Week3/assingnment/3.4/seedDatabase.js new file mode 100644 index 000000000..534297723 --- /dev/null +++ b/Week3/assingnment/3.4/seedDatabase.js @@ -0,0 +1,23 @@ +import client from "./db.js"; +import fs from "fs/promises"; + +const data = JSON.parse(await fs.readFile("./data.json", "utf-8")); + +async function seed() { + try { + await client.connect(); + const db = client.db("databaseWeek3"); + const collection = db.collection("bob_ross_episodes"); + + // Clean up old data + await collection.deleteMany(); + await collection.insertMany(data); + + console.log("database seeded successfully"); + } catch (err) { + console.error("seeding failed", err.message); + } finally { + await client.close(); + } +} +seed(); From 35edddfbce7b568865a715bb297168ee02bddb83 Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Sun, 8 Jun 2025 23:50:27 +0200 Subject: [PATCH 4/5] Finished 3.4 MongoDB CRUD homework with full implementation --- Week3/homework/mongodb/.gitignore | 2 + Week3/homework/mongodb/index.js | 95 ++++++++---- Week3/homework/mongodb/package-lock.json | 176 +++++++++++++++++++++++ Week3/homework/mongodb/package.json | 16 +++ 4 files changed, 263 insertions(+), 26 deletions(-) create mode 100644 Week3/homework/mongodb/.gitignore create mode 100644 Week3/homework/mongodb/package-lock.json create mode 100644 Week3/homework/mongodb/package.json diff --git a/Week3/homework/mongodb/.gitignore b/Week3/homework/mongodb/.gitignore new file mode 100644 index 000000000..47caa6930 --- /dev/null +++ b/Week3/homework/mongodb/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules/ diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js index 41ee8b618..6a9a60774 100644 --- a/Week3/homework/mongodb/index.js +++ b/Week3/homework/mongodb/index.js @@ -1,4 +1,7 @@ const { MongoClient, ServerApiVersion } = require("mongodb"); +const dotenv = require("dotenv"); +const path = require("path"); +dotenv.config({ path: path.resolve(__dirname, ".env") }); const { seedDatabase } = require("./seedDatabase.js"); @@ -13,73 +16,113 @@ async function createEpisodeExercise(client) { // Write code that will add this to the collection! + const db = client.db("databaseWeek3"); + const episodes = db.collection("bob_ross_episodes"); + + const episode = { + episode: "S09E13", + title: "MOUNTAIN HIDE-AWAY", + elements: [ + "CIRRUS", + "CLOUDS", + "CONIFER", + "DECIDIOUS", + "GRASS", + "MOUNTAIN", + "MOUNTAINS", + "RIVER", + "SNOWY_MOUNTAIN", + "TREE", + "TREES", + ], + }; + + const result = await episodes.insertOne(episode); + console.log( - `Created season 9 episode 13 and the document got the id ${"TODO: fill in variable here"}` + `Created season 9 episode 13 and the document got the id ${result.insertedId}` ); } async function findEpisodesExercises(client) { - /** - * Complete the following exercises. - * The comments indicate what to do and what the result should be! - */ - // Find the title of episode 2 in season 2 [Should be: WINTER SUN] + const db = client.db("databaseWeek3"); + const episodes = db.collection("bob_ross_episodes"); + const result1 = await episodes.findOne({ episode: "S02E02" }); - console.log( - `The title of episode 2 in season 2 is ${"TODO: fill in variable here"}` - ); + console.log(`The title of episode 2 in season 2 is ${result1?.title}`); // Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06] + const result2 = await episodes.findOne({ title: "BLACK RIVER" }); console.log( - `The season and episode number of the "BLACK RIVER" episode is ${"TODO: fill in variable here"}` + `The season and episode number of the "BLACK RIVER" episode is ${result2?.episode}` ); // Find all of the episode titles where Bob Ross painted a CLIFF [Should be: NIGHT LIGHT, EVENING SEASCAPE, SURF'S UP, CLIFFSIDE, BY THE SEA, DEEP WILDERNESS HOME, CRIMSON TIDE, GRACEFUL WATERFALL] + const result3 = await episodes + .find({ elements: "CLIFF" }, { projection: { title: 1, _id: 0 } }) + .toArray(); + const titles3 = result3.map((e) => e.title); + console.log( - `The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}` + `The episodes that Bob Ross painted a CLIFF are ${titles3.join(", ")}}` ); // Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT] + const result4 = await episodes + .find( + { elements: { $all: ["CLIFF", "LIGHTHOUSE"] } }, + { projection: { title: 1, _id: 0 } } + ) + .toArray(); + const titles4 = result4.map((e) => e.title); + console.log( - `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${"TODO: fill in variable here"}` + `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${titles4.join( + ", " + )}` ); } async function updateEpisodeExercises(client) { - /** - * There are some problems in the initial data that was filled in. - * Let's use update functions to update this information. - * - * Note: do NOT change the data.json file - */ - // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that + const db = client.db("databaseWeek3"); + const episodes = db.collection("bob_ross_episodes"); + + const result1 = await episodes.updateOne( + { episode: "S30E13" }, + { $set: { title: "BLUE RIDGE FALLS" } } + ); + console.log( - `Ran a command to update episode 13 in season 30 and it updated ${"TODO: fill in variable here"} episodes` + `Ran a command to update episode 13 in season 30 and it updated ${result1.modifiedCount} episodes` ); // Unfortunately we made a mistake in the arrays and the element type called 'BUSHES' should actually be 'BUSH' as sometimes only one bush was painted. // Update all of the documents in the collection that have `BUSHES` in the elements array to now have `BUSH` // It should update 120 episodes! + const result2 = await episodes.updateMany( + { elements: "BUSHES" }, + { $set: { "elements.$": "BUSH" } } + ); console.log( - `Ran a command to update all the BUSHES to BUSH and it updated ${"TODO: fill in variable here"} episodes` + `Ran a command to update all the BUSHES to BUSH and it updated ${result2.modifiedCount} episodes` ); } async function deleteEpisodeExercise(client) { - /** - * It seems an errand episode has gotten into our data. - * This is episode 14 in season 31. Please remove it and verify that it has been removed! - */ + const db = client.db("databaseWeek3"); + const episodes = db.collection("bob_ross_episodes"); + + const result = await episodes.deleteOne({ episode: "S31E14" }); console.log( - `Ran a command to delete episode and it deleted ${"TODO: fill in variable here"} episodes` + `Ran a command to delete episode and it deleted ${result.deletedCount} episodes` ); } diff --git a/Week3/homework/mongodb/package-lock.json b/Week3/homework/mongodb/package-lock.json new file mode 100644 index 000000000..f07042071 --- /dev/null +++ b/Week3/homework/mongodb/package-lock.json @@ -0,0 +1,176 @@ +{ + "name": "mongodb", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mongodb", + "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/Week3/homework/mongodb/package.json b/Week3/homework/mongodb/package.json new file mode 100644 index 000000000..fbb2bb0b0 --- /dev/null +++ b/Week3/homework/mongodb/package.json @@ -0,0 +1,16 @@ +{ + "name": "mongodb", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "dotenv": "^16.5.0", + "mongodb": "^6.17.0" + } +} From c32c3519f07c6bb689abf2b8c2a07410e6c37add Mon Sep 17 00:00:00 2001 From: rizan-ibrahim Date: Fri, 13 Jun 2025 21:03:28 +0200 Subject: [PATCH 5/5] added answer for question 3.3.1 --- Week3/assingnment/3.3/sql-injection.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Week3/assingnment/3.3/sql-injection.js b/Week3/assingnment/3.3/sql-injection.js index 0f0a32a58..ffd62b077 100644 --- a/Week3/assingnment/3.3/sql-injection.js +++ b/Week3/assingnment/3.3/sql-injection.js @@ -1,3 +1,10 @@ +//3.3.1 + +//name = '' OR '1'='1' , code = '' OR '1'='1' +// this will make the query be like +// SELECT Population FROM ${Country} WHERE Name = '' OR '1'='1' AND code = '' OR '1'='1' + +// 3.3.2 function getPopulation(conn, table, name, code, cb) { // assuming that connection to the database is established and stored as conn