📦 Proper Package + Black Config

This commit is contained in:
hay-kot 2021-03-20 11:00:48 -08:00
commit 904c817337
170 changed files with 337 additions and 445 deletions

View file

@ -5,13 +5,14 @@
"python.linting.enabled": true, "python.linting.enabled": true,
"python.autoComplete.extraPaths": ["mealie", "mealie/mealie"], "python.autoComplete.extraPaths": ["mealie", "mealie/mealie"],
"python.analysis.extraPaths": ["mealie", "mealie/mealie"], "python.analysis.extraPaths": ["mealie", "mealie/mealie"],
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false, "python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"python.testing.autoTestDiscoverOnSaveEnabled": false, "python.testing.autoTestDiscoverOnSaveEnabled": false,
"python.testing.pytestArgs": [
"tests"
],
"cSpell.enableFiletypes": ["!javascript", "!python"], "cSpell.enableFiletypes": ["!javascript", "!python"],
"python.testing.pytestArgs": ["mealie"],
"i18n-ally.localesPaths": "frontend/src/locales", "i18n-ally.localesPaths": "frontend/src/locales",
"i18n-ally.enabledFrameworks": ["vue"], "i18n-ally.enabledFrameworks": ["vue"],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",

View file

@ -1,19 +1,17 @@
![Recipe Image](../images/{{ recipe.image }}) ![Recipe Image](../../images/{{ recipe.image }})
# {{ recipe.name }} # {{ recipe.name }}
{{ recipe.description }} {{ recipe.description }}
## Ingredients ## Ingredients
{% for ingredient in recipe.recipeIngredient %} {% for ingredient in recipe.recipeIngredient %}
- [ ] {{ ingredient }} - [ ] {{ ingredient }} {% endfor %}
{% endfor %}
## Instructions ## Instructions
{% for step in recipe.recipeInstructions %} {% for step in recipe.recipeInstructions %}
- [ ] {{ step.text }} - [ ] {{ step.text }} {% endfor %}
{% endfor %}
{% for note in recipe.notes %} {% for note in recipe.notes %}
**{{ note.title }}:** {{ note.text }} **{{ note.title }}:** {{ note.text }}

View file

@ -3,25 +3,25 @@ from fastapi import FastAPI
from fastapi.logger import logger from fastapi.logger import logger
# import utils.startup as startup # import utils.startup as startup
from core.config import APP_VERSION, PORT, docs_url, redoc_url from mealie.core.config import APP_VERSION, PORT, docs_url, redoc_url
from db.db_setup import sql_exists from mealie.db.db_setup import sql_exists
from db.init_db import init_db from mealie.db.init_db import init_db
from routes import ( from mealie.routes import (
backup_routes, backup_routes,
debug_routes, debug_routes,
migration_routes, migration_routes,
setting_routes, setting_routes,
theme_routes, theme_routes,
) )
from routes.groups import groups from mealie.routes.groups import groups
from routes.mealplans import mealplans from mealie.routes.mealplans import mealplans
from routes.recipe import ( from mealie.routes.recipe import (
all_recipe_routes, all_recipe_routes,
category_routes, category_routes,
recipe_crud_routes, recipe_crud_routes,
tag_routes, tag_routes,
) )
from routes.users import users from mealie.routes.users import users
app = FastAPI( app = FastAPI(
title="Mealie", title="Mealie",
@ -37,7 +37,7 @@ def data_base_first_run():
def start_scheduler(): def start_scheduler():
import services.scheduler.scheduled_jobs import mealie.services.scheduler.scheduled_jobs
def api_routers(): def api_routers():
@ -68,8 +68,8 @@ if not sql_exists:
api_routers() api_routers()
start_scheduler() start_scheduler()
if __name__ == "__main__":
logger.info("-----SYSTEM STARTUP-----") def main():
uvicorn.run( uvicorn.run(
"app:app", "app:app",
@ -81,3 +81,8 @@ if __name__ == "__main__":
workers=1, workers=1,
forwarded_allow_ips="*", forwarded_allow_ips="*",
) )
if __name__ == "__main__":
logger.info("-----SYSTEM STARTUP-----")
main()

View file

@ -78,9 +78,7 @@ if DATABASE_TYPE == "sqlite":
SQLITE_FILE = SQLITE_DIR.joinpath(f"mealie_{DB_VERSION}.sqlite") SQLITE_FILE = SQLITE_DIR.joinpath(f"mealie_{DB_VERSION}.sqlite")
else: else:
raise Exception( raise Exception("Unable to determine database type. Acceptible options are 'sqlite' ")
"Unable to determine database type. Acceptible options are 'sqlite' "
)
# Mongo Database # Mongo Database
MEALIE_DB_NAME = os.getenv("mealie_db_name", "mealie") MEALIE_DB_NAME = os.getenv("mealie_db_name", "mealie")

View file

@ -1,21 +1,21 @@
from schema.category import RecipeCategoryResponse, RecipeTagResponse from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse
from schema.meal import MealPlanInDB from mealie.schema.meal import MealPlanInDB
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
from schema.settings import SiteSettings as SiteSettingsSchema from mealie.schema.settings import SiteSettings as SiteSettingsSchema
from schema.sign_up import SignUpOut from mealie.schema.sign_up import SignUpOut
from schema.theme import SiteTheme from mealie.schema.theme import SiteTheme
from schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, UserInDB
from sqlalchemy.orm import load_only from sqlalchemy.orm import load_only
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from db.db_base import BaseDocument from mealie.db.db_base import BaseDocument
from db.models.group import Group from mealie.db.models.group import Group
from db.models.mealplan import MealPlanModel from mealie.db.models.mealplan import MealPlanModel
from db.models.recipe.recipe import Category, RecipeModel, Tag from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag
from db.models.settings import SiteSettings from mealie.db.models.settings import SiteSettings
from db.models.sign_up import SignUp from mealie.db.models.sign_up import SignUp
from db.models.theme import SiteThemeModel from mealie.db.models.theme import SiteThemeModel
from db.models.users import User from mealie.db.models.users import User
class _Recipes(BaseDocument): class _Recipes(BaseDocument):
@ -95,9 +95,7 @@ class _Groups(BaseDocument):
self.orm_mode = True self.orm_mode = True
self.schema = GroupInDB self.schema = GroupInDB
def get_meals( def get_meals(self, session: Session, match_value: str, match_key: str = "name") -> list[MealPlanInDB]:
self, session: Session, match_value: str, match_key: str = "name"
) -> list[MealPlanInDB]:
"""A Helper function to get the group from the database and return a sorted list of """A Helper function to get the group from the database and return a sorted list of
Args: Args:
@ -108,13 +106,11 @@ class _Groups(BaseDocument):
Returns: Returns:
list[MealPlanInDB]: [description] list[MealPlanInDB]: [description]
""" """
group: GroupInDB = ( group: GroupInDB = session.query(self.sql_model).filter_by(**{match_key: match_value}).one_or_none()
session.query(self.sql_model)
.filter_by(**{match_key: match_value})
.one_or_none()
)
return sorted(group.mealplans, key=lambda mealplan: mealplan.startDate) # Potentially not needed? column is sorted by SqlAlchemy based on startDate
# return sorted(group.mealplans, key=lambda mealplan: mealplan.startDate)
return group.mealplans
class _SignUps(BaseDocument): class _SignUps(BaseDocument):

View file

@ -4,7 +4,7 @@ from pydantic import BaseModel
from sqlalchemy.orm import load_only from sqlalchemy.orm import load_only
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class BaseDocument: class BaseDocument:
@ -16,15 +16,10 @@ class BaseDocument:
self.schema: BaseModel self.schema: BaseModel
# TODO: Improve Get All Query Functionality # TODO: Improve Get All Query Functionality
def get_all( def get_all(self, session: Session, limit: int = None, order_by: str = None) -> List[dict]:
self, session: Session, limit: int = None, order_by: str = None
) -> List[dict]:
if self.orm_mode: if self.orm_mode:
return [ return [self.schema.from_orm(x) for x in session.query(self.sql_model).limit(limit).all()]
self.schema.from_orm(x)
for x in session.query(self.sql_model).limit(limit).all()
]
list = [x.dict() for x in session.query(self.sql_model).limit(limit).all()] list = [x.dict() for x in session.query(self.sql_model).limit(limit).all()]
@ -33,9 +28,7 @@ class BaseDocument:
return list return list
def get_all_limit_columns( def get_all_limit_columns(self, session: Session, fields: List[str], limit: int = None) -> List[SqlAlchemyBase]:
self, session: Session, fields: List[str], limit: int = None
) -> List[SqlAlchemyBase]:
"""Queries the database for the selected model. Restricts return responses to the """Queries the database for the selected model. Restricts return responses to the
keys specified under "fields" keys specified under "fields"
@ -47,9 +40,7 @@ class BaseDocument:
Returns: Returns:
list[SqlAlchemyBase]: Returns a list of ORM objects list[SqlAlchemyBase]: Returns a list of ORM objects
""" """
results = ( results = session.query(self.sql_model).options(load_only(*fields)).limit(limit).all()
session.query(self.sql_model).options(load_only(*fields)).limit(limit).all()
)
return results return results
@ -63,15 +54,11 @@ class BaseDocument:
Returns: Returns:
list[str]: list[str]:
""" """
results = session.query(self.sql_model).options( results = session.query(self.sql_model).options(load_only(str(self.primary_key)))
load_only(str(self.primary_key))
)
results_as_dict = [x.dict() for x in results] results_as_dict = [x.dict() for x in results]
return [x.get(self.primary_key) for x in results_as_dict] return [x.get(self.primary_key) for x in results_as_dict]
def _query_one( def _query_one(self, session: Session, match_value: str, match_key: str = None) -> SqlAlchemyBase:
self, session: Session, match_value: str, match_key: str = None
) -> SqlAlchemyBase:
"""Query the sql database for one item an return the sql alchemy model """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. object. If no match key is provided the primary_key attribute will be used.
@ -85,15 +72,11 @@ class BaseDocument:
if match_key == None: if match_key == None:
match_key = self.primary_key match_key = self.primary_key
result = ( result = session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
)
return result return result
def get( def get(self, session: Session, match_value: str, match_key: str = None, limit=1) -> dict or List[dict]:
self, session: Session, match_value: str, match_key: str = None, limit=1
) -> dict or List[dict]:
"""Retrieves an entry from the database by matching a key/value pair. If no """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. key is provided the class objects primary key will be used to match against.
@ -109,12 +92,7 @@ class BaseDocument:
if match_key == None: if match_key == None:
match_key = self.primary_key match_key = self.primary_key
result = ( result = session.query(self.sql_model).filter_by(**{match_key: match_value}).limit(limit).all()
session.query(self.sql_model)
.filter_by(**{match_key: match_value})
.limit(limit)
.all()
)
if limit == 1: if limit == 1:
try: try:
@ -167,11 +145,7 @@ class BaseDocument:
return return_data return return_data
def delete(self, session: Session, primary_key_value) -> dict: def delete(self, session: Session, primary_key_value) -> dict:
result = ( result = session.query(self.sql_model).filter_by(**{self.primary_key: primary_key_value}).one()
session.query(self.sql_model)
.filter_by(**{self.primary_key: primary_key_value})
.one()
)
session.delete(result) session.delete(result)
session.commit() session.commit()

View file

@ -1,7 +1,7 @@
from core.config import SQLITE_FILE, USE_SQL from mealie.core.config import SQLITE_FILE, USE_SQL
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from db.models.db_session import sql_global_init from mealie.db.models.db_session import sql_global_init
sql_exists = True sql_exists = True

View file

@ -1,13 +1,13 @@
from core.config import DEFAULT_GROUP from mealie.core.config import DEFAULT_GROUP
from core.security import get_password_hash from mealie.core.security import get_password_hash
from fastapi.logger import logger from fastapi.logger import logger
from schema.settings import SiteSettings from mealie.schema.settings import SiteSettings
from schema.theme import SiteTheme from mealie.schema.theme import SiteTheme
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
def init_db(db: Session = None) -> None: def init_db(db: Session = None) -> None:

View file

@ -1,7 +1,7 @@
from db.models.mealplan import * from mealie.db.models.mealplan import *
from db.models.recipe.recipe import * from mealie.db.models.recipe.recipe import *
from db.models.settings import * from mealie.db.models.settings import *
from db.models.theme import * from mealie.db.models.theme import *
from db.models.users import * from mealie.db.models.users import *
from db.models.sign_up import * from mealie.db.models.sign_up import *
from db.models.group import * from mealie.db.models.group import *

View file

@ -1,7 +1,7 @@
from pathlib import Path from pathlib import Path
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
@ -18,7 +18,7 @@ def sql_global_init(db_file: Path, check_thread=False):
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
import db.models._all_models import mealie.db.models._all_models
SqlAlchemyBase.metadata.create_all(engine) SqlAlchemyBase.metadata.create_all(engine)

View file

@ -1,8 +1,8 @@
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from core.config import DEFAULT_GROUP from mealie.core.config import DEFAULT_GROUP
from db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
from db.models.recipe.category import Category, group2categories from mealie.db.models.recipe.category import Category, group2categories
from fastapi.logger import logger from fastapi.logger import logger
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -20,18 +20,17 @@ class Group(SqlAlchemyBase, BaseMixins):
name = sa.Column(sa.String, index=True, nullable=False, unique=True) name = sa.Column(sa.String, index=True, nullable=False, unique=True)
users = orm.relationship("User", back_populates="group") users = orm.relationship("User", back_populates="group")
mealplans = orm.relationship( mealplans = orm.relationship(
"MealPlanModel", back_populates="group", single_parent=True "MealPlanModel",
) back_populates="group",
categories = orm.relationship( single_parent=True,
"Category", secondary=group2categories, single_parent=True order_by="MealPlanModel.startDate",
) )
categories = orm.relationship("Category", secondary=group2categories, single_parent=True)
# Webhook Settings # Webhook Settings
webhook_enable = sa.Column(sa.Boolean, default=False) webhook_enable = sa.Column(sa.Boolean, default=False)
webhook_time = sa.Column(sa.String, default="00:00") webhook_time = sa.Column(sa.String, default="00:00")
webhook_urls = orm.relationship( webhook_urls = orm.relationship("WebhookURLModel", uselist=True, cascade="all, delete")
"WebhookURLModel", uselist=True, cascade="all, delete"
)
def __init__( def __init__(
self, self,
@ -46,10 +45,7 @@ class Group(SqlAlchemyBase, BaseMixins):
webhook_urls=[], webhook_urls=[],
) -> None: ) -> None:
self.name = name self.name = name
self.categories = [ self.categories = [Category.get_ref(session=session, slug=cat.get("slug")) for cat in categories]
Category.get_ref(session=session, slug=cat.get("slug"))
for cat in categories
]
self.webhook_enable = webhook_enable self.webhook_enable = webhook_enable
self.webhook_time = webhook_time self.webhook_time = webhook_time

View file

@ -2,8 +2,8 @@ from typing import List
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.models.group import Group from mealie.db.models.group import Group
from db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
class Meal(SqlAlchemyBase): class Meal(SqlAlchemyBase):
@ -33,9 +33,7 @@ class MealPlanModel(SqlAlchemyBase, BaseMixins):
group_id = sa.Column(sa.String, sa.ForeignKey("groups.id")) group_id = sa.Column(sa.String, sa.ForeignKey("groups.id"))
group = orm.relationship("Group", back_populates="mealplans") group = orm.relationship("Group", back_populates="mealplans")
def __init__( def __init__(self, startDate, endDate, meals, group: str, uid=None, session=None) -> None:
self, startDate, endDate, meals, group: str, uid=None, session=None
) -> None:
self.startDate = startDate self.startDate = startDate
self.endDate = endDate self.endDate = endDate
self.group = Group.get_ref(session, group) self.group = Group.get_ref(session, group)

View file

@ -1,7 +1,7 @@
from datetime import date from datetime import date
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class ApiExtras(SqlAlchemyBase): class ApiExtras(SqlAlchemyBase):

View file

@ -1,6 +1,6 @@
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
from fastapi.logger import logger from fastapi.logger import logger
from slugify import slugify from slugify import slugify
from sqlalchemy.orm import validates from sqlalchemy.orm import validates
@ -32,9 +32,7 @@ class Category(SqlAlchemyBase):
id = sa.Column(sa.Integer, primary_key=True) id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String, index=True, nullable=False) name = sa.Column(sa.String, index=True, nullable=False)
slug = sa.Column(sa.String, index=True, unique=True, nullable=False) slug = sa.Column(sa.String, index=True, unique=True, nullable=False)
recipes = orm.relationship( recipes = orm.relationship("RecipeModel", secondary=recipes2categories, back_populates="recipeCategory")
"RecipeModel", secondary=recipes2categories, back_populates="recipeCategory"
)
@validates("name") @validates("name")
def validate_name(self, key, name): def validate_name(self, key, name):

View file

@ -1,5 +1,5 @@
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class RecipeIngredient(SqlAlchemyBase): class RecipeIngredient(SqlAlchemyBase):

View file

@ -1,5 +1,5 @@
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class RecipeInstruction(SqlAlchemyBase): class RecipeInstruction(SqlAlchemyBase):

View file

@ -1,5 +1,5 @@
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class Note(SqlAlchemyBase): class Note(SqlAlchemyBase):
@ -12,4 +12,3 @@ class Note(SqlAlchemyBase):
def __init__(self, title, text) -> None: def __init__(self, title, text) -> None:
self.title = title self.title = title
self.text = text self.text = text

View file

@ -1,5 +1,5 @@
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class Nutrition(SqlAlchemyBase): class Nutrition(SqlAlchemyBase):
@ -28,4 +28,3 @@ class Nutrition(SqlAlchemyBase):
self.proteinContent = proteinContent self.proteinContent = proteinContent
self.sodiumContent = sodiumContent self.sodiumContent = sodiumContent
self.sugarContent = sugarContent self.sugarContent = sugarContent

View file

@ -4,15 +4,15 @@ from typing import List
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
from db.models.recipe.api_extras import ApiExtras from mealie.db.models.recipe.api_extras import ApiExtras
from db.models.recipe.category import Category, recipes2categories from mealie.db.models.recipe.category import Category, recipes2categories
from db.models.recipe.ingredient import RecipeIngredient from mealie.db.models.recipe.ingredient import RecipeIngredient
from db.models.recipe.instruction import RecipeInstruction from mealie.db.models.recipe.instruction import RecipeInstruction
from db.models.recipe.note import Note from mealie.db.models.recipe.note import Note
from db.models.recipe.nutrition import Nutrition from mealie.db.models.recipe.nutrition import Nutrition
from db.models.recipe.tag import Tag, recipes2tags from mealie.db.models.recipe.tag import Tag, recipes2tags
from db.models.recipe.tool import Tool from mealie.db.models.recipe.tool import Tool
from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import validates from sqlalchemy.orm import validates
@ -33,12 +33,8 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
recipeYield = sa.Column(sa.String) recipeYield = sa.Column(sa.String)
recipeCuisine = sa.Column(sa.String) recipeCuisine = sa.Column(sa.String)
tool: List[Tool] = orm.relationship("Tool", cascade="all, delete") tool: List[Tool] = orm.relationship("Tool", cascade="all, delete")
nutrition: Nutrition = orm.relationship( nutrition: Nutrition = orm.relationship("Nutrition", uselist=False, cascade="all, delete")
"Nutrition", uselist=False, cascade="all, delete" recipeCategory: List = orm.relationship("Category", secondary=recipes2categories, back_populates="recipes")
)
recipeCategory: List = orm.relationship(
"Category", secondary=recipes2categories, back_populates="recipes"
)
recipeIngredient: List[RecipeIngredient] = orm.relationship( recipeIngredient: List[RecipeIngredient] = orm.relationship(
"RecipeIngredient", "RecipeIngredient",
@ -55,9 +51,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
# Mealie Specific # Mealie Specific
slug = sa.Column(sa.String, index=True, unique=True) slug = sa.Column(sa.String, index=True, unique=True)
tags: List[Tag] = orm.relationship( tags: List[Tag] = orm.relationship("Tag", secondary=recipes2tags, back_populates="recipes")
"Tag", secondary=recipes2tags, back_populates="recipes"
)
dateAdded = sa.Column(sa.Date, default=date.today) dateAdded = sa.Column(sa.Date, default=date.today)
notes: List[Note] = orm.relationship("Note", cascade="all, delete") notes: List[Note] = orm.relationship("Note", cascade="all, delete")
rating = sa.Column(sa.Integer) rating = sa.Column(sa.Integer)
@ -106,9 +100,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
self.tool = [Tool(tool=x) for x in tool] if tool else [] self.tool = [Tool(tool=x) for x in tool] if tool else []
self.recipeYield = recipeYield self.recipeYield = recipeYield
self.recipeIngredient = [ self.recipeIngredient = [RecipeIngredient(ingredient=ingr) for ingr in recipeIngredient]
RecipeIngredient(ingredient=ingr) for ingr in recipeIngredient
]
self.recipeInstructions = [ self.recipeInstructions = [
RecipeInstruction(text=instruc.get("text"), type=instruc.get("@type", None)) RecipeInstruction(text=instruc.get("text"), type=instruc.get("@type", None))
for instruc in recipeInstructions for instruc in recipeInstructions
@ -117,10 +109,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
self.prepTime = prepTime self.prepTime = prepTime
self.performTime = performTime self.performTime = performTime
self.recipeCategory = [ self.recipeCategory = [Category.create_if_not_exist(session=session, name=cat) for cat in recipeCategory]
Category.create_if_not_exist(session=session, name=cat)
for cat in recipeCategory
]
# Mealie Specific # Mealie Specific
self.tags = [Tag.create_if_not_exist(session=session, name=tag) for tag in tags] self.tags = [Tag.create_if_not_exist(session=session, name=tag) for tag in tags]

View file

@ -1,6 +1,6 @@
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
from fastapi.logger import logger from fastapi.logger import logger
from slugify import slugify from slugify import slugify
from sqlalchemy.orm import validates from sqlalchemy.orm import validates
@ -18,9 +18,7 @@ class Tag(SqlAlchemyBase):
id = sa.Column(sa.Integer, primary_key=True) id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String, index=True, nullable=False) name = sa.Column(sa.String, index=True, nullable=False)
slug = sa.Column(sa.String, index=True, unique=True, nullable=False) slug = sa.Column(sa.String, index=True, unique=True, nullable=False)
recipes = orm.relationship( recipes = orm.relationship("RecipeModel", secondary=recipes2tags, back_populates="tags")
"RecipeModel", secondary=recipes2tags, back_populates="tags"
)
@validates("name") @validates("name")
def validate_name(self, key, name): def validate_name(self, key, name):
@ -31,7 +29,6 @@ class Tag(SqlAlchemyBase):
self.name = name.strip() self.name = name.strip()
self.slug = slugify(self.name) self.slug = slugify(self.name)
@staticmethod @staticmethod
def create_if_not_exist(session, name: str = None): def create_if_not_exist(session, name: str = None):
test_slug = slugify(name) test_slug = slugify(name)

View file

@ -1,5 +1,5 @@
import sqlalchemy as sa import sqlalchemy as sa
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class Tool(SqlAlchemyBase): class Tool(SqlAlchemyBase):

View file

@ -1,7 +1,7 @@
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
from db.models.recipe.category import Category, site_settings2categories from mealie.db.models.recipe.category import Category, site_settings2categories
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@ -29,10 +29,7 @@ class SiteSettings(SqlAlchemyBase, BaseMixins):
self.language = language self.language = language
self.cards_per_section = cards_per_section self.cards_per_section = cards_per_section
self.show_recent = show_recent self.show_recent = show_recent
self.categories = [ self.categories = [Category.get_ref(session=session, name=cat.get("slug")) for cat in categories]
Category.get_ref(session=session, name=cat.get("slug"))
for cat in categories
]
def update(self, *args, **kwarg): def update(self, *args, **kwarg):
self.__init__(*args, **kwarg) self.__init__(*args, **kwarg)

View file

@ -1,4 +1,4 @@
from db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
from sqlalchemy import Boolean, Column, Integer, String from sqlalchemy import Boolean, Column, Integer, String

View file

@ -1,6 +1,6 @@
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.models.model_base import SqlAlchemyBase from mealie.db.models.model_base import SqlAlchemyBase
class SiteThemeModel(SqlAlchemyBase): class SiteThemeModel(SqlAlchemyBase):

View file

@ -1,6 +1,6 @@
from core.config import DEFAULT_GROUP from mealie.core.config import DEFAULT_GROUP
from db.models.group import Group from mealie.db.models.group import Group
from db.models.model_base import BaseMixins, SqlAlchemyBase from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, orm from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, orm
# I'm not sure this is necessasry, browser based settings may be sufficient # I'm not sure this is necessasry, browser based settings may be sufficient

View file

@ -1,13 +1,13 @@
import operator import operator
import shutil import shutil
from core.config import BACKUP_DIR, TEMPLATE_DIR from mealie.core.config import BACKUP_DIR, TEMPLATE_DIR
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from schema.backup import BackupJob, ImportJob, Imports, LocalBackup from mealie.schema.backup import BackupJob, ImportJob, Imports, LocalBackup
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from services.backups.exports import backup_all from mealie.services.backups.exports import backup_all
from services.backups.imports import ImportDatabase from mealie.services.backups.imports import ImportDatabase
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse from starlette.responses import FileResponse
@ -71,17 +71,13 @@ async def upload_nextcloud_zipfile(file_name: str):
file = BACKUP_DIR.joinpath(file_name) file = BACKUP_DIR.joinpath(file_name)
if file.is_file: if file.is_file:
return FileResponse( return FileResponse(file, media_type="application/octet-stream", filename=file_name)
file, media_type="application/octet-stream", filename=file_name
)
else: else:
return SnackResponse.error("No File Found") return SnackResponse.error("No File Found")
@router.post("/{file_name}/import", status_code=200) @router.post("/{file_name}/import", status_code=200)
def import_database( def import_database(file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)):
file_name: str, import_data: ImportJob, session: Session = Depends(generate_session)
):
""" Import a database backup file generated from Mealie. """ """ Import a database backup file generated from Mealie. """
import_db = ImportDatabase( import_db = ImportDatabase(

View file

@ -1,6 +1,6 @@
import json import json
from core.config import APP_VERSION, DEBUG_DIR, LOGGER_FILE from mealie.core.config import APP_VERSION, DEBUG_DIR, LOGGER_FILE
from fastapi import APIRouter from fastapi import APIRouter
router = APIRouter(prefix="/api/debug", tags=["Debug"]) router = APIRouter(prefix="/api/debug", tags=["Debug"])

View file

@ -1,10 +1,10 @@
from core.config import SECRET from mealie.core.config import SECRET
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
from fastapi_login import LoginManager from fastapi_login import LoginManager
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from schema.user import UserInDB from mealie.schema.user import UserInDB
manager = LoginManager(SECRET, "/api/auth/token") manager = LoginManager(SECRET, "/api/auth/token")
@ -21,4 +21,3 @@ def query_user(user_email: str, session: Session = None) -> UserInDB:
user = db.users.get(session, user_email, "email") user = db.users.get(session, user_email, "email")
session.close() session.close()
return user return user

View file

@ -1,9 +1,9 @@
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from routes.deps import manager from mealie.routes.deps import manager
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.user import GroupBase, GroupInDB, UpdateGroup, UserInDB from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/groups", tags=["Groups"]) router = APIRouter(prefix="/api/groups", tags=["Groups"])
@ -59,9 +59,7 @@ async def update_group_data(
@router.delete("/{id}") @router.delete("/{id}")
async def delete_user_group( async def delete_user_group(id: int, current_user=Depends(manager), session: Session = Depends(generate_session)):
id: int, current_user=Depends(manager), session: Session = Depends(generate_session)
):
""" Removes a user group from the database """ """ Removes a user group from the database """
if id == 1: if id == 1:

View file

@ -1,7 +1,6 @@
from fastapi import APIRouter from fastapi import APIRouter
from routes.groups import crud from mealie.routes.groups import crud
router = APIRouter() router = APIRouter()
router.include_router(crud.router) router.include_router(crud.router)

View file

@ -1,13 +1,13 @@
import datetime import datetime
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from routes.deps import manager from mealie.routes.deps import manager
from schema.meal import MealPlanIn, MealPlanInDB from mealie.schema.meal import MealPlanIn, MealPlanInDB
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, UserInDB
from services.meal_services import get_todays_meal, process_meals from mealie.services.meal_services import get_todays_meal, process_meals
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"]) router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
@ -37,9 +37,7 @@ def create_meal_plan(
@router.put("/{plan_id}") @router.put("/{plan_id}")
def update_meal_plan( def update_meal_plan(plan_id: str, meal_plan: MealPlanIn, session: Session = Depends(generate_session)):
plan_id: str, meal_plan: MealPlanIn, session: Session = Depends(generate_session)
):
""" Updates a meal plan based off ID """ """ Updates a meal plan based off ID """
processed_plan = process_meals(session, meal_plan) processed_plan = process_meals(session, meal_plan)
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict()) processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())

View file

@ -1,8 +1,8 @@
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from schema.meal import MealPlanInDB from mealie.schema.meal import MealPlanInDB
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"]) router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
@ -16,8 +16,6 @@ def get_shopping_list(id: str, session: Session = Depends(generate_session)):
mealplan: MealPlanInDB mealplan: MealPlanInDB
slugs = [x.slug for x in mealplan.meals] slugs = [x.slug for x in mealplan.meals]
recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs] recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs]
ingredients = [ ingredients = [{"name": x.name, "recipeIngredient": x.recipeIngredient} for x in recipes if x]
{"name": x.name, "recipeIngredient": x.recipeIngredient} for x in recipes if x
]
return ingredients return ingredients

View file

@ -1,5 +1,5 @@
from fastapi import APIRouter from fastapi import APIRouter
from routes.mealplans import crud, helpers from mealie.routes.mealplans import crud, helpers
router = APIRouter() router = APIRouter()

View file

@ -2,14 +2,14 @@ import operator
import shutil import shutil
from typing import List from typing import List
from core.config import MIGRATION_DIR from mealie.core.config import MIGRATION_DIR
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from schema.migration import MigrationFile, Migrations from mealie.schema.migration import MigrationFile, Migrations
from services.migrations.chowdown import chowdown_migrate as chowdow_migrate from mealie.services.migrations.chowdown import chowdown_migrate as chowdow_migrate
from services.migrations.nextcloud import migrate as nextcloud_migrate from mealie.services.migrations.nextcloud import migrate as nextcloud_migrate
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
router = APIRouter(prefix="/api/migrations", tags=["Migration"]) router = APIRouter(prefix="/api/migrations", tags=["Migration"])
@ -36,9 +36,7 @@ def get_avaiable_nextcloud_imports():
@router.post("/{type}/{file_name}/import") @router.post("/{type}/{file_name}/import")
def import_nextcloud_directory( def import_nextcloud_directory(type: str, file_name: str, session: Session = Depends(generate_session)):
type: str, file_name: str, session: Session = Depends(generate_session)
):
""" Imports all the recipes in a given directory """ """ Imports all the recipes in a given directory """
file_path = MIGRATION_DIR.joinpath(type, file_name) file_path = MIGRATION_DIR.joinpath(type, file_name)
if type == "nextcloud": if type == "nextcloud":

View file

@ -1,9 +1,9 @@
from typing import List, Optional from typing import List, Optional
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query
from schema.recipe import AllRecipeRequest from mealie.schema.recipe import AllRecipeRequest
from slugify import slugify from slugify import slugify
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -44,9 +44,7 @@ def get_all_recipes(
@router.post("/api/recipes") @router.post("/api/recipes")
def get_all_recipes_post( def get_all_recipes_post(body: AllRecipeRequest, session: Session = Depends(generate_session)):
body: AllRecipeRequest, session: Session = Depends(generate_session)
):
""" """
Returns key data for all recipes based off the body data provided. Returns key data for all recipes based off the body data provided.
For example, if slug, image, and name are provided you will recieve a list of For example, if slug, image, and name are provided you will recieve a list of
@ -76,9 +74,7 @@ def get_all_recipes_post(
def filter_by_category(categories: list, session: Session = Depends(generate_session)): def filter_by_category(categories: list, session: Session = Depends(generate_session)):
""" pass a list of categories and get a list of recipes associated with those categories """ """ pass a list of categories and get a list of recipes associated with those categories """
#! This should be refactored into a single database call, but I couldn't figure it out #! This should be refactored into a single database call, but I couldn't figure it out
in_category = [ in_category = [db.categories.get(session, slugify(cat), limit=1) for cat in categories]
db.categories.get(session, slugify(cat), limit=1) for cat in categories
]
in_category = [cat.get("recipes") for cat in in_category if cat] in_category = [cat.get("recipes") for cat in in_category if cat]
in_category = [item for sublist in in_category for item in sublist] in_category = [item for sublist in in_category for item in sublist]
return in_category return in_category

View file

@ -1,11 +1,11 @@
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from schema.category import RecipeCategoryResponse from mealie.schema.category import RecipeCategoryResponse
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
router = APIRouter( router = APIRouter(
prefix="/api/categories", prefix="/api/categories",
@ -20,17 +20,13 @@ async def get_all_recipe_categories(session: Session = Depends(generate_session)
@router.get("/{category}", response_model=RecipeCategoryResponse) @router.get("/{category}", response_model=RecipeCategoryResponse)
def get_all_recipes_by_category( def get_all_recipes_by_category(category: str, session: Session = Depends(generate_session)):
category: str, session: Session = Depends(generate_session)
):
""" Returns a list of recipes associated with the provided category. """ """ Returns a list of recipes associated with the provided category. """
return db.categories.get(session, category) return db.categories.get(session, category)
@router.delete("/{category}") @router.delete("/{category}")
async def delete_recipe_category( async def delete_recipe_category(category: str, session: Session = Depends(generate_session)):
category: str, session: Session = Depends(generate_session)
):
"""Removes a recipe category from the database. Deleting a """Removes a recipe category from the database. Deleting a
category does not impact a recipe. The category will be removed category does not impact a recipe. The category will be removed
from any recipes that contain it""" from any recipes that contain it"""

View file

@ -1,12 +1,12 @@
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, Form, HTTPException from fastapi import APIRouter, Depends, File, Form, HTTPException
from fastapi.logger import logger from fastapi.logger import logger
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from schema.recipe import Recipe, RecipeURLIn from mealie.schema.recipe import Recipe, RecipeURLIn
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from services.image_services import read_image, write_image from mealie.services.image_services import read_image, write_image
from services.scraper.scraper import create_from_url from mealie.services.scraper.scraper import create_from_url
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter( router = APIRouter(
@ -41,9 +41,7 @@ def get_recipe(recipe_slug: str, session: Session = Depends(generate_session)):
@router.put("/{recipe_slug}") @router.put("/{recipe_slug}")
def update_recipe( def update_recipe(recipe_slug: str, data: Recipe, session: Session = Depends(generate_session)):
recipe_slug: str, data: Recipe, session: Session = Depends(generate_session)
):
""" Updates a recipe by existing slug and data. """ """ Updates a recipe by existing slug and data. """
recipe: Recipe = db.recipes.update(session, recipe_slug, data.dict()) recipe: Recipe = db.recipes.update(session, recipe_slug, data.dict())
@ -58,9 +56,7 @@ def delete_recipe(recipe_slug: str, session: Session = Depends(generate_session)
try: try:
db.recipes.delete(session, recipe_slug) db.recipes.delete(session, recipe_slug)
except: except:
raise HTTPException( raise HTTPException(status_code=404, detail=SnackResponse.error("Unable to Delete Recipe"))
status_code=404, detail=SnackResponse.error("Unable to Delete Recipe")
)
return SnackResponse.error(f"Recipe {recipe_slug} Deleted") return SnackResponse.error(f"Recipe {recipe_slug} Deleted")

View file

@ -1,10 +1,10 @@
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
router = APIRouter(tags=["Recipes"]) router = APIRouter(tags=["Recipes"])

View file

@ -1,13 +1,13 @@
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from schema.settings import SiteSettings from mealie.schema.settings import SiteSettings
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from utils.post_webhooks import post_webhooks from mealie.utils.post_webhooks import post_webhooks
from routes.deps import manager from mealie.routes.deps import manager
router = APIRouter(prefix="/api/site-settings", tags=["Settings"]) router = APIRouter(prefix="/api/site-settings", tags=["Settings"])

View file

@ -1,9 +1,9 @@
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from schema.theme import SiteTheme from mealie.schema.theme import SiteTheme
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from db.database import db from mealie.db.database import db
router = APIRouter(prefix="/api", tags=["Themes"]) router = APIRouter(prefix="/api", tags=["Themes"])
@ -30,9 +30,7 @@ def get_single_theme(theme_name: str, session: Session = Depends(generate_sessio
@router.put("/themes/{theme_name}") @router.put("/themes/{theme_name}")
def update_theme( def update_theme(theme_name: str, data: SiteTheme, session: Session = Depends(generate_session)):
theme_name: str, data: SiteTheme, session: Session = Depends(generate_session)
):
""" Update a theme database entry """ """ Update a theme database entry """
db.themes.update(session, theme_name, data.dict()) db.themes.update(session, theme_name, data.dict())

View file

@ -1,13 +1,13 @@
from datetime import timedelta from datetime import timedelta
from core.security import verify_password from mealie.core.security import verify_password
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from fastapi_login.exceptions import InvalidCredentialsException from fastapi_login.exceptions import InvalidCredentialsException
from routes.deps import manager, query_user from mealie.routes.deps import manager, query_user
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.user import UserInDB from mealie.schema.user import UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/auth", tags=["Authentication"]) router = APIRouter(prefix="/api/auth", tags=["Authentication"])
@ -27,9 +27,7 @@ def get_token(
elif not verify_password(password, user.password): elif not verify_password(password, user.password):
raise InvalidCredentialsException raise InvalidCredentialsException
access_token = manager.create_access_token( access_token = manager.create_access_token(data=dict(sub=email), expires=timedelta(hours=2))
data=dict(sub=email), expires=timedelta(hours=2)
)
return SnackResponse.success( return SnackResponse.success(
"User Successfully Logged In", "User Successfully Logged In",
{"access_token": access_token, "token_type": "bearer"}, {"access_token": access_token, "token_type": "bearer"},
@ -51,9 +49,7 @@ def get_long_token(
elif not verify_password(password, user.password): elif not verify_password(password, user.password):
raise InvalidCredentialsException raise InvalidCredentialsException
access_token = manager.create_access_token( access_token = manager.create_access_token(data=dict(sub=email), expires=timedelta(days=1))
data=dict(sub=email), expires=timedelta(days=1)
)
return SnackResponse.success( return SnackResponse.success(
"User Successfully Logged In", "User Successfully Logged In",
{"access_token": access_token, "token_type": "bearer"}, {"access_token": access_token, "token_type": "bearer"},
@ -63,7 +59,5 @@ def get_long_token(
@router.get("/refresh") @router.get("/refresh")
async def refresh_token(current_user: UserInDB = Depends(manager)): async def refresh_token(current_user: UserInDB = Depends(manager)):
""" Use a valid token to get another token""" """ Use a valid token to get another token"""
access_token = manager.create_access_token( access_token = manager.create_access_token(data=dict(sub=current_user.email), expires=timedelta(hours=1))
data=dict(sub=current_user.email), expires=timedelta(hours=1)
)
return {"access_token": access_token, "token_type": "bearer"} return {"access_token": access_token, "token_type": "bearer"}

View file

@ -2,15 +2,15 @@ import shutil
from datetime import timedelta from datetime import timedelta
from os import access from os import access
from core.config import USER_DIR from mealie.core.config import USER_DIR
from core.security import get_password_hash, verify_password from mealie.core.security import get_password_hash, verify_password
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, UploadFile from fastapi import APIRouter, Depends, File, UploadFile
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from routes.deps import manager from mealie.routes.deps import manager
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut from mealie.schema.user import ChangePassword, UserBase, UserIn, UserInDB, UserOut
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/users", tags=["Users"]) router = APIRouter(prefix="/api/users", tags=["Users"])
@ -71,9 +71,7 @@ async def update_user(
updated_user: UserInDB = db.users.update(session, id, new_data.dict()) updated_user: UserInDB = db.users.update(session, id, new_data.dict())
email = updated_user.email email = updated_user.email
if current_user.id == id: if current_user.id == id:
access_token = manager.create_access_token( access_token = manager.create_access_token(data=dict(sub=email), expires=timedelta(hours=2))
data=dict(sub=email), expires=timedelta(hours=2)
)
access_token = {"access_token": access_token, "token_type": "bearer"} access_token = {"access_token": access_token, "token_type": "bearer"}
return SnackResponse.success("User Updated", access_token) return SnackResponse.success("User Updated", access_token)
@ -126,9 +124,7 @@ async def update_password(
): ):
""" Resets the User Password""" """ Resets the User Password"""
match_passwords = verify_password( match_passwords = verify_password(password_change.current_password, current_user.password)
password_change.current_password, current_user.password
)
match_id = current_user.id == id match_id = current_user.id == id
if match_passwords and match_id: if match_passwords and match_id:

View file

@ -1,13 +1,13 @@
import uuid import uuid
from core.security import get_password_hash from mealie.core.security import get_password_hash
from db.database import db from mealie.db.database import db
from db.db_setup import generate_session from mealie.db.db_setup import generate_session
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from routes.deps import manager from mealie.routes.deps import manager
from schema.sign_up import SignUpIn, SignUpOut, SignUpToken from mealie.schema.sign_up import SignUpIn, SignUpOut, SignUpToken
from schema.snackbar import SnackResponse from mealie.schema.snackbar import SnackResponse
from schema.user import UserIn, UserInDB from mealie.schema.user import UserIn, UserInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"]) router = APIRouter(prefix="/api/users/sign-ups", tags=["User Signup"])

View file

@ -1,5 +1,5 @@
from fastapi import APIRouter from fastapi import APIRouter
from routes.users import auth, crud, sign_up from mealie.routes.users import auth, crud, sign_up
router = APIRouter() router = APIRouter()

View file

View file

@ -8,6 +8,8 @@ class BackupOptions(BaseModel):
recipes: bool = True recipes: bool = True
settings: bool = True settings: bool = True
themes: bool = True themes: bool = True
groups: bool = True
users: bool = True
class Config: class Config:
schema_extra = { schema_extra = {
@ -15,6 +17,8 @@ class BackupOptions(BaseModel):
"recipes": True, "recipes": True,
"settings": True, "settings": True,
"themes": True, "themes": True,
"groups": True,
"users": True,
} }
} }

View file

@ -2,7 +2,7 @@ from typing import List, Optional
from fastapi_camelcase import CamelModel from fastapi_camelcase import CamelModel
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
class CategoryBase(CamelModel): class CategoryBase(CamelModel):

View file

@ -1,7 +1,7 @@
from datetime import date from datetime import date
from typing import List, Optional from typing import List, Optional
from db.models.mealplan import MealPlanModel from mealie.db.models.mealplan import MealPlanModel
from pydantic import BaseModel, validator from pydantic import BaseModel, validator
from pydantic.utils import GetterDict from pydantic.utils import GetterDict

View file

@ -1,7 +1,7 @@
import datetime import datetime
from typing import Any, List, Optional from typing import Any, List, Optional
from db.models.recipe.recipe import RecipeModel from mealie.db.models.recipe.recipe import RecipeModel
from pydantic import BaseModel, validator from pydantic import BaseModel, validator
from pydantic.utils import GetterDict from pydantic.utils import GetterDict
from slugify import slugify from slugify import slugify

View file

@ -9,11 +9,13 @@ class RecipeImport(BaseModel):
status: bool status: bool
exception: Optional[str] exception: Optional[str]
class ThemeImport(BaseModel): class ThemeImport(BaseModel):
name: str name: str
status: bool status: bool
exception: Optional[str] exception: Optional[str]
class SettingsImport(BaseModel): class SettingsImport(BaseModel):
name: str name: str
status: bool status: bool

View file

@ -2,7 +2,7 @@ from typing import Optional
from fastapi_camelcase import CamelModel from fastapi_camelcase import CamelModel
from schema.category import CategoryBase from mealie.schema.category import CategoryBase
class SiteSettings(CamelModel): class SiteSettings(CamelModel):

View file

@ -1,13 +1,13 @@
from typing import Any, Optional from typing import Any, Optional
from core.config import DEFAULT_GROUP from mealie.core.config import DEFAULT_GROUP
from db.models.group import Group from mealie.db.models.group import Group
from db.models.users import User from mealie.db.models.users import User
from fastapi_camelcase import CamelModel from fastapi_camelcase import CamelModel
from pydantic.utils import GetterDict from pydantic.utils import GetterDict
from schema.category import CategoryBase from mealie.schema.category import CategoryBase
from schema.meal import MealPlanInDB from mealie.schema.meal import MealPlanInDB
class ChangePassword(CamelModel): class ChangePassword(CamelModel):

View file

View file

View file

@ -4,13 +4,12 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Union from typing import Union
from core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR, TEMPLATE_DIR from mealie.core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR, TEMPLATE_DIR
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
from fastapi.logger import logger from fastapi.logger import logger
from jinja2 import Template from jinja2 import Template
from pydantic.main import BaseModel from pydantic.main import BaseModel
from schema.recipe import Recipe
class ExportDatabase: class ExportDatabase:
@ -75,14 +74,10 @@ class ExportDatabase:
out_dir.mkdir(parents=True, exist_ok=True) out_dir.mkdir(parents=True, exist_ok=True)
if export_list: if export_list:
ExportDatabase._write_json_file( ExportDatabase._write_json_file(items, out_dir.joinpath(f"{folder_name}.json"))
items, out_dir.joinpath(f"{folder_name}.json")
)
else: else:
for item in items: for item in items:
ExportDatabase._write_json_file( ExportDatabase._write_json_file(item, out_dir.joinpath(f"{item.get('name')}.json"))
item, out_dir.joinpath(f"{item.get('name')}.json")
)
@staticmethod @staticmethod
def _write_json_file(data: Union[dict, list], out_file: Path): def _write_json_file(data: Union[dict, list], out_file: Path):

View file

@ -4,13 +4,13 @@ import zipfile
from pathlib import Path from pathlib import Path
from typing import List from typing import List
from core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR from mealie.core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
from fastapi.logger import logger from fastapi.logger import logger
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
from schema.restore import RecipeImport, SettingsImport, ThemeImport from mealie.schema.restore import RecipeImport, SettingsImport, ThemeImport
from schema.theme import SiteTheme from mealie.schema.theme import SiteTheme
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -93,9 +93,7 @@ class ImportDatabase:
recipe_obj = Recipe(**recipe_dict) recipe_obj = Recipe(**recipe_dict)
db.recipes.create(session, recipe_obj.dict()) db.recipes.create(session, recipe_obj.dict())
import_status = RecipeImport( import_status = RecipeImport(name=recipe_obj.name, slug=recipe_obj.slug, status=True)
name=recipe_obj.name, slug=recipe_obj.slug, status=True
)
imports.append(import_status) imports.append(import_status)
successful_imports.append(recipe.stem) successful_imports.append(recipe.stem)
logger.info(f"Imported: {recipe.stem}") logger.info(f"Imported: {recipe.stem}")
@ -125,17 +123,13 @@ class ImportDatabase:
# Migration from list to Object Type Data # Migration from list to Object Type Data
try: try:
if "" in recipe_dict["tags"]: if "" in recipe_dict["tags"]:
recipe_dict["tags"] = [ recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if not tag == ""]
tag for tag in recipe_dict["tags"] if not tag == ""
]
except: except:
pass pass
try: try:
if "" in recipe_dict["categories"]: if "" in recipe_dict["categories"]:
recipe_dict["categories"] = [ recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if not cat == ""]
cat for cat in recipe_dict["categories"] if not cat == ""
]
except: except:
pass pass
@ -165,9 +159,7 @@ class ImportDatabase:
theme_imports.append(ThemeImport(name=new_theme.name, status=True)) theme_imports.append(ThemeImport(name=new_theme.name, status=True))
except Exception as inst: except Exception as inst:
logger.info(f"Unable Import Theme {new_theme.name}") logger.info(f"Unable Import Theme {new_theme.name}")
theme_imports.append( theme_imports.append(ThemeImport(name=new_theme.name, status=False, exception=str(inst)))
ThemeImport(name=new_theme.name, status=False, exception=str(inst))
)
return theme_imports return theme_imports
@ -185,9 +177,7 @@ class ImportDatabase:
import_status = SettingsImport(name=name, status=True) import_status = SettingsImport(name=name, status=True)
except Exception as inst: except Exception as inst:
import_status = SettingsImport( import_status = SettingsImport(name=name, status=False, exception=str(inst))
name=name, status=False, exception=str(inst)
)
settings_imports.append(import_status) settings_imports.append(import_status)

View file

@ -2,7 +2,7 @@ import shutil
from pathlib import Path from pathlib import Path
import requests import requests
from core.config import IMG_DIR from mealie.core.config import IMG_DIR
from fastapi.logger import logger from fastapi.logger import logger

View file

@ -2,12 +2,18 @@ from datetime import date, timedelta, timezone
from typing import Union from typing import Union
import pytz import pytz
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
from pydantic.tools import T from pydantic.tools import T
from schema.meal import MealIn, MealOut, MealPlanIn, MealPlanInDB, MealPlanProcessed from mealie.schema.meal import (
from schema.recipe import Recipe MealIn,
from schema.user import GroupInDB MealOut,
MealPlanIn,
MealPlanInDB,
MealPlanProcessed,
)
from mealie.schema.recipe import Recipe
from mealie.schema.user import GroupInDB
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session

View file

View file

@ -2,11 +2,11 @@ import shutil
from pathlib import Path from pathlib import Path
import yaml import yaml
from core.config import IMG_DIR, TEMP_DIR from mealie.core.config import IMG_DIR, TEMP_DIR
from db.database import db from mealie.db.database import db
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from utils.unzip import unpack_zip from mealie.utils.unzip import unpack_zip
try: try:
from yaml import CLoader as Loader from yaml import CLoader as Loader

View file

@ -4,11 +4,11 @@ import shutil
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from core.config import IMG_DIR, MIGRATION_DIR, TEMP_DIR from mealie.core.config import IMG_DIR, MIGRATION_DIR, TEMP_DIR
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
from services.scraper.cleaner import Cleaner from mealie.services.scraper.cleaner import Cleaner
from core.config import IMG_DIR, TEMP_DIR from mealie.core.config import IMG_DIR, TEMP_DIR
from db.database import db from mealie.db.database import db
def process_selection(selection: Path) -> Path: def process_selection(selection: Path) -> Path:

View file

View file

@ -1,12 +1,12 @@
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
from fastapi.logger import logger from fastapi.logger import logger
from schema.user import GroupInDB from mealie.schema.user import GroupInDB
from services.backups.exports import auto_backup_job from mealie.services.backups.exports import auto_backup_job
from services.scheduler.global_scheduler import scheduler from mealie.services.scheduler.global_scheduler import scheduler
from services.scheduler.scheduler_utils import Cron, cron_parser from mealie.services.scheduler.scheduler_utils import Cron, cron_parser
from utils.post_webhooks import post_webhooks from mealie.utils.post_webhooks import post_webhooks
# TODO Fix Scheduler # TODO Fix Scheduler
@ -83,9 +83,7 @@ def init_webhook_schedule(scheduler, job_store: dict):
logger.info("----INIT SCHEDULE OBJECT-----") logger.info("----INIT SCHEDULE OBJECT-----")
JOB_STORE = { JOB_STORE = {
"backup_job": ScheduledFunction( "backup_job": ScheduledFunction(scheduler, auto_backup_job, Cron(hours=00, minutes=00), "backups"),
scheduler, auto_backup_job, Cron(hours=00, minutes=00), "backups"
),
} }
JOB_STORE = init_webhook_schedule(scheduler=scheduler, job_store=JOB_STORE) JOB_STORE = init_webhook_schedule(scheduler=scheduler, job_store=JOB_STORE)

View file

View file

@ -31,24 +31,15 @@ class Cleaner:
recipe_data["prepTime"] = Cleaner.time(recipe_data.get("prepTime", None)) recipe_data["prepTime"] = Cleaner.time(recipe_data.get("prepTime", None))
recipe_data["performTime"] = Cleaner.time(recipe_data.get("performTime", None)) recipe_data["performTime"] = Cleaner.time(recipe_data.get("performTime", None))
recipe_data["totalTime"] = Cleaner.time(recipe_data.get("totalTime", None)) recipe_data["totalTime"] = Cleaner.time(recipe_data.get("totalTime", None))
recipe_data["recipeCategory"] = Cleaner.category( recipe_data["recipeCategory"] = Cleaner.category(recipe_data.get("recipeCategory", []))
recipe_data.get("recipeCategory", [])
)
recipe_data["recipeYield"] = Cleaner.yield_amount( recipe_data["recipeYield"] = Cleaner.yield_amount(recipe_data.get("recipeYield"))
recipe_data.get("recipeYield") recipe_data["recipeIngredient"] = Cleaner.ingredient(recipe_data.get("recipeIngredient"))
) recipe_data["recipeInstructions"] = Cleaner.instructions(recipe_data["recipeInstructions"])
recipe_data["recipeIngredient"] = Cleaner.ingredient(
recipe_data.get("recipeIngredient")
)
recipe_data["recipeInstructions"] = Cleaner.instructions(
recipe_data["recipeInstructions"]
)
recipe_data["image"] = Cleaner.image(recipe_data.get("image")) recipe_data["image"] = Cleaner.image(recipe_data.get("image"))
recipe_data["slug"] = slugify(recipe_data.get("name")) recipe_data["slug"] = slugify(recipe_data.get("name"))
recipe_data["orgURL"] = url recipe_data["orgURL"] = url
return recipe_data return recipe_data
@staticmethod @staticmethod
@ -84,11 +75,7 @@ class Cleaner:
# One long string split by (possibly multiple) new lines # One long string split by (possibly multiple) new lines
if isinstance(instructions, str): if isinstance(instructions, str):
return [ return [{"text": Cleaner._instruction(line)} for line in instructions.splitlines() if line]
{"text": Cleaner._instruction(line)}
for line in instructions.splitlines()
if line
]
# Plain strings in a list # Plain strings in a list
elif type(instructions) == list and type(instructions[0]) == str: elif type(instructions) == list and type(instructions[0]) == str:
@ -97,13 +84,8 @@ class Cleaner:
# Dictionaries (let's assume it's a HowToStep) in a list # Dictionaries (let's assume it's a HowToStep) in a list
elif type(instructions) == list and type(instructions[0]) == dict: elif type(instructions) == list and type(instructions[0]) == dict:
# Try List of Dictionary without "@type" or "type" # Try List of Dictionary without "@type" or "type"
if not instructions[0].get("@type", False) and not instructions[0].get( if not instructions[0].get("@type", False) and not instructions[0].get("type", False):
"type", False return [{"text": Cleaner._instruction(step["text"])} for step in instructions]
):
return [
{"text": Cleaner._instruction(step["text"])}
for step in instructions
]
try: try:
# If HowToStep is under HowToSection # If HowToStep is under HowToSection

View file

@ -1,7 +1,7 @@
from typing import Tuple from typing import Tuple
import extruct import extruct
from core.config import DEBUG_DIR from mealie.core.config import DEBUG_DIR
from slugify import slugify from slugify import slugify
from w3lib.html import get_base_url from w3lib.html import get_base_url

View file

@ -3,12 +3,12 @@ from typing import List
import requests import requests
import scrape_schema_recipe import scrape_schema_recipe
from core.config import DEBUG_DIR from mealie.core.config import DEBUG_DIR
from fastapi.logger import logger from fastapi.logger import logger
from services.image_services import scrape_image from mealie.services.image_services import scrape_image
from schema.recipe import Recipe from mealie.schema.recipe import Recipe
from services.scraper import open_graph from mealie.services.scraper import open_graph
from services.scraper.cleaner import Cleaner from mealie.services.scraper.cleaner import Cleaner
LAST_JSON = DEBUG_DIR.joinpath("last_recipe.json") LAST_JSON = DEBUG_DIR.joinpath("last_recipe.json")
@ -36,15 +36,11 @@ def create_from_url(url: str) -> Recipe:
def extract_recipe_from_html(html: str, url: str) -> dict: def extract_recipe_from_html(html: str, url: str) -> dict:
try: try:
scraped_recipes: List[dict] = scrape_schema_recipe.loads( scraped_recipes: List[dict] = scrape_schema_recipe.loads(html, python_objects=True)
html, python_objects=True
)
dump_last_json(scraped_recipes) dump_last_json(scraped_recipes)
if not scraped_recipes: if not scraped_recipes:
scraped_recipes: List[dict] = scrape_schema_recipe.scrape_url( scraped_recipes: List[dict] = scrape_schema_recipe.scrape_url(url, python_objects=True)
url, python_objects=True
)
except Exception as e: except Exception as e:
# trying without python_objects # trying without python_objects
scraped_recipes: List[dict] = scrape_schema_recipe.loads(html) scraped_recipes: List[dict] = scrape_schema_recipe.loads(html)

0
mealie/utils/__init__.py Normal file
View file

View file

@ -1,6 +1,6 @@
import json import json
from core.config import DATA_DIR from mealie.core.config import DATA_DIR
"""Script to export the ReDoc documentation page into a standalone HTML file.""" """Script to export the ReDoc documentation page into a standalone HTML file."""

View file

@ -1,8 +1,8 @@
import requests import requests
from db.database import db from mealie.db.database import db
from db.db_setup import create_session from mealie.db.db_setup import create_session
from schema.user import GroupInDB from mealie.schema.user import GroupInDB
from services.meal_services import get_todays_meal from mealie.services.meal_services import get_todays_meal
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session

View file

@ -2,7 +2,7 @@ import tempfile
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from core.config import TEMP_DIR from mealie.core.config import TEMP_DIR
def unpack_zip(selection: Path) -> tempfile.TemporaryDirectory: def unpack_zip(selection: Path) -> tempfile.TemporaryDirectory:

View file

@ -1,12 +1,12 @@
[tool.poetry] [tool.poetry]
name = "mealie" name = "mealie"
version = "0.1.0" version = "0.3.0"
description = "A Recipe Manager" description = "A Recipe Manager"
authors = ["Hayden <hay-kot@pm.me>"] authors = ["Hayden <hay-kot@pm.me>"]
license = "MIT" license = "MIT"
[tool.poetry.scripts] [tool.poetry.scripts]
start = "app:app" start = "mealie.app:main"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "^3.9"
@ -40,3 +40,15 @@ mkdocs-material = "^7.0.2"
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 120
[tool.pytest.ini_options]
minversion = "6.0"
addopts = "-ra -q --cov=quick_zip"
python_files = 'test_*'
python_classes = '*Tests'
python_functions = 'test_*'
testpaths = [
"tests",
]

0
tests/__init__.py Normal file
View file

View file

@ -1,10 +1,10 @@
import json import json
import requests import requests
from app import app from mealie.app import app
from core.config import SQLITE_DIR from mealie.core.config import SQLITE_DIR
from db.db_setup import generate_session, sql_global_init from mealie.db.db_setup import generate_session, sql_global_init
from db.init_db import init_db from mealie.db.init_db import init_db
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pytest import fixture from pytest import fixture

View file

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more