diff --git a/src/entities/product.py b/src/entities/product.py index ae7341f..0dba882 100644 --- a/src/entities/product.py +++ b/src/entities/product.py @@ -7,6 +7,7 @@ class Product: name: str description: str price: float + quantity: int def post_init(self): if self.price < 0: diff --git a/src/entities/user.py b/src/entities/user.py new file mode 100644 index 0000000..3063c11 --- /dev/null +++ b/src/entities/user.py @@ -0,0 +1,7 @@ +#python imports +from dataclasses import dataclass + +@dataclass +class User: + pass + #TODO: implement \ No newline at end of file diff --git a/src/framework_driver/db/db_setup.py b/src/framework_driver/db/db_setup.py index 22a792c..5b135e9 100644 --- a/src/framework_driver/db/db_setup.py +++ b/src/framework_driver/db/db_setup.py @@ -41,6 +41,7 @@ def main(): id INTEGER PRIMARY KEY, name TEXT NOT NULL, price REAL NOT NULL, + description TEXT NOT NULL, quantity INTEGER NOT NULL )""" diff --git a/src/framework_driver/db/shop.db b/src/framework_driver/db/shop.db index 885ea58..1b074fa 100644 Binary files a/src/framework_driver/db/shop.db and b/src/framework_driver/db/shop.db differ diff --git a/src/framework_driver/ui/src/controller.js b/src/framework_driver/ui/src/controller.js index 7e45ad4..be844c3 100644 --- a/src/framework_driver/ui/src/controller.js +++ b/src/framework_driver/ui/src/controller.js @@ -35,6 +35,14 @@ const Controller = { event.preventDefault(); alert('User profile functionality not implemented.'); }); + + document.querySelectorAll('.product-link').forEach((link) => { + link.addEventListener('click', (event) => { + event.preventDefault(); + const productId = event.target.getAttribute('data-id'); + Controller.showProduct(productId); + }); + }); }, addProductClickHandlers() { diff --git a/src/framework_driver/ui/static/css/style.css b/src/framework_driver/ui/static/css/style.css deleted file mode 100644 index 3498937..0000000 --- a/src/framework_driver/ui/static/css/style.css +++ /dev/null @@ -1,65 +0,0 @@ -body { - font-family: Arial, sans-serif; -} - -header { - background-color: #f8f9fa; - padding: 1rem; - text-align: center; -} - -main { - padding: 1rem; -} - -.btn-primary { - display: inline-block; - padding: 10px 20px; - font-size: 16px; - color: #fff; - background-color: #007bff; - border: none; - border-radius: 4px; - text-align: center; - text-decoration: none; - cursor: pointer; -} - -.btn-primary:hover { - background-color: #0056b3; -} - - -.product-list { - list-style-type: none; - padding: 0; - margin: 0; -} - -.product-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 10px; - border-bottom: 1px solid #ddd; -} - -/* Styling der Produktnamen, -mengen und -preise */ -.product-name { - font-weight: bold; -} - -.product-quantity { - color: #555; -} - -.product-price { - color: #555; -} - -/* Styling für den Gesamtpreis */ -.total-price { - font-size: 18px; - font-weight: bold; - margin-top: 20px; -} \ No newline at end of file diff --git a/src/framework_driver/ui/static/js/script.js b/src/framework_driver/ui/static/js/script.js deleted file mode 100644 index 3035a99..0000000 --- a/src/framework_driver/ui/static/js/script.js +++ /dev/null @@ -1,3 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - console.log('JavaScript is loaded'); -}); \ No newline at end of file diff --git a/src/framework_driver/ui/templates/base.html b/src/framework_driver/ui/templates/base.html deleted file mode 100644 index 4d3d569..0000000 --- a/src/framework_driver/ui/templates/base.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - {% block title %}My Shop{% endblock %} - - - -
-

Shop

-
-
- {% block content %}{% endblock %} -
- - - \ No newline at end of file diff --git a/src/framework_driver/ui/templates/cart.html b/src/framework_driver/ui/templates/cart.html deleted file mode 100644 index 92d7c97..0000000 --- a/src/framework_driver/ui/templates/cart.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Cart{% endblock %} - -{% block content %} -

Cart Items

- - - -

Total Price: {{ cart_items.total_price }} €

- -Back to the prodcuts - -{% endblock %} \ No newline at end of file diff --git a/src/framework_driver/ui/templates/products.html b/src/framework_driver/ui/templates/products.html deleted file mode 100644 index f23f82a..0000000 --- a/src/framework_driver/ui/templates/products.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Products{% endblock %} - -{% block content %} -

Product Items

- - -Go to Cart - -{% endblock %} \ No newline at end of file diff --git a/src/interface_adapters/controllers/cart_controller.py b/src/interface_adapters/controllers/cart_controller.py index 57fe7ac..d288c6a 100644 --- a/src/interface_adapters/controllers/cart_controller.py +++ b/src/interface_adapters/controllers/cart_controller.py @@ -3,18 +3,12 @@ from fastapi import APIRouter, HTTPException from fastapi.responses import JSONResponse from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse -from fastapi.templating import Jinja2Templates - -import httpx #dependency imports from use_cases import ViewCart from interface_adapters.dtos import ViewCartRequestDTO, ViewCartResponseDTO, CartItemDTO from interface_adapters.repositories import SQLCartRepository -# Set up templates -templates = Jinja2Templates(directory="framework_driver/ui/templates") - # Initialize components cart_repository = SQLCartRepository() view_cart_use_case = ViewCart(cart_repository) @@ -36,14 +30,3 @@ async def view_cart(request_dto: ViewCartRequestDTO): return response_dto - -@router.get("/ui/view_cart", response_class=HTMLResponse, name="ui_view_cart") -async def ui_view_cart(request: Request, user_id: int=1): - request_dto = {"user_id": user_id} - async with httpx.AsyncClient() as client: - response = await client.post("http://127.0.0.1:8000/view_cart", json=request_dto) - response.raise_for_status() # Raise an exception for HTTP errors - response_dto = response.json() - - return templates.TemplateResponse("cart.html", {"request": request, "cart_items": response_dto}) - diff --git a/src/interface_adapters/controllers/product_controller.py b/src/interface_adapters/controllers/product_controller.py index c09f00c..07e94e8 100644 --- a/src/interface_adapters/controllers/product_controller.py +++ b/src/interface_adapters/controllers/product_controller.py @@ -3,9 +3,6 @@ from fastapi import APIRouter, HTTPException from fastapi.responses import JSONResponse from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse -from fastapi.templating import Jinja2Templates - -import httpx #dependency imports from use_cases import ViewProduct, ViewAllProducts @@ -40,7 +37,8 @@ async def view_products(): if not products: raise HTTPException(status_code=404, detail="Product not found") - + for product in products: + print(product) response_dto = ViewAllProductsResponseDTO( products=[ProductDTO(id=product.id, name=product.name, description=product.description, price=product.price) for product in products] ) diff --git a/src/interface_adapters/controllers/user_controller.py b/src/interface_adapters/controllers/user_controller.py new file mode 100644 index 0000000..235b3f3 --- /dev/null +++ b/src/interface_adapters/controllers/user_controller.py @@ -0,0 +1,16 @@ +#python imports +from fastapi import APIRouter, HTTPException +from fastapi.responses import JSONResponse +from fastapi import FastAPI, Request +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates + +#dependency imports +#TODO: use correct dependencies + +# Initialize components +#TODO: initialize + +router = APIRouter() + +#TODO: implement routes for creating and viewing user diff --git a/src/interface_adapters/repositories/sql_cart_repository.py b/src/interface_adapters/repositories/sql_cart_repository.py index 675c478..def6a23 100644 --- a/src/interface_adapters/repositories/sql_cart_repository.py +++ b/src/interface_adapters/repositories/sql_cart_repository.py @@ -4,11 +4,13 @@ import sqlite3 #dependency imports from use_cases import ICartRepository +from entities import Cart, CartItem class SQLCartRepository(ICartRepository): def __init__(self, db_file="framework_driver/db/shop.db"): """Initialize the CartDatabase with a connection to the SQLite database.""" self.conn = sqlite3.connect(db_file) + self.conn.row_factory = sqlite3.Row def add_to_cart(self, user_id, product_id, name, quantity, price): """Add a product to the user's cart.""" @@ -21,14 +23,14 @@ class SQLCartRepository(ICartRepository): except sqlite3.Error as e: print(e) - def get_cart_by_user_id(self, user_id: int) -> Optional[List[dict]]: + def get_cart_by_user_id(self, user_id: int) -> Optional[Cart]: """Fetch cart items from the database for a given user ID.""" sql = "SELECT product_id, name, quantity, price FROM cart WHERE user_id = ?" try: c = self.conn.cursor() c.execute(sql, (user_id,)) rows = c.fetchall() - return [{'product_id':r[0], 'name':r[1], 'quantity':r[2], 'price':r[3]} for r in rows] + return Cart([CartItem(**item) for item in rows]) except sqlite3.Error as e: print(e) return None @@ -66,7 +68,7 @@ class SQLCartRepository(ICartRepository): def main(): database = "framework_driver/db/shop.db" - cart_db = CartDatabase(database) + cart_db = SQLCartRepository(database) ex_user_id = 1 @@ -76,11 +78,11 @@ def main(): # user id, product id, name, quantity, price cart_db.add_to_cart(ex_user_id, 1, "Unsichtbare Tinte", 2, 3.0) cart_db.add_to_cart(ex_user_id, 3, "Geräuschloser Wecker", 5, 15.0) - print(f"Cart items for user {ex_user_id}:", cart_db.fetch_cart_items(ex_user_id)) + print(f"Cart items for user {ex_user_id}:", cart_db.get_cart_by_user_id(ex_user_id)) # user id, product id, quantity cart_db.update_cart_item(ex_user_id, 2, 3) - print(f"Cart items for user {ex_user_id}:", cart_db.fetch_cart_items(ex_user_id)) + print(f"Cart items for user {ex_user_id}:", cart_db.get_cart_by_user_id(ex_user_id)) if __name__ == "__main__": main() \ No newline at end of file diff --git a/src/interface_adapters/repositories/sql_product_repository.py b/src/interface_adapters/repositories/sql_product_repository.py index fce4e78..0bdbe0d 100644 --- a/src/interface_adapters/repositories/sql_product_repository.py +++ b/src/interface_adapters/repositories/sql_product_repository.py @@ -4,15 +4,17 @@ import sqlite3 #dependency imports from use_cases import IProductRepository +from entities import Product class SQLProductRepository(IProductRepository): def __init__(self, db_file="framework_driver/db/shop.db"): """Initialize the ProductDatabase with a connection to the SQLite database.""" self.conn = sqlite3.connect(db_file) + self.conn.row_factory = sqlite3.Row def add_product(self, product): """Add a new product to the products table.""" - sql = "INSERT INTO products (name, price, quantity) VALUES (?, ?, ?)" + sql = "INSERT INTO products (name, price, description, quantity) VALUES (?, ?, ?, ?)" try: c = self.conn.cursor() c.execute(sql, product) @@ -21,26 +23,26 @@ class SQLProductRepository(IProductRepository): except sqlite3.Error as e: print(e) - def get_product_by_id(self, product_id): + def get_product_by_id(self, product_id) -> Optional[Product]: """Retrieve a product by its ID.""" sql = "SELECT * FROM products WHERE id = ?" try: c = self.conn.cursor() c.execute(sql, (product_id,)) r = c.fetchone() - return {'id':r[0], 'name':str(r[1]), 'description':str(r[2]), 'price':r[3]} + return Product(**r) except sqlite3.Error as e: print(e) return None - def get_all_products(self): + def get_all_products(self) -> Optional[List[Product]]: """Retrieve all products.""" sql = "SELECT * FROM products" try: c = self.conn.cursor() c.execute(sql) rows = c.fetchall() - return [{'id':r[0], 'name':str(r[1]), 'description':str(r[2]), 'price':r[3]} for r in rows] + return [Product(**product) for product in rows] except sqlite3.Error as e: print(e) return [] @@ -68,16 +70,16 @@ class SQLProductRepository(IProductRepository): def main(): database = "framework_driver/db/shop.db" - product_db = ProductDatabase(database) + product_db = SQLProductRepository(database) # delete all products for product in product_db.get_all_products(): product_db.delete_product(product[0]) # name, price, quantity - products = [("Unsichtbare Tinte", 2.5, 5), - ("Luftdichter Sieb", 7.5, 10), - ("Geräuschloser Wecker", 15.0, 7)] + products = [("Unsichtbare Tinte", 2.5, "unsichtbar", 5), + ("Luftdichter Sieb", 7.5, "luftdicht", 10), + ("Geräuschloser Wecker", 15.0, "geräuschlos", 7)] for product in products: product_db.add_product(product) diff --git a/src/interface_adapters/repositories/sql_user_repository.py b/src/interface_adapters/repositories/sql_user_repository.py index c372c9f..c0f3bc0 100644 --- a/src/interface_adapters/repositories/sql_user_repository.py +++ b/src/interface_adapters/repositories/sql_user_repository.py @@ -4,11 +4,13 @@ import sqlite3 #dependency imports from use_cases import IUserRepository +from entities import User class SQLUserRepository(IUserRepository): def __init__(self, db_file="framework_driver/db/shop.db"): """Initialize the UserDatabase with a connection to the SQLite database.""" self.conn = sqlite3.connect(db_file) + self.conn.row_factory = sqlite3.Row def add_user(self, user): """Add a new user to the users table and return the new user's id.""" @@ -22,7 +24,7 @@ class SQLUserRepository(IUserRepository): print(e) return None - def get_user_by_id(self, user_id): + def get_user_by_id(self, user_id) -> Optional[User]: """Retrieve a user by their ID.""" sql = "SELECT * FROM users WHERE id = ?" try: @@ -67,7 +69,7 @@ class SQLUserRepository(IUserRepository): def main(): database = "framework_driver/db/shop.db" - user_db = UserDatabase(database) + user_db = SQLUserRepository(database) # delete all users for user in user_db.get_all_users(): diff --git a/src/main.py b/src/main.py index efbd9d8..4eb0880 100644 --- a/src/main.py +++ b/src/main.py @@ -1,8 +1,8 @@ from interface_adapters.controllers.cart_controller import router as cart_router from interface_adapters.controllers.product_controller import router as product_router +from interface_adapters.controllers.user_controller import router as user_router from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles -from fastapi.templating import Jinja2Templates from fastapi.responses import RedirectResponse from fastapi.responses import HTMLResponse from fastapi.responses import FileResponse @@ -26,33 +26,9 @@ app.add_middleware( allow_headers=["*"], ) -# Serve static files -# TODO fix this -#app.mount("/static", StaticFiles(directory="framework_driver/ui/static"), name="static") -# TODO so this is not needed -@app.get("/custom_static/{filename:path}", response_class=FileResponse, name="custom_static", include_in_schema=False) -async def custom_static(filename: str): - file_path = os.path.join("framework_driver/ui/static", filename) - if not os.path.exists(file_path): - raise HTTPException(status_code=404, detail="File not found") - return FileResponse(file_path) - - -# Set up templates -templates = Jinja2Templates(directory="framework_driver/ui/templates") - @app.get("/", include_in_schema=False) async def root(): return RedirectResponse(url="/docs") - - -@app.get("/ui", response_class=HTMLResponse) -async def ui(request: Request): - products = [ - {"id": 1, "name": "Product 1", "price": 10.0}, - {"id": 2, "name": "Product 2", "price": 20.0} - ] - return templates.TemplateResponse("products.html", {"request": request, "products": products}) if __name__ == "__main__": uvicorn.run(app, host="127.0.0.1", port=8000) diff --git a/src/setup_db.py b/src/setup_db.py new file mode 100644 index 0000000..3e16679 --- /dev/null +++ b/src/setup_db.py @@ -0,0 +1,8 @@ +from framework_driver.db import db_setup +from interface_adapters.repositories import sql_cart_repository +from interface_adapters.repositories import sql_product_repository +from interface_adapters.repositories import sql_user_repository +db_setup.main() +sql_cart_repository.main() +sql_product_repository.main() +sql_user_repository.main() \ No newline at end of file diff --git a/src/use_cases/cart_repository.py b/src/use_cases/cart_repository.py index 5cb7452..1503235 100644 --- a/src/use_cases/cart_repository.py +++ b/src/use_cases/cart_repository.py @@ -1,7 +1,10 @@ from typing import Optional, List +#dependency imports +from entities import Cart + class ICartRepository: - def get_cart_by_user_id(self, user_id: int) -> Optional[List[dict]]: + def get_cart_by_user_id(self, user_id: int) -> Optional[Cart]: """ Abstract method to fetch cart data by user ID. """ diff --git a/src/use_cases/create_user.py b/src/use_cases/create_user.py new file mode 100644 index 0000000..b29bde5 --- /dev/null +++ b/src/use_cases/create_user.py @@ -0,0 +1,14 @@ +#python imports +from typing import Optional + +#dependency imports +#TODO: use correct dependencies + +class CreateUser: + def __init__(self): + pass + #TODO: initialize required Interfaces + + def execute(self): + pass + #TODO: implement \ No newline at end of file diff --git a/src/use_cases/product_repository.py b/src/use_cases/product_repository.py index b89b4b7..e5a56df 100644 --- a/src/use_cases/product_repository.py +++ b/src/use_cases/product_repository.py @@ -1,13 +1,16 @@ from typing import Optional, List +#dependency imports +from entities import Product + class IProductRepository: - def get_product_by_id(self, product_id: int): + def get_product_by_id(self, product_id: int) -> Optional[Product]: """ Abstract method to fetch product data by ID. """ raise NotImplementedError("get_product_by_id must be implemented by a subclass") - def get_all_products(self): + def get_all_products(self) -> Optional[List[Product]]: """ Abstract method to fetch product data. """ diff --git a/src/use_cases/user_repository.py b/src/use_cases/user_repository.py index 1952ccc..3cd9182 100644 --- a/src/use_cases/user_repository.py +++ b/src/use_cases/user_repository.py @@ -1,8 +1,5 @@ from typing import Optional, List class IUserRepository: - def get_user_by_id(self, user_id: int): - """ - Abstract method to fetch user data by user ID. - """ - raise NotImplementedError("get_user_by_id must be implemented by a subclass") \ No newline at end of file + pass + #TODO: define interface functions for creating and getting user \ No newline at end of file diff --git a/src/use_cases/view_all_products.py b/src/use_cases/view_all_products.py index ded48be..ea41298 100644 --- a/src/use_cases/view_all_products.py +++ b/src/use_cases/view_all_products.py @@ -1,5 +1,5 @@ #python imports -from typing import Optional +from typing import Optional, List #dependency imports from entities import Product @@ -9,12 +9,11 @@ class ViewAllProducts: def __init__(self, product_repository: IProductRepository): self.product_repository = product_repository - def execute(self) -> Optional[Product]: + def execute(self) -> Optional[List[Product]]: """ - Fetches the product data from the repository, converts it to Product entity. + Fetches the product data from the repository. """ product_data = self.product_repository.get_all_products() if not product_data: return None - products = [Product(**product) for product in product_data] - return products \ No newline at end of file + return product_data \ No newline at end of file diff --git a/src/use_cases/view_cart.py b/src/use_cases/view_cart.py index 3ade8b6..9f7008b 100644 --- a/src/use_cases/view_cart.py +++ b/src/use_cases/view_cart.py @@ -11,12 +11,10 @@ class ViewCart: def execute(self, user_id: int) -> Optional[Cart]: """ - Fetches the cart data from the repository, converts it to Cart entity. + Fetches the cart data from the repository. """ cart_data = self.cart_repository.get_cart_by_user_id(user_id) if not cart_data: return None - # Convert raw data to domain entities - items = [CartItem(**item) for item in cart_data] - return Cart(items) \ No newline at end of file + return cart_data \ No newline at end of file diff --git a/src/use_cases/view_product.py b/src/use_cases/view_product.py index 54a3938..271f2f1 100644 --- a/src/use_cases/view_product.py +++ b/src/use_cases/view_product.py @@ -11,9 +11,10 @@ class ViewProduct: def execute(self, product_id: int) -> Optional[Product]: """ - Fetches the product data from the repository, converts it to Product entity. + Fetches the product data from the repository. """ product_data = self.product_repository.get_product_by_id(product_id) if not product_data: return None - return Product(**product_data) \ No newline at end of file + + return product_data \ No newline at end of file diff --git a/src/use_cases/view_user.py b/src/use_cases/view_user.py new file mode 100644 index 0000000..dab7561 --- /dev/null +++ b/src/use_cases/view_user.py @@ -0,0 +1,14 @@ +#python imports +from typing import Optional + +#dependency imports +#TODO: use correct dependencies + +class ViewUser: + def __init__(self): + pass + #TODO: initialize required Interfaces + + def execute(self): + pass + #TODO: implement \ No newline at end of file