Skip to content

Commit

Permalink
Add due date and description fields to Todoist To-do entity (#104655)
Browse files Browse the repository at this point in the history
* Add Todoist Due date and description fields

* Update entity features with new names

* Make items into walrus

* Update due_datetime field

* Add additional tests for adding new fields to items

* Fix call args in todoist test
  • Loading branch information
allenporter committed Nov 30, 2023
1 parent c72e4e8 commit 64a6a6a
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 21 deletions.
41 changes: 37 additions & 4 deletions homeassistant/components/todoist/todo.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""A todo platform for Todoist."""

import asyncio
from typing import cast
import datetime
from typing import Any, cast

from homeassistant.components.todo import (
TodoItem,
Expand All @@ -13,6 +14,7 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt as dt_util

from .const import DOMAIN
from .coordinator import TodoistCoordinator
Expand All @@ -30,13 +32,34 @@ async def async_setup_entry(
)


def _task_api_data(item: TodoItem) -> dict[str, Any]:
"""Convert a TodoItem to the set of add or update arguments."""
item_data: dict[str, Any] = {}
if summary := item.summary:
item_data["content"] = summary
if due := item.due:
if isinstance(due, datetime.datetime):
item_data["due"] = {
"date": due.date().isoformat(),
"datetime": due.isoformat(),
}
else:
item_data["due"] = {"date": due.isoformat()}
if description := item.description:
item_data["description"] = description
return item_data


class TodoistTodoListEntity(CoordinatorEntity[TodoistCoordinator], TodoListEntity):
"""A Todoist TodoListEntity."""

_attr_supported_features = (
TodoListEntityFeature.CREATE_TODO_ITEM
| TodoListEntityFeature.UPDATE_TODO_ITEM
| TodoListEntityFeature.DELETE_TODO_ITEM
| TodoListEntityFeature.SET_DUE_DATE_ON_ITEM
| TodoListEntityFeature.SET_DUE_DATETIME_ON_ITEM
| TodoListEntityFeature.SET_DESCRIPTION_ON_ITEM
)

def __init__(
Expand Down Expand Up @@ -66,11 +89,21 @@ def _handle_coordinator_update(self) -> None:
status = TodoItemStatus.COMPLETED
else:
status = TodoItemStatus.NEEDS_ACTION
due: datetime.date | datetime.datetime | None = None
if task_due := task.due:
if task_due.datetime:
due = dt_util.as_local(
datetime.datetime.fromisoformat(task_due.datetime)
)
elif task_due.date:
due = datetime.date.fromisoformat(task_due.date)
items.append(
TodoItem(
summary=task.content,
uid=task.id,
status=status,
due=due,
description=task.description or None, # Don't use empty string
)
)
self._attr_todo_items = items
Expand All @@ -81,16 +114,16 @@ async def async_create_todo_item(self, item: TodoItem) -> None:
if item.status != TodoItemStatus.NEEDS_ACTION:
raise ValueError("Only active tasks may be created.")
await self.coordinator.api.add_task(
content=item.summary or "",
**_task_api_data(item),
project_id=self._project_id,
)
await self.coordinator.async_refresh()

async def async_update_todo_item(self, item: TodoItem) -> None:
"""Update a To-do item."""
uid: str = cast(str, item.uid)
if item.summary:
await self.coordinator.api.update_task(task_id=uid, content=item.summary)
if update_data := _task_api_data(item):
await self.coordinator.api.update_task(task_id=uid, **update_data)
if item.status is not None:
if item.status == TodoItemStatus.COMPLETED:
await self.coordinator.api.close_task(task_id=uid)
Expand Down
5 changes: 3 additions & 2 deletions tests/components/todoist/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def make_api_task(
is_completed: bool = False,
due: Due | None = None,
project_id: str | None = None,
description: str | None = None,
) -> Task:
"""Mock a todoist Task instance."""
return Task(
Expand All @@ -55,8 +56,8 @@ def make_api_task(
content=content or SUMMARY,
created_at="2021-10-01T00:00:00",
creator_id="1",
description="A task",
due=due or Due(is_recurring=False, date=TODAY, string="today"),
description=description,
due=due,
id=id or "1",
labels=["Label1"],
order=1,
Expand Down

0 comments on commit 64a6a6a

Please sign in to comment.