278 files
This commit is contained in:
@@ -17,8 +17,10 @@ from typing import Any
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.storage import Store
|
||||
|
||||
from .const import CONF_TASKS, DEFAULT_MAX_HISTORY_ENTRIES, DOMAIN
|
||||
from .const import CONF_TASKS, DEFAULT_MAX_HISTORY_ENTRIES, DOMAIN, LIFECYCLE_HISTORY_TYPES
|
||||
from .helpers.dates import local_date_from_iso
|
||||
from .helpers.parts import round_qty
|
||||
from .helpers.pause import write_anchor
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -233,6 +235,24 @@ class MaintenanceStore:
|
||||
object's Store to another's."""
|
||||
self._data.setdefault("tasks", {})[task_id] = dict(state)
|
||||
|
||||
def update_task_state(self, task_id: str, **fields: Any) -> None:
|
||||
"""Write plain scalar fields of a task's dynamic state; a ``None``
|
||||
value removes the key. The public door for the few callers that used
|
||||
to reach into ``_ensure_task`` (the coordinator's cycle modifiers, the
|
||||
reference-number counter)."""
|
||||
state = self._ensure_task(task_id)
|
||||
for key, value in fields.items():
|
||||
if value is None:
|
||||
state.pop(key, None)
|
||||
else:
|
||||
state[key] = value
|
||||
|
||||
def all_task_states(self) -> dict[str, dict[str, Any]]:
|
||||
"""Every task's dynamic state, keyed by task id — the LIVE dicts, for
|
||||
in-place rewrites that then save (entity renames)."""
|
||||
tasks: dict[str, dict[str, Any]] = self._data.setdefault("tasks", {})
|
||||
return tasks
|
||||
|
||||
# --- in-cycle checklist progress (#73) -----------------------------------
|
||||
|
||||
def set_checklist_progress(self, task_id: str, state: dict[str, bool]) -> None:
|
||||
@@ -312,9 +332,21 @@ class MaintenanceStore:
|
||||
return self.get_task_state(task_id).get("last_performed")
|
||||
|
||||
def set_last_performed(self, task_id: str, date_str: str) -> None:
|
||||
"""Set last_performed date string."""
|
||||
"""Set last_performed date string (modifiers untouched — the
|
||||
coordinator's ``_persist_dynamic_state`` writes those itself)."""
|
||||
self._ensure_task(task_id)["last_performed"] = date_str
|
||||
|
||||
def set_anchor(self, task_id: str, date_str: str | None, *, clear_modifiers: bool = True) -> None:
|
||||
"""Move (or with ``None`` clear) a task's ``last_performed`` anchor.
|
||||
|
||||
Every anchor poke outside the coordinator goes through here: the
|
||||
history edit/delete re-derivation, the dialog's "last performed"
|
||||
field, unarchive/resume. With ``clear_modifiers`` (the default) the
|
||||
cycle's per-occurrence modifiers (``due_override``,
|
||||
``last_planned_due``) go too — see :func:`helpers.pause.write_anchor`.
|
||||
"""
|
||||
write_anchor(self._ensure_task(task_id), date_str, clear_modifiers=clear_modifiers)
|
||||
|
||||
def set_phase_cursor(self, task_id: str, cursor: int) -> None:
|
||||
"""Set the phase cursor (#139) — which cycle step is due next."""
|
||||
self._ensure_task(task_id)["phase_cursor"] = cursor
|
||||
@@ -461,6 +493,23 @@ class MaintenanceStore:
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
|
||||
def reanchor_from_history(store: MaintenanceStore, task_id: str, history: list[dict[str, Any]]) -> str | None:
|
||||
"""Re-derive a task's ``last_performed`` from its history and write it.
|
||||
|
||||
The single rule behind the history edit AND delete commands: the anchor
|
||||
is the day of the LATEST lifecycle entry (completed / reset / skipped /
|
||||
missed — ``LIFECYCLE_HISTORY_TYPES``); with none left the task reads as
|
||||
never performed. The cycle modifiers (a postpone's ``due_override``,
|
||||
``last_planned_due``) are dropped only when the anchor actually MOVES —
|
||||
editing a note on the last completion keeps a postponed occurrence,
|
||||
deleting that completion abandons it. Returns the new anchor.
|
||||
"""
|
||||
latest = max((h.get("timestamp") or "" for h in history if h.get("type") in LIFECYCLE_HISTORY_TYPES), default="")
|
||||
anchor = local_date_from_iso(latest) if latest else None # the stamp's local calendar day
|
||||
store.set_anchor(task_id, anchor, clear_modifiers=anchor != store.get_last_performed(task_id))
|
||||
return anchor
|
||||
|
||||
|
||||
def extract_dynamic_from_task(
|
||||
task_data: dict[str, Any],
|
||||
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||
|
||||
Reference in New Issue
Block a user