site settings refactor

This commit is contained in:
hay-kot 2021-03-07 17:28:28 -09:00
commit e5bd261a3c
16 changed files with 149 additions and 156 deletions

View file

@ -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):

View file

@ -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):

View file

@ -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"))

View file

@ -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]):

View file

@ -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,
}

View file

@ -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)

View file

@ -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):

View file

@ -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"

View file

@ -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
]

View file

@ -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")

View file

@ -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:

View file

@ -3,6 +3,8 @@
## Migrations
# TODO
# Database Init
## Web Server
caddy start --config ./Caddyfile

View file

@ -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:

View file

@ -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 = {

View file

@ -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"]}
}

View file

@ -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