refactor recipe schema/model

This commit is contained in:
hay-kot 2021-03-07 11:27:12 -09:00
commit 9a5fc25cb4
26 changed files with 221 additions and 396 deletions

View file

@ -7,6 +7,7 @@
<UploadBtn
class="mt-1"
:url="`/api/migrations/${folder}/upload`"
fileName="archive"
@uploaded="$emit('refresh')"
/>
</span>

View file

@ -1,5 +1,5 @@
<template>
<div v-if="items[0]">
<div v-if="items && items.length > 0">
<h2 class="mt-4">{{ title }}</h2>
<v-chip
class="ma-1"

View file

@ -1,3 +1,6 @@
from schema.meal import MealPlanInDB
from schema.recipe import Recipe
from schema.sign_up import SignUpOut
from schema.user import GroupInDB, UserInDB
from sqlalchemy.orm.session import Session
@ -15,7 +18,8 @@ class _Recipes(BaseDocument):
def __init__(self) -> None:
self.primary_key = "slug"
self.sql_model: RecipeModel = RecipeModel
self.orm_mode = False
self.orm_mode = True
self.schema: Recipe = Recipe
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
entry: RecipeModel = self._query_one(session, match_value=slug)
@ -43,7 +47,8 @@ class _Meals(BaseDocument):
def __init__(self) -> None:
self.primary_key = "uid"
self.sql_model = MealPlanModel
self.orm_mode = False
self.orm_mode = True
self.schema = MealPlanInDB
class _Settings(BaseDocument):
@ -70,10 +75,9 @@ class _Users(BaseDocument):
def update_password(self, session, id, password: str):
entry = self._query_one(session=session, match_value=id)
entry.update_password(password)
return_data = entry.dict()
session.commit()
return return_data
return self.schema.from_orm(entry)
class _Groups(BaseDocument):
@ -88,7 +92,8 @@ class _SignUps(BaseDocument):
def __init__(self) -> None:
self.primary_key = "token"
self.sql_model = SignUp
self.orm_mode = False
self.orm_mode = True
self.schema = SignUpOut
class Database:

View file

@ -122,7 +122,7 @@ class BaseDocument:
return self.schema.from_orm(result[0])
except IndexError:
return None
return [self.schema(x) for x in result]
return [self.schema.from_orm(x) for x in result]
db_entries = [x.dict() for x in result]

View file

@ -13,32 +13,16 @@ class Meal(SqlAlchemyBase):
slug = sa.Column(sa.String)
name = sa.Column(sa.String)
date = sa.Column(sa.Date)
dateText = sa.Column(sa.String)
image = sa.Column(sa.String)
description = sa.Column(sa.String)
def __init__(
self, slug, name, date, dateText, image, description, session=None
) -> None:
def __init__(self, slug, name, date, image, description, session=None) -> None:
self.slug = slug
self.name = name
self.date = date
self.dateText = dateText
self.image = image
self.description = description
def dict(self) -> dict:
data = {
"slug": self.slug,
"name": self.name,
"date": self.date,
"dateText": self.dateText,
"image": self.image,
"description": self.description,
}
return data
class MealPlanModel(SqlAlchemyBase, BaseMixins):
__tablename__ = "mealplan"
@ -58,13 +42,3 @@ class MealPlanModel(SqlAlchemyBase, BaseMixins):
MealPlanModel._sql_remove_list(session, [Meal], uid)
self.__init__(startDate, endDate, meals)
def dict(self) -> dict:
data = {
"uid": self.uid,
"startDate": self.startDate,
"endDate": self.endDate,
"meals": [meal.dict() for meal in self.meals],
}
return data

View file

@ -183,29 +183,29 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
extras=extras,
)
def dict(self):
data = {
"name": self.name,
"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,
"tool": [x.str() for x in self.tool],
# Mealie Specific
"slug": self.slug,
"tags": [x.to_str() 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),
}
# def dict(self):
# data = {
# "name": self.name,
# "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,
# "tool": [x.str() for x in self.tool],
# # Mealie Specific
# "slug": self.slug,
# "tags": [x.to_str() 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),
# }
return data
# return data

View file

@ -1,5 +1,5 @@
from db.models.model_base import BaseMixins, SqlAlchemyBase
from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy import Boolean, Column, Integer, String
class SignUp(SqlAlchemyBase, BaseMixins):
@ -19,11 +19,3 @@ class SignUp(SqlAlchemyBase, BaseMixins):
self.token = token
self.name = name
self.admin = admin
def dict(self):
return {
"id": self.id,
"name": self.name,
"token": self.token,
"admin": self.admin
}

View file

@ -5,11 +5,11 @@ from core.config import BACKUP_DIR, TEMPLATE_DIR
from db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
from schema.backup import BackupJob, ImportJob, Imports, LocalBackup
from schema.snackbar import SnackResponse
from services.backups.exports import backup_all
from services.backups.imports import ImportDatabase
from sqlalchemy.orm.session import Session
from starlette.responses import FileResponse
from schema.snackbar import SnackResponse
router = APIRouter(prefix="/api/backups", tags=["Backups"])

View file

@ -1,20 +1,22 @@
from datetime import date
from typing import List
from db.database import db
from db.db_setup import generate_session
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends
from schema.meal import MealPlanBase, MealPlanInDB
from schema.snackbar import SnackResponse
from services.meal_services import MealPlan
from services.meal_services import get_todays_meal, process_meals
from sqlalchemy.orm.session import Session
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
@router.get("/all", response_model=List[MealPlan])
@router.get("/all", response_model=List[MealPlanInDB])
def get_all_meals(session: Session = Depends(generate_session)):
""" Returns a list of all available Meal Plan """
return MealPlan.get_all(session)
return db.meals.get_all(session)
@router.get("/{id}/shopping-list")
@ -22,7 +24,8 @@ def get_shopping_list(id: str, session: Session = Depends(generate_session)):
#! Refactor into Single Database Call
mealplan = db.meals.get(session, id)
slugs = [x.get("slug") for x in mealplan.get("meals")]
mealplan: MealPlanInDB
slugs = [x.slug for x in mealplan.meals]
recipes = [db.recipes.get(session, x) for x in slugs]
ingredients = [
{"name": x.get("name"), "recipeIngredient": x.get("recipeIngredient")}
@ -34,28 +37,29 @@ def get_shopping_list(id: str, session: Session = Depends(generate_session)):
@router.post("/create")
def set_meal_plan(data: MealPlan, session: Session = Depends(generate_session)):
def create_meal_plan(data: MealPlanBase, session: Session = Depends(generate_session)):
""" Creates a meal plan database entry """
data.process_meals(session)
data.save_to_db(session)
processed_plan = process_meals(session, data)
db.meals.create(session, processed_plan.dict())
return SnackResponse.success("Mealplan Created")
@router.get("/this-week", response_model=MealPlan)
@router.get("/this-week", response_model=MealPlanInDB)
def get_this_week(session: Session = Depends(generate_session)):
""" Returns the meal plan data for this week """
return MealPlan.this_week(session)
return db.meals.get_all(session, limit=1, order_by="startDate")
@router.put("/{plan_id}")
def update_meal_plan(
plan_id: str, meal_plan: MealPlan, session: Session = Depends(generate_session)
plan_id: str, meal_plan: MealPlanBase, session: Session = Depends(generate_session)
):
""" Updates a meal plan based off ID """
meal_plan.process_meals(session)
meal_plan.update(session, plan_id)
processed_plan = process_meals(session, meal_plan)
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
db.meals.update(session, plan_id, processed_plan.dict())
return SnackResponse.info("Mealplan Updated")
@ -64,7 +68,7 @@ def update_meal_plan(
def delete_meal_plan(plan_id, session: Session = Depends(generate_session)):
""" Removes a meal plan from the database """
MealPlan.delete(session, plan_id)
db.meals.delete(session, plan_id)
return SnackResponse.error("Mealplan Deleted")
@ -76,4 +80,4 @@ def get_today(session: Session = Depends(generate_session)):
If no meal is scheduled nothing is returned
"""
return MealPlan.today(session)
return get_todays_meal(session)

View file

@ -1,13 +1,13 @@
from db.database import db
from db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, Form, HTTPException
from fastapi.logger import logger
from fastapi.responses import FileResponse
from schema.recipe import RecipeURLIn
from schema.recipe import Recipe, RecipeURLIn
from schema.snackbar import SnackResponse
from services.image_services import read_image, write_image
from services.recipe_services import Recipe
from services.scraper.scraper import create_from_url
from sqlalchemy.orm.session import Session
from schema.snackbar import SnackResponse
router = APIRouter(
prefix="/api/recipes",
@ -16,49 +16,47 @@ router = APIRouter(
@router.post("/create", status_code=201, response_model=str)
def create_from_json(data: Recipe, db: Session = Depends(generate_session)) -> str:
def create_from_json(data: Recipe, session: Session = Depends(generate_session)) -> str:
""" Takes in a JSON string and loads data into the database as a new entry"""
new_recipe_slug = data.save_to_db(db)
recipe: Recipe = db.recipes.create(session, data.dict())
return new_recipe_slug
return recipe.slug
@router.post("/create-url", status_code=201, response_model=str)
def parse_recipe_url(url: RecipeURLIn, db: Session = Depends(generate_session)):
def parse_recipe_url(url: RecipeURLIn, session: Session = Depends(generate_session)):
""" Takes in a URL and attempts to scrape data and load it into the database """
recipe = create_from_url(url.url)
recipe.save_to_db(db)
recipe: Recipe = db.recipes.create(session, recipe.dict())
return recipe.slug
@router.get("/{recipe_slug}", response_model=Recipe)
def get_recipe(recipe_slug: str, db: Session = Depends(generate_session)):
def get_recipe(recipe_slug: str, session: Session = Depends(generate_session)):
""" Takes in a recipe slug, returns all data for a recipe """
recipe = Recipe.get_by_slug(db, recipe_slug)
return recipe
return db.recipes.get(session, recipe_slug)
@router.put("/{recipe_slug}")
def update_recipe(
recipe_slug: str, data: Recipe, db: Session = Depends(generate_session)
recipe_slug: str, data: Recipe, session: Session = Depends(generate_session)
):
""" Updates a recipe by existing slug and data. """
new_slug = data.update(db, recipe_slug)
recipe: Recipe = db.recipes.update(session, recipe_slug, data.dict())
return new_slug
return recipe.slug
@router.delete("/{recipe_slug}")
def delete_recipe(recipe_slug: str, db: Session = Depends(generate_session)):
def delete_recipe(recipe_slug: str, session: Session = Depends(generate_session)):
""" Deletes a recipe by slug """
try:
Recipe.delete(db, recipe_slug)
db.recipes.delete(session, recipe_slug)
except:
raise HTTPException(
status_code=404, detail=SnackResponse.error("Unable to Delete Recipe")
@ -86,6 +84,6 @@ def update_recipe_image(
):
""" Removes an existing image and replaces it with the incoming file. """
response = write_image(recipe_slug, image, extension)
Recipe.update_image(session, recipe_slug, extension)
db.recipes.update_image(session, recipe_slug, extension)
return response

View file

@ -1,7 +1,7 @@
from typing import List, Optional
from pydantic.main import BaseModel
from services.recipe_services import Recipe
from schema.recipe import Recipe
class RecipeCategoryResponse(BaseModel):

View file

@ -1,29 +1,47 @@
from datetime import date
from typing import List, Optional
from pydantic import BaseModel
from pydantic import BaseModel, validator
class Meal(BaseModel):
slug: Optional[str]
class MealIn(BaseModel):
name: Optional[str]
date: date
dateText: str
slug: Optional[str]
date: Optional[date]
class MealOut(MealIn):
image: Optional[str]
description: Optional[str]
class Config:
orm_mode = True
class MealPlanBase(BaseModel):
startDate: date
endDate: date
meals: List[MealIn]
@validator("endDate")
def endDate_after_startDate(cls, v, values, **kwargs):
if "startDate" in values and v < values["startDate"]:
raise ValueError("EndDate should be greater than StartDate")
return v
class MealPlanProcessed(MealPlanBase):
meals: list[MealOut]
class MealPlanInDB(MealPlanProcessed):
uid: str
class Config:
orm_mode = True
class MealData(BaseModel):
name: Optional[str]
slug: str
dateText: str
class MealPlan(BaseModel):
uid: Optional[str]
startDate: date
endDate: date
meals: List[Meal]
class Config:
schema_extra = {

View file

@ -1,7 +1,10 @@
import datetime
from typing import Any, List, Optional
from pydantic import BaseModel, validator
from db.models.recipe.ingredient import RecipeIngredient
from db.models.recipe.recipe import RecipeModel
from pydantic import BaseModel, Schema, validator
from pydantic.utils import GetterDict
from slugify import slugify
@ -9,10 +12,16 @@ class RecipeNote(BaseModel):
title: str
text: str
class Config:
orm_mode = True
class RecipeStep(BaseModel):
text: str
class Config:
orm_mode = True
class Nutrition(BaseModel):
calories: Optional[str]
@ -22,6 +31,9 @@ class Nutrition(BaseModel):
sodiumContent: Optional[str]
sugarContent: Optional[str]
class Config:
orm_mode = True
class Recipe(BaseModel):
# Standard Schema
@ -30,8 +42,8 @@ class Recipe(BaseModel):
image: Optional[Any]
recipeYield: Optional[str]
recipeCategory: Optional[List[str]] = []
recipeIngredient: Optional[list]
recipeInstructions: Optional[list]
recipeIngredient: Optional[list[str]]
recipeInstructions: Optional[list[RecipeStep]]
nutrition: Optional[Nutrition]
totalTime: Optional[str] = None
@ -48,6 +60,18 @@ class Recipe(BaseModel):
extras: Optional[dict] = {}
class Config:
orm_mode = True
@classmethod
def getter_dict(cls, name_orm: RecipeModel):
print(name_orm.recipeIngredient)
return {
**GetterDict(name_orm),
"recipeIngredient": [x.ingredient for x in name_orm.recipeIngredient],
"recipeCategory": [x.name for x in name_orm.recipeCategory],
"tags": [x.name for x in name_orm.tags]
}
schema_extra = {
"example": {
"name": "Chicken and Rice With Leeks and Salsa Verde",

View file

@ -12,3 +12,6 @@ class SignUpToken(SignUpIn):
class SignUpOut(SignUpToken):
id: int
class Config:
orm_mode = True

View file

@ -8,8 +8,7 @@ from db.database import db
from db.db_setup import create_session
from fastapi.logger import logger
from jinja2 import Template
from services.meal_services import MealPlan
from services.recipe_services import Recipe
from schema.recipe import Recipe
class ExportDatabase:
@ -57,15 +56,16 @@ class ExportDatabase:
dir.mkdir(parents=True, exist_ok=True)
def export_recipes(self):
all_recipes = Recipe.get_all(self.session)
all_recipes = db.recipes.get_all(self.session)
for recipe in all_recipes:
recipe: Recipe
logger.info(f"Backing Up Recipes: {recipe}")
filename = recipe.get("slug") + ".json"
filename = recipe.slug + ".json"
file_path = self.recipe_dir.joinpath(filename)
ExportDatabase._write_json_file(recipe, file_path)
ExportDatabase._write_json_file(recipe.dict(), file_path)
if self.templates:
self._export_template(recipe)
@ -101,7 +101,7 @@ class ExportDatabase:
def export_meals(self):
#! Problem Parseing Datetime Objects... May come back to this
meal_plans = MealPlan.get_all(self.session)
meal_plans = db.meals.get_all(self.session)
if meal_plans:
meal_plans = [x.dict() for x in meal_plans]

View file

@ -6,10 +6,11 @@ from typing import List
from core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR
from db.database import db
from db.db_setup import create_session
from fastapi.logger import logger
from schema.recipe import Recipe
from schema.restore import RecipeImport, SettingsImport, ThemeImport
from schema.theme import SiteTheme
from services.recipe_services import Recipe
from sqlalchemy.orm.session import Session
@ -75,6 +76,7 @@ class ImportDatabase:
}
def import_recipes(self):
session = create_session()
recipe_dir: Path = self.import_dir.joinpath("recipes")
imports = []
@ -88,10 +90,9 @@ class ImportDatabase:
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)
db.recipes.create(session, recipe_obj.dict())
import_status = RecipeImport(
name=recipe_obj.name, slug=recipe_obj.slug, status=True
)

View file

@ -1,111 +1,43 @@
from datetime import date, timedelta
from typing import List, Optional
from db.database import db
from pydantic import BaseModel, validator
from schema.meal import MealIn, MealOut, MealPlanBase, MealPlanProcessed
from schema.recipe import Recipe
from sqlalchemy.orm.session import Session
from services.recipe_services import Recipe
def process_meals(session: Session, meal_plan_base: MealPlanBase) -> MealPlanProcessed:
meals = []
for x, meal in enumerate(meal_plan_base.meals):
meal: MealIn
try:
recipe: Recipe = db.recipes.get(session, meal.slug)
meal_data = MealOut(
slug=recipe.slug,
name=recipe.name,
date=meal_plan_base.startDate + timedelta(days=x),
image=recipe.image,
description=recipe.description,
)
except:
meal_data = MealOut(
date=meal_plan_base.startDate + timedelta(days=x),
)
meals.append(meal_data)
return MealPlanProcessed(
meals=meals, startDate=meal_plan_base.startDate, endDate=meal_plan_base.endDate
)
class Meal(BaseModel):
slug: Optional[str]
name: Optional[str]
date: date
dateText: str
image: Optional[str]
description: Optional[str]
def get_todays_meal(session):
meal_plan = db.meals.get_all(session, limit=1, order_by="startDate")
class MealData(BaseModel):
name: Optional[str]
slug: str
dateText: str
class MealPlan(BaseModel):
uid: Optional[str]
startDate: date
endDate: date
meals: List[Meal]
class Config:
schema_extra = {
"example": {
"startDate": date.today(),
"endDate": date.today(),
"meals": [
{"slug": "Packed Mac and Cheese", "date": date.today()},
{"slug": "Eggs and Toast", "date": date.today()},
],
}
}
@validator('endDate')
def endDate_after_startDate(cls, v, values, **kwargs):
if 'startDate' in values and v < values['startDate']:
raise ValueError('EndDate should be greater than StartDate')
return v
def process_meals(self, session: Session):
meals = []
for x, meal in enumerate(self.meals):
try:
recipe = Recipe.get_by_slug(session, meal.slug)
meal_data = {
"slug": recipe.slug,
"name": recipe.name,
"date": self.startDate + timedelta(days=x),
"dateText": meal.dateText,
"image": recipe.image,
"description": recipe.description,
}
except:
meal_data = {
"date": self.startDate + timedelta(days=x),
"dateText": meal.dateText,
}
meals.append(Meal(**meal_data))
self.meals = meals
def save_to_db(self, session: Session):
db.meals.create(session, self.dict())
@staticmethod
def get_all(session: Session) -> List:
all_meals = [
MealPlan(**x) for x in db.meals.get_all(session, order_by="startDate")
]
return all_meals
def update(self, session, uid):
db.meals.update(session, uid, self.dict())
@staticmethod
def delete(session, uid):
db.meals.delete(session, uid)
@staticmethod
def today(session: Session) -> str:
""" Returns the meal slug for Today """
meal_plan = db.meals.get_all(session, limit=1, order_by="startDate")
meal_docs = [Meal(**meal) for meal in meal_plan["meals"]]
for meal in meal_docs:
if meal.date == date.today():
return meal.slug
return "No Meal Today"
@staticmethod
def this_week(session: Session):
meal_plan = db.meals.get_all(session, limit=1, order_by="startDate")
return meal_plan
for meal in meal_plan:
meal: MealOut
if meal.date == date.today():
return meal.slug

View file

@ -3,7 +3,8 @@ from pathlib import Path
import yaml
from core.config import IMG_DIR, TEMP_DIR
from services.recipe_services import Recipe
from db.database import db
from schema.recipe import Recipe
from sqlalchemy.orm.session import Session
from utils.unzip import unpack_zip
@ -49,32 +50,42 @@ def read_chowdown_file(recipe_file: Path) -> Recipe:
"tags": recipe_data.get("tags").split(","),
}
new_recipe = Recipe(**reformat_data)
print(reformat_data)
reformated_list = []
for instruction in new_recipe.recipeInstructions:
for instruction in reformat_data["recipeInstructions"]:
reformated_list.append({"text": instruction})
reformat_data["recipeInstructions"] = reformated_list
new_recipe.recipeInstructions = reformated_list
return new_recipe
return Recipe(**reformat_data)
def chowdown_migrate(session: Session, zip_file: Path):
temp_dir = unpack_zip(zip_file)
temp_dir = unpack_zip(zip_file)
print(temp_dir.name)
path = Path(temp_dir.name)
for p in path.iterdir():
print("ItterDir", p)
for p in p.iterdir():
print("Sub Itter", p)
with temp_dir as dir:
image_dir = TEMP_DIR.joinpath(dir, zip_file.stem, "images")
recipe_dir = TEMP_DIR.joinpath(dir, zip_file.stem, "_recipes")
chow_dir = next(Path(dir).iterdir())
image_dir = TEMP_DIR.joinpath(chow_dir, "images")
recipe_dir = TEMP_DIR.joinpath(chow_dir, "_recipes")
print(image_dir.exists())
print(recipe_dir.exists())
failed_recipes = []
successful_recipes = []
for recipe in recipe_dir.glob("*.md"):
try:
new_recipe = read_chowdown_file(recipe)
new_recipe.save_to_db(session)
successful_recipes.append(recipe.stem)
except:
db.recipes.create(session, new_recipe.dict())
successful_recipes.append(new_recipe.name)
except Exception as inst:
failed_recipes.append(recipe.stem)
failed_images = []
@ -82,7 +93,8 @@ def chowdown_migrate(session: Session, zip_file: Path):
try:
if not image.stem in failed_recipes:
shutil.copy(image, IMG_DIR.joinpath(image.name))
except:
except Exception as inst:
print(inst)
failed_images.append(image.name)
report = {"successful": successful_recipes, "failed": failed_recipes}

View file

@ -5,9 +5,10 @@ import zipfile
from pathlib import Path
from core.config import IMG_DIR, MIGRATION_DIR, TEMP_DIR
from services.recipe_services import Recipe
from schema.recipe import Recipe
from services.scraper.cleaner import Cleaner
from core.config import IMG_DIR, TEMP_DIR
from db.database import db
def process_selection(selection: Path) -> Path:
@ -77,7 +78,8 @@ def migrate(session, selection: str):
try:
recipe = import_recipes(dir)
recipe.save_to_db(session)
db.recipes.create(session, recipe.dict())
successful_imports.append(recipe.name)
except:
logging.error(f"Failed Nextcloud Import: {dir.name}")

View file

@ -1,141 +0,0 @@
import datetime
from pathlib import Path
from typing import Any, List, Optional
from db.database import db
from pydantic import BaseModel, validator
from slugify import slugify
from sqlalchemy.orm.session import Session
from services.image_services import delete_image
class RecipeNote(BaseModel):
title: str
text: str
class RecipeStep(BaseModel):
text: str
class Nutrition(BaseModel):
calories: Optional[str]
fatContent: Optional[str]
fiberContent: Optional[str]
proteinContent: Optional[str]
sodiumContent: Optional[str]
sugarContent: Optional[str]
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
performTime: Optional[str] = None
# Mealie Specific
slug: Optional[str] = ""
tags: Optional[List[str]] = []
dateAdded: Optional[datetime.date]
notes: Optional[List[RecipeNote]] = []
rating: Optional[int] = 0
orgURL: Optional[str] = ""
extras: Optional[dict] = {}
class Config:
schema_extra = {
"example": {
"name": "Chicken and Rice With Leeks and Salsa Verde",
"description": "This one-skillet dinner gets deep oniony flavor from lots of leeks cooked down to jammy tenderness.",
"image": "chicken-and-rice-with-leeks-and-salsa-verde.jpg",
"recipeYield": "4 Servings",
"recipeIngredient": [
"1 1/2 lb. skinless, boneless chicken thighs (4-8 depending on size)",
"Kosher salt, freshly ground pepper",
"3 Tbsp. unsalted butter, divided",
],
"recipeInstructions": [
{
"text": "Season chicken with salt and pepper.",
},
],
"slug": "chicken-and-rice-with-leeks-and-salsa-verde",
"tags": ["favorite", "yummy!"],
"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,
"extras": {"message": "Don't forget to defrost the chicken!"},
}
}
@validator("slug", always=True, pre=True)
def validate_slug(slug: str, values):
name: str = values["name"]
calc_slug: str = slugify(name)
if slug == calc_slug:
return slug
else:
slug = calc_slug
return slug
@classmethod
def get_by_slug(cls, session, slug: str):
""" Returns a Recipe Object by Slug """
document = db.recipes.get(session, slug, "slug")
return cls(**document)
def save_to_db(self, session) -> str:
recipe_dict = self.dict()
try:
extension = Path(recipe_dict["image"]).suffix
recipe_dict["image"] = recipe_dict.get("slug") + extension
except:
recipe_dict["image"] = "no image"
recipe_doc = db.recipes.create(session, recipe_dict)
recipe = Recipe(**recipe_doc)
return recipe.slug
@staticmethod
def delete(session: Session, recipe_slug: str) -> str:
""" Removes the recipe from the database by slug """
delete_image(recipe_slug)
db.recipes.delete(session, recipe_slug)
return "Document Deleted"
def update(self, session: Session, recipe_slug: str):
""" Updates the recipe from the database by slug"""
updated_slug = db.recipes.update(session, recipe_slug, self.dict())
return updated_slug.get("slug")
@staticmethod
def update_image(session: Session, slug: str, extension: str = None) -> str:
"""A helper function to pass the new image name and extension
into the database.
Args:
slug (str): The current recipe slug
extension (str): the file extension of the new image
"""
return db.recipes.update_image(session, slug, extension)
@staticmethod
def get_all(session: Session):
return db.recipes.get_all(session)

View file

@ -48,6 +48,8 @@ class Cleaner:
recipe_data["slug"] = slugify(recipe_data.get("name"))
recipe_data["orgURL"] = url
print(recipe_data["recipeIngredient"])
return recipe_data
@staticmethod

View file

@ -6,7 +6,7 @@ import scrape_schema_recipe
from core.config import DEBUG_DIR
from fastapi.logger import logger
from services.image_services import scrape_image
from services.recipe_services import Recipe
from schema.recipe import Recipe
from services.scraper import open_graph
from services.scraper.cleaner import Cleaner

View file

@ -1,7 +1,8 @@
from pathlib import Path
from core.config import TEMP_DIR
import pytest
from core.config import TEMP_DIR
from schema.recipe import Recipe
from services.image_services import IMG_DIR
from services.migrations.nextcloud import (
cleanup,
@ -9,7 +10,6 @@ from services.migrations.nextcloud import (
prep,
process_selection,
)
from services.recipe_services import Recipe
from tests.test_config import TEST_NEXTCLOUD_DIR
CWD = Path(__file__).parent

View file

@ -19,12 +19,10 @@ def get_meal_plan_template(first=None, second=None):
{
"slug": first,
"date": "2021-1-17",
"dateText": "Monday, January 18, 2021",
},
{
"slug": second,
"date": "2021-1-18",
"dateText": "Tueday, January 19, 2021",
},
],
}

View file

@ -1,7 +1,8 @@
from pathlib import Path
from core.config import TEMP_DIR
import pytest
from core.config import TEMP_DIR
from schema.recipe import Recipe
from services.image_services import IMG_DIR
from services.migrations.nextcloud import (
cleanup,
@ -9,7 +10,6 @@ from services.migrations.nextcloud import (
prep,
process_selection,
)
from services.recipe_services import Recipe
from tests.test_config import TEST_NEXTCLOUD_DIR
CWD = Path(__file__).parent

View file

@ -4,8 +4,7 @@ import requests
from db.database import db
from db.db_setup import create_session
from schema.settings import SiteSettings
from services.meal_services import MealPlan
from services.recipe_services import Recipe
from services.meal_services import get_todays_meal
def post_webhooks():
@ -14,7 +13,8 @@ def post_webhooks():
all_settings = SiteSettings(**all_settings)
if all_settings.webhooks.enabled:
todays_meal = Recipe.get_by_slug(MealPlan.today()).dict()
today_slug = get_todays_meal(session)
todays_meal = db.recipes.get(session, today_slug)
urls = all_settings.webhooks.webhookURLs
for url in urls: