From 1f6a015ca400bb5d84561762a4adee95b1f1e190 Mon Sep 17 00:00:00 2001 From: Lefteris Rodinos Date: Sun, 7 Apr 2024 13:12:34 +0300 Subject: [PATCH 01/13] first attempt at products.html --- products.css | 12 +++- products.html | 166 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 170 insertions(+), 8 deletions(-) diff --git a/products.css b/products.css index 4d40fdc..65dfe51 100644 --- a/products.css +++ b/products.css @@ -1,3 +1,13 @@ /* BEGIN CODE HERE */ +header.container-fluid img { + width: 250px; + height: 58px; +} -/* END CODE HERE */ \ No newline at end of file +label.form-label { + display: inline-block; + width: 100px; + text-align: left; +} + +/* END CODE HERE */ diff --git a/products.html b/products.html index d5596c4..5ae7bc6 100644 --- a/products.html +++ b/products.html @@ -1,17 +1,169 @@ - + - - + + WIS Project - Products - - + + + +
+
+
+ Logo +
+
+ +
+
+
+ +
+ +
+
+
+

Search a product

+
+
+
+ + +
+
+
+ +
+
+

Results

+ + + + + + + + + + + + + + +
IDNameProduction yearPriceColorSize
+
+
+
+ + + +
+

Add a product

+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+ + + + + - + - \ No newline at end of file + From cc79e0794c54cb4ee2742fa513c064c02d8f328c Mon Sep 17 00:00:00 2001 From: orfikos Date: Sun, 14 Apr 2024 17:55:14 +0300 Subject: [PATCH 02/13] homepage html --- homepage.html | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/homepage.html b/homepage.html index 5cfa39a..c2d5ba5 100644 --- a/homepage.html +++ b/homepage.html @@ -9,6 +9,95 @@ + + +
+
+
+ Logo +
+
+ +
+
+
+ + +
+ + +
+
+
+

Heading

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+ Image 1 +
+
+
+ + + +
+
+
+

• Lorem ipsum

+

• Lorem ipsum

+

• Lorem ipsum

+

• Lorem ipsum

+

• Lorem ipsum

+

• Lorem ipsum

+
+
+

Heading

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed

+
+
+ image-2 + image-3 +
+
+
+

Lorem ipsum dolor

+
+
+

Lorem ipsum dolor

+
+
+
+
+ + + +
+
+ +
+
+ +
+ +
+
+
Full Name - 0000
+
+
+ From a8fa00b39284cadf9e793115835b15fb5f737879 Mon Sep 17 00:00:00 2001 From: Lefteris Rodinos Date: Tue, 16 Apr 2024 17:20:03 +0300 Subject: [PATCH 03/13] added .gitignore to ignore dependencies directory .venv --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b22887 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Ignore virtual environment folder +.venv/* \ No newline at end of file From ee0e42b72ca166e1bee9eb16ba12246fa67803ee Mon Sep 17 00:00:00 2001 From: Lefteris Rodinos Date: Wed, 1 May 2024 18:21:58 +0300 Subject: [PATCH 04/13] done content based filtering --- app.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 065415c..c448e06 100644 --- a/app.py +++ b/app.py @@ -1,8 +1,11 @@ # BEGIN CODE HERE -from flask import Flask +from flask import Flask, request, jsonify from flask_pymongo import PyMongo from flask_cors import CORS from pymongo import TEXT +from numpy import dot +from numpy.linalg import norm + # END CODE HERE app = Flask(__name__) @@ -29,7 +32,35 @@ def add_product(): @app.route("/content-based-filtering", methods=["POST"]) def content_based_filtering(): # BEGIN CODE HERE - return "" + def calculate_similarity(product1, product2): + cosine_similarity = dot(product1, product2) / (norm(product1) * norm(product2)) + return cosine_similarity + + given_product = request.get_json() + given_product_features = [ + given_product.get("production_year"), + given_product.get("price"), + given_product.get("color"), + given_product.get("size"), + ] + # TODO: should id and name be included in similarity calculation? + similar_products = [] + all_products = mongo.db.products.find( + {}, + {"name": 1, "production_year": 1, "price": 1, "color": 1, "size": 1}, + ) + for product in all_products: + product_features = [ + product["production_year"], + product["price"], + product["color"], + product["size"], + ] + similarity = calculate_similarity(given_product_features, product_features) + if similarity > 0.7: + similar_products.append(product["name"]) + + return jsonify(similar_products) # END CODE HERE From 1c93938d515eaee296bb3ce7f833986834e19c90 Mon Sep 17 00:00:00 2001 From: kostas Date: Thu, 9 May 2024 11:54:59 +0300 Subject: [PATCH 05/13] done add product --- app.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/app.py b/app.py index c448e06..239c796 100644 --- a/app.py +++ b/app.py @@ -25,7 +25,26 @@ def search(): @app.route("/add-product", methods=["POST"]) def add_product(): # BEGIN CODE HERE - return "" + data = request.json + + if not data: + return jsonify({"error": "No JSON data provided"}), 400 + + existing_product = mongo.db.products.find_one({"name": data["name"]}) + if existing_product: + # Ενημέρωση των πεδίων του υπάρχοντος προϊόντος + existing_product['price'] = data['price'] + existing_product['production_year'] = data['production_year'] + existing_product['color'] = data['color'] + existing_product['size'] = data['size'] + mongo.db.products.save(existing_product) + return jsonify({"message": "Product updated successfully"}), 200 + + # Προσθήκη νέου προϊόντος στη βάση + mongo.db.products.insert_one(data) + + return jsonify({"message": "Product added successfully"}), 201 + # END CODE HERE From bfc6e716e444cc8c968e1ed74bf1789d8d9aa70a Mon Sep 17 00:00:00 2001 From: smandroulaki Date: Tue, 21 May 2024 19:36:55 +0300 Subject: [PATCH 06/13] added search --- app.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 239c796..913e1d2 100644 --- a/app.py +++ b/app.py @@ -18,14 +18,22 @@ @app.route("/search", methods=["GET"]) def search(): # BEGIN CODE HERE - return "" + data = request.json + if not data: + return jsonify({"error":"No JSON data provided"}), 400 + + query = request.args.get('query') + result = mongo.db.products.find({'name':query}).sort('price',-1) + + return jsonify(result) + # END CODE HERE @app.route("/add-product", methods=["POST"]) def add_product(): # BEGIN CODE HERE - data = request.json + data = request.json if not data: return jsonify({"error": "No JSON data provided"}), 400 From b653ce4a126f50af3bdea045165801b8a2fb2950 Mon Sep 17 00:00:00 2001 From: Lefteris Rodinos Date: Fri, 24 May 2024 13:38:29 +0300 Subject: [PATCH 07/13] did products.js had to change python code because mistakes were causing errors --- app.py | 59 +++++++++++++++++++++++++++----------- products.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 114 insertions(+), 27 deletions(-) diff --git a/app.py b/app.py index 913e1d2..effc171 100644 --- a/app.py +++ b/app.py @@ -18,14 +18,18 @@ @app.route("/search", methods=["GET"]) def search(): # BEGIN CODE HERE - data = request.json - if not data: - return jsonify({"error":"No JSON data provided"}), 400 + query = request.args.get("name") + if not query: + return jsonify({"error": "No query provided"}), 400 + result = list(mongo.db.products.find({"name": query}).sort("price", -1)) + + if not result: + return jsonify({"error": "Product not found"}), 404 - query = request.args.get('query') - result = mongo.db.products.find({'name':query}).sort('price',-1) + for product in result: + product["_id"] = str(product["_id"]) - return jsonify(result) + return jsonify(result) # END CODE HERE @@ -34,23 +38,39 @@ def search(): def add_product(): # BEGIN CODE HERE data = request.json - - if not data: - return jsonify({"error": "No JSON data provided"}), 400 - + + def check_price(price): + parts = price.split(".") + if len(parts) != 2: + return False + return parts[0].isnumeric() and parts[1].isnumeric() + + if ( + not data + or not data["name"].strip() + or not data.get("production_year").isdigit() + or not check_price(data.get("price")) + ): + return jsonify({"error": "Wrong JSON data provided"}), 400 + + # Check valid values for color and size + valid_colors = ["1", "2", "3"] + valid_sizes = ["1", "2", "3", "4"] + if data["color"] not in valid_colors or data["size"] not in valid_sizes: + return jsonify({"error": "Invalid color or size provided"}), 400 + existing_product = mongo.db.products.find_one({"name": data["name"]}) if existing_product: # Ενημέρωση των πεδίων του υπάρχοντος προϊόντος - existing_product['price'] = data['price'] - existing_product['production_year'] = data['production_year'] - existing_product['color'] = data['color'] - existing_product['size'] = data['size'] - mongo.db.products.save(existing_product) + mongo.db.products.update_one( + {"_id": existing_product["_id"]}, + {"$set": data}, + ) return jsonify({"message": "Product updated successfully"}), 200 - + # Προσθήκη νέου προϊόντος στη βάση mongo.db.products.insert_one(data) - + return jsonify({"message": "Product added successfully"}), 201 # END CODE HERE @@ -96,3 +116,8 @@ def crawler(): # BEGIN CODE HERE return "" # END CODE HERE + + +# this is not inside BEGIN and END CODE but how else is the server supposed to run? +if __name__ == "__main__": + app.run(debug=True) diff --git a/products.js b/products.js index 40fe8f5..0e6118d 100644 --- a/products.js +++ b/products.js @@ -1,19 +1,81 @@ const api = "http://127.0.0.1:5000"; window.onload = () => { - // BEGIN CODE HERE - - // END CODE HERE -} + // BEGIN CODE HERE + document + .querySelector(".btn-primary") + .addEventListener("click", searchButtonOnClick); + document + .querySelector("form") + .addEventListener("submit", productFormOnSubmit); + // END CODE HERE +}; searchButtonOnClick = () => { - // BEGIN CODE HERE + // BEGIN CODE HERE + const searchInput = document.querySelector(".input-group input").value; + const tbody = document.querySelector("table tbody"); + tbody.innerHTML = ""; + if (!searchInput) return; - // END CODE HERE -} + fetch(`${api}/search?name=${encodeURIComponent(searchInput)}`) + .then((response) => { + if (!response.ok) { + throw new Error("Error fetching product data: " + response.statusText); + } + return response.json(); + }) + .then((product) => { + console.log(product[0]); + const row = document.createElement("tr"); + row.innerHTML = ` + ${product[0]._id} + ${product[0].name} + ${product[0].production_year} + ${product[0].price} + ${product[0].color} + ${product[0].size} + `; + tbody.appendChild(row); + }) + .catch((error) => { + console.error(error); + }); + // END CODE HERE +}; productFormOnSubmit = (event) => { - // BEGIN CODE HERE + // BEGIN CODE HERE + event.preventDefault(); + + const productData = { + name: document.getElementById("productName").value, + production_year: document.getElementById("productionYear").value, + price: document.getElementById("price").value, + color: document.getElementById("color").value, + size: document.getElementById("size").value, + }; - // END CODE HERE -} + fetch(`${api}/add-product`, { + method: "POST", + headers: { + "Content-Type": "application/json;charset=UTF-8", + }, + body: JSON.stringify(productData), + }) + .then((response) => { + if (response.status === 200) { + document.querySelector("form").reset(); + console.log("Product updated successfully"); + } else if (response.status === 201) { + document.querySelector("form").reset(); + console.log("Product added successfully"); + } else { + throw new Error("Error adding product: " + response.statusText); + } + }) + .catch((error) => { + console.error(error); + }); + // END CODE HERE +}; From 721c21aa639964da24c33ee7ff54f012fdb00f53 Mon Sep 17 00:00:00 2001 From: Orfeas galanos <56249800+orfikos@users.noreply.github.com> Date: Wed, 29 May 2024 00:57:45 +0300 Subject: [PATCH 08/13] Update app.py crawler --- app.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index effc171..74580fb 100644 --- a/app.py +++ b/app.py @@ -5,7 +5,9 @@ from pymongo import TEXT from numpy import dot from numpy.linalg import norm - +from selenium import webdriver +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By # END CODE HERE app = Flask(__name__) @@ -114,7 +116,20 @@ def calculate_similarity(product1, product2): @app.route("/crawler", methods=["GET"]) def crawler(): # BEGIN CODE HERE - return "" + url= "https:qa.auth.gr/el/x/studyguide/600000438/current" + options = Options() + options.headless = True + driver= webdriver.Chrome(options=options) + int(semester) + semester= request.args.get('semester') + driver.get(url) + print(semester) + section = driver.find_element(By.ID, "exam" + semester) + elements = section.find_elements(By.TAG_NAME, "a") + res = [] + for element in elements: + res.append(element.text) + return jsonify(res) # END CODE HERE From d0dd746d489ffe4db825bf59dee0d17816732c93 Mon Sep 17 00:00:00 2001 From: Lefteris Rodinos Date: Thu, 30 May 2024 15:21:16 +0300 Subject: [PATCH 09/13] fixed a bug in crawler --- app.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 74580fb..ef06734 100644 --- a/app.py +++ b/app.py @@ -8,6 +8,7 @@ from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By + # END CODE HERE app = Flask(__name__) @@ -116,19 +117,19 @@ def calculate_similarity(product1, product2): @app.route("/crawler", methods=["GET"]) def crawler(): # BEGIN CODE HERE - url= "https:qa.auth.gr/el/x/studyguide/600000438/current" + url = "https:qa.auth.gr/el/x/studyguide/600000438/current" options = Options() options.headless = True - driver= webdriver.Chrome(options=options) - int(semester) - semester= request.args.get('semester') + driver = webdriver.Chrome(options=options) + semester = request.args.get("semester") driver.get(url) print(semester) section = driver.find_element(By.ID, "exam" + semester) elements = section.find_elements(By.TAG_NAME, "a") res = [] for element in elements: - res.append(element.text) + res.append(element.text) + driver.quit() return jsonify(res) # END CODE HERE From 044e5b8bac0cf2a8ecade9f3114e4ea4e367676a Mon Sep 17 00:00:00 2001 From: Lefteris Rodinos Date: Thu, 30 May 2024 15:49:54 +0300 Subject: [PATCH 10/13] fixed html so hyperlink from home to products and vice versa works --- app.py | 2 +- homepage.html | 2 +- products.html | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index ef06734..78a2adc 100644 --- a/app.py +++ b/app.py @@ -108,7 +108,7 @@ def calculate_similarity(product1, product2): ] similarity = calculate_similarity(given_product_features, product_features) if similarity > 0.7: - similar_products.append(product["name"]) + similar_products.append(product) return jsonify(similar_products) # END CODE HERE diff --git a/homepage.html b/homepage.html index c2d5ba5..17e2c7d 100644 --- a/homepage.html +++ b/homepage.html @@ -26,7 +26,7 @@ >Home
  • - + Products
  • diff --git a/products.html b/products.html index 5ae7bc6..40ed55c 100644 --- a/products.html +++ b/products.html @@ -25,7 +25,11 @@