Compare commits

..

2 Commits

Author SHA1 Message Date
Felix Jan Michael Mucha d1f0ff3375 updated readme 2025-01-27 14:31:41 +01:00
michael 569589a4f5 add Setup Test 2025-01-26 13:24:46 +01:00
37 changed files with 62 additions and 830 deletions

1
.gitignore vendored
View File

@ -2,5 +2,4 @@
__pycache__/
*.py[cod]
*$py.class
/env

View File

@ -3,12 +3,11 @@
** Master MDS HSMA **
Clean Architecture - Architekturstil am praktischen Beispiel z.B. in Python.
Check README in layer for task description
## API:
- 0: open Terminal
- 1: navigate to src: cd .\src\
- 2: run python main.py
- 3 go to url: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)
- 4: close with: strg + c
## Setup Test
1. falls python noch nicht installiert, installieren
2. navigiere zum repo ```cd IWS_WS24_clean_architecture```
3. ```pip install -r requirements.txt```
4. ```python main.py``` ausführen und Terminal offen lassen
5. index.html mit Browser öffnen
6. Wenn alles geklappt hat sollte die index.html Hello World anzeigen
7. mit ```STRG + C``` kann das script im Terminal gestoppt werden

26
index.html 100644
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Setup Test</title>
</head>
<body>
<h1 id="greetings">Not Working</h1>
<script>
// Fetch Hello World from the FastAPI endpoint
fetch('http://127.0.0.1:8000/greetings')
.then(response => response.json())
.then(data => {
const greetingsContainer = document.getElementById('greetings');
greetingsContainer.innerHTML = '';
data.greetings.forEach(greeting => {
const div = document.createElement('div');
div.textContent = `${greeting[1]}`;
greetingsContainer.appendChild(div);
});
});
</script>
</body>
</html>

26
main.py 100644
View File

@ -0,0 +1,26 @@
import sqlite3
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
app = FastAPI()
# Allow CORS for all origins (for simplicity)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/greetings")
async def get_greetings():
connection = sqlite3.connect("HelloWorld.db")
cursor = connection.cursor()
cursor.execute("SELECT * FROM greetings")
rows = cursor.fetchall()
return {"greetings": rows}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)

2
requirements.txt 100644
View File

@ -0,0 +1,2 @@
fastapi
uvicorn

View File

@ -1,17 +0,0 @@
# ENTITIES
"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 doesnt
matter so long as the entities can be used by many different applications in the
enterprise.
If you dont have an enterprise and are writing just a single application, then these
entities are the business objects of the application. They encapsulate the most general
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." -- Robert C. Martin, Clean Architecture
# Aufgabe
### Anforderungen:
- **Entity** Implemntiere eine user entity in user.py

View File

@ -1,38 +0,0 @@
class Product:
"""
Represents a product with an ID, name, description, price, and quantity.
"""
def __init__(self, id: str, name: str, description: str, price: float, quantity: int):
"""
Initializes a new product.
Args:
id (str): The unique identifier for the product.
name (str): The name of the product.
description (str): A brief description of the product.
price (float): The price of the product.
quantity (int): The available quantity of the product.
"""
self.id = id
self.name = name
self.description = description
self.price = price
self.quantity = quantity
self.post_init()
def post_init(self):
"""
Validates the product's price.
Raises:
ValueError: If the price is negative.
"""
if self.price < 0:
raise ValueError("Der Preis darf nicht negativ sein.")
def __repr__(self):
"""
Returns a string representation of the product.
"""
return f"Product(id={self.id}, name={self.name}, description={self.description}, price={self.price}, quantity={self.quantity})"

View File

@ -1,7 +0,0 @@
class User:
#TODO: implement in exercise 'entities'
#TODO: user should have: id (int) but what other attributes should a user have?
#TODO: user should have methods but what methods should a user have?
pass

View File

@ -1,16 +0,0 @@
# FRAMEWORKS AND DRIVERS
The outermost layer of the model in Figure 22.1 is generally composed of
frameworks and tools such as the database and the web framework. Generally you
dont write much code in this layer, other than glue code that communicates to the
next circle inward.
The frameworks and drivers layer is where all the details go. The web is a detail. The
database is a detail. We keep these things on the outside where they can do little
harm.
# Aufgabe
### Anforderungen:
#### Implementierungen:
- **UI**: Die UI ist ein Framework. Innerhalb der UI ist es möglich ebenfalls Clean Architecture zu verwenden. In diesem Fall wird MVC verwendet. Implementiere die `createUser`-Methode in `conroller.js` um einen User über die UI anzulegen.
- **DB**: Die Datenbank ist ein Driver. Um einen User anlegen zu können wird eine passende Tabelle in der Datenbank benötigt. Lege diese über das `db/db_setup.py` script an. Über `python setup_db.py` in `src` kann die Datenbank neu aufgesetzt werden.

View File

@ -1,60 +0,0 @@
import sqlite3
def create_connection(db_file):
"""Create a database connection to the SQLite database specified by db_file."""
conn = None
try:
conn = sqlite3.connect(db_file)
print(f"Connected to {db_file}")
except sqlite3.Error as e:
print(e)
return conn
def create_table(conn, create_table_sql):
"""Create a table from the create_table_sql statement."""
try:
c = conn.cursor()
c.execute(create_table_sql)
print("Table created successfully")
except sqlite3.Error as e:
print(e)
def delete_table(conn, table_name):
"""Delete a table specified by table_name."""
try:
c = conn.cursor()
c.execute(f"DROP TABLE IF EXISTS {table_name}")
print(f"Table {table_name} deleted successfully")
except sqlite3.Error as e:
print(e)
def main():
database = "framework_driver/db/shop.db"
#TODO: Create User Table
sql_user_table = """
"""
sql_product_table = """CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL,
description TEXT NOT NULL,
quantity INTEGER NOT NULL
)"""
conn = create_connection(database)
if conn is not None:
# Delete tables if they exist
delete_table(conn, "products")
delete_table(conn, "users")
# Create tables
create_table(conn, sql_user_table)
create_table(conn, sql_product_table)
else:
print("Error! Cannot create the database connection.")
if __name__ == '__main__':
main()

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Webshop</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
header {
background: #f4f4f4;
padding: 10px;
display: flex;
justify-content: space-around;
}
header a {
text-decoration: none;
color: #333;
font-weight: bold;
cursor: pointer;
}
#main {
padding: 20px;
}
</style>
</head>
<body>
<header>
<a id="products-tab" href="#">Products</a>
<a id="user-tab" href="#">User</a>
</header>
<main id="main"></main>
<script src="./src/view.js"></script>
<script src="./src/model.js"></script>
<script src="./src/controller.js"></script>
</body>
</html>

View File

@ -1,75 +0,0 @@
// Controller: Handles user interactions
const Controller = {
async createUser(name, email, password){
//TODO: use model to send request to backend, then call View to render response
alert('User Creation functionality not implemented.');
},
async showProducts() {
const productsResponse = await Model.fetchProducts();
if(productsResponse.status=="success"){
const products = productsResponse.data;
View.renderProducts(products);
}
Controller.addProductClickHandlers();
},
async showProduct(productId) {
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();
});
},
showUserRegister(){
View.renderUserRegister();
document.getElementById('create-user-button').addEventListener('click', () => {
const name = document.getElementById('user-name').value;
const email = document.getElementById('user-email').value;
const password = document.getElementById('user-password').value;
Controller.createUser(name, email, password);
});
},
addEventListeners() {
document.getElementById('products-tab').addEventListener('click', (event) => {
event.preventDefault();
Controller.showProducts();
});
document.getElementById('user-tab').addEventListener('click', (event) => {
event.preventDefault();
Controller.showUserRegister();
});
document.querySelectorAll('.product-link').forEach((link) => {
link.addEventListener('click', (event) => {
event.preventDefault();
const productId = event.target.getAttribute('data-id');
Controller.showProduct(productId);
});
});
},
addProductClickHandlers() {
document.querySelectorAll('.product-link').forEach((link) => {
link.addEventListener('click', (event) => {
event.preventDefault();
const productId = event.target.getAttribute('data-id');
Controller.showProduct(productId);
});
});
},
init() {
this.addEventListeners();
this.showProducts(); // Default view
},
};
// Initialize the application
Controller.init();

View File

@ -1,20 +0,0 @@
// Model: Handles data fetching
const Model = {
//TODO: Add function to send CreateUserRequest to Backend
async fetchProducts() {
const response = await fetch('http://127.0.0.1:8000/view_products');
return response.json();
},
async fetchProduct(productId) {
const response = await fetch(`http://127.0.0.1:8000/view_product/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ product_id: productId }),
});
return response.json();
},
};

View File

@ -1,48 +0,0 @@
// View: Handles rendering
const View = {
//TODO: Add function to render UserCreateResponse
renderProducts(products) {
const main = document.getElementById('main');
main.innerHTML = `
<h1>Products</h1>
<ul>
${products.map(product => View.renderProductInList(product)).join('')}
</ul>
`;
},
renderProductInList(product) {
return `
<li>
<a href="#" data-id="${product.id}" class="product-link">
${product.name} - $${product.price}
</a>
</li>
`;
},
renderProduct(product) {
const main = document.getElementById('main');
main.innerHTML = `
<h1>${product.name}</h1>
<p>Price: $${product.price}</p>
<p>Description: ${product.description}</p>
<button id="back-button">Back to Products</button>
`;
},
renderUserRegister() {
const main = document.getElementById('main');
main.innerHTML = `
<div class="form-container" id="user-form-container">
<h2>Create User</h2>
<div id="user-form">
<input type="text" id="user-name" name="name" placeholder="Enter your name" required>
<input type="text" id="user-email" name="email" placeholder="Enter your email" required>
<input type="password" id="user-password" name="password" placeholder="Enter your password" required>
<button id="create-user-button">Create Account</button>
</div>
`;
},
};

View File

@ -1,27 +0,0 @@
# INTERFACE ADAPTERS
"The software in the interface adapters layer is a set of adapters that convert data from
the format most convenient for the use cases and entities, to the format most
convenient for some external agency such as the database or the web. It is this layer,
for example, that will wholly contain the MVC architecture of a GUI. The
presenters, views, and controllers all belong in the interface adapters layer. The
models are likely just data structures that are passed from the controllers to the use
cases, and then back from the use cases to the presenters and views.
Similarly, data is converted, in this layer, from the form most convenient for entities
and use cases, to the form most convenient for whatever persistence framework is
being used (i.e., the database). No code inward of this circle should know anything at
all about the database. If the database is a SQL database, then all SQL should be
restricted to this layer—and in particular to the parts of this layer that have to do with
the database.
Also in this layer is any other adapter necessary to convert data from some external
form, such as an external service, to the internal form used by the use cases and
entities." -- Robert C. Martin, Clean Architecture
# Aufgabe
### Anforderungen:
#### Implementierungen:
- **CreateUserRequest, CreateUserResponse**: Diese Objekte werden für die Kommunikation mit Frameworks wie der UI verwendet. Überlege welche Informationen zwischen Frontend und Backend ausgetauscht werden.
- **CreateUserController**: Der Controller ist dafür verantwortlich die Request entgegenzunehmen, zu validieren und in ein passendes Datenformat für das UseCase umzuwandeln.
- **CreateUserPresenter**: Der Presenter implementiert den OutputPort des UseCases. Er ist dafür verantwortlich die Asugabe zu formatieren und in ein Response DTO umzuwandlen.
- **SQLUserRepository**: Das SQLUserRepository implementiert das IUserRepository Interface und verwendet SQL-Statements um Daten aus der Datenbank zu holen und in ein passendes Format für das UseCase zu konvertieren.

View File

@ -1,84 +0,0 @@
#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.product import Product
from use_cases.view_all_products import ViewAllProducts, IViewAllProductsInputPort, IViewAllProductsOutputPort
from use_cases.view_product import ViewProduct, IViewProductInputPort, IViewProductOutputPort
from interface_adapters.sql_product_repository import SQLProductRepository
class ProductDTO(BaseModel):
id: int
name: str
description: str
price: float
class ViewProductRequest(BaseModel):
product_id: int
class ViewProductResponse(BaseModel):
status: str
data: ProductDTO
class ViewProductPresenter(IViewProductOutputPort):
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: IViewProductInputPort) -> 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_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(IViewAllProductsOutputPort):
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: IViewAllProductsInputPort) -> 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=ViewProductResponse)
async def view_product(request: ViewProductRequest):
return view_product_controller.handle_request(request)
@router.get("/view_products", response_model=ViewAllProductsResponse)
async def view_products():
return view_all_products_controller.handle_request()

View File

@ -1,74 +0,0 @@
#python imports
from typing import Optional, List
import sqlite3
#dependency imports
from use_cases.product_repository import IProductRepository
from entities.product 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 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 Product(**r)
except sqlite3.Error as e:
print(e)
return None
def add_product(self, product):
"""Add a new product to the products table."""
sql = "INSERT INTO products (name, price, description, quantity) VALUES (?, ?, ?, ?)"
try:
c = self.conn.cursor()
c.execute(sql, product)
self.conn.commit()
return c.lastrowid
except sqlite3.Error as e:
print(e)
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 [Product(**product) for product in rows]
except sqlite3.Error as e:
print(e)
return []
def main():
database = "framework_driver/db/shop.db"
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, "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)
products = product_db.get_all_products()
print("All products:")
for product in products:
print(product)
if __name__ == "__main__":
main()

View File

@ -1,13 +0,0 @@
#python imports
from typing import Optional, List
import sqlite3
#dependency imports
from use_cases.user_repository import IUserRepository
from entities.user 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

View File

@ -1,34 +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 fastapi.templating import Jinja2Templates
from pydantic import BaseModel
#dependency imports
#TODO: use correct dependencies
#TODO: define request and response data
class CreateUserRequest(BaseModel):
pass
class CreateUserResponse(BaseModel):
pass
#TODO: implement CreateUserController and CreateUserPresenter
#TODO: initialize all components
create_user_controller = None
router = APIRouter()
@router.post("/create_user", response_model=CreateUserResponse)
async def create_user(request:CreateUserRequest):
response = create_user_controller.handle_request(request)
if not response:
raise HTTPException(status_code=500, detail="Could not create user")
return response

View File

@ -1,34 +0,0 @@
from interface_adapters.product_controller import router as product_router
from interface_adapters.user_controller import router as user_router
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse
from fastapi.responses import HTMLResponse
from fastapi.responses import FileResponse
from fastapi.requests import Request
from fastapi.exceptions import HTTPException
from fastapi import FastAPI
import uvicorn
import os
app = FastAPI()
app.include_router(product_router)
app.include_router(user_router)
# Allow CORS for all origins (for simplicity)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/", include_in_schema=False)
async def root():
return RedirectResponse(url="/docs")
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)

View File

@ -1,5 +0,0 @@
fastapi
httpx
pydantic
uvicorn
sqllite3

View File

@ -1,4 +0,0 @@
from framework_driver.db import db_setup
from interface_adapters import sql_product_repository
db_setup.main()
sql_product_repository.main()

View File

@ -1,2 +0,0 @@
# Tests
To run tests: python -m unittest discover

View File

View File

@ -1,11 +0,0 @@
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)

View File

@ -1,21 +0,0 @@
#python imports
from typing import Optional, List
#dependency imports
from use_cases import ICartRepository
class CartDatabase(ICartRepository):
def __init__(self):
# Mock database setup
self.mock_db = {
1: [
{"product_id": 101, "name": "Apple", "quantity": 2, "price": 0.5},
{"product_id": 102, "name": "Milk", "quantity": 1, "price": 1.5},
],
2: [
{"product_id": 201, "name": "Bread", "quantity": 1, "price": 2.0},
],
}
def get_cart_by_user_id(self, user_id: int) -> Optional[List[dict]]:
return self.mock_db.get(user_id)

View File

@ -1,16 +0,0 @@
#python imports
from typing import Optional, List
#dependency imports
from use_cases import IProductRepository
class ProductDatabase(IProductRepository):
def __init__(self):
# Mock database setup
self.mock_db = {
1: {"id": 1, "name": "Apple", "description": "gsdg", "price": 0.5},
2: {"id": 2, "name": "Bread", "description": "sfg", "price": 2.0},
}
def get_product_by_id(self, product_id: int) -> Optional[List[dict]]:
return self.mock_db.get(product_id)

View File

@ -1,19 +0,0 @@
from use_cases import ViewCart
from tests.mocks import CartDatabase
import unittest
class TestViewCart(unittest.TestCase):
def setUp(self):
self.cart_database = CartDatabase()
self.view_cart_use_case = ViewCart(self.cart_database)
def test_view_cart_valid_id(self):
result = self.view_cart_use_case.execute(1)
self.assertIsNotNone(result)
#self.assertEqual(result['id'], 1)
if __name__ == "__main__":
unittest.main()

View File

@ -1,19 +0,0 @@
from use_cases import ViewProduct
from tests.mocks import ProductDatabase
import unittest
class TestViewProduct(unittest.TestCase):
def setUp(self):
self.product_database = ProductDatabase()
self.view_product_use_case = ViewProduct(self.product_database)
def test_view_product_valid_id(self):
result = self.view_product_use_case.execute(1)
self.assertIsNotNone(result)
#self.assertEqual(result['id'], 1)
if __name__ == "__main__":
unittest.main()

View File

@ -1,23 +0,0 @@
# USE CASES
"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.
We do not expect changes in this layer to affect the entities. We also do not expect
this layer to be affected by changes to externalities such as the database, the UI, or
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." -- Robert C. Martin, Clean Architecture
# Aufgabe
### Anforderungen:
#### Interfaces:
- **CreateUserInputPort**: Dieses Interface wird durch die execute methode des UseCases implementiert. Dadurch wird der Controller unabhängig von Änderungen im UseCase. Überlege dir welche Daten das UseCase als Input bekommt und definiere ein passendes Interface.
- **CreateUserOutputPort**: Dieses Interface wird vom UseCase verwendet um Daten auszugeben. Es wird durch die present methode des Presenters implementiert. Dadurch wird das UseCase unabhängig vom Presenter. Überlege dir welche daten das UseCase zurück gibt und definiere ein passendes Interface.
- **IUserRepository**: Dieses Interface wird vom UseCase verwendet um Daten aus der Datenbank zu holen. Dadurch wird das UseCase unabhängig von Datenbanken. Überlege dir welche Queries ausgeführt werden müssen und definiere ein passendes Interface.
#### UseCase
Implementiere einen UseCase zum Erstellen eines Users. Das UseCase sollte den CreateUserInputPort implementieren, das IUserRepository verwenden um mit der Datenbank zu kommunizieren und den CreateUserOutputPort verwenden um Daten auszugeben.

View File

@ -1,19 +0,0 @@
#python imports
from typing import Optional
#dependency imports
#TODO: A1: Use correct dependencies
#TODO: A2: Create InputPort
#TODO: A3: Create OutputPort
#TODO: A5: Use Case CreateUser:
class CreateUser:
def __init__(self):
pass
#TODO: Initialize required Interfaces
def execute(self):
pass
#TODO: Implement Use Case

View File

@ -1,17 +0,0 @@
from typing import Optional, List
#dependency imports
from entities.product import Product
class IProductRepository:
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) -> Optional[List[Product]]:
"""
Abstract method to fetch product data.
"""
raise NotImplementedError("get_all_products must be implemented by a subclass")

View File

@ -1,5 +0,0 @@
from typing import Optional, List
class IUserRepository:
pass
#TODO: A4: Define interface functions for creating a user

View File

@ -1,34 +0,0 @@
#python imports
from typing import Optional, List
#dependency imports
from entities.product import Product
from use_cases.product_repository import IProductRepository
class IViewAllProductsInputPort:
def execute(self):
"""
Abstract method to execute use case.
"""
raise NotImplementedError("execute must be implemented by a subclass")
class IViewAllProductsOutputPort:
def present(self, product_list:List[Product]):
"""
Abstract method to execute use case.
"""
raise NotImplementedError("execute must be implemented by a subclass")
class ViewAllProducts(IViewAllProductsInputPort):
def __init__(self, product_repository: IProductRepository, output_port: IViewAllProductsOutputPort):
self.product_repository = product_repository
self.output_port = output_port
def execute(self) -> Optional[List[Product]]:
"""
Fetches the product data from the repository.
"""
product_data = self.product_repository.get_all_products()
if not product_data:
return None
return self.output_port.present(product_data)

View File

@ -1,35 +0,0 @@
#python imports
from typing import Optional
#dependency imports
from entities.product import Product
from use_cases.product_repository import IProductRepository
class IViewProductInputPort:
def execute(self, product_id:int):
"""
Abstract method to execute use case.
"""
raise NotImplementedError("execute must be implemented by a subclass")
class IViewProductOutputPort:
def present(self, product:Product):
"""
Abstract method to execute use case.
"""
raise NotImplementedError("execute must be implemented by a subclass")
class ViewProduct(IViewProductInputPort):
def __init__(self, product_repository: IProductRepository, output_port: IViewProductOutputPort):
self.product_repository = product_repository
self.output_port = output_port
def execute(self, product_id: int):
"""
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 self.output_port.present(product_data)