mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 22:43:34 -07:00
refactor recipe schema/model
This commit is contained in:
parent
db61ac8a31
commit
9a5fc25cb4
26 changed files with 221 additions and 396 deletions
|
@ -7,6 +7,7 @@
|
||||||
<UploadBtn
|
<UploadBtn
|
||||||
class="mt-1"
|
class="mt-1"
|
||||||
:url="`/api/migrations/${folder}/upload`"
|
:url="`/api/migrations/${folder}/upload`"
|
||||||
|
fileName="archive"
|
||||||
@uploaded="$emit('refresh')"
|
@uploaded="$emit('refresh')"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="items[0]">
|
<div v-if="items && items.length > 0">
|
||||||
<h2 class="mt-4">{{ title }}</h2>
|
<h2 class="mt-4">{{ title }}</h2>
|
||||||
<v-chip
|
<v-chip
|
||||||
class="ma-1"
|
class="ma-1"
|
||||||
|
|
|
@ -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 schema.user import GroupInDB, UserInDB
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
@ -15,7 +18,8 @@ class _Recipes(BaseDocument):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.primary_key = "slug"
|
self.primary_key = "slug"
|
||||||
self.sql_model: RecipeModel = RecipeModel
|
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:
|
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
|
||||||
entry: RecipeModel = self._query_one(session, match_value=slug)
|
entry: RecipeModel = self._query_one(session, match_value=slug)
|
||||||
|
@ -43,7 +47,8 @@ class _Meals(BaseDocument):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.primary_key = "uid"
|
self.primary_key = "uid"
|
||||||
self.sql_model = MealPlanModel
|
self.sql_model = MealPlanModel
|
||||||
self.orm_mode = False
|
self.orm_mode = True
|
||||||
|
self.schema = MealPlanInDB
|
||||||
|
|
||||||
|
|
||||||
class _Settings(BaseDocument):
|
class _Settings(BaseDocument):
|
||||||
|
@ -70,10 +75,9 @@ class _Users(BaseDocument):
|
||||||
def update_password(self, session, id, password: str):
|
def update_password(self, session, id, password: str):
|
||||||
entry = self._query_one(session=session, match_value=id)
|
entry = self._query_one(session=session, match_value=id)
|
||||||
entry.update_password(password)
|
entry.update_password(password)
|
||||||
return_data = entry.dict()
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
return return_data
|
return self.schema.from_orm(entry)
|
||||||
|
|
||||||
|
|
||||||
class _Groups(BaseDocument):
|
class _Groups(BaseDocument):
|
||||||
|
@ -88,7 +92,8 @@ class _SignUps(BaseDocument):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.primary_key = "token"
|
self.primary_key = "token"
|
||||||
self.sql_model = SignUp
|
self.sql_model = SignUp
|
||||||
self.orm_mode = False
|
self.orm_mode = True
|
||||||
|
self.schema = SignUpOut
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
|
|
|
@ -122,7 +122,7 @@ class BaseDocument:
|
||||||
return self.schema.from_orm(result[0])
|
return self.schema.from_orm(result[0])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
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]
|
db_entries = [x.dict() for x in result]
|
||||||
|
|
||||||
|
|
|
@ -13,32 +13,16 @@ class Meal(SqlAlchemyBase):
|
||||||
slug = sa.Column(sa.String)
|
slug = sa.Column(sa.String)
|
||||||
name = sa.Column(sa.String)
|
name = sa.Column(sa.String)
|
||||||
date = sa.Column(sa.Date)
|
date = sa.Column(sa.Date)
|
||||||
dateText = sa.Column(sa.String)
|
|
||||||
image = sa.Column(sa.String)
|
image = sa.Column(sa.String)
|
||||||
description = sa.Column(sa.String)
|
description = sa.Column(sa.String)
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, slug, name, date, image, description, session=None) -> None:
|
||||||
self, slug, name, date, dateText, image, description, session=None
|
|
||||||
) -> None:
|
|
||||||
self.slug = slug
|
self.slug = slug
|
||||||
self.name = name
|
self.name = name
|
||||||
self.date = date
|
self.date = date
|
||||||
self.dateText = dateText
|
|
||||||
self.image = image
|
self.image = image
|
||||||
self.description = description
|
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):
|
class MealPlanModel(SqlAlchemyBase, BaseMixins):
|
||||||
__tablename__ = "mealplan"
|
__tablename__ = "mealplan"
|
||||||
|
@ -58,13 +42,3 @@ class MealPlanModel(SqlAlchemyBase, BaseMixins):
|
||||||
MealPlanModel._sql_remove_list(session, [Meal], uid)
|
MealPlanModel._sql_remove_list(session, [Meal], uid)
|
||||||
|
|
||||||
self.__init__(startDate, endDate, meals)
|
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
|
|
||||||
|
|
|
@ -183,29 +183,29 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
|
||||||
extras=extras,
|
extras=extras,
|
||||||
)
|
)
|
||||||
|
|
||||||
def dict(self):
|
# def dict(self):
|
||||||
data = {
|
# data = {
|
||||||
"name": self.name,
|
# "name": self.name,
|
||||||
"description": self.description,
|
# "description": self.description,
|
||||||
"image": self.image,
|
# "image": self.image,
|
||||||
"recipeYield": self.recipeYield,
|
# "recipeYield": self.recipeYield,
|
||||||
"recipeCuisine": self.recipeCuisine,
|
# "recipeCuisine": self.recipeCuisine,
|
||||||
"recipeCategory": [x.to_str() for x in self.recipeCategory],
|
# "recipeCategory": [x.to_str() for x in self.recipeCategory],
|
||||||
"recipeIngredient": [x.to_str() for x in self.recipeIngredient],
|
# "recipeIngredient": [x.to_str() for x in self.recipeIngredient],
|
||||||
"recipeInstructions": [x.dict() for x in self.recipeInstructions],
|
# "recipeInstructions": [x.dict() for x in self.recipeInstructions],
|
||||||
"nutrition": self.nutrition.dict(),
|
# "nutrition": self.nutrition.dict(),
|
||||||
"totalTime": self.totalTime,
|
# "totalTime": self.totalTime,
|
||||||
"prepTime": self.prepTime,
|
# "prepTime": self.prepTime,
|
||||||
"performTime": self.performTime,
|
# "performTime": self.performTime,
|
||||||
"tool": [x.str() for x in self.tool],
|
# "tool": [x.str() for x in self.tool],
|
||||||
# Mealie Specific
|
# # Mealie Specific
|
||||||
"slug": self.slug,
|
# "slug": self.slug,
|
||||||
"tags": [x.to_str() for x in self.tags],
|
# "tags": [x.to_str() for x in self.tags],
|
||||||
"dateAdded": self.dateAdded,
|
# "dateAdded": self.dateAdded,
|
||||||
"notes": [x.dict() for x in self.notes],
|
# "notes": [x.dict() for x in self.notes],
|
||||||
"rating": self.rating,
|
# "rating": self.rating,
|
||||||
"orgURL": self.orgURL,
|
# "orgURL": self.orgURL,
|
||||||
"extras": RecipeModel._flatten_dict(self.extras),
|
# "extras": RecipeModel._flatten_dict(self.extras),
|
||||||
}
|
# }
|
||||||
|
|
||||||
return data
|
# return data
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
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):
|
class SignUp(SqlAlchemyBase, BaseMixins):
|
||||||
|
@ -19,11 +19,3 @@ class SignUp(SqlAlchemyBase, BaseMixins):
|
||||||
self.token = token
|
self.token = token
|
||||||
self.name = name
|
self.name = name
|
||||||
self.admin = admin
|
self.admin = admin
|
||||||
|
|
||||||
def dict(self):
|
|
||||||
return {
|
|
||||||
"id": self.id,
|
|
||||||
"name": self.name,
|
|
||||||
"token": self.token,
|
|
||||||
"admin": self.admin
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,11 +5,11 @@ from core.config import BACKUP_DIR, TEMPLATE_DIR
|
||||||
from db.db_setup import generate_session
|
from 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 schema.backup import BackupJob, ImportJob, Imports, LocalBackup
|
||||||
|
from schema.snackbar import SnackResponse
|
||||||
from services.backups.exports import backup_all
|
from services.backups.exports import backup_all
|
||||||
from services.backups.imports import ImportDatabase
|
from 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
|
||||||
from schema.snackbar import SnackResponse
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/backups", tags=["Backups"])
|
router = APIRouter(prefix="/api/backups", tags=["Backups"])
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
|
from datetime import date
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from db.database import db
|
from db.database import db
|
||||||
from db.db_setup import generate_session
|
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 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
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
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)):
|
def get_all_meals(session: Session = Depends(generate_session)):
|
||||||
""" Returns a list of all available Meal Plan """
|
""" Returns a list of all available Meal Plan """
|
||||||
|
|
||||||
return MealPlan.get_all(session)
|
return db.meals.get_all(session)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{id}/shopping-list")
|
@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
|
#! Refactor into Single Database Call
|
||||||
mealplan = db.meals.get(session, id)
|
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]
|
recipes = [db.recipes.get(session, x) for x in slugs]
|
||||||
ingredients = [
|
ingredients = [
|
||||||
{"name": x.get("name"), "recipeIngredient": x.get("recipeIngredient")}
|
{"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")
|
@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 """
|
""" Creates a meal plan database entry """
|
||||||
data.process_meals(session)
|
processed_plan = process_meals(session, data)
|
||||||
data.save_to_db(session)
|
db.meals.create(session, processed_plan.dict())
|
||||||
|
|
||||||
return SnackResponse.success("Mealplan Created")
|
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)):
|
def get_this_week(session: Session = Depends(generate_session)):
|
||||||
""" Returns the meal plan data for this week """
|
""" 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}")
|
@router.put("/{plan_id}")
|
||||||
def update_meal_plan(
|
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 """
|
""" Updates a meal plan based off ID """
|
||||||
meal_plan.process_meals(session)
|
processed_plan = process_meals(session, meal_plan)
|
||||||
meal_plan.update(session, plan_id)
|
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
||||||
|
db.meals.update(session, plan_id, processed_plan.dict())
|
||||||
|
|
||||||
return SnackResponse.info("Mealplan Updated")
|
return SnackResponse.info("Mealplan Updated")
|
||||||
|
|
||||||
|
@ -64,7 +68,7 @@ def update_meal_plan(
|
||||||
def delete_meal_plan(plan_id, session: Session = Depends(generate_session)):
|
def delete_meal_plan(plan_id, session: Session = Depends(generate_session)):
|
||||||
""" Removes a meal plan from the database """
|
""" Removes a meal plan from the database """
|
||||||
|
|
||||||
MealPlan.delete(session, plan_id)
|
db.meals.delete(session, plan_id)
|
||||||
|
|
||||||
return SnackResponse.error("Mealplan Deleted")
|
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
|
If no meal is scheduled nothing is returned
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return MealPlan.today(session)
|
return get_todays_meal(session)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
from db.database import db
|
||||||
from db.db_setup import generate_session
|
from 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 RecipeURLIn
|
from schema.recipe import Recipe, RecipeURLIn
|
||||||
|
from schema.snackbar import SnackResponse
|
||||||
from services.image_services import read_image, write_image
|
from services.image_services import read_image, write_image
|
||||||
from services.recipe_services import Recipe
|
|
||||||
from services.scraper.scraper import create_from_url
|
from services.scraper.scraper import create_from_url
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
from schema.snackbar import SnackResponse
|
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/api/recipes",
|
prefix="/api/recipes",
|
||||||
|
@ -16,49 +16,47 @@ router = APIRouter(
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create", status_code=201, response_model=str)
|
@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"""
|
""" 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)
|
@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 """
|
""" Takes in a URL and attempts to scrape data and load it into the database """
|
||||||
|
|
||||||
recipe = create_from_url(url.url)
|
recipe = create_from_url(url.url)
|
||||||
|
recipe: Recipe = db.recipes.create(session, recipe.dict())
|
||||||
recipe.save_to_db(db)
|
|
||||||
|
|
||||||
return recipe.slug
|
return recipe.slug
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{recipe_slug}", response_model=Recipe)
|
@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 """
|
""" 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}")
|
@router.put("/{recipe_slug}")
|
||||||
def update_recipe(
|
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. """
|
""" 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}")
|
@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 """
|
""" Deletes a recipe by slug """
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Recipe.delete(db, 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")
|
||||||
|
@ -86,6 +84,6 @@ def update_recipe_image(
|
||||||
):
|
):
|
||||||
""" Removes an existing image and replaces it with the incoming file. """
|
""" Removes an existing image and replaces it with the incoming file. """
|
||||||
response = write_image(recipe_slug, image, extension)
|
response = write_image(recipe_slug, image, extension)
|
||||||
Recipe.update_image(session, recipe_slug, extension)
|
db.recipes.update_image(session, recipe_slug, extension)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from pydantic.main import BaseModel
|
from pydantic.main import BaseModel
|
||||||
from services.recipe_services import Recipe
|
from schema.recipe import Recipe
|
||||||
|
|
||||||
|
|
||||||
class RecipeCategoryResponse(BaseModel):
|
class RecipeCategoryResponse(BaseModel):
|
||||||
|
|
|
@ -1,29 +1,47 @@
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, validator
|
||||||
|
|
||||||
|
|
||||||
class Meal(BaseModel):
|
class MealIn(BaseModel):
|
||||||
slug: Optional[str]
|
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
date: date
|
slug: Optional[str]
|
||||||
dateText: str
|
date: Optional[date]
|
||||||
|
|
||||||
|
|
||||||
|
class MealOut(MealIn):
|
||||||
image: Optional[str]
|
image: Optional[str]
|
||||||
description: 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):
|
class MealPlan(BaseModel):
|
||||||
uid: Optional[str]
|
uid: Optional[str]
|
||||||
startDate: date
|
|
||||||
endDate: date
|
|
||||||
meals: List[Meal]
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
schema_extra = {
|
schema_extra = {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import datetime
|
import datetime
|
||||||
from typing import Any, List, Optional
|
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
|
from slugify import slugify
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,10 +12,16 @@ class RecipeNote(BaseModel):
|
||||||
title: str
|
title: str
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
class RecipeStep(BaseModel):
|
class RecipeStep(BaseModel):
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
class Nutrition(BaseModel):
|
class Nutrition(BaseModel):
|
||||||
calories: Optional[str]
|
calories: Optional[str]
|
||||||
|
@ -22,6 +31,9 @@ class Nutrition(BaseModel):
|
||||||
sodiumContent: Optional[str]
|
sodiumContent: Optional[str]
|
||||||
sugarContent: Optional[str]
|
sugarContent: Optional[str]
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
class Recipe(BaseModel):
|
class Recipe(BaseModel):
|
||||||
# Standard Schema
|
# Standard Schema
|
||||||
|
@ -30,8 +42,8 @@ class Recipe(BaseModel):
|
||||||
image: Optional[Any]
|
image: Optional[Any]
|
||||||
recipeYield: Optional[str]
|
recipeYield: Optional[str]
|
||||||
recipeCategory: Optional[List[str]] = []
|
recipeCategory: Optional[List[str]] = []
|
||||||
recipeIngredient: Optional[list]
|
recipeIngredient: Optional[list[str]]
|
||||||
recipeInstructions: Optional[list]
|
recipeInstructions: Optional[list[RecipeStep]]
|
||||||
nutrition: Optional[Nutrition]
|
nutrition: Optional[Nutrition]
|
||||||
|
|
||||||
totalTime: Optional[str] = None
|
totalTime: Optional[str] = None
|
||||||
|
@ -48,6 +60,18 @@ class Recipe(BaseModel):
|
||||||
extras: Optional[dict] = {}
|
extras: Optional[dict] = {}
|
||||||
|
|
||||||
class Config:
|
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 = {
|
schema_extra = {
|
||||||
"example": {
|
"example": {
|
||||||
"name": "Chicken and Rice With Leeks and Salsa Verde",
|
"name": "Chicken and Rice With Leeks and Salsa Verde",
|
||||||
|
|
|
@ -12,3 +12,6 @@ class SignUpToken(SignUpIn):
|
||||||
|
|
||||||
class SignUpOut(SignUpToken):
|
class SignUpOut(SignUpToken):
|
||||||
id: int
|
id: int
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
orm_mode = True
|
||||||
|
|
|
@ -8,8 +8,7 @@ from db.database import db
|
||||||
from db.db_setup import create_session
|
from 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 services.meal_services import MealPlan
|
from schema.recipe import Recipe
|
||||||
from services.recipe_services import Recipe
|
|
||||||
|
|
||||||
|
|
||||||
class ExportDatabase:
|
class ExportDatabase:
|
||||||
|
@ -57,15 +56,16 @@ class ExportDatabase:
|
||||||
dir.mkdir(parents=True, exist_ok=True)
|
dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
def export_recipes(self):
|
def export_recipes(self):
|
||||||
all_recipes = Recipe.get_all(self.session)
|
all_recipes = db.recipes.get_all(self.session)
|
||||||
|
|
||||||
for recipe in all_recipes:
|
for recipe in all_recipes:
|
||||||
|
recipe: Recipe
|
||||||
logger.info(f"Backing Up Recipes: {recipe}")
|
logger.info(f"Backing Up Recipes: {recipe}")
|
||||||
|
|
||||||
filename = recipe.get("slug") + ".json"
|
filename = recipe.slug + ".json"
|
||||||
file_path = self.recipe_dir.joinpath(filename)
|
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:
|
if self.templates:
|
||||||
self._export_template(recipe)
|
self._export_template(recipe)
|
||||||
|
@ -101,7 +101,7 @@ class ExportDatabase:
|
||||||
|
|
||||||
def export_meals(self):
|
def export_meals(self):
|
||||||
#! Problem Parseing Datetime Objects... May come back to this
|
#! 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:
|
if meal_plans:
|
||||||
meal_plans = [x.dict() for x in meal_plans]
|
meal_plans = [x.dict() for x in meal_plans]
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,11 @@ from typing import List
|
||||||
|
|
||||||
from core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR
|
from core.config import BACKUP_DIR, IMG_DIR, TEMP_DIR
|
||||||
from db.database import db
|
from db.database import db
|
||||||
|
from db.db_setup import create_session
|
||||||
from fastapi.logger import logger
|
from fastapi.logger import logger
|
||||||
|
from schema.recipe import Recipe
|
||||||
from schema.restore import RecipeImport, SettingsImport, ThemeImport
|
from schema.restore import RecipeImport, SettingsImport, ThemeImport
|
||||||
from schema.theme import SiteTheme
|
from schema.theme import SiteTheme
|
||||||
from services.recipe_services import Recipe
|
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ class ImportDatabase:
|
||||||
}
|
}
|
||||||
|
|
||||||
def import_recipes(self):
|
def import_recipes(self):
|
||||||
|
session = create_session()
|
||||||
recipe_dir: Path = self.import_dir.joinpath("recipes")
|
recipe_dir: Path = self.import_dir.joinpath("recipes")
|
||||||
|
|
||||||
imports = []
|
imports = []
|
||||||
|
@ -88,10 +90,9 @@ class ImportDatabase:
|
||||||
if recipe_dict.get("categories", False):
|
if recipe_dict.get("categories", False):
|
||||||
recipe_dict["recipeCategory"] = recipe_dict.get("categories")
|
recipe_dict["recipeCategory"] = recipe_dict.get("categories")
|
||||||
del recipe_dict["categories"]
|
del recipe_dict["categories"]
|
||||||
print(recipe_dict)
|
|
||||||
|
|
||||||
recipe_obj = Recipe(**recipe_dict)
|
recipe_obj = Recipe(**recipe_dict)
|
||||||
recipe_obj.save_to_db(self.session)
|
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
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,111 +1,43 @@
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from db.database import db
|
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 sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
from services.recipe_services import Recipe
|
|
||||||
|
|
||||||
|
def process_meals(session: Session, meal_plan_base: MealPlanBase) -> MealPlanProcessed:
|
||||||
class Meal(BaseModel):
|
|
||||||
slug: Optional[str]
|
|
||||||
name: Optional[str]
|
|
||||||
date: date
|
|
||||||
dateText: str
|
|
||||||
image: Optional[str]
|
|
||||||
description: Optional[str]
|
|
||||||
|
|
||||||
|
|
||||||
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 = []
|
meals = []
|
||||||
for x, meal in enumerate(self.meals):
|
for x, meal in enumerate(meal_plan_base.meals):
|
||||||
|
meal: MealIn
|
||||||
try:
|
try:
|
||||||
recipe = Recipe.get_by_slug(session, meal.slug)
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
meal_data = {
|
|
||||||
"slug": recipe.slug,
|
|
||||||
"name": recipe.name,
|
|
||||||
"date": self.startDate + timedelta(days=x),
|
|
||||||
"dateText": meal.dateText,
|
|
||||||
"image": recipe.image,
|
|
||||||
"description": recipe.description,
|
|
||||||
}
|
|
||||||
except:
|
except:
|
||||||
meal_data = {
|
|
||||||
"date": self.startDate + timedelta(days=x),
|
|
||||||
"dateText": meal.dateText,
|
|
||||||
}
|
|
||||||
|
|
||||||
meals.append(Meal(**meal_data))
|
meal_data = MealOut(
|
||||||
|
date=meal_plan_base.startDate + timedelta(days=x),
|
||||||
|
)
|
||||||
|
|
||||||
self.meals = meals
|
meals.append(meal_data)
|
||||||
|
|
||||||
def save_to_db(self, session: Session):
|
return MealPlanProcessed(
|
||||||
db.meals.create(session, self.dict())
|
meals=meals, startDate=meal_plan_base.startDate, endDate=meal_plan_base.endDate
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_all(session: Session) -> List:
|
|
||||||
|
|
||||||
all_meals = [
|
def get_todays_meal(session):
|
||||||
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_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_plan:
|
||||||
|
meal: MealOut
|
||||||
for meal in meal_docs:
|
|
||||||
if meal.date == date.today():
|
if meal.date == date.today():
|
||||||
return meal.slug
|
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
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ from pathlib import Path
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from core.config import IMG_DIR, TEMP_DIR
|
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 sqlalchemy.orm.session import Session
|
||||||
from utils.unzip import unpack_zip
|
from utils.unzip import unpack_zip
|
||||||
|
|
||||||
|
@ -49,32 +50,42 @@ def read_chowdown_file(recipe_file: Path) -> Recipe:
|
||||||
"tags": recipe_data.get("tags").split(","),
|
"tags": recipe_data.get("tags").split(","),
|
||||||
}
|
}
|
||||||
|
|
||||||
new_recipe = Recipe(**reformat_data)
|
print(reformat_data)
|
||||||
|
|
||||||
reformated_list = []
|
reformated_list = []
|
||||||
for instruction in new_recipe.recipeInstructions:
|
for instruction in reformat_data["recipeInstructions"]:
|
||||||
reformated_list.append({"text": instruction})
|
reformated_list.append({"text": instruction})
|
||||||
|
reformat_data["recipeInstructions"] = reformated_list
|
||||||
|
|
||||||
new_recipe.recipeInstructions = reformated_list
|
return Recipe(**reformat_data)
|
||||||
|
|
||||||
return new_recipe
|
|
||||||
|
|
||||||
|
|
||||||
def chowdown_migrate(session: Session, zip_file: Path):
|
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:
|
with temp_dir as dir:
|
||||||
image_dir = TEMP_DIR.joinpath(dir, zip_file.stem, "images")
|
chow_dir = next(Path(dir).iterdir())
|
||||||
recipe_dir = TEMP_DIR.joinpath(dir, zip_file.stem, "_recipes")
|
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 = []
|
failed_recipes = []
|
||||||
successful_recipes = []
|
successful_recipes = []
|
||||||
for recipe in recipe_dir.glob("*.md"):
|
for recipe in recipe_dir.glob("*.md"):
|
||||||
try:
|
try:
|
||||||
new_recipe = read_chowdown_file(recipe)
|
new_recipe = read_chowdown_file(recipe)
|
||||||
new_recipe.save_to_db(session)
|
db.recipes.create(session, new_recipe.dict())
|
||||||
successful_recipes.append(recipe.stem)
|
successful_recipes.append(new_recipe.name)
|
||||||
except:
|
except Exception as inst:
|
||||||
failed_recipes.append(recipe.stem)
|
failed_recipes.append(recipe.stem)
|
||||||
|
|
||||||
failed_images = []
|
failed_images = []
|
||||||
|
@ -82,7 +93,8 @@ def chowdown_migrate(session: Session, zip_file: Path):
|
||||||
try:
|
try:
|
||||||
if not image.stem in failed_recipes:
|
if not image.stem in failed_recipes:
|
||||||
shutil.copy(image, IMG_DIR.joinpath(image.name))
|
shutil.copy(image, IMG_DIR.joinpath(image.name))
|
||||||
except:
|
except Exception as inst:
|
||||||
|
print(inst)
|
||||||
failed_images.append(image.name)
|
failed_images.append(image.name)
|
||||||
|
|
||||||
report = {"successful": successful_recipes, "failed": failed_recipes}
|
report = {"successful": successful_recipes, "failed": failed_recipes}
|
||||||
|
|
|
@ -5,9 +5,10 @@ import zipfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from core.config import IMG_DIR, MIGRATION_DIR, TEMP_DIR
|
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 services.scraper.cleaner import Cleaner
|
||||||
from core.config import IMG_DIR, TEMP_DIR
|
from core.config import IMG_DIR, TEMP_DIR
|
||||||
|
from db.database import db
|
||||||
|
|
||||||
|
|
||||||
def process_selection(selection: Path) -> Path:
|
def process_selection(selection: Path) -> Path:
|
||||||
|
@ -77,7 +78,8 @@ def migrate(session, selection: str):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
recipe = import_recipes(dir)
|
recipe = import_recipes(dir)
|
||||||
recipe.save_to_db(session)
|
db.recipes.create(session, recipe.dict())
|
||||||
|
|
||||||
successful_imports.append(recipe.name)
|
successful_imports.append(recipe.name)
|
||||||
except:
|
except:
|
||||||
logging.error(f"Failed Nextcloud Import: {dir.name}")
|
logging.error(f"Failed Nextcloud Import: {dir.name}")
|
||||||
|
|
|
@ -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)
|
|
|
@ -48,6 +48,8 @@ class Cleaner:
|
||||||
recipe_data["slug"] = slugify(recipe_data.get("name"))
|
recipe_data["slug"] = slugify(recipe_data.get("name"))
|
||||||
recipe_data["orgURL"] = url
|
recipe_data["orgURL"] = url
|
||||||
|
|
||||||
|
print(recipe_data["recipeIngredient"])
|
||||||
|
|
||||||
return recipe_data
|
return recipe_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -6,7 +6,7 @@ import scrape_schema_recipe
|
||||||
from core.config import DEBUG_DIR
|
from core.config import DEBUG_DIR
|
||||||
from fastapi.logger import logger
|
from fastapi.logger import logger
|
||||||
from services.image_services import scrape_image
|
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 import open_graph
|
||||||
from services.scraper.cleaner import Cleaner
|
from services.scraper.cleaner import Cleaner
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from core.config import TEMP_DIR
|
|
||||||
import pytest
|
import pytest
|
||||||
from core.config import TEMP_DIR
|
from core.config import TEMP_DIR
|
||||||
|
from schema.recipe import Recipe
|
||||||
from services.image_services import IMG_DIR
|
from services.image_services import IMG_DIR
|
||||||
from services.migrations.nextcloud import (
|
from services.migrations.nextcloud import (
|
||||||
cleanup,
|
cleanup,
|
||||||
|
@ -9,7 +10,6 @@ from services.migrations.nextcloud import (
|
||||||
prep,
|
prep,
|
||||||
process_selection,
|
process_selection,
|
||||||
)
|
)
|
||||||
from services.recipe_services import Recipe
|
|
||||||
from tests.test_config import TEST_NEXTCLOUD_DIR
|
from tests.test_config import TEST_NEXTCLOUD_DIR
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
CWD = Path(__file__).parent
|
||||||
|
|
|
@ -19,12 +19,10 @@ def get_meal_plan_template(first=None, second=None):
|
||||||
{
|
{
|
||||||
"slug": first,
|
"slug": first,
|
||||||
"date": "2021-1-17",
|
"date": "2021-1-17",
|
||||||
"dateText": "Monday, January 18, 2021",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": second,
|
"slug": second,
|
||||||
"date": "2021-1-18",
|
"date": "2021-1-18",
|
||||||
"dateText": "Tueday, January 19, 2021",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from core.config import TEMP_DIR
|
|
||||||
import pytest
|
import pytest
|
||||||
from core.config import TEMP_DIR
|
from core.config import TEMP_DIR
|
||||||
|
from schema.recipe import Recipe
|
||||||
from services.image_services import IMG_DIR
|
from services.image_services import IMG_DIR
|
||||||
from services.migrations.nextcloud import (
|
from services.migrations.nextcloud import (
|
||||||
cleanup,
|
cleanup,
|
||||||
|
@ -9,7 +10,6 @@ from services.migrations.nextcloud import (
|
||||||
prep,
|
prep,
|
||||||
process_selection,
|
process_selection,
|
||||||
)
|
)
|
||||||
from services.recipe_services import Recipe
|
|
||||||
from tests.test_config import TEST_NEXTCLOUD_DIR
|
from tests.test_config import TEST_NEXTCLOUD_DIR
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
CWD = Path(__file__).parent
|
||||||
|
|
|
@ -4,8 +4,7 @@ import requests
|
||||||
from db.database import db
|
from db.database import db
|
||||||
from db.db_setup import create_session
|
from db.db_setup import create_session
|
||||||
from schema.settings import SiteSettings
|
from schema.settings import SiteSettings
|
||||||
from services.meal_services import MealPlan
|
from services.meal_services import get_todays_meal
|
||||||
from services.recipe_services import Recipe
|
|
||||||
|
|
||||||
|
|
||||||
def post_webhooks():
|
def post_webhooks():
|
||||||
|
@ -14,7 +13,8 @@ def post_webhooks():
|
||||||
all_settings = SiteSettings(**all_settings)
|
all_settings = SiteSettings(**all_settings)
|
||||||
|
|
||||||
if all_settings.webhooks.enabled:
|
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
|
urls = all_settings.webhooks.webhookURLs
|
||||||
|
|
||||||
for url in urls:
|
for url in urls:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue