Added identity

This commit is contained in:
jableader 2024-04-25 12:03:30 +10:00
parent 6ba6406427
commit edbe082387
6 changed files with 71 additions and 51 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
.venv/ .venv/
__pycache__ __pycache__
data/ data/
.vscode/*

22
main.py
View file

@ -4,8 +4,9 @@ import datetime
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Annotated 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.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
app = FastAPI() app = FastAPI()
@ -27,6 +28,9 @@ async def get_db():
finally: finally:
await sql_db.close() 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") @app.get("/recipes/parse")
async def parse_recipe_handler(url: str, conn: sqlite3.Connection = Depends(get_db)) -> recipes.Recipe: async def parse_recipe_handler(url: str, conn: sqlite3.Connection = Depends(get_db)) -> recipes.Recipe:
parsed = await recipes.parse_recipe(conn, url) 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 return r
@app.post('/recipes/') @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: if not item.ingredients:
return JSONResponse(status_code=400, content={'message': 'Recipe must have at least one ingredient'}) 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: if not ingredient.product:
return JSONResponse(status_code=400, content={'message': 'Ingredient must have a 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) await recipes.insert_recipe(conn, item)
for ingredient in item.ingredients: for ingredient in item.ingredients:
ingredient.recipe_id = item.id ingredient.recipe_id = item.id
@ -169,3 +174,16 @@ async def create_person(person: persons.Person, conn: sqlite3.Connection = Depen
await persons.insert(conn, person) await persons.insert(conn, person)
await conn.commit() await conn.commit()
return person 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

View file

@ -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
"""

View file

@ -1,5 +1,5 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, ClassVar from typing import List
class Person(BaseModel): class Person(BaseModel):
id: int id: int
@ -12,6 +12,22 @@ async def create(conn):
name TEXT UNIQUE 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: async def get_by_id(conn, id: int) -> Person:
cursor = await conn.execute(''' cursor = await conn.execute('''
SELECT id, name SELECT id, name

View file

@ -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 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 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: async def parse_recipe(conn, url: str) -> Recipe:
ldata = await _scrape_recipe_ldata(url) ldata = await _scrape_recipe_ldata(url)
if ldata: if ldata:
@ -30,6 +28,5 @@ async def _get_recipe_from_ldata(conn, url: str, ldata: dict) -> dict:
name=name, name=name,
link=url, link=url,
image_urls=images, image_urls=images,
raw_data=json.dumps(ldata),
ingredients=ingredients ingredients=ingredients
) )

View file

@ -1,34 +1,53 @@
import json import json, datetime
from persons import Person
from ingredients import Ingredient, find_ingredients_by_recipe_id from ingredients import Ingredient, find_ingredients_by_recipe_id
from pydantic import BaseModel, Field from pydantic import BaseModel
from typing import List, ClassVar, Tuple from typing import List, ClassVar, Tuple, Optional
class Recipe(BaseModel): 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 id: int
name: str name: str
link: str link: str
raw_data: str
image_urls: List[str] = [] image_urls: List[str] = []
ingredients: List[Ingredient] = [] 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): async def create(conn):
await conn.execute(''' await conn.execute('''
CREATE TABLE IF NOT EXISTS Recipe ( CREATE TABLE IF NOT EXISTS Recipe (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT, name TEXT NOT NULL,
link TEXT, link TEXT NOT NULL,
image_urls TEXT, image_urls TEXT NOT NULL,
raw_data TEXT 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 def insert_recipe(conn, recipe: Recipe):
async with conn.execute(''' async with conn.execute('''
INSERT INTO Recipe (name, link, raw_data, image_urls) INSERT INTO Recipe (name, link, image_urls, based_on_recipe, created_by_id)
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?, ?)
''', (recipe.name, recipe.link, recipe.raw_data, json.dumps(recipe.image_urls))) as cursor: ''', (recipe.name, recipe.link, json.dumps(recipe.image_urls), recipe.based_on_recipe, recipe.created_by_id)) as cursor:
recipe.id = cursor.lastrowid recipe.id = cursor.lastrowid
def row_to_recipe(col_tuples: List[Tuple[str, ...]]) -> Recipe: def row_to_recipe(col_tuples: List[Tuple[str, ...]]) -> Recipe: