mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
database abstraction
This commit is contained in:
parent
dd3b9666a5
commit
275ab47857
32 changed files with 494 additions and 216 deletions
|
@ -21,7 +21,7 @@ new Vue({
|
|||
}).$mount("#app");
|
||||
|
||||
// Truncate
|
||||
let filter = function (text, length, clamp) {
|
||||
let filter = function(text, length, clamp) {
|
||||
clamp = clamp || "...";
|
||||
let node = document.createElement("div");
|
||||
node.innerHTML = text;
|
||||
|
@ -32,5 +32,3 @@ let filter = function (text, length, clamp) {
|
|||
Vue.filter("truncate", filter);
|
||||
|
||||
export { router };
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from pathlib import Path
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
import startup
|
||||
from global_scheduler import start_scheduler
|
||||
from routes import (
|
||||
backup_routes,
|
||||
meal_routes,
|
||||
|
@ -14,10 +13,12 @@ from routes import (
|
|||
static_routes,
|
||||
user_routes,
|
||||
)
|
||||
from services.settings_services import Colors, SiteTheme
|
||||
from settings import PORT, PRODUCTION, WEB_PATH, docs_url, redoc_url
|
||||
from utils.logger import logger
|
||||
|
||||
startup.pre_start()
|
||||
# start_scheduler()
|
||||
|
||||
app = FastAPI(
|
||||
title="Mealie",
|
||||
|
@ -48,6 +49,10 @@ def invalid_api():
|
|||
app.include_router(static_routes.router)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Generate API Documentation
|
||||
if not PRODUCTION:
|
||||
startup.generate_api_docs(app)
|
||||
|
|
45
mealie/data/db/recipes.json
Normal file
45
mealie/data/db/recipes.json
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"_default": {
|
||||
"1": {
|
||||
"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\u00bd lb. skinless, boneless chicken thighs (4\u20138 depending on size)",
|
||||
"Kosher salt, freshly ground pepper",
|
||||
"3 Tbsp. unsalted butter, divided",
|
||||
"2 large or 3 medium leeks, white and pale green parts only, halved lengthwise, thinly sliced",
|
||||
"Zest and juice of 1 lemon, divided",
|
||||
"1\u00bd cups long-grain white rice, rinsed until water runs clear",
|
||||
"2\u00be cups low-sodium chicken broth",
|
||||
"1 oil-packed anchovy fillet",
|
||||
"2 garlic cloves",
|
||||
"1 Tbsp. drained capers",
|
||||
"Crushed red pepper flakes",
|
||||
"1 cup tender herb leaves (such as parsley, cilantro, and/or mint)",
|
||||
"4\u20135 Tbsp. extra-virgin olive oil"
|
||||
],
|
||||
"recipeInstructions": [
|
||||
{
|
||||
"text": "Season chicken with salt and pepper. Melt 2 Tbsp. butter in a large high-sided skillet over medium-high heat. Add leeks and half of lemon zest, season with salt and pepper, and mix to coat leeks in butter. Reduce heat to medium-low, cover, and cook, stirring occasionally, until leeks are somewhat tender, about 5 minutes. Remove lid, increase heat to medium-high, and cook, stirring occasionally, until tender and just starting to take on color, about 3 minutes. Add rice and cook, stirring often, 3 minutes, then add broth, scraping up any browned bits. Tuck short sides of each chicken thigh underneath so they are touching and nestle seam side down into rice mixture. Bring to a simmer. Cover, reduce heat to medium-low, and cook until rice is tender and chicken is cooked through, about 20 minutes. Remove from heat. Cut remaining 1 Tbsp. butter into small pieces and scatter over mixture. Re-cover and let sit 10 minutes."
|
||||
},
|
||||
{
|
||||
"text": "Meanwhile, pulse anchovy, garlic, capers, a few pinches of red pepper flakes, and remaining lemon zest in a food processor until finely chopped. Add herbs; process until a paste forms. With motor running, gradually stream in oil until loosened to a thick sauce. Add half of lemon juice; season salsa verde with salt."
|
||||
},
|
||||
{
|
||||
"text": "Drizzle remaining lemon juice over chicken and rice. Serve with salsa verde."
|
||||
}
|
||||
],
|
||||
"totalTime": "None",
|
||||
"slug": "chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"dateAdded": null,
|
||||
"notes": [],
|
||||
"rating": null,
|
||||
"orgURL": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"extras": {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,95 @@
|
|||
{
|
||||
"name": "Carottes Rapp\u00e9s with Rice and Sunflower Seeds \u2014 FEED THE SWIMMERS",
|
||||
"description": " Carottes R\u00e2p\u00e9es with Rice and Sunflower Seeds thanks to @think_rice and @thefeedfeed. Carottes R\u00e2p\u00e9es is a classic French Salad found ready to go (think picnic) at every charcuterie and on most cafe menus. This is one of those insanely simple salads that explode with flavor! The carrots ar",
|
||||
"image": "carottes-rappes-with-rice-and-sunflower-seeds-feed-the-swimmers.JPG?format=1500w",
|
||||
"recipeYield": "",
|
||||
"@context": "http://schema.org",
|
||||
"@type": "Recipe",
|
||||
"articleBody": "\u201cAfter a draining day juggling work, homeschooling, and urging children to stop using their masks as slingshots, the ideal food for me isn\u2019t perfectly prepared food that\u2019s been tweezered into position, but a meal that\u2019s simply comforting,\u201d writes the Smitten Kitchen\u2019s Deb Perelman. Right now, it\u2019s this deeply cozy pot of tender chicken thighs, jammy leeks, and broth-soaked rice.",
|
||||
"alternativeHeadline": "This one-skillet dinner gets deep oniony flavor from lots of leeks cooked down to jammy tenderness.",
|
||||
"dateModified": "2021-01-10 15:20:51.422000",
|
||||
"datePublished": "2020-08-18 04:00:00",
|
||||
"keywords": [
|
||||
"recipes",
|
||||
"chicken recipes",
|
||||
"kosher salt",
|
||||
"black pepper",
|
||||
"butter",
|
||||
"leek",
|
||||
"lemon zest",
|
||||
"rice",
|
||||
"chicken broth",
|
||||
"anchovy",
|
||||
"garlic",
|
||||
"capers",
|
||||
"herb",
|
||||
"olive oil",
|
||||
"healthyish",
|
||||
"web"
|
||||
],
|
||||
"thumbnailUrl": "https://assets.bonappetit.com/photos/5f29796456f43685a49327fb/1:1/w_1125,h_1125,c_limit/Chicken-and-Rice-With-Leeks-Salsa-Verde-01.jpg",
|
||||
"publisher": {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "Bon App\u00e9tit",
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": "https://www.bonappetit.com/verso/static/bon-appetit/assets/logo-seo.328de564b950e3d5d1fbe3e42f065290ca1d3844.png",
|
||||
"width": "479px",
|
||||
"height": "100px"
|
||||
},
|
||||
"url": "https://www.bonappetit.com"
|
||||
},
|
||||
"isPartOf": {
|
||||
"@type": [
|
||||
"CreativeWork",
|
||||
"Product"
|
||||
],
|
||||
"name": "Bon App\u00e9tit"
|
||||
},
|
||||
"isAccessibleForFree": true,
|
||||
"author": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"name": "Deb Perelman",
|
||||
"sameAs": "https://bon-appetit.com/contributor/deb-perelman/"
|
||||
}
|
||||
],
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": 4.02,
|
||||
"ratingCount": 48
|
||||
},
|
||||
"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",
|
||||
"headline": "Chicken and Rice With Leeks and Salsa Verde",
|
||||
"name": "Chicken and Rice With Leeks and Salsa Verde",
|
||||
"recipeIngredient": [
|
||||
"Could not detect ingredients"
|
||||
"1\u00bd lb. skinless, boneless chicken thighs (4\u20138 depending on size)",
|
||||
"Kosher salt, freshly ground pepper",
|
||||
"3 Tbsp. unsalted butter, divided",
|
||||
"2 large or 3 medium leeks, white and pale green parts only, halved lengthwise, thinly sliced",
|
||||
"Zest and juice of 1 lemon, divided",
|
||||
"1\u00bd cups long-grain white rice, rinsed until water runs clear",
|
||||
"2\u00be cups low-sodium chicken broth",
|
||||
"1 oil-packed anchovy fillet",
|
||||
"2 garlic cloves",
|
||||
"1 Tbsp. drained capers",
|
||||
"Crushed red pepper flakes",
|
||||
"1 cup tender herb leaves (such as parsley, cilantro, and/or mint)",
|
||||
"4\u20135 Tbsp. extra-virgin olive oil"
|
||||
],
|
||||
"recipeInstructions": [
|
||||
{
|
||||
"text": "Could not detect instructions"
|
||||
"text": "Season chicken with salt and pepper. Melt 2 Tbsp. butter in a large high-sided skillet over medium-high heat. Add leeks and half of lemon zest, season with salt and pepper, and mix to coat leeks in butter. Reduce heat to medium-low, cover, and cook, stirring occasionally, until leeks are somewhat tender, about 5 minutes. Remove lid, increase heat to medium-high, and cook, stirring occasionally, until tender and just starting to take on color, about 3 minutes. Add rice and cook, stirring often, 3 minutes, then add broth, scraping up any browned bits. Tuck short sides of each chicken thigh underneath so they are touching and nestle seam side down into rice mixture. Bring to a simmer. Cover, reduce heat to medium-low, and cook until rice is tender and chicken is cooked through, about 20 minutes. Remove from heat. Cut remaining 1 Tbsp. butter into small pieces and scatter over mixture. Re-cover and let sit 10 minutes."
|
||||
},
|
||||
{
|
||||
"text": "Meanwhile, pulse anchovy, garlic, capers, a few pinches of red pepper flakes, and remaining lemon zest in a food processor until finely chopped. Add herbs; process until a paste forms. With motor running, gradually stream in oil until loosened to a thick sauce. Add half of lemon juice; season salsa verde with salt."
|
||||
},
|
||||
{
|
||||
"text": "Drizzle remaining lemon juice over chicken and rice. Serve with salsa verde."
|
||||
}
|
||||
],
|
||||
"slug": "carottes-rappes-with-rice-and-sunflower-seeds-feed-the-swimmers",
|
||||
"orgURL": "https://www.feedtheswimmers.com/blog/2019/6/5/carottes-rapps-with-rice-and-sunflower-seeds",
|
||||
"recipeYield": "4 Servings",
|
||||
"url": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"slug": "chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"orgURL": "https://www.bonappetit.com/recipe/chicken-and-rice-with-leeks-and-salsa-verde",
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"dateAdded": null,
|
||||
|
|
BIN
mealie/data/mealie.sqlite
Normal file
BIN
mealie/data/mealie.sqlite
Normal file
Binary file not shown.
108
mealie/db/db_setup.py
Normal file
108
mealie/db/db_setup.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
import mongoengine
|
||||
from settings import USE_MONGO, USE_TINYDB
|
||||
|
||||
from db.mongo.meal_models import MealDocument, MealPlanDocument
|
||||
from db.mongo.recipe_models import RecipeDocument
|
||||
from db.mongo.settings_models import SiteSettingsDocument, SiteThemeDocument
|
||||
from db.tinydb.baseclass import StoreBase
|
||||
|
||||
"""
|
||||
Common Actions:
|
||||
- [ ] Create
|
||||
- [ ] Read
|
||||
- [ ] Update
|
||||
- [ ] Delete
|
||||
- [ ] Unpack Document Mongo Mostly.
|
||||
- [ ] Query by Primary Key
|
||||
|
||||
Recipe Actions
|
||||
- [ ] Query by Category
|
||||
- [ ] Query by dateAdded
|
||||
|
||||
"""
|
||||
|
||||
if USE_TINYDB:
|
||||
from db.tinydb.tinydb_setup import TinyDatabase
|
||||
|
||||
tiny_db = TinyDatabase()
|
||||
|
||||
elif USE_MONGO:
|
||||
from db.mongo.mongo_setup import global_init as mongo_global_init
|
||||
|
||||
mongo_global_init()
|
||||
|
||||
|
||||
class BaseDocument:
|
||||
def __init__(self) -> None:
|
||||
self.primary_key: str
|
||||
self.store: StoreBase
|
||||
self._document: mongoengine.Document
|
||||
|
||||
def get(self, match_value: str, match_key: str = None, limit=1) -> dict:
|
||||
if USE_MONGO:
|
||||
return self._document.objects.get(match_key=match_value).limit(limit)
|
||||
elif USE_TINYDB:
|
||||
return self.store.get(match_value, match_key)
|
||||
|
||||
def save_new(self, document: dict) -> str:
|
||||
if USE_MONGO:
|
||||
self._document = self._document(**document)
|
||||
self._document.save()
|
||||
return self._document.slug
|
||||
elif USE_TINYDB:
|
||||
return self.store.save(document)
|
||||
|
||||
def update(self) -> dict:
|
||||
if USE_MONGO:
|
||||
pass
|
||||
elif USE_TINYDB:
|
||||
pass
|
||||
|
||||
def delete(self) -> dict:
|
||||
if USE_MONGO:
|
||||
pass
|
||||
elif USE_TINYDB:
|
||||
pass
|
||||
|
||||
|
||||
class Database:
|
||||
def __init__(self) -> None:
|
||||
self.recipes = self._Recipes()
|
||||
self.meals = self._Meals()
|
||||
self.settings = self._Settings()
|
||||
self.themes = self._Themes()
|
||||
|
||||
class _Recipes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "slug"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.recipes
|
||||
self._document = RecipeDocument
|
||||
pass
|
||||
|
||||
class _Meals(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "uid"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.meals
|
||||
self.document = MealPlanDocument
|
||||
pass
|
||||
|
||||
class _Settings(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "name"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.settings
|
||||
self.document = SiteSettingsDocument
|
||||
pass
|
||||
|
||||
class _Themes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "name"
|
||||
if USE_TINYDB:
|
||||
self.store = tiny_db.themes
|
||||
self.document = SiteThemeDocument
|
||||
pass
|
||||
|
||||
|
||||
db = Database()
|
|
@ -1,5 +1,4 @@
|
|||
import datetime
|
||||
import uuid
|
||||
|
||||
import mongoengine
|
||||
|
||||
|
@ -19,7 +18,7 @@ class RecipeDocument(mongoengine.Document):
|
|||
slug = mongoengine.StringField(required=True, unique=True)
|
||||
categories = mongoengine.ListField(default=[])
|
||||
tags = mongoengine.ListField(default=[])
|
||||
dateAdded = mongoengine.DateTimeField(binary=True, default=datetime.date.today())
|
||||
dateAdded = mongoengine.DateTimeField(binary=True, default=datetime.date.today)
|
||||
notes = mongoengine.ListField(default=[])
|
||||
rating = mongoengine.IntField(required=True, default=0)
|
||||
orgURL = mongoengine.URLField(required=False)
|
6
mealie/db/mongo/user_models.py
Normal file
6
mealie/db/mongo/user_models.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
# import mongoengine
|
||||
|
||||
# class User(mongoengine.Document):
|
||||
# username: mongoengine.EmailField()
|
||||
# password: mongoengine.ReferenceField
|
|
@ -1,26 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from settings import SQLITE
|
||||
|
||||
|
||||
factory = None
|
||||
|
||||
def global_init(db_file: Path):
|
||||
if not SQLITE:
|
||||
pass
|
||||
|
||||
global factory
|
||||
|
||||
if factory:
|
||||
return
|
||||
|
||||
if not db_file or not db_file.strip:
|
||||
raise Exception("You must Specif a db file")
|
||||
|
||||
conn_str = "sqlite:///" + db_file.absolute()
|
||||
|
||||
engine = sa.create_engine(conn_str, echo=False)
|
||||
|
||||
factory = orm.sessionmaker(bind=engine)
|
|
@ -1,4 +0,0 @@
|
|||
import sqlalchemy.ext.declarative as dec
|
||||
|
||||
|
||||
SqlAlchemyBase = dec.declarative_base()
|
|
@ -1,28 +0,0 @@
|
|||
import mongoengine
|
||||
import sqlalchemy as sa
|
||||
from db.sql.model_base import SqlAlchemyBase
|
||||
|
||||
|
||||
class RecipeSQLModel(SqlAlchemyBase):
|
||||
__tablename__ = "recipes"
|
||||
|
||||
name = sa.Column(sa.String, primar_key=True)
|
||||
description = sa.Column(sa.String)
|
||||
image = sa.Column(sa.String)
|
||||
recipeYield = sa.Column(sa.String)
|
||||
recipeIngredient = mongoengine.ListField(required=True, default=[])
|
||||
recipeInstructions = mongoengine.ListField(requiredd=True, default=[])
|
||||
totalTime = sa.Column(sa.String)
|
||||
|
||||
# Mealie Specific
|
||||
slug = sa.Column(sa.String)
|
||||
categories = mongoengine.ListField(default=[])
|
||||
tags = mongoengine.ListField(default=[])
|
||||
dateAdded = mongoengine.DateTimeField(binary=True, default=datetime.date.today())
|
||||
notes = mongoengine.ListField(default=[])
|
||||
rating = sa.Column(sa.Integer)
|
||||
orgURL = sa.Column(sa.String)
|
||||
extras = mongoengine.DictField(required=False)
|
||||
|
||||
def __repr__(self):
|
||||
return f"SQL Entry Recipe {self.name}"
|
64
mealie/db/tinydb/baseclass.py
Normal file
64
mealie/db/tinydb/baseclass.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
from tinydb import Query, TinyDB, where
|
||||
|
||||
|
||||
class StoreBase:
|
||||
def __init__(self) -> None:
|
||||
self.store: TinyDB
|
||||
self.primary_key: str
|
||||
|
||||
def save(self, document: dict) -> str:
|
||||
data = self.store.search(
|
||||
Query()[self.primary_key] == document[self.primary_key]
|
||||
)
|
||||
|
||||
if data != []:
|
||||
raise Exception(
|
||||
f"Cannot save document. Primary Key: {self.primary_key} already exists"
|
||||
)
|
||||
else:
|
||||
return self.store.upsert(
|
||||
document, Query()[self.primary_key] == document[self.primary_key]
|
||||
)
|
||||
|
||||
def delete(self, document_primary_key: str):
|
||||
self.store.remove(where(self.primary_key) == document_primary_key)
|
||||
|
||||
def get(self, value: str, key: str = None, limit: int = None) -> list or dict:
|
||||
"""Retrieves an entry from the database matching the key/value provided.
|
||||
If no key is provided the classes primary_key will be used instead
|
||||
|
||||
Args: \n
|
||||
key (str, optional): Key to match against. Defaults to None. \n
|
||||
value (str, optional): Value the key should contain. Defaults to None. \n
|
||||
limit (int, optional): The limit of returned objects If 1 a single item is returned. If None all objects are returned. Defaults to None. \n
|
||||
|
||||
Returns:
|
||||
list or dict: a List if limit is greater than 1
|
||||
"""
|
||||
|
||||
if key == None:
|
||||
key = self.primary_key
|
||||
|
||||
data = self.store.search(Query()[key] == value)
|
||||
|
||||
if limit == 1:
|
||||
return data[0]
|
||||
if limit:
|
||||
return data[:limit]
|
||||
else:
|
||||
return data
|
||||
|
||||
def update_doc(self, id, document):
|
||||
data: dict = self.get(self.primary_key, id, limit=1)
|
||||
|
||||
if data:
|
||||
if data[self.primary_key] == document[self.primary_key]:
|
||||
data.update(document)
|
||||
else:
|
||||
self.delete(id)
|
||||
return self.save(document)
|
||||
|
||||
elif not data:
|
||||
raise Exception(
|
||||
f"Document'{document[self.primary_key]}' does not exists and cannot be updated. "
|
||||
)
|
32
mealie/db/tinydb/tinydb_setup.py
Normal file
32
mealie/db/tinydb/tinydb_setup.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from db.tinydb.baseclass import StoreBase
|
||||
from settings import TINYDB_DIR
|
||||
|
||||
from tinydb import TinyDB
|
||||
|
||||
|
||||
class TinyDatabase:
|
||||
def __init__(self) -> None:
|
||||
self.recipes = self._Recipes()
|
||||
self.meals = self._Meals()
|
||||
self.settings = self._Settings()
|
||||
self.themes = self._Themes()
|
||||
|
||||
class _Recipes(StoreBase):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "slug"
|
||||
self.store = TinyDB(TINYDB_DIR.joinpath("recipes.json"))
|
||||
|
||||
class _Meals(StoreBase):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "uid"
|
||||
self.store = TinyDB(TINYDB_DIR.joinpath("meals.json"))
|
||||
|
||||
class _Settings(StoreBase):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "name"
|
||||
self.store = TinyDB(TINYDB_DIR.joinpath("settings.json"))
|
||||
|
||||
class _Themes(StoreBase):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "name"
|
||||
self._store = TinyDB(TINYDB_DIR.joinpath("themes.json"))
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
import mongoengine
|
||||
|
||||
class User(mongoengine.Document):
|
||||
username: mongoengine.EmailField()
|
||||
# password: mongoengine.ReferenceField
|
8
mealie/global_scheduler.py
Normal file
8
mealie/global_scheduler.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from services.scheduler_services import Scheduler
|
||||
|
||||
scheduler = None
|
||||
|
||||
def start_scheduler():
|
||||
global scheduler
|
||||
scheduler = Scheduler()
|
||||
scheduler.startup_scheduler()
|
|
@ -1,7 +1,7 @@
|
|||
from fastapi import APIRouter, HTTPException
|
||||
from global_scheduler import scheduler
|
||||
from services.scheduler_services import post_webhooks
|
||||
from services.settings_services import SiteSettings, SiteTheme
|
||||
from startup import scheduler
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
|
14
mealie/scratch.py
Normal file
14
mealie/scratch.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from db.tinydb.tinydb_setup import TinyDatabase
|
||||
|
||||
db = TinyDatabase()
|
||||
|
||||
test_object = {"name": "dan", "job": "programmer"}
|
||||
test_object_2 = {"name": "jennifer", "job": "programmer"}
|
||||
test_object_3 = {"name": "steve", "job": "programmer"}
|
||||
|
||||
|
||||
db.recipes.delete("jennifer")
|
||||
db.recipes.delete("dan")
|
||||
db.recipes.save(test_object)
|
||||
db.recipes.save(test_object_2)
|
||||
db.recipes.update_doc("dan", test_object_3)
|
|
@ -4,7 +4,7 @@ import zipfile
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from db.recipe_models import RecipeDocument
|
||||
from db.mongo.recipe_models import RecipeDocument
|
||||
from jinja2 import Template
|
||||
from utils.logger import logger
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from datetime import date, timedelta
|
|||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from db.meal_models import MealDocument, MealPlanDocument
|
||||
from db.mongo.meal_models import MealDocument, MealPlanDocument
|
||||
from pydantic import BaseModel
|
||||
|
||||
from services.recipe_services import Recipe
|
||||
|
|
|
@ -3,7 +3,8 @@ import json
|
|||
from pathlib import Path
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from db.recipe_models import RecipeDocument
|
||||
from db.db_setup import db
|
||||
from db.mongo.recipe_models import RecipeDocument
|
||||
from pydantic import BaseModel, validator
|
||||
from slugify import slugify
|
||||
|
||||
|
@ -114,10 +115,9 @@ class Recipe(BaseModel):
|
|||
except:
|
||||
pass
|
||||
|
||||
recipeDoc = RecipeDocument(**recipe_dict)
|
||||
recipeDoc.save()
|
||||
recipe_slug = db.recipes.save_new(recipe_dict)
|
||||
|
||||
return recipeDoc.slug
|
||||
return recipe_slug
|
||||
|
||||
@staticmethod
|
||||
def delete(recipe_slug: str) -> str:
|
||||
|
|
|
@ -85,6 +85,12 @@ def process_recipe_data(new_recipe: dict, url=None) -> dict:
|
|||
|
||||
def extract_recipe_from_html(html: str, url: str) -> dict:
|
||||
scraped_recipes: List[dict] = scrape_schema_recipe.loads(html, python_objects=True)
|
||||
|
||||
if not scraped_recipes:
|
||||
scraped_recipes: List[dict] = scrape_schema_recipe.scrape_url(
|
||||
url, python_objects=True
|
||||
)
|
||||
|
||||
if scraped_recipes:
|
||||
new_recipe: dict = scraped_recipes[0]
|
||||
logger.info(f"Recipe Scraped From Web: {new_recipe}")
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import json
|
||||
from typing import List, Optional
|
||||
|
||||
from db.settings_models import (
|
||||
SiteSettingsDocument,
|
||||
SiteThemeDocument,
|
||||
ThemeColorsDocument,
|
||||
WebhooksDocument,
|
||||
)
|
||||
from db.mongo.settings_models import (SiteSettingsDocument, SiteThemeDocument,
|
||||
ThemeColorsDocument, WebhooksDocument)
|
||||
from pydantic import BaseModel
|
||||
from startup import USE_TINYDB
|
||||
from utils.logger import logger
|
||||
|
||||
|
||||
class Webhooks(BaseModel):
|
||||
|
@ -45,12 +43,15 @@ class SiteSettings(BaseModel):
|
|||
|
||||
@staticmethod
|
||||
def get_site_settings():
|
||||
try:
|
||||
document = SiteSettingsDocument.objects.get(name="main")
|
||||
except:
|
||||
webhooks = WebhooksDocument()
|
||||
document = SiteSettingsDocument(name="main", webhooks=webhooks)
|
||||
document.save()
|
||||
if USE_TINYDB:
|
||||
document = tinydb.settings.get("main")
|
||||
else:
|
||||
try:
|
||||
document = SiteSettingsDocument.objects.get(name="main")
|
||||
except:
|
||||
webhooks = WebhooksDocument()
|
||||
document = SiteSettingsDocument(name="main", webhooks=webhooks)
|
||||
document.save()
|
||||
|
||||
return SiteSettings._unpack_doc(document)
|
||||
|
||||
|
@ -95,14 +96,23 @@ class SiteTheme(BaseModel):
|
|||
|
||||
@staticmethod
|
||||
def get_by_name(theme_name):
|
||||
document = SiteThemeDocument.objects.get(name=theme_name)
|
||||
if USE_TINYDB:
|
||||
theme = Query()
|
||||
document = tinydb.themes.search(theme.name == theme_name)
|
||||
else:
|
||||
document = SiteThemeDocument.objects.get(name=theme_name)
|
||||
|
||||
return SiteTheme._unpack_doc(document)
|
||||
|
||||
@staticmethod
|
||||
def _unpack_doc(document):
|
||||
document = json.loads(document.to_json())
|
||||
del document["_id"]
|
||||
theme_colors = SiteTheme(**document)
|
||||
if USE_TINYDB:
|
||||
theme_colors = SiteTheme(**document)
|
||||
else:
|
||||
document = json.loads(document.to_json())
|
||||
del document["_id"]
|
||||
theme_colors = SiteTheme(**document)
|
||||
|
||||
return theme_colors
|
||||
|
||||
@staticmethod
|
||||
|
@ -114,6 +124,15 @@ class SiteTheme(BaseModel):
|
|||
return all_themes
|
||||
|
||||
def save_to_db(self):
|
||||
if USE_TINYDB:
|
||||
self._save_to_tinydb()
|
||||
else:
|
||||
self._save_to_mongo()
|
||||
|
||||
def _save_to_tinydb(self):
|
||||
tinydb.themes.insert(self.dict())
|
||||
|
||||
def _save_to_mongo(self):
|
||||
theme = self.dict()
|
||||
theme["colors"] = ThemeColorsDocument(**theme["colors"])
|
||||
|
||||
|
@ -140,3 +159,37 @@ class SiteTheme(BaseModel):
|
|||
if document:
|
||||
document.delete()
|
||||
return "Document Deleted"
|
||||
|
||||
|
||||
def default_theme_init():
|
||||
default_colors = {
|
||||
"primary": "#E58325",
|
||||
"accent": "#00457A",
|
||||
"secondary": "#973542",
|
||||
"success": "#5AB1BB",
|
||||
"info": "#4990BA",
|
||||
"warning": "#FF4081",
|
||||
"error": "#EF5350",
|
||||
}
|
||||
|
||||
if USE_TINYDB:
|
||||
theme = Query()
|
||||
item = tinydb.themes.search(theme.name == "default")
|
||||
print(item)
|
||||
if item == []:
|
||||
logger.info("Generating Default Theme")
|
||||
colors = Colors(**default_colors)
|
||||
default_theme = SiteTheme(name="default", colors=colors)
|
||||
default_theme.save_to_db()
|
||||
else:
|
||||
try:
|
||||
SiteTheme.get_by_name("default")
|
||||
return "default theme exists"
|
||||
except:
|
||||
logger.info("Generating Default Theme")
|
||||
colors = Colors(**default_colors)
|
||||
default_theme = SiteTheme(name="default", colors=colors)
|
||||
default_theme.save_to_db()
|
||||
|
||||
|
||||
# default_theme_init()
|
||||
|
|
|
@ -17,8 +17,19 @@ BACKUP_DIR = DATA_DIR.joinpath("backups")
|
|||
DEBUG_DIR = DATA_DIR.joinpath("debug")
|
||||
MIGRATION_DIR = DATA_DIR.joinpath("migration")
|
||||
TEMPLATE_DIR = DATA_DIR.joinpath("templates")
|
||||
TINYDB_DIR = DATA_DIR.joinpath("db")
|
||||
TEMP_DIR = DATA_DIR.joinpath("temp")
|
||||
|
||||
REQUIRED_DIRS = [
|
||||
DATA_DIR,
|
||||
IMG_DIR,
|
||||
BACKUP_DIR,
|
||||
DEBUG_DIR,
|
||||
MIGRATION_DIR,
|
||||
TEMPLATE_DIR,
|
||||
TINYDB_DIR,
|
||||
]
|
||||
|
||||
|
||||
# General
|
||||
PRODUCTION = os.environ.get("ENV")
|
||||
|
@ -34,14 +45,13 @@ else:
|
|||
|
||||
|
||||
# DATABASE ENV
|
||||
DATABASE_TYPE = os.getenv("db_type", "mongo") # mongo, sqlite
|
||||
SQLITE = False
|
||||
MONGO = False
|
||||
if DATABASE_TYPE == "sqlite":
|
||||
SQLITE = True
|
||||
SQLITE_DB_FILE = DATA_DIR.joinpath("mealie.sqlite")
|
||||
DATABASE_TYPE = os.getenv("db_type", "mongo") # mongo, tinydb
|
||||
USE_TINYDB = False
|
||||
USE_MONGO = False
|
||||
if DATABASE_TYPE == "tinydb":
|
||||
USE_TINYDB = True
|
||||
elif DATABASE_TYPE == "mongo":
|
||||
MONGO = True
|
||||
USE_MONGO = True
|
||||
else:
|
||||
raise Exception(
|
||||
"Unable to determine database type. Acceptible options are 'mongo' or 'sqlite' "
|
||||
|
|
|
@ -1,61 +1,23 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from db.mongo_setup import global_init as mongo_global_init
|
||||
from db.sql.db_session import global_init as sql_global_init
|
||||
from services.scheduler_services import Scheduler
|
||||
from services.settings_services import Colors, SiteTheme
|
||||
from settings import DATA_DIR, MONGO, SQLITE
|
||||
from utils.logger import logger
|
||||
from db.tinydb.tinydb_setup import TinyDatabase
|
||||
from settings import REQUIRED_DIRS, USE_MONGO, USE_TINYDB
|
||||
|
||||
CWD = Path(__file__).parent
|
||||
|
||||
scheduler = None
|
||||
|
||||
|
||||
|
||||
|
||||
def pre_start():
|
||||
if SQLITE:
|
||||
from settings import SQLITE_DB_FILE
|
||||
|
||||
sql_global_init(SQLITE_DB_FILE)
|
||||
elif MONGO:
|
||||
mongo_global_init()
|
||||
|
||||
global scheduler
|
||||
scheduler = Scheduler()
|
||||
scheduler.startup_scheduler()
|
||||
|
||||
ensure_dirs()
|
||||
generate_default_theme()
|
||||
|
||||
|
||||
def ensure_dirs():
|
||||
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("img").mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("backups").mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("templates").mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("debug").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def generate_default_theme():
|
||||
default_colors = {
|
||||
"primary": "#E58325",
|
||||
"accent": "#00457A",
|
||||
"secondary": "#973542",
|
||||
"success": "#5AB1BB",
|
||||
"info": "#4990BA",
|
||||
"warning": "#FF4081",
|
||||
"error": "#EF5350",
|
||||
}
|
||||
|
||||
try:
|
||||
SiteTheme.get_by_name("default")
|
||||
return "default theme exists"
|
||||
except:
|
||||
logger.info("Generating Default Theme")
|
||||
colors = Colors(**default_colors)
|
||||
default_theme = SiteTheme(name="default", colors=colors)
|
||||
default_theme.save_to_db()
|
||||
for dir in REQUIRED_DIRS:
|
||||
dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
"""Script to export the ReDoc documentation page into a standalone HTML file."""
|
||||
|
|
|
@ -1,79 +1,25 @@
|
|||
aiofiles==0.5.0
|
||||
aniso8601==7.0.0
|
||||
appdirs==1.4.4
|
||||
APScheduler==3.6.3
|
||||
astroid==2.4.2
|
||||
apscheduler==3.6.3
|
||||
async-exit-stack==1.0.1
|
||||
async-generator==1.10
|
||||
attrs==20.3.0
|
||||
beautifulsoup4==4.9.1
|
||||
black==20.8b1
|
||||
certifi==2020.6.20
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
colorama==0.4.3
|
||||
decorator==4.4.2
|
||||
dnspython==2.0.0
|
||||
email-validator==1.1.1
|
||||
extruct==0.10.0
|
||||
fastapi==0.61.1
|
||||
fastapi-login==1.5.1
|
||||
gitpython==3.1.11
|
||||
graphene==2.1.8
|
||||
graphql-core==2.3.2
|
||||
graphql-relay==2.0.1
|
||||
h11==0.9.0
|
||||
html-text==0.5.2
|
||||
html5lib==1.1
|
||||
httptools==0.1.1
|
||||
idna==2.10
|
||||
iniconfig==1.1.1
|
||||
isodate==0.6.0
|
||||
isort==5.4.2
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
jstyleson==0.0.2
|
||||
lazy-object-proxy==1.4.3
|
||||
lxml==4.5.2
|
||||
MarkupSafe==1.1.1
|
||||
mccabe==0.6.1
|
||||
mf2py==1.1.2
|
||||
mongoengine==0.21.0
|
||||
mypy-extensions==0.4.3
|
||||
packaging==20.8
|
||||
pathspec==0.8.0
|
||||
pluggy==0.13.1
|
||||
promise==2.3
|
||||
py==1.10.0
|
||||
pydantic==1.6.1
|
||||
nltk==3.5
|
||||
pdfkit==0.6.1
|
||||
pip-chill==1.0.0
|
||||
pylint==2.6.0
|
||||
pymongo==3.11.1
|
||||
pyparsing==2.4.7
|
||||
pytest==6.2.1
|
||||
python-dateutil==2.8.1
|
||||
python-dotenv==0.15.0
|
||||
python-multipart==0.0.5
|
||||
python-slugify==4.0.1
|
||||
pytz==2020.4
|
||||
PyYAML==5.3.1
|
||||
rdflib==4.2.2
|
||||
rdflib-jsonld==0.5.0
|
||||
regex==2020.11.13
|
||||
requests==2.24.0
|
||||
Rx==1.6.1
|
||||
scrape-schema-recipe==0.1.1
|
||||
six==1.15.0
|
||||
soupsieve==2.0.1
|
||||
starlette==0.13.6
|
||||
text-unidecode==1.3
|
||||
toml==0.10.1
|
||||
typed-ast==1.4.1
|
||||
typing-extensions==3.7.4.3
|
||||
tzlocal==2.1
|
||||
sqlalchemy==1.3.22
|
||||
ujson==3.1.0
|
||||
urllib3==1.25.10
|
||||
uvicorn==0.11.8
|
||||
uvloop==0.14.0
|
||||
validators==0.18.0
|
||||
w3lib==1.22.0
|
||||
webencodings==0.5.1
|
||||
websockets==8.1
|
||||
wrapt==1.12.1
|
||||
|
|
|
@ -5,6 +5,7 @@ APScheduler==3.6.3
|
|||
astroid==2.4.2
|
||||
async-exit-stack==1.0.1
|
||||
async-generator==1.10
|
||||
attrs==20.3.0
|
||||
beautifulsoup4==4.9.1
|
||||
black==20.8b1
|
||||
certifi==2020.6.20
|
||||
|
@ -16,6 +17,7 @@ dnspython==2.0.0
|
|||
email-validator==1.1.1
|
||||
extruct==0.10.0
|
||||
fastapi==0.61.1
|
||||
fastapi-login==1.5.1
|
||||
future==0.18.2
|
||||
gitdb==4.0.5
|
||||
GitPython==3.1.11
|
||||
|
@ -27,6 +29,7 @@ html-text==0.5.2
|
|||
html5lib==1.1
|
||||
httptools==0.1.1
|
||||
idna==2.10
|
||||
iniconfig==1.1.1
|
||||
isodate==0.6.0
|
||||
isort==5.4.2
|
||||
itsdangerous==1.1.0
|
||||
|
@ -36,7 +39,7 @@ jstyleson==0.0.2
|
|||
lazy-object-proxy==1.4.3
|
||||
livereload==2.6.3
|
||||
lunr==0.5.8
|
||||
lxml>=4.6.2
|
||||
lxml==4.6.2
|
||||
Markdown==3.3.3
|
||||
MarkupSafe==1.1.1
|
||||
mccabe==0.6.1
|
||||
|
@ -47,10 +50,14 @@ mkdocs-material-extensions==1.0.1
|
|||
mongoengine==0.21.0
|
||||
mypy-extensions==0.4.3
|
||||
nltk==3.5
|
||||
packaging==20.8
|
||||
passlib==1.7.4
|
||||
pathspec==0.8.0
|
||||
pdfkit==0.6.1
|
||||
pip-chill==1.0.0
|
||||
pluggy==0.13.1
|
||||
promise==2.3
|
||||
py==1.10.0
|
||||
pydantic==1.6.1
|
||||
Pygments==2.7.3
|
||||
PyJWT==1.7.1
|
||||
|
@ -58,6 +65,7 @@ pylint==2.6.0
|
|||
pymdown-extensions==8.0.1
|
||||
pymongo==3.11.1
|
||||
pyparsing==2.4.7
|
||||
pytest==6.2.1
|
||||
python-dateutil==2.8.1
|
||||
python-dotenv==0.15.0
|
||||
python-multipart==0.0.5
|
||||
|
@ -73,6 +81,7 @@ scrape-schema-recipe==0.1.1
|
|||
six==1.15.0
|
||||
smmap==3.0.4
|
||||
soupsieve==2.0.1
|
||||
SQLAlchemy==1.3.22
|
||||
starlette==0.13.6
|
||||
text-unidecode==1.3
|
||||
toml==0.10.1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue