Fires one nestquest_quest_missed event per past-due open instance at the configured rollover time and at startup, watermark-guarded in the new nestquest_meta_state table (migration 7, strictly additive). Read-only over the domain tables; never reports an instance that was ever completed.
44 lines
1.7 KiB
Python
44 lines
1.7 KiB
Python
"""Typed DAO for the integration-owned ``nestquest_meta_state`` table.
|
|
|
|
Generic persistent key/value bookkeeping that belongs to the
|
|
integration itself rather than any domain table — currently the
|
|
Feature 11 missed-sweep watermark. Values are plain strings; callers
|
|
interpret them. All SQL for this table lives here per the feature
|
|
guardrails, and every method runs through
|
|
:class:`~.db.NestQuestDatabase` so nothing blocks the event loop.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from .dao_children import _connection_lock
|
|
from .db import NestQuestDatabase
|
|
|
|
_TABLE = "nestquest_meta_state"
|
|
|
|
|
|
class MetaStateDao:
|
|
"""Typed async access to the ``nestquest_meta_state`` table."""
|
|
|
|
def __init__(self, database: NestQuestDatabase) -> None:
|
|
self._database = database
|
|
|
|
async def get(self, key: str) -> str | None:
|
|
"""Return the stored value for ``key``, or None when absent."""
|
|
row = await self._database.fetch_one(
|
|
f"SELECT value FROM {_TABLE} WHERE key = ?", (key,)
|
|
)
|
|
return row[0] if row is not None else None
|
|
|
|
async def set(self, key: str, value: str) -> None:
|
|
"""Upsert one key/value row atomically.
|
|
|
|
The write runs inside one transaction under the shared
|
|
connection lock, so a racing reader either sees the old value
|
|
or the fully-written new one, never a half-applied state.
|
|
"""
|
|
async with _connection_lock(self._database):
|
|
async with self._database.transaction():
|
|
await self._database.execute(
|
|
f"INSERT INTO {_TABLE} (key, value) VALUES (?, ?) "
|
|
"ON CONFLICT (key) DO UPDATE SET value = excluded.value",
|
|
(key, value),
|
|
) |