feat: add temporal context and contextual help

This commit is contained in:
2026-08-05 00:03:31 +02:00
parent 982ef636b8
commit add7a99f6d
43 changed files with 1878 additions and 167 deletions
+74
View File
@@ -0,0 +1,74 @@
from __future__ import annotations
from typing import Any
from sqlalchemy import or_
from govoplan_core.core.temporal import (
TemporalContextError,
TemporalDataContext,
current_temporal_data_context,
)
def apply_temporal_revision_filter(
query: Any,
model: type[Any],
*,
context: TemporalDataContext | None = None,
valid_from: str | None = "valid_from",
valid_to: str | None = "valid_to",
recorded_at: str | None = "recorded_at",
superseded_at: str | None = "superseded_at",
) -> Any:
"""Apply latest/as-recorded and valid-time clauses to a revision query."""
resolved = context or current_temporal_data_context()
clauses: list[Any] = []
superseded_column = _optional_column(model, superseded_at)
recorded_column = _optional_column(model, recorded_at)
if resolved.recorded_at is None:
if superseded_column is not None:
clauses.append(superseded_column.is_(None))
else:
if recorded_column is None or superseded_column is None:
raise TemporalContextError(
f"{model.__name__} does not expose recorded/superseded revision time."
)
clauses.extend(
(
recorded_column <= resolved.recorded_at,
or_(
superseded_column.is_(None),
superseded_column > resolved.recorded_at,
),
)
)
instant = resolved.validity_instant
if instant is not None:
valid_from_column = _optional_column(model, valid_from)
valid_to_column = _optional_column(model, valid_to)
if valid_from_column is not None:
clauses.append(
or_(valid_from_column.is_(None), valid_from_column <= instant)
)
if valid_to_column is not None:
clauses.append(or_(valid_to_column.is_(None), valid_to_column > instant))
return query.filter(*clauses) if clauses else query
def _optional_column(model: type[Any], name: str | None) -> Any | None:
if name is None:
return None
column = getattr(model, name, None)
if column is None:
raise TemporalContextError(
f"{model.__name__} has no temporal column named {name!r}."
)
return column
__all__ = ["apply_temporal_revision_filter"]