recon_gen.common.tree.date_view

The date-view tree primitive (D5).

A DateView is the single source of truth for a subjective view-window — the analyst-facing “look at the latest day,” “show me the last 30 days,” “today’s statement.” It bundles the owned AsOfFrame (D1; the anchor) with the view’s own definition (empty-behavior + the required-coverage contract). Promoted from the AP.1 spike’s BalanceDateView (tests/unit/test_ap1_view_primitive.py).

Why this exists — the C1 release-blocker shape: a subjective view today is split across THREE independently-authored encodings — the analysis- param default (RollingDate), the dataset-param default (StaticValues sentinel), and the App2 binding. Each is authored in a different place, and they can disagree (that disagreement IS C1: QS reads “yesterday off wall-clock,” App2 reads “latest day,” the dataset KPI summary lands at a day with no rows, the KPI tile reads 0). The DateView inverts the derivation: ONE typed object is the source of truth; every renderer binding derives from it. AR.2 wires those emissions onto this primitive; AR.1 lands the primitive itself.

What is intentionally NOT here (deferred to AR.2):

  • DateTimeParam.default emission for QS analysis param defaults.

  • Dataset StaticValues emission for QS dataset param defaults.

  • ParameterDateTimePicker control emission for the picker widget.

  • App2 date_from / date_to binding emission.

AR.1 is the authoring abstraction — what app authors construct. AR.2 adds the wired emissions that make the derivation inversion concrete.

Classes

DateView(frame[, empty_behavior])

A typed view over a date range / single date, owning its own definition (anchor, span, empty-behavior, required-coverage).

EmptyBehavior(*values)

How a view resolves when its anchor day has no rows.

class recon_gen.common.tree.date_view.DateView(frame, empty_behavior=EmptyBehavior.LATEST_ON_EMPTY)[source]

Bases: object

A typed view over a date range / single date, owning its own definition (anchor, span, empty-behavior, required-coverage).

The renderer bindings (analysis-param default, dataset-param default, picker widget, App2 binding) all derive from this one object — AR.2’s emission layer. ONE source of truth; the C1 dual-default split becomes unrepresentable.

The frame carries the anchor + window: frame.as_of is the right-edge anchor, frame.window is the typed DateInterval the view queries against (single-day when frame.window.days == 1; rolling N-day when frame.window.days > 1). BD.1 replaced the v1 frame.window_days: int field with frame.window: DateInterval so the “span > 0 means rolling” convention is now a closed-closed days count, not a separate scalar.

Authoring abstraction — not end-user config. App authors construct one of these per surface; the operator never picks one.

Parameters:
property anchor_day: date

The single date the view points at — frame.as_of. For single-date views this is the only day; for range views this is the right edge.

emit_qs_analysis_default()[source]

Alias for emit_qs_analysis_default_end() — kept for the single-date case where the “default” is unambiguously the anchor (the AR.2 balance-date wiring’s caller name).

Return type:

DateTimeDefaultValues

emit_qs_analysis_default_end()[source]

The QS analysis-param default for the END of a range view (or the single-date case) — a StaticValues literal day, NOT a RollingDate expression. Strict-collapse: the anchor is the owned as_of, baked at deploy.

Return type:

DateTimeDefaultValues

emit_qs_analysis_default_start()[source]

The QS analysis-param default for the START of a range view — window_start as StaticValues. AR.4 wires this onto the L1 universal-range start param + the Exec 30-day start param, replacing the per-app RollingDate(addDateTime(-N, …)) expressions.

Return type:

DateTimeDefaultValues

emit_qs_dataset_default()[source]

The QS dataset-param default — the SAME literal day as the analysis default. App2 reads this directly; QS receives it via MappedDataSetParameters from the analysis side. Both renderers land on the same day.

For single-date views (Daily Statement) this is unambiguously the anchor. Range views use the start / end variants below; this is kept as an alias of the END for symmetry with emit_qs_analysis_default.

Return type:

DateTimeDatasetParameterDefaultValues

emit_qs_dataset_default_end()[source]

Phase BM — dataset-param default for the END of a range view (the anchor day). Mirrors emit_qs_analysis_default_end.

Return type:

DateTimeDatasetParameterDefaultValues

emit_qs_dataset_default_start()[source]

Phase BM — dataset-param default for the START of a range view (window_start). Mirrors emit_qs_analysis_default_start; used by L1 + Exec date-pushdown datasets so the BM-shape DateTimeDatasetParameter for the start picks up the same 7-day / 30-day window the analysis-level picker shows.

Return type:

DateTimeDatasetParameterDefaultValues

empty_behavior: EmptyBehavior = 1
frame: AsOfFrame
is_satisfied_by(available_days)[source]

Does at least one available day fall inside required_coverage? The view’s stated limit, checkable BEFORE render.

Return type:

bool

Parameters:

available_days (list[date])

property required_coverage: tuple[date, date]

The date range this view needs data inside to be meaningful. For range views: [window_start, anchor_day]. For single-date views with LATEST_ON_EMPTY: [date.min, anchor] (any prior day will do). For single-date SHOW_EMPTY: [anchor, anchor] (exact-match-or-blank is the contract).

The seed-coverage assertion in AR.3 calls is_satisfied_by(available_days) to make the plant ⟷ query-window contract a test, not developer-memory.

resolve_day(available_days)[source]

Apply empty_behavior to pick the day this view actually renders.

Returns the anchor_day if it has data, else (for LATEST_ON_EMPTY) the latest day ≤ anchor that does, else None (the view can’t satisfy itself — under SHOW_EMPTY means “render blank for the anchor”; under LATEST_ON_EMPTY means “no data anywhere ≤ anchor”). Range views resolve to their right-edge day the same way.

Return type:

date | None

Parameters:

available_days (list[date])

property window_start: date

The look-back’s lower bound — frame.window_start. Equal to anchor_day when the view is single-date (span=0).

class recon_gen.common.tree.date_view.EmptyBehavior(*values)[source]

Bases: Enum

How a view resolves when its anchor day has no rows.

Captures the implicit precondition every QS view carries today (audit §5 residual tension): a view “knows” what to do when its declared anchor has no data, but that knowledge lives in developer-memory. Making it explicit on the View object turns the precondition into a property of the type.

LATEST_ON_EMPTY = 1

If anchor_day has no data, fall back to the latest day with data ≤ anchor. The default for KPI-style “latest statement” views — renders something useful instead of going blank.

SHOW_EMPTY = 2

Honor anchor_day literally even if it has no rows. The view shows empty. Right for “as-of date is a hard precondition” surfaces where blank IS the correct answer (e.g., a regulator snapshot at an unsettled date).