Refactor to-do list order and reordering in Habitica (#138566)

This commit is contained in:
Manu 2025-02-24 17:36:20 +01:00 committed by GitHub
parent 461039f06a
commit 2e5f56b70d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 84 additions and 28 deletions

View File

@ -117,20 +117,24 @@ class BaseHabiticaListEntity(HabiticaBase, TodoListEntity):
"""Move an item in the To-do list.""" """Move an item in the To-do list."""
if TYPE_CHECKING: if TYPE_CHECKING:
assert self.todo_items assert self.todo_items
tasks_order = (
self.coordinator.data.user.tasksOrder.todos
if self.entity_description.key is HabiticaTodoList.TODOS
else self.coordinator.data.user.tasksOrder.dailys
)
if previous_uid: if previous_uid:
pos = self.todo_items.index( pos = tasks_order.index(UUID(previous_uid))
next(item for item in self.todo_items if item.uid == previous_uid) if pos < tasks_order.index(UUID(uid)):
)
if pos < self.todo_items.index(
next(item for item in self.todo_items if item.uid == uid)
):
pos += 1 pos += 1
else: else:
pos = 0 pos = 0
try: try:
await self.coordinator.habitica.reorder_task(UUID(uid), pos) tasks_order[:] = (
await self.coordinator.habitica.reorder_task(UUID(uid), pos)
).data
except TooManyRequestsError as e: except TooManyRequestsError as e:
raise HomeAssistantError( raise HomeAssistantError(
translation_domain=DOMAIN, translation_domain=DOMAIN,
@ -144,20 +148,6 @@ class BaseHabiticaListEntity(HabiticaBase, TodoListEntity):
translation_key=f"move_{self.entity_description.key}_item_failed", translation_key=f"move_{self.entity_description.key}_item_failed",
translation_placeholders={"pos": str(pos)}, translation_placeholders={"pos": str(pos)},
) from e ) from e
else:
# move tasks in the coordinator until we have fresh data
tasks = self.coordinator.data.tasks
new_pos = (
tasks.index(
next(task for task in tasks if task.id == UUID(previous_uid))
)
+ 1
if previous_uid
else 0
)
old_pos = tasks.index(next(task for task in tasks if task.id == UUID(uid)))
tasks.insert(new_pos, tasks.pop(old_pos))
await self.coordinator.async_request_refresh()
async def async_update_todo_item(self, item: TodoItem) -> None: async def async_update_todo_item(self, item: TodoItem) -> None:
"""Update a Habitica todo.""" """Update a Habitica todo."""
@ -271,7 +261,7 @@ class HabiticaTodosListEntity(BaseHabiticaListEntity):
def todo_items(self) -> list[TodoItem]: def todo_items(self) -> list[TodoItem]:
"""Return the todo items.""" """Return the todo items."""
return [ tasks = [
*( *(
TodoItem( TodoItem(
uid=str(task.id), uid=str(task.id),
@ -288,6 +278,15 @@ class HabiticaTodosListEntity(BaseHabiticaListEntity):
if task.Type is TaskType.TODO if task.Type is TaskType.TODO
), ),
] ]
return sorted(
tasks,
key=lambda task: (
float("inf")
if (uid := UUID(task.uid))
not in (tasks_order := self.coordinator.data.user.tasksOrder.todos)
else tasks_order.index(uid)
),
)
async def async_create_todo_item(self, item: TodoItem) -> None: async def async_create_todo_item(self, item: TodoItem) -> None:
"""Create a Habitica todo.""" """Create a Habitica todo."""
@ -348,7 +347,7 @@ class HabiticaDailiesListEntity(BaseHabiticaListEntity):
if TYPE_CHECKING: if TYPE_CHECKING:
assert self.coordinator.data.user.lastCron assert self.coordinator.data.user.lastCron
return [ tasks = [
*( *(
TodoItem( TodoItem(
uid=str(task.id), uid=str(task.id),
@ -365,3 +364,12 @@ class HabiticaDailiesListEntity(BaseHabiticaListEntity):
if task.Type is TaskType.DAILY if task.Type is TaskType.DAILY
) )
] ]
return sorted(
tasks,
key=lambda task: (
float("inf")
if (uid := UUID(task.uid))
not in (tasks_order := self.coordinator.data.user.tasksOrder.dailys)
else tasks_order.index(uid)
),
)

View File

@ -0,0 +1,15 @@
{
"success": true,
"data": [
"f21fa608-cfc6-4413-9fc7-0eb1b48ca43a",
"2c6d136c-a1c3-4bef-b7c4-fa980784b1e1",
"bc1d1855-b2b8-4663-98ff-62e7b763dfc4",
"e97659e0-2c42-4599-a7bb-00282adc410d",
"564b9ac9-c53d-4638-9e7f-1cd96fe19baa",
"f2c85972-1a19-4426-bc6d-ce3337b9d99f",
"6e53f1f5-a315-4edd-984d-8d762e4a08ef"
],
"notifications": [],
"userV": 589,
"appVersion": "5.28.6"
}

View File

@ -0,0 +1,12 @@
{
"success": true,
"data": [
"88de7cd9-af2b-49ce-9afd-bf941d87336b",
"1aa3137e-ef72-4d1f-91ee-41933602f438",
"2f6fcabc-f670-4ec3-ba65-817e8deea490",
"86ea2475-d1b5-4020-bdcc-c188c7996afa"
],
"notifications": [],
"userV": 589,
"appVersion": "5.28.6"
}

View File

@ -6,7 +6,13 @@ from typing import Any
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
from uuid import UUID from uuid import UUID
from habiticalib import Direction, HabiticaTasksResponse, Task, TaskType from habiticalib import (
Direction,
HabiticaTaskOrderResponse,
HabiticaTasksResponse,
Task,
TaskType,
)
import pytest import pytest
from syrupy.assertion import SnapshotAssertion from syrupy.assertion import SnapshotAssertion
@ -601,19 +607,23 @@ async def test_delete_completed_todo_items_exception(
@pytest.mark.parametrize( @pytest.mark.parametrize(
("entity_id", "uid", "second_pos", "third_pos"), ("entity_id", "uid", "second_pos", "third_pos", "fixture", "task_type"),
[ [
( (
"todo.test_user_to_do_s", "todo.test_user_to_do_s",
"1aa3137e-ef72-4d1f-91ee-41933602f438", "1aa3137e-ef72-4d1f-91ee-41933602f438",
"88de7cd9-af2b-49ce-9afd-bf941d87336b", "88de7cd9-af2b-49ce-9afd-bf941d87336b",
"2f6fcabc-f670-4ec3-ba65-817e8deea490", "2f6fcabc-f670-4ec3-ba65-817e8deea490",
"reorder_todos_response.json",
"todos",
), ),
( (
"todo.test_user_dailies", "todo.test_user_dailies",
"2c6d136c-a1c3-4bef-b7c4-fa980784b1e1", "2c6d136c-a1c3-4bef-b7c4-fa980784b1e1",
"564b9ac9-c53d-4638-9e7f-1cd96fe19baa", "f21fa608-cfc6-4413-9fc7-0eb1b48ca43a",
"f2c85972-1a19-4426-bc6d-ce3337b9d99f", "bc1d1855-b2b8-4663-98ff-62e7b763dfc4",
"reorder_dailies_response.json",
"dailys",
), ),
], ],
ids=["todo", "daily"], ids=["todo", "daily"],
@ -627,9 +637,14 @@ async def test_move_todo_item(
uid: str, uid: str,
second_pos: str, second_pos: str,
third_pos: str, third_pos: str,
fixture: str,
task_type: str,
) -> None: ) -> None:
"""Test move todo items.""" """Test move todo items."""
reorder_response = HabiticaTaskOrderResponse.from_json(
load_fixture(fixture, DOMAIN)
)
habitica.reorder_task.return_value = reorder_response
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id) await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -650,6 +665,7 @@ async def test_move_todo_item(
assert resp.get("success") assert resp.get("success")
habitica.reorder_task.assert_awaited_once_with(UUID(uid), 1) habitica.reorder_task.assert_awaited_once_with(UUID(uid), 1)
habitica.reorder_task.reset_mock() habitica.reorder_task.reset_mock()
# move down to third position # move down to third position
@ -665,6 +681,7 @@ async def test_move_todo_item(
assert resp.get("success") assert resp.get("success")
habitica.reorder_task.assert_awaited_once_with(UUID(uid), 2) habitica.reorder_task.assert_awaited_once_with(UUID(uid), 2)
habitica.reorder_task.reset_mock() habitica.reorder_task.reset_mock()
# move to top position # move to top position
@ -679,6 +696,10 @@ async def test_move_todo_item(
assert resp.get("success") assert resp.get("success")
habitica.reorder_task.assert_awaited_once_with(UUID(uid), 0) habitica.reorder_task.assert_awaited_once_with(UUID(uid), 0)
assert (
getattr(config_entry.runtime_data.data.user.tasksOrder, task_type)
== reorder_response.data
)
@pytest.mark.parametrize( @pytest.mark.parametrize(