mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-03-04 04:33:12 -05:00
refactor(backend): ♻️ cleanup HTTP service classes and remove database singleton (#687)
* refactor(backend): ♻️ cleanup duplicate code in http services * refactor(backend): ♻️ refactor database away from singleton design removed the database single and instead injected the session into a new Database class that is created during each request life-cycle. Now sessions no longer need to be passed into each method on the database All tests pass, but there are likely some hidden breaking changes that were not discovered. * fix venv * disable venv cache * fix install script * bump poetry version * postgres fixes * revert install * fix db initialization for postgres * add postgres to docker * refactor(backend): ♻️ cleanup unused and duplicate code in http services * refactor(backend): remove sessions from arguments * refactor(backend): ♻️ convert units and ingredients to use http service class * test(backend): ✅ add unit and food tests * lint * update tags * re-enable cache * fix missing fraction in db * fix lint Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
@@ -6,7 +6,6 @@ from mealie.core.config import APP_VERSION, settings
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.routes import backup_routes, migration_routes, router, utility_routes
|
||||
from mealie.routes.about import about_router
|
||||
from mealie.routes.mealplans import meal_plan_router
|
||||
from mealie.routes.media import media_router
|
||||
from mealie.routes.site_settings import settings_router
|
||||
from mealie.services.events import create_general_event
|
||||
@@ -36,7 +35,6 @@ def api_routers():
|
||||
app.include_router(media_router)
|
||||
app.include_router(about_router)
|
||||
# Meal Routes
|
||||
app.include_router(meal_plan_router)
|
||||
# Settings Routes
|
||||
app.include_router(settings_router)
|
||||
# Backups/Imports Routes
|
||||
|
||||
@@ -7,7 +7,7 @@ from jose import JWTError, jwt
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import app_dirs, settings
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.schema.user import LongLiveTokenInDB, PrivateUser, TokenData
|
||||
|
||||
@@ -69,7 +69,9 @@ async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
|
||||
user = db.users.get(session, token_data.username, "email", any_case=True)
|
||||
db = get_database(session)
|
||||
|
||||
user = db.users.get(token_data.username, "email", any_case=True)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
@@ -82,8 +84,9 @@ async def get_admin_user(current_user=Depends(get_current_user)) -> PrivateUser:
|
||||
|
||||
|
||||
def validate_long_live_token(session: Session, client_token: str, id: int) -> PrivateUser:
|
||||
db = get_database(session)
|
||||
|
||||
tokens: list[LongLiveTokenInDB] = db.api_tokens.get(session, id, "parent_id", limit=9999)
|
||||
tokens: list[LongLiveTokenInDB] = db.api_tokens.get(id, "parent_id", limit=9999)
|
||||
|
||||
for token in tokens:
|
||||
token: LongLiveTokenInDB
|
||||
|
||||
@@ -5,7 +5,7 @@ from jose import jwt
|
||||
from passlib.context import CryptContext
|
||||
|
||||
from mealie.core.config import settings
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.schema.user import PrivateUser
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
@@ -28,10 +28,12 @@ def create_file_token(file_path: Path) -> bool:
|
||||
|
||||
|
||||
def authenticate_user(session, email: str, password: str) -> PrivateUser:
|
||||
user: PrivateUser = db.users.get(session, email, "email", any_case=True)
|
||||
db = get_database(session)
|
||||
|
||||
user: PrivateUser = db.users.get(email, "email", any_case=True)
|
||||
|
||||
if not user:
|
||||
user = db.users.get(session, email, "username", any_case=True)
|
||||
user = db.users.get(email, "username", any_case=True)
|
||||
if not user:
|
||||
return False
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
from .db_access import DatabaseAccessLayer
|
||||
from .access_model_factory import Database
|
||||
|
||||
@@ -14,7 +14,7 @@ T = TypeVar("T")
|
||||
D = TypeVar("D")
|
||||
|
||||
|
||||
class BaseAccessModel(Generic[T, D]):
|
||||
class AccessModel(Generic[T, D]):
|
||||
"""A Generic BaseAccess Model method to perform common operations on the database
|
||||
|
||||
Args:
|
||||
@@ -22,7 +22,8 @@ class BaseAccessModel(Generic[T, D]):
|
||||
Generic ([D]): Represents the SqlAlchemyModel Model
|
||||
"""
|
||||
|
||||
def __init__(self, primary_key: Union[str, int], sql_model: D, schema: T) -> None:
|
||||
def __init__(self, session: Session, primary_key: Union[str, int], sql_model: D, schema: T) -> None:
|
||||
self.session = session
|
||||
self.primary_key = primary_key
|
||||
self.sql_model = sql_model
|
||||
self.schema = schema
|
||||
@@ -37,9 +38,7 @@ class BaseAccessModel(Generic[T, D]):
|
||||
for observer in self.observers:
|
||||
observer()
|
||||
|
||||
def get_all(
|
||||
self, session: Session, limit: int = None, order_by: str = None, start=0, override_schema=None
|
||||
) -> list[T]:
|
||||
def get_all(self, limit: int = None, order_by: str = None, start=0, override_schema=None) -> list[T]:
|
||||
eff_schema = override_schema or self.schema
|
||||
|
||||
if order_by:
|
||||
@@ -47,27 +46,20 @@ class BaseAccessModel(Generic[T, D]):
|
||||
|
||||
return [
|
||||
eff_schema.from_orm(x)
|
||||
for x in session.query(self.sql_model).order_by(order_attr.desc()).offset(start).limit(limit).all()
|
||||
for x in self.session.query(self.sql_model).order_by(order_attr.desc()).offset(start).limit(limit).all()
|
||||
]
|
||||
|
||||
return [eff_schema.from_orm(x) for x in session.query(self.sql_model).offset(start).limit(limit).all()]
|
||||
return [eff_schema.from_orm(x) for x in self.session.query(self.sql_model).offset(start).limit(limit).all()]
|
||||
|
||||
def multi_query(
|
||||
self,
|
||||
session: Session,
|
||||
query_by: dict[str, str],
|
||||
start=0,
|
||||
limit: int = None,
|
||||
override_schema=None,
|
||||
) -> list[T]:
|
||||
def multi_query(self, query_by: dict[str, str], start=0, limit: int = None, override_schema=None) -> list[T]:
|
||||
eff_schema = override_schema or self.schema
|
||||
|
||||
return [
|
||||
eff_schema.from_orm(x)
|
||||
for x in session.query(self.sql_model).filter_by(**query_by).offset(start).limit(limit).all()
|
||||
for x in self.session.query(self.sql_model).filter_by(**query_by).offset(start).limit(limit).all()
|
||||
]
|
||||
|
||||
def get_all_limit_columns(self, session: Session, fields: list[str], limit: int = None) -> list[D]:
|
||||
def get_all_limit_columns(self, fields: list[str], limit: int = None) -> list[D]:
|
||||
"""Queries the database for the selected model. Restricts return responses to the
|
||||
keys specified under "fields"
|
||||
|
||||
@@ -79,9 +71,9 @@ class BaseAccessModel(Generic[T, D]):
|
||||
Returns:
|
||||
list[SqlAlchemyBase]: Returns a list of ORM objects
|
||||
"""
|
||||
return session.query(self.sql_model).options(load_only(*fields)).limit(limit).all()
|
||||
return self.session.query(self.sql_model).options(load_only(*fields)).limit(limit).all()
|
||||
|
||||
def get_all_primary_keys(self, session: Session) -> list[str]:
|
||||
def get_all_primary_keys(self) -> list[str]:
|
||||
"""Queries the database of the selected model and returns a list
|
||||
of all primary_key values
|
||||
|
||||
@@ -91,11 +83,11 @@ class BaseAccessModel(Generic[T, D]):
|
||||
Returns:
|
||||
list[str]:
|
||||
"""
|
||||
results = session.query(self.sql_model).options(load_only(str(self.primary_key)))
|
||||
results = self.session.query(self.sql_model).options(load_only(str(self.primary_key)))
|
||||
results_as_dict = [x.dict() for x in results]
|
||||
return [x.get(self.primary_key) for x in results_as_dict]
|
||||
|
||||
def _query_one(self, session: Session, match_value: str, match_key: str = None) -> D:
|
||||
def _query_one(self, match_value: str, match_key: str = None) -> D:
|
||||
"""
|
||||
Query the sql database for one item an return the sql alchemy model
|
||||
object. If no match key is provided the primary_key attribute will be used.
|
||||
@@ -103,16 +95,16 @@ class BaseAccessModel(Generic[T, D]):
|
||||
if match_key is None:
|
||||
match_key = self.primary_key
|
||||
|
||||
return session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
|
||||
return self.session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
|
||||
|
||||
def get_one(self, session: Session, value: str | int, key: str = None, any_case=False, override_schema=None) -> T:
|
||||
def get_one(self, value: str | int, key: str = None, any_case=False, override_schema=None) -> T:
|
||||
key = key or self.primary_key
|
||||
|
||||
if any_case:
|
||||
search_attr = getattr(self.sql_model, key)
|
||||
result = session.query(self.sql_model).filter(func.lower(search_attr) == key.lower()).one_or_none()
|
||||
result = self.session.query(self.sql_model).filter(func.lower(search_attr) == key.lower()).one_or_none()
|
||||
else:
|
||||
result = session.query(self.sql_model).filter_by(**{key: value}).one_or_none()
|
||||
result = self.session.query(self.sql_model).filter_by(**{key: value}).one_or_none()
|
||||
|
||||
if not result:
|
||||
return
|
||||
@@ -121,7 +113,7 @@ class BaseAccessModel(Generic[T, D]):
|
||||
return eff_schema.from_orm(result)
|
||||
|
||||
def get(
|
||||
self, session: Session, match_value: str, match_key: str = None, limit=1, any_case=False, override_schema=None
|
||||
self, match_value: str, match_key: str = None, limit=1, any_case=False, override_schema=None
|
||||
) -> T | list[T]:
|
||||
"""Retrieves an entry from the database by matching a key/value pair. If no
|
||||
key is provided the class objects primary key will be used to match against.
|
||||
@@ -141,10 +133,13 @@ class BaseAccessModel(Generic[T, D]):
|
||||
if any_case:
|
||||
search_attr = getattr(self.sql_model, match_key)
|
||||
result = (
|
||||
session.query(self.sql_model).filter(func.lower(search_attr) == match_value.lower()).limit(limit).all()
|
||||
self.session.query(self.sql_model)
|
||||
.filter(func.lower(search_attr) == match_value.lower())
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
result = session.query(self.sql_model).filter_by(**{match_key: match_value}).limit(limit).all()
|
||||
result = self.session.query(self.sql_model).filter_by(**{match_key: match_value}).limit(limit).all()
|
||||
|
||||
eff_schema = override_schema or self.schema
|
||||
|
||||
@@ -156,7 +151,7 @@ class BaseAccessModel(Generic[T, D]):
|
||||
|
||||
return [eff_schema.from_orm(x) for x in result]
|
||||
|
||||
def create(self, session: Session, document: T) -> T:
|
||||
def create(self, document: T) -> T:
|
||||
"""Creates a new database entry for the given SQL Alchemy Model.
|
||||
|
||||
Args:
|
||||
@@ -167,17 +162,17 @@ class BaseAccessModel(Generic[T, D]):
|
||||
dict: A dictionary representation of the database entry
|
||||
"""
|
||||
document = document if isinstance(document, dict) else document.dict()
|
||||
new_document = self.sql_model(session=session, **document)
|
||||
session.add(new_document)
|
||||
session.commit()
|
||||
session.refresh(new_document)
|
||||
new_document = self.sql_model(session=self.session, **document)
|
||||
self.session.add(new_document)
|
||||
self.session.commit()
|
||||
self.session.refresh(new_document)
|
||||
|
||||
if self.observers:
|
||||
self.update_observers()
|
||||
|
||||
return self.schema.from_orm(new_document)
|
||||
|
||||
def update(self, session: Session, match_value: str, new_data: dict) -> T:
|
||||
def update(self, match_value: str, new_data: dict) -> T:
|
||||
"""Update a database entry.
|
||||
Args:
|
||||
session (Session): Database Session
|
||||
@@ -189,19 +184,19 @@ class BaseAccessModel(Generic[T, D]):
|
||||
"""
|
||||
new_data = new_data if isinstance(new_data, dict) else new_data.dict()
|
||||
|
||||
entry = self._query_one(session=session, match_value=match_value)
|
||||
entry.update(session=session, **new_data)
|
||||
entry = self._query_one(match_value=match_value)
|
||||
entry.update(session=self.session, **new_data)
|
||||
|
||||
if self.observers:
|
||||
self.update_observers()
|
||||
|
||||
session.commit()
|
||||
self.session.commit()
|
||||
return self.schema.from_orm(entry)
|
||||
|
||||
def patch(self, session: Session, match_value: str, new_data: dict) -> T:
|
||||
def patch(self, match_value: str, new_data: dict) -> T:
|
||||
new_data = new_data if isinstance(new_data, dict) else new_data.dict()
|
||||
|
||||
entry = self._query_one(session=session, match_value=match_value)
|
||||
entry = self._query_one(match_value=match_value)
|
||||
|
||||
if not entry:
|
||||
return
|
||||
@@ -209,43 +204,43 @@ class BaseAccessModel(Generic[T, D]):
|
||||
entry_as_dict = self.schema.from_orm(entry).dict()
|
||||
entry_as_dict.update(new_data)
|
||||
|
||||
return self.update(session, match_value, entry_as_dict)
|
||||
return self.update(match_value, entry_as_dict)
|
||||
|
||||
def delete(self, session: Session, primary_key_value) -> D:
|
||||
result = session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one()
|
||||
def delete(self, primary_key_value) -> D:
|
||||
result = self.session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one()
|
||||
results_as_model = self.schema.from_orm(result)
|
||||
|
||||
session.delete(result)
|
||||
session.commit()
|
||||
self.session.delete(result)
|
||||
self.session.commit()
|
||||
|
||||
if self.observers:
|
||||
self.update_observers()
|
||||
|
||||
return results_as_model
|
||||
|
||||
def delete_all(self, session: Session) -> None:
|
||||
session.query(self.sql_model).delete()
|
||||
session.commit()
|
||||
def delete_all(self) -> None:
|
||||
self.session.query(self.sql_model).delete()
|
||||
self.session.commit()
|
||||
|
||||
if self.observers:
|
||||
self.update_observers()
|
||||
|
||||
def count_all(self, session: Session, match_key=None, match_value=None) -> int:
|
||||
def count_all(self, match_key=None, match_value=None) -> int:
|
||||
if None in [match_key, match_value]:
|
||||
return session.query(self.sql_model).count()
|
||||
return self.session.query(self.sql_model).count()
|
||||
else:
|
||||
return session.query(self.sql_model).filter_by(**{match_key: match_value}).count()
|
||||
return self.session.query(self.sql_model).filter_by(**{match_key: match_value}).count()
|
||||
|
||||
def _count_attribute(
|
||||
self, session: Session, attribute_name: str, attr_match: str = None, count=True, override_schema=None
|
||||
self, attribute_name: str, attr_match: str = None, count=True, override_schema=None
|
||||
) -> Union[int, T]:
|
||||
eff_schema = override_schema or self.schema
|
||||
# attr_filter = getattr(self.sql_model, attribute_name)
|
||||
|
||||
if count:
|
||||
return session.query(self.sql_model).filter(attribute_name == attr_match).count() # noqa: 711
|
||||
return self.session.query(self.sql_model).filter(attribute_name == attr_match).count() # noqa: 711
|
||||
else:
|
||||
return [
|
||||
eff_schema.from_orm(x)
|
||||
for x in session.query(self.sql_model).filter(attribute_name == attr_match).all() # noqa: 711
|
||||
for x in self.session.query(self.sql_model).filter(attribute_name == attr_match).all() # noqa: 711
|
||||
]
|
||||
145
mealie/db/data_access_layer/access_model_factory.py
Normal file
145
mealie/db/data_access_layer/access_model_factory.py
Normal file
@@ -0,0 +1,145 @@
|
||||
from functools import cached_property
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.db.models.event import Event, EventNotification
|
||||
from mealie.db.models.group import Group, GroupMealPlan
|
||||
from mealie.db.models.group.cookbook import CookBook
|
||||
from mealie.db.models.group.invite_tokens import GroupInviteToken
|
||||
from mealie.db.models.group.preferences import GroupPreferencesModel
|
||||
from mealie.db.models.group.webhooks import GroupWebhooksModel
|
||||
from mealie.db.models.recipe.category import Category
|
||||
from mealie.db.models.recipe.comment import RecipeComment
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
from mealie.db.models.recipe.tag import Tag
|
||||
from mealie.db.models.settings import SiteSettings
|
||||
from mealie.db.models.sign_up import SignUp
|
||||
from mealie.db.models.users import LongLiveToken, User
|
||||
from mealie.schema.admin import SiteSettings as SiteSettingsSchema
|
||||
from mealie.schema.cookbook.cookbook import ReadCookBook
|
||||
from mealie.schema.events import Event as EventSchema
|
||||
from mealie.schema.events import EventNotificationIn
|
||||
from mealie.schema.group.group_preferences import ReadGroupPreferences
|
||||
from mealie.schema.group.invite_token import ReadInviteToken
|
||||
from mealie.schema.group.webhook import ReadWebhook
|
||||
from mealie.schema.meal_plan.new_meal import ReadPlanEntry
|
||||
from mealie.schema.recipe import CommentOut, Recipe, RecipeCategoryResponse, RecipeTagResponse
|
||||
from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit
|
||||
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser, SignUpOut
|
||||
|
||||
from ._access_model import AccessModel
|
||||
from .group_access_model import GroupDataAccessModel
|
||||
from .meal_access_model import MealDataAccessModel
|
||||
from .recipe_access_model import RecipeDataAccessModel
|
||||
from .user_access_model import UserDataAccessModel
|
||||
|
||||
pk_id = "id"
|
||||
pk_slug = "slug"
|
||||
pk_token = "token"
|
||||
|
||||
|
||||
class CategoryDataAccessModel(AccessModel):
|
||||
def get_empty(self):
|
||||
return self.session.query(Category).filter(~Category.recipes.any()).all()
|
||||
|
||||
|
||||
class TagsDataAccessModel(AccessModel):
|
||||
def get_empty(self):
|
||||
return self.session.query(Tag).filter(~Tag.recipes.any()).all()
|
||||
|
||||
|
||||
class Database:
|
||||
def __init__(self, session: Session) -> None:
|
||||
"""
|
||||
`DatabaseAccessLayer` class is the data access layer for all database actions within
|
||||
Mealie. Database uses composition from classes derived from AccessModel. These
|
||||
can be substantiated from the AccessModel class or through inheritance when
|
||||
additional methods are required.
|
||||
"""
|
||||
|
||||
self.session = session
|
||||
|
||||
# ================================================================
|
||||
# Recipe Items
|
||||
|
||||
@cached_property
|
||||
def recipes(self) -> RecipeDataAccessModel:
|
||||
return RecipeDataAccessModel(self.session, pk_slug, RecipeModel, Recipe)
|
||||
|
||||
@cached_property
|
||||
def ingredient_foods(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, IngredientFoodModel, IngredientFood)
|
||||
|
||||
@cached_property
|
||||
def ingredient_units(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, IngredientUnitModel, IngredientUnit)
|
||||
|
||||
@cached_property
|
||||
def comments(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, RecipeComment, CommentOut)
|
||||
|
||||
@cached_property
|
||||
def categories(self) -> CategoryDataAccessModel:
|
||||
return CategoryDataAccessModel(self.session, pk_id, Category, RecipeCategoryResponse)
|
||||
|
||||
@cached_property
|
||||
def tags(self) -> TagsDataAccessModel:
|
||||
return TagsDataAccessModel(self.session, pk_id, Tag, RecipeTagResponse)
|
||||
|
||||
# ================================================================
|
||||
# Site Items
|
||||
|
||||
@cached_property
|
||||
def settings(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, SiteSettings, SiteSettingsSchema)
|
||||
|
||||
@cached_property
|
||||
def sign_up(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, SignUp, SignUpOut)
|
||||
|
||||
@cached_property
|
||||
def event_notifications(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, EventNotification, EventNotificationIn)
|
||||
|
||||
@cached_property
|
||||
def events(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, Event, EventSchema)
|
||||
|
||||
# ================================================================
|
||||
# User Items
|
||||
|
||||
@cached_property
|
||||
def users(self) -> UserDataAccessModel:
|
||||
return UserDataAccessModel(self.session, pk_id, User, PrivateUser)
|
||||
|
||||
@cached_property
|
||||
def api_tokens(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, LongLiveToken, LongLiveTokenInDB)
|
||||
|
||||
# ================================================================
|
||||
# Group Items
|
||||
|
||||
@cached_property
|
||||
def groups(self) -> GroupDataAccessModel:
|
||||
return GroupDataAccessModel(self.session, pk_id, Group, GroupInDB)
|
||||
|
||||
@cached_property
|
||||
def group_invite_tokens(self) -> AccessModel:
|
||||
return AccessModel(self.session, "token", GroupInviteToken, ReadInviteToken)
|
||||
|
||||
@cached_property
|
||||
def group_preferences(self) -> AccessModel:
|
||||
return AccessModel(self.session, "group_id", GroupPreferencesModel, ReadGroupPreferences)
|
||||
|
||||
@cached_property
|
||||
def meals(self) -> MealDataAccessModel:
|
||||
return MealDataAccessModel(self.session, pk_id, GroupMealPlan, ReadPlanEntry)
|
||||
|
||||
@cached_property
|
||||
def cookbooks(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, CookBook, ReadCookBook)
|
||||
|
||||
@cached_property
|
||||
def webhooks(self) -> AccessModel:
|
||||
return AccessModel(self.session, pk_id, GroupWebhooksModel, ReadWebhook)
|
||||
@@ -1,98 +0,0 @@
|
||||
from logging import getLogger
|
||||
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.data_access_layer.group_access_model import GroupDataAccessModel
|
||||
from mealie.db.data_access_layer.meal_access_model import MealDataAccessModel
|
||||
from mealie.db.models.event import Event, EventNotification
|
||||
from mealie.db.models.group import Group, GroupMealPlan
|
||||
from mealie.db.models.group.cookbook import CookBook
|
||||
from mealie.db.models.group.invite_tokens import GroupInviteToken
|
||||
from mealie.db.models.group.preferences import GroupPreferencesModel
|
||||
from mealie.db.models.group.shopping_list import ShoppingList
|
||||
from mealie.db.models.group.webhooks import GroupWebhooksModel
|
||||
from mealie.db.models.recipe.category import Category
|
||||
from mealie.db.models.recipe.comment import RecipeComment
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
from mealie.db.models.recipe.recipe import RecipeModel, Tag
|
||||
from mealie.db.models.settings import SiteSettings
|
||||
from mealie.db.models.sign_up import SignUp
|
||||
from mealie.db.models.users import LongLiveToken, User
|
||||
from mealie.schema.admin import SiteSettings as SiteSettingsSchema
|
||||
from mealie.schema.cookbook import ReadCookBook
|
||||
from mealie.schema.events import Event as EventSchema
|
||||
from mealie.schema.events import EventNotificationIn
|
||||
from mealie.schema.group.group_preferences import ReadGroupPreferences
|
||||
from mealie.schema.group.invite_token import ReadInviteToken
|
||||
from mealie.schema.group.webhook import ReadWebhook
|
||||
from mealie.schema.meal_plan import ShoppingListOut
|
||||
from mealie.schema.meal_plan.new_meal import ReadPlanEntry
|
||||
from mealie.schema.recipe import (
|
||||
CommentOut,
|
||||
IngredientFood,
|
||||
IngredientUnit,
|
||||
Recipe,
|
||||
RecipeCategoryResponse,
|
||||
RecipeTagResponse,
|
||||
)
|
||||
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser, SignUpOut
|
||||
|
||||
from ._base_access_model import BaseAccessModel
|
||||
from .recipe_access_model import RecipeDataAccessModel
|
||||
from .user_access_model import UserDataAccessModel
|
||||
|
||||
logger = getLogger()
|
||||
|
||||
|
||||
pk_id = "id"
|
||||
pk_slug = "slug"
|
||||
pk_token = "token"
|
||||
|
||||
|
||||
class CategoryDataAccessModel(BaseAccessModel):
|
||||
def get_empty(self, session: Session):
|
||||
return session.query(Category).filter(~Category.recipes.any()).all()
|
||||
|
||||
|
||||
class TagsDataAccessModel(BaseAccessModel):
|
||||
def get_empty(self, session: Session):
|
||||
return session.query(Tag).filter(~Tag.recipes.any()).all()
|
||||
|
||||
|
||||
class DatabaseAccessLayer:
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
`DatabaseAccessLayer` class is the data access layer for all database actions within
|
||||
Mealie. Database uses composition from classes derived from BaseAccessModel. These
|
||||
can be substantiated from the BaseAccessModel class or through inheritance when
|
||||
additional methods are required.
|
||||
"""
|
||||
|
||||
# Recipes
|
||||
self.recipes = RecipeDataAccessModel(pk_slug, RecipeModel, Recipe)
|
||||
self.ingredient_foods = BaseAccessModel(pk_id, IngredientFoodModel, IngredientFood)
|
||||
self.ingredient_units = BaseAccessModel(pk_id, IngredientUnitModel, IngredientUnit)
|
||||
self.comments = BaseAccessModel(pk_id, RecipeComment, CommentOut)
|
||||
|
||||
# Tags and Categories
|
||||
self.categories = CategoryDataAccessModel(pk_slug, Category, RecipeCategoryResponse)
|
||||
self.tags = TagsDataAccessModel(pk_slug, Tag, RecipeTagResponse)
|
||||
|
||||
# Site
|
||||
self.settings = BaseAccessModel(pk_id, SiteSettings, SiteSettingsSchema)
|
||||
self.sign_ups = BaseAccessModel(pk_token, SignUp, SignUpOut)
|
||||
self.event_notifications = BaseAccessModel(pk_id, EventNotification, EventNotificationIn)
|
||||
self.events = BaseAccessModel(pk_id, Event, EventSchema)
|
||||
|
||||
# Users
|
||||
self.users = UserDataAccessModel(pk_id, User, PrivateUser)
|
||||
self.api_tokens = BaseAccessModel(pk_id, LongLiveToken, LongLiveTokenInDB)
|
||||
|
||||
# Group Data
|
||||
self.groups = GroupDataAccessModel(pk_id, Group, GroupInDB)
|
||||
self.group_tokens = BaseAccessModel("token", GroupInviteToken, ReadInviteToken)
|
||||
self.meals = MealDataAccessModel(pk_id, GroupMealPlan, ReadPlanEntry)
|
||||
self.webhooks = BaseAccessModel(pk_id, GroupWebhooksModel, ReadWebhook)
|
||||
self.shopping_lists = BaseAccessModel(pk_id, ShoppingList, ShoppingListOut)
|
||||
self.cookbooks = BaseAccessModel(pk_id, CookBook, ReadCookBook)
|
||||
self.group_preferences = BaseAccessModel("group_id", GroupPreferencesModel, ReadGroupPreferences)
|
||||
@@ -4,10 +4,10 @@ from mealie.db.models.group import Group
|
||||
from mealie.schema.meal_plan.meal import MealPlanOut
|
||||
from mealie.schema.user.user import GroupInDB
|
||||
|
||||
from ._base_access_model import BaseAccessModel
|
||||
from ._access_model import AccessModel
|
||||
|
||||
|
||||
class GroupDataAccessModel(BaseAccessModel[GroupInDB, Group]):
|
||||
class GroupDataAccessModel(AccessModel[GroupInDB, Group]):
|
||||
def get_meals(self, session: Session, match_value: str, match_key: str = "name") -> list[MealPlanOut]:
|
||||
"""A Helper function to get the group from the database and return a sorted list of
|
||||
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
from datetime import date
|
||||
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.models.group import GroupMealPlan
|
||||
from mealie.schema.meal_plan.new_meal import ReadPlanEntry
|
||||
|
||||
from ._base_access_model import BaseAccessModel
|
||||
from ._access_model import AccessModel
|
||||
|
||||
|
||||
class MealDataAccessModel(BaseAccessModel[ReadPlanEntry, GroupMealPlan]):
|
||||
def get_slice(self, session: Session, start: date, end: date, group_id: int) -> list[ReadPlanEntry]:
|
||||
class MealDataAccessModel(AccessModel[ReadPlanEntry, GroupMealPlan]):
|
||||
def get_slice(self, start: date, end: date, group_id: int) -> list[ReadPlanEntry]:
|
||||
start = start.strftime("%Y-%m-%d")
|
||||
end = end.strftime("%Y-%m-%d")
|
||||
qry = session.query(GroupMealPlan).filter(
|
||||
qry = self.session.query(GroupMealPlan).filter(
|
||||
GroupMealPlan.date.between(start, end),
|
||||
GroupMealPlan.group_id == group_id,
|
||||
)
|
||||
|
||||
return [self.schema.from_orm(x) for x in qry.all()]
|
||||
|
||||
def get_today(self, session: Session, group_id: int) -> list[ReadPlanEntry]:
|
||||
def get_today(self, group_id: int) -> list[ReadPlanEntry]:
|
||||
today = date.today()
|
||||
qry = session.query(GroupMealPlan).filter(GroupMealPlan.date == today, GroupMealPlan.group_id == group_id)
|
||||
qry = self.session.query(GroupMealPlan).filter(GroupMealPlan.date == today, GroupMealPlan.group_id == group_id)
|
||||
|
||||
return [self.schema.from_orm(x) for x in qry.all()]
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
from random import randint
|
||||
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
from mealie.db.models.recipe.settings import RecipeSettings
|
||||
from mealie.schema.recipe import Recipe
|
||||
|
||||
from ._base_access_model import BaseAccessModel
|
||||
from ._access_model import AccessModel
|
||||
|
||||
|
||||
class RecipeDataAccessModel(BaseAccessModel[Recipe, RecipeModel]):
|
||||
def get_all_public(self, session: Session, limit: int = None, order_by: str = None, start=0, override_schema=None):
|
||||
class RecipeDataAccessModel(AccessModel[Recipe, RecipeModel]):
|
||||
def get_all_public(self, limit: int = None, order_by: str = None, start=0, override_schema=None):
|
||||
eff_schema = override_schema or self.schema
|
||||
|
||||
if order_by:
|
||||
@@ -18,7 +16,7 @@ class RecipeDataAccessModel(BaseAccessModel[Recipe, RecipeModel]):
|
||||
|
||||
return [
|
||||
eff_schema.from_orm(x)
|
||||
for x in session.query(self.sql_model)
|
||||
for x in self.session.query(self.sql_model)
|
||||
.join(RecipeSettings)
|
||||
.filter(RecipeSettings.public == True) # noqa: 711
|
||||
.order_by(order_attr.desc())
|
||||
@@ -29,7 +27,7 @@ class RecipeDataAccessModel(BaseAccessModel[Recipe, RecipeModel]):
|
||||
|
||||
return [
|
||||
eff_schema.from_orm(x)
|
||||
for x in session.query(self.sql_model)
|
||||
for x in self.session.query(self.sql_model)
|
||||
.join(RecipeSettings)
|
||||
.filter(RecipeSettings.public == True) # noqa: 711
|
||||
.offset(start)
|
||||
@@ -37,23 +35,25 @@ class RecipeDataAccessModel(BaseAccessModel[Recipe, RecipeModel]):
|
||||
.all()
|
||||
]
|
||||
|
||||
def update_image(self, session: Session, slug: str, _: str = None) -> str:
|
||||
entry: RecipeModel = self._query_one(session, match_value=slug)
|
||||
def update_image(self, slug: str, _: str = None) -> str:
|
||||
entry: RecipeModel = self._query_one(match_value=slug)
|
||||
entry.image = randint(0, 255)
|
||||
session.commit()
|
||||
self.session.commit()
|
||||
|
||||
return entry.image
|
||||
|
||||
def count_uncategorized(self, session: Session, count=True, override_schema=None) -> int:
|
||||
def count_uncategorized(self, count=True, override_schema=None) -> int:
|
||||
return self._count_attribute(
|
||||
session,
|
||||
attribute_name=RecipeModel.recipe_category,
|
||||
attr_match=None,
|
||||
count=count,
|
||||
override_schema=override_schema,
|
||||
)
|
||||
|
||||
def count_untagged(self, session: Session, count=True, override_schema=None) -> int:
|
||||
def count_untagged(self, count=True, override_schema=None) -> int:
|
||||
return self._count_attribute(
|
||||
session, attribute_name=RecipeModel.tags, attr_match=None, count=count, override_schema=override_schema
|
||||
attribute_name=RecipeModel.tags,
|
||||
attr_match=None,
|
||||
count=count,
|
||||
override_schema=override_schema,
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from mealie.db.models.users import User
|
||||
from mealie.schema.user.user import PrivateUser
|
||||
|
||||
from ._base_access_model import BaseAccessModel
|
||||
from ._access_model import AccessModel
|
||||
|
||||
|
||||
class UserDataAccessModel(BaseAccessModel[PrivateUser, User]):
|
||||
class UserDataAccessModel(AccessModel[PrivateUser, User]):
|
||||
def update_password(self, session, id, password: str):
|
||||
entry = self._query_one(session=session, match_value=id)
|
||||
entry = self._query_one(match_value=id)
|
||||
entry.update_password(password)
|
||||
session.commit()
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from ..data_access_layer import DatabaseAccessLayer
|
||||
from mealie.db.data_access_layer.access_model_factory import Database
|
||||
|
||||
CWD = Path(__file__).parent
|
||||
|
||||
@@ -20,15 +18,15 @@ def get_default_units():
|
||||
return units
|
||||
|
||||
|
||||
def default_recipe_unit_init(db: DatabaseAccessLayer, session: Session) -> None:
|
||||
def default_recipe_unit_init(db: Database) -> None:
|
||||
for unit in get_default_units():
|
||||
try:
|
||||
db.ingredient_units.create(session, unit)
|
||||
db.ingredient_units.create(unit)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
for food in get_default_foods():
|
||||
try:
|
||||
db.ingredient_foods.create(session, food)
|
||||
db.ingredient_foods.create(food)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
from functools import lru_cache
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .data_access_layer import DatabaseAccessLayer
|
||||
|
||||
db = DatabaseAccessLayer()
|
||||
from .data_access_layer.access_model_factory import Database
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_database():
|
||||
return db
|
||||
def get_database(session: Session):
|
||||
return Database(session)
|
||||
|
||||
@@ -10,7 +10,7 @@ def sql_global_init(db_url: str):
|
||||
if "sqlite" in db_url:
|
||||
connect_args["check_same_thread"] = False
|
||||
|
||||
engine = sa.create_engine(db_url, echo=False, connect_args=connect_args)
|
||||
engine = sa.create_engine(db_url, echo=False, connect_args=connect_args, pool_pre_ping=True)
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.core import root_logger
|
||||
from mealie.core.config import settings
|
||||
from mealie.core.security import hash_password
|
||||
from mealie.db.data_access_layer.access_model_factory import Database
|
||||
from mealie.db.data_initialization.init_units_foods import default_recipe_unit_init
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import create_session, engine
|
||||
from mealie.db.models._model_base import SqlAlchemyBase
|
||||
from mealie.schema.admin import SiteSettings
|
||||
@@ -21,30 +20,24 @@ def create_all_models():
|
||||
SqlAlchemyBase.metadata.create_all(engine)
|
||||
|
||||
|
||||
def init_db(session: Session = None) -> None:
|
||||
create_all_models()
|
||||
|
||||
if not session:
|
||||
session = create_session()
|
||||
|
||||
with session:
|
||||
default_group_init(session)
|
||||
default_settings_init(session)
|
||||
default_user_init(session)
|
||||
default_recipe_unit_init(db, session)
|
||||
def init_db(db: Database) -> None:
|
||||
default_group_init(db)
|
||||
default_settings_init(db)
|
||||
default_user_init(db)
|
||||
default_recipe_unit_init(db)
|
||||
|
||||
|
||||
def default_settings_init(session: Session):
|
||||
document = db.settings.create(session, SiteSettings().dict())
|
||||
def default_settings_init(db: Database):
|
||||
document = db.settings.create(SiteSettings().dict())
|
||||
logger.info(f"Created Site Settings: \n {document}")
|
||||
|
||||
|
||||
def default_group_init(session: Session):
|
||||
def default_group_init(db: Database):
|
||||
logger.info("Generating Default Group")
|
||||
create_new_group(session, GroupBase(name=settings.DEFAULT_GROUP))
|
||||
create_new_group(db, GroupBase(name=settings.DEFAULT_GROUP))
|
||||
|
||||
|
||||
def default_user_init(session: Session):
|
||||
def default_user_init(db: Database):
|
||||
default_user = {
|
||||
"full_name": "Change Me",
|
||||
"username": "admin",
|
||||
@@ -55,21 +48,26 @@ def default_user_init(session: Session):
|
||||
}
|
||||
|
||||
logger.info("Generating Default User")
|
||||
db.users.create(session, default_user)
|
||||
db.users.create(default_user)
|
||||
|
||||
|
||||
def main():
|
||||
create_all_models()
|
||||
|
||||
session = create_session()
|
||||
db = get_database(session)
|
||||
|
||||
try:
|
||||
session = create_session()
|
||||
init_user = db.users.get(session, "1", "id")
|
||||
init_user = db.users.get("1", "id")
|
||||
except Exception:
|
||||
init_db()
|
||||
init_db(db)
|
||||
return
|
||||
|
||||
if init_user:
|
||||
logger.info("Database Exists")
|
||||
else:
|
||||
logger.info("Database Doesn't Exists, Initializing...")
|
||||
init_db()
|
||||
init_db(db)
|
||||
create_general_event("Initialize Database", "Initialize database with default values", session)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String, orm
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, orm
|
||||
|
||||
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
|
||||
|
||||
@@ -11,6 +11,7 @@ class IngredientUnitModel(SqlAlchemyBase, BaseMixins):
|
||||
name = Column(String)
|
||||
description = Column(String)
|
||||
abbreviation = Column(String)
|
||||
fraction = Column(Boolean)
|
||||
ingredients = orm.relationship("RecipeIngredient", back_populates="unit")
|
||||
|
||||
@auto_init()
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.schema.events import EventsOut
|
||||
@@ -15,18 +15,20 @@ logger = get_logger()
|
||||
@router.get("", response_model=EventsOut)
|
||||
async def get_events(session: Session = Depends(generate_session)):
|
||||
""" Get event from the Database """
|
||||
# Get Item
|
||||
return EventsOut(total=db.events.count_all(session), events=db.events.get_all(session, order_by="time_stamp"))
|
||||
db = get_database(session)
|
||||
|
||||
return EventsOut(total=db.events.count_all(), events=db.events.get_all(order_by="time_stamp"))
|
||||
|
||||
|
||||
@router.delete("")
|
||||
async def delete_events(session: Session = Depends(generate_session)):
|
||||
""" Get event from the Database """
|
||||
# Get Item
|
||||
db = get_database(session)
|
||||
return db.events.delete_all(session)
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_event(id: int, session: Session = Depends(generate_session)):
|
||||
""" Delete event from the Database """
|
||||
db = get_database(session)
|
||||
return db.events.delete(session, id)
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi import Depends, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.schema.events import EventNotificationIn, EventNotificationOut, TestEvent
|
||||
@@ -21,8 +21,9 @@ async def create_event_notification(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Create event_notification in the Database """
|
||||
db = get_database(session)
|
||||
|
||||
return db.event_notifications.create(session, event_data)
|
||||
return db.event_notifications.create(event_data)
|
||||
|
||||
|
||||
@router.post("/notifications/test")
|
||||
@@ -31,9 +32,10 @@ async def test_notification_route(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Create event_notification in the Database """
|
||||
db = get_database(session)
|
||||
|
||||
if test_data.id:
|
||||
event_obj: EventNotificationIn = db.event_notifications.get(session, test_data.id)
|
||||
event_obj: EventNotificationIn = db.event_notifications.get(test_data.id)
|
||||
test_data.test_url = event_obj.notification_url
|
||||
|
||||
try:
|
||||
@@ -46,8 +48,8 @@ async def test_notification_route(
|
||||
@router.get("/notifications", response_model=list[EventNotificationOut])
|
||||
async def get_all_event_notification(session: Session = Depends(generate_session)):
|
||||
""" Get all event_notification from the Database """
|
||||
# Get Item
|
||||
return db.event_notifications.get_all(session, override_schema=EventNotificationOut)
|
||||
db = get_database(session)
|
||||
return db.event_notifications.get_all(override_schema=EventNotificationOut)
|
||||
|
||||
|
||||
@router.put("/notifications/{id}")
|
||||
@@ -61,4 +63,5 @@ async def update_event_notification(id: int, session: Session = Depends(generate
|
||||
async def delete_event_notification(id: int, session: Session = Depends(generate_session)):
|
||||
""" Delete event_notification from the Database """
|
||||
# Delete Item
|
||||
return db.event_notifications.delete(session, id)
|
||||
db = get_database(session)
|
||||
return db.event_notifications.delete(id)
|
||||
|
||||
@@ -28,11 +28,11 @@ async def get_app_info():
|
||||
|
||||
@router.get("/statistics", response_model=AppStatistics)
|
||||
async def get_app_statistics(session: Session = Depends(generate_session)):
|
||||
db = get_database()
|
||||
db = get_database(session)
|
||||
return AppStatistics(
|
||||
total_recipes=db.recipes.count_all(session),
|
||||
uncategorized_recipes=db.recipes.count_uncategorized(session),
|
||||
untagged_recipes=db.recipes.count_untagged(session),
|
||||
total_users=db.users.count_all(session),
|
||||
total_groups=db.groups.count_all(session),
|
||||
total_recipes=db.recipes.count_all(),
|
||||
uncategorized_recipes=db.recipes.count_uncategorized(),
|
||||
untagged_recipes=db.recipes.count_untagged(),
|
||||
total_users=db.users.count_all(),
|
||||
total_groups=db.groups.count_all(),
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import BackgroundTasks, Depends, HTTPException, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.schema.user import GroupBase, GroupInDB, PrivateUser, UpdateGroup
|
||||
@@ -14,8 +14,9 @@ router = AdminAPIRouter(prefix="/groups")
|
||||
@router.get("", response_model=list[GroupInDB])
|
||||
async def get_all_groups(session: Session = Depends(generate_session)):
|
||||
""" Returns a list of all groups in the database """
|
||||
db = get_database(session)
|
||||
|
||||
return db.groups.get_all(session)
|
||||
return db.groups.get_all()
|
||||
|
||||
|
||||
@router.post("", status_code=status.HTTP_201_CREATED, response_model=GroupInDB)
|
||||
@@ -25,9 +26,10 @@ async def create_group(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Creates a Group in the Database """
|
||||
db = get_database(session)
|
||||
|
||||
try:
|
||||
new_group = db.groups.create(session, group_data.dict())
|
||||
new_group = db.groups.create(group_data.dict())
|
||||
background_tasks.add_task(create_group_event, "Group Created", f"'{group_data.name}' created", session)
|
||||
return new_group
|
||||
except Exception:
|
||||
@@ -37,7 +39,8 @@ async def create_group(
|
||||
@router.put("/{id}")
|
||||
async def update_group_data(id: int, group_data: UpdateGroup, session: Session = Depends(generate_session)):
|
||||
""" Updates a User Group """
|
||||
db.groups.update(session, id, group_data.dict())
|
||||
db = get_database(session)
|
||||
db.groups.update(id, group_data.dict())
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
@@ -48,11 +51,12 @@ async def delete_user_group(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Removes a user group from the database """
|
||||
db = get_database(session)
|
||||
|
||||
if id == 1:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="DEFAULT_GROUP")
|
||||
|
||||
group: GroupInDB = db.groups.get(session, id)
|
||||
group: GroupInDB = db.groups.get(id)
|
||||
|
||||
if not group:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="GROUP_NOT_FOUND")
|
||||
@@ -64,4 +68,4 @@ async def delete_user_group(
|
||||
create_group_event, "Group Deleted", f"'{group.name}' deleted by {current_user.full_name}", session
|
||||
)
|
||||
|
||||
db.groups.delete(session, id)
|
||||
db.groups.delete(id)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import is_logged_in
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter, UserAPIRouter
|
||||
from mealie.schema.recipe import CategoryIn, RecipeCategoryResponse
|
||||
@@ -15,13 +15,15 @@ admin_router = AdminAPIRouter()
|
||||
@public_router.get("")
|
||||
async def get_all_recipe_categories(session: Session = Depends(generate_session)):
|
||||
""" Returns a list of available categories in the database """
|
||||
return db.categories.get_all_limit_columns(session, ["slug", "name"])
|
||||
db = get_database(session)
|
||||
return db.categories.get_all_limit_columns(fields=["slug", "name"])
|
||||
|
||||
|
||||
@public_router.get("/empty")
|
||||
def get_empty_categories(session: Session = Depends(generate_session)):
|
||||
""" Returns a list of categories that do not contain any recipes"""
|
||||
return db.categories.get_empty(session)
|
||||
db = get_database(session)
|
||||
return db.categories.get_empty()
|
||||
|
||||
|
||||
@public_router.get("/{category}", response_model=RecipeCategoryResponse)
|
||||
@@ -29,8 +31,9 @@ def get_all_recipes_by_category(
|
||||
category: str, session: Session = Depends(generate_session), is_user: bool = Depends(is_logged_in)
|
||||
):
|
||||
""" Returns a list of recipes associated with the provided category. """
|
||||
db = get_database(session)
|
||||
|
||||
category_obj = db.categories.get(session, category)
|
||||
category_obj = db.categories.get(category)
|
||||
category_obj = RecipeCategoryResponse.from_orm(category_obj)
|
||||
|
||||
if not is_user:
|
||||
@@ -42,9 +45,10 @@ def get_all_recipes_by_category(
|
||||
@user_router.post("")
|
||||
async def create_recipe_category(category: CategoryIn, session: Session = Depends(generate_session)):
|
||||
""" Creates a Category in the database """
|
||||
db = get_database(session)
|
||||
|
||||
try:
|
||||
return db.categories.create(session, category.dict())
|
||||
return db.categories.create(category.dict())
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -52,9 +56,10 @@ async def create_recipe_category(category: CategoryIn, session: Session = Depend
|
||||
@admin_router.put("/{category}", response_model=RecipeCategoryResponse)
|
||||
async def update_recipe_category(category: str, new_category: CategoryIn, session: Session = Depends(generate_session)):
|
||||
""" Updates an existing Tag in the database """
|
||||
db = get_database(session)
|
||||
|
||||
try:
|
||||
return db.categories.update(session, category, new_category.dict())
|
||||
return db.categories.update(category, new_category.dict())
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -66,8 +71,9 @@ async def delete_recipe_category(category: str, session: Session = Depends(gener
|
||||
category does not impact a recipe. The category will be removed
|
||||
from any recipes that contain it
|
||||
"""
|
||||
db = get_database(session)
|
||||
|
||||
try:
|
||||
db.categories.delete(session, category)
|
||||
db.categories.delete(category)
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -7,12 +7,10 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=list[ReadInviteToken])
|
||||
def get_invite_tokens(g_service: GroupSelfService = Depends(GroupSelfService.write_existing)):
|
||||
def get_invite_tokens(g_service: GroupSelfService = Depends(GroupSelfService.private)):
|
||||
return g_service.get_invite_tokens()
|
||||
|
||||
|
||||
@router.post("", response_model=ReadInviteToken, status_code=status.HTTP_201_CREATED)
|
||||
def create_invite_token(
|
||||
uses: CreateInviteToken, g_service: GroupSelfService = Depends(GroupSelfService.write_existing)
|
||||
):
|
||||
def create_invite_token(uses: CreateInviteToken, g_service: GroupSelfService = Depends(GroupSelfService.private)):
|
||||
return g_service.create_invite_token(uses.uses)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import crud, helpers, mealplans
|
||||
|
||||
meal_plan_router = APIRouter()
|
||||
|
||||
meal_plan_router.include_router(crud.router)
|
||||
meal_plan_router.include_router(crud.public_router)
|
||||
meal_plan_router.include_router(helpers.router)
|
||||
meal_plan_router.include_router(mealplans.router)
|
||||
@@ -1,126 +0,0 @@
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.meal_plan import MealPlanIn, MealPlanOut
|
||||
from mealie.schema.user import GroupInDB, PrivateUser
|
||||
from mealie.services.events import create_group_event
|
||||
from mealie.services.image import image
|
||||
from mealie.services.meal_services import get_todays_meal, set_mealplan_dates
|
||||
|
||||
router = UserAPIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||
public_router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||
|
||||
|
||||
@router.get("/all", response_model=list[MealPlanOut])
|
||||
def get_all_meals(
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Returns a list of all available Meal Plan """
|
||||
|
||||
return db.groups.get_meals(session, current_user.group)
|
||||
|
||||
|
||||
@router.get("/this-week", response_model=MealPlanOut)
|
||||
def get_this_week(session: Session = Depends(generate_session), current_user: PrivateUser = Depends(get_current_user)):
|
||||
""" Returns the meal plan data for this week """
|
||||
plans = db.groups.get_meals(session, current_user.group)
|
||||
if plans:
|
||||
return plans[0]
|
||||
|
||||
|
||||
@router.get("/today", tags=["Meal Plan"])
|
||||
def get_today(session: Session = Depends(generate_session), current_user: PrivateUser = Depends(get_current_user)):
|
||||
"""
|
||||
Returns the recipe slug for the meal scheduled for today.
|
||||
If no meal is scheduled nothing is returned
|
||||
"""
|
||||
|
||||
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||
recipe = get_todays_meal(session, group_in_db)
|
||||
if recipe:
|
||||
return recipe
|
||||
|
||||
|
||||
@public_router.get("/today/image", tags=["Meal Plan"])
|
||||
def get_todays_image(session: Session = Depends(generate_session), group_name: str = "Home"):
|
||||
"""
|
||||
Returns the image for todays meal-plan.
|
||||
"""
|
||||
|
||||
group_in_db: GroupInDB = db.groups.get(session, group_name, "name")
|
||||
recipe = get_todays_meal(session, group_in_db)
|
||||
recipe_image = recipe.image_dir.joinpath(image.ImageOptions.ORIGINAL_IMAGE)
|
||||
|
||||
if not recipe and not recipe_image.exists():
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
return FileResponse(recipe_image)
|
||||
|
||||
|
||||
@router.get("/{id}", response_model=MealPlanOut)
|
||||
def get_meal_plan(
|
||||
id,
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Returns a single Meal Plan from the Database """
|
||||
|
||||
return db.meals.get(session, id)
|
||||
|
||||
|
||||
@router.post("/create", status_code=status.HTTP_201_CREATED)
|
||||
def create_meal_plan(
|
||||
background_tasks: BackgroundTasks,
|
||||
data: MealPlanIn,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
""" Creates a meal plan database entry """
|
||||
set_mealplan_dates(data)
|
||||
background_tasks.add_task(
|
||||
create_group_event, "Meal Plan Created", f"Mealplan Created for '{current_user.group}'", session=session
|
||||
)
|
||||
return db.meals.create(session, data.dict())
|
||||
|
||||
|
||||
@router.put("/{plan_id}")
|
||||
def update_meal_plan(
|
||||
background_tasks: BackgroundTasks,
|
||||
plan_id: str,
|
||||
meal_plan: MealPlanIn,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
""" Updates a meal plan based off ID """
|
||||
set_mealplan_dates(meal_plan)
|
||||
processed_plan = MealPlanOut(id=plan_id, **meal_plan.dict())
|
||||
try:
|
||||
db.meals.update(session, plan_id, processed_plan.dict())
|
||||
background_tasks.add_task(
|
||||
create_group_event, "Meal Plan Updated", f"Mealplan Updated for '{current_user.group}'", session=session
|
||||
)
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.delete("/{plan_id}")
|
||||
def delete_meal_plan(
|
||||
background_tasks: BackgroundTasks,
|
||||
plan_id,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
""" Removes a meal plan from the database """
|
||||
|
||||
try:
|
||||
db.meals.delete(session, plan_id)
|
||||
background_tasks.add_task(
|
||||
create_group_event, "Meal Plan Deleted", f"Mealplan Deleted for '{current_user.group}'", session=session
|
||||
)
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
@@ -1,50 +0,0 @@
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.meal_plan import ListItem, MealPlanOut, ShoppingListIn, ShoppingListOut
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.user import PrivateUser
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
router = UserAPIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||
|
||||
|
||||
@router.get("/{id}/shopping-list")
|
||||
def get_shopping_list(
|
||||
id: str,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
|
||||
mealplan: MealPlanOut = db.meals.get(session, id)
|
||||
|
||||
all_ingredients = []
|
||||
|
||||
for plan_day in mealplan.plan_days:
|
||||
for meal in plan_day.meals:
|
||||
if not meal.slug:
|
||||
continue
|
||||
|
||||
try:
|
||||
recipe: Recipe = db.recipes.get(session, meal.slug)
|
||||
all_ingredients += recipe.recipe_ingredient
|
||||
except Exception:
|
||||
logger.error("Recipe Not Found")
|
||||
|
||||
new_list = ShoppingListIn(
|
||||
name="MealPlan Shopping List", group=current_user.group, items=[ListItem(text=t.note) for t in all_ingredients]
|
||||
)
|
||||
|
||||
created_list: ShoppingListOut = db.shopping_lists.create(session, new_list)
|
||||
|
||||
mealplan.shopping_list = created_list.id
|
||||
|
||||
db.meals.update(session, mealplan.id, mealplan)
|
||||
|
||||
return created_list
|
||||
@@ -1,8 +0,0 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from mealie.routes.mealplans import crud, helpers
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(crud.router)
|
||||
router.include_router(helpers.router)
|
||||
@@ -1,7 +1,7 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.schema.recipe import RecipeSummary
|
||||
|
||||
@@ -10,9 +10,11 @@ router = APIRouter()
|
||||
|
||||
@router.get("/summary/untagged", response_model=list[RecipeSummary])
|
||||
async def get_untagged_recipes(count: bool = False, session: Session = Depends(generate_session)):
|
||||
return db.recipes.count_untagged(session, count=count, override_schema=RecipeSummary)
|
||||
db = get_database(session)
|
||||
return db.recipes.count_untagged(count=count, override_schema=RecipeSummary)
|
||||
|
||||
|
||||
@router.get("/summary/uncategorized", response_model=list[RecipeSummary])
|
||||
async def get_uncategorized_recipes(count: bool = False, session: Session = Depends(generate_session)):
|
||||
return db.recipes.count_uncategorized(session, count=count, override_schema=RecipeSummary)
|
||||
db = get_database(session)
|
||||
return db.recipes.count_uncategorized(count=count, override_schema=RecipeSummary)
|
||||
|
||||
@@ -4,7 +4,7 @@ from fastapi import Depends, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.recipe import CommentOut, CreateComment, SaveComment
|
||||
@@ -21,9 +21,10 @@ async def create_comment(
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
""" Create comment in the Database """
|
||||
db = get_database(session)
|
||||
|
||||
new_comment = SaveComment(user=current_user.id, text=new_comment.text, recipe_slug=slug)
|
||||
return db.comments.create(session, new_comment)
|
||||
return db.comments.create(new_comment)
|
||||
|
||||
|
||||
@router.put("/{slug}/comments/{id}")
|
||||
@@ -34,12 +35,13 @@ async def update_comment(
|
||||
current_user: PrivateUser = Depends(get_current_user),
|
||||
):
|
||||
""" Update comment in the Database """
|
||||
old_comment: CommentOut = db.comments.get(session, id)
|
||||
db = get_database(session)
|
||||
old_comment: CommentOut = db.comments.get(id)
|
||||
|
||||
if current_user.id != old_comment.user.id:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
return db.comments.update(session, id, new_comment)
|
||||
return db.comments.update(id, new_comment)
|
||||
|
||||
|
||||
@router.delete("/{slug}/comments/{id}")
|
||||
@@ -47,9 +49,10 @@ async def delete_comment(
|
||||
id: int, session: Session = Depends(generate_session), current_user: PrivateUser = Depends(get_current_user)
|
||||
):
|
||||
""" Delete comment from the Database """
|
||||
comment: CommentOut = db.comments.get(session, id)
|
||||
db = get_database(session)
|
||||
comment: CommentOut = db.comments.get(id)
|
||||
if current_user.id == comment.user.id or current_user.admin:
|
||||
db.comments.delete(session, id)
|
||||
db.comments.delete(id)
|
||||
return
|
||||
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi.datastructures import UploadFile
|
||||
from slugify import slugify
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.recipe import CreateRecipeByURL, Recipe, RecipeAsset
|
||||
@@ -32,8 +32,9 @@ def update_recipe_image(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Removes an existing image and replaces it with the incoming file. """
|
||||
db = get_database(session)
|
||||
write_image(slug, image, extension)
|
||||
new_version = db.recipes.update_image(session, slug, extension)
|
||||
new_version = db.recipes.update_image(slug, extension)
|
||||
|
||||
return {"image": new_version}
|
||||
|
||||
@@ -58,7 +59,9 @@ def upload_recipe_asset(
|
||||
if not dest.is_file():
|
||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
recipe: Recipe = db.recipes.get(session, slug)
|
||||
db = get_database(session)
|
||||
|
||||
recipe: Recipe = db.recipes.get(slug)
|
||||
recipe.assets.append(asset_in)
|
||||
db.recipes.update(session, slug, recipe.dict())
|
||||
db.recipes.update(slug, recipe.dict())
|
||||
return asset_in
|
||||
|
||||
@@ -10,7 +10,7 @@ from starlette.responses import FileResponse
|
||||
|
||||
from mealie.core.dependencies import temporary_zip_path
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.recipe import CreateRecipeByURL, Recipe, RecipeImageTypes
|
||||
@@ -71,7 +71,9 @@ async def create_recipe_from_zip(
|
||||
with myzip.open(file) as myfile:
|
||||
recipe_image = myfile.read()
|
||||
|
||||
recipe: Recipe = db.recipes.create(session, Recipe(**recipe_dict))
|
||||
db = get_database(session)
|
||||
|
||||
recipe: Recipe = db.recipes.create(Recipe(**recipe_dict))
|
||||
|
||||
write_image(recipe.slug, recipe_image, "webp")
|
||||
|
||||
@@ -89,7 +91,9 @@ async def get_recipe_as_zip(
|
||||
slug: str, session: Session = Depends(generate_session), temp_path=Depends(temporary_zip_path)
|
||||
):
|
||||
""" Get a Recipe and It's Original Image as a Zip File """
|
||||
recipe: Recipe = db.recipes.get(session, slug)
|
||||
db = get_database(session)
|
||||
|
||||
recipe: Recipe = db.recipes.get(slug)
|
||||
|
||||
image_asset = recipe.image_dir.joinpath(RecipeImageTypes.original.value)
|
||||
|
||||
@@ -105,14 +109,12 @@ async def get_recipe_as_zip(
|
||||
@user_router.put("/{slug}")
|
||||
def update_recipe(data: Recipe, recipe_service: RecipeService = Depends(RecipeService.write_existing)):
|
||||
""" Updates a recipe by existing slug and data. """
|
||||
|
||||
return recipe_service.update_one(data)
|
||||
|
||||
|
||||
@user_router.patch("/{slug}")
|
||||
def patch_recipe(data: Recipe, recipe_service: RecipeService = Depends(RecipeService.write_existing)):
|
||||
""" Updates a recipe by existing slug and data. """
|
||||
|
||||
return recipe_service.patch_one(data)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.meal_plan import ShoppingListIn, ShoppingListOut
|
||||
@@ -18,25 +18,28 @@ async def create_shopping_list(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Create Shopping List in the Database """
|
||||
|
||||
db = get_database(session)
|
||||
list_in.group = current_user.group
|
||||
|
||||
return db.shopping_lists.create(session, list_in)
|
||||
return db.shopping_lists.create(list_in)
|
||||
|
||||
|
||||
@router.get("/{id}", response_model=ShoppingListOut)
|
||||
async def get_shopping_list(id: int, session: Session = Depends(generate_session)):
|
||||
""" Get Shopping List from the Database """
|
||||
return db.shopping_lists.get(session, id)
|
||||
db = get_database(session)
|
||||
return db.shopping_lists.get(id)
|
||||
|
||||
|
||||
@router.put("/{id}", response_model=ShoppingListOut)
|
||||
async def update_shopping_list(id: int, new_data: ShoppingListIn, session: Session = Depends(generate_session)):
|
||||
""" Update Shopping List in the Database """
|
||||
return db.shopping_lists.update(session, id, new_data)
|
||||
db = get_database(session)
|
||||
return db.shopping_lists.update(id, new_data)
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_shopping_list(id: int, session: Session = Depends(generate_session)):
|
||||
""" Delete Shopping List from the Database """
|
||||
return db.shopping_lists.delete(session, id)
|
||||
db = get_database(session)
|
||||
return db.shopping_lists.delete(id)
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.schema.admin import SiteSettings
|
||||
@@ -16,8 +16,9 @@ admin_router = AdminAPIRouter(prefix="/api/site-settings", tags=["Settings"])
|
||||
@public_router.get("")
|
||||
def get_main_settings(session: Session = Depends(generate_session)):
|
||||
""" Returns basic site settings """
|
||||
db = get_database(session)
|
||||
|
||||
return db.settings.get(session, 1)
|
||||
return db.settings.get(1)
|
||||
|
||||
|
||||
@admin_router.put("")
|
||||
@@ -26,7 +27,8 @@ def update_settings(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Returns Site Settings """
|
||||
db.settings.update(session, 1, data.dict())
|
||||
db = get_database(session)
|
||||
db.settings.update(1, data.dict())
|
||||
|
||||
|
||||
@admin_router.post("/webhooks/test")
|
||||
@@ -35,7 +37,8 @@ def test_webhooks(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Run the function to test your webhooks """
|
||||
group_entry: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||
db = get_database(session)
|
||||
group_entry: GroupInDB = db.groups.get(current_user.group, "name")
|
||||
|
||||
try:
|
||||
post_webhooks(group_entry.id, session)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm.session import Session
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.core.dependencies import is_logged_in
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter, UserAPIRouter
|
||||
from mealie.schema.recipe import RecipeTagResponse, TagIn
|
||||
@@ -15,13 +15,15 @@ admin_router = AdminAPIRouter()
|
||||
@public_router.get("")
|
||||
async def get_all_recipe_tags(session: Session = Depends(generate_session)):
|
||||
""" Returns a list of available tags in the database """
|
||||
return db.tags.get_all_limit_columns(session, ["slug", "name"])
|
||||
db = get_database(session)
|
||||
return db.tags.get_all_limit_columns(["slug", "name"])
|
||||
|
||||
|
||||
@public_router.get("/empty")
|
||||
def get_empty_tags(session: Session = Depends(generate_session)):
|
||||
""" Returns a list of tags that do not contain any recipes"""
|
||||
return db.tags.get_empty(session)
|
||||
db = get_database(session)
|
||||
return db.tags.get_empty()
|
||||
|
||||
|
||||
@public_router.get("/{tag}", response_model=RecipeTagResponse)
|
||||
@@ -29,7 +31,8 @@ def get_all_recipes_by_tag(
|
||||
tag: str, session: Session = Depends(generate_session), is_user: bool = Depends(is_logged_in)
|
||||
):
|
||||
""" Returns a list of recipes associated with the provided tag. """
|
||||
tag_obj = db.tags.get(session, tag)
|
||||
db = get_database(session)
|
||||
tag_obj = db.tags.get(tag)
|
||||
tag_obj = RecipeTagResponse.from_orm(tag_obj)
|
||||
|
||||
if not is_user:
|
||||
@@ -41,15 +44,15 @@ def get_all_recipes_by_tag(
|
||||
@user_router.post("")
|
||||
async def create_recipe_tag(tag: TagIn, session: Session = Depends(generate_session)):
|
||||
""" Creates a Tag in the database """
|
||||
|
||||
return db.tags.create(session, tag.dict())
|
||||
db = get_database(session)
|
||||
return db.tags.create(tag.dict())
|
||||
|
||||
|
||||
@admin_router.put("/{tag}", response_model=RecipeTagResponse)
|
||||
async def update_recipe_tag(tag: str, new_tag: TagIn, session: Session = Depends(generate_session)):
|
||||
""" Updates an existing Tag in the database """
|
||||
|
||||
return db.tags.update(session, tag, new_tag.dict())
|
||||
db = get_database(session)
|
||||
return db.tags.update(tag, new_tag.dict())
|
||||
|
||||
|
||||
@admin_router.delete("/{tag}")
|
||||
@@ -59,6 +62,7 @@ async def delete_recipe_tag(tag: str, session: Session = Depends(generate_sessio
|
||||
from any recipes that contain it"""
|
||||
|
||||
try:
|
||||
db.tags.delete(session, tag)
|
||||
db = get_database(session)
|
||||
db.tags.delete(tag)
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import food_routes, unit_routes
|
||||
from mealie.services._base_http_service.router_factory import RouterFactory
|
||||
from mealie.services.recipe.recipe_food_service import RecipeFoodService
|
||||
from mealie.services.recipe.recipe_unit_service import RecipeUnitService
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(food_routes.router, prefix="/foods", tags=["Recipes: Foods"])
|
||||
router.include_router(unit_routes.router, prefix="/units", tags=["Recipes: Units"])
|
||||
router.include_router(RouterFactory(RecipeFoodService, prefix="/foods", tags=["Recipes: Foods"]))
|
||||
router.include_router(RouterFactory(RecipeUnitService, prefix="/units", tags=["Recipes: Units"]))
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
from fastapi import Depends, status
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import Session, generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.recipe import CreateIngredientFood, IngredientFood
|
||||
|
||||
router = UserAPIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=list[IngredientFood])
|
||||
async def get_all(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Get unit from the Database """
|
||||
# Get unit
|
||||
return db.ingredient_foods.get_all(session)
|
||||
|
||||
|
||||
@router.post("", response_model=IngredientFood, status_code=status.HTTP_201_CREATED)
|
||||
async def create_food(unit: CreateIngredientFood, session: Session = Depends(generate_session)):
|
||||
""" Create unit in the Database """
|
||||
|
||||
return db.ingredient_foods.create(session, unit)
|
||||
|
||||
|
||||
@router.get("/{id}")
|
||||
async def get_food(id: str, session: Session = Depends(generate_session)):
|
||||
""" Get unit from the Database """
|
||||
|
||||
return db.ingredient_foods.get(session, id)
|
||||
|
||||
|
||||
@router.put("/{id}")
|
||||
async def update_food(id: str, unit: CreateIngredientFood, session: Session = Depends(generate_session)):
|
||||
""" Update unit in the Database """
|
||||
|
||||
return db.ingredient_foods.update(session, id, unit)
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_food(id: str, session: Session = Depends(generate_session)):
|
||||
""" Delete unit from the Database """
|
||||
return db.ingredient_foods.delete(session, id)
|
||||
@@ -1,44 +0,0 @@
|
||||
from fastapi import Depends, status
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import Session, generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.recipe import CreateIngredientUnit, IngredientUnit
|
||||
|
||||
router = UserAPIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=list[IngredientUnit])
|
||||
async def get_all(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Get unit from the Database """
|
||||
# Get unit
|
||||
return db.ingredient_units.get_all(session)
|
||||
|
||||
|
||||
@router.post("", response_model=IngredientUnit, status_code=status.HTTP_201_CREATED)
|
||||
async def create_unit(unit: CreateIngredientUnit, session: Session = Depends(generate_session)):
|
||||
""" Create unit in the Database """
|
||||
|
||||
return db.ingredient_units.create(session, unit)
|
||||
|
||||
|
||||
@router.get("/{id}")
|
||||
async def get_unit(id: str, session: Session = Depends(generate_session)):
|
||||
""" Get unit from the Database """
|
||||
|
||||
return db.ingredient_units.get(session, id)
|
||||
|
||||
|
||||
@router.put("/{id}")
|
||||
async def update_unit(id: str, unit: CreateIngredientUnit, session: Session = Depends(generate_session)):
|
||||
""" Update unit in the Database """
|
||||
|
||||
return db.ingredient_units.update(session, id, unit)
|
||||
|
||||
|
||||
@router.delete("/{id}")
|
||||
async def delete_unit(id: str, session: Session = Depends(generate_session)):
|
||||
""" Delete unit from the Database """
|
||||
return db.ingredient_units.delete(session, id)
|
||||
@@ -6,7 +6,7 @@ from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.core.security import create_access_token
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.user import CreateToken, LoingLiveTokenIn, LongLiveTokenInDB, PrivateUser
|
||||
@@ -33,7 +33,9 @@ async def create_api_token(
|
||||
parent_id=current_user.id,
|
||||
)
|
||||
|
||||
new_token_in_db = db.api_tokens.create(session, token_model)
|
||||
db = get_database(session)
|
||||
|
||||
new_token_in_db = db.api_tokens.create(token_model)
|
||||
|
||||
if new_token_in_db:
|
||||
return {"token": token}
|
||||
@@ -46,13 +48,14 @@ async def delete_api_token(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Delete api_token from the Database """
|
||||
token: LongLiveTokenInDB = db.api_tokens.get(session, token_id)
|
||||
db = get_database(session)
|
||||
token: LongLiveTokenInDB = db.api_tokens.get(token_id)
|
||||
|
||||
if not token:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, f"Could not locate token with id '{token_id}' in database")
|
||||
|
||||
if token.user.email == current_user.email:
|
||||
deleted_token = db.api_tokens.delete(session, token_id)
|
||||
deleted_token = db.api_tokens.delete(token_id)
|
||||
return {"token_delete": deleted_token.name}
|
||||
else:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy.orm.session import Session
|
||||
from mealie.core import security
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.core.security import hash_password
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import AdminAPIRouter, UserAPIRouter
|
||||
from mealie.routes.users._helpers import assert_user_change_allowed
|
||||
@@ -17,7 +17,8 @@ admin_router = AdminAPIRouter(prefix="")
|
||||
|
||||
@admin_router.get("", response_model=list[UserOut])
|
||||
async def get_all_users(session: Session = Depends(generate_session)):
|
||||
return db.users.get_all(session)
|
||||
db = get_database(session)
|
||||
return db.users.get_all()
|
||||
|
||||
|
||||
@admin_router.post("", response_model=UserOut, status_code=201)
|
||||
@@ -33,12 +34,14 @@ async def create_user(
|
||||
create_user_event, "User Created", f"Created by {current_user.full_name}", session=session
|
||||
)
|
||||
|
||||
return db.users.create(session, new_user.dict())
|
||||
db = get_database(session)
|
||||
return db.users.create(new_user.dict())
|
||||
|
||||
|
||||
@admin_router.get("/{id}", response_model=UserOut)
|
||||
async def get_user(id: int, session: Session = Depends(generate_session)):
|
||||
return db.users.get(session, id)
|
||||
db = get_database(session)
|
||||
return db.users.get(id)
|
||||
|
||||
|
||||
@admin_router.delete("/{id}")
|
||||
@@ -56,7 +59,8 @@ def delete_user(
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="SUPER_USER")
|
||||
|
||||
try:
|
||||
db.users.delete(session, id)
|
||||
db = get_database(session)
|
||||
db.users.delete(id)
|
||||
background_tasks.add_task(create_user_event, "User Deleted", f"User ID: {id}", session=session)
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
@@ -87,7 +91,9 @@ async def update_user(
|
||||
# prevent an admin from demoting themself
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
db.users.update(session, id, new_data.dict())
|
||||
db = get_database(session)
|
||||
db.users.update(id, new_data.dict())
|
||||
|
||||
if current_user.id == id:
|
||||
access_token = security.create_access_token(data=dict(sub=new_data.email))
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
@@ -2,7 +2,7 @@ from fastapi import Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.dependencies import get_current_user
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.routes.users._helpers import assert_user_change_allowed
|
||||
@@ -14,8 +14,8 @@ user_router = UserAPIRouter()
|
||||
@user_router.get("/{id}/favorites", response_model=UserFavorites)
|
||||
async def get_favorites(id: str, session: Session = Depends(generate_session)):
|
||||
""" Get user's favorite recipes """
|
||||
|
||||
return db.users.get(session, id, override_schema=UserFavorites)
|
||||
db = get_database(session)
|
||||
return db.users.get(id, override_schema=UserFavorites)
|
||||
|
||||
|
||||
@user_router.post("/{id}/favorites/{slug}")
|
||||
@@ -29,7 +29,8 @@ def add_favorite(
|
||||
assert_user_change_allowed(id, current_user)
|
||||
current_user.favorite_recipes.append(slug)
|
||||
|
||||
db.users.update(session, current_user.id, current_user)
|
||||
db = get_database(session)
|
||||
db.users.update(current_user.id, current_user)
|
||||
|
||||
|
||||
@user_router.delete("/{id}/favorites/{slug}")
|
||||
@@ -43,6 +44,7 @@ def remove_favorite(
|
||||
assert_user_change_allowed(id, current_user)
|
||||
current_user.favorite_recipes = [x for x in current_user.favorite_recipes if x != slug]
|
||||
|
||||
db.users.update(session, current_user.id, current_user)
|
||||
db = get_database(session)
|
||||
db.users.update(current_user.id, current_user)
|
||||
|
||||
return
|
||||
|
||||
@@ -3,7 +3,7 @@ from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import settings
|
||||
from mealie.core.security import hash_password
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.routers import UserAPIRouter
|
||||
from mealie.schema.user import ChangePassword
|
||||
@@ -15,7 +15,9 @@ user_router = UserAPIRouter(prefix="")
|
||||
@user_router.put("/{id}/reset-password")
|
||||
async def reset_user_password(id: int, session: Session = Depends(generate_session)):
|
||||
new_password = hash_password(settings.DEFAULT_PASSWORD)
|
||||
db.users.update_password(session, id, new_password)
|
||||
|
||||
db = get_database(session)
|
||||
db.users.update_password(id, new_password)
|
||||
|
||||
|
||||
@user_router.put("/{id}/password")
|
||||
|
||||
@@ -7,7 +7,6 @@ from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_dirs, get_settings
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.data_access_layer.db_access import DatabaseAccessLayer
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import SessionLocal
|
||||
from mealie.schema.user.user import PrivateUser
|
||||
@@ -45,8 +44,6 @@ class BaseHttpService(Generic[T, D], ABC):
|
||||
delete_one: Callable = None
|
||||
delete_all: Callable = None
|
||||
|
||||
db_access: DatabaseAccessLayer = None
|
||||
|
||||
# Type Definitions
|
||||
_schema = None
|
||||
|
||||
@@ -64,7 +61,7 @@ class BaseHttpService(Generic[T, D], ABC):
|
||||
self.background_tasks = background_tasks
|
||||
|
||||
# Static Globals Dependency Injection
|
||||
self.db = get_database()
|
||||
self.db = get_database(session)
|
||||
self.app_dirs = get_app_dirs()
|
||||
self.settings = get_settings()
|
||||
|
||||
@@ -110,7 +107,7 @@ class BaseHttpService(Generic[T, D], ABC):
|
||||
def group_id(self):
|
||||
# TODO: Populate Group in Private User Call WARNING: May require significant refactoring
|
||||
if not self._group_id_cache:
|
||||
group = self.db.groups.get(self.session, self.user.group, "name")
|
||||
group = self.db.groups.get(self.user.group, "name")
|
||||
self._group_id_cache = group.id
|
||||
return self._group_id_cache
|
||||
|
||||
|
||||
@@ -1,27 +1,37 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.data_access_layer.db_access import DatabaseAccessLayer
|
||||
from mealie.db.data_access_layer._access_model import AccessModel
|
||||
|
||||
C = TypeVar("C", bound=BaseModel)
|
||||
R = TypeVar("R", bound=BaseModel)
|
||||
U = TypeVar("U", bound=BaseModel)
|
||||
DAL = TypeVar("DAL", bound=DatabaseAccessLayer)
|
||||
DAL = TypeVar("DAL", bound=AccessModel)
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
class CrudHttpMixins(Generic[C, R, U]):
|
||||
item: C
|
||||
class CrudHttpMixins(Generic[C, R, U], ABC):
|
||||
item: R
|
||||
session: Session
|
||||
dal: DAL
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def dal(self) -> DAL:
|
||||
...
|
||||
|
||||
def populate_item(self, id: int) -> R:
|
||||
self.item = self.dal.get_one(id)
|
||||
return self.item
|
||||
|
||||
def _create_one(self, data: C, exception_msg="generic-create-error") -> R:
|
||||
try:
|
||||
self.item = self.dal.create(self.session, data)
|
||||
self.item = self.dal.create(data)
|
||||
except Exception as ex:
|
||||
logger.exception(ex)
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail={"message": exception_msg, "exception": str(ex)})
|
||||
@@ -33,17 +43,26 @@ class CrudHttpMixins(Generic[C, R, U]):
|
||||
return
|
||||
|
||||
target_id = item_id or self.item.id
|
||||
self.item = self.dal.update(self.session, target_id, data)
|
||||
self.item = self.dal.update(target_id, data)
|
||||
|
||||
return self.item
|
||||
|
||||
def _patch_one(self) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def _delete_one(self, item_id: int = None) -> None:
|
||||
if not self.item:
|
||||
return
|
||||
def _patch_one(self, data: U, item_id: int) -> None:
|
||||
try:
|
||||
self.item = self.dal.patch(item_id, data.dict(exclude_unset=True, exclude_defaults=True))
|
||||
except IntegrityError:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail={"message": "generic-patch-error"})
|
||||
|
||||
def _delete_one(self, item_id: int = None) -> R:
|
||||
target_id = item_id or self.item.id
|
||||
self.item = self.dal.delete(self.session, target_id)
|
||||
logger.info(f"Deleting item with id {target_id}")
|
||||
|
||||
try:
|
||||
self.item = self.dal.delete(target_id)
|
||||
except Exception as ex:
|
||||
logger.exception(ex)
|
||||
raise HTTPException(
|
||||
status.HTTP_400_BAD_REQUEST, detail={"message": "generic-delete-error", "exception": str(ex)}
|
||||
)
|
||||
|
||||
return self.item
|
||||
|
||||
@@ -10,7 +10,7 @@ from pydantic.main import BaseModel
|
||||
|
||||
from mealie.core import root_logger
|
||||
from mealie.core.config import app_dirs
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.services.events import create_backup_event
|
||||
|
||||
@@ -114,29 +114,31 @@ def backup_all(
|
||||
):
|
||||
db_export = ExportDatabase(tag=tag, templates=templates)
|
||||
|
||||
db = get_database(session)
|
||||
|
||||
if export_users:
|
||||
all_users = db.users.get_all(session)
|
||||
all_users = db.users.get_all()
|
||||
db_export.export_items(all_users, "users")
|
||||
|
||||
if export_groups:
|
||||
all_groups = db.groups.get_all(session)
|
||||
all_groups = db.groups.get_all()
|
||||
db_export.export_items(all_groups, "groups")
|
||||
|
||||
if export_recipes:
|
||||
all_recipes = db.recipes.get_all(session)
|
||||
all_recipes = db.recipes.get_all()
|
||||
db_export.export_recipe_dirs()
|
||||
db_export.export_items(all_recipes, "recipes", export_list=False, slug_folder=True)
|
||||
db_export.export_templates(all_recipes)
|
||||
|
||||
all_comments = db.comments.get_all(session)
|
||||
all_comments = db.comments.get_all()
|
||||
db_export.export_items(all_comments, "comments")
|
||||
|
||||
if export_settings:
|
||||
all_settings = db.settings.get_all(session)
|
||||
all_settings = db.settings.get_all()
|
||||
db_export.export_items(all_settings, "settings")
|
||||
|
||||
if export_notifications:
|
||||
all_notifications = db.event_notifications.get_all(session)
|
||||
all_notifications = db.event_notifications.get_all()
|
||||
db_export.export_items(all_notifications, "notifications")
|
||||
|
||||
return db_export.finish_export()
|
||||
|
||||
@@ -8,7 +8,7 @@ from pydantic.main import BaseModel
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import app_dirs
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.schema.admin import (
|
||||
CommentImport,
|
||||
GroupImport,
|
||||
@@ -44,6 +44,7 @@ class ImportDatabase:
|
||||
"""
|
||||
self.user = user
|
||||
self.session = session
|
||||
self.db = get_database(session)
|
||||
self.archive = app_dirs.BACKUP_DIR.joinpath(zip_archive)
|
||||
self.force_imports = force_import
|
||||
|
||||
@@ -72,7 +73,7 @@ class ImportDatabase:
|
||||
recipe.user_id = self.user.id
|
||||
|
||||
import_status = self.import_model(
|
||||
db_table=db.recipes,
|
||||
db_table=self.db.recipes,
|
||||
model=recipe,
|
||||
return_model=RecipeImport,
|
||||
name_attr="name",
|
||||
@@ -101,7 +102,7 @@ class ImportDatabase:
|
||||
comment: CommentOut
|
||||
|
||||
self.import_model(
|
||||
db_table=db.comments,
|
||||
db_table=self.db.comments,
|
||||
model=comment,
|
||||
return_model=CommentImport,
|
||||
name_attr="uuid",
|
||||
@@ -166,7 +167,7 @@ class ImportDatabase:
|
||||
|
||||
for notify in notifications:
|
||||
import_status = self.import_model(
|
||||
db_table=db.event_notifications,
|
||||
db_table=self.db.event_notifications,
|
||||
model=notify,
|
||||
return_model=NotificationImport,
|
||||
name_attr="name",
|
||||
@@ -183,7 +184,7 @@ class ImportDatabase:
|
||||
settings = settings[0]
|
||||
|
||||
try:
|
||||
db.settings.update(self.session, 1, settings.dict())
|
||||
self.db.settings.update(1, settings.dict())
|
||||
import_status = SettingsImport(name="Site Settings", status=True)
|
||||
|
||||
except Exception as inst:
|
||||
@@ -198,7 +199,7 @@ class ImportDatabase:
|
||||
group_imports = []
|
||||
|
||||
for group in groups:
|
||||
import_status = self.import_model(db.groups, group, GroupImport, search_key="name")
|
||||
import_status = self.import_model(self.db.groups, group, GroupImport, search_key="name")
|
||||
group_imports.append(import_status)
|
||||
|
||||
return group_imports
|
||||
@@ -209,13 +210,13 @@ class ImportDatabase:
|
||||
user_imports = []
|
||||
for user in users:
|
||||
if user.id == 1: # Update Default User
|
||||
db.users.update(self.session, 1, user.dict())
|
||||
self.db.users.update(1, user.dict())
|
||||
import_status = UserImport(name=user.full_name, status=True)
|
||||
user_imports.append(import_status)
|
||||
continue
|
||||
|
||||
import_status = self.import_model(
|
||||
db_table=db.users,
|
||||
db_table=self.db.users,
|
||||
model=user,
|
||||
return_model=UserImport,
|
||||
name_attr="full_name",
|
||||
@@ -283,7 +284,7 @@ class ImportDatabase:
|
||||
model_name = getattr(model, name_attr)
|
||||
search_value = getattr(model, search_key)
|
||||
|
||||
item = db_table.get(self.session, search_value, search_key)
|
||||
item = db_table.get(search_value, search_key)
|
||||
if item:
|
||||
if not self.force_imports:
|
||||
return return_model(
|
||||
@@ -293,9 +294,9 @@ class ImportDatabase:
|
||||
)
|
||||
|
||||
primary_key = getattr(item, db_table.primary_key)
|
||||
db_table.delete(self.session, primary_key)
|
||||
db_table.delete(primary_key)
|
||||
try:
|
||||
db_table.create(self.session, model.dict())
|
||||
db_table.create(model.dict())
|
||||
import_status = return_model(name=model_name, status=True)
|
||||
|
||||
except Exception as inst:
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import apprise
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.events import Event, EventCategory
|
||||
|
||||
|
||||
def test_notification(notification_url, event=None) -> bool:
|
||||
|
||||
if event is None:
|
||||
event = Event(
|
||||
title="Test Notification",
|
||||
@@ -38,9 +37,10 @@ def post_notifications(event: Event, notification_urls=list[str], hard_fail=Fals
|
||||
def save_event(title, text, category, session: Session, attachment=None):
|
||||
event = Event(title=title, text=text, category=category)
|
||||
session = session or create_session()
|
||||
db.events.create(session, event.dict())
|
||||
db = get_database(session)
|
||||
db.events.create(event.dict())
|
||||
|
||||
notification_objects = db.event_notifications.get(session=session, match_value=True, match_key=category, limit=9999)
|
||||
notification_objects = db.event_notifications.get(match_value=True, match_key=category, limit=9999)
|
||||
notification_urls = [x.notification_url for x in notification_objects]
|
||||
post_notifications(event, notification_urls, attachment=attachment)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import get_database
|
||||
from mealie.schema.cookbook.cookbook import CreateCookBook, ReadCookBook, RecipeCookBook, SaveCookBook, UpdateCookBook
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
from mealie.services._base_http_service.http_services import UserHttpService
|
||||
@@ -12,17 +13,16 @@ logger = get_logger(module=__name__)
|
||||
|
||||
|
||||
class CookbookService(
|
||||
UserHttpService[int, ReadCookBook],
|
||||
CrudHttpMixins[CreateCookBook, ReadCookBook, UpdateCookBook],
|
||||
UserHttpService[int, ReadCookBook],
|
||||
):
|
||||
event_func = create_group_event
|
||||
_restrict_by_group = True
|
||||
|
||||
_schema = ReadCookBook
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.dal = get_database().cookbooks
|
||||
@cached_property
|
||||
def dal(self):
|
||||
return self.db.cookbooks
|
||||
|
||||
def populate_item(self, item_id: int) -> RecipeCookBook:
|
||||
try:
|
||||
@@ -31,13 +31,13 @@ class CookbookService(
|
||||
pass
|
||||
|
||||
if isinstance(item_id, int):
|
||||
self.item = self.db.cookbooks.get_one(self.session, item_id, override_schema=RecipeCookBook)
|
||||
self.item = self.dal.get_one(item_id, override_schema=RecipeCookBook)
|
||||
|
||||
else:
|
||||
self.item = self.db.cookbooks.get_one(self.session, item_id, key="slug", override_schema=RecipeCookBook)
|
||||
self.item = self.dal.get_one(item_id, key="slug", override_schema=RecipeCookBook)
|
||||
|
||||
def get_all(self) -> list[ReadCookBook]:
|
||||
items = self.db.cookbooks.get(self.session, self.group_id, "group_id", limit=999)
|
||||
items = self.dal.get(self.group_id, "group_id", limit=999)
|
||||
items.sort(key=lambda x: x.position)
|
||||
return items
|
||||
|
||||
@@ -52,7 +52,7 @@ class CookbookService(
|
||||
updated = []
|
||||
|
||||
for cookbook in data:
|
||||
cb = self.db.cookbooks.update(self.session, cookbook.id, cookbook)
|
||||
cb = self.dal.update(cookbook.id, cookbook)
|
||||
updated.append(cb)
|
||||
|
||||
return updated
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi import Depends
|
||||
|
||||
from mealie.core.dependencies.grouped import UserDeps
|
||||
from mealie.core.root_logger import get_logger
|
||||
@@ -17,7 +17,7 @@ logger = get_logger(module=__name__)
|
||||
|
||||
|
||||
class GroupSelfService(UserHttpService[int, str]):
|
||||
_restrict_by_group = True
|
||||
_restrict_by_group = False
|
||||
event_func = create_group_event
|
||||
item: GroupInDB
|
||||
|
||||
@@ -31,31 +31,21 @@ class GroupSelfService(UserHttpService[int, str]):
|
||||
"""Override parent method to remove `item_id` from arguments"""
|
||||
return super().write_existing(item_id=0, deps=deps)
|
||||
|
||||
def assert_existing(self, _: str = None):
|
||||
self.populate_item()
|
||||
|
||||
if not self.item:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if self.item.id != self.group_id:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
def populate_item(self, _: str = None) -> GroupInDB:
|
||||
self.item = self.db.groups.get(self.session, self.group_id)
|
||||
self.item = self.db.groups.get(self.group_id)
|
||||
return self.item
|
||||
|
||||
def update_categories(self, new_categories: list[CategoryBase]):
|
||||
self.item.categories = new_categories
|
||||
return self.db.groups.update(self.session, self.group_id, self.item)
|
||||
return self.db.groups.update(self.group_id, self.item)
|
||||
|
||||
def update_preferences(self, new_preferences: UpdateGroupPreferences):
|
||||
self.db.group_preferences.update(self.session, self.group_id, new_preferences)
|
||||
|
||||
self.db.group_preferences.update(self.group_id, new_preferences)
|
||||
return self.populate_item()
|
||||
|
||||
def create_invite_token(self, uses: int = 1) -> None:
|
||||
token = SaveInviteToken(uses_left=uses, group_id=self.group_id, token=uuid4().hex)
|
||||
return self.db.group_tokens.create(self.session, token)
|
||||
return self.db.group_invite_tokens.create(token)
|
||||
|
||||
def get_invite_tokens(self) -> list[ReadInviteToken]:
|
||||
return self.db.group_tokens.multi_query(self.session, {"group_id": self.group_id})
|
||||
return self.db.group_invite_tokens.multi_query({"group_id": self.group_id})
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.data_access_layer.access_model_factory import Database
|
||||
from mealie.schema.group.group_preferences import CreateGroupPreferences
|
||||
from mealie.schema.user.user import GroupBase, GroupInDB
|
||||
|
||||
|
||||
def create_new_group(session, g_base: GroupBase, g_preferences: CreateGroupPreferences = None) -> GroupInDB:
|
||||
db = get_database()
|
||||
created_group = db.groups.create(session, g_base)
|
||||
def create_new_group(db: Database, g_base: GroupBase, g_preferences: CreateGroupPreferences = None) -> GroupInDB:
|
||||
created_group = db.groups.create(g_base)
|
||||
|
||||
g_preferences = g_preferences or CreateGroupPreferences(group_id=0) # Assign Temporary ID before group is created
|
||||
|
||||
g_preferences.group_id = created_group.id
|
||||
|
||||
db.group_preferences.create(session, g_preferences)
|
||||
db.group_preferences.create(g_preferences)
|
||||
|
||||
return created_group
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
from functools import cached_property
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import get_database
|
||||
from mealie.schema.meal_plan import CreatePlanEntry, ReadPlanEntry, SavePlanEntry, UpdatePlanEntry
|
||||
|
||||
from .._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
@@ -13,26 +13,27 @@ from ..events import create_group_event
|
||||
logger = get_logger(module=__name__)
|
||||
|
||||
|
||||
class MealService(UserHttpService[int, ReadPlanEntry], CrudHttpMixins[CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry]):
|
||||
class MealService(CrudHttpMixins[CreatePlanEntry, ReadPlanEntry, UpdatePlanEntry], UserHttpService[int, ReadPlanEntry]):
|
||||
event_func = create_group_event
|
||||
_restrict_by_group = True
|
||||
|
||||
_schema = ReadPlanEntry
|
||||
item: ReadPlanEntry
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.dal = get_database().meals
|
||||
@cached_property
|
||||
def dal(self):
|
||||
return self.db.meals
|
||||
|
||||
def populate_item(self, id: int) -> ReadPlanEntry:
|
||||
self.item = self.db.meals.get_one(self.session, id)
|
||||
self.item = self.dal.get_one(id)
|
||||
return self.item
|
||||
|
||||
def get_slice(self, start: date = None, end: date = None) -> list[ReadPlanEntry]:
|
||||
# 2 days ago
|
||||
return self.db.meals.get_slice(self.session, start, end, group_id=self.group_id)
|
||||
return self.dal.get_slice(start, end, group_id=self.group_id)
|
||||
|
||||
def get_today(self) -> list[ReadPlanEntry]:
|
||||
return self.db.meals.get_today(self.session, group_id=self.group_id)
|
||||
return self.dal.get_today(group_id=self.group_id)
|
||||
|
||||
def create_one(self, data: CreatePlanEntry) -> ReadPlanEntry:
|
||||
data = self.cast(data, SavePlanEntry)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import get_database
|
||||
from mealie.schema.group import ReadWebhook
|
||||
from mealie.schema.group.webhook import CreateWebhook, SaveWebhook
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
@@ -11,22 +12,21 @@ from mealie.services.events import create_group_event
|
||||
logger = get_logger(module=__name__)
|
||||
|
||||
|
||||
class WebhookService(UserHttpService[int, ReadWebhook], CrudHttpMixins[ReadWebhook, CreateWebhook, CreateWebhook]):
|
||||
class WebhookService(CrudHttpMixins[ReadWebhook, CreateWebhook, CreateWebhook], UserHttpService[int, ReadWebhook]):
|
||||
event_func = create_group_event
|
||||
_restrict_by_group = True
|
||||
|
||||
_schema = ReadWebhook
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.dal = get_database().webhooks
|
||||
@cached_property
|
||||
def dal(self):
|
||||
return self.db.webhooks
|
||||
|
||||
def populate_item(self, id: int) -> ReadWebhook:
|
||||
self.item = self.db.webhooks.get_one(self.session, id)
|
||||
self.item = self.dal.get_one(id)
|
||||
return self.item
|
||||
|
||||
def get_all(self) -> list[ReadWebhook]:
|
||||
return self.db.webhooks.get(self.session, self.group_id, match_key="group_id", limit=9999)
|
||||
return self.dal.get(self.group_id, match_key="group_id", limit=9999)
|
||||
|
||||
def create_one(self, data: CreateWebhook) -> ReadWebhook:
|
||||
data = self.cast(data, SaveWebhook)
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
from datetime import date, timedelta
|
||||
from typing import Union
|
||||
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.meal_plan import MealDayIn, MealPlanIn
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.user import GroupInDB
|
||||
|
||||
|
||||
def set_mealplan_dates(meal_plan_base: MealPlanIn) -> MealPlanIn:
|
||||
for x, plan_days in enumerate(meal_plan_base.plan_days):
|
||||
plan_days: MealDayIn
|
||||
plan_days.date = meal_plan_base.start_date + timedelta(days=x)
|
||||
|
||||
|
||||
def get_todays_meal(session: Session, group: Union[int, GroupInDB]) -> Recipe:
|
||||
"""Returns the given mealplan for today based off the group. If the group
|
||||
Type is of type int, then a query will be made to the database to get the
|
||||
grop object."
|
||||
|
||||
Args:
|
||||
session (Session): SqlAlchemy Session
|
||||
group (Union[int, GroupInDB]): Either the id of the group or the GroupInDB Object
|
||||
|
||||
Returns:
|
||||
Recipe: Pydantic Recipe Object
|
||||
"""
|
||||
|
||||
session = session or create_session()
|
||||
|
||||
if isinstance(group, int):
|
||||
group: GroupInDB = db.groups.get(session, group)
|
||||
|
||||
today_slug = None
|
||||
|
||||
for mealplan in group.mealplans:
|
||||
for plan_day in mealplan.plan_days:
|
||||
if plan_day.date == date.today():
|
||||
if plan_day.meals[0].slug and plan_day.meals[0].slug != "":
|
||||
today_slug = plan_day.meals[0].slug
|
||||
else:
|
||||
return plan_day.meals[0]
|
||||
|
||||
if today_slug:
|
||||
return db.recipes.get(session, today_slug)
|
||||
else:
|
||||
return None
|
||||
@@ -7,7 +7,7 @@ import yaml
|
||||
from pydantic import BaseModel
|
||||
|
||||
from mealie.core import root_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.schema.admin import MigrationImport
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.user.user import PrivateUser
|
||||
@@ -37,6 +37,10 @@ class MigrationBase(BaseModel):
|
||||
|
||||
user: PrivateUser
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return get_database(self.session)
|
||||
|
||||
@property
|
||||
def temp_dir(self) -> TemporaryDirectory:
|
||||
"""unpacks the migration_file into a temporary directory
|
||||
@@ -66,7 +70,7 @@ class MigrationBase(BaseModel):
|
||||
with open(yaml_file, "r") as f:
|
||||
contents = f.read().split("---")
|
||||
recipe_data = {}
|
||||
for x, document in enumerate(contents):
|
||||
for _, document in enumerate(contents):
|
||||
|
||||
# Check if None or Empty String
|
||||
if document is None or document == "":
|
||||
@@ -172,7 +176,7 @@ class MigrationBase(BaseModel):
|
||||
exception = ""
|
||||
status = False
|
||||
try:
|
||||
db.recipes.create(self.session, recipe.dict())
|
||||
self.db.recipes.create(recipe.dict())
|
||||
status = True
|
||||
|
||||
except Exception as inst:
|
||||
|
||||
37
mealie/services/recipe/recipe_food_service.py
Normal file
37
mealie/services/recipe/recipe_food_service.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
from mealie.schema.recipe.recipe_ingredient import CreateIngredientFood, IngredientFood
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
from mealie.services._base_http_service.http_services import UserHttpService
|
||||
from mealie.services.events import create_recipe_event
|
||||
|
||||
|
||||
class RecipeFoodService(
|
||||
CrudHttpMixins[IngredientFood, CreateIngredientFood, CreateIngredientFood],
|
||||
UserHttpService[int, IngredientFood],
|
||||
):
|
||||
event_func = create_recipe_event
|
||||
_restrict_by_group = False
|
||||
_schema = IngredientFood
|
||||
|
||||
@cached_property
|
||||
def dal(self):
|
||||
return self.db.ingredient_foods
|
||||
|
||||
def populate_item(self, id: int) -> IngredientFood:
|
||||
self.item = self.dal.get_one(id)
|
||||
return self.item
|
||||
|
||||
def get_all(self) -> list[IngredientFood]:
|
||||
return self.dal.get_all()
|
||||
|
||||
def create_one(self, data: CreateIngredientFood) -> IngredientFood:
|
||||
return self._create_one(data)
|
||||
|
||||
def update_one(self, data: IngredientFood, item_id: int = None) -> IngredientFood:
|
||||
return self._update_one(data, item_id)
|
||||
|
||||
def delete_one(self, id: int = None) -> IngredientFood:
|
||||
return self._delete_one(id)
|
||||
@@ -1,13 +1,15 @@
|
||||
from functools import cached_property
|
||||
from pathlib import Path
|
||||
from shutil import copytree, rmtree
|
||||
from typing import Union
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from mealie.core.dependencies.grouped import PublicDeps, UserDeps
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.data_access_layer.recipe_access_model import RecipeDataAccessModel
|
||||
from mealie.schema.recipe.recipe import CreateRecipe, Recipe, RecipeSummary
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
from mealie.services._base_http_service.http_services import UserHttpService
|
||||
from mealie.services.events import create_recipe_event
|
||||
from mealie.services.recipe.mixins import recipe_creation_factory
|
||||
@@ -15,7 +17,7 @@ from mealie.services.recipe.mixins import recipe_creation_factory
|
||||
logger = get_logger(module=__name__)
|
||||
|
||||
|
||||
class RecipeService(UserHttpService[str, Recipe]):
|
||||
class RecipeService(CrudHttpMixins[CreateRecipe, Recipe, Recipe], UserHttpService[str, Recipe]):
|
||||
"""
|
||||
Class Methods:
|
||||
`read_existing`: Reads an existing recipe from the database.
|
||||
@@ -25,6 +27,10 @@ class RecipeService(UserHttpService[str, Recipe]):
|
||||
|
||||
event_func = create_recipe_event
|
||||
|
||||
@cached_property
|
||||
def dal(self) -> RecipeDataAccessModel:
|
||||
return self.db.recipes
|
||||
|
||||
@classmethod
|
||||
def write_existing(cls, slug: str, deps: UserDeps = Depends()):
|
||||
return super().write_existing(slug, deps)
|
||||
@@ -35,75 +41,49 @@ class RecipeService(UserHttpService[str, Recipe]):
|
||||
|
||||
def assert_existing(self, slug: str):
|
||||
self.populate_item(slug)
|
||||
|
||||
if not self.item:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if not self.item.settings.public and not self.user:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN)
|
||||
|
||||
def populate_item(self, slug: str) -> Recipe:
|
||||
self.item = self.db.recipes.get(self.session, slug)
|
||||
return self.item
|
||||
|
||||
# CRUD METHODS
|
||||
def get_all(self, start=0, limit=None):
|
||||
return self.db.recipes.multi_query(
|
||||
self.session, {"group_id": self.user.group_id}, start=start, limit=limit, override_schema=RecipeSummary
|
||||
{"group_id": self.user.group_id},
|
||||
start=start,
|
||||
limit=limit,
|
||||
override_schema=RecipeSummary,
|
||||
)
|
||||
|
||||
def create_one(self, create_data: Union[Recipe, CreateRecipe]) -> Recipe:
|
||||
create_data = recipe_creation_factory(self.user, name=create_data.name, additional_attrs=create_data.dict())
|
||||
|
||||
try:
|
||||
self.item = self.db.recipes.create(self.session, create_data)
|
||||
except IntegrityError:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail={"message": "RECIPE_ALREADY_EXISTS"})
|
||||
|
||||
self._create_one(create_data, "RECIPE_ALREAD_EXISTS")
|
||||
self._create_event(
|
||||
"Recipe Created",
|
||||
f"'{self.item.name}' by {self.user.username} \n {self.settings.BASE_URL}/recipe/{self.item.slug}",
|
||||
)
|
||||
|
||||
return self.item
|
||||
|
||||
def update_one(self, update_data: Recipe) -> Recipe:
|
||||
original_slug = self.item.slug
|
||||
|
||||
try:
|
||||
self.item = self.db.recipes.update(self.session, original_slug, update_data)
|
||||
except IntegrityError:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail={"message": "RECIPE_ALREADY_EXISTS"})
|
||||
|
||||
self._check_assets(original_slug)
|
||||
|
||||
self._update_one(update_data, original_slug)
|
||||
self.check_assets(original_slug)
|
||||
return self.item
|
||||
|
||||
def patch_one(self, patch_data: Recipe) -> Recipe:
|
||||
original_slug = self.item.slug
|
||||
|
||||
try:
|
||||
self.item = self.db.recipes.patch(
|
||||
self.session, original_slug, patch_data.dict(exclude_unset=True, exclude_defaults=True)
|
||||
)
|
||||
except IntegrityError:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail={"message": "RECIPE_ALREADY_EXISTS"})
|
||||
|
||||
self._check_assets(original_slug)
|
||||
|
||||
self._patch_one(patch_data, original_slug)
|
||||
self.check_assets(original_slug)
|
||||
return self.item
|
||||
|
||||
def delete_one(self) -> Recipe:
|
||||
try:
|
||||
recipe: Recipe = self.db.recipes.delete(self.session, self.item.slug)
|
||||
self._delete_assets()
|
||||
except Exception:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
self._delete_one(self.item.slug)
|
||||
self.delete_assets()
|
||||
self._create_event("Recipe Delete", f"'{self.item.name}' deleted by {self.user.full_name}")
|
||||
return self.item
|
||||
|
||||
self._create_event("Recipe Delete", f"'{recipe.name}' deleted by {self.user.full_name}")
|
||||
return recipe
|
||||
|
||||
def _check_assets(self, original_slug: str) -> None:
|
||||
def check_assets(self, original_slug: str) -> None:
|
||||
"""Checks if the recipe slug has changed, and if so moves the assets to a new file with the new slug."""
|
||||
if original_slug != self.item.slug:
|
||||
current_dir = self.app_dirs.RECIPE_DATA_DIR.joinpath(original_slug)
|
||||
@@ -123,7 +103,7 @@ class RecipeService(UserHttpService[str, Recipe]):
|
||||
if file.name not in all_asset_files:
|
||||
file.unlink()
|
||||
|
||||
def _delete_assets(self) -> None:
|
||||
def delete_assets(self) -> None:
|
||||
recipe_dir = self.item.directory
|
||||
rmtree(recipe_dir, ignore_errors=True)
|
||||
logger.info(f"Recipe Directory Removed: {self.item.slug}")
|
||||
|
||||
37
mealie/services/recipe/recipe_unit_service.py
Normal file
37
mealie/services/recipe/recipe_unit_service.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
from mealie.schema.recipe.recipe_ingredient import CreateIngredientUnit, IngredientUnit
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
from mealie.services._base_http_service.http_services import UserHttpService
|
||||
from mealie.services.events import create_recipe_event
|
||||
|
||||
|
||||
class RecipeUnitService(
|
||||
CrudHttpMixins[IngredientUnit, CreateIngredientUnit, CreateIngredientUnit],
|
||||
UserHttpService[int, IngredientUnit],
|
||||
):
|
||||
event_func = create_recipe_event
|
||||
_restrict_by_group = False
|
||||
_schema = IngredientUnit
|
||||
|
||||
@cached_property
|
||||
def dal(self):
|
||||
return self.db.ingredient_units
|
||||
|
||||
def populate_item(self, id: int) -> IngredientUnit:
|
||||
self.item = self.dal.get_one(id)
|
||||
return self.item
|
||||
|
||||
def get_all(self) -> list[IngredientUnit]:
|
||||
return self.dal.get_all()
|
||||
|
||||
def create_one(self, data: CreateIngredientUnit) -> IngredientUnit:
|
||||
return self._create_one(data)
|
||||
|
||||
def update_one(self, data: IngredientUnit, item_id: int = None) -> IngredientUnit:
|
||||
return self._update_one(data, item_id)
|
||||
|
||||
def delete_one(self, id: int = None) -> IngredientUnit:
|
||||
return self._delete_one(id)
|
||||
@@ -3,7 +3,7 @@ import datetime
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
from mealie.core import root_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.db.models.event import Event
|
||||
from mealie.schema.user import GroupInDB
|
||||
@@ -39,7 +39,8 @@ def update_webhook_schedule():
|
||||
poll the database for changes and reschedule the webhook time
|
||||
"""
|
||||
session = create_session()
|
||||
all_groups: list[GroupInDB] = db.groups.get_all(session)
|
||||
db = get_database(session)
|
||||
all_groups: list[GroupInDB] = db.groups.get_all()
|
||||
|
||||
for group in all_groups:
|
||||
|
||||
@@ -100,7 +101,8 @@ def add_group_to_schedule(scheduler, group: GroupInDB):
|
||||
|
||||
def init_webhook_schedule(scheduler, job_store: dict):
|
||||
session = create_session()
|
||||
all_groups: list[GroupInDB] = db.groups.get_all(session)
|
||||
db = get_database(session)
|
||||
all_groups: list[GroupInDB] = db.groups.get_all()
|
||||
|
||||
for group in all_groups:
|
||||
job_store.update(add_group_to_schedule(scheduler, group))
|
||||
|
||||
@@ -29,14 +29,14 @@ class RegistrationService(PublicHttpService[int, str]):
|
||||
|
||||
elif registration.group_token and registration.group_token != "":
|
||||
|
||||
token_entry = self.db.group_tokens.get(self.session, registration.group_token)
|
||||
token_entry = self.db.group_invite_tokens.get(registration.group_token)
|
||||
|
||||
print("Token Entry", token_entry)
|
||||
|
||||
if not token_entry:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, {"message": "Invalid group token"})
|
||||
|
||||
group = self.db.groups.get(self.session, token_entry.group_id)
|
||||
group = self.db.groups.get(token_entry.group_id)
|
||||
|
||||
else:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, {"message": "Missing group"})
|
||||
@@ -47,10 +47,10 @@ class RegistrationService(PublicHttpService[int, str]):
|
||||
token_entry.uses_left = token_entry.uses_left - 1
|
||||
|
||||
if token_entry.uses_left == 0:
|
||||
self.db.group_tokens.delete(self.session, token_entry.token)
|
||||
self.db.group_invite_tokens.delete(token_entry.token)
|
||||
|
||||
else:
|
||||
self.db.group_tokens.update(self.session, token_entry.token, token_entry)
|
||||
self.db.group_invite_tokens.update(token_entry.token, token_entry)
|
||||
|
||||
return user
|
||||
|
||||
@@ -64,7 +64,7 @@ class RegistrationService(PublicHttpService[int, str]):
|
||||
group=group.name,
|
||||
)
|
||||
|
||||
return self.db.users.create(self.session, new_user)
|
||||
return self.db.users.create(new_user)
|
||||
|
||||
def _register_new_group(self) -> GroupInDB:
|
||||
group_data = GroupBase(name=self.registration.group)
|
||||
@@ -81,4 +81,4 @@ class RegistrationService(PublicHttpService[int, str]):
|
||||
recipe_disable_amount=self.registration.advanced,
|
||||
)
|
||||
|
||||
return create_new_group(self.session, group_data, group_preferences)
|
||||
return create_new_group(self.db, group_data, group_preferences)
|
||||
|
||||
@@ -25,7 +25,7 @@ class UserService(UserHttpService[int, str]):
|
||||
|
||||
def _populate_target_user(self, id: int = None):
|
||||
if id:
|
||||
self.target_user = self.db.users.get(self.session, id)
|
||||
self.target_user = self.db.users.get(id)
|
||||
if not self.target_user:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND)
|
||||
else:
|
||||
@@ -38,4 +38,4 @@ class UserService(UserHttpService[int, str]):
|
||||
|
||||
self.target_user.password = hash_password(password_change.new_password)
|
||||
|
||||
return self.db.users.update_password(self.session, self.target_user.id, self.target_user.password)
|
||||
return self.db.users.update_password(self.target_user.id, self.target_user.password)
|
||||
|
||||
@@ -3,21 +3,22 @@ import json
|
||||
import requests
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.database import get_database
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.user import GroupInDB
|
||||
from mealie.services.events import create_scheduled_event
|
||||
from mealie.services.meal_services import get_todays_meal
|
||||
|
||||
|
||||
def post_webhooks(group: int, session: Session = None, force=True):
|
||||
session = session or create_session()
|
||||
group_settings: GroupInDB = db.groups.get(session, group)
|
||||
db = get_database(session)
|
||||
group_settings: GroupInDB = db.groups.get(group)
|
||||
|
||||
if not group_settings.webhook_enable and not force:
|
||||
return
|
||||
|
||||
todays_recipe = get_todays_meal(session, group)
|
||||
# TODO: Fix Mealplan Webhooks
|
||||
todays_recipe = None
|
||||
|
||||
if not todays_recipe:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user