mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 06:23:34 -07:00
site settings refactor
This commit is contained in:
parent
9a5fc25cb4
commit
e5bd261a3c
16 changed files with 149 additions and 156 deletions
|
@ -1,5 +1,6 @@
|
|||
from schema.meal import MealPlanInDB
|
||||
from schema.recipe import Recipe
|
||||
from schema.settings import SiteSettings as SiteSettingsSchema
|
||||
from schema.sign_up import SignUpOut
|
||||
from schema.user import GroupInDB, UserInDB
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
@ -8,7 +9,7 @@ from db.db_base import BaseDocument
|
|||
from db.models.group import Group
|
||||
from db.models.mealplan import MealPlanModel
|
||||
from db.models.recipe.recipe import Category, RecipeModel, Tag
|
||||
from db.models.settings import SiteSettingsModel
|
||||
from db.models.settings import SiteSettings
|
||||
from db.models.sign_up import SignUp
|
||||
from db.models.theme import SiteThemeModel
|
||||
from db.models.users import User
|
||||
|
@ -53,9 +54,10 @@ class _Meals(BaseDocument):
|
|||
|
||||
class _Settings(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "name"
|
||||
self.sql_model = SiteSettingsModel
|
||||
self.orm_mode = False
|
||||
self.primary_key = "id"
|
||||
self.sql_model = SiteSettings
|
||||
self.orm_mode = True
|
||||
self.schema = SiteSettingsSchema
|
||||
|
||||
|
||||
class _Themes(BaseDocument):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from core.config import DEFAULT_GROUP
|
||||
from core.security import get_password_hash
|
||||
from fastapi.logger import logger
|
||||
from schema.settings import SiteSettings, Webhooks
|
||||
from schema.settings import SiteSettings
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
|
@ -43,13 +43,9 @@ def default_theme_init(session: Session):
|
|||
|
||||
|
||||
def default_settings_init(session: Session):
|
||||
try:
|
||||
webhooks = Webhooks()
|
||||
default_entry = SiteSettings(name="main", webhooks=webhooks)
|
||||
document = db.settings.create(session, default_entry.dict())
|
||||
logger.info(f"Created Site Settings: \n {document}")
|
||||
except:
|
||||
pass
|
||||
data = {"language": "en", "sidebar": {"categories": []}}
|
||||
document = db.settings.create(session, data)
|
||||
logger.info(f"Created Site Settings: \n {document}")
|
||||
|
||||
|
||||
def default_group_init(session: Session):
|
||||
|
|
|
@ -2,8 +2,8 @@ import sqlalchemy as sa
|
|||
import sqlalchemy.orm as orm
|
||||
from core.config import DEFAULT_GROUP
|
||||
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
from db.models.recipe.category import group2categories
|
||||
from fastapi.logger import logger
|
||||
from slugify import slugify
|
||||
|
||||
|
||||
class Group(SqlAlchemyBase, BaseMixins):
|
||||
|
@ -12,6 +12,8 @@ class Group(SqlAlchemyBase, BaseMixins):
|
|||
name = sa.Column(sa.String, index=True, nullable=False, unique=True)
|
||||
users = orm.relationship("User", back_populates="group")
|
||||
mealplans = orm.relationship("MealPlanModel", back_populates="group")
|
||||
categories = orm.relationship("Category", secondary=group2categories)
|
||||
webhooks = orm.relationship("WebHookModel", uselist=False, cascade="all, delete")
|
||||
|
||||
def __init__(self, name, session=None) -> None:
|
||||
self.name = name
|
||||
|
@ -29,3 +31,37 @@ class Group(SqlAlchemyBase, BaseMixins):
|
|||
except:
|
||||
logger.info("Category doesn't exists, creating category")
|
||||
return Group(name=name)
|
||||
|
||||
|
||||
class WebHookModel(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "webhook_settings"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("groups.id"))
|
||||
webhookURLs = orm.relationship(
|
||||
"WebhookURLModel", uselist=True, cascade="all, delete"
|
||||
)
|
||||
webhookTime = sa.Column(sa.String, default="00:00")
|
||||
enabled = sa.Column(sa.Boolean, default=False)
|
||||
|
||||
def __init__(
|
||||
self, webhookURLs: list, webhookTime: str, enabled: bool = False, session=None
|
||||
) -> None:
|
||||
|
||||
self.webhookURLs = [WebhookURLModel(url=x) for x in webhookURLs]
|
||||
self.webhookTime = webhookTime
|
||||
self.enabled = enabled
|
||||
|
||||
def update(
|
||||
self, session, webhookURLs: list, webhookTime: str, enabled: bool
|
||||
) -> None:
|
||||
|
||||
self._sql_remove_list(session, [WebhookURLModel], self.id)
|
||||
|
||||
self.__init__(webhookURLs, webhookTime, enabled)
|
||||
|
||||
|
||||
class WebhookURLModel(SqlAlchemyBase):
|
||||
__tablename__ = "webhook_urls"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
url = sa.Column(sa.String)
|
||||
parent_id = sa.Column(sa.Integer, sa.ForeignKey("webhook_settings.id"))
|
||||
|
|
|
@ -8,9 +8,8 @@ SqlAlchemyBase = dec.declarative_base()
|
|||
class BaseMixins:
|
||||
@staticmethod
|
||||
def _sql_remove_list(session, list_of_tables: list, parent_id):
|
||||
|
||||
for table in list_of_tables:
|
||||
session.query(table).filter_by(parent_id=parent_id).delete()
|
||||
session.query(table).filter(parent_id == parent_id).delete()
|
||||
|
||||
@staticmethod
|
||||
def _flatten_dict(list_of_dict: List[dict]):
|
||||
|
|
|
@ -5,6 +5,20 @@ from fastapi.logger import logger
|
|||
from slugify import slugify
|
||||
from sqlalchemy.orm import validates
|
||||
|
||||
sidebar2categories = sa.Table(
|
||||
"sidebar2categories",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_sidebar.id")),
|
||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
||||
)
|
||||
|
||||
group2categories = sa.Table(
|
||||
"group2categories",
|
||||
SqlAlchemyBase.metadata,
|
||||
sa.Column("group_id", sa.Integer, sa.ForeignKey("groups.id")),
|
||||
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
|
||||
)
|
||||
|
||||
recipes2categories = sa.Table(
|
||||
"recipes2categories",
|
||||
SqlAlchemyBase.metadata,
|
||||
|
@ -45,21 +59,3 @@ class Category(SqlAlchemyBase):
|
|||
except:
|
||||
logger.info("Category doesn't exists, creating category")
|
||||
return Category(name=name)
|
||||
|
||||
def to_str(self):
|
||||
return self.name
|
||||
|
||||
def dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"slug": self.slug,
|
||||
"name": self.name,
|
||||
"recipes": [x.dict() for x in self.recipes],
|
||||
}
|
||||
|
||||
def dict_no_recipes(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"slug": self.slug,
|
||||
"name": self.name,
|
||||
}
|
||||
|
|
|
@ -1,93 +1,42 @@
|
|||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
from db.models.recipe.category import Category, sidebar2categories
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
class SiteSettingsModel(SqlAlchemyBase, BaseMixins):
|
||||
class Sidebar(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "site_sidebar"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.Integer, sa.ForeignKey("site_settings.id"))
|
||||
categories = orm.relationship("Category", secondary=sidebar2categories, cascade="delete")
|
||||
|
||||
def __init__(self, session: Session, sidebar: dict) -> None:
|
||||
categories = sidebar.get("categories")
|
||||
new_categories = []
|
||||
if not categories:
|
||||
return None
|
||||
for cat in categories:
|
||||
slug = cat.get("slug")
|
||||
cat_in_db = session.query(Category).filter(Category.slug == slug).one()
|
||||
new_categories.append(cat_in_db)
|
||||
|
||||
self.categories = new_categories
|
||||
|
||||
|
||||
class SiteSettings(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "site_settings"
|
||||
name = sa.Column(sa.String, primary_key=True)
|
||||
planCategories = orm.relationship(
|
||||
"MealCategory", uselist=True, cascade="all, delete"
|
||||
)
|
||||
webhooks = orm.relationship("WebHookModel", uselist=False, cascade="all, delete")
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
language = sa.Column(sa.String)
|
||||
sidebar = orm.relationship("Sidebar", uselist=False, cascade="all")
|
||||
|
||||
def __init__(
|
||||
self, name: str = None, webhooks: dict = None, planCategories=[], session=None
|
||||
self, session=None, language="en", sidebar: list = {"categories": []}
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.planCategories = [MealCategory(cat) for cat in planCategories]
|
||||
self.webhooks = WebHookModel(**webhooks)
|
||||
self._sql_remove_list(session, [Sidebar], self.id)
|
||||
|
||||
def update(self, session, name, webhooks: dict, planCategories=[]) -> dict:
|
||||
self.language = language
|
||||
self.sidebar = Sidebar(session, sidebar)
|
||||
|
||||
self._sql_remove_list(session, [MealCategory], self.name)
|
||||
self.name = name
|
||||
self.planCategories = [MealCategory(x) for x in planCategories]
|
||||
self.webhooks.update(session=session, **webhooks)
|
||||
return
|
||||
|
||||
def dict(self):
|
||||
data = {
|
||||
"name": self.name,
|
||||
"planCategories": [cat.to_str() for cat in self.planCategories],
|
||||
"webhooks": self.webhooks.dict(),
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
class MealCategory(SqlAlchemyBase):
|
||||
__tablename__ = "meal_plan_categories"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
name = sa.Column(sa.String)
|
||||
parent_id = sa.Column(sa.Integer, sa.ForeignKey("site_settings.name"))
|
||||
|
||||
def __init__(self, name) -> None:
|
||||
self.name = name
|
||||
|
||||
def to_str(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class WebHookModel(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "webhook_settings"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.String, sa.ForeignKey("site_settings.name"))
|
||||
webhookURLs = orm.relationship(
|
||||
"WebhookURLModel", uselist=True, cascade="all, delete"
|
||||
)
|
||||
webhookTime = sa.Column(sa.String, default="00:00")
|
||||
enabled = sa.Column(sa.Boolean, default=False)
|
||||
|
||||
def __init__(
|
||||
self, webhookURLs: list, webhookTime: str, enabled: bool = False, session=None
|
||||
) -> None:
|
||||
|
||||
self.webhookURLs = [WebhookURLModel(url=x) for x in webhookURLs]
|
||||
self.webhookTime = webhookTime
|
||||
self.enabled = enabled
|
||||
|
||||
def update(
|
||||
self, session, webhookURLs: list, webhookTime: str, enabled: bool
|
||||
) -> None:
|
||||
|
||||
self._sql_remove_list(session, [WebhookURLModel], self.id)
|
||||
|
||||
self.__init__(webhookURLs, webhookTime, enabled)
|
||||
|
||||
def dict(self):
|
||||
data = {
|
||||
"webhookURLs": [url.to_str() for url in self.webhookURLs],
|
||||
"webhookTime": self.webhookTime,
|
||||
"enabled": self.enabled,
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
class WebhookURLModel(SqlAlchemyBase):
|
||||
__tablename__ = "webhook_urls"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
url = sa.Column(sa.String)
|
||||
parent_id = sa.Column(sa.Integer, sa.ForeignKey("webhook_settings.id"))
|
||||
|
||||
def to_str(self):
|
||||
return self.url
|
||||
def update(self, session, language, sidebar):
|
||||
self.__init__(session=session, language=language, sidebar=sidebar)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
from db.models.model_base import SqlAlchemyBase
|
||||
|
||||
|
||||
class SiteThemeModel(SqlAlchemyBase):
|
||||
|
|
|
@ -3,6 +3,12 @@ from db.models.group import Group
|
|||
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, orm
|
||||
|
||||
# I'm not sure this is necessasry, browser based settings may be sufficient
|
||||
# class UserSettings(SqlAlchemyBase, BaseMixins):
|
||||
# __tablename__ = "user_settings"
|
||||
# id = Column(Integer, primary_key=True, index=True)
|
||||
# parent_id = Column(String, ForeignKey("users.id"))
|
||||
|
||||
|
||||
class User(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "users"
|
||||
|
|
|
@ -5,6 +5,7 @@ from db.database import db
|
|||
from db.db_setup import generate_session
|
||||
from fastapi import APIRouter, Depends
|
||||
from schema.meal import MealPlanBase, MealPlanInDB
|
||||
from schema.recipe import Recipe
|
||||
from schema.snackbar import SnackResponse
|
||||
from services.meal_services import get_todays_meal, process_meals
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
@ -26,9 +27,9 @@ def get_shopping_list(id: str, session: Session = Depends(generate_session)):
|
|||
mealplan = db.meals.get(session, id)
|
||||
mealplan: MealPlanInDB
|
||||
slugs = [x.slug for x in mealplan.meals]
|
||||
recipes = [db.recipes.get(session, x) for x in slugs]
|
||||
recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs]
|
||||
ingredients = [
|
||||
{"name": x.get("name"), "recipeIngredient": x.get("recipeIngredient")}
|
||||
{"name": x.name, "recipeIngredient": x.recipeIngredient}
|
||||
for x in recipes
|
||||
if x
|
||||
]
|
||||
|
|
|
@ -2,9 +2,9 @@ from db.database import db
|
|||
from db.db_setup import generate_session
|
||||
from fastapi import APIRouter, Depends
|
||||
from schema.settings import SiteSettings
|
||||
from schema.snackbar import SnackResponse
|
||||
from sqlalchemy.orm.session import Session
|
||||
from utils.post_webhooks import post_webhooks
|
||||
from schema.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter(prefix="/api/site-settings", tags=["Settings"])
|
||||
|
||||
|
@ -13,17 +13,15 @@ router = APIRouter(prefix="/api/site-settings", tags=["Settings"])
|
|||
def get_main_settings(session: Session = Depends(generate_session)):
|
||||
""" Returns basic site settings """
|
||||
|
||||
try:
|
||||
data = db.settings.get(session, "main")
|
||||
except:
|
||||
return
|
||||
data = db.settings.get(session, 1)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@router.put("")
|
||||
def update_settings(data: SiteSettings, session: Session = Depends(generate_session)):
|
||||
""" Returns Site Settings """
|
||||
db.settings.update(session, "main", data.dict())
|
||||
db.settings.update(session, 1, data.dict())
|
||||
|
||||
return SnackResponse.success("Settings Updated")
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ async def get_user_image(id: str):
|
|||
""" Returns a users profile picture """
|
||||
user_dir = USER_DIR.joinpath(id)
|
||||
for recipe_image in user_dir.glob("profile_image.*"):
|
||||
print(recipe_image)
|
||||
return FileResponse(recipe_image)
|
||||
else:
|
||||
return False
|
||||
|
@ -128,7 +127,6 @@ async def update_password(
|
|||
match_passwords = verify_password(
|
||||
password_change.current_password, current_user.password
|
||||
)
|
||||
print(match_passwords)
|
||||
match_id = current_user.id == id
|
||||
|
||||
if match_passwords and match_id:
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
## Migrations
|
||||
# TODO
|
||||
|
||||
# Database Init
|
||||
|
||||
## Web Server
|
||||
caddy start --config ./Caddyfile
|
||||
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from pydantic.main import BaseModel
|
||||
from fastapi_camelcase import CamelModel
|
||||
|
||||
from schema.recipe import Recipe
|
||||
|
||||
|
||||
class RecipeCategoryResponse(BaseModel):
|
||||
class CategoryBase(CamelModel):
|
||||
id: int
|
||||
name: str
|
||||
slug: str
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class RecipeCategoryResponse(CategoryBase):
|
||||
recipes: Optional[List[Recipe]]
|
||||
|
||||
class Config:
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import datetime
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from db.models.recipe.ingredient import RecipeIngredient
|
||||
from db.models.recipe.recipe import RecipeModel
|
||||
from pydantic import BaseModel, Schema, validator
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic.utils import GetterDict
|
||||
from slugify import slugify
|
||||
|
||||
|
@ -36,7 +35,6 @@ class Nutrition(BaseModel):
|
|||
|
||||
|
||||
class Recipe(BaseModel):
|
||||
# Standard Schema
|
||||
name: str
|
||||
description: Optional[str]
|
||||
image: Optional[Any]
|
||||
|
@ -63,13 +61,13 @@ class Recipe(BaseModel):
|
|||
orm_mode = True
|
||||
|
||||
@classmethod
|
||||
def getter_dict(cls, name_orm: RecipeModel):
|
||||
print(name_orm.recipeIngredient)
|
||||
def getter_dict(_cls, name_orm: RecipeModel):
|
||||
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]
|
||||
"tags": [x.name for x in name_orm.tags],
|
||||
"extras": {x.key_name: x.value for x in name_orm.extras},
|
||||
}
|
||||
|
||||
schema_extra = {
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from fastapi_camelcase import CamelModel
|
||||
|
||||
from schema.category import CategoryBase
|
||||
|
||||
|
||||
class Webhooks(BaseModel):
|
||||
webhookTime: str = "00:00"
|
||||
webhookURLs: Optional[List[str]] = []
|
||||
enabled: bool = False
|
||||
|
||||
|
||||
class SiteSettings(BaseModel):
|
||||
name: str = "main"
|
||||
planCategories: list[str] = []
|
||||
webhooks: Webhooks
|
||||
class Sidebar(CamelModel):
|
||||
categories: Optional[list[CategoryBase]]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class SiteSettings(CamelModel):
|
||||
language: str
|
||||
sidebar: Sidebar
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"name": "main",
|
||||
"planCategories": ["dinner", "lunch"],
|
||||
"webhooks": {
|
||||
"webhookTime": "00:00",
|
||||
"webhookURLs": ["https://mywebhookurl.com/webhook"],
|
||||
"enable": False,
|
||||
},
|
||||
}
|
||||
"example": {"id": "1", "language": "en", "sidebar": ["// TODO"]}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from typing import Optional
|
||||
|
||||
from core.config import DEFAULT_GROUP
|
||||
from db.models.users import User
|
||||
from fastapi_camelcase import CamelModel
|
||||
from pydantic.utils import GetterDict
|
||||
|
||||
|
||||
class ChangePassword(CamelModel):
|
||||
|
@ -40,11 +42,18 @@ class UserIn(UserBase):
|
|||
|
||||
class UserOut(UserBase):
|
||||
id: int
|
||||
group: GroupBase
|
||||
group: str
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@classmethod
|
||||
def getter_dict(cls, ormModel: User):
|
||||
return {
|
||||
**GetterDict(ormModel),
|
||||
"group": ormModel.group.name,
|
||||
}
|
||||
|
||||
|
||||
class UserInDB(UserOut):
|
||||
password: str
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue