From 29d65fddc470459c1d31955d3784c664e1a19516 Mon Sep 17 00:00:00 2001 From: hay-kot Date: Sat, 6 Mar 2021 13:42:14 -0900 Subject: [PATCH] recipe databse refactor --- .../components/Recipe/RecipeEditor/index.vue | 4 +- frontend/src/pages/RecipePage.vue | 2 +- mealie/db/database.py | 2 +- mealie/db/models/_all_models.py | 2 +- mealie/db/models/recipe/api_extras.py | 19 ++ mealie/db/models/recipe/category.py | 65 +++++ mealie/db/models/recipe/ingredient.py | 16 ++ mealie/db/models/recipe/instruction.py | 16 ++ mealie/db/models/recipe/note.py | 17 ++ mealie/db/models/recipe/nutrition.py | 40 +++ mealie/db/models/{ => recipe}/recipe.py | 245 ++++-------------- mealie/db/models/recipe/tag.py | 60 +++++ mealie/db/models/recipe/tool.py | 15 ++ mealie/schema/recipe.py | 14 +- mealie/services/backups/imports.py | 7 +- mealie/services/recipe_services.py | 15 +- mealie/tests/conftest.py | 2 +- 17 files changed, 337 insertions(+), 204 deletions(-) create mode 100644 mealie/db/models/recipe/api_extras.py create mode 100644 mealie/db/models/recipe/category.py create mode 100644 mealie/db/models/recipe/ingredient.py create mode 100644 mealie/db/models/recipe/instruction.py create mode 100644 mealie/db/models/recipe/note.py create mode 100644 mealie/db/models/recipe/nutrition.py rename mealie/db/models/{ => recipe}/recipe.py (51%) create mode 100644 mealie/db/models/recipe/tag.py create mode 100644 mealie/db/models/recipe/tool.py diff --git a/frontend/src/components/Recipe/RecipeEditor/index.vue b/frontend/src/components/Recipe/RecipeEditor/index.vue index 508b9583b..9dab654e5 100644 --- a/frontend/src/components/Recipe/RecipeEditor/index.vue +++ b/frontend/src/components/Recipe/RecipeEditor/index.vue @@ -118,7 +118,7 @@ chips item-color="secondary" deletable-chips - v-model="value.categories" + v-model="value.recipeCategory" hide-selected :items="categories" text="name" @@ -359,7 +359,7 @@ export default { this.value.notes.splice(index, 1); }, removeCategory(index) { - this.value.categories.splice(index, 1); + this.value.recipeCategory.splice(index, 1); }, removeTags(index) { this.value.tags.splice(index, 1); diff --git a/frontend/src/pages/RecipePage.vue b/frontend/src/pages/RecipePage.vue index 5c8009963..1b3751b00 100644 --- a/frontend/src/pages/RecipePage.vue +++ b/frontend/src/pages/RecipePage.vue @@ -34,7 +34,7 @@ :description="recipeDetails.description" :instructions="recipeDetails.recipeInstructions" :tags="recipeDetails.tags" - :categories="recipeDetails.categories" + :categories="recipeDetails.recipeCategory" :notes="recipeDetails.notes" :rating="recipeDetails.rating" :yields="recipeDetails.recipeYield" diff --git a/mealie/db/database.py b/mealie/db/database.py index f7726fb90..12175565b 100644 --- a/mealie/db/database.py +++ b/mealie/db/database.py @@ -2,7 +2,7 @@ from sqlalchemy.orm.session import Session from db.db_base import BaseDocument from db.models.mealplan import MealPlanModel -from db.models.recipe import Category, RecipeModel, Tag +from db.models.recipe.recipe import Category, RecipeModel, Tag from db.models.settings import SiteSettingsModel from db.models.sign_up import SignUp from db.models.theme import SiteThemeModel diff --git a/mealie/db/models/_all_models.py b/mealie/db/models/_all_models.py index a47c4d820..2debf7b2b 100644 --- a/mealie/db/models/_all_models.py +++ b/mealie/db/models/_all_models.py @@ -1,5 +1,5 @@ from db.models.mealplan import * -from db.models.recipe import * +from db.models.recipe.recipe import * from db.models.settings import * from db.models.theme import * from db.models.users import * diff --git a/mealie/db/models/recipe/api_extras.py b/mealie/db/models/recipe/api_extras.py new file mode 100644 index 000000000..41b4866eb --- /dev/null +++ b/mealie/db/models/recipe/api_extras.py @@ -0,0 +1,19 @@ +from datetime import date + +import sqlalchemy as sa +from db.models.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.id")) + key_name = sa.Column(sa.String, unique=True) + value = sa.Column(sa.String) + + def __init__(self, key, value) -> None: + self.key_name = key + self.value = value + + def dict(self): + return {self.key_name: self.value} \ No newline at end of file diff --git a/mealie/db/models/recipe/category.py b/mealie/db/models/recipe/category.py new file mode 100644 index 000000000..ce5ac087b --- /dev/null +++ b/mealie/db/models/recipe/category.py @@ -0,0 +1,65 @@ +import sqlalchemy as sa +import sqlalchemy.orm as orm +from db.models.model_base import SqlAlchemyBase +from fastapi.logger import logger +from slugify import slugify +from sqlalchemy.orm import validates + +recipes2categories = sa.Table( + "recipes2categories", + SqlAlchemyBase.metadata, + sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")), + sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")), +) + + +class Category(SqlAlchemyBase): + __tablename__ = "categories" + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.String, index=True, nullable=False) + slug = sa.Column(sa.String, index=True, unique=True, nullable=False) + recipes = orm.relationship( + "RecipeModel", secondary=recipes2categories, back_populates="recipeCategory" + ) + + @validates("name") + def validate_name(self, key, name): + assert not name == "" + return name + + def __init__(self, name) -> None: + self.name = name.strip() + self.slug = slugify(name) + + @staticmethod + def create_if_not_exist(session, name: str = None): + test_slug = slugify(name) + try: + result = session.query(Category).filter(Category.slug == test_slug).one() + if result: + logger.info("Category exists, associating recipe") + return result + else: + logger.info("Category doesn't exists, creating tag") + return Category(name=name) + except: + logger.info("Category doesn't exists, creating category") + return Category(name=name) + + def to_str(self): + return self.name + + def dict(self): + return { + "id": self.id, + "slug": self.slug, + "name": self.name, + "recipes": [x.dict() for x in self.recipes], + } + + def dict_no_recipes(self): + return { + "id": self.id, + "slug": self.slug, + "name": self.name, + } diff --git a/mealie/db/models/recipe/ingredient.py b/mealie/db/models/recipe/ingredient.py new file mode 100644 index 000000000..018e32586 --- /dev/null +++ b/mealie/db/models/recipe/ingredient.py @@ -0,0 +1,16 @@ +import sqlalchemy as sa +from db.models.model_base import SqlAlchemyBase + + +class RecipeIngredient(SqlAlchemyBase): + __tablename__ = "recipes_ingredients" + id = sa.Column(sa.Integer, primary_key=True) + position = sa.Column(sa.Integer) + parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + ingredient = sa.Column(sa.String) + + def update(self, ingredient): + self.ingredient = ingredient + + def to_str(self): + return self.ingredient diff --git a/mealie/db/models/recipe/instruction.py b/mealie/db/models/recipe/instruction.py new file mode 100644 index 000000000..f8a50107f --- /dev/null +++ b/mealie/db/models/recipe/instruction.py @@ -0,0 +1,16 @@ +import sqlalchemy as sa +from db.models.model_base import SqlAlchemyBase + + +class RecipeInstruction(SqlAlchemyBase): + __tablename__ = "recipe_instructions" + id = sa.Column(sa.Integer, primary_key=True) + parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + position = sa.Column(sa.Integer) + type = sa.Column(sa.String, default="") + text = sa.Column(sa.String) + + def dict(self): + data = {"@type": self.type, "text": self.text} + + return data diff --git a/mealie/db/models/recipe/note.py b/mealie/db/models/recipe/note.py new file mode 100644 index 000000000..54e5b2da8 --- /dev/null +++ b/mealie/db/models/recipe/note.py @@ -0,0 +1,17 @@ +import sqlalchemy as sa +from db.models.model_base import SqlAlchemyBase + + +class Note(SqlAlchemyBase): + __tablename__ = "notes" + id = sa.Column(sa.Integer, primary_key=True) + parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + title = sa.Column(sa.String) + text = sa.Column(sa.String) + + def __init__(self, title, text) -> None: + self.title = title + self.text = text + + def dict(self): + return {"title": self.title, "text": self.text} diff --git a/mealie/db/models/recipe/nutrition.py b/mealie/db/models/recipe/nutrition.py new file mode 100644 index 000000000..9e11c67cb --- /dev/null +++ b/mealie/db/models/recipe/nutrition.py @@ -0,0 +1,40 @@ +import sqlalchemy as sa +from db.models.model_base import SqlAlchemyBase + + +class Nutrition(SqlAlchemyBase): + __tablename__ = "recipe_nutrition" + id = sa.Column(sa.Integer, primary_key=True) + parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + calories = sa.Column(sa.Integer) + fatContent = sa.Column(sa.Integer) + fiberContent = sa.Column(sa.Integer) + proteinContent = sa.Column(sa.Integer) + sodiumContent = sa.Column(sa.Integer) + sugarContent = sa.Column(sa.Integer) + + def __init__( + self, + calories=None, + fatContent=None, + fiberContent=None, + proteinContent=None, + sodiumContent=None, + sugarContent=None, + ) -> None: + self.calories = calories + self.fatContent = fatContent + self.fiberContent = fiberContent + self.proteinContent = proteinContent + self.sodiumContent = sodiumContent + self.sugarContent = sugarContent + + def dict(self) -> dict: + return { + "calories": self.calories, + "fatContent": self.fatContent, + "fiberContent": self.fiberContent, + "proteinContent": self.proteinContent, + "sodiumContent": self.sodiumContent, + "sugarContent": self.sugarContent, + } diff --git a/mealie/db/models/recipe.py b/mealie/db/models/recipe/recipe.py similarity index 51% rename from mealie/db/models/recipe.py rename to mealie/db/models/recipe/recipe.py index a2d341c3b..30518f817 100644 --- a/mealie/db/models/recipe.py +++ b/mealie/db/models/recipe/recipe.py @@ -5,182 +5,16 @@ from typing import List import sqlalchemy as sa import sqlalchemy.orm as orm from db.models.model_base import BaseMixins, SqlAlchemyBase -from slugify import slugify +from db.models.recipe.api_extras import ApiExtras +from db.models.recipe.category import Category, recipes2categories +from db.models.recipe.ingredient import RecipeIngredient +from db.models.recipe.instruction import RecipeInstruction +from db.models.recipe.note import Note +from db.models.recipe.nutrition import Nutrition +from db.models.recipe.tag import Tag, recipes2tags +from db.models.recipe.tool import Tool from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.orm import validates -from fastapi.logger import logger - - -class ApiExtras(SqlAlchemyBase): - __tablename__ = "api_extras" - id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) - key_name = sa.Column(sa.String, unique=True) - value = sa.Column(sa.String) - - def __init__(self, key, value) -> None: - self.key_name = key - self.value = value - - def dict(self): - return {self.key_name: self.value} - - -recipes2categories = sa.Table( - "recipes2categories", - SqlAlchemyBase.metadata, - sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")), - sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")), -) - -recipes2tags = sa.Table( - "recipes2tags", - SqlAlchemyBase.metadata, - sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")), - sa.Column("tag_slug", sa.Integer, sa.ForeignKey("tags.slug")), -) - - -class Category(SqlAlchemyBase): - __tablename__ = "categories" - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.String, index=True, nullable=False) - slug = sa.Column(sa.String, index=True, unique=True, nullable=False) - recipes = orm.relationship( - "RecipeModel", secondary=recipes2categories, back_populates="categories" - ) - - @validates("name") - def validate_name(self, key, name): - assert not name == "" - return name - - def __init__(self, name) -> None: - self.name = name.strip() - self.slug = slugify(name) - - @staticmethod - def create_if_not_exist(session, name: str = None): - test_slug = slugify(name) - try: - result = session.query(Category).filter(Category.slug == test_slug).one() - if result: - logger.info("Category exists, associating recipe") - return result - else: - logger.info("Category doesn't exists, creating tag") - return Category(name=name) - except: - logger.info("Category doesn't exists, creating category") - return Category(name=name) - - def to_str(self): - return self.name - - def dict(self): - return { - "id": self.id, - "slug": self.slug, - "name": self.name, - "recipes": [x.dict() for x in self.recipes], - } - - def dict_no_recipes(self): - return { - "id": self.id, - "slug": self.slug, - "name": self.name, - } - - -class Tag(SqlAlchemyBase): - __tablename__ = "tags" - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.String, index=True, nullable=False) - slug = sa.Column(sa.String, index=True, unique=True, nullable=False) - recipes = orm.relationship( - "RecipeModel", secondary=recipes2tags, back_populates="tags" - ) - - @validates("name") - def validate_name(self, key, name): - assert not name == "" - return name - - def to_str(self): - return self.name - - def __init__(self, name) -> None: - self.name = name.strip() - self.slug = slugify(self.name) - - def dict(self): - return { - "id": self.id, - "slug": self.slug, - "name": self.name, - "recipes": [x.dict() for x in self.recipes], - } - - @staticmethod - def create_if_not_exist(session, name: str = None): - test_slug = slugify(name) - try: - result = session.query(Tag).filter(Tag.slug == test_slug).first() - - if result: - logger.info("Tag exists, associating recipe") - - return result - else: - logger.info("Tag doesn't exists, creating tag") - return Tag(name=name) - except: - logger.info("Tag doesn't exists, creating tag") - return Tag(name=name) - - -class Note(SqlAlchemyBase): - __tablename__ = "notes" - id = sa.Column(sa.Integer, primary_key=True) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) - title = sa.Column(sa.String) - text = sa.Column(sa.String) - - def __init__(self, title, text) -> None: - self.title = title - self.text = text - - def dict(self): - return {"title": self.title, "text": self.text} - - -class RecipeIngredient(SqlAlchemyBase): - __tablename__ = "recipes_ingredients" - id = sa.Column(sa.Integer, primary_key=True) - position = sa.Column(sa.Integer) - parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) - ingredient = sa.Column(sa.String) - - def update(self, ingredient): - self.ingredient = ingredient - - def to_str(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.id")) - position = sa.Column(sa.Integer) - type = sa.Column(sa.String, default="") - text = sa.Column(sa.String) - - def dict(self): - data = {"@type": self.type, "text": self.text} - - return data class RecipeModel(SqlAlchemyBase, BaseMixins): @@ -192,7 +26,20 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): name = sa.Column(sa.String, nullable=False) description = sa.Column(sa.String) image = sa.Column(sa.String) + totalTime = sa.Column(sa.String) + prepTime = sa.Column(sa.String) + performTime = sa.Column(sa.String) + cookTime = sa.Column(sa.String) recipeYield = sa.Column(sa.String) + recipeCuisine = sa.Column(sa.String) + tool: List[Tool] = orm.relationship("Tool", cascade="all, delete") + nutrition: Nutrition = orm.relationship( + "Nutrition", uselist=False, cascade="all, delete" + ) + recipeCategory: List = orm.relationship( + "Category", secondary=recipes2categories, back_populates="recipes" + ) + recipeIngredient: List[RecipeIngredient] = orm.relationship( "RecipeIngredient", cascade="all, delete", @@ -206,16 +53,8 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): collection_class=ordering_list("position"), ) - # How to Properties - totalTime = sa.Column(sa.String) - prepTime = sa.Column(sa.String) - performTime = sa.Column(sa.String) - # Mealie Specific slug = sa.Column(sa.String, index=True, unique=True) - categories: List = orm.relationship( - "Category", secondary=recipes2categories, back_populates="recipes" - ) tags: List[Tag] = orm.relationship( "Tag", secondary=recipes2tags, back_populates="recipes" ) @@ -239,11 +78,14 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): recipeYield: str = None, recipeIngredient: List[str] = None, recipeInstructions: List[dict] = None, + recipeCuisine: str = None, totalTime: str = None, prepTime: str = None, + nutrition: dict = None, + tool: list[str] = [], performTime: str = None, slug: str = None, - categories: List[str] = None, + recipeCategory: List[str] = None, tags: List[str] = None, dateAdded: datetime.date = None, notes: List[dict] = None, @@ -254,6 +96,15 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): self.name = name self.description = description self.image = image + self.recipeCuisine = recipeCuisine + + if self.nutrition: + self.nutrition = Nutrition(**nutrition) + else: + self.nutrition = Nutrition() + + self.tool = [Tool(tool=x) for x in tool] if tool else [] + self.recipeYield = recipeYield self.recipeIngredient = [ RecipeIngredient(ingredient=ingr) for ingr in recipeIngredient @@ -266,15 +117,14 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): self.prepTime = prepTime self.performTime = performTime - # Mealie Specific - self.slug = slug - self.categories = [ + self.recipeCategory = [ Category.create_if_not_exist(session=session, name=cat) - for cat in categories + for cat in recipeCategory ] + # Mealie Specific self.tags = [Tag.create_if_not_exist(session=session, name=tag) for tag in tags] - + self.slug = slug self.dateAdded = dateAdded self.notes = [Note(**note) for note in notes] self.rating = rating @@ -290,11 +140,14 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): recipeYield: str = None, recipeIngredient: List[str] = None, recipeInstructions: List[dict] = None, + recipeCuisine: str = None, totalTime: str = None, + tool: list[str] = [], prepTime: str = None, performTime: str = None, + nutrition: dict = None, slug: str = None, - categories: List[str] = None, + recipeCategory: List[str] = None, tags: List[str] = None, dateAdded: datetime.date = None, notes: List[dict] = None, @@ -303,7 +156,7 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): extras: dict = None, ): """Updated a database entry by removing nested rows and rebuilds the row through the __init__ functions""" - list_of_tables = [RecipeIngredient, RecipeInstruction, ApiExtras] + list_of_tables = [RecipeIngredient, RecipeInstruction, ApiExtras, Tool] RecipeModel._sql_remove_list(session, list_of_tables, self.id) self.__init__( @@ -315,10 +168,13 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): recipeIngredient=recipeIngredient, recipeInstructions=recipeInstructions, totalTime=totalTime, + recipeCuisine=recipeCuisine, prepTime=prepTime, performTime=performTime, + nutrition=nutrition, + tool=tool, slug=slug, - categories=categories, + recipeCategory=recipeCategory, tags=tags, dateAdded=dateAdded, notes=notes, @@ -333,14 +189,17 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): "description": self.description, "image": self.image, "recipeYield": self.recipeYield, + "recipeCuisine": self.recipeCuisine, + "recipeCategory": [x.to_str() for x in self.recipeCategory], "recipeIngredient": [x.to_str() for x in self.recipeIngredient], "recipeInstructions": [x.dict() for x in self.recipeInstructions], + "nutrition": self.nutrition.dict(), "totalTime": self.totalTime, "prepTime": self.prepTime, "performTime": self.performTime, - # Mealie + "tool": [x.str() for x in self.tool], + # Mealie Specific "slug": self.slug, - "categories": [x.to_str() for x in self.categories], "tags": [x.to_str() for x in self.tags], "dateAdded": self.dateAdded, "notes": [x.dict() for x in self.notes], diff --git a/mealie/db/models/recipe/tag.py b/mealie/db/models/recipe/tag.py new file mode 100644 index 000000000..f81683a2b --- /dev/null +++ b/mealie/db/models/recipe/tag.py @@ -0,0 +1,60 @@ +import sqlalchemy as sa +import sqlalchemy.orm as orm +from db.models.model_base import SqlAlchemyBase +from fastapi.logger import logger +from slugify import slugify +from sqlalchemy.orm import validates + +recipes2tags = sa.Table( + "recipes2tags", + SqlAlchemyBase.metadata, + sa.Column("recipe_id", sa.Integer, sa.ForeignKey("recipes.id")), + sa.Column("tag_slug", sa.Integer, sa.ForeignKey("tags.slug")), +) + + +class Tag(SqlAlchemyBase): + __tablename__ = "tags" + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.String, index=True, nullable=False) + slug = sa.Column(sa.String, index=True, unique=True, nullable=False) + recipes = orm.relationship( + "RecipeModel", secondary=recipes2tags, back_populates="tags" + ) + + @validates("name") + def validate_name(self, key, name): + assert not name == "" + return name + + def to_str(self): + return self.name + + def __init__(self, name) -> None: + self.name = name.strip() + self.slug = slugify(self.name) + + def dict(self): + return { + "id": self.id, + "slug": self.slug, + "name": self.name, + "recipes": [x.dict() for x in self.recipes], + } + + @staticmethod + def create_if_not_exist(session, name: str = None): + test_slug = slugify(name) + try: + result = session.query(Tag).filter(Tag.slug == test_slug).first() + + if result: + logger.info("Tag exists, associating recipe") + + return result + else: + logger.info("Tag doesn't exists, creating tag") + return Tag(name=name) + except: + logger.info("Tag doesn't exists, creating tag") + return Tag(name=name) diff --git a/mealie/db/models/recipe/tool.py b/mealie/db/models/recipe/tool.py new file mode 100644 index 000000000..c9bf31a9d --- /dev/null +++ b/mealie/db/models/recipe/tool.py @@ -0,0 +1,15 @@ +import sqlalchemy as sa +from db.models.model_base import SqlAlchemyBase + + +class Tool(SqlAlchemyBase): + __tablename__ = "tools" + id = sa.Column(sa.Integer, primary_key=True) + parent_id = sa.Column(sa.String, sa.ForeignKey("recipes.id")) + tool = sa.Column(sa.String) + + def __init__(self, tool) -> None: + self.tool = tool + + def str(self): + return self.tool diff --git a/mealie/schema/recipe.py b/mealie/schema/recipe.py index f1c7e59d9..91adeef42 100644 --- a/mealie/schema/recipe.py +++ b/mealie/schema/recipe.py @@ -14,14 +14,25 @@ class RecipeStep(BaseModel): text: str +class Nutrition(BaseModel): + calories: Optional[int] + fatContent: Optional[int] + fiberContent: Optional[int] + proteinContent: Optional[int] + sodiumContent: Optional[int] + sugarContent: Optional[int] + + class Recipe(BaseModel): # Standard Schema name: str description: Optional[str] image: Optional[Any] recipeYield: Optional[str] + recipeCategory: Optional[List[str]] = [] recipeIngredient: Optional[list] recipeInstructions: Optional[list] + nutrition: Optional[Nutrition] totalTime: Optional[str] = None prepTime: Optional[str] = None @@ -29,7 +40,6 @@ class Recipe(BaseModel): # Mealie Specific slug: Optional[str] = "" - categories: Optional[List[str]] = [] tags: Optional[List[str]] = [] dateAdded: Optional[datetime.date] notes: Optional[List[RecipeNote]] = [] @@ -56,7 +66,7 @@ class Recipe(BaseModel): ], "slug": "chicken-and-rice-with-leeks-and-salsa-verde", "tags": ["favorite", "yummy!"], - "categories": ["Dinner", "Pasta"], + "recipeCategory": ["Dinner", "Pasta"], "notes": [{"title": "Watch Out!", "text": "Prep the day before!"}], "orgURL": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde", "rating": 3, diff --git a/mealie/services/backups/imports.py b/mealie/services/backups/imports.py index 2f0944179..4c357865c 100644 --- a/mealie/services/backups/imports.py +++ b/mealie/services/backups/imports.py @@ -6,11 +6,11 @@ from typing import List from core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR from db.database import db +from fastapi.logger import logger from schema.restore import RecipeImport, SettingsImport, ThemeImport from schema.theme import SiteTheme from services.recipe_services import Recipe from sqlalchemy.orm.session import Session -from fastapi.logger import logger class ImportDatabase: @@ -85,6 +85,11 @@ class ImportDatabase: recipe_dict = json.loads(f.read()) recipe_dict = ImportDatabase._recipe_migration(recipe_dict) try: + if recipe_dict.get("categories", False): + recipe_dict["recipeCategory"] = recipe_dict.get("categories") + del recipe_dict["categories"] + print(recipe_dict) + recipe_obj = Recipe(**recipe_dict) recipe_obj.save_to_db(self.session) import_status = RecipeImport( diff --git a/mealie/services/recipe_services.py b/mealie/services/recipe_services.py index 913ad785d..27e226640 100644 --- a/mealie/services/recipe_services.py +++ b/mealie/services/recipe_services.py @@ -19,14 +19,26 @@ class RecipeStep(BaseModel): text: str +class Nutrition(BaseModel): + calories: Optional[int] + fatContent: Optional[int] + fiberContent: Optional[int] + proteinContent: Optional[int] + sodiumContent: Optional[int] + sugarContent: Optional[int] + + class Recipe(BaseModel): # Standard Schema name: str description: Optional[str] image: Optional[Any] + nutrition: Optional[Nutrition] recipeYield: Optional[str] + recipeCategory: Optional[List[str]] = [] recipeIngredient: Optional[list] recipeInstructions: Optional[list] + tool: Optional[list[str]] totalTime: Optional[str] = None prepTime: Optional[str] = None @@ -34,7 +46,6 @@ class Recipe(BaseModel): # Mealie Specific slug: Optional[str] = "" - categories: Optional[List[str]] = [] tags: Optional[List[str]] = [] dateAdded: Optional[datetime.date] notes: Optional[List[RecipeNote]] = [] @@ -61,7 +72,7 @@ class Recipe(BaseModel): ], "slug": "chicken-and-rice-with-leeks-and-salsa-verde", "tags": ["favorite", "yummy!"], - "categories": ["Dinner", "Pasta"], + "recipeCategory": ["Dinner", "Pasta"], "notes": [{"title": "Watch Out!", "text": "Prep the day before!"}], "orgURL": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde", "rating": 3, diff --git a/mealie/tests/conftest.py b/mealie/tests/conftest.py index e73807528..2852210ca 100644 --- a/mealie/tests/conftest.py +++ b/mealie/tests/conftest.py @@ -34,7 +34,7 @@ def api_client(): yield TestClient(app) - # SQLITE_FILE.unlink() + SQLITE_FILE.unlink() @fixture(scope="session")