[refactor] cleanup dead code
This commit is contained in:
parent
2deb66f7e5
commit
20437905ba
27 changed files with 7 additions and 791 deletions
|
|
@ -1,6 +0,0 @@
|
||||||
FROM alpine
|
|
||||||
|
|
||||||
RUN apk add --no-cache curl
|
|
||||||
RUN crontab -l | { cat; echo '1 * * * * curl -X POST "http://tasks:8000/api/cache_product_usage_data" -H "Authorization: Bearer ${API_KEY}"'; } | crontab -
|
|
||||||
|
|
||||||
CMD ["crond", "-f", "-l", "2"]
|
|
||||||
24
Makefile
24
Makefile
|
|
@ -4,56 +4,46 @@ VERSION=0.`git rev-list --count HEAD`
|
||||||
DOCKER_BUILD=docker build
|
DOCKER_BUILD=docker build
|
||||||
|
|
||||||
ifeq ($(shell uname -m), arm64)
|
ifeq ($(shell uname -m), arm64)
|
||||||
DOCKER_BUILD=docker buildx build --platform linux/amd64
|
DOCKER_BUILD=docker buildx build --platform linux/amd64 --load
|
||||||
endif
|
endif
|
||||||
|
|
||||||
api: fooder Dockerfile requirements.txt
|
api: fooder Dockerfile requirements/docker.txt
|
||||||
$(DOCKER_BUILD) -t registry.domandoman.xyz/fooder/api -f Dockerfile .
|
$(DOCKER_BUILD) -t registry.domandoman.xyz/fooder/api -f Dockerfile .
|
||||||
|
|
||||||
cron: Dockerfile.cron
|
build: api
|
||||||
$(DOCKER_BUILD) -t registry.domandoman.xyz/fooder/cron -f Dockerfile.cron .
|
|
||||||
|
|
||||||
build: api cron
|
|
||||||
|
|
||||||
push:
|
push:
|
||||||
docker push registry.domandoman.xyz/fooder/api
|
docker push registry.domandoman.xyz/fooder/api
|
||||||
docker push registry.domandoman.xyz/fooder/cron
|
|
||||||
|
|
||||||
|
.PHONY: black mypy flake lint version create-venv test
|
||||||
black:
|
black:
|
||||||
python -m black fooder
|
python -m black fooder
|
||||||
|
|
||||||
.PHONY: mypy
|
|
||||||
mypy:
|
mypy:
|
||||||
python -m mypy fooder
|
python -m mypy fooder
|
||||||
|
|
||||||
.PHONY: flake
|
|
||||||
flake:
|
flake:
|
||||||
python -m flake8 fooder
|
python -m flake8 fooder
|
||||||
|
|
||||||
.PHONY: lint
|
|
||||||
lint: black mypy flake
|
lint: black mypy flake
|
||||||
|
|
||||||
.PHONY: version
|
|
||||||
version:
|
version:
|
||||||
@echo $(VERSION)
|
@echo $(VERSION)
|
||||||
|
|
||||||
.PHONY: create-venv
|
|
||||||
create-venv:
|
create-venv:
|
||||||
python3 -m venv .venv --prompt="fooderapi-venv" --system-site-packages
|
python3 -m venv .venv --prompt="fooderapi-venv" --system-site-packages
|
||||||
bash -c "source .venv/bin/activate && pip install -r requirements/local.txt"
|
bash -c "source .venv/bin/activate && pip install -r requirements/local.txt"
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test:
|
test:
|
||||||
./test.sh
|
./test.sh
|
||||||
|
|
||||||
.PHONY: alembic
|
# Alembic
|
||||||
alembic:
|
.PHONY: alembic alembic-upgrade alembic-downgrade
|
||||||
|
alembic: fooder
|
||||||
docker compose exec -e MSG="$(MSG)" api bash -c 'alembic -c /opt/fooder/fooder/alembic.ini revision --autogenerate -m "$${MSG}"'
|
docker compose exec -e MSG="$(MSG)" api bash -c 'alembic -c /opt/fooder/fooder/alembic.ini revision --autogenerate -m "$${MSG}"'
|
||||||
|
|
||||||
.PHONY: alembic-upgrade
|
|
||||||
alembic-upgrade:
|
alembic-upgrade:
|
||||||
docker compose exec api bash -c 'alembic -c /opt/fooder/fooder/alembic.ini upgrade head'
|
docker compose exec api bash -c 'alembic -c /opt/fooder/fooder/alembic.ini upgrade head'
|
||||||
|
|
||||||
.PHONY: alembic-downgrade
|
|
||||||
alembic-downgrade:
|
alembic-downgrade:
|
||||||
docker compose exec api bash -c 'alembic -c /opt/fooder/fooder/alembic.ini downgrade -1'
|
docker compose exec api bash -c 'alembic -c /opt/fooder/fooder/alembic.ini downgrade -1'
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ DB_URI="postgresql+asyncpg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database/${PO
|
||||||
ECHO_SQL=0
|
ECHO_SQL=0
|
||||||
|
|
||||||
SECRET_KEY="${SECRET_KEY}" # generate with $ openssl rand -hex 32
|
SECRET_KEY="${SECRET_KEY}" # generate with $ openssl rand -hex 32
|
||||||
API_KEY="${API_KEY}" # generate with $ openssl rand -hex 32
|
|
||||||
REFRESH_SECRET_KEY="${REFRESH_SECRET_KEY}" # generate with $ openssl rand -hex 32
|
REFRESH_SECRET_KEY="${REFRESH_SECRET_KEY}" # generate with $ openssl rand -hex 32
|
||||||
ALGORITHM="HS256"
|
ALGORITHM="HS256"
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||||
REFRESH_TOKEN_EXPIRE_DAYS=30
|
REFRESH_TOKEN_EXPIRE_DAYS=30
|
||||||
|
|
||||||
API_KEY="${API_KEY}" # generate with $ openssl rand -hex 32
|
|
||||||
|
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
from typing import Annotated, Any
|
|
||||||
|
|
||||||
from fastapi import Depends
|
|
||||||
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
||||||
|
|
||||||
from ..auth import authorize_api_key, get_current_user
|
|
||||||
from ..db import get_db_session, AsyncSession
|
|
||||||
from ..domain.user import User
|
|
||||||
|
|
||||||
UserDependency = Annotated[User, Depends(get_current_user)]
|
|
||||||
ApiKeyDependency = Annotated[None, Depends(authorize_api_key)]
|
|
||||||
|
|
||||||
|
|
||||||
class BaseController:
|
|
||||||
def __init__(self, session: AsyncSession) -> None:
|
|
||||||
self.session = session
|
|
||||||
|
|
||||||
async def call(self, *args, **kwargs) -> Any:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
async def __call__(self, *args, **kwargs) -> Any:
|
|
||||||
return await self.call(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class AuthorizedController(BaseController):
|
|
||||||
def __init__(self, session: AsyncSession, user: UserDependency) -> None:
|
|
||||||
super().__init__(session)
|
|
||||||
self.user = user
|
|
||||||
|
|
||||||
|
|
||||||
class TasksSessionController(BaseController):
|
|
||||||
def __init__(self, session: AsyncSession, api_key: ApiKeyDependency) -> None:
|
|
||||||
super().__init__(session)
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.diary import Diary as DBDiary
|
|
||||||
from ..model.diary import Diary
|
|
||||||
from .base import AuthorizedController
|
|
||||||
|
|
||||||
|
|
||||||
class GetDiary(AuthorizedController):
|
|
||||||
async def call(self, date: date) -> Diary:
|
|
||||||
diary = await DBDiary.get_diary(self.session, self.user.id, date)
|
|
||||||
|
|
||||||
if diary is not None:
|
|
||||||
return Diary.from_orm(diary)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
await DBDiary.create(session, self.user.id, date)
|
|
||||||
await session.commit()
|
|
||||||
return Diary.from_orm(
|
|
||||||
await DBDiary.get_diary(session, self.user.id, date)
|
|
||||||
)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.entry import Entry as DBEntry
|
|
||||||
from ..domain.meal import Meal as DBMeal
|
|
||||||
from ..model.entry import CreateEntryPayload, Entry, UpdateEntryPayload
|
|
||||||
from .base import AuthorizedController
|
|
||||||
|
|
||||||
|
|
||||||
class CreateEntry(AuthorizedController):
|
|
||||||
async def call(self, content: CreateEntryPayload) -> Entry:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
meal = await DBMeal.get_by_id(session, self.user.id, content.meal_id)
|
|
||||||
if meal is None:
|
|
||||||
raise HTTPException(status_code=404, detail="meal not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
entry = await DBEntry.create(
|
|
||||||
session, content.meal_id, content.product_id, content.grams
|
|
||||||
)
|
|
||||||
return Entry.from_orm(entry)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateEntry(AuthorizedController):
|
|
||||||
async def call(self, entry_id: int, content: UpdateEntryPayload) -> Entry:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
entry = await DBEntry.get_by_id(session, self.user.id, entry_id)
|
|
||||||
if entry is None:
|
|
||||||
raise HTTPException(status_code=404, detail="entry not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
await entry.update(
|
|
||||||
session, content.meal_id, content.product_id, content.grams
|
|
||||||
)
|
|
||||||
return Entry.from_orm(entry)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteEntry(AuthorizedController):
|
|
||||||
async def call(self, entry_id: int) -> None:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
entry = await DBEntry.get_by_id(session, self.user.id, entry_id)
|
|
||||||
if entry is None:
|
|
||||||
raise HTTPException(status_code=404, detail="entry not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
await entry.delete(session)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.diary import Diary as DBDiary
|
|
||||||
from ..domain.meal import Meal as DBMeal
|
|
||||||
from ..domain.preset import Preset as DBPreset
|
|
||||||
from ..model.meal import (
|
|
||||||
CreateMealFromPresetPayload,
|
|
||||||
CreateMealPayload,
|
|
||||||
Meal,
|
|
||||||
RenameMealPayload,
|
|
||||||
SaveMealPayload,
|
|
||||||
)
|
|
||||||
from ..model.preset import Preset
|
|
||||||
from .base import AuthorizedController
|
|
||||||
|
|
||||||
|
|
||||||
class CreateMeal(AuthorizedController):
|
|
||||||
async def call(self, content: CreateMealPayload) -> Meal:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
if not await DBDiary.has_permission(
|
|
||||||
session, self.user.id, content.diary_id
|
|
||||||
):
|
|
||||||
raise HTTPException(status_code=404, detail="not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
meal = await DBMeal.create(session, content.diary_id, content.name)
|
|
||||||
return Meal.from_orm(meal)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
class SaveMeal(AuthorizedController):
|
|
||||||
async def call(self, meal_id: int, payload: SaveMealPayload) -> Preset:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
meal = await DBMeal.get_by_id(session, self.user.id, meal_id)
|
|
||||||
if meal is None:
|
|
||||||
raise HTTPException(status_code=404, detail="meal not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
return Preset.from_orm(
|
|
||||||
await DBPreset.create(
|
|
||||||
session,
|
|
||||||
user_id=self.user.id,
|
|
||||||
name=payload.name or meal.name,
|
|
||||||
meal=meal,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
class RenameMeal(AuthorizedController):
|
|
||||||
async def call(self, meal_id: int, payload: RenameMealPayload) -> Meal:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
meal = await DBMeal.get_by_id(session, self.user.id, meal_id)
|
|
||||||
if meal is None:
|
|
||||||
raise HTTPException(status_code=404, detail="meal not found")
|
|
||||||
meal.name = payload.name
|
|
||||||
await session.flush()
|
|
||||||
return Meal.from_orm(meal)
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteMeal(AuthorizedController):
|
|
||||||
async def call(self, meal_id: int) -> None:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
meal = await DBMeal.get_by_id(session, self.user.id, meal_id)
|
|
||||||
if meal is None:
|
|
||||||
raise HTTPException(status_code=404, detail="meal not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
await meal.delete(session)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
class CreateMealFromPreset(AuthorizedController):
|
|
||||||
async def call(self, content: CreateMealFromPresetPayload) -> Meal:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
if not await DBDiary.has_permission(
|
|
||||||
session, self.user.id, content.diary_id
|
|
||||||
):
|
|
||||||
raise HTTPException(status_code=404, detail="diary not found")
|
|
||||||
|
|
||||||
preset = await DBPreset.get(session, self.user.id, content.preset_id)
|
|
||||||
|
|
||||||
if preset is None:
|
|
||||||
raise HTTPException(status_code=404, detail="preset not found")
|
|
||||||
|
|
||||||
try:
|
|
||||||
meal = await DBMeal.create_from_preset(
|
|
||||||
session, content.diary_id, content.name, preset
|
|
||||||
)
|
|
||||||
return Meal.from_orm(meal)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
from typing import AsyncIterator, Optional
|
|
||||||
|
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.preset import Preset as DBPreset
|
|
||||||
from ..model.preset import Preset, PresetDetails
|
|
||||||
from .base import AuthorizedController
|
|
||||||
|
|
||||||
|
|
||||||
class ListPresets(AuthorizedController):
|
|
||||||
async def call(
|
|
||||||
self, limit: int, offset: int, q: Optional[str]
|
|
||||||
) -> AsyncIterator[Preset]:
|
|
||||||
async with self.async_session() as session:
|
|
||||||
async for preset in DBPreset.list_all(
|
|
||||||
session, user_id=self.user.id, limit=limit, offset=offset, q=q
|
|
||||||
):
|
|
||||||
yield Preset.from_orm(preset)
|
|
||||||
|
|
||||||
|
|
||||||
class GetPreset(AuthorizedController):
|
|
||||||
async def call(self, id: int) -> PresetDetails:
|
|
||||||
async with self.async_session() as session:
|
|
||||||
preset = await DBPreset.get(session, self.user.id, id)
|
|
||||||
|
|
||||||
if preset is not None:
|
|
||||||
return PresetDetails.from_orm(preset)
|
|
||||||
|
|
||||||
raise HTTPException(status_code=404, detail="preset not found")
|
|
||||||
|
|
||||||
|
|
||||||
class DeletePreset(AuthorizedController):
|
|
||||||
async def call(
|
|
||||||
self,
|
|
||||||
id: int,
|
|
||||||
) -> None:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
preset = await DBPreset.get(session, self.user.id, id)
|
|
||||||
|
|
||||||
if preset is None:
|
|
||||||
raise HTTPException(status_code=404, detail="preset not found")
|
|
||||||
|
|
||||||
await preset.delete(session)
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
from typing import AsyncIterator, Optional
|
|
||||||
|
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.product import Product as DBProduct
|
|
||||||
from ..model.product import CreateProductPayload, Product
|
|
||||||
from ..utils import product_finder
|
|
||||||
from .base import AuthorizedController
|
|
||||||
|
|
||||||
|
|
||||||
class CreateProduct(AuthorizedController):
|
|
||||||
async def call(self, content: CreateProductPayload) -> Product:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
try:
|
|
||||||
product = await DBProduct.create(
|
|
||||||
session,
|
|
||||||
content.name,
|
|
||||||
content.carb,
|
|
||||||
content.protein,
|
|
||||||
content.fat,
|
|
||||||
content.fiber,
|
|
||||||
)
|
|
||||||
return Product.from_orm(product)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
class ListProduct(AuthorizedController):
|
|
||||||
async def call(
|
|
||||||
self, limit: int, offset: int, q: Optional[str]
|
|
||||||
) -> AsyncIterator[Product]:
|
|
||||||
async with self.async_session() as session:
|
|
||||||
async for product in DBProduct.list_all(
|
|
||||||
session, limit=limit, offset=offset, q=q
|
|
||||||
):
|
|
||||||
yield Product.from_orm(product)
|
|
||||||
|
|
||||||
|
|
||||||
class GetProductByBarCode(AuthorizedController):
|
|
||||||
async def call(self, barcode: str) -> Product:
|
|
||||||
async with self.async_session() as session:
|
|
||||||
product = await DBProduct.get_by_barcode(session, barcode)
|
|
||||||
|
|
||||||
if product:
|
|
||||||
return Product.from_orm(product)
|
|
||||||
|
|
||||||
try:
|
|
||||||
product_data = product_finder.find(barcode)
|
|
||||||
except product_finder.NotFound:
|
|
||||||
raise HTTPException(status_code=404, detail="Product not found")
|
|
||||||
except product_finder.ParseError:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="Product was found, but unable to import"
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
product = await DBProduct.create(
|
|
||||||
session,
|
|
||||||
product_data.name,
|
|
||||||
product_data.carb,
|
|
||||||
product_data.protein,
|
|
||||||
product_data.fat,
|
|
||||||
product_data.fiber,
|
|
||||||
product_data.kcal,
|
|
||||||
barcode,
|
|
||||||
)
|
|
||||||
await session.commit()
|
|
||||||
|
|
||||||
return Product.from_orm(
|
|
||||||
await DBProduct.get_by_barcode(session, barcode)
|
|
||||||
)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.product import Product as DBProduct
|
|
||||||
from .base import TasksSessionController
|
|
||||||
|
|
||||||
|
|
||||||
class CacheProductUsageData(TasksSessionController):
|
|
||||||
async def call(self) -> None:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
try:
|
|
||||||
await DBProduct.cache_usage_data(session)
|
|
||||||
await session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
from fastapi import HTTPException
|
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
|
||||||
|
|
||||||
from ..auth import (
|
|
||||||
authenticate_user,
|
|
||||||
create_access_token,
|
|
||||||
create_refresh_token,
|
|
||||||
verify_refresh_token,
|
|
||||||
)
|
|
||||||
from ..domain.user import User as DBUser
|
|
||||||
from ..model.token import RefreshTokenPayload, Token
|
|
||||||
from .base import BaseController
|
|
||||||
|
|
||||||
|
|
||||||
class CreateToken(BaseController):
|
|
||||||
async def call(self, content: OAuth2PasswordRequestForm) -> Token:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
user = await authenticate_user(session, content.username, content.password)
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=401, detail="Invalid username or password"
|
|
||||||
)
|
|
||||||
|
|
||||||
refresh_token = await create_refresh_token(session, user)
|
|
||||||
access_token = create_access_token(user)
|
|
||||||
|
|
||||||
return Token(
|
|
||||||
access_token=access_token,
|
|
||||||
refresh_token=refresh_token.token,
|
|
||||||
token_type="bearer",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RefreshToken(BaseController):
|
|
||||||
async def call(self, content: RefreshTokenPayload) -> Token:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
current_token = await verify_refresh_token(session, content.refresh_token)
|
|
||||||
|
|
||||||
if current_token is None:
|
|
||||||
raise HTTPException(status_code=401, detail="Invalid token")
|
|
||||||
|
|
||||||
user = await DBUser.get(session, current_token.user_id)
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
raise HTTPException(status_code=401, detail="Invalid token")
|
|
||||||
|
|
||||||
assert user is not None
|
|
||||||
await current_token.delete(session)
|
|
||||||
|
|
||||||
refresh_token = await create_refresh_token(session, user)
|
|
||||||
access_token = create_access_token(user)
|
|
||||||
|
|
||||||
return Token(
|
|
||||||
access_token=access_token,
|
|
||||||
refresh_token=refresh_token.token,
|
|
||||||
token_type="bearer",
|
|
||||||
)
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
from fastapi import HTTPException
|
|
||||||
|
|
||||||
from ..domain.user import User as DBUser
|
|
||||||
from ..model.user import CreateUserPayload, User
|
|
||||||
from .base import BaseController
|
|
||||||
|
|
||||||
|
|
||||||
class CreateUser(BaseController):
|
|
||||||
async def call(self, content: CreateUserPayload) -> User:
|
|
||||||
async with self.async_session.begin() as session:
|
|
||||||
try:
|
|
||||||
user = await DBUser.create(
|
|
||||||
session,
|
|
||||||
content.username,
|
|
||||||
content.password,
|
|
||||||
)
|
|
||||||
return User.from_orm(user)
|
|
||||||
except AssertionError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=e.args[0])
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
from datetime import date
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .meal import Meal
|
|
||||||
|
|
||||||
|
|
||||||
class Diary(BaseModel):
|
|
||||||
"""Diary represents user diary for given day"""
|
|
||||||
|
|
||||||
id: int
|
|
||||||
date: date
|
|
||||||
meals: List[Meal]
|
|
||||||
calories: float
|
|
||||||
protein: float
|
|
||||||
carb: float
|
|
||||||
fat: float
|
|
||||||
fiber: float
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .product import Product
|
|
||||||
|
|
||||||
|
|
||||||
class Entry(BaseModel):
|
|
||||||
"""Entry."""
|
|
||||||
|
|
||||||
id: int
|
|
||||||
grams: float
|
|
||||||
product: Product
|
|
||||||
meal_id: int
|
|
||||||
calories: float
|
|
||||||
protein: float
|
|
||||||
carb: float
|
|
||||||
fat: float
|
|
||||||
fiber: float
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
|
|
||||||
class CreateEntryPayload(BaseModel):
|
|
||||||
"""CreateEntryPayload."""
|
|
||||||
|
|
||||||
grams: float
|
|
||||||
product_id: int
|
|
||||||
meal_id: int
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateEntryPayload(BaseModel):
|
|
||||||
"""CreateEntryPayload."""
|
|
||||||
|
|
||||||
grams: Optional[float] = None
|
|
||||||
product_id: Optional[int] = None
|
|
||||||
meal_id: Optional[int] = None
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .entry import Entry
|
|
||||||
|
|
||||||
|
|
||||||
class Meal(BaseModel):
|
|
||||||
"""Meal."""
|
|
||||||
|
|
||||||
id: int
|
|
||||||
name: str
|
|
||||||
order: int
|
|
||||||
calories: float
|
|
||||||
protein: float
|
|
||||||
carb: float
|
|
||||||
fat: float
|
|
||||||
fiber: float
|
|
||||||
entries: List[Entry]
|
|
||||||
diary_id: int
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
|
|
||||||
class CreateMealPayload(BaseModel):
|
|
||||||
"""CreateMealPayload."""
|
|
||||||
|
|
||||||
name: Optional[str]
|
|
||||||
diary_id: int
|
|
||||||
|
|
||||||
|
|
||||||
class RenameMealPayload(BaseModel):
|
|
||||||
"""RenameMealPayload."""
|
|
||||||
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
class SaveMealPayload(BaseModel):
|
|
||||||
"""SaveMealPayload."""
|
|
||||||
|
|
||||||
name: Optional[str]
|
|
||||||
|
|
||||||
|
|
||||||
class CreateMealFromPresetPayload(BaseModel):
|
|
||||||
"""CreateMealPayload."""
|
|
||||||
|
|
||||||
name: Optional[str]
|
|
||||||
diary_id: int
|
|
||||||
preset_id: int
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
from typing import List
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .preset_entry import PresetEntry
|
|
||||||
|
|
||||||
|
|
||||||
class Preset(BaseModel):
|
|
||||||
"""Preset."""
|
|
||||||
|
|
||||||
id: int
|
|
||||||
name: str
|
|
||||||
calories: float
|
|
||||||
protein: float
|
|
||||||
carb: float
|
|
||||||
fat: float
|
|
||||||
fiber: float
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
||||||
|
|
||||||
class PresetDetails(Preset):
|
|
||||||
"""PresetDetails."""
|
|
||||||
|
|
||||||
entries: List[PresetEntry]
|
|
||||||
|
|
||||||
|
|
||||||
class ListPresetsPayload(BaseModel):
|
|
||||||
"""ListPresetsPayload."""
|
|
||||||
|
|
||||||
presets: List[Preset]
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .product import Product
|
|
||||||
|
|
||||||
|
|
||||||
class PresetEntry(BaseModel):
|
|
||||||
"""PresetEntry."""
|
|
||||||
|
|
||||||
id: int
|
|
||||||
grams: float
|
|
||||||
product: Product
|
|
||||||
preset_id: int
|
|
||||||
calories: float
|
|
||||||
protein: float
|
|
||||||
carb: float
|
|
||||||
fat: float
|
|
||||||
fiber: float
|
|
||||||
|
|
||||||
class Config:
|
|
||||||
from_attributes = True
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
from pydantic import BaseModel, ConfigDict
|
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel):
|
|
||||||
model_config = ConfigDict(from_attributes=True)
|
|
||||||
|
|
||||||
username: str
|
|
||||||
|
|
||||||
|
|
||||||
class CreateUserPayload(BaseModel):
|
|
||||||
username: str
|
|
||||||
password: str
|
|
||||||
|
|
@ -15,8 +15,6 @@ class Settings(BaseSettings):
|
||||||
|
|
||||||
ALLOWED_ORIGINS: list[str] = ["*"]
|
ALLOWED_ORIGINS: list[str] = ["*"]
|
||||||
|
|
||||||
API_KEY: str
|
|
||||||
|
|
||||||
PASSWORD_SCHEMES: list[str] = ["bcrypt"]
|
PASSWORD_SCHEMES: list[str] = ["bcrypt"]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
from datetime import date
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Request
|
|
||||||
|
|
||||||
from ..controller.diary import GetDiary
|
|
||||||
from ..model.diary import Diary
|
|
||||||
|
|
||||||
router = APIRouter(tags=["diary"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=Diary)
|
|
||||||
async def get_diary(
|
|
||||||
request: Request,
|
|
||||||
date: date,
|
|
||||||
controller: GetDiary = Depends(GetDiary),
|
|
||||||
):
|
|
||||||
return await controller.call(date)
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
from fastapi import APIRouter, Depends, Request
|
|
||||||
|
|
||||||
from ..controller.entry import CreateEntry, DeleteEntry, UpdateEntry
|
|
||||||
from ..model.entry import CreateEntryPayload, Entry, UpdateEntryPayload
|
|
||||||
|
|
||||||
router = APIRouter(tags=["entry"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=Entry)
|
|
||||||
async def create_entry(
|
|
||||||
request: Request,
|
|
||||||
data: CreateEntryPayload,
|
|
||||||
contoller: CreateEntry = Depends(CreateEntry),
|
|
||||||
):
|
|
||||||
return await contoller.call(data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/{entry_id}", response_model=Entry)
|
|
||||||
async def update_entry(
|
|
||||||
request: Request,
|
|
||||||
entry_id: int,
|
|
||||||
data: UpdateEntryPayload,
|
|
||||||
contoller: UpdateEntry = Depends(UpdateEntry),
|
|
||||||
):
|
|
||||||
return await contoller.call(entry_id, data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{entry_id}")
|
|
||||||
async def delete_entry(
|
|
||||||
request: Request,
|
|
||||||
entry_id: int,
|
|
||||||
contoller: DeleteEntry = Depends(DeleteEntry),
|
|
||||||
):
|
|
||||||
return await contoller.call(entry_id)
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
from fastapi import APIRouter, Depends, Request
|
|
||||||
|
|
||||||
from ..controller.meal import CreateMeal, CreateMealFromPreset, DeleteMeal, RenameMeal, SaveMeal
|
|
||||||
from ..model.meal import (
|
|
||||||
CreateMealFromPresetPayload,
|
|
||||||
CreateMealPayload,
|
|
||||||
Meal,
|
|
||||||
RenameMealPayload,
|
|
||||||
SaveMealPayload,
|
|
||||||
)
|
|
||||||
from ..model.preset import Preset
|
|
||||||
|
|
||||||
router = APIRouter(tags=["meal"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=Meal)
|
|
||||||
async def create_meal(
|
|
||||||
request: Request,
|
|
||||||
data: CreateMealPayload,
|
|
||||||
contoller: CreateMeal = Depends(CreateMeal),
|
|
||||||
):
|
|
||||||
return await contoller.call(data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{meal_id}/save", response_model=Preset)
|
|
||||||
async def save_meal(
|
|
||||||
request: Request,
|
|
||||||
meal_id: int,
|
|
||||||
data: SaveMealPayload,
|
|
||||||
contoller: SaveMeal = Depends(SaveMeal),
|
|
||||||
):
|
|
||||||
return await contoller.call(meal_id, data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/{meal_id}", response_model=Meal)
|
|
||||||
async def rename_meal(
|
|
||||||
request: Request,
|
|
||||||
meal_id: int,
|
|
||||||
data: RenameMealPayload,
|
|
||||||
contoller: RenameMeal = Depends(RenameMeal),
|
|
||||||
):
|
|
||||||
return await contoller.call(meal_id, data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{meal_id}")
|
|
||||||
async def delete_meal(
|
|
||||||
request: Request,
|
|
||||||
meal_id: int,
|
|
||||||
contoller: DeleteMeal = Depends(DeleteMeal),
|
|
||||||
):
|
|
||||||
return await contoller.call(meal_id)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/from_preset", response_model=Meal)
|
|
||||||
async def create_meal_from_preset(
|
|
||||||
request: Request,
|
|
||||||
data: CreateMealFromPresetPayload,
|
|
||||||
contoller: CreateMealFromPreset = Depends(CreateMealFromPreset),
|
|
||||||
):
|
|
||||||
return await contoller.call(data)
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
from fastapi import APIRouter, Depends, Request
|
|
||||||
|
|
||||||
from ..controller.preset import DeletePreset, GetPreset, ListPresets
|
|
||||||
from ..model.preset import ListPresetsPayload, PresetDetails
|
|
||||||
|
|
||||||
router = APIRouter(tags=["preset"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=ListPresetsPayload)
|
|
||||||
async def list_presets(
|
|
||||||
request: Request,
|
|
||||||
limit: int = 10,
|
|
||||||
offset: int = 0,
|
|
||||||
q: str | None = None,
|
|
||||||
controller: ListPresets = Depends(ListPresets),
|
|
||||||
):
|
|
||||||
return ListPresetsPayload(
|
|
||||||
presets=[p async for p in controller.call(limit=limit, offset=offset, q=q)]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{preset_id}", response_model=PresetDetails)
|
|
||||||
async def get_preset(
|
|
||||||
request: Request,
|
|
||||||
preset_id: int,
|
|
||||||
controller: GetPreset = Depends(GetPreset),
|
|
||||||
):
|
|
||||||
return await controller.call(preset_id)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{preset_id}")
|
|
||||||
async def delete_preset(
|
|
||||||
request: Request,
|
|
||||||
preset_id: int,
|
|
||||||
controller: DeletePreset = Depends(DeletePreset),
|
|
||||||
):
|
|
||||||
await controller.call(preset_id)
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
from fastapi import APIRouter, Depends, Request
|
|
||||||
|
|
||||||
from ..controller.tasks import CacheProductUsageData
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api", tags=["tasks"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/cache_product_usage_data")
|
|
||||||
async def cache_product_usage_data(
|
|
||||||
request: Request,
|
|
||||||
contoller: CacheProductUsageData = Depends(CacheProductUsageData),
|
|
||||||
):
|
|
||||||
return await contoller.call()
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
from fastapi import APIRouter, Depends, Request
|
|
||||||
|
|
||||||
from fooder.controller.user import CreateUser
|
|
||||||
from fooder.model.user import CreateUserPayload, User
|
|
||||||
|
|
||||||
router = APIRouter(tags=["user"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", response_model=User)
|
|
||||||
async def create_user(
|
|
||||||
request: Request,
|
|
||||||
data: CreateUserPayload,
|
|
||||||
contoller: CreateUser = Depends(CreateUser),
|
|
||||||
):
|
|
||||||
return await contoller.call(data)
|
|
||||||
3
mypy.ini
3
mypy.ini
|
|
@ -10,6 +10,3 @@ platform = linux
|
||||||
|
|
||||||
warn_unused_configs = True
|
warn_unused_configs = True
|
||||||
warn_unused_ignores = True
|
warn_unused_ignores = True
|
||||||
|
|
||||||
[mypy-fooder.controller.*]
|
|
||||||
disable_error_code=override
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue