-
-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: more shopping list enhancements (#2587)
* fix new position calculataion * ensure consistent list item ordering * fix recipe ref overflow on small screens * added recipe ref elevation * tweaked line height (for long notes) * removed unused user dependency * remove old shopping list items when there's >100 * 🤷 * cleaned up function generator * fixed test * fix potential type error * made max position calc more efficient
- Loading branch information
1 parent
f35bc77
commit b153ddf
Showing
9 changed files
with
189 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
mealie/services/scheduler/tasks/delete_old_checked_shopping_list_items.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from collections.abc import Callable | ||
|
||
from pydantic import UUID4 | ||
|
||
from mealie.db.db_setup import session_context | ||
from mealie.repos.all_repositories import get_repositories | ||
from mealie.routes.groups.controller_shopping_lists import publish_list_item_events | ||
from mealie.schema.response.pagination import OrderDirection, PaginationQuery | ||
from mealie.schema.user.user import DEFAULT_INTEGRATION_ID | ||
from mealie.services.event_bus_service.event_bus_service import EventBusService | ||
from mealie.services.event_bus_service.event_types import EventDocumentDataBase, EventTypes | ||
from mealie.services.group_services.shopping_lists import ShoppingListService | ||
|
||
MAX_CHECKED_ITEMS = 100 | ||
|
||
|
||
def _create_publish_event(event_bus_service: EventBusService, group_id: UUID4): | ||
def publish_event(event_type: EventTypes, document_data: EventDocumentDataBase, message: str = ""): | ||
event_bus_service.dispatch( | ||
integration_id=DEFAULT_INTEGRATION_ID, | ||
group_id=group_id, | ||
event_type=event_type, | ||
document_data=document_data, | ||
message=message, | ||
) | ||
|
||
return publish_event | ||
|
||
|
||
def _trim_list_items(shopping_list_service: ShoppingListService, shopping_list_id: UUID4, event_publisher: Callable): | ||
pagination = PaginationQuery( | ||
page=1, | ||
per_page=-1, | ||
query_filter=f'shopping_list_id="{shopping_list_id}" AND checked=true', | ||
order_by="update_at", | ||
order_direction=OrderDirection.desc, | ||
) | ||
query = shopping_list_service.list_items.page_all(pagination) | ||
if len(query.items) <= MAX_CHECKED_ITEMS: | ||
return | ||
|
||
items_to_delete = query.items[MAX_CHECKED_ITEMS:] | ||
items_response = shopping_list_service.bulk_delete_items([item.id for item in items_to_delete]) | ||
publish_list_item_events(event_publisher, items_response) | ||
|
||
|
||
def delete_old_checked_list_items(group_id: UUID4 | None = None): | ||
with session_context() as session: | ||
repos = get_repositories(session) | ||
if group_id is None: | ||
# if not specified, we check all groups | ||
groups = repos.groups.page_all(PaginationQuery(page=1, per_page=-1)).items | ||
|
||
else: | ||
group = repos.groups.get_one(group_id) | ||
if not group: | ||
raise Exception(f'Group not found: "{group_id}"') | ||
|
||
groups = [group] | ||
|
||
for group in groups: | ||
event_bus_service = EventBusService(session=session, group_id=group.id) | ||
shopping_list_service = ShoppingListService(repos, group) | ||
shopping_list_data = repos.group_shopping_lists.by_group(group.id).page_all( | ||
PaginationQuery(page=1, per_page=-1) | ||
) | ||
for shopping_list in shopping_list_data.items: | ||
_trim_list_items( | ||
shopping_list_service, shopping_list.id, _create_publish_event(event_bus_service, group.id) | ||
) |
90 changes: 90 additions & 0 deletions
90
.../unit_tests/services_tests/scheduler/tasks/test_delete_old_checked_shopping_list_items.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
from datetime import datetime | ||
|
||
from mealie.repos.repository_factory import AllRepositories | ||
from mealie.schema.group.group_shopping_list import ShoppingListItemCreate, ShoppingListItemOut, ShoppingListSave | ||
from mealie.services.scheduler.tasks.delete_old_checked_shopping_list_items import ( | ||
MAX_CHECKED_ITEMS, | ||
delete_old_checked_list_items, | ||
) | ||
from tests.utils.factories import random_int, random_string | ||
from tests.utils.fixture_schemas import TestUser | ||
|
||
|
||
def test_cleanup(database: AllRepositories, unique_user: TestUser): | ||
list_repo = database.group_shopping_lists.by_group(unique_user.group_id) | ||
list_item_repo = database.group_shopping_list_item | ||
|
||
shopping_list = list_repo.create(ShoppingListSave(name=random_string(), group_id=unique_user.group_id)) | ||
unchecked_items = list_item_repo.create_many( | ||
[ | ||
ShoppingListItemCreate(note=random_string(), shopping_list_id=shopping_list.id) | ||
for _ in range(random_int(MAX_CHECKED_ITEMS + 10, MAX_CHECKED_ITEMS + 20)) | ||
] | ||
) | ||
|
||
# create them one at a time so the update timestamps are different | ||
checked_items: list[ShoppingListItemOut] = [] | ||
for _ in range(random_int(MAX_CHECKED_ITEMS + 10, MAX_CHECKED_ITEMS + 20)): | ||
new_item = list_item_repo.create( | ||
ShoppingListItemCreate(note=random_string(), shopping_list_id=shopping_list.id) | ||
) | ||
new_item.checked = True | ||
checked_items.append(list_item_repo.update(new_item.id, new_item)) | ||
|
||
# make sure we see all items | ||
shopping_list = list_repo.get_one(shopping_list.id) # type: ignore | ||
assert shopping_list | ||
assert len(shopping_list.list_items) == len(unchecked_items) + len(checked_items) | ||
for item in unchecked_items + checked_items: | ||
assert item in shopping_list.list_items | ||
|
||
checked_items.sort(key=lambda x: x.update_at or datetime.now(), reverse=True) | ||
expected_kept_items = unchecked_items + checked_items[:MAX_CHECKED_ITEMS] | ||
expected_deleted_items = checked_items[MAX_CHECKED_ITEMS:] | ||
|
||
# make sure we only see the expected items | ||
delete_old_checked_list_items() | ||
shopping_list = list_repo.get_one(shopping_list.id) # type: ignore | ||
assert shopping_list | ||
assert len(shopping_list.list_items) == len(expected_kept_items) | ||
for item in expected_kept_items: | ||
assert item in shopping_list.list_items | ||
for item in expected_deleted_items: | ||
assert item not in shopping_list.list_items | ||
|
||
|
||
def test_no_cleanup(database: AllRepositories, unique_user: TestUser): | ||
list_repo = database.group_shopping_lists.by_group(unique_user.group_id) | ||
list_item_repo = database.group_shopping_list_item | ||
|
||
shopping_list = list_repo.create(ShoppingListSave(name=random_string(), group_id=unique_user.group_id)) | ||
unchecked_items = list_item_repo.create_many( | ||
[ | ||
ShoppingListItemCreate(note=random_string(), shopping_list_id=shopping_list.id) | ||
for _ in range(MAX_CHECKED_ITEMS) | ||
] | ||
) | ||
|
||
# create them one at a time so the update timestamps are different | ||
checked_items: list[ShoppingListItemOut] = [] | ||
for _ in range(MAX_CHECKED_ITEMS): | ||
new_item = list_item_repo.create( | ||
ShoppingListItemCreate(note=random_string(), shopping_list_id=shopping_list.id) | ||
) | ||
new_item.checked = True | ||
checked_items.append(list_item_repo.update(new_item.id, new_item)) | ||
|
||
# make sure we see all items | ||
shopping_list = list_repo.get_one(shopping_list.id) # type: ignore | ||
assert shopping_list | ||
assert len(shopping_list.list_items) == len(unchecked_items) + len(checked_items) | ||
for item in unchecked_items + checked_items: | ||
assert item in shopping_list.list_items | ||
|
||
# make sure we still see all items | ||
delete_old_checked_list_items() | ||
shopping_list = list_repo.get_one(shopping_list.id) # type: ignore | ||
assert shopping_list | ||
assert len(shopping_list.list_items) == len(unchecked_items) + len(checked_items) | ||
for item in unchecked_items + checked_items: | ||
assert item in shopping_list.list_items |