From 71e636572fa85d37e384745049eee313c4189d07 Mon Sep 17 00:00:00 2001 From: Aaron Godfrey Date: Sun, 21 Jan 2024 15:09:08 -0800 Subject: [PATCH] Send recurrence data when updating a task in todoist (#108269) * Send recurrence data when updating a task in todoist * Update tests/components/todoist/test_todo.py Co-authored-by: Allen Porter * Move logic into _task_api_data. * Add comment about sending potentinally stale data. --------- Co-authored-by: Allen Porter --- homeassistant/components/todoist/todo.py | 13 +++++-- tests/components/todoist/test_todo.py | 46 +++++++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/todoist/todo.py b/homeassistant/components/todoist/todo.py index 5067e98642e..490e4ad9f1a 100644 --- a/homeassistant/components/todoist/todo.py +++ b/homeassistant/components/todoist/todo.py @@ -4,6 +4,8 @@ import asyncio import datetime from typing import Any, cast +from todoist_api_python.models import Task + from homeassistant.components.todo import ( TodoItem, TodoItemStatus, @@ -32,7 +34,7 @@ async def async_setup_entry( ) -def _task_api_data(item: TodoItem) -> dict[str, Any]: +def _task_api_data(item: TodoItem, api_data: Task | None = None) -> dict[str, Any]: """Convert a TodoItem to the set of add or update arguments.""" item_data: dict[str, Any] = { "content": item.summary, @@ -44,6 +46,12 @@ def _task_api_data(item: TodoItem) -> dict[str, Any]: item_data["due_datetime"] = due.isoformat() else: item_data["due_date"] = due.isoformat() + # In order to not lose any recurrence metadata for the task, we need to + # ensure that we send the `due_string` param if the task has it set. + # NOTE: It's ok to send stale data for non-recurring tasks. Any provided + # date/datetime will override this string. + if api_data and api_data.due: + item_data["due_string"] = api_data.due.string else: # Special flag "no date" clears the due date/datetime. # See https://developer.todoist.com/rest/v2/#update-a-task for more. @@ -126,7 +134,8 @@ class TodoistTodoListEntity(CoordinatorEntity[TodoistCoordinator], TodoListEntit async def async_update_todo_item(self, item: TodoItem) -> None: """Update a To-do item.""" uid: str = cast(str, item.uid) - if update_data := _task_api_data(item): + api_data = next((d for d in self.coordinator.data if d.id == uid), None) + if update_data := _task_api_data(item, api_data): await self.coordinator.api.update_task(task_id=uid, **update_data) if item.status is not None: # Only update status if changed diff --git a/tests/components/todoist/test_todo.py b/tests/components/todoist/test_todo.py index 5aa1e2af9de..a227ec858e4 100644 --- a/tests/components/todoist/test_todo.py +++ b/tests/components/todoist/test_todo.py @@ -402,8 +402,52 @@ async def test_update_todo_item_status( "status": "needs_action", }, ), + ( + [ + make_api_task( + id="task-id-1", + content="Soda", + description="6-pack", + is_completed=False, + # Create a mock task with a string value in the Due object and verify it + # gets preserved when verifying the kwargs to update below + due=Due(date="2024-01-01", is_recurring=True, string="every day"), + ) + ], + {"due_date": "2024-02-01"}, + [ + make_api_task( + id="task-id-1", + content="Soda", + description="6-pack", + is_completed=False, + due=Due(date="2024-02-01", is_recurring=True, string="every day"), + ) + ], + { + "task_id": "task-id-1", + "content": "Soda", + "description": "6-pack", + "due_date": "2024-02-01", + "due_string": "every day", + }, + { + "uid": "task-id-1", + "summary": "Soda", + "status": "needs_action", + "description": "6-pack", + "due": "2024-02-01", + }, + ), + ], + ids=[ + "rename", + "due_date", + "due_datetime", + "description", + "clear_description", + "due_date_with_recurrence", ], - ids=["rename", "due_date", "due_datetime", "description", "clear_description"], ) async def test_update_todo_items( hass: HomeAssistant,