Welcome back!
+Welcome back!
+ {% if login_error %} +
Incorrect username or password.
+ {% endif %}diff --git a/P3 CHANGELOG.md b/P3 CHANGELOG.md new file mode 100644 index 00000000..ad0883a2 --- /dev/null +++ b/P3 CHANGELOG.md @@ -0,0 +1,37 @@ +# P3 Implementation Changelog + +## **Changes by Alberto** +### Website: + +- **REQ-1:** Added Homepage navbar with home button to redirect to homepage - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/18caea8cbcf944fd41e9b9a98fd1623d5565908e) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/370521892db1ca19dc27f95c0b657920d2da17db) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/0208311f4df315586c961a88ccda8a430d727032) +- **REQ-2:** Users can register account into the database and login - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/9072eacd0b405eebb15afbdb7c6357a643ff2616) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/9072eacd0b405eebb15afbdb7c6357a643ff2616) +- **REQ-3:** Users have their cart saved even after logging out - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/1d6dc85768a670a6f7f3af8e7404e2b336209318) +- **REQ-4:** Customers can now add products to the shopping cart and view the content - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/238ba64e30c9bc58f8ff03d8792e8e08b91e144f) +- **REQ-5:** `Admin Panel` added to manage some database functions. Only accessible through admin login - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/7696795c4466f83ee2be3e97e6510c5d14c7202d) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/9f6f53fca778f01a1dee26c16d15a982e1f17028) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/07e4b425f239c1ac5c1d221a535feda6e2512cfc) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/7696795c4466f83ee2be3e97e6510c5d14c7202d) +- **REQ-6:** Customers can checkout product in the index page, but needs to have an account to checkout - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/b96975418ae36ef286ce214c9c54ddfb7066cafc) +- **REQ-7:** Items are displayed neatly with name, image, info and price - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/a9f203fced50599f64ee3ad2aa8e51ccc65529a1) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/441ed3288de037ea8bcedc3eafb43a85aafa807f) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/441ed3288de037ea8bcedc3eafb43a85aafa807f) +- **REQ-8:** Database storing inventory, sales and users' informations - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/545671b11dc075284acd5c714f931c56a2249de1) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/a9f203fced50599f64ee3ad2aa8e51ccc65529a1) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/0a0933441cafe76ac1258b12cb1671568431b363) +- **REQ-9:** Added items stock function to keep track of items available, can't buy items if not enough in stock warning - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/789a41d5e2b3bb15d3d537a150a13fb8eeba81dc) +- **REQ-10:** Checkout page will now show total cost of the order - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/238ba64e30c9bc58f8ff03d8792e8e08b91e144f) - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/b76f8b0f67809ad985299cf0dce4e4e627a1d05f) +- **REQ-11:** Admin Panel will now allow admin to update products information - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/07e4b425f239c1ac5c1d221a535feda6e2512cfc) +- **REQ-12:** Admin Panel will now allow admin to add products to database - [COMMIT](https://github.com/olivialba/onlineStoreTemplate/commit/7696795c4466f83ee2be3e97e6510c5d14c7202d) +- Added Header +- Added Logout +- Added Order Page table to see a user's past orders +- Added Users can update quantity of items at checkout +- Changed overall CSS style of the Website +- Changed Login and Register buttons + +### Admin Panel: +- Bookstoremanager login will show an Admin panel, the panel is defined in 'admin_panel.html' and 'admin_panel.py' +- Bookstoremanager login info: + - Username: `Admin` + - Password: `Admin` + +### Fixed Bugs: +- Prevent: users can't register with 'default' or 'admin' in their username, to prevent bugs +- Fixed: login users didn't change session to their new login +- Fixed: customers can't login if username or password invalid +- Fixed: incorrect and uneven css style for login and register .html pages +- Fixed: Incorrect backend for login-logout +- Fixed: Users now keep their cart saved even after logging out diff --git a/admin_panel.py b/admin_panel.py new file mode 100644 index 00000000..550d4260 --- /dev/null +++ b/admin_panel.py @@ -0,0 +1,132 @@ +# Admin Panel by Alberto Olivi + +from authentication.auth_tools import login_pipeline, update_passwords, hash_password +from flask import Blueprint, Flask, render_template, request +from core_info import sessions, db + +admin_panel_bp = Blueprint('admin_panel', __name__) + +""" +Used to update product info +First is textfield from which to take value, second is method to use to update said value +""" +field_to_db_update = { + 'itemname_update': db.set_item_name, + 'info_update': db.set_item_info, + 'price_update': db.set_item_price, + 'stock_update': db.set_item_stock, + 'image_url_update': db.set_item_image_url, + 'category_update': db.set_item_category +} + + +@admin_panel_bp.route('/admin_panel/add-product', methods=['POST']) +def add_product(): + """ + Renders the add-product request when the user is at the `/admin_panel/add-product` endpoint with a POST request. + Add product to the database. + + args: + - None + + returns: + - None + + modifies: + - database/store_records.db: add a product to the inventory in the database. + """ + item_name = request.form['itemname'] + price = request.form['price'] + info = request.form['info'] + stock = request.form['stock'] + image_url = request.form['image_url'] + category = request.form['category'] + if not item_name or not price or not info or not stock or not image_url or not category: # All fields needs to be filled + return render_template('admin_panel.html', empty_field=True) + price = float(price) + stock = float(stock) + if not stock.is_integer(): # Stock needs to be full integer + return render_template('admin_panel.html', stock_not_integer=True) + if price < 0 or stock < 0: # Price and stock can't be negative, but can be zero + return render_template('admin_panel.html', negative_number=True) + db.insert_new_item(item_name, price, info, stock, image_url, category) + print("Product added") + return render_template('admin_panel.html', product_added=True) + + +@admin_panel_bp.route('/admin_panel/update-product', methods=['POST']) +def update_product(): + """ + Renders the update-product page when the user is at the `/admin_panel/update-product` endpoint with a POST request. + Shows product data and allows for product data to be edited in the database. + + args: + - None + + returns: + - Data: shows product's data in the /update-product page + + modifies: + - database/store_records.db: change a product data + """ + + # Button to search for product in database + if 'button_send' in request.form and request.form['button_send'] == 'find_product': + item_id = request.form['itemid'] + return get_item_info(item_id) + + # Button to update a product in database + elif 'button_send' in request.form and request.form['button_send'] == 'update_product': + item_id = request.form['id_update'] + for field, update_info in field_to_db_update.items(): + new_value = request.form[field].strip() + if new_value: + update_info(item_id, new_value) + return get_item_info(item_id) + + else: + print('Error: Could not detect button pressed. How were you even able to cause this error?') + return render_template('admin_panel.html') + + +@admin_panel_bp.route('/admin_panel/view-inventory', methods=['POST']) # With 'GET' you can go directly to the url even without admin access or log in? +def view_inventory(): + """ + Renders the inventory products when the user is at the `/admin_panel/view-inventory` endpoint with a POST request. + + args: + - None + + returns: + - None + + modifies: + - None + """ + inventory = db.get_full_inventory() + return render_template('admin_panel.html', inventory=inventory) + + + +# Used for displaying item info after updating a product +def get_item_info(item_id: int): + """ + Get all the product data corresponding to the product item_id from the database. + Used to display data for the 'Update Product' panel + + args: + - item_id: integer for product id + + returns: + - Render admin_panel page with product/item information + """ + check = db.get_item_name_by_id(item_id) + if check is None: # Couldn't find item ID in database, send item_not_found error + return render_template('admin_panel.html', item_not_found=True) + name = check['item_name'] + info = db.get_item_info_by_id(item_id)['info'] + price = db.get_item_price_by_id(item_id)['price'] + stock = db.get_item_stock_by_id(item_id)['stock'] + image_url = db.get_item_image_url_by_id(item_id)['image_url'] + category = db.get_item_category_by_id(item_id)['category'] + return render_template('admin_panel.html', item_found=True, item_id=item_id, name=name, info=info, price=price, stock=stock, image_url=image_url, category=category) \ No newline at end of file diff --git a/app.py b/app.py index a6cf8b3f..8a1920cc 100644 --- a/app.py +++ b/app.py @@ -1,17 +1,16 @@ #!/usr/bin/env python3 from authentication.auth_tools import login_pipeline, update_passwords, hash_password -from database.db import Database from flask import Flask, render_template, request -from core.session import Sessions +from admin_panel import admin_panel_bp +from core_info import sessions, db # Sessions and Database app = Flask(__name__) +app.register_blueprint(admin_panel_bp) # Register blueprint to connect 'app.py' to 'admin_panel.py' HOST, PORT = 'localhost', 8080 global username, products, db, sessions username = 'default' -db = Database('database/store_records.db') products = db.get_full_inventory() -sessions = Sessions() sessions.add_new_session(username, db) @@ -26,6 +25,42 @@ def index_page(): returns: - None """ + products = db.get_full_inventory() # Reload Inventory in case a new product was just added + if username != 'default': # If a user is logged in, re-route to show home.html + return render_template('home.html', username=username, products=products, sessions=sessions) + return render_template('index.html', username=username, products=products, sessions=sessions) + +@app.route('/about_us') +def about_us(): + """ + Renders the about us page when the user is at the `/about_us` endpoint. + + args: + - None + + returns: + - None + """ + return render_template('about_us.html', username=username) + + +@app.route('/logout') +def logout(): + """ + Logout of an account and renders the index page + + args: + - None + + returns: + - None + """ + global username + products = db.get_full_inventory() # Reload Inventory in case a new product was just added + if username == 'default': + return render_template('index.html', username=username, products=products, sessions=sessions) + else: + username = 'default' return render_template('index.html', username=username, products=products, sessions=sessions) @@ -40,7 +75,7 @@ def login_page(): returns: - None """ - return render_template('login.html') + return render_template('login.html', username=username) @app.route('/home', methods=['POST']) @@ -58,14 +93,20 @@ def login(): - sessions: adds a new session to the sessions object """ - username = request.form['username'] + user = request.form['username'] password = request.form['password'] - if login_pipeline(username, password): - sessions.add_new_session(username, db) - return render_template('home.html', products=products, sessions=sessions) + if login_pipeline(user, password): + global username + if (user not in sessions.get_all_sessions()): # Add new session only if session didn't already exist, to keep cart items even after log out + sessions.add_new_session(user, db) + username = user # Set global username of new session + + if user == 'Admin': + print(f"Special username logged in: {user}") + return render_template('home.html', username=username, products=products, sessions=sessions) else: - print(f"Incorrect username ({username}) or password ({password}).") - return render_template('index.html') + print(f"Incorrect username ({user}) or password ({password}).") + return render_template('login.html', username=username, login_error=True) @app.route('/register') @@ -79,7 +120,7 @@ def register_page(): returns: - None """ - return render_template('register.html') + return render_template('register.html', username=username) @app.route('/register', methods=['POST']) @@ -98,6 +139,10 @@ def register(): - database/store_records.db: adds a new user to the database """ username = request.form['username'] + # ERROR: Customers can't have 'admin' or 'default' in their username + if 'admin' in username.lower() or 'default' in username.lower(): + return render_template('register.html', register_error=True) + password = request.form['password'] email = request.form['email'] first_name = request.form['first_name'] @@ -125,17 +170,107 @@ def checkout(): order = {} user_session = sessions.get_session(username) for item in products: - print(f"item ID: {item['id']}") if request.form[str(item['id'])] > '0': - count = request.form[str(item['id'])] + count = request.form[str(item['id'])] # Get quantity inputted. Quantity text-bar: name={{product.id}} order[item['item_name']] = count user_session.add_new_item( item['id'], item['item_name'], item['price'], count) user_session.submit_cart() + return render_template('checkout.html', sessions=sessions, total_cost=user_session.total_cost, cart=user_session.get_cart_with_quantity()) + + +@app.route('/checkout-update', methods=['POST']) +def reload_checkout_page(): + """ + Renders the checkout page when the user is at the `/checkout` endpoint with a POST request. + Update cart quantity or create a new sale, and renders the checkout page with new information. + + args: + - None + + returns: + - None + + modifies: + - Update: Update quantity of an item in the user session's cart + - New Sale: Create a new sale in the database and empties cart + """ + user_session = sessions.get_session(username) + cart = user_session.get_cart_with_quantity() + if 'update_checkout' in request.form: # Updating quantity of product in checkout - button + for item_id, item_info in cart.items(): + new_quantity = int(request.form[str(item_id)]) + user_session.update_item_quantity(item_id, new_quantity) + print('Quantity updated') + + elif 'send_checkout' in request.form: # Create new sale - button + transaction_id = db.get_new_sale_transaction_id() + user_session.update_date() + for item_id, item_info in cart.items(): + item_stock = db.get_item_stock_by_id(item_id)['stock'] + remaining_stock = item_stock - int(item_info['quantity']) + if (remaining_stock >= 0): # Check if item's stock is enough + db.insert_new_sale(transaction_id, username, item_id, item_info['quantity'], user_session.date, item_info['subtotal']) + db.set_item_stock(item_id, remaining_stock) + else: + not_enough_stock = {} + item_name = db.get_item_name_by_id(item_id)['item_name'] + not_enough_stock[item_id] = {'item_name': item_name, 'item_stock': item_stock} + return render_template('checkout.html', sessions=sessions, not_enough_stock=not_enough_stock, total_cost=user_session.total_cost, cart=user_session.get_cart_with_quantity()) + user_session.reset_cart() # Reset cart in user session to be empty after a sale + if len(cart.items()) == 0: + print("Cart is empty.") + else: + print("Sales added") + user_session.submit_cart() # Reset or Get total_cost - This needs to be here regardless of if statement outcome + return render_template('checkout.html', sessions=sessions, sale_made=True, total_cost=user_session.total_cost, cart=user_session.get_cart_with_quantity()) + + +@app.route('/orders', methods=['GET']) +def orders_page(): + """ + Renders the orders page when the user is at the `/orders` endpoint with a GET request. + Create a table with the orders of the user. Show error if no user is not logged in. + + args: + - None + + returns: + - None + + modifies: + - None + """ + if username == 'default': + return render_template('orders.html', username=username, no_user_error=True) + else: + sales = db.get_sales_by_username(username) + print(sales) + return render_template('orders.html', username=username, sales=sales) + - return render_template('checkout.html', order=order, sessions=sessions, total_cost=user_session.total_cost) +@app.route('/admin_panel', methods=['POST']) +def admin_panel(): + """ + Renders the admin panel when the user is at the `/admin_panel` endpoint with a POST request and + is using the admin account. + + args: + - None + returns: + - None + + modifies: + - None + """ + global username + current_session = sessions.get_session(username) + if 'Admin' == current_session.username: + print("Admin verified: proceeding to Admin Panel") + return render_template('admin_panel.html', sessions=sessions) + return render_template('index.html', username=username, products=products, sessions=sessions) if __name__ == '__main__': app.run(debug=True, host=HOST, port=PORT) diff --git a/authentication/passwords.txt b/authentication/passwords.txt index 3d93be76..9da358f2 100644 --- a/authentication/passwords.txt +++ b/authentication/passwords.txt @@ -1,4 +1,6 @@ aturing:0d911297a1e34f4fcce78537f9aaa66a:b93727798b520dc10d145b53909c061f082ff14cd5f8cb4ab24c3b71bfa57d7e12e1296029be74c06a0d91ba32756f9fc978047fbe7232be67f94dfc1de9ced9 dritchie:e11d3b1a66b1ad362223c30b78138519:67aff785bd17ac24448d491926ff7aadd8fa75e51a2f7a9bfc31889bad0adcd2989061a27ccd9eff9e5e31f2bc14b5c193727e116dc8dc48259acb3919171cd4 llamport:89d0e5fe8d06ec113839c8f319d7033e:9171d14954eeda4e70777c23d98e349818125cdaeb884ff97ebf8cc0a9c7778f54ce394256588148132a03ebea891e44077c659e6c0132fa87a8cf77e436ae11 -bliskov:e71dda285effa69e1c29ac810fe7a986:1e4b9ae956cad1385cfa6fffd8323dd16c3fe18c54e6447e49bddef2138d042e84e1505a541c6ef19a5026e684b2559efd366145870a0a8d4d4173c0877f6cd2 \ No newline at end of file +bliskov:e71dda285effa69e1c29ac810fe7a986:1e4b9ae956cad1385cfa6fffd8323dd16c3fe18c54e6447e49bddef2138d042e84e1505a541c6ef19a5026e684b2559efd366145870a0a8d4d4173c0877f6cd2 +Admin:026df71fffe6ebee732001c380a4a17e:f90747b096a0bb34bcae8ed0b623367ac42aac304853f446957b37314a2c07606b7c292dfc9e1e672cec634365b02f863bfd9b9217c44f8756366da2994955a4 +prova:9a23e19e644c212fbdff778a72386fc2:8f8822969d51a6f1d9080d9dd421f5a0f0d64c147b76df5f9421f5711b733d24da9932c50e5b070d7ce3d6794ee2325567f66f96613b85a2b4e68ada2fd9119a \ No newline at end of file diff --git a/core/session.py b/core/session.py index 0c8a0898..f45555bf 100644 --- a/core/session.py +++ b/core/session.py @@ -42,6 +42,12 @@ def empty_cart(self) -> dict: new_cart[item["id"]] = {"name": item["item_name"], "price": item["price"], "quantity": 0, "discount": 0, "tax_rate": 0} return new_cart + + def reset_cart(self): + """ + Reset the user's cart + """ + self.cart = self.empty_cart() def is_item_in_cart(self, id: str) -> bool: """ @@ -71,7 +77,7 @@ def add_new_item(self, id: str, name: str, price: int, quantity: int, discount: - None """ self.cart[id] = {"name": name, "price": price, "quantity": quantity, - "discount": discount, "tax_rate": tax_rate} + "discount": discount, "tax_rate": tax_rate} def update_item_quantity(self, id: str, change_to_quantity: int) -> None: """ @@ -81,10 +87,10 @@ def update_item_quantity(self, id: str, change_to_quantity: int) -> None: - id: The id of the item. - quantity: The quantity of the item. """ - if self.cart[id]["quantity"] + change_to_quantity <= 0: + if change_to_quantity <= 0: self.remove_item(id) else: - self.cart[id]["quantity"] += change_to_quantity + self.cart[id]["quantity"] = change_to_quantity def remove_item(self, id: str) -> None: """ @@ -100,7 +106,44 @@ def update_total_cost(self) -> None: Updates the total cost of the user's cart. """ self.total_cost = calculate_total_cost(self.cart) + + def get_cart_with_quantity(self) -> dict: + """ + Returns the items in the cart with quantity over 0. + + returns: + - Items in the cart with quantity > 0. + """ + full_cart = self.cart.copy() + items_to_remove = [] + + for item_id, item in self.cart.items(): + if int(item['quantity']) <= 0: + items_to_remove.append(item_id) + else: + price = float(item['price']) + quantity = int(item['quantity']) + discount = float(item['discount']) + tax_rate = float(item['tax_rate']) + subtotal = (price * quantity) * (1 - discount) * (1 + tax_rate) + item['subtotal'] = round(subtotal, 2) + for item_id in items_to_remove: + del full_cart[item_id] + return full_cart + + def update_date(self) -> None: + """ + Update the data of the sale. + args: + - None + + returns: + - None + """ + now = datetime.now() + self.date = now.strftime('%m-%d-%Y %H:%M:%S') # Otherwise it would display decimals for the seconds + def submit_cart(self) -> None: """ Called when the order is submitted. Finalizes user session details. diff --git a/core/utils.py b/core/utils.py index 45153070..3679c3ef 100644 --- a/core/utils.py +++ b/core/utils.py @@ -33,7 +33,8 @@ def calculate_cost(price: int, quantity: int, discount: float = 0.0, tax_rate: f returns: - The cost of the item as a float. """ - return (price * quantity) * (1 - discount) * (1 + tax_rate) + subtotal = (price * quantity) * (1 - discount) * (1 + tax_rate) + return round(subtotal, 2) def calculate_total_cost(items: dict) -> float: @@ -47,12 +48,12 @@ def calculate_total_cost(items: dict) -> float: - The total cost of the sale as a float. """ total_cost = 0 - print(items) + #print(items) for i in items: item = items[i] total_cost += calculate_cost(float(item["price"]), int(item["quantity"]), - float(item["discount"]), int(item["tax_rate"])) - return total_cost + float(item["discount"]), float(item["tax_rate"])) + return round(total_cost, 2) def generate_unique_id() -> str: @@ -66,3 +67,15 @@ def generate_unique_id() -> str: - A unique ID as a string. """ return str(uuid.uuid4()) + +def generate_transaction_id(length) -> str: + """ + Generates a unique transaction ID with 5 characters. + + args: + - None + + returns: + - A unique transaction ID as a string. + """ + return str(uuid.uuid4())[:length] diff --git a/core_info.py b/core_info.py new file mode 100644 index 00000000..49c38eac --- /dev/null +++ b/core_info.py @@ -0,0 +1,5 @@ +from core.session import Sessions +from database.db import Database + +db = Database('database/store_records.db') # Database +sessions = Sessions() # Sessions \ No newline at end of file diff --git a/database/db.py b/database/db.py index e321cce7..76492a6a 100644 --- a/database/db.py +++ b/database/db.py @@ -1,4 +1,4 @@ -from core.utils import dict_factory, calculate_cost +from core.utils import dict_factory, calculate_cost, generate_transaction_id import datetime as dt import sqlite3 @@ -27,7 +27,7 @@ def __init__(self, database_path: str = "store_records.db") -> None: # ----------------- INVENTORY ---------------- # -------------------------------------------- - def insert_new_item(self, item_name: str, price: int, info: str) -> None: + def insert_new_item(self, item_name: str, price: int, info: str, stock: int, image_url: str, category: str) -> None: """ Inserts a new item_item into the database. @@ -40,7 +40,7 @@ def insert_new_item(self, item_name: str, price: int, info: str) -> None: - None """ self.cursor.execute( - "INSERT INTO inventory (item_name, price, info) VALUES (?, ?, ?)", (item_name, price, info)) + "INSERT INTO inventory (item_name, price, info, stock, image_url, category) VALUES (?, ?, ?, ?, ?, ?)", (item_name, price, info, stock, image_url, category)) self.connection.commit() # ------ Getter methods ------ @@ -81,7 +81,7 @@ def get_item_name_by_id(self, item_id: int): returns: - The item with the given id. """ - self.cursor.execute("SELECT * FROM inventory WHERE id = ?", (item_id,)) + self.cursor.execute("SELECT item_name FROM inventory WHERE id = ?", (item_id,)) return self.cursor.fetchone() def get_item_info_by_id(self, item_id: int): @@ -168,7 +168,7 @@ def set_item_name(self, item_id: int, new_name: str): - None """ self.cursor.execute( - "UPDATE inventory SET name = ? WHERE id = ?", (new_name, item_id)) + "UPDATE inventory SET item_name = ? WHERE id = ?", (new_name, item_id)) self.connection.commit() def set_item_info(self, item_id: int, new_info: str): @@ -190,7 +190,7 @@ def set_item_price(self, item_id: int, new_price: float): """ Updates the price of an item in the database. - args: + args:f - item_id: The id of the item to update. - new_price: The new price of the item. @@ -425,6 +425,17 @@ def insert_new_sale(self, transaction_id: int, username: str, item_id: int, quan self.connection.commit() # ------ Getter methods ------ + def get_new_sale_transaction_id(self): + """ + Create a new unique transaction ID. + + args: + - None + + returns: + - Unique transaction ID. + """ + return generate_transaction_id(5) def get_full_sales_information(self): """ diff --git a/database/starting_data.sql b/database/starting_data.sql index 19da65d3..aa631fbd 100644 --- a/database/starting_data.sql +++ b/database/starting_data.sql @@ -10,20 +10,26 @@ VALUES ('llamport', '9171d14954eeda4e70777c23d98e349818125cdaeb884ff97ebf8cc0a9c INSERT into `users` (`username`, `password_hash`, `email`, `first_name`, `last_name`) VALUES ('bliskov', '1e4b9ae956cad1385cfa6fffd8323dd16c3fe18c54e6447e49bddef2138d042e84e1505a541c6ef19a5026e684b2559efd366145870a0a8d4d4173c0877f6cd2', 'barbara@thor.com', 'Barbara', 'Liskov'); +INSERT into `users` (`username`, `password_hash`, `email`, `first_name`, `last_name`) +VALUES ('Admin', 'f90747b096a0bb34bcae8ed0b623367ac42aac304853f446957b37314a2c07606b7c292dfc9e1e672cec634365b02f863bfd9b9217c44f8756366da2994955a4', 'admin@gmail.com', 'Admin', 'Admin'); + +INSERT into `inventory` (`item_name`, `info`, `price`, `stock`, `image_url`, `category`) +VALUES ('A Little Life', 'A novel by Hanya Yanagihara following the lives of four friends, grappling with love, trauma, and resilience, while one carries a haunting past.', 14.99, 49, 'static/books/a_little_life.jpg', 'Literary fiction'); + INSERT into `inventory` (`item_name`, `info`, `price`, `stock`, `image_url`, `category`) -VALUES ('Apples', 'An edible cultivation of the Malus genus.', 2.00, 100, 'static/images/apple.jpeg', 'Fruit'); +VALUES ('American Prometheus', 'Kai Bird and Martin J. Sherwin"s biography explores the life of physicist J. Robert Oppenheimer, his scientific achievements, controversies, and tragedies.', 22.49, 26, 'static/books/american_prometheus.jpg', 'Biography'); INSERT into `inventory` (`item_name`, `info`, `price`, `stock`, `image_url`, `category`) -VALUES ('Bananas', 'A long curved fruit which grows in clusters and has soft pulpy flesh and yellow skin when ripe.', 1.00, 100, 'static/images/banana.jpeg', 'Fruit'); +VALUES ('Archers Voice', 'Mia Sheridan"s heartwarming romance about Archer Hale, a mute man haunted by his past, and Bree Prescott, who brings healing and love into his life.', 30.00, 28, 'static/books/archers_voice.jpg', 'Romance fiction'); INSERT into `inventory` (`item_name`, `info`, `price`, `stock`, `image_url`, `category`) -VALUES ('Mangos', 'The best fruit on the planet.', 4.00, 100, 'static/images/mango.jpeg', 'Fruit'); +VALUES ('House of Earth and Blood', 'Sarah J. Maas"s captivating fantasy follows Bryce Quinlan and Hunt Athalar as they seek justice in a city of intrigue.', 16.99, 34, 'static/books/house_of_earth_and_blood.jpg', 'Fantasy fiction'); INSERT into `sales` (`transaction_id`, `username`, `item_id`, `quantity`, `sale_date`, `cost`) -VALUES ('1', 'aturing', '1', 10, '2022-12-21 7:30:30', 5.50); +VALUES ('c8c1d', 'aturing', '1', 2, '2022-12-27 7:30:30', 31.48); INSERT into `sales` (`transaction_id`, `username`, `item_id`, `quantity`, `sale_date`, `cost`) -VALUES ('2', 'dritchie', '2', 10, '2022-12-21 7:30:30', 5.50); +VALUES ('3f1e2', 'dritchie', '2', 3, '2021-11-17 7:30:30', 70.84); INSERT into `sales` (`transaction_id`, `username`, `item_id`, `quantity`, `sale_date`, `cost`) -VALUES ('3', 'llamport', '3', 10, '2022-12-21 7:30:30', 5.50); +VALUES ('b4d7r', 'llamport', '3', 1, '2020-12-13 7:30:30', 31.50); diff --git a/docs/diagrams/cases/bookStoreManager.png b/docs/diagrams/cases/bookStoreManager.png new file mode 100644 index 00000000..9d1f8ca8 Binary files /dev/null and b/docs/diagrams/cases/bookStoreManager.png differ diff --git a/docs/diagrams/cases/bookStoreManager.puml b/docs/diagrams/cases/bookStoreManager.puml new file mode 100644 index 00000000..bb47741c --- /dev/null +++ b/docs/diagrams/cases/bookStoreManager.puml @@ -0,0 +1,23 @@ +@startuml BookStoreManager +skinparam actorStyle awesome +left to right direction +actor BookStoreManager +rectangle "System" as system #lightblue { + (Login) + (Add Product) + (Disable Product) + (Enable Product) + (Update Product) + (View Products) + (View Orders) + (Logout) +} +BookStoreManager --> (Login) +BookStoreManager --> (Add Product) +BookStoreManager --> (Disable Product) +BookStoreManager --> (Enable Product) +BookStoreManager --> (Update Product) +BookStoreManager --> (View Products) +BookStoreManager --> (View Orders) +BookStoreManager --> (Logout) +@enduml \ No newline at end of file diff --git a/docs/diagrams/cases/customer.png b/docs/diagrams/cases/customer.png new file mode 100644 index 00000000..ff7d70be Binary files /dev/null and b/docs/diagrams/cases/customer.png differ diff --git a/docs/diagrams/cases/customer.puml b/docs/diagrams/cases/customer.puml index f8c8575f..66ef6352 100644 --- a/docs/diagrams/cases/customer.puml +++ b/docs/diagrams/cases/customer.puml @@ -1,20 +1,27 @@ @startuml customer +skinparam actorStyle awesome left to right direction actor Customer rectangle "System" as system { (Login) (Register) (View Cart) - (View Items) + (Search Items) (View Orders) + (Purchase Cart) (View Profile) + (authentication.auth_tools) + (View Wishlist) (Logout) } Customer --> (Login) Customer --> (Register) Customer --> (View Cart) -Customer --> (View Items) +(View Cart) --> (Purchase Cart) +Customer --> (Search Items) Customer --> (View Orders) Customer --> (View Profile) +(View Profile) --> authentication.auth_tools +Customer --> (View Wishlist) Customer --> (Logout) @enduml \ No newline at end of file diff --git a/docs/diagrams/sequences/bookStoreManager/Login.PNG b/docs/diagrams/sequences/bookStoreManager/Login.PNG new file mode 100644 index 00000000..601999ad Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/Login.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/Login.puml b/docs/diagrams/sequences/bookStoreManager/Login.puml new file mode 100644 index 00000000..c71d22f5 --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/Login.puml @@ -0,0 +1,10 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Enter Username & Password +webapp -> db: Check Credentials +db --> webapp: Valid/Invalid +webapp --> manager: Grant Permission/Error +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/Logout.PNG b/docs/diagrams/sequences/bookStoreManager/Logout.PNG new file mode 100644 index 00000000..6c85ccef Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/Logout.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/Logout.puml b/docs/diagrams/sequences/bookStoreManager/Logout.puml new file mode 100644 index 00000000..ec9f1a9e --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/Logout.puml @@ -0,0 +1,8 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp + +manager -> webapp: Click "Logout" +webapp -> webapp: Clear Session +webapp --> manager: Redirect to Login +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/addProduct.PNG b/docs/diagrams/sequences/bookStoreManager/addProduct.PNG new file mode 100644 index 00000000..68f42fad Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/addProduct.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/addProduct.puml b/docs/diagrams/sequences/bookStoreManager/addProduct.puml new file mode 100644 index 00000000..51c0a270 --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/addProduct.puml @@ -0,0 +1,12 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Click "Add Product" +webapp -> manager: Display Product Form +manager -> webapp: Fill Product Details +webapp -> db: Add Product to Database +db --> webapp: Product Added +webapp --> manager: Product Added/Error +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/disableProduct.PNG b/docs/diagrams/sequences/bookStoreManager/disableProduct.PNG new file mode 100644 index 00000000..b8e96597 Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/disableProduct.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/disableProduct.puml b/docs/diagrams/sequences/bookStoreManager/disableProduct.puml new file mode 100644 index 00000000..ecc62fc4 --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/disableProduct.puml @@ -0,0 +1,14 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Click Product Row +webapp -> db: Fetch Product Details +db --> webapp: Product Data +webapp -> manager: Display Product Details +manager -> webapp: Click "Disable" +webapp -> db: Update Product Disabled Field +db --> webapp: Product Disabled +webapp --> manager: Product Disabled +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/enableProduct.PNG b/docs/diagrams/sequences/bookStoreManager/enableProduct.PNG new file mode 100644 index 00000000..aef19e93 Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/enableProduct.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/enableProduct.puml b/docs/diagrams/sequences/bookStoreManager/enableProduct.puml new file mode 100644 index 00000000..d48b5f1d --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/enableProduct.puml @@ -0,0 +1,14 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Click Product Row +webapp -> db: Fetch Product Details +db --> webapp: Product Data +webapp -> manager: Display Product Details +manager -> webapp: Click "Enable" +webapp -> db: Update Product Enabled Field +db --> webapp: Product Enabled +webapp --> manager: Product Enabled +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/updateProduct.PNG b/docs/diagrams/sequences/bookStoreManager/updateProduct.PNG new file mode 100644 index 00000000..c06d4172 Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/updateProduct.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/updateProduct.puml b/docs/diagrams/sequences/bookStoreManager/updateProduct.puml new file mode 100644 index 00000000..f8ac0af4 --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/updateProduct.puml @@ -0,0 +1,14 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Click Product Row +webapp -> db: Fetch Product Details +db --> webapp: Product Data +webapp -> manager: Display Product Details +manager -> webapp: Update Product Details +webapp -> db: Update Product in Database +db --> webapp: Product Updated +webapp --> manager: Product Updated/Error +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/viewOrders.PNG b/docs/diagrams/sequences/bookStoreManager/viewOrders.PNG new file mode 100644 index 00000000..7503fb23 Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/viewOrders.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/viewOrders.puml b/docs/diagrams/sequences/bookStoreManager/viewOrders.puml new file mode 100644 index 00000000..6affe7e8 --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/viewOrders.puml @@ -0,0 +1,10 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Click "Orders" +webapp -> db: Fetch All Orders +db --> webapp: Order Data +webapp --> manager: Display Order List +@enduml diff --git a/docs/diagrams/sequences/bookStoreManager/viewProducts.PNG b/docs/diagrams/sequences/bookStoreManager/viewProducts.PNG new file mode 100644 index 00000000..aeed6ac4 Binary files /dev/null and b/docs/diagrams/sequences/bookStoreManager/viewProducts.PNG differ diff --git a/docs/diagrams/sequences/bookStoreManager/viewProducts.puml b/docs/diagrams/sequences/bookStoreManager/viewProducts.puml new file mode 100644 index 00000000..9d04b467 --- /dev/null +++ b/docs/diagrams/sequences/bookStoreManager/viewProducts.puml @@ -0,0 +1,10 @@ +@startuml +actor "Bookstore Manager" as manager +participant "Web Application" as webapp +database "Database" as db + +manager -> webapp: Click "All Products" +webapp -> db: Fetch All Products +db --> webapp: Product Data +webapp --> manager: Display Product List +@enduml diff --git a/docs/diagrams/sequences/customer/Logout.png b/docs/diagrams/sequences/customer/Logout.png new file mode 100644 index 00000000..065771a6 Binary files /dev/null and b/docs/diagrams/sequences/customer/Logout.png differ diff --git a/docs/diagrams/sequences/customer/Logout.puml b/docs/diagrams/sequences/customer/Logout.puml new file mode 100644 index 00000000..e3fba419 --- /dev/null +++ b/docs/diagrams/sequences/customer/Logout.puml @@ -0,0 +1,8 @@ +@startuml +actor "Customer" as customer +participant "Web Application" as webapp + +customer -> webapp: Click "Logout" +webapp -> webapp: Clear Session +webapp --> customer: Redirect to Login +@enduml diff --git a/docs/diagrams/sequences/customer/PurchaseCart.png b/docs/diagrams/sequences/customer/PurchaseCart.png new file mode 100644 index 00000000..2b7d78ab Binary files /dev/null and b/docs/diagrams/sequences/customer/PurchaseCart.png differ diff --git a/docs/diagrams/sequences/customer/PurchaseCart.puml b/docs/diagrams/sequences/customer/PurchaseCart.puml new file mode 100644 index 00000000..fffbe7bc --- /dev/null +++ b/docs/diagrams/sequences/customer/PurchaseCart.puml @@ -0,0 +1,19 @@ +@startuml + +actor Customer +participant "Bookstore Website" as Website +database "Customer Database" as db +control "PurchaseCart" as PurchaseCart + +Customer -> Website: Browse books +Website -> Customer: Display available books +Customer -> Website: Choose books to purchase +Website -> Customer: Display cart and total +Customer -> Website: Proceed to checkout +Website -> PurchaseCart: Create order +PurchaseCart -> db: Update inventory +db --> PurchaseCart: Inventory updated +PurchaseCart --> Website: Order confirmation +Website --> Customer: Order confirmation message + +@enduml diff --git a/docs/diagrams/sequences/customer/SearchItems.png b/docs/diagrams/sequences/customer/SearchItems.png new file mode 100644 index 00000000..860e27ea Binary files /dev/null and b/docs/diagrams/sequences/customer/SearchItems.png differ diff --git a/docs/diagrams/sequences/customer/SearchItems.puml b/docs/diagrams/sequences/customer/SearchItems.puml new file mode 100644 index 00000000..f88aef95 --- /dev/null +++ b/docs/diagrams/sequences/customer/SearchItems.puml @@ -0,0 +1,12 @@ +@startuml +actor Customer +participant "Bookstore Website" as Website +database "Customer Database" as db +control "SearchItem" as SearchItem + +Website -> Customer: Display search form +Customer -> Website: Enter search keywords +Website -> SearchItem: Perform search +SearchItem --> Website: Return search results +Website -> Customer: Display search results +@enduml diff --git a/docs/diagrams/sequences/customer/ViewCart.png b/docs/diagrams/sequences/customer/ViewCart.png new file mode 100644 index 00000000..89f07ec3 Binary files /dev/null and b/docs/diagrams/sequences/customer/ViewCart.png differ diff --git a/docs/diagrams/sequences/customer/ViewCart.puml b/docs/diagrams/sequences/customer/ViewCart.puml new file mode 100644 index 00000000..04100b7c --- /dev/null +++ b/docs/diagrams/sequences/customer/ViewCart.puml @@ -0,0 +1,11 @@ +@startuml +actor "Customer" as customer +participant "Web Application" as webapp +database "Database" as db + + +customer -> webapp: Click "Cart" +webapp -> db: Fetch Shopping Cart +db --> webapp: Shopping Cart Data +webapp --> customer: Display Shopping Cart +@enduml diff --git a/docs/diagrams/sequences/customer/ViewOrders.png b/docs/diagrams/sequences/customer/ViewOrders.png new file mode 100644 index 00000000..64667444 Binary files /dev/null and b/docs/diagrams/sequences/customer/ViewOrders.png differ diff --git a/docs/diagrams/sequences/customer/ViewOrders.puml b/docs/diagrams/sequences/customer/ViewOrders.puml new file mode 100644 index 00000000..49f3ef0d --- /dev/null +++ b/docs/diagrams/sequences/customer/ViewOrders.puml @@ -0,0 +1,11 @@ +@startuml +actor "Customer" as customer +participant "Web Application" as webapp +database "Database" as db + + +customer -> webapp: Click "Orders" +webapp -> db: Fetch Previous Orders +db --> webapp: Order Data +webapp --> customer: Display Orders +@enduml diff --git a/docs/diagrams/sequences/customer/ViewProfile.png b/docs/diagrams/sequences/customer/ViewProfile.png new file mode 100644 index 00000000..29121dc9 Binary files /dev/null and b/docs/diagrams/sequences/customer/ViewProfile.png differ diff --git a/docs/diagrams/sequences/customer/ViewProfile.puml b/docs/diagrams/sequences/customer/ViewProfile.puml new file mode 100644 index 00000000..a3b71cbe --- /dev/null +++ b/docs/diagrams/sequences/customer/ViewProfile.puml @@ -0,0 +1,11 @@ +@startuml +actor "Customer" as customer +participant "Web Application" as webapp +database "Database" as db + + +customer -> webapp: Click "Profile" +webapp -> db: Fetch Profile Information +db --> webapp: Order Profile Data +webapp --> customer: Display Profile Information +@enduml diff --git a/docs/diagrams/sequences/customer/ViewWishlist.png b/docs/diagrams/sequences/customer/ViewWishlist.png new file mode 100644 index 00000000..1b5cda71 Binary files /dev/null and b/docs/diagrams/sequences/customer/ViewWishlist.png differ diff --git a/docs/diagrams/sequences/customer/ViewWishlist.puml b/docs/diagrams/sequences/customer/ViewWishlist.puml new file mode 100644 index 00000000..7392960d --- /dev/null +++ b/docs/diagrams/sequences/customer/ViewWishlist.puml @@ -0,0 +1,16 @@ +@startuml +actor "Customer" as customer +participant "Web Application" as webapp +database "Database" as db + +customer -> Website: Log in +Website -> customer: Prompt for login credentials +customer -> Website: Provide login credentials +Website -> db: Validate credentials +db --> Website: Confirmation +customer -> webapp: Click "Wishlist" +Website --> customer: Redirect to Wishlist page +Website -> db: Retrieve Wishlist items +db --> Website: Wishlist items +Website --> customer: Display Wishlist +@enduml diff --git a/docs/diagrams/sequences/customer/authentication.auth_tools.png b/docs/diagrams/sequences/customer/authentication.auth_tools.png new file mode 100644 index 00000000..d7030c6b Binary files /dev/null and b/docs/diagrams/sequences/customer/authentication.auth_tools.png differ diff --git a/docs/diagrams/sequences/customer/authentication.auth_tools.puml b/docs/diagrams/sequences/customer/authentication.auth_tools.puml new file mode 100644 index 00000000..95ba267d --- /dev/null +++ b/docs/diagrams/sequences/customer/authentication.auth_tools.puml @@ -0,0 +1,18 @@ +@startuml +actor Customer +participant "Bookstore Website" as Website +database "Customer Database" as db +participant "authentication.auth_tools" as AuthTools + +Customer -> Website: Log in +Website -> Customer: Prompt for login credentials +Customer -> Website: Provide login credentials +Website -> AuthTools: Request authentication +AuthTools -> db: Retrieve customer data +db --> AuthTools: Customer data +AuthTools --> Website: Authentication result +Website --> Customer: Redirect to Profile page +Website -> db: Retrieve customer profile data +db --> Website: Profile data +Website --> Customer: Display Profile +@enduml diff --git a/docs/diagrams/sequences/customer/login.png b/docs/diagrams/sequences/customer/login.png new file mode 100644 index 00000000..8332abc1 Binary files /dev/null and b/docs/diagrams/sequences/customer/login.png differ diff --git a/docs/diagrams/sequences/customer/login.puml b/docs/diagrams/sequences/customer/login.puml new file mode 100644 index 00000000..6ddb8666 --- /dev/null +++ b/docs/diagrams/sequences/customer/login.puml @@ -0,0 +1,18 @@ +@startuml login +actor Customer +boundary "System" as system +control "Sessions" as sessions +control "Authentication" as auth +Customer -> system: Login +activate system +system -> auth: login_pipeline(username, password) +activate auth +auth --> system: True +deactivate auth +system -> sessions: add_new_session(username, db) +activate sessions +sessions --> system: None +deactivate sessions +system -> system: redirect to home page +deactivate system +@enduml diff --git a/docs/diagrams/sequences/customer/register.png b/docs/diagrams/sequences/customer/register.png new file mode 100644 index 00000000..ff8555cf Binary files /dev/null and b/docs/diagrams/sequences/customer/register.png differ diff --git a/docs/diagrams/sequences/customer/register.puml b/docs/diagrams/sequences/customer/register.puml new file mode 100644 index 00000000..68680e6b --- /dev/null +++ b/docs/diagrams/sequences/customer/register.puml @@ -0,0 +1,12 @@ +@startuml +actor Customer +participant "Bookstore Website" as Website +database "Customer Database" as db + +Customer -> Website: Go to registration page +Website -> Customer: Display registration form +Customer -> Website: Fill in registration details +Website -> db: Save customer details +db --> Website: Confirmation +Website --> Customer: Display confirmation message +@enduml diff --git a/docs/diagrams/system/authentication_packages_updated.png b/docs/diagrams/system/authentication_packages_updated.png new file mode 100644 index 00000000..ef6b7ace Binary files /dev/null and b/docs/diagrams/system/authentication_packages_updated.png differ diff --git a/docs/diagrams/system/authentication_packages_updated.puml b/docs/diagrams/system/authentication_packages_updated.puml new file mode 100644 index 00000000..749d7888 --- /dev/null +++ b/docs/diagrams/system/authentication_packages_updated.puml @@ -0,0 +1,25 @@ +@startuml packages +set namespaceSeparator none + +package "database" as database #lightblue { + rectangle "wishlist" as wishlist #lightgreen + rectangle "favourite" as favourite #lightgreen +} + +package "authentication" as authentication #aliceblue { + rectangle "User Login" as login #lightgreen + rectangle "User Registration" as register #lightgreen +} + +package "authentication.auth_tools" as authentication.auth_tools #aliceblue { + rectangle "Logout" as logout #lightgreen + rectangle "Email confirmation" as email #lightgreen + rectangle "Change password" as password #lightgreen + rectangle "Change username" as username #lightgreen +} + +register --> login : Register an account +login --> database : Database Storage for accounts +login --> authentication.auth_tools : Tools for accounts + +@enduml \ No newline at end of file diff --git a/docs/diagrams/system/core_classes copy.puml b/docs/diagrams/system/core_classes copy.puml new file mode 100644 index 00000000..71ea962a --- /dev/null +++ b/docs/diagrams/system/core_classes copy.puml @@ -0,0 +1,125 @@ +@startuml classes +set namespaceSeparator none +class "core.session.Sessions" as core.session.Sessions #aliceblue { + sessions : dict + add_new_session(username: str, db: Database) -> None + get_all_sessions() -> dict + get_session(username: str) -> UserSession + remove_session(username: str) -> None +} +class "core.session.UserSession" as core.session.UserSession #aliceblue { + cart : dict + date : NoneType + db + total_cost : int + username : str + add_new_item(id: str, name: str, price: int, quantity: int, discount: float, tax_rate: float) -> None + empty_cart() -> dict + is_item_in_cart(id: str) -> bool + remove_item(id: str) -> None + submit_cart() -> None + update_item_quantity(id: str, change_to_quantity: int) -> None + update_total_cost() -> None +} +class "database.db.Database" as database.db.Database #antiquewhite { + connection + cursor + database_path : str + get_all_item_ids() + get_all_user_information() + get_cost_by_sale_id(sale_id: int) + get_email_by_username(username: str) + get_first_name_by_username(username: str) + get_full_inventory() + get_full_sale_by_id(sale_id: int) + get_full_sales_information() + get_item_category_by_id(item_id: int) + get_item_id_by_sale_id(sale_id: int) + get_item_image_url_by_id(item_id: int) + get_item_info_by_id(item_id: int) + get_item_name_by_id(item_id: int) + get_item_price_by_id(item_id: int) + get_item_stock_by_id(item_id: int) + get_last_name_by_username(username: str) + get_password_hash_by_username(username: str) + get_quantity_by_sale_id(sale_id: int) + get_sale_date_by_sale_id(sale_id: int) + get_sales_by_cost_range(start_cost: float, end_cost: float) + get_sales_by_date_range(start_date: dt.date, end_date: dt.date) + get_sales_by_item_id(item_id: int) + get_sales_by_quantity_range(start_quantity: int, end_quantity: int) + get_sales_by_transaction_id(transaction_id: int) + get_sales_by_username(username: str) + get_transaction_id_by_sale_id(sale_id: int) + get_username_by_sale_id(sale_id: int) + insert_new_item(item_name: str, price: int, info: str) -> None + insert_new_sale(transaction_id: int, username: str, item_id: int, quantity: int, sale_date: dt.date, cost: float) + insert_user(username: str, password_hash: str, email: str, first_name: str, last_name: str) -> None + set_email(username: str, new_email: str) + set_first_name(username: str, new_first_name: str) + set_item_category(item_id: int, new_category: str) + set_item_image_url(item_id: int, new_image_url: str) + set_item_info(item_id: int, new_info: str) + set_item_name(item_id: int, new_name: str) + set_item_price(item_id: int, new_price: float) + set_item_stock(item_id: int, new_stock: int) + set_last_name(username: str, new_last_name: str) + set_password_hash(username: str, new_password_hash: str) + set_sale_cost(sale_id: int, discount: float, tax: float) + set_sale_date(sale_id: int, new_sale_date: dt.date) + set_sale_item_id(sale_id: int, new_item_id: int) + set_sale_quantity(sale_id: int, new_quantity: int) + set_sale_transaction_id(sale_id: int, new_transaction_id: int) + set_sale_username(sale_id: int, new_username: str) +} +class "datetime.date" as datetime.date #grey { + day + month + year + ctime() + fromisocalendar(year, week, day) + fromisoformat(date_string) + fromordinal(n) + fromtimestamp(t) + isocalendar() + isoformat() + isoweekday() + replace(year, month, day) + strftime(fmt) + timetuple() + today() + toordinal() + weekday() +} +class "datetime.datetime" as datetime.datetime #grey { + fold + hour + microsecond + minute + second + tzinfo + astimezone(tz) + combine(date, time, tzinfo) + ctime() + date() + dst() + fromisoformat(date_string) + fromtimestamp(t, tz) + isoformat(sep, timespec) + now(tz) + replace(year, month, day, hour, minute, second, microsecond, tzinfo) + strptime(date_string, format) + time() + timestamp() + timetuple() + timetz() + tzname() + utcfromtimestamp(t) + utcnow() + utcoffset() + utctimetuple() +} +datetime.datetime --|> datetime.date +datetime.datetime --* core.session.UserSession : date +database.db.Database --o core.session.UserSession : db +@enduml diff --git a/docs/diagrams/system/core_classes_updated.png b/docs/diagrams/system/core_classes_updated.png new file mode 100644 index 00000000..aefd450f Binary files /dev/null and b/docs/diagrams/system/core_classes_updated.png differ diff --git a/docs/diagrams/system/core_classes.puml b/docs/diagrams/system/core_classes_updated.puml similarity index 100% rename from docs/diagrams/system/core_classes.puml rename to docs/diagrams/system/core_classes_updated.puml diff --git a/docs/diagrams/system/database_classes_updated.png b/docs/diagrams/system/database_classes_updated.png new file mode 100644 index 00000000..8fc85463 Binary files /dev/null and b/docs/diagrams/system/database_classes_updated.png differ diff --git a/docs/diagrams/system/database_classes_updated.puml b/docs/diagrams/system/database_classes_updated.puml new file mode 100644 index 00000000..2841ce26 --- /dev/null +++ b/docs/diagrams/system/database_classes_updated.puml @@ -0,0 +1,66 @@ +@startuml classes +set namespaceSeparator none +class "Database" as database.db.Database #aliceblue { + connection + cursor + database_path : str + get_all_user_information() + get_all_item_ids() + get_full_inventory() + get_full_sales_information() + } + +class "Customers" as customers { + insert_user(username: str, password_hash: str, email: str, first_name: str, last_name: str) -> None + set_email(username: str, new_email: str) + set_first_name(username: str, new_first_name: str) + set_last_name(username: str, new_last_name: str) + set_password_hash(username: str, new_password_hash: str) + get_email_by_username(username: str) + get_first_name_by_username(username: str) + get_last_name_by_username(username: str) + get_password_hash_by_username(username: str) + get_sales_by_username(username: str) +} + +class "Sales" as sales { + insert_new_sale(transaction_id: int, username: str, item_id: int, quantity: int, sale_date: dt.date, cost: float) + get_cost_by_sale_id(sale_id: int) + get_full_sale_by_id(sale_id: int) + get_quantity_by_sale_id(sale_id: int) + get_sale_date_by_sale_id(sale_id: int) + get_sales_by_cost_range(start_cost: float, end_cost: float) + get_sales_by_date_range(start_date: dt.date, end_date: dt.date) + get_sales_by_item_id(item_id: int) + get_sales_by_quantity_range(start_quantity: int, end_quantity: int) + get_sales_by_transaction_id(transaction_id: int) + get_transaction_id_by_sale_id(sale_id: int) + get_username_by_sale_id(sale_id: int) + set_sale_cost(sale_id: int, discount: float, tax: float) + set_sale_date(sale_id: int, new_sale_date: dt.date) + set_sale_item_id(sale_id: int, new_item_id: int) + set_sale_quantity(sale_id: int, new_quantity: int) + set_sale_transaction_id(sale_id: int, new_transaction_id: int) + set_sale_username(sale_id: int, new_username: str) +} + +class "Items" as item { + insert_new_item(item_name: str, price: int, info: str) -> None + get_item_category_by_id(item_id: int) + get_item_id_by_sale_id(sale_id: int) + get_item_image_url_by_id(item_id: int) + get_item_info_by_id(item_id: int) + get_item_name_by_id(item_id: int) + get_item_price_by_id(item_id: int) + get_item_stock_by_id(item_id: int) + set_item_category(item_id: int, new_category: str) + set_item_image_url(item_id: int, new_image_url: str) + set_item_info(item_id: int, new_info: str) + set_item_name(item_id: int, new_name: str) + set_item_price(item_id: int, new_price: float) + set_item_stock(item_id: int, new_stock: int) +} +database.db.Database --> customers +database.db.Database --> sales +database.db.Database --> item +@enduml diff --git a/docs/diagrams/system/database_packages_updated.png b/docs/diagrams/system/database_packages_updated.png new file mode 100644 index 00000000..d512bef1 Binary files /dev/null and b/docs/diagrams/system/database_packages_updated.png differ diff --git a/docs/diagrams/system/database_packages_updated.puml b/docs/diagrams/system/database_packages_updated.puml new file mode 100644 index 00000000..94fa2010 --- /dev/null +++ b/docs/diagrams/system/database_packages_updated.puml @@ -0,0 +1,25 @@ +@startuml packages +set namespaceSeparator none + +package "database" as database #aliceblue { + package "database.db" as db #aliceblue { + rectangle "User Accounts" as users #lightgray + rectangle "Inventory" as inventory #lightgray + rectangle "Sales" as cart #lightgray + note "Wishlist" as wishlist #lightgray + note "Information" as info #lightgray + } + + + package "database.reset_database" as reset_database #aliceblue { + rectangle "Remove Database" as remove #lightgray + rectangle "Create Database" as create #lightgray + database "Starting Data" as starting #lightgray + } +} +remove --> db +starting --> db +create --> starting +users --> wishlist +users --> info +@enduml diff --git a/docs/diagrams/system/initial diagrams/authentication_packages.png b/docs/diagrams/system/initial diagrams/authentication_packages.png new file mode 100644 index 00000000..c0d45d96 Binary files /dev/null and b/docs/diagrams/system/initial diagrams/authentication_packages.png differ diff --git a/docs/diagrams/system/initial diagrams/authentication_packages.puml b/docs/diagrams/system/initial diagrams/authentication_packages.puml new file mode 100644 index 00000000..bc069e70 --- /dev/null +++ b/docs/diagrams/system/initial diagrams/authentication_packages.puml @@ -0,0 +1,7 @@ +@startuml packages +set namespaceSeparator none +package "authentication" as authentication #aliceblue { +} +package "authentication.auth_tools" as authentication.auth_tools #aliceblue { +} +@enduml diff --git a/docs/diagrams/system/initial diagrams/core_classes.png b/docs/diagrams/system/initial diagrams/core_classes.png new file mode 100644 index 00000000..aefd450f Binary files /dev/null and b/docs/diagrams/system/initial diagrams/core_classes.png differ diff --git a/docs/diagrams/system/initial diagrams/core_classes.puml b/docs/diagrams/system/initial diagrams/core_classes.puml new file mode 100644 index 00000000..71ea962a --- /dev/null +++ b/docs/diagrams/system/initial diagrams/core_classes.puml @@ -0,0 +1,125 @@ +@startuml classes +set namespaceSeparator none +class "core.session.Sessions" as core.session.Sessions #aliceblue { + sessions : dict + add_new_session(username: str, db: Database) -> None + get_all_sessions() -> dict + get_session(username: str) -> UserSession + remove_session(username: str) -> None +} +class "core.session.UserSession" as core.session.UserSession #aliceblue { + cart : dict + date : NoneType + db + total_cost : int + username : str + add_new_item(id: str, name: str, price: int, quantity: int, discount: float, tax_rate: float) -> None + empty_cart() -> dict + is_item_in_cart(id: str) -> bool + remove_item(id: str) -> None + submit_cart() -> None + update_item_quantity(id: str, change_to_quantity: int) -> None + update_total_cost() -> None +} +class "database.db.Database" as database.db.Database #antiquewhite { + connection + cursor + database_path : str + get_all_item_ids() + get_all_user_information() + get_cost_by_sale_id(sale_id: int) + get_email_by_username(username: str) + get_first_name_by_username(username: str) + get_full_inventory() + get_full_sale_by_id(sale_id: int) + get_full_sales_information() + get_item_category_by_id(item_id: int) + get_item_id_by_sale_id(sale_id: int) + get_item_image_url_by_id(item_id: int) + get_item_info_by_id(item_id: int) + get_item_name_by_id(item_id: int) + get_item_price_by_id(item_id: int) + get_item_stock_by_id(item_id: int) + get_last_name_by_username(username: str) + get_password_hash_by_username(username: str) + get_quantity_by_sale_id(sale_id: int) + get_sale_date_by_sale_id(sale_id: int) + get_sales_by_cost_range(start_cost: float, end_cost: float) + get_sales_by_date_range(start_date: dt.date, end_date: dt.date) + get_sales_by_item_id(item_id: int) + get_sales_by_quantity_range(start_quantity: int, end_quantity: int) + get_sales_by_transaction_id(transaction_id: int) + get_sales_by_username(username: str) + get_transaction_id_by_sale_id(sale_id: int) + get_username_by_sale_id(sale_id: int) + insert_new_item(item_name: str, price: int, info: str) -> None + insert_new_sale(transaction_id: int, username: str, item_id: int, quantity: int, sale_date: dt.date, cost: float) + insert_user(username: str, password_hash: str, email: str, first_name: str, last_name: str) -> None + set_email(username: str, new_email: str) + set_first_name(username: str, new_first_name: str) + set_item_category(item_id: int, new_category: str) + set_item_image_url(item_id: int, new_image_url: str) + set_item_info(item_id: int, new_info: str) + set_item_name(item_id: int, new_name: str) + set_item_price(item_id: int, new_price: float) + set_item_stock(item_id: int, new_stock: int) + set_last_name(username: str, new_last_name: str) + set_password_hash(username: str, new_password_hash: str) + set_sale_cost(sale_id: int, discount: float, tax: float) + set_sale_date(sale_id: int, new_sale_date: dt.date) + set_sale_item_id(sale_id: int, new_item_id: int) + set_sale_quantity(sale_id: int, new_quantity: int) + set_sale_transaction_id(sale_id: int, new_transaction_id: int) + set_sale_username(sale_id: int, new_username: str) +} +class "datetime.date" as datetime.date #grey { + day + month + year + ctime() + fromisocalendar(year, week, day) + fromisoformat(date_string) + fromordinal(n) + fromtimestamp(t) + isocalendar() + isoformat() + isoweekday() + replace(year, month, day) + strftime(fmt) + timetuple() + today() + toordinal() + weekday() +} +class "datetime.datetime" as datetime.datetime #grey { + fold + hour + microsecond + minute + second + tzinfo + astimezone(tz) + combine(date, time, tzinfo) + ctime() + date() + dst() + fromisoformat(date_string) + fromtimestamp(t, tz) + isoformat(sep, timespec) + now(tz) + replace(year, month, day, hour, minute, second, microsecond, tzinfo) + strptime(date_string, format) + time() + timestamp() + timetuple() + timetz() + tzname() + utcfromtimestamp(t) + utcnow() + utcoffset() + utctimetuple() +} +datetime.datetime --|> datetime.date +datetime.datetime --* core.session.UserSession : date +database.db.Database --o core.session.UserSession : db +@enduml diff --git a/docs/diagrams/system/initial diagrams/core_packages.png b/docs/diagrams/system/initial diagrams/core_packages.png new file mode 100644 index 00000000..2ff30d2a Binary files /dev/null and b/docs/diagrams/system/initial diagrams/core_packages.png differ diff --git a/docs/diagrams/system/initial diagrams/core_packages.puml b/docs/diagrams/system/initial diagrams/core_packages.puml new file mode 100644 index 00000000..9ffa5cec --- /dev/null +++ b/docs/diagrams/system/initial diagrams/core_packages.puml @@ -0,0 +1,10 @@ +@startuml packages +set namespaceSeparator none +package "core" as core #aliceblue { +} +package "core.session" as core.session #aliceblue { +} +package "core.utils" as core.utils #aliceblue { +} +core.session --> core.utils +@enduml diff --git a/docs/diagrams/system/initial diagrams/database_classes.png b/docs/diagrams/system/initial diagrams/database_classes.png new file mode 100644 index 00000000..50581fd5 Binary files /dev/null and b/docs/diagrams/system/initial diagrams/database_classes.png differ diff --git a/docs/diagrams/system/initial diagrams/database_classes.puml b/docs/diagrams/system/initial diagrams/database_classes.puml new file mode 100644 index 00000000..a90ae0b6 --- /dev/null +++ b/docs/diagrams/system/initial diagrams/database_classes.puml @@ -0,0 +1,54 @@ +@startuml classes +set namespaceSeparator none +class "Database" as database.db.Database #aliceblue { + connection + cursor + database_path : str + get_all_item_ids() + get_all_user_information() + get_cost_by_sale_id(sale_id: int) + get_email_by_username(username: str) + get_first_name_by_username(username: str) + get_full_inventory() + get_full_sale_by_id(sale_id: int) + get_full_sales_information() + get_item_category_by_id(item_id: int) + get_item_id_by_sale_id(sale_id: int) + get_item_image_url_by_id(item_id: int) + get_item_info_by_id(item_id: int) + get_item_name_by_id(item_id: int) + get_item_price_by_id(item_id: int) + get_item_stock_by_id(item_id: int) + get_last_name_by_username(username: str) + get_password_hash_by_username(username: str) + get_quantity_by_sale_id(sale_id: int) + get_sale_date_by_sale_id(sale_id: int) + get_sales_by_cost_range(start_cost: float, end_cost: float) + get_sales_by_date_range(start_date: dt.date, end_date: dt.date) + get_sales_by_item_id(item_id: int) + get_sales_by_quantity_range(start_quantity: int, end_quantity: int) + get_sales_by_transaction_id(transaction_id: int) + get_sales_by_username(username: str) + get_transaction_id_by_sale_id(sale_id: int) + get_username_by_sale_id(sale_id: int) + insert_new_item(item_name: str, price: int, info: str) -> None + insert_new_sale(transaction_id: int, username: str, item_id: int, quantity: int, sale_date: dt.date, cost: float) + insert_user(username: str, password_hash: str, email: str, first_name: str, last_name: str) -> None + set_email(username: str, new_email: str) + set_first_name(username: str, new_first_name: str) + set_item_category(item_id: int, new_category: str) + set_item_image_url(item_id: int, new_image_url: str) + set_item_info(item_id: int, new_info: str) + set_item_name(item_id: int, new_name: str) + set_item_price(item_id: int, new_price: float) + set_item_stock(item_id: int, new_stock: int) + set_last_name(username: str, new_last_name: str) + set_password_hash(username: str, new_password_hash: str) + set_sale_cost(sale_id: int, discount: float, tax: float) + set_sale_date(sale_id: int, new_sale_date: dt.date) + set_sale_item_id(sale_id: int, new_item_id: int) + set_sale_quantity(sale_id: int, new_quantity: int) + set_sale_transaction_id(sale_id: int, new_transaction_id: int) + set_sale_username(sale_id: int, new_username: str) +} +@enduml diff --git a/docs/diagrams/system/initial diagrams/database_packages.png b/docs/diagrams/system/initial diagrams/database_packages.png new file mode 100644 index 00000000..0550b2e2 Binary files /dev/null and b/docs/diagrams/system/initial diagrams/database_packages.png differ diff --git a/docs/diagrams/system/initial diagrams/database_packages.puml b/docs/diagrams/system/initial diagrams/database_packages.puml new file mode 100644 index 00000000..da26ca36 --- /dev/null +++ b/docs/diagrams/system/initial diagrams/database_packages.puml @@ -0,0 +1,9 @@ +@startuml packages +set namespaceSeparator none +package "database" as database #aliceblue { +} +package "database.db" as database.db #aliceblue { +} +package "database.reset_database" as database.reset_database #aliceblue { +} +@enduml diff --git a/docs/diagrams/system/initial diagrams/testing_packages.png b/docs/diagrams/system/initial diagrams/testing_packages.png new file mode 100644 index 00000000..6447d9e8 Binary files /dev/null and b/docs/diagrams/system/initial diagrams/testing_packages.png differ diff --git a/docs/diagrams/system/initial diagrams/testing_packages.puml b/docs/diagrams/system/initial diagrams/testing_packages.puml new file mode 100644 index 00000000..c4fb2e28 --- /dev/null +++ b/docs/diagrams/system/initial diagrams/testing_packages.puml @@ -0,0 +1,9 @@ +@startuml + +package "Testing" { + [auth_tests.py] + [core_tests.py] + [db_tests.py] +} + +@enduml diff --git a/docs/instructions/p0.md b/docs/instructions/p0.md index 8895ba8b..c371ae83 100644 --- a/docs/instructions/p0.md +++ b/docs/instructions/p0.md @@ -12,18 +12,18 @@ One member of your group should fork the [template repository](https://github.co Replace the following information with your team's information. Remember that two people working on the same file at the same time can cause merge conflicts, so be sure to communicate with your team members to avoid this. Before you make any changes, pull the latest changes from the `main` branch to your local machine. After you make your changes, commit and push them to the `main` branch. Alternatively, one person can make all required changes to this document and then push them to the `main` branch. -- **Team Member Name:** Jane Doe - - **Github Username:** jdoe - - **Niner Net ID:** jdoe -- **Team Member Name:** John Doe - - **Github Username:** jdoe1 - - **Niner Net ID:** jdoe1 -- **Team Member Name:** Tim H - - **Github Username:** electric-sun-20 - - **Niner Net ID:** theideck -- **Team Member Name:** Greg T - - **Github Username:** vville-film-archives - - **Niner Net ID:** gturk +- **Team Member Name:** Alberto Olivi + - **Github Username:** olivialba + - **Niner Net ID:** aolivi +- **Team Member Name:** Abdulrahamn Aljabali + - **Github Username:** aaljabali1 + - **Niner Net ID:** aaljabal +- **Team Member Name:** Alima Conde + - **Github Username:** alimacnde + - **Niner Net ID:** aconde4 +- **Team Member Name:** Jacob George + - **Github Username:** jgeorg24 + - **Niner Net ID:** jgeorg24 ## Task 3: Commit and Push Changes diff --git a/docs/instructions/p1.md b/docs/instructions/p1.md index c090556e..3f68d3dd 100644 --- a/docs/instructions/p1.md +++ b/docs/instructions/p1.md @@ -6,10 +6,10 @@ Fill the document out following the guidelines listed in each section. Maintain ## Group Members -- [Name](mailto:email@uncc.edu) -- [Name](mmailto:email@uncc.edu) -- [Name](mmailto:email@uncc.edu) -- [Name](mmailto:email@uncc.edu) +- [Alberto Olivi](mailto:aolivi@uncc.edu) +- [Jacob George](mailto:jgeorg24@uncc.edu) +- [Abdulrahamn Aljabali](mailto:aaljabal@uncc.edu) +- [Alima Conde](mailto:aconde4@uncc.edu) ## Revisions @@ -18,6 +18,13 @@ When a change is made to the document, a new revision should be created. The rev | Version | Date | Description | Author | Reviewed By | | --- | --- | --- | --- | --- | | 1.0 | 03/22/23 | Initial draft | [David Gary](mailto:dgary9@uncc.edu) | [David Gary](mailto:dgary@uncc.edu) | +| 1.1 | 07/17/23 | AO added: Requirements, Constraints, Use cases, User stories, Glossary | [Alberto Olivi](mailto:aolivi@uncc.edu) | [Alberto Olivi](mailto:aolivi@uncc.edu) | +| 1.2 | 07/17/23 | JG added: Requirements, Constraints, Use cases, User Stories, Glossary | [Jacob George](mailto:jgeorg24@uncc.edu) | [Jacob George](mailto:jgeorg24@uncc.edu) | +| 1.3 | 07/17/23 | AA added: Requirements, Constraints, Use cases, User Stories, Glossary | [Abdulrahamn Aljabali](mailto:aaljabal@uncc.edu) | [Abdulrahamn Aljabali](mailto:aaljabal@uncc.edu) | +| 1.4 | 07/17/23 | AC added: Requirements, Constraints | [Alima Conde](mailto:aconde4@uncc.edu) | [Alima Conde](mailto:aconde4@uncc.edu) | +| 1.5 | 07/19/23 | Fixed some formatting | [Alberto Olivi](mailto:aolivi@uncc.edu) | [Alberto Olivi](mailto:aolivi@uncc.edu) | +| 1.6 | 07/31/23 | Adjusted some features and requirements | [Alberto Olivi](mailto:aolivi@uncc.edu) | [Alberto Olivi](mailto:aolivi@uncc.edu) | + ## Table of Contents @@ -30,7 +37,7 @@ When a change is made to the document, a new revision should be created. The rev ## Introduction -In this section, you should give a brief overview of what your project will be. Describe the software system you are building and what problems it solves. You should also give a short description of the stakeholders (users of the system) and what their needs are. There is no set formatting requirement, but you should maintain a consistent structure across future sections. Not all members must contribute to this section. +This project's objective is to develop an easy-to-use online store using a combination of Python, CSS, and HTML. Our goal is to provide a superior electronic shopping experience by showcasing a diverse selection of meticulously curated high-quality products, with a particular focus on the tech field. We are hoping to integrate many useful as well as eye catching elements for the store to set us apart from the competition. ## Requirements @@ -49,10 +56,110 @@ Each group member must supply at least three functional requirements for the pro - **Rationale:** A short description of why the requirement is important. This should be a single sentence that describes why the requirement is important. - **Testing:** A short description of how the requirement can be tested. This should be a single sentence that describes how the requirement can be tested. +- **REQ-1:** (AO) + - **Description:** The online store should display a website logo that redirects to the site's homepage. + - **Type:** Functional + - **Priority:** 3 + - **Rationale:** This function allows for easy navigation, and easy access to the homepage for customers. + - **Testing:** Can be tested by clicking on the website logo and that it redirects the user to the homepage everytime. + +- **REQ-2:** (AO) + - **Description:** The store should allow customers to make their personal accounts. + - **Type:** Functional + - **Priority:** 1 + - **Rationale:** Personal accounts allow to save information about a specific customer, like orders, wishlist, or payment methods. + - **Testing:** Test that accounts can be successfully created and they can store the customer's information and orders. + +- **REQ-3:** (AO) + - **Description:** The customers should be able to create a favorite / wishlist to save products they want. + - **Type:** Functional + - **Priority:** 2 + - **Rationale:** Easy way for customers to track items that they want and may buy in the future. + - **Testing:** Test that items can be saved to the wishlist and can be accessed again later. + +- **REQ-4:** (JG) + - **Description:** The customers should be able to add products to a shopping cart and view the contents of the cart. + - **Type:** Functional + - **Priority:** 2 + - **Rationale:** Allows users to select products for purchase and review their selections before proceeding to checkout. + - **Testing:** Test by adding products to the cart and verifying that the products are added to the cart. + +- **REQ-5:** (JG) + - **Description:** The online store should have an admin panel to manage the database info. + - **Type:** Functional + - **Priority:** 1 + - **Rationale:** Easy way for managers and admin to check and manage the database if some changes are needed. + - **Testing:** Test to see if the admin panel is present and its function works. + +- **REQ-6:** (JG) + - **Description:** The customers can checkout products but needs to login in to buy them. + - **Type:** Functional + - **Priority:** 1 + - **Rationale:** Customers can see if there is something that they want to buy before making an account. + - **Testing:** Test if it's possible to checkout without being logged in an account. + +- **REQ-7:** (AA) + - **Description:** The store must be able to display thew items neatly showing a picture of the item with it's name and price + - **Type:** Functional & visual + - **Priority:** 1 + - **Rationale:** Having neat and visually appealing items displayed is a bonus for the website as the customers can see the products + directly. + - **Testing:** Create some items, and when checking the items page see how they display. + +- **REQ-8:** (AA) + - **Description:** Create a database system that stores all information of the store including but not limited to: customers, items, orders. + - **Type:** Functional + - **Priority:** 1 + - **Rationale:** the database is a must in order to save all the information and be able to provide updated information of the online store as that information is constantly being modified and viewed. + - **Testing:** load the database with some basic data and interact with every component (functions) of the online store and see if database is updating accordingly or is missing parameters. + +- **REQ-9:** (AA) + - **Description:** The website must keep track of number of items in stock and mention when a product is out of stock when quantity reaches zero. + - **Priority:** 2 + - **Rationale:** If a product is out of stock it should be made apparent to the customer that the item is no longer available. + - **Testing:** Set item stock to 0 and see if it updates accordingly when displaying products. + +- **REQ-10:** (AC) + - **Description:** A function that will display the customer's total amount in dollars. + - **Type:** Functional + - **Priority:** 1 + - **Rationale:** Allows payments to be accepted and recieve correct amount + - **Testing:** Can be tested by adding items and checking out + +- **REQ-11:** (AC) + - **Description:** A features that allows admins to update a product information + - **Type:** Functional + - **Priority:** 3 + - **Rationale:** Allows admins to update a product price or information. + - **Testing:** Can be tested by seeing if the feature correctly changes the product info in the database and the changes are displayed on the website. + +- **REQ-12:** (AC) + - **Description:** A feature that will allow admin to add product to the database. + - **Type:** Functional + - **Priority:** 2 + - **Rationale:** Allows for easy and quick way to add products. + - **Testing:** Can be tested by adding multiple product and see if they display and are able to be bought. + ## Constraints In this section, you should list any constraints that you have for the project. Each group member must supply at least two constraints. These can be constraints on the project itself, the software system, or the stakeholders. Constraints can be anything that limits the scope of the project. For example, that this project's template code is written using Flask and Python constitutes a constraint on the backend of the project. Constraints can also be things like the required timeline of the project. Be creative. +- **1**: Resource Constraint (AO): The project team consists of four members, limiting the amount of work that can to be done to complete the project before the deadline. All communication and coordination is conducted online, which can provide an additional constraint in teamwork. + +- **2**: Programming Knowledge Constraint (AO): The team may not have all the required or needed knowledge to successfully complete the entire project, or fix every problem/bug that we may find; possibly resulting in additional challenges that will take up more of our time. + +- **3**: Language Constraint (JG): Python, CSS, and HTML must be the only programming languages and frameworks used in the development of the online store. + +- **4**: Time Constraint Constraint (JG): The project has a strict deadline, which limits the available time for development, testing. + +- **5**: Uniquness constraint (AA): The project's topic is derived from a set of templates that every group is going to put to use, so having a truly unique project, visuals and functionalities in order to set us apart from other teams is going to take a lot of creativity. + +- **6**: Lack of functionalities constraint (AA): due to the team's lack of professional programming expertise, it might be hard to come up with functionalities that you might expect to have in a regular online shop such as automated payments, advanced visuals and other advanced functionalities which makes other functionalities kind of bland and more criticised. + +- **7**: Time Constraint (AC): Unlike Spring and Fall semesters, this summer course is fast-paced and there's not much time to spend doing research and planning out ideas. We just have to kind of start and work with the limited time we all have. + +- **8**: Software/Platform Constraint (AC): Must be available to all users on various devices and web browers. For example, we must adhere to the many screen resolutions that exist. + ## Use Cases In this section, you should list use cases for the project. Use cases are a thorough description of how the system will be used. Each group member must supply at least two use cases. Each use case should be written in the following format: @@ -63,6 +170,67 @@ In this section, you should list use cases for the project. Use cases are a thor - **Preconditions:** A list of the preconditions for the use case. This should be a list of the preconditions for the use case, which are the conditions that must be met before the use case can be executed. Continuing with the restaurant example, the customer must have money in their wallet and the cashier must be logged in to the system before the use case of ordering food can be executed. - **Postconditions:** A list of the postconditions for the use case. This should be a list of the postconditions for the use case, which are the conditions that must be met after the use case has been executed. Continuing with the restaurant example, the customer must have their food and the cashier must have the customer's money after the use case of ordering food has been executed. +- **UC-1:** (AO) + - **Description:** The customer register a new account. + - **Actors:** Customer, Registering System + - **Preconditions:** + - The customer must have access to the store website. + - The customer must have an email. + - **Postconditions:** + - The customer will have a registered a new account. + - The customer can log in the system with that new account. + +- **UC-2:** (AO) + - **Description:** The customer search for a product. + - **Actors:** Customer, Search Database + - **Preconditions:** + - The website must be online and available to the customer. + - **Postconditions:** + - The customer will see results that match their search request. + - The customer can click on a product to open the product's page. + +- **UC-3:** (JG) + - **Description:** The customer adds a product to their shopping cart. + - **Actors:** : Customer, Shopping Cart + - **Preconditions:** + - The website must be online and available to the customer. + - The product must be available in the online store. + - **Postconditions:** + - The product is added to the customer's shopping cart. + - The shopping cart updates with the added product and its quantity. + +- **UC-4:** (JG) + - **Description:** The customer completes a purchase. + - **Actors:** Customer, Checkout Function + - **Preconditions:** + - The website must be online and available to the customer. + - The customer must have items in their shopping cart. + - **Postconditions:** + - The payment is processed and confirmed. + - The purchased items are removed from the customer's shopping cart. + +- **UC-5:** (AA) + - **Description:** The customer login into their account. + - **Actors:** Customer, login System + - **Preconditions:** + - The customer must have access to the store website. + - The customer must have an already registered account. + - the customer must enter the correct username and password. + - **Postconditions:** + - The customer will now have access to their account on the system. + - The customer can view their favorties, wishlist, and order history. + +- **UC-6:** (AA) + - **Description:** The administrator (user still unclear) adds a new product. + - **Actors:** admin, admin page + - **Preconditions:** + - The adminstrator must have an account in the database. + - The user must be logged in as an administrator. + - **Postconditions:** + - A newly added product will be available in the store. + - the new product is registred in the database. + + ## User Stories In this section, you should list user stories for the project. User stories are a short description of how a user will be interacting with the system. Each group member must supply at least two user stories. Each user story should be written in the following format: @@ -71,6 +239,30 @@ In this section, you should list user stories for the project. User stories are - **Type of User:** The type of user that the user story is for. This should be a single word that describes the type of user. For example, a user story for a customer might be `Customer` and a user story for an administrator might be `Admin`. - **Description:** A description of the user story that gives a narrative from that user's perspective. This can be any length, but it must paint the picture of what the user wants to do, how they intend to do it, why they want to, and what they expect to happen. +- **US-1:** (AO) + - **Type of User:** Customer + - **Description:** As a customer, I want to be able to search directly for a product without having to register an account. I want to be able to go the store, look into the category that I want and search for any products that matches my description. + +- **US-2:** (AO) + - **Type of User:** Administrator + - **Description:** As an administrator, I want to be able to go into the site database to see and track the inventory, as well as any product that was sold, while also being able to sort the inventory alphabetically, by price, or date sold. + +- **US-3:** (JG) + - **Type of User:** Customer + - **Description:** As a customer, I want to be able to complete a purchase as a guest without the need to create an account, so that I can make a quick purchase without providing additional personal information. + +- **US-4:** (JG) + - **Type of User:** Administrator + - **Description:** As an administrator, I want to be able to manage customer orders, including checking order data and changing order statuses. + +- **US-5:** (AA) + - **Type of User:** customer + - **Description:** As a customer i want to be able to log into the store system and access my past orders as well as my favorite items. + +- **US-6:** (AA) + - **Type of User:** Administrator + - **Description:** As an administrator, I want to be able to add new products and be able to edit their description, quantity or update their images. + ## Glossary In this section, you should list any terms that are used in the document that may not be immediately obvious to a naive reader. Each group member must supply at least one term. Each term should be written in the following format: @@ -78,6 +270,15 @@ In this section, you should list any terms that are used in the document that ma - **Term:** The term that is being defined. This should be a single word or phrase that is being defined. - **Definition:** A definition of the term. This should be a short description of the term that is being defined. This should be a single sentence that describes the term. +- **Term:** Database (AO) + - **Definition:** A collection of structured information or data that is stored electronically and can be easily accessed and managed. Here, it's used to store products, orders and user's information. + +- **Term:** Function (JG) + - **Definition:** A block of code that performs a specific task when called, taking in inputs and producing outputs. In this example it could be multiple things including the checkout function that allows customers to make a purchase with the input being the items in the shopping cart. + +- **Term:** constraint (AA) + - **Definition:** a constraint is a limitation or restriction to be able to do or achieve something. + Try to only list terms that a naive user would not understand. ## Submission Details diff --git a/static/books/a_little_life.jpg b/static/books/a_little_life.jpg new file mode 100644 index 00000000..1a953a52 Binary files /dev/null and b/static/books/a_little_life.jpg differ diff --git a/static/books/american_prometheus.jpg b/static/books/american_prometheus.jpg new file mode 100644 index 00000000..7199282a Binary files /dev/null and b/static/books/american_prometheus.jpg differ diff --git a/static/books/archers_voice.jpg b/static/books/archers_voice.jpg new file mode 100644 index 00000000..6d5bc78b Binary files /dev/null and b/static/books/archers_voice.jpg differ diff --git a/static/books/house_of_earth_and_blood.jpg b/static/books/house_of_earth_and_blood.jpg new file mode 100644 index 00000000..362a3b7d Binary files /dev/null and b/static/books/house_of_earth_and_blood.jpg differ diff --git a/static/css/admin_panel.css b/static/css/admin_panel.css new file mode 100644 index 00000000..e0000da4 --- /dev/null +++ b/static/css/admin_panel.css @@ -0,0 +1,85 @@ +/* + By Alberto Olivi +*/ +#admin_panel_button { + background-color: #375efd; + border-color: #ccc; + color: rgb(236, 236, 236); + font-weight: bold; + margin-top: 10px; + margin-bottom: 20px; +} + +.admin_panel_menu { + margin: 0 auto; + margin-top: 35px; + margin-bottom: 35px; + padding-top: 20px; + padding-bottom: 10px; + width: 500px; + background-color: #f5f5f5; + border: 1px solid #d8d6d6; + text-align: center; +} + +#admin_info { + margin-top: 20px; +} + +#admin_warning { + width: 60%; + margin: 0 auto; +} + +#admin_text { + width: 200px; + padding: 10px; + margin: 8px 0 auto; + display: inline-block; + border: 1px solid #ccc; + box-sizing: border-box; + resize: both; + max-width: 400px; + margin: 0 auto; +} + +#admin_button { + background-color: #4963f4; + border-color: #ccc; + color: rgb(236, 236, 236); + font-weight: bold; + margin-top: 25px; + margin-bottom: 10px; +} + +.navbar { + list-style: none; + padding: 0; + margin: 0; + text-align: center; +} + +.navbar a { + display: block; + padding: 12px 20px; + text-decoration: none; + color: #333; + font-weight: bold; + transition: background-color 0.3s ease; + border-bottom: 1px solid #ccc; +} + +.navbar a:hover { + background-color: #f2f2f2; +} + +.inventory_table { + width: 90%; + margin: 0 auto; + margin-bottom: 20px; +} + +.inventory_table tr, .inventory_table td, .inventory_table th { + padding: 8px 10px; + border: 1px solid #ddd; +} \ No newline at end of file diff --git a/static/css/header.css b/static/css/header.css new file mode 100644 index 00000000..2e9012a5 --- /dev/null +++ b/static/css/header.css @@ -0,0 +1,90 @@ +.header1 { + display: flex; + flex-direction: column; + background-color:#f5f5f5; + justify-content: center; + align-items: center; + position: relative; + min-height: 35vh; + font-family: Hack, monospace; + border: 2px solid; + border-right: inset gray; + border-left: outset gray; + border-radius: 50px 0px 50px 0px; + margin-top:20px; +} + +body{ + background-color: white; + font-family: 'Lato', sans-serif; + margin: 0px; + padding: 0px; + +} + +div { + color: #727272; + text-align: center; +} + +pop { + margin: 16px; + font-size: 96px; + color: #ccc; + text-transform: uppercase; + font-weight: 600; + transition: all 1s ease-in-out; + position: relative; +} + +pop::before { + content: attr(data-item); + transition: all 1s ease-in-out; + color: #8254ff; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 0; + overflow: hidden; +} + +pop:hover::before { + width: 100%; +} + +nav { + margin: 25px; + background: #8254ff; + padding: 0px; +} + +nav .menuItems { + list-style: none; + display: flex; + padding-left: 0; + margin: 0 60px; +} + +nav .menuItems li { + padding: 30px; +} + +nav .menuItems space { + padding: 30px; +} + +nav .menuItems li a { + text-decoration: none; + color: white; + font-size: 24px; + font-weight: 400; + transition: all 0.5s ease-in-out; + position: relative; + text-transform: uppercase; +} + +nav .menuItems li:hover { + background-color: rgba(0, 0, 0, 0.1); +} diff --git a/static/css/style.css b/static/css/style.css index f6313b4b..ceccb23c 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,3 +1,20 @@ +* { + padding: 0; + margin: 0; +} + +.content { + margin-top: 30px; +} + +h3 { + margin-bottom: 0; +} + +.container-fluid { + padding: 0; +} + #index_buttons { padding-top: 15px; margin: 0 auto; @@ -6,34 +23,54 @@ } #login_button { - background-color: #4963f4; + background-color: #7c8acc; + border-color: #ccc; + color: rgb(255, 255, 255); + width: 100px; + font-weight: bold; + margin-top: 15px; + margin-bottom: 10px; +} + +#register_button { + background-color: #5b5e70; border-color: #ccc; color: rgb(236, 236, 236); width: 100px; font-weight: bold; - margin-top: 10px; + margin-top: 15px; margin-bottom: 10px; } +#checkout_page_button { + background-color: #7c8acc; + border-color: #ccc; + color: rgb(255, 255, 255); + width: auto; + font-weight: bold; +} + #info_row { padding-top: 15px; margin: 0 auto; width: 320px; background-color: #f5f5f5; border: 1px solid #ccc; + text-align: center; + justify-content: center; } -#register_button { - background-color: #444654; - border-color: #ccc; - color: rgb(236, 236, 236); - width: 100px; - font-weight: bold; - margin-top: 10px; - margin-bottom: 10px; +#register_page { + padding-top: 15px; + margin: 0 auto; + width: 420px; + background-color: #f5f5f5; + border: 1px solid #ccc; + text-align: center; } -#info_form_group { +#info_box { + margin: 0 auto; margin-top: 10px; width: 200px; } @@ -45,16 +82,16 @@ width: 302px; background-color: #cac2c2; border: 1px solid rgb(178, 178, 178); - height: 500px; + height: 550px; } .product_card-img { - width: 300px; + width: auto; height: 300px; + padding-top: 15px; } .product_card-title { - /* Centered */ text-align: center; font-size: 20px; font-weight: bold; @@ -65,16 +102,22 @@ .product_card-text { text-align: center; font-size: 15px; - margin-top: 10px; - margin-bottom: 10px; + margin: 10px 5px; + font-style: italic; + height: 75px; +} + +.product_card-stock { + text-align: center; + font-size: 15px; + margin: 10px 5px; font-style: italic; - height: 60px; } .product_card-price { text-align: center; font-size: 15px; - margin-top: 10px; + margin-top: 25px; margin-bottom: 10px; font-weight: bold; font-style: italic; @@ -91,25 +134,6 @@ text-align: center; } - -#count_row { - margin: 0 auto; - margin-top: 10px; - margin-bottom: 10px; - width: 300px; - background-color: #fff9f9; - border: 1px solid rgb(178, 178, 178); - height: 50px; - -} - -.quanity { - margin-left: 130px; - margin-right: 130px; - text-align: center; - font-size: 20px; -} - #checkout_button { background-color: #49f4d8; border-color: #ccc; @@ -119,4 +143,53 @@ margin-top: 10px; margin-bottom: 10px; text-align: center; +} + +button:hover { + opacity: 0.8; +} + + +/* Checkout Page Table */ + +#product_page { + justify-content: center; +} + +#cart-table-form { + min-width: 600px; + max-width: 900px; +} + +.cart-table { + width: 100%; + border-collapse: collapse; + margin: 15px 0; +} + +.cart-table th, .cart-table td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; +} + +.cart-table th { + background-color: #f2f2f2; +} + +.total-row { + font-weight: bold; +} + + +/* Order Page Table */ + +.order_table { + width: 60%; + margin: 0 auto; + margin-bottom: 50px; +} + +.order_table tr, .order_table td, .order_table th { + padding: 8px 10px; } \ No newline at end of file diff --git a/templates/about_us.html b/templates/about_us.html new file mode 100644 index 00000000..944badba --- /dev/null +++ b/templates/about_us.html @@ -0,0 +1,28 @@ +{% extends "layout.html" %} + +{% block content %} +
+ This is our work for class ITSC-3155-081 (Software Engineering). +
++ Our project is BOOKCARE, a bookstore website where you can explore and buy a variety of books from different genres. +
++ To view the Admin Panel, please login as an admin: +
++ Username: Admin +
++ Password: Admin +
++ Thank you for visiting and viewing our project! +
+{{ total_cost }}
+{{ product.info }}
${{ product.price }}
+Stock: {{ product.stock }}
{{ product.info }}
${{ product.price }}
+Stock: {{ product.stock }}
Incorrect username or password.
+ {% endif %}