1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-03 04:25:24 +02:00

Feature/automated meal planner (#939)

* cleanup oversized buttons

* add get all by category function to reciep repos

* fix shopping-list can_merge logic

* use randomized data for testing

* add random getter to repository for meal-planner

* add stub route for random meals

* cleanup global namespace

* add rules database type

* fix type

* add plan rules schema

* test plan rules methods

* add mealplan rules controller

* add new repository

* update frontend types

* formatting

* fix regression

* update autogenerated types

* add api class for mealplan rules

* add tests and fix bugs

* fix data returns

* proof of concept rules editor

* add tag support

* remove old group categories

* add tag support

* implement random by rules api

* change snack to sides

* remove incorrect typing

* split repo for custom methods

* fix query and use and_ clause

* use repo function

* remove old test

* update changelog
This commit is contained in:
Hayden 2022-02-07 19:03:11 -09:00 committed by GitHub
parent 40d1f586cd
commit d1024e272d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 1153 additions and 175 deletions

View file

@ -0,0 +1,127 @@
from uuid import UUID
import pytest
from fastapi.testclient import TestClient
from pydantic import UUID4
from mealie.repos.all_repositories import AllRepositories
from mealie.schema.meal_plan.plan_rules import PlanRulesOut, PlanRulesSave
from mealie.schema.recipe.recipe import RecipeCategory
from tests import utils
from tests.utils.fixture_schemas import TestUser
class Routes:
base = "/api/groups/mealplans/rules"
@staticmethod
def item(item_id: UUID4) -> str:
return f"{Routes.base}/{item_id}"
@pytest.fixture(scope="function")
def category(database: AllRepositories):
slug = utils.random_string(length=10)
model = database.categories.create(RecipeCategory(slug=slug, name=slug))
yield model
try:
database.categories.delete(model.slug)
except Exception:
pass
@pytest.fixture(scope="function")
def plan_rule(database: AllRepositories, unique_user: TestUser):
schema = PlanRulesSave(
group_id=unique_user.group_id,
day="monday",
entry_type="breakfast",
categories=[],
)
model = database.group_meal_plan_rules.create(schema)
yield model
try:
database.group_meal_plan_rules.delete(model.id)
except Exception:
pass
def test_group_mealplan_rules_create(
api_client: TestClient, unique_user: TestUser, category: RecipeCategory, database: AllRepositories
):
payload = {
"groupId": unique_user.group_id,
"day": "monday",
"entryType": "breakfast",
"categories": [category.dict()],
}
response = api_client.post(Routes.base, json=payload, headers=unique_user.token)
assert response.status_code == 201
# Validate the response data
response_data = response.json()
assert response_data["groupId"] == str(unique_user.group_id)
assert response_data["day"] == "monday"
assert response_data["entryType"] == "breakfast"
assert len(response_data["categories"]) == 1
assert response_data["categories"][0]["slug"] == category.slug
# Validate database entry
rule = database.group_meal_plan_rules.get_one(UUID(response_data["id"]))
assert str(rule.group_id) == unique_user.group_id
assert rule.day == "monday"
assert rule.entry_type == "breakfast"
assert len(rule.categories) == 1
assert rule.categories[0].slug == category.slug
# Cleanup
database.group_meal_plan_rules.delete(rule.id)
def test_group_mealplan_rules_read(api_client: TestClient, unique_user: TestUser, plan_rule: PlanRulesOut):
response = api_client.get(Routes.item(plan_rule.id), headers=unique_user.token)
assert response.status_code == 200
# Validate the response data
response_data = response.json()
assert response_data["id"] == str(plan_rule.id)
assert response_data["groupId"] == str(unique_user.group_id)
assert response_data["day"] == "monday"
assert response_data["entryType"] == "breakfast"
assert len(response_data["categories"]) == 0
def test_group_mealplan_rules_update(api_client: TestClient, unique_user: TestUser, plan_rule: PlanRulesOut):
payload = {
"groupId": unique_user.group_id,
"day": "tuesday",
"entryType": "lunch",
}
response = api_client.put(Routes.item(plan_rule.id), json=payload, headers=unique_user.token)
assert response.status_code == 200
# Validate the response data
response_data = response.json()
assert response_data["id"] == str(plan_rule.id)
assert response_data["groupId"] == str(unique_user.group_id)
assert response_data["day"] == "tuesday"
assert response_data["entryType"] == "lunch"
assert len(response_data["categories"]) == 0
def test_group_mealplan_rules_delete(
api_client: TestClient, unique_user: TestUser, plan_rule: PlanRulesOut, database: AllRepositories
):
response = api_client.delete(Routes.item(plan_rule.id), headers=unique_user.token)
assert response.status_code == 200
# Validate no entry in database
assert database.group_meal_plan_rules.get_one(plan_rule.id) is None

View file

@ -0,0 +1,115 @@
from mealie.repos.repository_factory import AllRepositories
from mealie.repos.repository_recipes import RepositoryRecipes
from mealie.schema.recipe.recipe import Recipe, RecipeCategory
from tests.utils.factories import random_string
from tests.utils.fixture_schemas import TestUser
def test_recipe_repo_get_by_categories_basic(database: AllRepositories, unique_user: TestUser):
# Bootstrap the database with categories
slug1, slug2, slug3 = [random_string(10) for _ in range(3)]
categories = [
RecipeCategory(name=slug1, slug=slug1),
RecipeCategory(name=slug2, slug=slug2),
RecipeCategory(name=slug3, slug=slug3),
]
created_categories = []
for category in categories:
model = database.categories.create(category)
created_categories.append(model)
# Bootstrap the database with recipes
recipes = []
for idx in range(15):
if idx % 3 == 0:
category = created_categories[0]
elif idx % 3 == 1:
category = created_categories[1]
else:
category = created_categories[2]
recipes.append(
Recipe(
user_id=unique_user.user_id,
group_id=unique_user.group_id,
name=random_string(),
recipe_category=[category],
),
)
created_recipes = []
for recipe in recipes:
models = database.recipes.create(recipe)
created_recipes.append(models)
# Get all recipes by category
for category in created_categories:
repo: RepositoryRecipes = database.recipes.by_group(unique_user.group_id)
recipes = repo.get_by_categories([category])
assert len(recipes) == 5
for recipe in recipes:
found_cat = recipe.recipe_category[0]
assert found_cat.name == category.name
assert found_cat.slug == category.slug
assert found_cat.id == category.id
def test_recipe_repo_get_by_categories_multi(database: AllRepositories, unique_user: TestUser):
slug1, slug2 = [random_string(10) for _ in range(2)]
categories = [
RecipeCategory(name=slug1, slug=slug1),
RecipeCategory(name=slug2, slug=slug2),
]
created_categories = []
known_category_ids = []
for category in categories:
model = database.categories.create(category)
created_categories.append(model)
known_category_ids.append(model.id)
# Bootstrap the database with recipes
recipes = []
for _ in range(10):
recipes.append(
Recipe(
user_id=unique_user.user_id,
group_id=unique_user.group_id,
name=random_string(),
recipe_category=created_categories,
),
)
# Insert Non-Category Recipes
recipes.append(
Recipe(
user_id=unique_user.user_id,
group_id=unique_user.group_id,
name=random_string(),
)
)
for recipe in recipes:
database.recipes.create(recipe)
# Get all recipes by both categories
repo: RepositoryRecipes = database.recipes.by_group(unique_user.group_id)
by_category = repo.get_by_categories(created_categories)
assert len(by_category) == 10
for recipe in by_category:
for category in recipe.recipe_category:
assert category.id in known_category_ids

View file

@ -0,0 +1,20 @@
from datetime import datetime
import pytest
from mealie.schema.meal_plan.plan_rules import PlanRulesDay
test_cases = [
(datetime(2022, 2, 7), PlanRulesDay.monday),
(datetime(2022, 2, 8), PlanRulesDay.tuesday),
(datetime(2022, 2, 9), PlanRulesDay.wednesday),
(datetime(2022, 2, 10), PlanRulesDay.thursday),
(datetime(2022, 2, 11), PlanRulesDay.friday),
(datetime(2022, 2, 12), PlanRulesDay.saturday),
(datetime(2022, 2, 13), PlanRulesDay.sunday),
]
@pytest.mark.parametrize("date, expected", test_cases)
def test_date_obj_to_enum(date: datetime, expected: PlanRulesDay):
assert PlanRulesDay.from_date(date) == expected