mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
Feature/authentication (#207)
* basic crud NOT SECURE * refactor/database init on startup * added scratch.py * tests/user CRUD routes * password hashing * change app_config location * bump python version * formatting * login ui starter * change import from url design * move components * remove old snackbar * refactor/Componenet folder structure rework * refactor/remove old code * refactor/rename componenets/js files * remove console.logs * refactor/ models to schema and sql to models * new header styling for imports * token request * fix url scrapper * refactor/rename schema files * split routes file * redesigned admin page * enable relative imports for vue components * refactor/switch to pages view * add CamelCase package * majors settings rework * user management second pass * super user CRUD * refactor/consistent models names * refactor/consistent model names * password reset * store refactor * dependency update * abstract button props * profile page refactor * basic password validation * login form refactor/split v-container * remo unused code * hide editor buttons when not logged in * mkdocs dev dependency * v0.4.0 docs update * profile image upload * additional token routes * Smaller recipe cards for smaller viewports * fix admin sidebar * add users * change to outlined * theme card starter * code cleanup * signups * signup pages * fix #194 * fix #193 * clarify mealie_port * fix #184 * fixes #178 * fix blank card error on meal-plan creator * admin signup * formatting * improved search bar * improved search bar * refresh token on page refresh * allow mealplan with no categories * fix card layout * remove cdn dependencies * start on groups * Fixes #196 * recipe databse refactor * changelog draft * database refactoring * refactor recipe schema/model * site settings refactor * continued model refactor * merge docs changes from master * site-settings work * cleanup + tag models * notes * typo * user table * sign up data validation * package updates * group store init * Fix home page settings * group admin init * group dashboard init * update deps * formatting * bug / added libffi-dev * pages refactor * fix mealplan Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
4a9955450c
commit
b6b014f515
23 changed files with 159 additions and 120 deletions
|
@ -79,6 +79,7 @@ export default {
|
||||||
this.$store.dispatch("requestHomePageSettings");
|
this.$store.dispatch("requestHomePageSettings");
|
||||||
this.$store.dispatch("requestSiteSettings");
|
this.$store.dispatch("requestSiteSettings");
|
||||||
this.$store.dispatch("refreshToken");
|
this.$store.dispatch("refreshToken");
|
||||||
|
this.$store.dispatch("requestCurrentGroup");
|
||||||
this.darkModeSystemCheck();
|
this.darkModeSystemCheck();
|
||||||
this.darkModeAddEventListener();
|
this.darkModeAddEventListener();
|
||||||
},
|
},
|
||||||
|
|
|
@ -82,6 +82,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const CREATE_EVENT = "created";
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import utils from "@/utils";
|
import utils from "@/utils";
|
||||||
import MealPlanCard from "./MealPlanCard";
|
import MealPlanCard from "./MealPlanCard";
|
||||||
|
@ -116,9 +117,8 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
let settings = await api.settings.requestAll();
|
let categories = Array.from(this.groupSettings, x => x.name);
|
||||||
this.items = await api.recipes.getAllByCategory(settings.planCategories);
|
this.items = await api.recipes.getAllByCategory(categories);
|
||||||
console.log(this.items);
|
|
||||||
|
|
||||||
if (this.items.length === 0) {
|
if (this.items.length === 0) {
|
||||||
const keys = [
|
const keys = [
|
||||||
|
@ -134,6 +134,9 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
groupSettings() {
|
||||||
|
return this.$store.getters.getCurrentGroup;
|
||||||
|
},
|
||||||
actualStartDate() {
|
actualStartDate() {
|
||||||
return Date.parse(this.startDate);
|
return Date.parse(this.startDate);
|
||||||
},
|
},
|
||||||
|
@ -146,7 +149,6 @@ export default {
|
||||||
|
|
||||||
let dateDif = (endDate - startDate) / (1000 * 3600 * 24) + 1;
|
let dateDif = (endDate - startDate) / (1000 * 3600 * 24) + 1;
|
||||||
|
|
||||||
|
|
||||||
if (dateDif < 1) {
|
if (dateDif < 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -190,12 +192,13 @@ export default {
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
const mealBody = {
|
const mealBody = {
|
||||||
|
group: this.groupSettings.name,
|
||||||
startDate: this.startDate,
|
startDate: this.startDate,
|
||||||
endDate: this.endDate,
|
endDate: this.endDate,
|
||||||
meals: this.meals,
|
meals: this.meals,
|
||||||
};
|
};
|
||||||
await api.mealPlans.create(mealBody);
|
await api.mealPlans.create(mealBody);
|
||||||
this.$emit("created");
|
this.$emit(CREATE_EVENT);
|
||||||
this.meals = [];
|
this.meals = [];
|
||||||
this.startDate = null;
|
this.startDate = null;
|
||||||
this.endDate = null;
|
this.endDate = null;
|
||||||
|
|
|
@ -165,11 +165,11 @@ export default {
|
||||||
showPassword: false,
|
showPassword: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
user: {
|
user: {
|
||||||
fullName: "Change Me",
|
fullName: "",
|
||||||
email: "changeme@email.com",
|
email: "",
|
||||||
group: "public",
|
group: "",
|
||||||
admin: true,
|
admin: false,
|
||||||
id: 1,
|
id: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -87,9 +87,9 @@
|
||||||
<script>
|
<script>
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import utils from "@/utils";
|
import utils from "@/utils";
|
||||||
import NewMeal from "../components/MealPlan/MealPlanNew";
|
import NewMeal from "@/components/MealPlan/MealPlanNew";
|
||||||
import EditPlan from "../components/MealPlan/MealPlanEditor";
|
import EditPlan from "@/components/MealPlan/MealPlanEditor";
|
||||||
import ShoppingListDialog from "../components/MealPlan/ShoppingListDialog";
|
import ShoppingListDialog from "@/components/MealPlan/ShoppingListDialog";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
|
@ -18,7 +18,9 @@
|
||||||
<v-card-title class="justify-center">
|
<v-card-title class="justify-center">
|
||||||
{{ meal.name }}
|
{{ meal.name }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-subtitle> {{ $d(new Date(meal.date), 'short' ) }}</v-card-subtitle>
|
<v-card-subtitle>
|
||||||
|
{{ $d(new Date(meal.date), "short") }}</v-card-subtitle
|
||||||
|
>
|
||||||
|
|
||||||
<v-card-text> {{ meal.description }} </v-card-text>
|
<v-card-text> {{ meal.description }} </v-card-text>
|
||||||
|
|
|
@ -43,9 +43,9 @@
|
||||||
<script>
|
<script>
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
|
|
||||||
import RecipeEditor from "../components/Recipe/RecipeEditor";
|
import RecipeEditor from "@/components/Recipe/RecipeEditor";
|
||||||
import VJsoneditor from "v-jsoneditor";
|
import VJsoneditor from "v-jsoneditor";
|
||||||
import EditorButtonRow from "../components/Recipe/EditorButtonRow";
|
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
VJsoneditor,
|
VJsoneditor,
|
|
@ -62,10 +62,10 @@
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import utils from "@/utils";
|
import utils from "@/utils";
|
||||||
import VJsoneditor from "v-jsoneditor";
|
import VJsoneditor from "v-jsoneditor";
|
||||||
import RecipeViewer from "../components/Recipe/RecipeViewer";
|
import RecipeViewer from "@/components/Recipe/RecipeViewer";
|
||||||
import RecipeEditor from "../components/Recipe/RecipeEditor";
|
import RecipeEditor from "@/components/Recipe/RecipeEditor";
|
||||||
import RecipeTimeCard from "../components/Recipe/RecipeTimeCard.vue";
|
import RecipeTimeCard from "@/components/Recipe/RecipeTimeCard.vue";
|
||||||
import EditorButtonRow from "../components/Recipe/EditorButtonRow";
|
import EditorButtonRow from "@/components/Recipe/EditorButtonRow";
|
||||||
import { user } from "@/mixins/user";
|
import { user } from "@/mixins/user";
|
||||||
|
|
||||||
export default {
|
export default {
|
|
@ -13,8 +13,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CardSection from "../components/UI/CardSection";
|
import CardSection from "@/components/UI/CardSection";
|
||||||
import CategorySidebar from "../components/UI/CategorySidebar";
|
import CategorySidebar from "@/components/UI/CategorySidebar";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
CardSection,
|
CardSection,
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import CardSection from "../components/UI/CardSection";
|
import CardSection from "@/components/UI/CardSection";
|
||||||
import CategorySidebar from "../components/UI/CategorySidebar";
|
import CategorySidebar from "@/components/UI/CategorySidebar";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
CardSection,
|
CardSection,
|
|
@ -1,15 +1,15 @@
|
||||||
import HomePage from "../pages/HomePage";
|
import HomePage from "@/pages/HomePage";
|
||||||
import Page404 from "../pages/404Page";
|
import Page404 from "@/pages/404Page";
|
||||||
import SearchPage from "../pages/SearchPage";
|
import SearchPage from "@/pages/SearchPage";
|
||||||
import RecipePage from "../pages/RecipePage";
|
import ViewRecipe from "@/pages/Recipe/ViewRecipe";
|
||||||
import RecipeNewPage from "../pages/RecipeNewPage";
|
import NewRecipe from "@/pages/Recipe/NewRecipe";
|
||||||
import AllRecipesPage from "../pages/AllRecipesPage";
|
import AllRecipes from "@/pages/Recipes/AllRecipes";
|
||||||
import CategoryPage from "../pages/CategoryPage";
|
import CategoryPage from "@/pages/Recipes/CategoryPage";
|
||||||
import MeaplPlanPage from "../pages/MealPlanPage";
|
import Planner from "@/pages/MealPlan/Planner";
|
||||||
import Debug from "../pages/Debug";
|
import Debug from "@/pages/Debug";
|
||||||
import LoginPage from "../pages/LoginPage";
|
import LoginPage from "@/pages/LoginPage";
|
||||||
import SignUpPage from "../pages/SignUpPage";
|
import SignUpPage from "@/pages/SignUpPage";
|
||||||
import MealPlanThisWeekPage from "../pages/MealPlanThisWeekPage";
|
import ThisWeek from "@/pages/MealPlan/ThisWeek";
|
||||||
import api from "@/api";
|
import api from "@/api";
|
||||||
import Admin from "./admin";
|
import Admin from "./admin";
|
||||||
import { store } from "../store";
|
import { store } from "../store";
|
||||||
|
@ -30,12 +30,12 @@ export const routes = [
|
||||||
{ path: "/sign-up/:token", component: SignUpPage },
|
{ path: "/sign-up/:token", component: SignUpPage },
|
||||||
{ path: "/debug", component: Debug },
|
{ path: "/debug", component: Debug },
|
||||||
{ path: "/search", component: SearchPage },
|
{ path: "/search", component: SearchPage },
|
||||||
{ path: "/recipes/all", component: AllRecipesPage },
|
{ path: "/recipes/all", component: AllRecipes },
|
||||||
{ path: "/recipes/:category", component: CategoryPage },
|
{ path: "/recipes/:category", component: CategoryPage },
|
||||||
{ path: "/recipe/:recipe", component: RecipePage },
|
{ path: "/recipe/:recipe", component: ViewRecipe },
|
||||||
{ path: "/new/", component: RecipeNewPage },
|
{ path: "/new/", component: NewRecipe },
|
||||||
{ path: "/meal-plan/planner", component: MeaplPlanPage },
|
{ path: "/meal-plan/planner", component: Planner },
|
||||||
{ path: "/meal-plan/this-week", component: MealPlanThisWeekPage },
|
{ path: "/meal-plan/this-week", component: ThisWeek },
|
||||||
Admin,
|
Admin,
|
||||||
{
|
{
|
||||||
path: "/meal-plan/today",
|
path: "/meal-plan/today",
|
||||||
|
|
|
@ -28,7 +28,7 @@ const actions = {
|
||||||
const getters = {
|
const getters = {
|
||||||
getGroups: state => state.groups,
|
getGroups: state => state.groups,
|
||||||
getGroupNames: state => Array.from(state.groups, x => x.name),
|
getGroupNames: state => Array.from(state.groups, x => x.name),
|
||||||
getCurrentGroup: state => state.currentGroup
|
getCurrentGroup: state => state.currentGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -9,12 +9,12 @@ from db.init_db import init_db
|
||||||
from routes import (
|
from routes import (
|
||||||
backup_routes,
|
backup_routes,
|
||||||
debug_routes,
|
debug_routes,
|
||||||
meal_routes,
|
|
||||||
migration_routes,
|
migration_routes,
|
||||||
setting_routes,
|
setting_routes,
|
||||||
theme_routes,
|
theme_routes,
|
||||||
)
|
)
|
||||||
from routes.groups import groups
|
from routes.groups import groups
|
||||||
|
from routes.mealplans import mealplans
|
||||||
from routes.recipe import (
|
from routes.recipe import (
|
||||||
all_recipe_routes,
|
all_recipe_routes,
|
||||||
category_routes,
|
category_routes,
|
||||||
|
@ -51,7 +51,7 @@ def api_routers():
|
||||||
app.include_router(recipe_crud_routes.router)
|
app.include_router(recipe_crud_routes.router)
|
||||||
|
|
||||||
# Meal Routes
|
# Meal Routes
|
||||||
app.include_router(meal_routes.router)
|
app.include_router(mealplans.router)
|
||||||
# Settings Routes
|
# Settings Routes
|
||||||
app.include_router(setting_routes.router)
|
app.include_router(setting_routes.router)
|
||||||
app.include_router(theme_routes.router)
|
app.include_router(theme_routes.router)
|
||||||
|
|
|
@ -3,6 +3,9 @@ from pathlib import Path
|
||||||
|
|
||||||
import dotenv
|
import dotenv
|
||||||
|
|
||||||
|
APP_VERSION = "v0.3.0"
|
||||||
|
DB_VERSION = "v0.3.0"
|
||||||
|
|
||||||
CWD = Path(__file__).parent
|
CWD = Path(__file__).parent
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,8 +22,6 @@ dotenv.load_dotenv(ENV)
|
||||||
SECRET = "super-secret-key"
|
SECRET = "super-secret-key"
|
||||||
|
|
||||||
# General
|
# General
|
||||||
APP_VERSION = "v0.3.0"
|
|
||||||
DB_VERSION = "v0.3.0"
|
|
||||||
PRODUCTION = os.environ.get("ENV")
|
PRODUCTION = os.environ.get("ENV")
|
||||||
PORT = int(os.getenv("mealie_port", 9000))
|
PORT = int(os.getenv("mealie_port", 9000))
|
||||||
API = os.getenv("api_docs", True)
|
API = os.getenv("api_docs", True)
|
||||||
|
@ -83,7 +84,7 @@ else:
|
||||||
|
|
||||||
# Mongo Database
|
# Mongo Database
|
||||||
MEALIE_DB_NAME = os.getenv("mealie_db_name", "mealie")
|
MEALIE_DB_NAME = os.getenv("mealie_db_name", "mealie")
|
||||||
DEFAULT_GROUP = os.getenv("default_group", "home")
|
DEFAULT_GROUP = os.getenv("default_group", "Home")
|
||||||
DB_USERNAME = os.getenv("db_username", "root")
|
DB_USERNAME = os.getenv("db_username", "root")
|
||||||
DB_PASSWORD = os.getenv("db_password", "example")
|
DB_PASSWORD = os.getenv("db_password", "example")
|
||||||
DB_HOST = os.getenv("db_host", "mongo")
|
DB_HOST = os.getenv("db_host", "mongo")
|
||||||
|
|
|
@ -60,6 +60,10 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||||
|
|
||||||
self.__init__(session=session, *args, **kwargs)
|
self.__init__(session=session, *args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_ref(session: Session, name: str):
|
||||||
|
return session.query(Group).filter(Group.name == name).one()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_if_not_exist(session, name: str = DEFAULT_GROUP):
|
def create_if_not_exist(session, name: str = DEFAULT_GROUP):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import uuid
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
import sqlalchemy.orm as orm
|
import sqlalchemy.orm as orm
|
||||||
|
from db.models.group import Group
|
||||||
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
from db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,16 +29,25 @@ class MealPlanModel(SqlAlchemyBase, BaseMixins):
|
||||||
uid = sa.Column(sa.Integer, primary_key=True, unique=True) #! Probably Bad?
|
uid = sa.Column(sa.Integer, primary_key=True, unique=True) #! Probably Bad?
|
||||||
startDate = sa.Column(sa.Date)
|
startDate = sa.Column(sa.Date)
|
||||||
endDate = sa.Column(sa.Date)
|
endDate = sa.Column(sa.Date)
|
||||||
meals: List[Meal] = orm.relation(Meal)
|
meals: List[Meal] = orm.relationship(Meal, cascade="all, delete")
|
||||||
group_id = sa.Column(sa.String, sa.ForeignKey("groups.id"))
|
group_id = sa.Column(sa.String, sa.ForeignKey("groups.id"))
|
||||||
group = orm.relationship("Group", back_populates="mealplans")
|
group = orm.relationship("Group", back_populates="mealplans")
|
||||||
|
|
||||||
def __init__(self, startDate, endDate, meals, uid=None, session=None) -> None:
|
def __init__(
|
||||||
|
self, startDate, endDate, meals, group: str, uid=None, session=None
|
||||||
|
) -> None:
|
||||||
self.startDate = startDate
|
self.startDate = startDate
|
||||||
self.endDate = endDate
|
self.endDate = endDate
|
||||||
|
self.group = Group.get_ref(session, group)
|
||||||
self.meals = [Meal(**meal) for meal in meals]
|
self.meals = [Meal(**meal) for meal in meals]
|
||||||
|
|
||||||
def update(self, session, startDate, endDate, meals, uid) -> None:
|
def update(self, session, startDate, endDate, meals, uid, group) -> None:
|
||||||
MealPlanModel._sql_remove_list(session, [Meal], uid)
|
MealPlanModel._sql_remove_list(session, [Meal], uid)
|
||||||
|
|
||||||
self.__init__(startDate, endDate, meals)
|
self.__init__(
|
||||||
|
startDate=startDate,
|
||||||
|
endDate=endDate,
|
||||||
|
meals=meals,
|
||||||
|
group=group,
|
||||||
|
session=session,
|
||||||
|
)
|
||||||
|
|
|
@ -1,44 +1,33 @@
|
||||||
from datetime import date
|
|
||||||
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
|
from fastapi import APIRouter, Depends
|
||||||
from schema.meal import MealPlanBase, MealPlanInDB
|
from routes.deps import manager
|
||||||
from schema.recipe import Recipe
|
from schema.meal import MealPlanIn, MealPlanInDB
|
||||||
from schema.snackbar import SnackResponse
|
from schema.snackbar import SnackResponse
|
||||||
|
from schema.user import GroupInDB, UserInDB
|
||||||
from services.meal_services import get_todays_meal, process_meals
|
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[MealPlanInDB])
|
@router.get("/all", response_model=list[MealPlanInDB])
|
||||||
def get_all_meals(session: Session = Depends(generate_session)):
|
def get_all_meals(
|
||||||
|
current_user: UserInDB = Depends(manager),
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
):
|
||||||
""" Returns a list of all available Meal Plan """
|
""" Returns a list of all available Meal Plan """
|
||||||
|
print(current_user.group)
|
||||||
return db.meals.get_all(session)
|
group_entry: GroupInDB = db.groups.get(session, current_user.group, "name")
|
||||||
|
return group_entry.mealplans
|
||||||
|
|
||||||
@router.get("/{id}/shopping-list")
|
|
||||||
def get_shopping_list(id: str, session: Session = Depends(generate_session)):
|
|
||||||
|
|
||||||
#! Refactor into Single Database Call
|
|
||||||
mealplan = db.meals.get(session, id)
|
|
||||||
mealplan: MealPlanInDB
|
|
||||||
slugs = [x.slug for x in mealplan.meals]
|
|
||||||
recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs]
|
|
||||||
ingredients = [
|
|
||||||
{"name": x.name, "recipeIngredient": x.recipeIngredient}
|
|
||||||
for x in recipes
|
|
||||||
if x
|
|
||||||
]
|
|
||||||
|
|
||||||
return ingredients
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/create")
|
@router.post("/create")
|
||||||
def create_meal_plan(data: MealPlanBase, session: Session = Depends(generate_session)):
|
def create_meal_plan(
|
||||||
|
data: MealPlanIn,
|
||||||
|
session: Session = Depends(generate_session),
|
||||||
|
current_user=Depends(manager),
|
||||||
|
):
|
||||||
""" Creates a meal plan database entry """
|
""" Creates a meal plan database entry """
|
||||||
processed_plan = process_meals(session, data)
|
processed_plan = process_meals(session, data)
|
||||||
db.meals.create(session, processed_plan.dict())
|
db.meals.create(session, processed_plan.dict())
|
||||||
|
@ -46,16 +35,9 @@ def create_meal_plan(data: MealPlanBase, session: Session = Depends(generate_ses
|
||||||
return SnackResponse.success("Mealplan Created")
|
return SnackResponse.success("Mealplan Created")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/this-week", response_model=MealPlanInDB)
|
|
||||||
def get_this_week(session: Session = Depends(generate_session)):
|
|
||||||
""" Returns the meal plan data for this week """
|
|
||||||
|
|
||||||
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: MealPlanBase, session: Session = Depends(generate_session)
|
plan_id: str, meal_plan: MealPlanIn, session: Session = Depends(generate_session)
|
||||||
):
|
):
|
||||||
""" Updates a meal plan based off ID """
|
""" Updates a meal plan based off ID """
|
||||||
processed_plan = process_meals(session, meal_plan)
|
processed_plan = process_meals(session, meal_plan)
|
||||||
|
@ -74,6 +56,13 @@ def delete_meal_plan(plan_id, session: Session = Depends(generate_session)):
|
||||||
return SnackResponse.error("Mealplan Deleted")
|
return SnackResponse.error("Mealplan Deleted")
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/this-week", response_model=MealPlanInDB)
|
||||||
|
def get_this_week(session: Session = Depends(generate_session)):
|
||||||
|
""" Returns the meal plan data for this week """
|
||||||
|
|
||||||
|
return db.meals.get_all(session, limit=1, order_by="startDate")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/today", tags=["Meal Plan"])
|
@router.get("/today", tags=["Meal Plan"])
|
||||||
def get_today(session: Session = Depends(generate_session)):
|
def get_today(session: Session = Depends(generate_session)):
|
||||||
"""
|
"""
|
23
mealie/routes/mealplans/helpers.py
Normal file
23
mealie/routes/mealplans/helpers.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from db.database import db
|
||||||
|
from db.db_setup import generate_session
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from schema.meal import MealPlanInDB
|
||||||
|
from schema.recipe import Recipe
|
||||||
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{id}/shopping-list")
|
||||||
|
def get_shopping_list(id: str, session: Session = Depends(generate_session)):
|
||||||
|
|
||||||
|
#! Refactor into Single Database Call
|
||||||
|
mealplan = db.meals.get(session, id)
|
||||||
|
mealplan: MealPlanInDB
|
||||||
|
slugs = [x.slug for x in mealplan.meals]
|
||||||
|
recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs]
|
||||||
|
ingredients = [
|
||||||
|
{"name": x.name, "recipeIngredient": x.recipeIngredient} for x in recipes if x
|
||||||
|
]
|
||||||
|
|
||||||
|
return ingredients
|
7
mealie/routes/mealplans/mealplans.py
Normal file
7
mealie/routes/mealplans/mealplans.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from routes.mealplans import crud, helpers
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
router.include_router(crud.router)
|
||||||
|
router.include_router(helpers.router)
|
|
@ -1,7 +1,9 @@
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from db.models.mealplan import MealPlanModel
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, validator
|
||||||
|
from pydantic.utils import GetterDict
|
||||||
|
|
||||||
|
|
||||||
class MealIn(BaseModel):
|
class MealIn(BaseModel):
|
||||||
|
@ -18,7 +20,8 @@ class MealOut(MealIn):
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
class MealPlanBase(BaseModel):
|
class MealPlanIn(BaseModel):
|
||||||
|
group: str
|
||||||
startDate: date
|
startDate: date
|
||||||
endDate: date
|
endDate: date
|
||||||
meals: List[MealIn]
|
meals: List[MealIn]
|
||||||
|
@ -30,7 +33,7 @@ class MealPlanBase(BaseModel):
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|
||||||
class MealPlanProcessed(MealPlanBase):
|
class MealPlanProcessed(MealPlanIn):
|
||||||
meals: list[MealOut]
|
meals: list[MealOut]
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,18 +43,9 @@ class MealPlanInDB(MealPlanProcessed):
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
class MealPlan(BaseModel):
|
def getter_dict(_cls, name_orm: MealPlanModel):
|
||||||
uid: Optional[str]
|
return {
|
||||||
|
**GetterDict(name_orm),
|
||||||
class Config:
|
"group": name_orm.group.name,
|
||||||
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()},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
|
|
||||||
from db.database import db
|
from db.database import db
|
||||||
from schema.meal import MealIn, MealOut, MealPlanBase, MealPlanProcessed
|
from schema.meal import MealIn, MealOut, MealPlanIn, MealPlanProcessed
|
||||||
from schema.recipe import Recipe
|
from schema.recipe import Recipe
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
|
||||||
def process_meals(session: Session, meal_plan_base: MealPlanBase) -> MealPlanProcessed:
|
def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed:
|
||||||
meals = []
|
meals = []
|
||||||
for x, meal in enumerate(meal_plan_base.meals):
|
for x, meal in enumerate(meal_plan_base.meals):
|
||||||
meal: MealIn
|
meal: MealIn
|
||||||
|
@ -30,7 +30,10 @@ def process_meals(session: Session, meal_plan_base: MealPlanBase) -> MealPlanPro
|
||||||
meals.append(meal_data)
|
meals.append(meal_data)
|
||||||
|
|
||||||
return MealPlanProcessed(
|
return MealPlanProcessed(
|
||||||
meals=meals, startDate=meal_plan_base.startDate, endDate=meal_plan_base.endDate
|
group=meal_plan_base.group,
|
||||||
|
meals=meals,
|
||||||
|
startDate=meal_plan_base.startDate,
|
||||||
|
endDate=meal_plan_base.endDate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ from tests.utils.routes import (
|
||||||
|
|
||||||
def get_meal_plan_template(first=None, second=None):
|
def get_meal_plan_template(first=None, second=None):
|
||||||
return {
|
return {
|
||||||
|
"group": "Home",
|
||||||
"startDate": "2021-01-18",
|
"startDate": "2021-01-18",
|
||||||
"endDate": "2021-01-19",
|
"endDate": "2021-01-19",
|
||||||
"meals": [
|
"meals": [
|
||||||
|
@ -54,17 +55,17 @@ def slug_2(api_client):
|
||||||
api_client.delete(RECIPES_PREFIX + "/" + slug_2)
|
api_client.delete(RECIPES_PREFIX + "/" + slug_2)
|
||||||
|
|
||||||
|
|
||||||
def test_create_mealplan(api_client, slug_1, slug_2):
|
def test_create_mealplan(api_client, slug_1, slug_2, token):
|
||||||
meal_plan = get_meal_plan_template()
|
meal_plan = get_meal_plan_template()
|
||||||
meal_plan["meals"][0]["slug"] = slug_1
|
meal_plan["meals"][0]["slug"] = slug_1
|
||||||
meal_plan["meals"][1]["slug"] = slug_2
|
meal_plan["meals"][1]["slug"] = slug_2
|
||||||
|
|
||||||
response = api_client.post(MEALPLAN_CREATE, json=meal_plan)
|
response = api_client.post(MEALPLAN_CREATE, json=meal_plan, headers=token)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_read_mealplan(api_client, slug_1, slug_2):
|
def test_read_mealplan(api_client, slug_1, slug_2, token):
|
||||||
response = api_client.get(MEALPLAN_ALL)
|
response = api_client.get(MEALPLAN_ALL, headers=token)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
@ -77,9 +78,9 @@ def test_read_mealplan(api_client, slug_1, slug_2):
|
||||||
assert meals[1]["slug"] == meal_plan["meals"][1]["slug"]
|
assert meals[1]["slug"] == meal_plan["meals"][1]["slug"]
|
||||||
|
|
||||||
|
|
||||||
def test_update_mealplan(api_client, slug_1, slug_2):
|
def test_update_mealplan(api_client, slug_1, slug_2, token):
|
||||||
|
|
||||||
response = api_client.get(MEALPLAN_ALL)
|
response = api_client.get(MEALPLAN_ALL, headers=token)
|
||||||
|
|
||||||
existing_mealplan = json.loads(response.text)
|
existing_mealplan = json.loads(response.text)
|
||||||
existing_mealplan = existing_mealplan[0]
|
existing_mealplan = existing_mealplan[0]
|
||||||
|
@ -89,11 +90,13 @@ def test_update_mealplan(api_client, slug_1, slug_2):
|
||||||
existing_mealplan["meals"][0]["slug"] = slug_2
|
existing_mealplan["meals"][0]["slug"] = slug_2
|
||||||
existing_mealplan["meals"][1]["slug"] = slug_1
|
existing_mealplan["meals"][1]["slug"] = slug_1
|
||||||
|
|
||||||
response = api_client.put(f"{MEALPLAN_PREFIX}/{plan_uid}", json=existing_mealplan)
|
response = api_client.put(
|
||||||
|
f"{MEALPLAN_PREFIX}/{plan_uid}", json=existing_mealplan, headers=token
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
response = api_client.get(MEALPLAN_ALL)
|
response = api_client.get(MEALPLAN_ALL, headers=token)
|
||||||
existing_mealplan = json.loads(response.text)
|
existing_mealplan = json.loads(response.text)
|
||||||
existing_mealplan = existing_mealplan[0]
|
existing_mealplan = existing_mealplan[0]
|
||||||
|
|
||||||
|
@ -101,8 +104,8 @@ def test_update_mealplan(api_client, slug_1, slug_2):
|
||||||
assert existing_mealplan["meals"][1]["slug"] == slug_1
|
assert existing_mealplan["meals"][1]["slug"] == slug_1
|
||||||
|
|
||||||
|
|
||||||
def test_delete_mealplan(api_client):
|
def test_delete_mealplan(api_client, token):
|
||||||
response = api_client.get(MEALPLAN_ALL)
|
response = api_client.get(MEALPLAN_ALL, headers=token)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
existing_mealplan = json.loads(response.text)
|
existing_mealplan = json.loads(response.text)
|
||||||
|
|
|
@ -16,7 +16,7 @@ def default_user():
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"fullName": "Change Me",
|
"fullName": "Change Me",
|
||||||
"email": "changeme@email.com",
|
"email": "changeme@email.com",
|
||||||
"group": "home",
|
"group": "Home",
|
||||||
"admin": True
|
"admin": True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def new_user():
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"fullName": "My New User",
|
"fullName": "My New User",
|
||||||
"email": "newuser@email.com",
|
"email": "newuser@email.com",
|
||||||
"group": "home",
|
"group": "Home",
|
||||||
"admin": False
|
"admin": False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ def test_create_user(api_client: requests, token, new_user):
|
||||||
"fullName": "My New User",
|
"fullName": "My New User",
|
||||||
"email": "newuser@email.com",
|
"email": "newuser@email.com",
|
||||||
"password": "MyStrongPassword",
|
"password": "MyStrongPassword",
|
||||||
"group": "home",
|
"group": "Home",
|
||||||
"admin": False
|
"admin": False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ def test_update_user(api_client: requests, token):
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"fullName": "Updated Name",
|
"fullName": "Updated Name",
|
||||||
"email": "updated@email.com",
|
"email": "updated@email.com",
|
||||||
"group": "home",
|
"group": "Home",
|
||||||
"admin": True
|
"admin": True
|
||||||
}
|
}
|
||||||
response = api_client.put(f"{BASE}/1", headers=token, json=update_data)
|
response = api_client.put(f"{BASE}/1", headers=token, json=update_data)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue