mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
add base for meal-plan categories
This commit is contained in:
parent
dc6ff37252
commit
d9b9b36747
20 changed files with 133 additions and 31 deletions
|
@ -11,8 +11,6 @@
|
|||
</span>
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<HomePageSettings />
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<h2 class="mt-1 mb-4">{{ $t("settings.language") }}</h2>
|
||||
<v-row>
|
||||
|
@ -30,6 +28,8 @@
|
|||
</v-row>
|
||||
</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<HomePageSettings />
|
||||
<v-divider></v-divider>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
|
@ -59,6 +59,9 @@ export default {
|
|||
this.langOptions = this.$store.getters.getAllLangs;
|
||||
this.selectedLang = this.$store.getters.getActiveLang;
|
||||
},
|
||||
removeCategory(index) {
|
||||
this.value.categories.splice(index, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,45 @@
|
|||
<template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
{{ $t("settings.webhooks.meal-planner-webhooks") }}
|
||||
{{ $t("meal-plan.meal-planner") }}
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<h2 class="mt-1">{{ $t("recipe.categories") }}</h2>
|
||||
|
||||
<v-row>
|
||||
<v-col sm="12" md="6">
|
||||
<v-select
|
||||
v-model="planCategories"
|
||||
:items="categories"
|
||||
item-text="name"
|
||||
item-value="name"
|
||||
label="Allowed Categories"
|
||||
multiple
|
||||
chips
|
||||
hint="Only recipes with these categories will be used in Meal Plans"
|
||||
persistent-hint
|
||||
>
|
||||
<template v-slot:selection="data">
|
||||
<v-chip
|
||||
:input-value="data.selected"
|
||||
close
|
||||
@click:close="removeCategory(data.index)"
|
||||
color="secondary"
|
||||
dark
|
||||
>
|
||||
{{ data.item.name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-divider> </v-divider>
|
||||
<v-card-text>
|
||||
<h2 class="mt-1 mb-4">
|
||||
{{ $t("settings.webhooks.meal-planner-webhooks") }}
|
||||
</h2>
|
||||
<p>
|
||||
{{
|
||||
$t(
|
||||
|
@ -68,21 +104,29 @@ export default {
|
|||
webhooks: [],
|
||||
enabled: false,
|
||||
time: "",
|
||||
planCategories: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getSiteSettings();
|
||||
},
|
||||
computed: {
|
||||
categories() {
|
||||
return this.$store.getters.getCategories;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
saveTime(value) {
|
||||
this.time = value;
|
||||
},
|
||||
async getSiteSettings() {
|
||||
let settings = await api.settings.requestAll();
|
||||
console.log(settings);
|
||||
this.webhooks = settings.webhooks.webhookURLs;
|
||||
this.name = settings.name;
|
||||
this.time = settings.webhooks.webhookTime;
|
||||
this.enabled = settings.webhooks.enabled;
|
||||
this.planCategories = settings.planCategories;
|
||||
},
|
||||
addWebhook() {
|
||||
this.webhooks.push(" ");
|
||||
|
@ -93,6 +137,7 @@ export default {
|
|||
saveWebhooks() {
|
||||
const body = {
|
||||
name: this.name,
|
||||
planCategories: this.planCategories,
|
||||
webhooks: {
|
||||
webhookURLs: this.webhooks,
|
||||
webhookTime: this.time,
|
||||
|
@ -104,6 +149,9 @@ export default {
|
|||
testWebhooks() {
|
||||
api.settings.testWebhooks();
|
||||
},
|
||||
removeCategory(index) {
|
||||
this.planCategories.splice(index, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -45,6 +45,7 @@
|
|||
},
|
||||
"meal-plan": {
|
||||
"dinner-this-week": "Dinner This Week",
|
||||
"meal-planner": "Meal Planner",
|
||||
"dinner-today": "Dinner Today",
|
||||
"planner": "Planner",
|
||||
"edit-meal-plan": "Edit Meal Plan",
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<General />
|
||||
<Theme class="mt-2" />
|
||||
<Backup class="mt-2" />
|
||||
<Webhooks class="mt-2" />
|
||||
<MealPlanner class="mt-2" />
|
||||
<Migration class="mt-2" />
|
||||
<p class="text-center my-2">
|
||||
{{ $t("settings.current") }}
|
||||
|
@ -41,7 +41,7 @@
|
|||
<script>
|
||||
import Backup from "../components/Settings/Backup";
|
||||
import General from "../components/Settings/General";
|
||||
import Webhooks from "../components/Settings/Webhook";
|
||||
import MealPlanner from "../components/Settings/MealPlanner";
|
||||
import Theme from "../components/Settings/Theme";
|
||||
import Migration from "../components/Settings/Migration";
|
||||
import api from "@/api";
|
||||
|
@ -50,7 +50,7 @@ import axios from "axios";
|
|||
export default {
|
||||
components: {
|
||||
Backup,
|
||||
Webhooks,
|
||||
MealPlanner,
|
||||
Theme,
|
||||
Migration,
|
||||
General,
|
||||
|
|
|
@ -29,6 +29,7 @@ const store = new Vuex.Store({
|
|||
// All Recipe Data Store
|
||||
recentRecipes: [],
|
||||
allRecipes: [],
|
||||
mealPlanCategories: [],
|
||||
},
|
||||
|
||||
mutations: {
|
||||
|
@ -44,6 +45,11 @@ const store = new Vuex.Store({
|
|||
setRecentRecipes(state, payload) {
|
||||
state.recentRecipes = payload;
|
||||
},
|
||||
|
||||
setMealPlanCategories(state, payload) {
|
||||
console.log(payload);
|
||||
state.mealPlanCategories = payload;
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -69,6 +75,7 @@ const store = new Vuex.Store({
|
|||
getSnackType: state => state.snackType,
|
||||
|
||||
getRecentRecipes: state => state.recentRecipes,
|
||||
getMealPlanCategories: state => state.mealPlanCategories,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ dotenv.load_dotenv(ENV)
|
|||
|
||||
# General
|
||||
APP_VERSION = "v0.2.1"
|
||||
DB_VERSION = "v0.2.0"
|
||||
DB_VERSION = "v0.2.1"
|
||||
PRODUCTION = os.environ.get("ENV")
|
||||
PORT = int(os.getenv("mealie_port", 9000))
|
||||
API = os.getenv("api_docs", True)
|
||||
|
|
|
@ -15,7 +15,7 @@ from db.sql.theme_models import SiteThemeModel
|
|||
class _Recipes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "slug"
|
||||
self.sql_model = RecipeModel
|
||||
self.sql_model: RecipeModel = RecipeModel
|
||||
|
||||
def update_image(self, session: Session, slug: str, extension: str = None) -> str:
|
||||
entry: RecipeModel = self._query_one(session, match_value=slug)
|
||||
|
@ -48,15 +48,6 @@ class _Settings(BaseDocument):
|
|||
self.primary_key = "name"
|
||||
self.sql_model = SiteSettingsModel
|
||||
|
||||
def create(self, session: Session, main: dict, webhooks: dict) -> str:
|
||||
new_settings = self.sql_model(main.get("name"), webhooks)
|
||||
|
||||
session.add(new_settings)
|
||||
return_data = new_settings.dict()
|
||||
session.commit()
|
||||
|
||||
return return_data
|
||||
|
||||
|
||||
class _Themes(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
|
|
|
@ -85,6 +85,13 @@ class Category(SqlAlchemyBase):
|
|||
"recipes": [x.dict() for x in self.recipes],
|
||||
}
|
||||
|
||||
def dict_no_recipes(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"slug": self.slug,
|
||||
"name": self.name,
|
||||
}
|
||||
|
||||
|
||||
class Tag(SqlAlchemyBase):
|
||||
__tablename__ = "tags"
|
||||
|
|
|
@ -1,27 +1,54 @@
|
|||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from db.sql.model_base import BaseMixins, SqlAlchemyBase
|
||||
from db.sql.recipe_models import Category
|
||||
|
||||
|
||||
class SiteSettingsModel(SqlAlchemyBase):
|
||||
class SiteSettingsModel(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")
|
||||
|
||||
def __init__(self, name: str = None, webhooks: dict = None, session=None) -> None:
|
||||
def __init__(
|
||||
self, name: str = None, webhooks: dict = None, planCategories=[], session=None
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.planCategories = [MealCategory(cat) for cat in planCategories]
|
||||
self.webhooks = WebHookModel(**webhooks)
|
||||
|
||||
def update(self, session, name, webhooks: dict) -> dict:
|
||||
def update(self, session, name, webhooks: dict, planCategories=[]) -> dict:
|
||||
|
||||
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, "webhooks": self.webhooks.dict()}
|
||||
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)
|
||||
|
|
0
mealie/dist/.gitkeep
vendored
0
mealie/dist/.gitkeep
vendored
|
@ -1,4 +1,4 @@
|
|||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic.main import BaseModel
|
||||
from services.recipe_services import Recipe
|
||||
|
@ -8,7 +8,7 @@ class RecipeCategoryResponse(BaseModel):
|
|||
id: int
|
||||
name: str
|
||||
slug: str
|
||||
recipes: List[Recipe]
|
||||
recipes: Optional[List[Recipe]]
|
||||
|
||||
class Config:
|
||||
schema_extra = {"example": {"id": 1, "name": "dinner", "recipes": [{}]}}
|
||||
|
|
|
@ -3,6 +3,7 @@ from typing import List, Optional
|
|||
from pydantic import BaseModel
|
||||
|
||||
|
||||
|
||||
class Webhooks(BaseModel):
|
||||
webhookTime: str = "00:00"
|
||||
webhookURLs: Optional[List[str]] = []
|
||||
|
@ -11,12 +12,14 @@ class Webhooks(BaseModel):
|
|||
|
||||
class SiteSettings(BaseModel):
|
||||
name: str = "main"
|
||||
planCategories: list[str] = []
|
||||
webhooks: Webhooks
|
||||
|
||||
class Config:
|
||||
schema_extra = {
|
||||
"example": {
|
||||
"name": "main",
|
||||
"planCategories": ["dinner", "lunch"],
|
||||
"webhooks": {
|
||||
"webhookTime": "00:00",
|
||||
"webhookURLs": ["https://mywebhookurl.com/webhook"],
|
||||
|
|
|
@ -69,3 +69,15 @@ def get_all_recipes_post(
|
|||
"""
|
||||
|
||||
return db.recipes.get_all_limit_columns(session, body.properties, body.limit)
|
||||
|
||||
|
||||
@router.post("/api/category")
|
||||
async def filter_by_category(
|
||||
categories: list, session: Session = Depends(generate_session)
|
||||
):
|
||||
""" pass a list of categories and get a list of recipes associated with those categories """
|
||||
#! This should be refactored into a single database call, but I couldn't figure it out
|
||||
in_category = [db.categories.get(session, cat) for cat in categories]
|
||||
in_category = [cat.get("recipes") for cat in in_category]
|
||||
in_category = [item for sublist in in_category for item in sublist]
|
||||
return in_category
|
||||
|
|
|
@ -3,7 +3,6 @@ from db.db_setup import generate_session
|
|||
from fastapi import APIRouter, Depends
|
||||
from models.category_models import RecipeCategoryResponse
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from utils.snackbar import SnackResponse
|
||||
|
||||
router = APIRouter(
|
||||
|
@ -26,13 +25,14 @@ def get_all_recipes_by_category(
|
|||
return db.categories.get(session, category)
|
||||
|
||||
|
||||
|
||||
@router.delete("/{category}")
|
||||
async def delete_recipe_category(
|
||||
category: str, session: Session = Depends(generate_session)
|
||||
):
|
||||
""" Removes a recipe category from the database. Deleting a
|
||||
"""Removes a recipe category from the database. Deleting a
|
||||
category does not impact a recipe. The category will be removed
|
||||
from any recipes that contain it """
|
||||
from any recipes that contain it"""
|
||||
|
||||
db.categories.delete(session, category)
|
||||
|
||||
|
|
|
@ -19,13 +19,14 @@ def get_main_settings(session: Session = Depends(generate_session)):
|
|||
except:
|
||||
default_settings_init(session)
|
||||
data = db.settings.get(session, "main")
|
||||
|
||||
print(data)
|
||||
return data
|
||||
|
||||
|
||||
@router.put("")
|
||||
def update_settings(data: SiteSettings, session: Session = Depends(generate_session)):
|
||||
""" Returns Site Settings """
|
||||
print("Categories", data.planCategories)
|
||||
db.settings.update(session, "main", data.dict())
|
||||
|
||||
return SnackResponse.success("Settings Updated")
|
||||
|
|
|
@ -3,6 +3,7 @@ from pathlib import Path
|
|||
|
||||
import requests
|
||||
from app_config import IMG_DIR
|
||||
from utils.logger import logger
|
||||
|
||||
|
||||
def read_image(recipe_slug: str) -> Path:
|
||||
|
@ -48,6 +49,7 @@ def scrape_image(image_url: str, slug: str) -> Path:
|
|||
try:
|
||||
r = requests.get(image_url, stream=True)
|
||||
except:
|
||||
logger.exception("Fatal Image Request Exception")
|
||||
return None
|
||||
|
||||
if r.status_code == 200:
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from datetime import date, timedelta
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from db.database import db
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from db.database import db
|
||||
from db.db_setup import create_session, sql_exists
|
||||
from db.db_setup import create_session
|
||||
from models.settings_models import SiteSettings, Webhooks
|
||||
from sqlalchemy.orm.session import Session
|
||||
from utils.logger import logger
|
||||
|
||||
|
||||
def default_settings_init(session: Session = None):
|
||||
|
@ -10,7 +11,7 @@ def default_settings_init(session: Session = None):
|
|||
try:
|
||||
webhooks = Webhooks()
|
||||
default_entry = SiteSettings(name="main", webhooks=webhooks)
|
||||
document = db.settings.create(session, default_entry.dict(), webhooks.dict())
|
||||
document = db.settings.create(session, default_entry.dict())
|
||||
logger.info(f"Created Site Settings: \n {document}")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue