recipe models almost done

This commit is contained in:
Hayden 2021-01-13 19:33:17 -09:00
commit 765422af9a
14 changed files with 1313 additions and 82 deletions

View file

@ -1,6 +1,6 @@
{ {
"python.formatting.provider": "black", "python.formatting.provider": "black",
"python.pythonPath": "venv/bin/python3.8", "python.pythonPath": ".venv/bin/python",
"python.linting.pylintEnabled": true, "python.linting.pylintEnabled": true,
"python.linting.enabled": true, "python.linting.enabled": true,
"python.autoComplete.extraPaths": ["mealie", "mealie/mealie"], "python.autoComplete.extraPaths": ["mealie", "mealie/mealie"],

Binary file not shown.

View file

@ -1,15 +1,13 @@
import json import json
import mongoengine import mongoengine
from settings import USE_MONGO, USE_TINYDB from settings import USE_MONGO, USE_SQL
from db.tinydb.baseclass import StoreBase
class BaseDocument: class BaseDocument:
def __init__(self) -> None: def __init__(self) -> None:
self.primary_key: str self.primary_key: str
self.store: StoreBase self.store: str
self.document: mongoengine.Document self.document: mongoengine.Document
@staticmethod # TODO: Probably Put a version in each class to speed up reads? @staticmethod # TODO: Probably Put a version in each class to speed up reads?
@ -58,8 +56,8 @@ class BaseDocument:
return docs[0] return docs[0]
return docs return docs
elif USE_TINYDB: elif USE_SQL:
return self.store.get_all() return self.get_all_sql()
def get( def get(
self, match_value: str, match_key: str = None, limit=1 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}) document = self.document.objects.get(**{str(match_key): match_value})
db_entry = BaseDocument._unpack_mongo(document) db_entry = BaseDocument._unpack_mongo(document)
elif USE_TINYDB: elif USE_SQL:
db_entry = self.store.get(match_value, match_key, limit=limit) return self.get_by_slug(match_value, match_key)
else: else:
raise Exception("No database type established") raise Exception("No database type established")
@ -99,8 +97,8 @@ class BaseDocument:
new_document = self.document(**document) new_document = self.document(**document)
new_document.save() new_document.save()
return BaseDocument._unpack_mongo(new_document) return BaseDocument._unpack_mongo(new_document)
elif USE_TINYDB: elif USE_SQL:
return self.store.save(document) return self.save_new_sql(document)
def delete(self, primary_key) -> dict: def delete(self, primary_key) -> dict:
if USE_MONGO: if USE_MONGO:
@ -108,5 +106,5 @@ class BaseDocument:
if document: if document:
document.delete() document.delete()
elif USE_TINYDB: elif USE_SQL:
self.store.delete(primary_key) pass

View file

@ -1,17 +1,17 @@
from typing import List 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_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 from db.mongo.meal_models import MealDocument, MealPlanDocument
class _Meals(BaseDocument): class _Meals(BaseDocument):
def __init__(self) -> None: def __init__(self) -> None:
self.primary_key = "uid" self.primary_key = "uid"
if USE_TINYDB: if USE_SQL:
self.store = tiny_db.meals self.sql_model = None
self.document = MealPlanDocument self.document = MealPlanDocument
@staticmethod @staticmethod
@ -45,7 +45,7 @@ class _Meals(BaseDocument):
document = self.document(**plan_data) document = self.document(**plan_data)
document.save() document.save()
elif USE_TINYDB: elif USE_SQL:
pass pass
def update(self, uid: str, plan_data: dict) -> dict: def update(self, uid: str, plan_data: dict) -> dict:
@ -55,5 +55,5 @@ class _Meals(BaseDocument):
new_meals = _Meals._process_meals(plan_data["meals"]) new_meals = _Meals._process_meals(plan_data["meals"])
document.update(set__meals=new_meals) document.update(set__meals=new_meals)
document.save() document.save()
elif USE_TINYDB: elif USE_SQL:
pass pass

View file

@ -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_base import BaseDocument
from db.db_setup import tiny_db from db.db_setup import tiny_db
from db.mongo.recipe_models import RecipeDocument 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): class _Recipes(BaseDocument):
def __init__(self) -> None: def __init__(self) -> None:
self.primary_key = "slug" self.primary_key = "slug"
if USE_TINYDB: if USE_SQL:
self.store = tiny_db.recipes self.sql_model = RecipeModel
self.document = RecipeDocument 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: def update(self, slug: str, new_data: dict) -> None:
if USE_MONGO: if USE_MONGO:
document = self.document.objects.get(slug=slug) document = self.document.objects.get(slug=slug)
@ -37,9 +117,8 @@ class _Recipes(BaseDocument):
document.save() document.save()
return new_data.get("slug") return new_data.get("slug")
elif USE_TINYDB: elif USE_SQL:
self.store.update_doc(slug, new_data) pass
return new_data.get("slug")
def update_image(self, slug: str, extension: str) -> None: def update_image(self, slug: str, extension: str) -> None:
if USE_MONGO: if USE_MONGO:
@ -47,5 +126,5 @@ class _Recipes(BaseDocument):
if document: if document:
document.update(set__image=f"{slug}.{extension}") document.update(set__image=f"{slug}.{extension}")
elif USE_TINYDB: elif USE_SQL:
self.store.update_doc(slug, {"image": f"{slug}.{extension}"}) pass

View file

@ -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_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 from db.mongo.settings_models import SiteSettingsDocument, WebhooksDocument
@ -10,8 +10,8 @@ class _Settings(BaseDocument):
self.primary_key = "name" self.primary_key = "name"
if USE_TINYDB: if USE_SQL:
self.store = tiny_db.settings self.sql_model = None
self.document = SiteSettingsDocument self.document = SiteSettingsDocument
@ -22,9 +22,8 @@ class _Settings(BaseDocument):
new_doc = self.document(**main) new_doc = self.document(**main)
return new_doc.save() return new_doc.save()
elif USE_TINYDB: elif USE_SQL:
main["webhooks"] = webhooks pass
return self.store.save(main)
def update(self, name: str, new_data: dict) -> dict: def update(self, name: str, new_data: dict) -> dict:
if USE_MONGO: if USE_MONGO:
@ -32,5 +31,5 @@ class _Settings(BaseDocument):
if document: if document:
document.update(set__webhooks=WebhooksDocument(**new_data["webhooks"])) document.update(set__webhooks=WebhooksDocument(**new_data["webhooks"]))
document.save() document.save()
elif USE_TINYDB: elif USE_SQL:
pass pass

View file

@ -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 from db.tinydb.tinydb_setup import TinyDatabase
tiny_db: TinyDatabase = None tiny_db: TinyDatabase = None
if USE_TINYDB: if USE_SQL:
db_file = DATA_DIR.joinpath("mealie.sqlite")
tiny_db = TinyDatabase() sql_global_init(db_file)
elif USE_MONGO: elif USE_MONGO:
from db.mongo.mongo_setup import global_init as mongo_global_init from db.mongo.mongo_setup import global_init as mongo_global_init

View file

@ -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_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 from db.mongo.settings_models import SiteThemeDocument, ThemeColorsDocument
class _Themes(BaseDocument): class _Themes(BaseDocument):
def __init__(self) -> None: def __init__(self) -> None:
self.primary_key = "name" self.primary_key = "name"
if USE_TINYDB: if USE_SQL:
self.store = tiny_db.themes self.sql_model = None
else: else:
self.document = SiteThemeDocument self.document = SiteThemeDocument
@ -20,7 +20,7 @@ class _Themes(BaseDocument):
document = self.document(**theme_data) document = self.document(**theme_data)
document.save() document.save()
elif USE_TINYDB: elif USE_SQL:
pass pass
def update(self, data: dict) -> dict: def update(self, data: dict) -> dict:
@ -34,5 +34,5 @@ class _Themes(BaseDocument):
else: else:
raise Exception("No database entry was found to update") raise Exception("No database entry was found to update")
elif USE_TINYDB: elif USE_SQL:
pass pass

View file

@ -3,22 +3,28 @@ from pathlib import Path
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.sql.model_base import SqlAlchemyBase from db.sql.model_base import SqlAlchemyBase
from sqlalchemy.orm.session import Session
factory = None __factory = None
def globa_init(db_file: Path): def globa_init(db_file: Path):
global factory global __factory
if factory: if __factory:
return return
conn_str = "sqlite:///" + db_file.absolute() conn_str = "sqlite:///" + str(db_file.absolute())
engine = sa.create_engine(conn_str, echo=False) engine = sa.create_engine(conn_str, echo=False)
factory = orm.sessionmaker(bind=engine) __factory = orm.sessionmaker(bind=engine)
import db.sql._all_models import db.sql._all_models
SqlAlchemyBase.metadata.create_all(engine) SqlAlchemyBase.metadata.create_all(engine)
def create_session() -> Session:
global __factory
return __factory()

View file

@ -1,52 +1,122 @@
from datetime import date from datetime import date
from typing import List
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from db.sql.model_base import SqlAlchemyBase 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): class RecipeModel(SqlAlchemyBase):
__tablename__ = "recipes" __tablename__ = "recipes"
# id = mongoengine.UUIDField(primary_key=True)
name = sa.Column(sa.String) name = sa.Column(sa.String)
description = sa.Column(sa.String) description = sa.Column(sa.String)
image = sa.Column(sa.String) image = sa.Column(sa.String)
recipeYield = sa.Column(sa.String) recipeYield = sa.Column(sa.String)
recipeIngredient = orm.relation("RecipeIngredient") recipeIngredient: List[RecipeIngredient] = orm.relation("RecipeIngredient")
recipeInstructions = orm.relation("RecipeInstruction") recipeInstructions: List[RecipeInstruction] = orm.relation("RecipeInstruction")
totalTime = sa.Column(sa.String) totalTime = sa.Column(sa.String)
# Mealie Specific # Mealie Specific
slug = sa.Column(sa.String, primary_key=True, index=True, unique=True) slug = sa.Column(sa.String, primary_key=True, index=True, unique=True)
categories = orm.relation("Category") categories: List[Category] = orm.relation("Category")
tags = orm.relation("Tag") tags: List[Tag] = orm.relation("Tag")
dateAdded = sa.Column(sa.Date, default=date.today()) dateAdded = sa.Column(sa.Date, default=date.today())
notes = orm.relation("Note") notes: List[Note] = orm.relation("Note")
rating = sa.Column(sa.Integer) rating = sa.Column(sa.Integer)
orgURL = sa.Column(sa.String) orgURL = sa.Column(sa.String)
extras = orm.relation("ApiExtras") extras: List[ApiExtras] = orm.relation("ApiExtras")
class ApiExtras(SqlAlchemyBase): @staticmethod
key: sa.Column(sa.String) def _flatten_dict(list_of_dict: List[dict]):
value: sa.Column(sa.String) finalMap = {}
for d in list_of_dict:
finalMap.update(d)
class Category(SqlAlchemyBase): return finalMap
name = sa.Column(sa.String, index=True)
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): return data
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)

View file

@ -47,14 +47,14 @@ else:
# DATABASE ENV # DATABASE ENV
DATABASE_TYPE = os.getenv("db_type", "mongo") # mongo, tinydb DATABASE_TYPE = os.getenv("db_type", "sql") # mongo, tinydb
if DATABASE_TYPE == "tinydb": if DATABASE_TYPE == "sql":
USE_TINYDB = True USE_SQL = True
USE_MONGO = False USE_MONGO = False
elif DATABASE_TYPE == "mongo": elif DATABASE_TYPE == "mongo":
USE_MONGO = True USE_MONGO = True
USE_TINYDB = False USE_SQL = False
else: else:
raise Exception( raise Exception(

1041
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

37
pyproject.toml Normal file
View 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
View file