mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
feat: Added feature to limit mealplan data by date range (#4111)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
parent
0aaa40432d
commit
5b3be18fe2
@ -19,3 +19,15 @@ class RepositoryMeals(HouseholdRepositoryGeneric[ReadPlanEntry, GroupMealPlan]):
|
||||
)
|
||||
plans = self.session.execute(stmt).scalars().all()
|
||||
return [self.schema.model_validate(x) for x in plans]
|
||||
|
||||
def get_meals_by_date_range(self, start_date: datetime, end_date: datetime) -> list[ReadPlanEntry]:
|
||||
if not self.household_id:
|
||||
raise Exception("household_id not set")
|
||||
|
||||
stmt = select(GroupMealPlan).filter(
|
||||
GroupMealPlan.date >= start_date.date(),
|
||||
GroupMealPlan.date <= end_date.date(),
|
||||
GroupMealPlan.household_id == self.household_id,
|
||||
)
|
||||
plans = self.session.execute(stmt).scalars().all()
|
||||
return [self.schema.model_validate(x) for x in plans]
|
||||
|
@ -16,7 +16,6 @@ from mealie.db.models.household.webhooks import GroupWebhooksModel
|
||||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.schema.household.group_events import GroupEventNotifierPrivate
|
||||
from mealie.schema.household.webhook import ReadWebhook
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
|
||||
from .event_types import Event, EventDocumentType, EventTypes, EventWebhookData
|
||||
from .publisher import ApprisePublisher, PublisherLike, WebhookPublisher
|
||||
@ -123,7 +122,14 @@ class AppriseEventListener(EventListenerBase):
|
||||
|
||||
@staticmethod
|
||||
def is_custom_url(url: str):
|
||||
return url.split(":", 1)[0].lower() in ["form", "forms", "json", "jsons", "xml", "xmls"]
|
||||
return url.split(":", 1)[0].lower() in [
|
||||
"form",
|
||||
"forms",
|
||||
"json",
|
||||
"jsons",
|
||||
"xml",
|
||||
"xmls",
|
||||
]
|
||||
|
||||
|
||||
class WebhookEventListener(EventListenerBase):
|
||||
@ -143,12 +149,12 @@ class WebhookEventListener(EventListenerBase):
|
||||
def publish_to_subscribers(self, event: Event, subscribers: list[ReadWebhook]) -> None:
|
||||
with self.ensure_repos(self.group_id, self.household_id) as repos:
|
||||
if event.document_data.document_type == EventDocumentType.mealplan:
|
||||
# TODO: limit mealplan data to a date range instead of returning all mealplans
|
||||
webhook_data = cast(EventWebhookData, event.document_data)
|
||||
meal_repo = repos.meals
|
||||
meal_pagination_data = meal_repo.page_all(pagination=PaginationQuery(page=1, per_page=-1))
|
||||
meal_data = meal_pagination_data.items
|
||||
meal_data = meal_repo.get_meals_by_date_range(
|
||||
webhook_data.webhook_start_dt, webhook_data.webhook_end_dt
|
||||
)
|
||||
if meal_data:
|
||||
webhook_data = cast(EventWebhookData, event.document_data)
|
||||
webhook_data.webhook_body = meal_data
|
||||
self.publisher.publish(event, [webhook.url for webhook in subscribers])
|
||||
|
||||
|
@ -5,6 +5,13 @@ from pydantic import UUID4
|
||||
|
||||
from mealie.schema.household.webhook import SaveWebhook, WebhookType
|
||||
from mealie.services.event_bus_service.event_bus_listeners import WebhookEventListener
|
||||
from mealie.services.event_bus_service.event_types import (
|
||||
Event,
|
||||
EventBusMessage,
|
||||
EventDocumentType,
|
||||
EventTypes,
|
||||
EventWebhookData,
|
||||
)
|
||||
from tests.utils import random_string
|
||||
from tests.utils.factories import random_bool
|
||||
from tests.utils.fixture_schemas import TestUser
|
||||
@ -69,3 +76,222 @@ def test_get_scheduled_webhooks_filter_query(unique_user: TestUser):
|
||||
if result.name == expected_item.name: # Names are uniquely generated so we can use this to compare
|
||||
assert result.enabled == expected_item.enabled
|
||||
break
|
||||
|
||||
|
||||
def test_event_listener_get_meals_by_date_range(unique_user: TestUser):
|
||||
"""
|
||||
Test that WebhookEventListener correctly uses the get_meals_by_date_range method
|
||||
to retrieve meals and publish the webhook event.
|
||||
"""
|
||||
meal_repo = unique_user.repos.meals
|
||||
|
||||
start_date = datetime.now(timezone.utc) - timedelta(days=7)
|
||||
end_date = datetime.now(timezone.utc)
|
||||
|
||||
meal_1 = meal_repo.create(
|
||||
{
|
||||
"date": start_date + timedelta(days=1),
|
||||
"entry_type": "lunch",
|
||||
"title": "Meal 1",
|
||||
"text": "Test meal 1",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
meal_2 = meal_repo.create(
|
||||
{
|
||||
"date": start_date + timedelta(days=3),
|
||||
"entry_type": "dinner",
|
||||
"title": "Meal 2",
|
||||
"text": "Test meal 2",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
|
||||
webhook_data = EventWebhookData(
|
||||
webhook_start_dt=start_date,
|
||||
webhook_end_dt=end_date,
|
||||
document_type=EventDocumentType.mealplan,
|
||||
operation="create",
|
||||
)
|
||||
event = Event(
|
||||
event_type=EventTypes.webhook_task,
|
||||
document_data=webhook_data,
|
||||
message=EventBusMessage(title="Test event message"),
|
||||
integration_id="00000000-0000-0000-0000-000000000000",
|
||||
)
|
||||
|
||||
event_bus_listener = WebhookEventListener(UUID(unique_user.group_id), UUID(unique_user.household_id))
|
||||
subscribers = event_bus_listener.get_scheduled_webhooks(start_date, end_date)
|
||||
|
||||
event_bus_listener.publish_to_subscribers(event, subscribers)
|
||||
|
||||
assert event.document_data.webhook_body is not None
|
||||
meals = event.document_data.webhook_body
|
||||
assert len(meals) == 2
|
||||
|
||||
assert any(meal.title == "Meal 1" for meal in meals)
|
||||
assert any(meal.title == "Meal 2" for meal in meals)
|
||||
|
||||
try:
|
||||
assert event.document_data.webhook_body is not None
|
||||
meals = event.document_data.webhook_body
|
||||
assert len(meals) == 2
|
||||
|
||||
assert any(meal.title == "Meal 1" for meal in meals)
|
||||
assert any(meal.title == "Meal 2" for meal in meals)
|
||||
|
||||
finally:
|
||||
meal_repo.delete(meal_1.id)
|
||||
meal_repo.delete(meal_2.id)
|
||||
|
||||
|
||||
def test_get_meals_by_date_range(unique_user: TestUser):
|
||||
meal_repo = unique_user.repos.meals
|
||||
|
||||
start_date = datetime.now(timezone.utc) - timedelta(days=7)
|
||||
end_date = datetime.now(timezone.utc)
|
||||
|
||||
meal_1 = meal_repo.create(
|
||||
{
|
||||
"date": start_date + timedelta(days=1),
|
||||
"entry_type": "breakfast",
|
||||
"title": "Meal 1",
|
||||
"text": "Test meal 1",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
meal_2 = meal_repo.create(
|
||||
{
|
||||
"date": start_date + timedelta(days=3),
|
||||
"entry_type": "lunch",
|
||||
"title": "Meal 2",
|
||||
"text": "Test meal 2",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
meal_3 = meal_repo.create(
|
||||
{
|
||||
"date": start_date - timedelta(days=10),
|
||||
"entry_type": "dinner",
|
||||
"title": "Meal 3",
|
||||
"text": "Test meal 3",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
meals_in_range = meal_repo.get_meals_by_date_range(start_date, end_date)
|
||||
|
||||
assert len(meals_in_range) == 2
|
||||
assert any(meal.title == "Meal 1" for meal in meals_in_range)
|
||||
assert any(meal.title == "Meal 2" for meal in meals_in_range)
|
||||
|
||||
assert all(meal.title != "Meal 3" for meal in meals_in_range)
|
||||
|
||||
finally:
|
||||
meal_repo.delete(meal_1.id)
|
||||
meal_repo.delete(meal_2.id)
|
||||
meal_repo.delete(meal_3.id)
|
||||
|
||||
|
||||
def test_get_meals_by_date_range_no_meals(unique_user: TestUser):
|
||||
"""
|
||||
Test that get_meals_by_date_range returns an empty list when there are no meals in the given date range.
|
||||
"""
|
||||
meal_repo = unique_user.repos.meals
|
||||
|
||||
start_date = datetime.now(timezone.utc) - timedelta(days=7)
|
||||
end_date = datetime.now(timezone.utc)
|
||||
|
||||
meals_in_range = meal_repo.get_meals_by_date_range(start_date, end_date)
|
||||
|
||||
assert len(meals_in_range) == 0
|
||||
|
||||
|
||||
def test_get_meals_by_date_range_single_day(unique_user: TestUser):
|
||||
"""
|
||||
Test that get_meals_by_date_range returns meals correctly when start_date and end_date are the same.
|
||||
"""
|
||||
meal_repo = unique_user.repos.meals
|
||||
|
||||
single_day = datetime.now(timezone.utc)
|
||||
|
||||
meal_1 = meal_repo.create(
|
||||
{
|
||||
"date": single_day,
|
||||
"entry_type": "breakfast",
|
||||
"title": "Single Day Meal",
|
||||
"text": "Test meal for a single day",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
meals_in_range = meal_repo.get_meals_by_date_range(single_day, single_day)
|
||||
|
||||
assert len(meals_in_range) == 1
|
||||
assert meals_in_range[0].title == "Single Day Meal"
|
||||
assert meals_in_range[0].date == single_day.date()
|
||||
|
||||
finally:
|
||||
meal_repo.delete(meal_1.id)
|
||||
|
||||
|
||||
def test_get_meals_by_date_range_no_overlap(unique_user: TestUser):
|
||||
"""
|
||||
Test that get_meals_by_date_range returns an empty list when there are no meals that overlap with the date range.
|
||||
"""
|
||||
meal_repo = unique_user.repos.meals
|
||||
|
||||
start_date = datetime.now(timezone.utc) + timedelta(days=1)
|
||||
end_date = datetime.now(timezone.utc) + timedelta(days=10)
|
||||
|
||||
meal_1 = meal_repo.create(
|
||||
{
|
||||
"date": datetime.now(timezone.utc) - timedelta(days=5),
|
||||
"entry_type": "dinner",
|
||||
"title": "Meal Outside Range",
|
||||
"text": "This meal is outside the tested date range",
|
||||
"group_id": unique_user.group_id,
|
||||
"household_id": unique_user.household_id,
|
||||
"user_id": unique_user.user_id,
|
||||
}
|
||||
)
|
||||
|
||||
meals_in_range = meal_repo.get_meals_by_date_range(start_date, end_date)
|
||||
|
||||
assert len(meals_in_range) == 0
|
||||
|
||||
try:
|
||||
meals_in_range = meal_repo.get_meals_by_date_range(start_date, end_date)
|
||||
|
||||
assert len(meals_in_range) == 0
|
||||
|
||||
finally:
|
||||
meal_repo.delete(meal_1.id)
|
||||
|
||||
|
||||
def test_get_meals_by_date_range_invalid_date_range(unique_user: TestUser):
|
||||
"""
|
||||
Test that get_meals_by_date_range raises an exception or returns empty when start_date is greater than end_date.
|
||||
"""
|
||||
meal_repo = unique_user.repos.meals
|
||||
|
||||
start_date = datetime.now(timezone.utc)
|
||||
end_date = start_date - timedelta(days=1)
|
||||
|
||||
meals_in_range = meal_repo.get_meals_by_date_range(start_date, end_date)
|
||||
|
||||
assert len(meals_in_range) == 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user