Fix local todo list persistence for due dates (#110830)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
9ac199a8f2
commit
babb436512
3 changed files with 152 additions and 18 deletions
|
@ -1,5 +1,6 @@
|
|||
"""A Local To-do todo platform."""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from ical.calendar import Calendar
|
||||
|
@ -24,7 +25,8 @@ from .store import LocalTodoListStore
|
|||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
PRODID = "-//homeassistant.io//local_todo 1.0//EN"
|
||||
PRODID = "-//homeassistant.io//local_todo 2.0//EN"
|
||||
PRODID_REQUIRES_MIGRATION = "-//homeassistant.io//local_todo 1.0//EN"
|
||||
|
||||
ICS_TODO_STATUS_MAP = {
|
||||
TodoStatus.IN_PROCESS: TodoItemStatus.NEEDS_ACTION,
|
||||
|
@ -38,6 +40,25 @@ ICS_TODO_STATUS_MAP_INV = {
|
|||
}
|
||||
|
||||
|
||||
def _migrate_calendar(calendar: Calendar) -> bool:
|
||||
"""Upgrade due dates to rfc5545 format.
|
||||
|
||||
In rfc5545 due dates are exclusive, however we previously set the due date
|
||||
as inclusive based on what the user set in the UI. A task is considered
|
||||
overdue at midnight at the start of a date so we need to shift the due date
|
||||
to the next day for old calendar versions.
|
||||
"""
|
||||
if calendar.prodid is None or calendar.prodid != PRODID_REQUIRES_MIGRATION:
|
||||
return False
|
||||
migrated = False
|
||||
for todo in calendar.todos:
|
||||
if todo.due is None or isinstance(todo.due, datetime.datetime):
|
||||
continue
|
||||
todo.due += datetime.timedelta(days=1)
|
||||
migrated = True
|
||||
return migrated
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
|
@ -48,12 +69,16 @@ async def async_setup_entry(
|
|||
store = hass.data[DOMAIN][config_entry.entry_id]
|
||||
ics = await store.async_load()
|
||||
calendar = IcsCalendarStream.calendar_from_ics(ics)
|
||||
migrated = _migrate_calendar(calendar)
|
||||
calendar.prodid = PRODID
|
||||
|
||||
name = config_entry.data[CONF_TODO_LIST_NAME]
|
||||
entity = LocalTodoListEntity(store, calendar, name, unique_id=config_entry.entry_id)
|
||||
async_add_entities([entity], True)
|
||||
|
||||
if migrated:
|
||||
await entity.async_save()
|
||||
|
||||
|
||||
def _convert_item(item: TodoItem) -> Todo:
|
||||
"""Convert a HomeAssistant TodoItem to an ical Todo."""
|
||||
|
@ -65,6 +90,8 @@ def _convert_item(item: TodoItem) -> Todo:
|
|||
if item.status:
|
||||
todo.status = ICS_TODO_STATUS_MAP_INV[item.status]
|
||||
todo.due = item.due
|
||||
if todo.due and not isinstance(todo.due, datetime.datetime):
|
||||
todo.due += datetime.timedelta(days=1)
|
||||
todo.description = item.description
|
||||
return todo
|
||||
|
||||
|
@ -99,31 +126,36 @@ class LocalTodoListEntity(TodoListEntity):
|
|||
|
||||
async def async_update(self) -> None:
|
||||
"""Update entity state based on the local To-do items."""
|
||||
self._attr_todo_items = [
|
||||
TodoItem(
|
||||
uid=item.uid,
|
||||
summary=item.summary or "",
|
||||
status=ICS_TODO_STATUS_MAP.get(
|
||||
item.status or TodoStatus.NEEDS_ACTION, TodoItemStatus.NEEDS_ACTION
|
||||
),
|
||||
due=item.due,
|
||||
description=item.description,
|
||||
todo_items = []
|
||||
for item in self._calendar.todos:
|
||||
if (due := item.due) and not isinstance(due, datetime.datetime):
|
||||
due -= datetime.timedelta(days=1)
|
||||
todo_items.append(
|
||||
TodoItem(
|
||||
uid=item.uid,
|
||||
summary=item.summary or "",
|
||||
status=ICS_TODO_STATUS_MAP.get(
|
||||
item.status or TodoStatus.NEEDS_ACTION,
|
||||
TodoItemStatus.NEEDS_ACTION,
|
||||
),
|
||||
due=due,
|
||||
description=item.description,
|
||||
)
|
||||
)
|
||||
for item in self._calendar.todos
|
||||
]
|
||||
self._attr_todo_items = todo_items
|
||||
|
||||
async def async_create_todo_item(self, item: TodoItem) -> None:
|
||||
"""Add an item to the To-do list."""
|
||||
todo = _convert_item(item)
|
||||
TodoStore(self._calendar).add(todo)
|
||||
await self._async_save()
|
||||
await self.async_save()
|
||||
await self.async_update_ha_state(force_refresh=True)
|
||||
|
||||
async def async_update_todo_item(self, item: TodoItem) -> None:
|
||||
"""Update an item to the To-do list."""
|
||||
todo = _convert_item(item)
|
||||
TodoStore(self._calendar).edit(todo.uid, todo)
|
||||
await self._async_save()
|
||||
await self.async_save()
|
||||
await self.async_update_ha_state(force_refresh=True)
|
||||
|
||||
async def async_delete_todo_items(self, uids: list[str]) -> None:
|
||||
|
@ -131,7 +163,7 @@ class LocalTodoListEntity(TodoListEntity):
|
|||
store = TodoStore(self._calendar)
|
||||
for uid in uids:
|
||||
store.delete(uid)
|
||||
await self._async_save()
|
||||
await self.async_save()
|
||||
await self.async_update_ha_state(force_refresh=True)
|
||||
|
||||
async def async_move_todo_item(
|
||||
|
@ -156,10 +188,10 @@ class LocalTodoListEntity(TodoListEntity):
|
|||
if dst_idx > src_idx:
|
||||
dst_idx -= 1
|
||||
todos.insert(dst_idx, src_item)
|
||||
await self._async_save()
|
||||
await self.async_save()
|
||||
await self.async_update_ha_state(force_refresh=True)
|
||||
|
||||
async def _async_save(self) -> None:
|
||||
async def async_save(self) -> None:
|
||||
"""Persist the todo list to disk."""
|
||||
content = IcsCalendarStream.calendar_to_ics(self._calendar)
|
||||
await self._store.async_store(content)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue