Skip to content

Commit

Permalink
feat: Added feature to limit mealplan data by date range (#4111)
Browse files Browse the repository at this point in the history
Co-authored-by: Kuchenpirat <[email protected]>
  • Loading branch information
vshulcz and Kuchenpirat authored Sep 7, 2024
1 parent 0aaa404 commit 5b3be18
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 6 deletions.
12 changes: 12 additions & 0 deletions mealie/repos/repository_meals.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ def get_today(self) -> list[ReadPlanEntry]:
)
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]
18 changes: 12 additions & 6 deletions mealie/services/event_bus_service/event_bus_listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
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
Expand Down Expand Up @@ -123,7 +122,14 @@ def merge_query_parameters(url: str, params: dict):

@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):
Expand All @@ -143,12 +149,12 @@ def get_subscribers(self, event: Event) -> list[ReadWebhook]:
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])

Expand Down
226 changes: 226 additions & 0 deletions tests/unit_tests/services_tests/scheduler/tasks/test_post_webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

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
Expand Down Expand Up @@ -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

0 comments on commit 5b3be18

Please sign in to comment.