remove any cart stuff. add port interfaces for products
parent
5c84416fe0
commit
d8ca863c8c
|
|
@ -1,5 +1,5 @@
|
|||
# ENTITIES
|
||||
Entities encapsulate enterprise-wide Critical Business Rules. An entity can be an
|
||||
"Entities encapsulate enterprise-wide Critical Business Rules. An entity can be an
|
||||
object with methods, or it can be a set of data structures and functions. It doesn’t
|
||||
matter so long as the entities can be used by many different applications in the
|
||||
enterprise.
|
||||
|
|
@ -8,4 +8,23 @@ entities are the business objects of the application. They encapsulate the most
|
|||
and high-level rules. They are the least likely to change when something external
|
||||
changes. For example, you would not expect these objects to be affected by a change
|
||||
to page navigation or security. No operational change to any particular application
|
||||
should affect the entity layer.
|
||||
should affect the entity layer." -- Robert C. Martin, Clean Architecture
|
||||
|
||||
# Aufgabe
|
||||
|
||||
### Anforderungen:
|
||||
|
||||
#### Attribute:
|
||||
- **name**: Der Name des Benutzers.
|
||||
- **email**: Die E-Mail-Adresse des Benutzers.
|
||||
- **password**: Das Passwort des Benutzers.
|
||||
- **id**: Eine eindeutige ID, die den Benutzer identifiziert.
|
||||
|
||||
#### Getter und Setter Methoden:
|
||||
- Implementiere für jedes Attribut (`name`, `email`, `password`, `id`) Getter- und Setter-Methoden. Diese Methoden sollten es ermöglichen, die Werte der Attribute abzurufen und zu ändern.
|
||||
|
||||
#### Datenvalidierung (optional):
|
||||
- Validiere die Eingabedaten, z.B. sicherstellen, dass die E-Mail-Adresse ein gültiges Format hat und dass das Passwort eine Mindestlänge aufweist.
|
||||
|
||||
#### Konstruktor:
|
||||
- Erstelle einen Konstruktor, der die Attribute `name`, `email`, `password` und `id` beim Erstellen eines Benutzerobjekts initialisiert.
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
#python imports
|
||||
from typing import List
|
||||
|
||||
#dependency imports
|
||||
from .cart_item import CartItem
|
||||
from .product import Product
|
||||
|
||||
class Cart:
|
||||
"""
|
||||
Represents a shopping cart containing multiple cart items.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initializes a new shopping cart.
|
||||
"""
|
||||
self.items: List[CartItem] = []
|
||||
self.total_price: float = 0.0
|
||||
|
||||
def add_item(self, product: Product, quantity: int):
|
||||
"""
|
||||
Adds a product to the cart or updates the quantity if it already exists.
|
||||
|
||||
Args:
|
||||
product (Product): The product to add to the cart.
|
||||
quantity (int): The quantity of the product to add.
|
||||
"""
|
||||
for item in self.items:
|
||||
if item.product_id == product.id:
|
||||
item.quantity += quantity
|
||||
return
|
||||
self.items.append(CartItem(product_id=product.id, name=product.name, quantity=quantity, price=product.price))
|
||||
|
||||
def remove_item(self, product_id: str):
|
||||
"""
|
||||
Removes a product from the cart by its ID.
|
||||
|
||||
Args:
|
||||
product_id (str): The unique identifier of the product to remove.
|
||||
"""
|
||||
self.items = [item for item in self.items if item.product_id != product_id]
|
||||
|
||||
def calculate_total_price(self) -> float:
|
||||
"""
|
||||
Calculates the total price of all items in the cart.
|
||||
|
||||
Returns:
|
||||
float: The total price of all items in the cart.
|
||||
"""
|
||||
self.total_price = sum(item.calculate_total_price() for item in self.items)
|
||||
return self.total_price
|
||||
|
||||
def list_items(self) -> List[str]:
|
||||
"""
|
||||
Lists all items in the cart with their quantities and total prices.
|
||||
|
||||
Returns:
|
||||
List[str]: A list of strings representing the items in the cart.
|
||||
"""
|
||||
return [f"{item.quantity} x {item.name} - {item.calculate_total_price()} EUR" for item in self.items]
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Returns a string representation of the cart.
|
||||
|
||||
Returns:
|
||||
str: A string representation of the cart.
|
||||
"""
|
||||
return f"Cart(items={self.items}, total_price={self.total_price})"
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#python imports
|
||||
from typing import List
|
||||
|
||||
class CartItem:
|
||||
"""
|
||||
Represents an item in the cart with a product ID, name, quantity, and price.
|
||||
"""
|
||||
def __init__(self, product_id: int, name: str, quantity: int, price: float):
|
||||
"""
|
||||
Initializes a new cart item.
|
||||
|
||||
Args:
|
||||
product_id (int): The unique identifier for the product.
|
||||
name (str): The name of the product.
|
||||
quantity (int): The quantity of the product in the cart.
|
||||
price (float): The price of the product.
|
||||
"""
|
||||
self.product_id = product_id
|
||||
self.name = name
|
||||
self.quantity = quantity
|
||||
self.price = price
|
||||
self.post_init()
|
||||
|
||||
def post_init(self):
|
||||
"""
|
||||
Validates the cart item's quantity.
|
||||
|
||||
Raises:
|
||||
ValueError: If the quantity is less than or equal to 0.
|
||||
"""
|
||||
if self.quantity <= 0:
|
||||
raise ValueError("Quantity has to be at least 1")
|
||||
|
||||
def calculate_total_price(self) -> float:
|
||||
"""
|
||||
Calculates the total price of the cart item.
|
||||
|
||||
Returns:
|
||||
float: The total price of the cart item.
|
||||
"""
|
||||
return self.quantity * self.price
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Returns a string representation of the cart item.
|
||||
|
||||
Returns:
|
||||
str: A string representation of the cart item.
|
||||
"""
|
||||
return f"CartItem(product_id={self.product_id}, name={self.name}, quantity={self.quantity}, price={self.price})"
|
||||
|
|
@ -33,7 +33,6 @@
|
|||
<body>
|
||||
<header>
|
||||
<a id="products-tab" href="#">Products</a>
|
||||
<a id="cart-tab" href="#">Cart</a>
|
||||
<a id="user-tab" href="#">User</a>
|
||||
</header>
|
||||
<main id="main"></main>
|
||||
|
|
|
|||
|
|
@ -7,23 +7,20 @@ const Controller = {
|
|||
},
|
||||
|
||||
async showProducts() {
|
||||
const products = await Model.fetchProducts();
|
||||
View.renderProducts(products);
|
||||
const productsResponse = await Model.fetchProducts();
|
||||
if(productsResponse.status=="success"){
|
||||
const products = productsResponse.data;
|
||||
View.renderProducts(products);
|
||||
}
|
||||
Controller.addProductClickHandlers();
|
||||
},
|
||||
|
||||
async showCart() {
|
||||
const userId = "1"; // Hardcoded for simplicity
|
||||
const cartResponse = await Model.fetchCart(userId);
|
||||
if(cartResponse.status=="success"){
|
||||
const cart = cartResponse.data;
|
||||
View.renderCart(cart);
|
||||
}
|
||||
},
|
||||
|
||||
async showProduct(productId) {
|
||||
const product = await Model.fetchProduct(productId);
|
||||
View.renderProduct(product);
|
||||
const productResponse = await Model.fetchProduct(productId);
|
||||
if(productResponse.status=="success"){
|
||||
const product = productResponse.data;
|
||||
View.renderProduct(product);
|
||||
}
|
||||
document.getElementById('back-button').addEventListener('click', () => {
|
||||
Controller.showProducts();
|
||||
});
|
||||
|
|
@ -45,11 +42,6 @@ const Controller = {
|
|||
Controller.showProducts();
|
||||
});
|
||||
|
||||
document.getElementById('cart-tab').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
Controller.showCart();
|
||||
});
|
||||
|
||||
document.getElementById('user-tab').addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
Controller.showUserRegister();
|
||||
|
|
|
|||
|
|
@ -5,17 +5,6 @@ const Model = {
|
|||
return response.json();
|
||||
},
|
||||
|
||||
async fetchCart(userId) {
|
||||
const response = await fetch('http://127.0.0.1:8000/view_cart', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user_id: userId }),
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
|
||||
async fetchProduct(productId) {
|
||||
const response = await fetch(`http://127.0.0.1:8000/view_product/`, {
|
||||
method: 'POST',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// View: Handles rendering
|
||||
const View = {
|
||||
renderProducts(products) {
|
||||
var products = products.products;
|
||||
const main = document.getElementById('main');
|
||||
main.innerHTML = `
|
||||
<h1>Products</h1>
|
||||
|
|
@ -21,25 +20,7 @@ const View = {
|
|||
`;
|
||||
},
|
||||
|
||||
renderCart(cart) {
|
||||
var cartItems = cart.items;
|
||||
const main = document.getElementById('main');
|
||||
main.innerHTML = `
|
||||
<h1>Your Cart</h1>
|
||||
<ul>
|
||||
${cartItems
|
||||
.map(
|
||||
(item) => `
|
||||
<li>${item.name} - $${item.price} (Quantity: ${item.quantity})</li>`
|
||||
)
|
||||
.join('')}
|
||||
</ul>
|
||||
<p>Total: $${cart.total_price}</p>
|
||||
`;
|
||||
},
|
||||
|
||||
renderProduct(product) {
|
||||
var product = product.product;
|
||||
const main = document.getElementById('main');
|
||||
main.innerHTML = `
|
||||
<h1>${product.name}</h1>
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
from use_cases.interfaces import BaseInputPort, BaseOutputPort
|
||||
|
||||
class BaseController:
|
||||
def __init__(self, input_port: BaseInputPort) -> None:
|
||||
self.input_port = input_port
|
||||
|
||||
def handle_request(self, request) -> dict:
|
||||
"""Handles the incoming API request and delegates to the use case."""
|
||||
return self.input_port.execute(request)
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
from typing import Type
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from .base_controller import BaseController
|
||||
from use_cases.interfaces import BaseOutputPort, BaseInputPort, BaseRepository
|
||||
|
||||
class Endpoint:
|
||||
def __init__(self,
|
||||
router: APIRouter,
|
||||
controller_impl: Type[BaseController],
|
||||
presenter_impl: Type[BaseOutputPort],
|
||||
repository_impl: Type[BaseRepository],
|
||||
use_case_impl: Type[BaseInputPort]
|
||||
):
|
||||
self.router = router
|
||||
self.presenter = presenter_impl()
|
||||
self.repository = repository_impl()
|
||||
self.use_case = use_case_impl(repository=self.repository, output_port=self.presenter)
|
||||
self.controller = controller_impl(input_port=self.use_case)
|
||||
|
||||
def create(self, route: str, request_model: Type, response_model: Type):
|
||||
@self.router.post(route, response_model=response_model)
|
||||
async def endpoint(request: request_model):
|
||||
try:
|
||||
response = self.controller.handle_request(request)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#python imports
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pydantic import BaseModel
|
||||
#dependency imports
|
||||
from use_cases.interactors import ViewCart
|
||||
from use_cases.interfaces import BaseInputPort, BaseOutputPort
|
||||
from interface_adapters.dtos import ViewCartRequestDTO, ViewCartResponseDTO, CartItemDTO, CartDTO
|
||||
from .base_controller import BaseController
|
||||
|
||||
|
||||
|
||||
#move to main
|
||||
from .base_endpoint import Endpoint
|
||||
from interface_adapters.repositories import SQLCartRepository
|
||||
|
||||
class ViewCartRequest(BaseModel):
|
||||
user_id: str
|
||||
|
||||
class ViewCartResponse(BaseModel):
|
||||
status: str
|
||||
data: CartDTO
|
||||
|
||||
class ViewCartController(BaseController):
|
||||
def __init__(self, input_port: BaseInputPort) -> None:
|
||||
self.input_port = input_port
|
||||
|
||||
def handle_request(self, request) -> dict:
|
||||
"""Handles the incoming API request and delegates to the use case."""
|
||||
return self.input_port.execute(request)
|
||||
|
||||
class ViewCartPresenter(BaseOutputPort):
|
||||
def present(self, output) -> dict:
|
||||
"""Format the response data for API."""
|
||||
cart_items=[CartItemDTO(product_id=item.product_id, name=item.name, quantity=item.quantity, price=item.price) for item in output.items]
|
||||
cart_data = CartDTO(items=cart_items,total_price=output.total_price)
|
||||
response = ViewCartResponse(status="success",data=cart_data)
|
||||
return response
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
endpoint = Endpoint(router, ViewCartController, ViewCartPresenter, SQLCartRepository, ViewCart)
|
||||
endpoint.create("/view_cart", ViewCartRequest, ViewCartResponse)
|
||||
|
||||
"""
|
||||
# Initialize components
|
||||
presenter = ViewCartPresenter()
|
||||
repository = SQLCartRepository()
|
||||
use_case = ViewCart(repository=repository, output_port=presenter)
|
||||
controller = ViewCartController(input_port=use_case)
|
||||
|
||||
|
||||
@router.post("/view_cart/")
|
||||
async def view_cart(request: ViewCartRequest):
|
||||
try:
|
||||
result = controller.handle_request(request.user_id)
|
||||
return result # Returns a dictionary which FastAPI will convert to JSON
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@router.post("/view_cart", response_model=ViewCartResponseDTO)
|
||||
async def view_cart(request_dto: ViewCartRequestDTO):
|
||||
|
||||
cart = view_cart_use_case.execute(request_dto.user_id)
|
||||
|
||||
if not cart:
|
||||
raise HTTPException(status_code=404, detail="Cart not found")
|
||||
|
||||
response_dto = ViewCartResponseDTO(
|
||||
items=[CartItemDTO(product_id=item.product_id, name=item.name, quantity=item.quantity, price=item.price) for item in cart.items],
|
||||
total_price=sum(item.price * item.quantity for item in cart.items)
|
||||
)
|
||||
|
||||
return response_dto
|
||||
"""
|
||||
|
|
@ -1,46 +1,76 @@
|
|||
#python imports
|
||||
from typing import List
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
#dependency imports
|
||||
from entities import Product
|
||||
from use_cases.interactors import ViewProduct, ViewAllProducts
|
||||
from interface_adapters.dtos import ViewProductRequestDTO, ViewProductResponseDTO, ViewAllProductsResponseDTO, ProductDTO
|
||||
from use_cases.interfaces.ports import ViewProductInputPort, ViewAllProductsInputPort, ViewProductOutputPort, ViewAllProductsOutputPort
|
||||
from interface_adapters.dtos import ProductDTO
|
||||
from interface_adapters.repositories import SQLProductRepository
|
||||
|
||||
# Initialize components
|
||||
|
||||
class ViewProductRequest(BaseModel):
|
||||
product_id: int
|
||||
|
||||
class ViewProductResponse(BaseModel):
|
||||
status: str
|
||||
data: ProductDTO
|
||||
|
||||
class ViewProductPresenter(ViewProductOutputPort):
|
||||
def present(self, product:Product) -> ViewProductResponse:
|
||||
"""Format the response data for API."""
|
||||
product_dto=ProductDTO(id=product.id, name=product.name, description=product.description, price=product.price)
|
||||
response = ViewProductResponse(status="success",data=product_dto)
|
||||
return response
|
||||
|
||||
class ViewProductController:
|
||||
def __init__(self, input_port: ViewProductInputPort) -> None:
|
||||
self.input_port = input_port
|
||||
|
||||
def handle_request(self, request:ViewProductRequest):
|
||||
"""Handles the incoming API request and delegates to the use case."""
|
||||
return self.input_port.execute(request.product_id)
|
||||
|
||||
product_repository = SQLProductRepository()
|
||||
view_product_use_case = ViewProduct(product_repository)
|
||||
view_all_products_use_case = ViewAllProducts(product_repository)
|
||||
view_product_presenter = ViewProductPresenter()
|
||||
view_product_use_case = ViewProduct(product_repository, view_product_presenter)
|
||||
view_product_controller = ViewProductController(view_product_use_case)
|
||||
|
||||
|
||||
class ViewAllProductsResponse(BaseModel):
|
||||
status: str
|
||||
data: List[ProductDTO]
|
||||
|
||||
class ViewAllProductsPresenter(ViewAllProductsOutputPort):
|
||||
def present(self, products:List[Product]) -> ViewAllProductsResponse:
|
||||
"""Format the response data for API."""
|
||||
products_dto=[ProductDTO(id=product.id, name=product.name, description=product.description, price=product.price) for product in products]
|
||||
response = ViewAllProductsResponse(status="success",data=products_dto)
|
||||
return response
|
||||
|
||||
class ViewAllProductsController:
|
||||
def __init__(self, input_port: ViewAllProductsInputPort) -> None:
|
||||
self.input_port = input_port
|
||||
|
||||
def handle_request(self):
|
||||
"""Handles the incoming API request and delegates to the use case."""
|
||||
return self.input_port.execute()
|
||||
|
||||
view_all_products_presenter = ViewAllProductsPresenter()
|
||||
view_all_products_use_case = ViewAllProducts(product_repository, view_all_products_presenter)
|
||||
view_all_products_controller = ViewAllProductsController(view_all_products_use_case)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/view_product", response_model=ViewProductResponseDTO)
|
||||
async def view_product(request_dto: ViewProductRequestDTO):
|
||||
@router.post("/view_product", response_model=ViewProductResponse)
|
||||
async def view_product(request: ViewProductRequest):
|
||||
return view_product_controller.handle_request(request)
|
||||
|
||||
product = view_product_use_case.execute(request_dto.product_id)
|
||||
|
||||
if not product:
|
||||
raise HTTPException(status_code=404, detail="Product not found")
|
||||
|
||||
response_dto = ViewProductResponseDTO(
|
||||
product=ProductDTO(id=product.id, name=product.name, description=product.description, price=product.price)
|
||||
)
|
||||
|
||||
return response_dto
|
||||
|
||||
@router.get("/view_products", response_model=ViewAllProductsResponseDTO)
|
||||
@router.get("/view_products", response_model=ViewAllProductsResponse)
|
||||
async def view_products():
|
||||
|
||||
products = view_all_products_use_case.execute()
|
||||
|
||||
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]
|
||||
)
|
||||
|
||||
return response_dto
|
||||
return view_all_products_controller.handle_request()
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#python imports
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
class CartItemDTO(BaseModel):
|
||||
product_id: int
|
||||
name: str
|
||||
quantity: int
|
||||
price: float
|
||||
|
||||
class CartDTO(BaseModel):
|
||||
items: List[CartItemDTO]
|
||||
total_price: float
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#python imports
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
from .product_dto import ProductDTO
|
||||
|
||||
class ViewAllProductsResponseDTO(BaseModel):
|
||||
products: List[ProductDTO] = []
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#python imports
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
class ViewCartRequestDTO(BaseModel):
|
||||
user_id: int
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#python imports
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
from .cart_dto import CartItemDTO
|
||||
|
||||
|
||||
class ViewCartResponseDTO(BaseModel):
|
||||
items: List[CartItemDTO] = []
|
||||
total_price: float = 0.0
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#python imports
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
class ViewProductRequestDTO(BaseModel):
|
||||
product_id: int
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#python imports
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
from .product_dto import ProductDTO
|
||||
|
||||
class ViewProductResponseDTO(BaseModel):
|
||||
product: ProductDTO
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
#python imports
|
||||
from typing import Optional, List
|
||||
import sqlite3
|
||||
|
||||
#dependency imports
|
||||
from use_cases.interfaces 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."""
|
||||
sql = "INSERT INTO cart (user_id, product_id, name, quantity, price) VALUES (?, ?, ?, ?, ?)"
|
||||
try:
|
||||
c = self.conn.cursor()
|
||||
c.execute(sql, (user_id, product_id, name, quantity, price))
|
||||
self.conn.commit()
|
||||
return c.lastrowid
|
||||
except sqlite3.Error as e:
|
||||
print(e)
|
||||
|
||||
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 Cart([CartItem(**item) for item in rows])
|
||||
except sqlite3.Error as e:
|
||||
print(e)
|
||||
return None
|
||||
|
||||
def update_cart_item(self, user_id, product_id, quantity):
|
||||
"""Update the quantity of a specific cart item based on user_id and product_id."""
|
||||
sql = "UPDATE cart SET quantity = ? WHERE user_id = ? AND product_id = ?"
|
||||
try:
|
||||
c = self.conn.cursor()
|
||||
c.execute(sql, (quantity, user_id, product_id))
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(e)
|
||||
|
||||
def remove_from_cart(self, cart_id):
|
||||
"""Remove a specific item from the cart."""
|
||||
sql = "DELETE FROM cart WHERE id = ?"
|
||||
try:
|
||||
c = self.conn.cursor()
|
||||
c.execute(sql, (cart_id,))
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(e)
|
||||
|
||||
def clear_cart(self, user_id):
|
||||
"""Clear all items from a user's cart."""
|
||||
sql = "DELETE FROM cart WHERE user_id = ?"
|
||||
try:
|
||||
c = self.conn.cursor()
|
||||
c.execute(sql, (user_id,))
|
||||
self.conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
print(e)
|
||||
|
||||
def main():
|
||||
database = "framework_driver/db/shop.db"
|
||||
|
||||
cart_db = SQLCartRepository(database)
|
||||
|
||||
ex_user_id = 1
|
||||
|
||||
# delete all cart items
|
||||
cart_db.clear_cart(ex_user_id)
|
||||
|
||||
# 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.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.get_cart_by_user_id(ex_user_id))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -3,7 +3,7 @@ from typing import Optional, List
|
|||
import sqlite3
|
||||
|
||||
#dependency imports
|
||||
from use_cases.interfaces import IProductRepository
|
||||
from use_cases.interfaces.repositories import IProductRepository
|
||||
from entities import Product
|
||||
|
||||
class SQLProductRepository(IProductRepository):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Optional, List
|
|||
import sqlite3
|
||||
|
||||
#dependency imports
|
||||
from use_cases.interfaces import IUserRepository
|
||||
from use_cases.interfaces.repositories import IUserRepository
|
||||
from entities import User
|
||||
|
||||
class SQLUserRepository(IUserRepository):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
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
|
||||
|
|
@ -14,8 +13,8 @@ import os
|
|||
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(cart_router)
|
||||
app.include_router(product_router)
|
||||
app.include_router(user_router)
|
||||
|
||||
# Allow CORS for all origins (for simplicity)
|
||||
app.add_middleware(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# USE CASES
|
||||
The software in the use cases layer contains application-specific business rules. It
|
||||
"The software in the use cases layer contains application-specific business rules. It
|
||||
encapsulates and implements all of the use cases of the system. These use cases
|
||||
orchestrate the flow of data to and from the entities, and direct those entities to use
|
||||
their Critical Business Rules to achieve the goals of the use case.
|
||||
|
|
@ -8,4 +8,6 @@ this layer to be affected by changes to externalities such as the database, the
|
|||
any of the common frameworks. The use cases layer is isolated from such concerns.
|
||||
We do, however, expect that changes to the operation of the application will affect
|
||||
the use cases and, therefore, the software in this layer. If the details of a use case
|
||||
change, then some code in this layer will certainly be affected.
|
||||
change, then some code in this layer will certainly be affected." -- Robert C. Martin, Clean Architecture
|
||||
|
||||
# Aufgabe
|
||||
|
|
@ -3,11 +3,13 @@ from typing import Optional, List
|
|||
|
||||
#dependency imports
|
||||
from entities import Product
|
||||
from use_cases.interfaces.product_repository import IProductRepository
|
||||
from use_cases.interfaces.repositories import IProductRepository
|
||||
from use_cases.interfaces.ports import ViewAllProductsInputPort, ViewAllProductsOutputPort
|
||||
|
||||
class ViewAllProducts:
|
||||
def __init__(self, product_repository: IProductRepository):
|
||||
class ViewAllProducts(ViewAllProductsInputPort):
|
||||
def __init__(self, product_repository: IProductRepository, output_port: ViewAllProductsOutputPort):
|
||||
self.product_repository = product_repository
|
||||
self.output_port = output_port
|
||||
|
||||
def execute(self) -> Optional[List[Product]]:
|
||||
"""
|
||||
|
|
@ -16,4 +18,4 @@ class ViewAllProducts:
|
|||
product_data = self.product_repository.get_all_products()
|
||||
if not product_data:
|
||||
return None
|
||||
return product_data
|
||||
return self.output_port.present(product_data)
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#python imports
|
||||
from typing import Optional
|
||||
|
||||
#dependency imports
|
||||
from entities import Cart, CartItem
|
||||
from use_cases.interfaces.cart_repository import ICartRepository
|
||||
from use_cases.interfaces.base_output_port import BaseOutputPort
|
||||
from use_cases.interfaces.base_input_port import BaseInputPort
|
||||
|
||||
"""
|
||||
class ViewCartInputPort():
|
||||
def execute(self, name: str, email: str) -> None:
|
||||
pass
|
||||
|
||||
class ViewCartOutputPort():
|
||||
def present(self, user: dict) -> dict:
|
||||
pass
|
||||
"""
|
||||
class ViewCart(BaseInputPort):
|
||||
def __init__(self, repository: ICartRepository, output_port: BaseOutputPort):
|
||||
self.repository = repository
|
||||
self.output_port = output_port
|
||||
|
||||
def execute(self, input_data) -> Optional[Cart]:
|
||||
"""
|
||||
Fetches the cart data from the repository.
|
||||
"""
|
||||
if not hasattr(input_data, 'user_id'):
|
||||
raise ValueError("Missing 'user_id' in input_data")
|
||||
cart = self.repository.get_cart_by_user_id(input_data.user_id)
|
||||
if not cart:
|
||||
return None
|
||||
cart.calculate_total_price()
|
||||
return self.output_port.present(cart)
|
||||
|
|
@ -3,11 +3,13 @@ from typing import Optional
|
|||
|
||||
#dependency imports
|
||||
from entities import Product
|
||||
from use_cases.interfaces.product_repository import IProductRepository
|
||||
from use_cases.interfaces.repositories import IProductRepository
|
||||
from use_cases.interfaces.ports import ViewProductInputPort, ViewProductOutputPort
|
||||
|
||||
class ViewProduct:
|
||||
def __init__(self, product_repository: IProductRepository):
|
||||
class ViewProduct(ViewProductInputPort):
|
||||
def __init__(self, product_repository: IProductRepository, output_port: ViewProductOutputPort):
|
||||
self.product_repository = product_repository
|
||||
self.output_port = output_port
|
||||
|
||||
def execute(self, product_id: int) -> Optional[Product]:
|
||||
"""
|
||||
|
|
@ -17,4 +19,4 @@ class ViewProduct:
|
|||
if not product_data:
|
||||
return None
|
||||
|
||||
return product_data
|
||||
return self.output_port.present(product_data)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
class BaseOutputPort:
|
||||
def present(self, output_data):
|
||||
"""
|
||||
Abstract method to present use case output.
|
||||
"""
|
||||
raise NotImplementedError("present must be implemented by a subclass")
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
class BaseRepository:
|
||||
pass
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from typing import Optional, List
|
||||
|
||||
#dependency imports
|
||||
from entities import Cart
|
||||
from .base_repository import BaseRepository
|
||||
|
||||
class ICartRepository(BaseRepository):
|
||||
def get_cart_by_user_id(self, user_id: int) -> Optional[Cart]:
|
||||
"""
|
||||
Abstract method to fetch cart data by user ID.
|
||||
"""
|
||||
raise NotImplementedError("get_cart_by_user_id must be implemented by a subclass")
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
class BaseInputPort:
|
||||
def execute(self, input_data):
|
||||
class ViewAllProductsInputPort:
|
||||
def execute(self):
|
||||
"""
|
||||
Abstract method to execute use case.
|
||||
"""
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from entities import Product
|
||||
from typing import List
|
||||
class ViewAllProductsOutputPort:
|
||||
def present(self, product_list:List[Product]):
|
||||
"""
|
||||
Abstract method to execute use case.
|
||||
"""
|
||||
raise NotImplementedError("execute must be implemented by a subclass")
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class ViewProductInputPort:
|
||||
def execute(self, product_id:int):
|
||||
"""
|
||||
Abstract method to execute use case.
|
||||
"""
|
||||
raise NotImplementedError("execute must be implemented by a subclass")
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from entities import Product
|
||||
class ViewProductOutputPort:
|
||||
def present(self, product:Product):
|
||||
"""
|
||||
Abstract method to execute use case.
|
||||
"""
|
||||
raise NotImplementedError("execute must be implemented by a subclass")
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import importlib
|
||||
import pkgutil
|
||||
import inspect
|
||||
|
||||
__all__ = []
|
||||
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
|
||||
module = importlib.import_module(f".{module_name}", __name__)
|
||||
for name, obj in inspect.getmembers(module, inspect.isclass):
|
||||
if obj.__module__ == module.__name__:
|
||||
globals()[name] = obj
|
||||
__all__.append(name)
|
||||
Loading…
Reference in New Issue