recon_gen.common.tree.visuals

Typed Visual subtypes — one per visual kind in active use.

L.1.1 catalog: KPI ×29, Table ×22, BarChart ×13, Sankey ×2 across the three apps. Each subtype owns its field-well shape and emits the corresponding models.py Visual instance.

Classes

BarChart(title, subtitle[, category, ...])

Bar chart visual — one bar per distinct category, height by values.

ForceGraph(title, subtitle[, actions, visual_id])

Force-directed network visual — HTMX-dialect only (X.2 spike capability test for X.4).

KPI(title, subtitle[, values, ...])

KPI visual — single number per values entry, no grouping.

KPIValueSignIndicator([inflow_is_healthy])

BK.9 — sign-aware (▲ inflow / ▼ outflow) state indicator for a KPI's primary value.

KPIValueZeroIndicator([healthy_when_zero])

BK.2 — binary healthy-when-zero state indicator for a KPI's primary value.

LineChart(title, subtitle[, category, ...])

Line chart visual — one line per distinct colors value, plotted across category (x-axis) with height by values (y-axis).

Sankey(title, subtitle[, source, target, ...])

Sankey diagram visual — flows from source nodes to target nodes, ribbon thickness by weight.

Table(title, subtitle[, group_by, values, ...])

Table visual — two field-well shapes:

VisualLike(*args, **kwargs)

Structural type for tree-level visual nodes.

class recon_gen.common.tree.visuals.BarChart(title, subtitle, category=<factory>, values=<factory>, colors=<factory>, orientation=None, bars_arrangement=None, category_label=None, value_label=None, color_label=None, sort_by=None, actions=<factory>, log_scale=False, visual_id=AUTO)[source]

Bases: object

Bar chart visual — one bar per distinct category, height by values.

Field-well shape: Category=[Dim, ...] + Values=[Measure, ...].

orientation ("VERTICAL" or "HORIZONTAL") and bars_arrangement ("CLUSTERED" / "STACKED" / "STACKED_PERCENT") pass through to the underlying BarChartConfiguration. sort_by is a (field_id, direction) tuple — direction "ASC" or "DESC" — and emits a CategorySort entry. All three default to None so the QuickSight defaults apply when not specified.

visual_id is optional (L.1.8.5 auto-ID).

Parameters:
  • title (str)

  • subtitle (str)

  • category (list[Dim])

  • values (list[Measure])

  • colors (list[Dim])

  • orientation (Literal['HORIZONTAL', 'VERTICAL'] | None)

  • bars_arrangement (Literal['CLUSTERED', 'STACKED', 'STACKED_PERCENT'] | None)

  • category_label (str | None)

  • value_label (str | None)

  • color_label (str | None)

  • sort_by (tuple[Dim | Measure | str, Literal['ASC', 'DESC']] | None)

  • actions (list[Drill | SameSheetFilter])

  • log_scale (bool)

  • visual_id (VisualId | Literal[_AutoSentinel.AUTO])

actions: list[Drill | SameSheetFilter]
bars_arrangement: Literal['CLUSTERED', 'STACKED', 'STACKED_PERCENT'] | None = None
calc_fields()[source]
Return type:

set[CalcField]

category: list[Dim]
category_label: str | None = None
color_label: str | None = None
colors: list[Dim]
datasets()[source]
Return type:

set[Dataset]

property element_id: str
property element_type: Literal['VISUAL', 'FILTER_CONTROL', 'PARAMETER_CONTROL', 'TEXT_BOX', 'IMAGE']
emit()[source]
Return type:

Visual

log_scale: bool = False
orientation: Literal['HORIZONTAL', 'VERTICAL'] | None = None
sort_by: tuple[Dim | Measure | str, Literal['ASC', 'DESC']] | None = None
subtitle: str
title: str
value_label: str | None = None
values: list[Measure]
visual_id: VisualId | Literal[_AutoSentinel.AUTO] = 'auto'
class recon_gen.common.tree.visuals.ForceGraph(title, subtitle, actions=<factory>, visual_id=AUTO)[source]

Bases: object

Force-directed network visual — HTMX-dialect only (X.2 spike capability test for X.4).

QuickSight’s standard visual library doesn’t include a force layout (only hierarchical SankeyDiagramVisual), so this primitive exists to prove the L1 tree primitives can host a visual kind that no QS dialect emit knows how to render. The HTMX renderer’s bootstrap dispatches to renderForceGraph via d3-force; emit() raises because the QS pipeline intentionally has no path for this kind.

Phase.1 design call: either keep this HTMX-only or wire a custom-visual emitter for QS. The capability test is the artifact; the layering decision is downstream.

No field-well slots — the visual’s data shape (nodes + links) flows through the data fetcher directly, not through QS field wells. visual_id is optional (L.1.8.5 auto-ID).

Parameters:
  • title (str)

  • subtitle (str)

  • actions (list[Drill | SameSheetFilter])

  • visual_id (VisualId | Literal[_AutoSentinel.AUTO])

actions: list[Drill | SameSheetFilter]
calc_fields()[source]
Return type:

set[CalcField]

datasets()[source]
Return type:

set[Dataset]

property element_id: str
property element_type: Literal['VISUAL', 'FILTER_CONTROL', 'PARAMETER_CONTROL', 'TEXT_BOX', 'IMAGE']
emit()[source]
Return type:

Visual

subtitle: str
title: str
visual_id: VisualId | Literal[_AutoSentinel.AUTO] = 'auto'
class recon_gen.common.tree.visuals.KPI(title, subtitle, values=<factory>, value_zero_indicator=None, value_sign_indicator=None, visual_id=AUTO)[source]

Bases: object

KPI visual — single number per values entry, no grouping.

Field-well shape: Values=[Measure, ...]. Most KPIs use one measure; multiple are allowed and render as side-by-side numbers.

value_zero_indicator (BK.2) — optional binary check/X+color state on the primary value. See KPIValueZeroIndicator. Only fires on single-value KPIs (multi-value KPIs would need per-value indicators; not currently supported).

visual_id is optional (L.1.8.5 auto-ID). When omitted, the App’s tree walker assigns v-kpi-s{sheet_idx}-{visual_idx} at emit time. Pass an explicit VisualId(...) to override.

Parameters:
calc_fields()[source]

CalcFields this visual references via its field-well leaves.

Return type:

set[CalcField]

datasets()[source]
Return type:

set[Dataset]

property element_id: str
property element_type: Literal['VISUAL', 'FILTER_CONTROL', 'PARAMETER_CONTROL', 'TEXT_BOX', 'IMAGE']
emit()[source]
Return type:

Visual

subtitle: str
title: str
value_sign_indicator: KPIValueSignIndicator | None = None
value_zero_indicator: KPIValueZeroIndicator | None = None
values: list[Measure]
visual_id: VisualId | Literal[_AutoSentinel.AUTO] = 'auto'
class recon_gen.common.tree.visuals.KPIValueSignIndicator(inflow_is_healthy=True)[source]

Bases: object

BK.9 — sign-aware (▲ inflow / ▼ outflow) state indicator for a KPI’s primary value. Renders ARROW_UP in green when the aggregated value is non-negative, ARROW_DOWN in red when it’s negative.

Accessibility: icon is the load-bearing channel (▲ vs ▼ is distinguishable to all viewers regardless of color perception); color rides along as a parallel signal.

Same QS wire shape as KPIValueZeroIndicator (see for the three layered gotchas around hex case + expression DSL); the semantic differs — sign of the aggregated value, not zero-vs- not-zero. Currently used on Exec “Net Money Moved” (signed flow).

Parameters:

inflow_is_healthy (bool)

inflow_is_healthy: bool = True
class recon_gen.common.tree.visuals.KPIValueZeroIndicator(healthy_when_zero=True)[source]

Bases: object

BK.2 — binary healthy-when-zero state indicator for a KPI’s primary value. Renders a CHECKMARK in green when the aggregated value equals zero, an X in red otherwise.

Accessibility (per user 2026-05-29 — colorblind users in the loop): the ICON is the load-bearing channel. Color is a parallel signal for users who can read it, but the icon alone fully communicates healthy/broken state to red/green-colorblind viewers.

Wire shape: - QS: emits KPIVisual.ConditionalFormatting with two

ConditionalFormattingOptions entries, each carrying a PrimaryValue.Icon.CustomCondition block — the first matches zero, the second matches non-zero. QS evaluates the expression at render time against the displayed primary value.

  • App2: the data-fetcher reads the primary value and emits state_icon (Unicode glyph) + state_color (semantic keyword) on each values entry. bootstrap.js::renderKPI prepends the glyph + applies the color class.

The TWO renderers see different payloads but render the semantically-equivalent shape — same icon glyph, same color intent — so the operator gets the same signal on either surface.

Parameters:

healthy_when_zero (bool)

healthy_when_zero: bool = True
class recon_gen.common.tree.visuals.LineChart(title, subtitle, category=<factory>, values=<factory>, colors=<factory>, chart_type=None, category_label=None, value_label=None, sort_by=None, actions=<factory>, visual_id=AUTO)[source]

Bases: object

Line chart visual — one line per distinct colors value, plotted across category (x-axis) with height by values (y-axis).

Field-well shape: Category=[Dim, ...] + Values=[Measure, ...] + Colors=[Dim, ...].

chart_type selects LINE (default), AREA, or STACKED_AREA. sort_by is a (field_id, direction) tuple — direction "ASC" or "DESC" — and emits a CategorySort entry. All optional fields default to None so the QuickSight defaults apply when not specified.

visual_id is optional (L.1.8.5 auto-ID).

Parameters:
  • title (str)

  • subtitle (str)

  • category (list[Dim])

  • values (list[Measure])

  • colors (list[Dim])

  • chart_type (Literal['LINE', 'AREA', 'STACKED_AREA'] | None)

  • category_label (str | None)

  • value_label (str | None)

  • sort_by (tuple[Dim | Measure | str, Literal['ASC', 'DESC']] | None)

  • actions (list[Drill | SameSheetFilter])

  • visual_id (VisualId | Literal[_AutoSentinel.AUTO])

actions: list[Drill | SameSheetFilter]
calc_fields()[source]
Return type:

set[CalcField]

category: list[Dim]
category_label: str | None = None
chart_type: Literal['LINE', 'AREA', 'STACKED_AREA'] | None = None
colors: list[Dim]
datasets()[source]
Return type:

set[Dataset]

property element_id: str
property element_type: Literal['VISUAL', 'FILTER_CONTROL', 'PARAMETER_CONTROL', 'TEXT_BOX', 'IMAGE']
emit()[source]
Return type:

Visual

sort_by: tuple[Dim | Measure | str, Literal['ASC', 'DESC']] | None = None
subtitle: str
title: str
value_label: str | None = None
values: list[Measure]
visual_id: VisualId | Literal[_AutoSentinel.AUTO] = 'auto'
class recon_gen.common.tree.visuals.Sankey(title, subtitle, source=None, target=None, weight=None, items_limit=None, actions=<factory>, visual_id=AUTO)[source]

Bases: object

Sankey diagram visual — flows from source nodes to target nodes, ribbon thickness by weight.

Field-well shape: each of source / target / weight is a single Dim / Measure (the underlying model expects lists, but every usage today has exactly one entry; emit wraps).

items_limit caps the number of source / destination nodes rendered (matches the ItemsLimit shape on the underlying sort configuration). OtherCategories defaults to "INCLUDE" so capped flows roll into a “(others)” bucket rather than being dropped silently.

visual_id is optional (L.1.8.5 auto-ID).

Parameters:
  • title (str)

  • subtitle (str)

  • source (Dim | None)

  • target (Dim | None)

  • weight (Measure | None)

  • items_limit (int | None)

  • actions (list[Drill | SameSheetFilter])

  • visual_id (VisualId | Literal[_AutoSentinel.AUTO])

actions: list[Drill | SameSheetFilter]
calc_fields()[source]
Return type:

set[CalcField]

datasets()[source]
Return type:

set[Dataset]

property element_id: str
property element_type: Literal['VISUAL', 'FILTER_CONTROL', 'PARAMETER_CONTROL', 'TEXT_BOX', 'IMAGE']
emit()[source]
Return type:

Visual

items_limit: int | None = None
source: Dim | None = None
subtitle: str
target: Dim | None = None
title: str
visual_id: VisualId | Literal[_AutoSentinel.AUTO] = 'auto'
weight: Measure | None = None
class recon_gen.common.tree.visuals.Table(title, subtitle, group_by=<factory>, values=<factory>, columns=<factory>, sort_by=None, actions=<factory>, conditional_formatting=None, visual_id=AUTO)[source]

Bases: object

Table visual — two field-well shapes:

  • Aggregated (default): group_by=[Dim, ...] + values=[Measure, ...]. One row per distinct group_by combination, aggregated by values. Emits TableAggregatedFieldWells.

  • Unaggregated: pass columns=[Dim, ...] (and leave group_by / values empty). Each cell shows the raw column value — no aggregation, one row per source row. Emits TableUnaggregatedFieldWells. Use this for detail/drill-source tables (AR Balances, AR Daily Statement transaction list).

Optional sort_by is a (field_ref, direction) tuple — direction is "ASC" or "DESC".

Optional conditional_formatting passes through to the model’s raw dict (see common/clickability.py for the standard accent-text and tint-background helpers).

visual_id is optional (L.1.8.5 auto-ID).

Parameters:
  • title (str)

  • subtitle (str)

  • group_by (list[Dim])

  • values (list[Measure])

  • columns (list[Dim])

  • sort_by (tuple[Dim | Measure | str, Literal['ASC', 'DESC']] | list[tuple[~recon_gen.common.tree.fields.Dim | ~recon_gen.common.tree.fields.Measure | str, ~typing.Literal['ASC', 'DESC']]] | None)

  • actions (list[Drill | SameSheetFilter])

  • conditional_formatting (list[CellAccentText | CellAccentMenu] | None)

  • visual_id (VisualId | Literal[_AutoSentinel.AUTO])

actions: list[Drill | SameSheetFilter]
calc_fields()[source]
Return type:

set[CalcField]

columns: list[Dim]
conditional_formatting: list[CellAccentText | CellAccentMenu] | None = None
datasets()[source]
Return type:

set[Dataset]

property element_id: str
property element_type: Literal['VISUAL', 'FILTER_CONTROL', 'PARAMETER_CONTROL', 'TEXT_BOX', 'IMAGE']
emit()[source]
Return type:

Visual

group_by: list[Dim]
sort_by: tuple[Dim | Measure | str, Literal['ASC', 'DESC']] | list[tuple[Dim | Measure | str, Literal['ASC', 'DESC']]] | None = None
subtitle: str
title: str
values: list[Measure]
visual_id: VisualId | Literal[_AutoSentinel.AUTO] = 'auto'
class recon_gen.common.tree.visuals.VisualLike(*args, **kwargs)[source]

Bases: Protocol

Structural type for tree-level visual nodes.

Typed subtypes (KPI / Table / BarChart / Sankey) satisfy this Protocol — duck-typed so subtypes don’t have to inherit from a base class. Subtypes contribute to the L.1.7 dependency-graph walk via datasets() / calc_fields().

All visual nodes also satisfy LayoutNode (in structure.py) via element_id + element_type so they can be placed in a sheet’s grid layout (sheet.layout.row(...).add_<kind>(...)).

visual_id is VisualId | AutoResolved — typed subtypes default to AUTO and App.resolve_auto_ids replaces it with the derived id before emit. The walker / emit assert via isinstance narrowing.

calc_fields()[source]
Return type:

set[CalcField]

datasets()[source]
Return type:

set[Dataset]

emit()[source]
Return type:

Visual

visual_id: VisualId | Literal[_AutoSentinel.AUTO]