continued model refactor

This commit is contained in:
hay-kot 2021-03-08 19:23:41 -09:00
commit 1ac2d7a4ce
17 changed files with 198 additions and 139 deletions

View file

@ -0,0 +1,14 @@
import { baseURL } from "./api-utils";
import { apiReq } from "./api-utils";
const groupPrefix = baseURL + "groups";
const groupsURLs = {
groups: `${groupPrefix}`,
};
export default {
async allGroups() {
let response = await apiReq.get(groupsURLs.groups);
return response.data;
},
};

View file

@ -9,6 +9,7 @@ import category from "./category";
import meta from "./meta";
import users from "./users";
import signUps from "./signUps";
import groups from "./groups";
export default {
recipes: recipe,
@ -22,4 +23,5 @@ export default {
meta: meta,
users: users,
signUps: signUps,
groups: groups,
};

View file

@ -19,6 +19,10 @@
User Groups
</v-toolbar-title>
<div>
<v-select class="mt-7 ml-5" dense flat solo> </v-select>
</div>
<v-spacer> </v-spacer>
<v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }">
@ -136,7 +140,7 @@ export default {
activeName: null,
headers: [
{
text: "User ID",
text: "User IDs",
align: "start",
sortable: false,
value: "id",

View file

@ -26,7 +26,7 @@
</v-tab>
</v-tabs>
<v-tabs-items v-model="tab" >
<v-tabs-items v-model="tab">
<v-tab-item>
<TheUserTable />
</v-tab-item>

View file

@ -1,7 +1,9 @@
from schema.category import RecipeCategoryResponse
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.theme import SiteTheme
from schema.user import GroupInDB, UserInDB
from sqlalchemy.orm.session import Session
@ -34,7 +36,8 @@ class _Categories(BaseDocument):
def __init__(self) -> None:
self.primary_key = "slug"
self.sql_model = Category
self.orm_mode = False
self.orm_mode = True
self.schema = RecipeCategoryResponse
class _Tags(BaseDocument):
@ -64,7 +67,8 @@ class _Themes(BaseDocument):
def __init__(self) -> None:
self.primary_key = "name"
self.sql_model = SiteThemeModel
self.orm_mode = False
self.orm_mode = True
self.schema = SiteTheme
class _Users(BaseDocument):

View file

@ -2,6 +2,7 @@ from core.config import DEFAULT_GROUP
from core.security import get_password_hash
from fastapi.logger import logger
from schema.settings import SiteSettings
from schema.theme import SiteTheme
from sqlalchemy.orm import Session
from sqlalchemy.orm.session import Session
@ -22,29 +23,17 @@ def init_db(db: Session = None) -> None:
def default_theme_init(session: Session):
default_theme = {
"name": "default",
"colors": {
"primary": "#E58325",
"accent": "#00457A",
"secondary": "#973542",
"success": "#5AB1BB",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF5350",
},
}
db.themes.create(session, SiteTheme().dict())
try:
db.themes.create(session, default_theme)
logger.info("Generating default theme...")
except:
logger.info("Default Theme Exists.. skipping generation")
def default_settings_init(session: Session):
data = {"language": "en", "sidebar": {"categories": []}}
document = db.settings.create(session, data)
data = {"language": "en", "home_page_settings": {"categories": []}}
document = db.settings.create(session, SiteSettings().dict())
logger.info(f"Created Site Settings: \n {document}")

View file

@ -1,13 +1,14 @@
from typing import List
import sqlalchemy.ext.declarative as dec
from sqlalchemy.orm.session import Session
SqlAlchemyBase = dec.declarative_base()
class BaseMixins:
@staticmethod
def _sql_remove_list(session, list_of_tables: list, parent_id):
def _sql_remove_list(session: Session, list_of_tables: list, parent_id):
for table in list_of_tables:
session.query(table).filter(parent_id == parent_id).delete()
@ -19,3 +20,29 @@ class BaseMixins:
finalMap.update(d.dict())
return finalMap
# ! Don't use!
def update_generics(func):
"""An experimental function that does the initial work of updating attributes on a class
and passing "complex" data types recuresively to an "self.update()" function if one exists.
Args:
func ([type]): [description]
"""
def wrapper(class_object, session, new_data: dict):
complex_attributed = {}
for key, value in new_data.items():
attribute = getattr(class_object, key, None)
if attribute and isinstance(attribute, SqlAlchemyBase):
attribute.update(session, value)
elif attribute:
setattr(class_object, key, value)
print("Complex", complex_attributed)
func(class_object, complex_attributed)
return wrapper

View file

@ -5,10 +5,10 @@ from fastapi.logger import logger
from slugify import slugify
from sqlalchemy.orm import validates
sidebar2categories = sa.Table(
"sidebar2categories",
site_settings2categories = sa.Table(
"site_settings2categoories",
SqlAlchemyBase.metadata,
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_sidebar.id")),
sa.Column("sidebar_id", sa.Integer, sa.ForeignKey("site_settings.id")),
sa.Column("category_slug", sa.String, sa.ForeignKey("categories.slug")),
)

View file

@ -182,30 +182,3 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
orgURL=orgURL,
extras=extras,
)
# def dict(self):
# data = {
# "name": self.name,
# "description": self.description,
# "image": self.image,
# "recipeYield": self.recipeYield,
# "recipeCuisine": self.recipeCuisine,
# "recipeCategory": [x.to_str() for x in self.recipeCategory],
# "recipeIngredient": [x.to_str() for x in self.recipeIngredient],
# "recipeInstructions": [x.dict() for x in self.recipeInstructions],
# "nutrition": self.nutrition.dict(),
# "totalTime": self.totalTime,
# "prepTime": self.prepTime,
# "performTime": self.performTime,
# "tool": [x.str() for x in self.tool],
# # Mealie Specific
# "slug": self.slug,
# "tags": [x.to_str() for x in self.tags],
# "dateAdded": self.dateAdded,
# "notes": [x.dict() for x in self.notes],
# "rating": self.rating,
# "orgURL": self.orgURL,
# "extras": RecipeModel._flatten_dict(self.extras),
# }
# return data

View file

@ -1,42 +1,32 @@
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 db.models.recipe.category import Category, site_settings2categories
from sqlalchemy.orm import Session
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"
id = sa.Column(sa.Integer, primary_key=True)
language = sa.Column(sa.String)
sidebar = orm.relationship("Sidebar", uselist=False, cascade="all")
categories = orm.relationship(
"Category",
secondary=site_settings2categories,
single_parent=True,
)
show_recent = sa.Column(sa.Boolean, default=True)
def __init__(
self, session=None, language="en", sidebar: list = {"categories": []}
self, session: Session = None, language="en", categories: list = [], show_recent=True
) -> None:
self._sql_remove_list(session, [Sidebar], self.id)
session.commit()
self.language = language
self.sidebar = Sidebar(session, sidebar)
def update(self, session, language, sidebar):
self.__init__(session=session, language=language, sidebar=sidebar)
self.show_recent = show_recent
self.categories = [
Category.create_if_not_exist(session=session, name=cat.get("name"))
for cat in categories
]
def update(self, *args, **kwarg):
self.__init__(*args, **kwarg)

View file

@ -1,12 +1,6 @@
import shutil
from datetime import timedelta
from core.config import USER_DIR
from core.security import get_password_hash, verify_password
from db.database import db
from db.db_setup import generate_session
from fastapi import APIRouter, Depends, File, UploadFile
from fastapi.responses import FileResponse
from fastapi import APIRouter, Depends
from routes.deps import manager
from schema.snackbar import SnackResponse
from schema.user import GroupBase, GroupInDB
@ -21,4 +15,52 @@ async def get_all_groups(
session: Session = Depends(generate_session),
):
""" Returns a list of all groups in the database """
return db.groups.get_all(session)
@router.post("")
async def create_group(
group_data: GroupBase,
current_user=Depends(manager),
session: Session = Depends(generate_session),
):
""" Creates a Group in the Database """
db.groups.create(session, group_data.dict())
return
@router.put("/{id}")
async def update_group_data(
id: int,
group_data: GroupInDB,
current_user=Depends(manager),
session: Session = Depends(generate_session),
):
""" Updates a User Group """
return db.groups.update(session, id, group_data)
@router.delete("/{id}")
async def delete_user_group(
id: int, current_user=Depends(manager), session: Session = Depends(generate_session)
):
""" Removes a user group from the database """
if id == 1:
return SnackResponse.error("Cannot delete default group")
group: GroupInDB = db.groups.get(session, id)
if not group:
return SnackResponse.error("Group not found")
if not group.users == []:
return SnackResponse.error("Cannot delete group with users")
db.groups.delete(session, id)
return

View file

@ -29,9 +29,11 @@ class MealPlanBase(BaseModel):
raise ValueError("EndDate should be greater than StartDate")
return v
class MealPlanProcessed(MealPlanBase):
meals: list[MealOut]
class MealPlanInDB(MealPlanProcessed):
uid: str
@ -39,7 +41,6 @@ class MealPlanInDB(MealPlanProcessed):
orm_mode = True
class MealPlan(BaseModel):
uid: Optional[str]

View file

@ -5,20 +5,22 @@ from fastapi_camelcase import CamelModel
from schema.category import CategoryBase
class Sidebar(CamelModel):
categories: Optional[list[CategoryBase]]
class Config:
orm_mode = True
class SiteSettings(CamelModel):
language: str
sidebar: Sidebar
language: str = "en"
show_recent: bool = True
categories: Optional[list[CategoryBase]] = []
class Config:
orm_mode = True
schema_extra = {
"example": {"id": "1", "language": "en", "sidebar": ["// TODO"]}
"example": {
"language": "en",
"showRecent": True,
"categories": [
{"id": 1, "name": "thanksgiving", "slug": "thanksgiving"},
{"id": 2, "name": "homechef", "slug": "homechef"},
{"id": 3, "name": "potatoes", "slug": "potatoes"},
],
}
}

View file

@ -1,20 +1,25 @@
from pydantic import BaseModel
class Colors(BaseModel):
primary: str
accent: str
secondary: str
success: str
info: str
warning: str
error: str
primary: str = "#E58325"
accent: str = "#00457A"
secondary: str = "#973542"
success: str = "#5AB1BB"
info: str = "#4990BA"
warning: str = "#FF4081"
error: str = "#EF5350"
class Config:
orm_mode = True
class SiteTheme(BaseModel):
name: str
colors: Colors
name: str = "default"
colors: Colors = Colors()
class Config:
orm_mode = True
schema_extra = {
"example": {
"name": "default",
@ -28,4 +33,4 @@ class SiteTheme(BaseModel):
"error": "#EF5350",
},
}
}
}

View file

@ -1,10 +1,14 @@
from typing import Optional
from typing import Any, Optional
from core.config import DEFAULT_GROUP
from db.models.group import WebHookModel
from db.models.users import User
from fastapi_camelcase import CamelModel
from pydantic.utils import GetterDict
from schema.category import CategoryBase
from schema.meal import MealPlanInDB
class ChangePassword(CamelModel):
current_password: str
@ -27,7 +31,13 @@ class UserBase(CamelModel):
class Config:
orm_mode = True
class Config:
@classmethod
def getter_dict(_cls, name_orm: User):
return {
**GetterDict(name_orm),
"group": name_orm.group.name,
}
schema_extra = {
"fullName": "Change Me",
"email": "changeme@email.com",
@ -63,10 +73,29 @@ class UserInDB(UserOut):
orm_mode = True
class Webhooks(CamelModel):
webhookURLs: list[str] = []
webhookTime: str = "00:00"
enabled: bool = False
class Config:
orm_mode = True
@classmethod
def getter_dict(_cls, orm_model: WebHookModel):
return {
**GetterDict(orm_model),
"webookURLs": [x.url for x in orm_model.webhookURLs],
}
class GroupInDB(GroupBase):
id: int
name: str
users: Optional[list[UserOut]]
mealplans: Optional[list[MealPlanInDB]]
categories: Optional[list[CategoryBase]]
webhooks: Optional[Webhooks]
class Config:
orm_mode = True

View file

@ -1,4 +1,6 @@
import json
from schema.settings import SiteSettings
from schema.theme import SiteTheme
import pytest
from tests.utils.routes import (
@ -11,30 +13,12 @@ from tests.utils.routes import (
@pytest.fixture(scope="function")
def default_settings():
return {
"name": "main",
"planCategories": [],
"webhooks": {"webhookTime": "00:00", "webhookURLs": [], "enabled": False},
}
return SiteSettings().dict(by_alias=True)
@pytest.fixture(scope="session")
def default_theme(api_client):
default_theme = {
"name": "default",
"colors": {
"primary": "#E58325",
"accent": "#00457A",
"secondary": "#973542",
"success": "#5AB1BB",
"info": "#4990BA",
"warning": "#FF4081",
"error": "#EF5350",
},
}
return default_theme
def default_theme():
return SiteTheme().dict()
@pytest.fixture(scope="session")
@ -62,11 +46,8 @@ def test_default_settings(api_client, default_settings):
def test_update_settings(api_client, default_settings):
default_settings["webhooks"]["webhookURLs"] = [
"https://test1.url.com",
"https://test2.url.com",
"https://test3.url.com",
]
default_settings["language"] = "fr"
default_settings["showRecent"] = False
response = api_client.put(SETTINGS_UPDATE, json=default_settings)

View file

@ -16,9 +16,7 @@ def default_user():
"id": 1,
"fullName": "Change Me",
"email": "changeme@email.com",
"group": {
"name": "home"
},
"group": "home",
"admin": True
}
@ -29,9 +27,7 @@ def new_user():
"id": 2,
"fullName": "My New User",
"email": "newuser@email.com",
"group": {
"name": "home"
},
"group": "home",
"admin": False
}