From edbe082387888bf7e047dd67069c6cd883dc158b Mon Sep 17 00:00:00 2001 From: jableader Date: Thu, 25 Apr 2024 12:03:30 +1000 Subject: [PATCH] Added identity --- .gitignore | 3 ++- main.py | 24 +++++++++++++++++++++--- model.py | 31 ------------------------------- persons/db.py | 18 +++++++++++++++++- recipes/__init__.py | 3 --- recipes/db.py | 43 +++++++++++++++++++++++++++++++------------ 6 files changed, 71 insertions(+), 51 deletions(-) delete mode 100644 model.py diff --git a/.gitignore b/.gitignore index c0b4f74..5b73011 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .venv/ __pycache__ -data/ \ No newline at end of file +data/ +.vscode/* \ No newline at end of file diff --git a/main.py b/main.py index f50ad23..b0771cb 100644 --- a/main.py +++ b/main.py @@ -4,8 +4,9 @@ import datetime from pydantic import BaseModel from typing import List, Annotated -from fastapi import FastAPI, Depends, Query +from fastapi import FastAPI, Depends, Query, Cookie from fastapi.responses import JSONResponse +from fastapi.encoders import jsonable_encoder from fastapi.middleware.cors import CORSMiddleware app = FastAPI() @@ -27,6 +28,9 @@ async def get_db(): finally: await sql_db.close() +async def cookie_person(user_id: Annotated[int, Cookie(alias='user_id')], conn: sqlite3.Connection = Depends(get_db)) -> persons.Person: + return await persons.find_person_by_id(conn, user_id) + @app.get("/recipes/parse") async def parse_recipe_handler(url: str, conn: sqlite3.Connection = Depends(get_db)) -> recipes.Recipe: parsed = await recipes.parse_recipe(conn, url) @@ -89,7 +93,7 @@ async def get_recipe(recipe_id: int, conn: sqlite3.Connection = Depends(get_db)) return r @app.post('/recipes/') -async def create_recipe(item: recipes.Recipe, conn: sqlite3.Connection = Depends(get_db)) -> recipes.Recipe: +async def create_recipe(item: recipes.Recipe, conn: sqlite3.Connection = Depends(get_db), user: persons.Person = Depends(cookie_person)) -> recipes.Recipe: if not item.ingredients: return JSONResponse(status_code=400, content={'message': 'Recipe must have at least one ingredient'}) @@ -97,6 +101,7 @@ async def create_recipe(item: recipes.Recipe, conn: sqlite3.Connection = Depends if not ingredient.product: return JSONResponse(status_code=400, content={'message': 'Ingredient must have a product'}) + item.created_by_id = user.id await recipes.insert_recipe(conn, item) for ingredient in item.ingredients: ingredient.recipe_id = item.id @@ -168,4 +173,17 @@ async def get_persons(conn: sqlite3.Connection = Depends(get_db)) -> List[meals. async def create_person(person: persons.Person, conn: sqlite3.Connection = Depends(get_db)) -> persons.Person: await persons.insert(conn, person) await conn.commit() - return person \ No newline at end of file + return person + +class LoginBody(BaseModel): + username: str + +@app.post('/auth/login') +async def login(data: LoginBody, conn: sqlite3.Connection = Depends(get_db)) -> persons.Person: + person = await persons.get_by_name(conn, data.username) + if not person: + return JSONResponse(status_code=404, content={'message': 'Person not found'}) + + response = JSONResponse(content=jsonable_encoder(person)) + response.set_cookie(key='user_id', value=str(person.id)) + return response \ No newline at end of file diff --git a/model.py b/model.py deleted file mode 100644 index 3bca6d4..0000000 --- a/model.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import List, ClassVar -from pydantic import BaseModel - -""" -class Person: - def __init__(self, id: int, name: str) -> None: - self.id = id - self.name = name - -class Meal: - def __init__(self, id: int, date: datetime, chef: List[Person], cleanup: List[Person], - consumers: List[Person], recipes: List[Recipe]) -> None: - self.id = id - self.date = date - self.chef = chef - self.cleanup = cleanup - self.consumers = consumers - self.recipes = recipes - -class ShoppingListItem: - def __init__(self, id: int, food_item: Product, measure: Measurement, source: Union[Meal, None]) -> None: - self.id = id - self.food_item = food_item - self.measure = measure - self.source = source - -class ShoppingList: - def __init__(self, date: datetime, items: List[ShoppingListItem]) -> None: - self.date = date - self.items = items -""" diff --git a/persons/db.py b/persons/db.py index 4fd313e..618d4c2 100644 --- a/persons/db.py +++ b/persons/db.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -from typing import List, ClassVar +from typing import List class Person(BaseModel): id: int @@ -11,6 +11,22 @@ async def create(conn): id INTEGER PRIMARY KEY, name TEXT UNIQUE );''') + + await insert(conn, Person(id=0, name='Jacob')) + await insert(conn, Person(id=0, name='Ellie')) + await insert(conn, Person(id=0, name='Ryan')) + await insert(conn, Person(id=0, name='Chris')) + +async def get_by_name(conn, name: str) -> Person: + cursor = await conn.execute(''' + SELECT id, name + FROM Person + WHERE name = ? + ''', (name,)) + row = await cursor.fetchone() + if not row: + return None + return Person(id=row[0], name=row[1]) async def get_by_id(conn, id: int) -> Person: cursor = await conn.execute(''' diff --git a/recipes/__init__.py b/recipes/__init__.py index 246f976..2180908 100644 --- a/recipes/__init__.py +++ b/recipes/__init__.py @@ -2,8 +2,6 @@ from recipes.db import Recipe, insert_recipe, find_recipe_by_id, get_all, find_r from recipes.scraping import scrape_recipe_ldata as _scrape_recipe_ldata from ingredients import parse_ingredient_from_nlp as _parse_ingredient_from_nlp, match_existing_products as _match_existing_products -import json - async def parse_recipe(conn, url: str) -> Recipe: ldata = await _scrape_recipe_ldata(url) if ldata: @@ -30,6 +28,5 @@ async def _get_recipe_from_ldata(conn, url: str, ldata: dict) -> dict: name=name, link=url, image_urls=images, - raw_data=json.dumps(ldata), ingredients=ingredients ) \ No newline at end of file diff --git a/recipes/db.py b/recipes/db.py index 20b5a52..d068f4d 100644 --- a/recipes/db.py +++ b/recipes/db.py @@ -1,34 +1,53 @@ -import json +import json, datetime +from persons import Person from ingredients import Ingredient, find_ingredients_by_recipe_id -from pydantic import BaseModel, Field -from typing import List, ClassVar, Tuple +from pydantic import BaseModel +from typing import List, ClassVar, Tuple, Optional class Recipe(BaseModel): - KEYS: ClassVar[List[str]] = ['id', 'name', 'link', 'image_urls', 'raw_data'] + KEYS: ClassVar[List[str]] = ['id', 'name', 'link', 'image_urls', 'based_on_recipe', 'created_by_id', 'date_created', 'hidden_by_id', 'date_hidden'] id: int name: str link: str - raw_data: str image_urls: List[str] = [] ingredients: List[Ingredient] = [] + based_on_recipe: Optional[int] = None + + date_created: Optional[datetime.datetime] = None + created_by_id: Optional[int] = None + created_by: Optional[Person] = None + + date_hidden: Optional[datetime.datetime] = None + hidden_by_id: Optional[int] = None + hidden_by: Optional[Person] = None async def create(conn): await conn.execute(''' CREATE TABLE IF NOT EXISTS Recipe ( id INTEGER PRIMARY KEY, - name TEXT, - link TEXT, - image_urls TEXT, - raw_data TEXT + name TEXT NOT NULL, + link TEXT NOT NULL, + image_urls TEXT NOT NULL, + based_on_recipe INTEGER NULL, + + date_created DATETIME DEFAULT CURRENT_TIMESTAMP, + created_by_id INTEGER NOT NULL, + + date_hidden DATETIME DEFAULT NULL, + hidden_by_id INTEGER DEFAULT NULL, + + FOREIGN KEY (based_on_recipe) REFERENCES Recipe(id) + FOREIGN KEY (created_by_id) REFERENCES Person(id) + FOREIGN KEY (hidden_by_id) REFERENCES Person(id) );''') async def insert_recipe(conn, recipe: Recipe): async with conn.execute(''' - INSERT INTO Recipe (name, link, raw_data, image_urls) - VALUES (?, ?, ?, ?) - ''', (recipe.name, recipe.link, recipe.raw_data, json.dumps(recipe.image_urls))) as cursor: + INSERT INTO Recipe (name, link, image_urls, based_on_recipe, created_by_id) + VALUES (?, ?, ?, ?, ?) + ''', (recipe.name, recipe.link, json.dumps(recipe.image_urls), recipe.based_on_recipe, recipe.created_by_id)) as cursor: recipe.id = cursor.lastrowid def row_to_recipe(col_tuples: List[Tuple[str, ...]]) -> Recipe: