Add core presence write layer and admin presence routes
The presence business operations (schedule upsert, override
create/delete, each followed by regenerate_for_child) lived only in the
HA-coupled service handlers, so the API admin plane could not reuse
them. Extract them into core/presence_management.py — model-validated
PresenceSchedule/PresenceOverride construction, the typed DAO calls
and the regeneration side effect in ONE Home-Assistant-free
implementation — and reduce the three HA service handlers to thin
wrappers over it.
Expose the admin plane's presence routes on the existing admin router
(PUT /children/{child_id}/presence-schedule, POST
/presence-overrides, DELETE /presence-overrides/{override_id}): the
body models carry only the shape, validation stays in the presence
model, and the error mapping stays narrow (unknown child/override 404,
rejected schedule/override 422). Tests drive all three routes as an
admin, prove the schedule upserts in place, an override is created and
then deleted, and the child's resolved presence (PresenceEngine)
changes accordingly.
This commit is contained in:
@@ -4,9 +4,11 @@ Registration is domain-scoped and happens once (see
|
||||
:func:`async_register_services`); handlers resolve a live database at
|
||||
call time through the injected ``find_runtime`` callable. Permission
|
||||
checks are Feature 09 later tasks — this module only validates payload
|
||||
shape and forwards to the Feature 03/06/08 business layers (and the
|
||||
presence DAOs). ``export_history_csv`` is registered with a schema but
|
||||
raises until Feature 13. ``regenerate`` is supplied by the caller so
|
||||
shape and forwards to the Feature 03/06/08 business layers (the
|
||||
presence writes included, through :mod:`.presence_management` — the
|
||||
ONE implementation the API admin plane shares).
|
||||
``export_history_csv`` is registered with a schema but raises until
|
||||
Feature 13. ``regenerate`` is supplied by the caller so
|
||||
the Feature 07 handler stays the one in ``__init__``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -22,6 +24,7 @@ from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from . import children as children_layer
|
||||
from . import completion as completion_layer
|
||||
from . import presence_management as presence_management_layer
|
||||
from . import quest_definitions as quest_definitions_layer
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
@@ -39,11 +42,8 @@ from .const import (
|
||||
SERVICE_UNCOMPLETE_QUEST,
|
||||
SERVICE_UPDATE_QUEST_DEFINITION,
|
||||
)
|
||||
from .dao_presence import PresenceOverridesDao, PresenceSchedulesDao
|
||||
from .events import fire_quest_completed, fire_quest_uncompleted
|
||||
from .materialize import regenerate_for_child
|
||||
from .permissions import permission_gate
|
||||
from .presence import PresenceSchedule
|
||||
from .recurrence import ScheduleRule
|
||||
from .service_policy import SERVICE_POLICY
|
||||
|
||||
@@ -405,21 +405,12 @@ def _set_presence_pattern(
|
||||
async def _handler(call: Any) -> None:
|
||||
runtime = _require_runtime(hass, find_runtime)
|
||||
data = call.data
|
||||
schedule = PresenceSchedule(
|
||||
await presence_management_layer.set_presence_schedule(
|
||||
runtime.database,
|
||||
data["child_id"],
|
||||
data["cycle_length_weeks"],
|
||||
data["anchor_date"],
|
||||
data["pattern"],
|
||||
)
|
||||
await PresenceSchedulesDao(runtime.database).upsert_by_child(
|
||||
schedule.child_id,
|
||||
schedule.cycle_length_weeks,
|
||||
schedule.anchor_date.isoformat(),
|
||||
schedule.encode(),
|
||||
)
|
||||
await regenerate_for_child(
|
||||
runtime.database,
|
||||
schedule.child_id,
|
||||
**_generation_kwargs(hass, runtime),
|
||||
)
|
||||
|
||||
@@ -432,16 +423,13 @@ def _create_presence_override(
|
||||
async def _handler(call: Any) -> None:
|
||||
runtime = _require_runtime(hass, find_runtime)
|
||||
data = call.data
|
||||
await PresenceOverridesDao(runtime.database).create(
|
||||
await presence_management_layer.create_presence_override(
|
||||
runtime.database,
|
||||
data["child_id"],
|
||||
data["start_date"],
|
||||
data["end_date"],
|
||||
data["is_present"],
|
||||
note=data.get("note"),
|
||||
)
|
||||
await regenerate_for_child(
|
||||
runtime.database,
|
||||
data["child_id"],
|
||||
**_generation_kwargs(hass, runtime),
|
||||
)
|
||||
|
||||
@@ -453,17 +441,9 @@ def _delete_presence_override(
|
||||
) -> ServiceHandler:
|
||||
async def _handler(call: Any) -> None:
|
||||
runtime = _require_runtime(hass, find_runtime)
|
||||
dao = PresenceOverridesDao(runtime.database)
|
||||
override_id = call.data["override_id"]
|
||||
override = await dao.get(override_id)
|
||||
if override is None:
|
||||
raise HomeAssistantError(
|
||||
f"override_id: presence override {override_id} does not exist"
|
||||
)
|
||||
await dao.delete(override_id)
|
||||
await regenerate_for_child(
|
||||
await presence_management_layer.delete_presence_override(
|
||||
runtime.database,
|
||||
override.child_id,
|
||||
call.data["override_id"],
|
||||
**_generation_kwargs(hass, runtime),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user