mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
recipe models almost done
This commit is contained in:
parent
a1e6252508
commit
765422af9a
14 changed files with 1313 additions and 82 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"python.formatting.provider": "black",
|
||||
"python.pythonPath": "venv/bin/python3.8",
|
||||
"python.pythonPath": ".venv/bin/python",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.autoComplete.extraPaths": ["mealie", "mealie/mealie"],
|
||||
|
|
Binary file not shown.
|
@ -1,15 +1,13 @@
|
|||
import json
|
||||
|
||||
import mongoengine
|
||||
from settings import USE_MONGO, USE_TINYDB
|
||||
|
||||
from db.tinydb.baseclass import StoreBase
|
||||
from settings import USE_MONGO, USE_SQL
|
||||
|
||||
|
||||
class BaseDocument:
|
||||
def __init__(self) -> None:
|
||||
self.primary_key: str
|
||||
self.store: StoreBase
|
||||
self.store: str
|
||||
self.document: mongoengine.Document
|
||||
|
||||
@staticmethod # TODO: Probably Put a version in each class to speed up reads?
|
||||
|
@ -58,8 +56,8 @@ class BaseDocument:
|
|||
return docs[0]
|
||||
return docs
|
||||
|
||||
elif USE_TINYDB:
|
||||
return self.store.get_all()
|
||||
elif USE_SQL:
|
||||
return self.get_all_sql()
|
||||
|
||||
def get(
|
||||
self, match_value: str, match_key: str = None, limit=1
|
||||
|
@ -83,8 +81,8 @@ class BaseDocument:
|
|||
document = self.document.objects.get(**{str(match_key): match_value})
|
||||
db_entry = BaseDocument._unpack_mongo(document)
|
||||
|
||||
elif USE_TINYDB:
|
||||
db_entry = self.store.get(match_value, match_key, limit=limit)
|
||||
elif USE_SQL:
|
||||
return self.get_by_slug(match_value, match_key)
|
||||
|
||||
else:
|
||||
raise Exception("No database type established")
|
||||
|
@ -99,8 +97,8 @@ class BaseDocument:
|
|||
new_document = self.document(**document)
|
||||
new_document.save()
|
||||
return BaseDocument._unpack_mongo(new_document)
|
||||
elif USE_TINYDB:
|
||||
return self.store.save(document)
|
||||
elif USE_SQL:
|
||||
return self.save_new_sql(document)
|
||||
|
||||
def delete(self, primary_key) -> dict:
|
||||
if USE_MONGO:
|
||||
|
@ -108,5 +106,5 @@ class BaseDocument:
|
|||
|
||||
if document:
|
||||
document.delete()
|
||||
elif USE_TINYDB:
|
||||
self.store.delete(primary_key)
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
from typing import List
|
||||
|
||||
from settings import USE_MONGO, USE_TINYDB
|
||||
from settings import USE_MONGO, USE_SQL
|
||||
|
||||
from db.db_base import BaseDocument
|
||||
from db.db_setup import USE_MONGO, USE_TINYDB, tiny_db
|
||||
from db.db_setup import USE_MONGO, USE_SQL, tiny_db
|
||||
from db.mongo.meal_models import MealDocument, MealPlanDocument
|
||||
|
||||
|
||||
class _Meals(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "uid"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.meals
|
||||
if USE_SQL:
|
||||
self.sql_model = None
|
||||
self.document = MealPlanDocument
|
||||
|
||||
@staticmethod
|
||||
|
@ -45,7 +45,7 @@ class _Meals(BaseDocument):
|
|||
document = self.document(**plan_data)
|
||||
|
||||
document.save()
|
||||
elif USE_TINYDB:
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
||||
def update(self, uid: str, plan_data: dict) -> dict:
|
||||
|
@ -55,5 +55,5 @@ class _Meals(BaseDocument):
|
|||
new_meals = _Meals._process_meals(plan_data["meals"])
|
||||
document.update(set__meals=new_meals)
|
||||
document.save()
|
||||
elif USE_TINYDB:
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
|
|
@ -1,17 +1,97 @@
|
|||
from settings import USE_MONGO, USE_TINYDB
|
||||
from settings import USE_MONGO, USE_SQL
|
||||
|
||||
from db.db_base import BaseDocument
|
||||
from db.db_setup import tiny_db
|
||||
from db.mongo.recipe_models import RecipeDocument
|
||||
from db.sql.db_session import create_session
|
||||
from db.sql.recipe_models import (ApiExtras, Note, RecipeIngredient,
|
||||
RecipeInstruction, RecipeModel, Tag)
|
||||
|
||||
|
||||
class _Recipes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "slug"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.recipes
|
||||
if USE_SQL:
|
||||
self.sql_model = RecipeModel
|
||||
self.document = RecipeDocument
|
||||
|
||||
def get_by_slug(self, match_value: str, match_key: str):
|
||||
session = create_session()
|
||||
result = session.query(self.sql_model).filter_by(**{match_key: match_value}).one()
|
||||
|
||||
|
||||
return result.dict()
|
||||
|
||||
def get_all_sql(self):
|
||||
session = create_session()
|
||||
list = [x.dict() for x in session.query(self.sql_model).all()]
|
||||
session.close()
|
||||
return list
|
||||
|
||||
def save_new_sql(self, recipe_data: dict):
|
||||
session = create_session()
|
||||
new_recipe = self.sql_model()
|
||||
new_recipe.name = recipe_data.get("name")
|
||||
new_recipe.description = recipe_data.get("description")
|
||||
new_recipe.image = recipe_data.get("image")
|
||||
new_recipe.totalTime = recipe_data.get("totalTime")
|
||||
new_recipe.slug = recipe_data.get("slug")
|
||||
new_recipe.rating = recipe_data.get("rating")
|
||||
new_recipe.orgURL = recipe_data.get("orgURL")
|
||||
new_recipe.dateAdded = recipe_data.get("dateAdded")
|
||||
new_recipe.recipeYield = recipe_data.get("recipeYield")
|
||||
|
||||
for ingredient in recipe_data.get("recipeIngredient"):
|
||||
new_ingredient = RecipeIngredient()
|
||||
new_ingredient.ingredient = ingredient
|
||||
new_recipe.recipeIngredient.append(new_ingredient)
|
||||
|
||||
for step in recipe_data.get("recipeInstructions"):
|
||||
new_step = RecipeInstruction()
|
||||
new_step.type = "Step"
|
||||
new_step.text = step.get("text")
|
||||
new_recipe.recipeInstructions.append(new_step)
|
||||
|
||||
try:
|
||||
for tag in recipe_data.get("tags"):
|
||||
new_tag = Tag()
|
||||
new_tag.name = tag
|
||||
new_recipe.tags.append(new_tag)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
for category in recipe_data.get("category"):
|
||||
new_category = Tag()
|
||||
new_category.name = category
|
||||
new_recipe.categories.append(new_category)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
new_recipe.notes = recipe_data.get("name")
|
||||
for note in recipe_data.get("notes"):
|
||||
new_note = Note()
|
||||
new_note.title = note.get("title")
|
||||
new_note.text = note.get("text")
|
||||
new_recipe.notes.append(note)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
for key, value in recipe_data.get("extras").items():
|
||||
new_extra = ApiExtras()
|
||||
new_extra.key = key
|
||||
new_extra.key = value
|
||||
new_recipe.extras.append(new_extra)
|
||||
except:
|
||||
pass
|
||||
|
||||
session.add(new_recipe)
|
||||
session.commit()
|
||||
|
||||
return recipe_data
|
||||
|
||||
def update(self, slug: str, new_data: dict) -> None:
|
||||
if USE_MONGO:
|
||||
document = self.document.objects.get(slug=slug)
|
||||
|
@ -37,9 +117,8 @@ class _Recipes(BaseDocument):
|
|||
document.save()
|
||||
|
||||
return new_data.get("slug")
|
||||
elif USE_TINYDB:
|
||||
self.store.update_doc(slug, new_data)
|
||||
return new_data.get("slug")
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
||||
def update_image(self, slug: str, extension: str) -> None:
|
||||
if USE_MONGO:
|
||||
|
@ -47,5 +126,5 @@ class _Recipes(BaseDocument):
|
|||
|
||||
if document:
|
||||
document.update(set__image=f"{slug}.{extension}")
|
||||
elif USE_TINYDB:
|
||||
self.store.update_doc(slug, {"image": f"{slug}.{extension}"})
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from settings import USE_MONGO, USE_TINYDB
|
||||
from settings import USE_MONGO, USE_SQL
|
||||
|
||||
from db.db_base import BaseDocument
|
||||
from db.db_setup import USE_MONGO, USE_TINYDB, tiny_db
|
||||
from db.db_setup import USE_MONGO, USE_SQL, tiny_db
|
||||
from db.mongo.settings_models import SiteSettingsDocument, WebhooksDocument
|
||||
|
||||
|
||||
|
@ -10,8 +10,8 @@ class _Settings(BaseDocument):
|
|||
|
||||
self.primary_key = "name"
|
||||
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.settings
|
||||
if USE_SQL:
|
||||
self.sql_model = None
|
||||
|
||||
self.document = SiteSettingsDocument
|
||||
|
||||
|
@ -22,9 +22,8 @@ class _Settings(BaseDocument):
|
|||
new_doc = self.document(**main)
|
||||
return new_doc.save()
|
||||
|
||||
elif USE_TINYDB:
|
||||
main["webhooks"] = webhooks
|
||||
return self.store.save(main)
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
||||
def update(self, name: str, new_data: dict) -> dict:
|
||||
if USE_MONGO:
|
||||
|
@ -32,5 +31,5 @@ class _Settings(BaseDocument):
|
|||
if document:
|
||||
document.update(set__webhooks=WebhooksDocument(**new_data["webhooks"]))
|
||||
document.save()
|
||||
elif USE_TINYDB:
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from settings import USE_MONGO, USE_TINYDB
|
||||
from settings import DATA_DIR, USE_MONGO, USE_SQL
|
||||
|
||||
from db.sql.db_session import globa_init as sql_global_init
|
||||
from db.tinydb.tinydb_setup import TinyDatabase
|
||||
|
||||
tiny_db: TinyDatabase = None
|
||||
if USE_TINYDB:
|
||||
|
||||
tiny_db = TinyDatabase()
|
||||
if USE_SQL:
|
||||
db_file = DATA_DIR.joinpath("mealie.sqlite")
|
||||
sql_global_init(db_file)
|
||||
|
||||
elif USE_MONGO:
|
||||
from db.mongo.mongo_setup import global_init as mongo_global_init
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
from settings import USE_MONGO, USE_TINYDB
|
||||
from settings import USE_MONGO, USE_SQL
|
||||
|
||||
from db.db_base import BaseDocument
|
||||
from db.db_setup import USE_MONGO, USE_TINYDB, tiny_db
|
||||
from db.db_setup import USE_MONGO, USE_SQL, tiny_db
|
||||
from db.mongo.settings_models import SiteThemeDocument, ThemeColorsDocument
|
||||
|
||||
|
||||
class _Themes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "name"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.themes
|
||||
if USE_SQL:
|
||||
self.sql_model = None
|
||||
else:
|
||||
self.document = SiteThemeDocument
|
||||
|
||||
|
@ -20,7 +20,7 @@ class _Themes(BaseDocument):
|
|||
document = self.document(**theme_data)
|
||||
|
||||
document.save()
|
||||
elif USE_TINYDB:
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
||||
def update(self, data: dict) -> dict:
|
||||
|
@ -34,5 +34,5 @@ class _Themes(BaseDocument):
|
|||
else:
|
||||
raise Exception("No database entry was found to update")
|
||||
|
||||
elif USE_TINYDB:
|
||||
elif USE_SQL:
|
||||
pass
|
||||
|
|
|
@ -3,22 +3,28 @@ from pathlib import Path
|
|||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from db.sql.model_base import SqlAlchemyBase
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
factory = None
|
||||
__factory = None
|
||||
|
||||
|
||||
def globa_init(db_file: Path):
|
||||
global factory
|
||||
global __factory
|
||||
|
||||
if factory:
|
||||
if __factory:
|
||||
return
|
||||
|
||||
conn_str = "sqlite:///" + db_file.absolute()
|
||||
conn_str = "sqlite:///" + str(db_file.absolute())
|
||||
|
||||
engine = sa.create_engine(conn_str, echo=False)
|
||||
|
||||
factory = orm.sessionmaker(bind=engine)
|
||||
__factory = orm.sessionmaker(bind=engine)
|
||||
|
||||
import db.sql._all_models
|
||||
|
||||
SqlAlchemyBase.metadata.create_all(engine)
|
||||
|
||||
|
||||
def create_session() -> Session:
|
||||
global __factory
|
||||
return __factory()
|
||||
|
|
|
@ -1,52 +1,122 @@
|
|||
from datetime import date
|
||||
from typing import List
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from db.sql.model_base import SqlAlchemyBase
|
||||
|
||||
|
||||
class ApiExtras(SqlAlchemyBase):
|
||||
__tablename__ = "api_extras"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.slug"))
|
||||
key: sa.Column(sa.String, primary_key=True)
|
||||
value: sa.Column(sa.String)
|
||||
|
||||
def dict(self):
|
||||
return {self.key: self.value}
|
||||
|
||||
|
||||
class Category(SqlAlchemyBase):
|
||||
__tablename__ = "categories"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.slug"))
|
||||
name = sa.Column(sa.String, index=True)
|
||||
|
||||
def dict(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Tag(SqlAlchemyBase):
|
||||
__tablename__ = "tags"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.slug"))
|
||||
name = sa.Column(sa.String, index=True)
|
||||
|
||||
def dict(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Note(SqlAlchemyBase):
|
||||
__tablename__ = "notes"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.slug"))
|
||||
title = sa.Column(sa.String)
|
||||
text = sa.Column(sa.String)
|
||||
|
||||
def dict(self):
|
||||
return {"title": self.title, "text": self.text}
|
||||
|
||||
|
||||
class RecipeIngredient(SqlAlchemyBase):
|
||||
__tablename__ = "recipes_ingredients"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.slug"))
|
||||
ingredient = sa.Column(sa.String)
|
||||
|
||||
def dict(self):
|
||||
return self.ingredient
|
||||
|
||||
|
||||
class RecipeInstruction(SqlAlchemyBase):
|
||||
__tablename__ = "recipe_instructions"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.slug"))
|
||||
type = sa.Column(sa.String)
|
||||
text = sa.Column(sa.String)
|
||||
|
||||
def dict(self):
|
||||
data = {"@type": self.type, "text": self.text}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class RecipeModel(SqlAlchemyBase):
|
||||
__tablename__ = "recipes"
|
||||
# id = mongoengine.UUIDField(primary_key=True)
|
||||
name = sa.Column(sa.String)
|
||||
description = sa.Column(sa.String)
|
||||
image = sa.Column(sa.String)
|
||||
recipeYield = sa.Column(sa.String)
|
||||
recipeIngredient = orm.relation("RecipeIngredient")
|
||||
recipeInstructions = orm.relation("RecipeInstruction")
|
||||
recipeIngredient: List[RecipeIngredient] = orm.relation("RecipeIngredient")
|
||||
recipeInstructions: List[RecipeInstruction] = orm.relation("RecipeInstruction")
|
||||
totalTime = sa.Column(sa.String)
|
||||
|
||||
# Mealie Specific
|
||||
slug = sa.Column(sa.String, primary_key=True, index=True, unique=True)
|
||||
categories = orm.relation("Category")
|
||||
tags = orm.relation("Tag")
|
||||
categories: List[Category] = orm.relation("Category")
|
||||
tags: List[Tag] = orm.relation("Tag")
|
||||
dateAdded = sa.Column(sa.Date, default=date.today())
|
||||
notes = orm.relation("Note")
|
||||
notes: List[Note] = orm.relation("Note")
|
||||
rating = sa.Column(sa.Integer)
|
||||
orgURL = sa.Column(sa.String)
|
||||
extras = orm.relation("ApiExtras")
|
||||
extras: List[ApiExtras] = orm.relation("ApiExtras")
|
||||
|
||||
class ApiExtras(SqlAlchemyBase):
|
||||
key: sa.Column(sa.String)
|
||||
value: sa.Column(sa.String)
|
||||
@staticmethod
|
||||
def _flatten_dict(list_of_dict: List[dict]):
|
||||
finalMap = {}
|
||||
for d in list_of_dict:
|
||||
finalMap.update(d)
|
||||
|
||||
class Category(SqlAlchemyBase):
|
||||
name = sa.Column(sa.String, index=True)
|
||||
return finalMap
|
||||
|
||||
def dict(self):
|
||||
data = {
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"image": self.image,
|
||||
"recipeYield": self.recipeYield,
|
||||
"recipeIngredient": [x.dict() for x in self.recipeIngredient],
|
||||
"recipeInstructions": [x.dict() for x in self.recipeInstructions],
|
||||
"totalTime": self.totalTime,
|
||||
# Mealie
|
||||
"slug": self.slug,
|
||||
"categories": [x.dict() for x in self.categories],
|
||||
"tags": [x.dict() for x in self.tags],
|
||||
"dateAdded": self.dateAdded,
|
||||
"notes": [x.dict() for x in self.notes],
|
||||
"rating": self.rating,
|
||||
"orgURL": self.orgURL,
|
||||
"extras": RecipeModel._flatten_dict(self.extras),
|
||||
}
|
||||
|
||||
class Tag(SqlAlchemyBase):
|
||||
name = sa.Column(sa.String, index=True)
|
||||
|
||||
|
||||
class Note(SqlAlchemyBase):
|
||||
title = sa.Column(sa.String)
|
||||
text = sa.Column(sa.String)
|
||||
|
||||
|
||||
class RecipeIngredient(SqlAlchemyBase):
|
||||
ingredient: sa.Column(sa.String)
|
||||
|
||||
|
||||
class RecipeInstruction(SqlAlchemyBase):
|
||||
type = sa.Column(sa.String)
|
||||
text = sa.Column(sa.String)
|
||||
return data
|
||||
|
|
|
@ -47,14 +47,14 @@ else:
|
|||
|
||||
|
||||
# DATABASE ENV
|
||||
DATABASE_TYPE = os.getenv("db_type", "mongo") # mongo, tinydb
|
||||
if DATABASE_TYPE == "tinydb":
|
||||
USE_TINYDB = True
|
||||
DATABASE_TYPE = os.getenv("db_type", "sql") # mongo, tinydb
|
||||
if DATABASE_TYPE == "sql":
|
||||
USE_SQL = True
|
||||
USE_MONGO = False
|
||||
|
||||
elif DATABASE_TYPE == "mongo":
|
||||
USE_MONGO = True
|
||||
USE_TINYDB = False
|
||||
USE_SQL = False
|
||||
|
||||
else:
|
||||
raise Exception(
|
||||
|
|
1041
poetry.lock
generated
Normal file
1041
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
37
pyproject.toml
Normal file
37
pyproject.toml
Normal file
|
@ -0,0 +1,37 @@
|
|||
[tool.poetry]
|
||||
name = "mealie"
|
||||
version = "0.1.0"
|
||||
description = "A Recipe Manager"
|
||||
authors = ["Hayden <hay-kot@pm.me>"]
|
||||
license = "MIT"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
start = "app:app"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
aiofiles = "0.5.0"
|
||||
aniso8601 = "7.0.0"
|
||||
appdirs = "1.4.4"
|
||||
fastapi = "^0.63.0"
|
||||
uvicorn = "^0.13.3"
|
||||
GitPython = "^3.1.12"
|
||||
APScheduler = "^3.6.3"
|
||||
SQLAlchemy = "^1.3.22"
|
||||
Jinja2 = "^2.11.2"
|
||||
python-dotenv = "^0.15.0"
|
||||
mongoengine = "^0.22.1"
|
||||
tinydb = "^4.3.0"
|
||||
tinydb-serialization = "^2.0.0"
|
||||
python-slugify = "^4.0.1"
|
||||
requests = "^2.25.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pylint = "^2.6.0"
|
||||
black = "^20.8b1"
|
||||
pytest = "^6.2.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
0
scratch.json
Normal file
0
scratch.json
Normal file
Loading…
Add table
Add a link
Reference in a new issue