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:
Vlad Shulcz 2024-09-07 05:37:42 +03:00 committed by GitHub
parent 0aaa40432d
commit 5b3be18fe2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 250 additions and 6 deletions

View File

@ -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]

View File

@ -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])

View File

@ -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