recon_gen.common.tree.filters
Filter primitives — typed Filter wrappers + FilterGroup.
Filter groups carry their scope as object refs (Sheet + [VisualLike])
and validate at the call site that scoped visuals belong to the
referenced sheet. Catches the wrong-sheet bug class — the type
checker carries the wiring; raise at construction confirms.
Typed Filter wrappers (CategoryFilter / NumericRangeFilter /
TimeRangeFilter) sit alongside the FilterGroup. They share names
with the underlying models.py classes — models are aliased on
import so user-facing code reads cleanly:
from recon_gen.common.tree import CategoryFilter, FilterGroup
The NumericRangeFilter’s minimum_parameter /
maximum_parameter fields take a ParameterDeclLike object ref —
the type checker catches “filter bound to undeclared parameter” at
the wiring site, where the existing string-keyed Parameter=name
pattern lets typos through to deploy.
Classes
|
Filter on a categorical (string) column or calc field. |
|
Inline default widget config for |
|
Inline default widget config for |
|
Inline default widget config for |
|
Tree node for one analysis-level filter group. |
|
Structural type for tree-level filter nodes. |
|
Filter on a numeric column. |
|
A parameter-driven bound — emits |
|
A literal numeric bound — emits |
|
Single-day equality filter on a date column. |
|
Filter on a date / datetime column. |
- class recon_gen.common.tree.filters.CategoryFilter(dataset, column, binding, match_operator='CONTAINS', null_option='ALL_VALUES', default_control=None, filter_id=AUTO)[source]
Bases:
objectFilter on a categorical (string) column or calc field.
datasetis aDatasetobject ref (L.1.7 hard switch).columnmay name a real dataset column or an analysis-level calc field — both resolve to aColumnIdentifieragainst the given dataset.Construct via the factory methods (L.1.22 — the discriminated binding makes the “neither/both set” bug class structurally impossible):
CategoryFilter.with_values(dataset, column, values, ...)— static list. EmitsFilterListConfigurationwithCategoryValues. Use for the calc-field'yes'sentinel pattern or a hardcoded include-list.CategoryFilter.with_parameter(dataset, column, parameter, ...)— parameter-bound. EmitsCustomFilterConfigurationwithParameterNamefrom the param ref. Use when a dropdown writes a single value into a string parameter and the filter narrows to it (e.g. Money Trail’s chain root selector).
null_optiononly surfaces in the parameter-bound emit (the list-basedFilterListConfigurationdoesn’t carry it).- Parameters:
dataset (Dataset)
binding (_ValuesBinding | _ParameterBinding | _LiteralBinding)
match_operator (Literal['CONTAINS', 'EQUALS', 'DOES_NOT_EQUAL', 'STARTS_WITH'])
null_option (Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'])
default_control (DefaultDropdownControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- binding: _ValuesBinding | _ParameterBinding | _LiteralBinding
- calc_field()[source]
The CalcField this filter references, or None if it points at a real dataset column.
- Return type:
CalcField|None
- default_control: DefaultDropdownControl | None = None
- filter_id: str | Literal[_AutoSentinel.AUTO] = 'auto'
- match_operator: Literal['CONTAINS', 'EQUALS', 'DOES_NOT_EQUAL', 'STARTS_WITH'] = 'CONTAINS'
- null_option: Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'] = 'ALL_VALUES'
- classmethod with_literal(*, dataset, column, value, match_operator='EQUALS', null_option='NON_NULLS_ONLY', default_control=None, filter_id=AUTO)[source]
Single-literal exact-match category filter — emits
CustomFilterConfigurationwith a literalCategoryValue. The list-basedFilterListConfigurationrejects EQUALS at the API (only CONTAINS / DOES_NOT_CONTAIN), so this is the only shape that supports an exact-equality test against a single value. Used for the K.2 calc-field PASS pattern: a calc field returns"PASS"and the filter requires equality against that literal.- Return type:
- Parameters:
dataset (Dataset)
value (str)
match_operator (Literal['CONTAINS', 'EQUALS', 'DOES_NOT_EQUAL', 'STARTS_WITH'])
null_option (Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'])
default_control (DefaultDropdownControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- classmethod with_parameter(*, dataset, column, parameter, match_operator='EQUALS', null_option='ALL_VALUES', default_control=None, filter_id=AUTO)[source]
Parameter-bound category filter —
ParameterNameis read from the parameter ref at emit time. Defaultmatch_operatorisEQUALSsince dropdown-driven parameters typically write a single value.- Return type:
- Parameters:
dataset (Dataset)
parameter (ParameterDeclLike)
match_operator (Literal['CONTAINS', 'EQUALS', 'DOES_NOT_EQUAL', 'STARTS_WITH'])
null_option (Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'])
default_control (DefaultDropdownControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- classmethod with_values(*, dataset, column, values, match_operator='CONTAINS', null_option='ALL_VALUES', select_all_options=None, default_control=None, filter_id=AUTO)[source]
Static-list category filter —
CategoryValuesis the literal list of allowed values. Passvalues=[]plusselect_all_options="FILTER_ALL_VALUES"for the multi-select-with-all-default pattern: an empty values list means “every distinct column value is selected at runtime”.- Return type:
- Parameters:
dataset (Dataset)
values (list[str])
match_operator (Literal['CONTAINS', 'EQUALS', 'DOES_NOT_EQUAL', 'STARTS_WITH'])
null_option (Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'])
select_all_options (Literal['FILTER_ALL_VALUES'] | None)
default_control (DefaultDropdownControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- class recon_gen.common.tree.filters.DefaultDateTimePickerControl(title, type='DATE_RANGE')[source]
Bases:
objectInline default widget config for
TimeRangeFilter.- Parameters:
title (str)
type (Literal['SINGLE_VALUED', 'DATE_RANGE'])
- title: str
- type: Literal['SINGLE_VALUED', 'DATE_RANGE'] = 'DATE_RANGE'
- class recon_gen.common.tree.filters.DefaultDropdownControl(title, type='MULTI_SELECT')[source]
Bases:
objectInline default widget config for
CategoryFilter(or any list/parameter-driven filter).- Parameters:
title (str)
type (Literal['MULTI_SELECT', 'SINGLE_SELECT'])
- title: str
- type: Literal['MULTI_SELECT', 'SINGLE_SELECT'] = 'MULTI_SELECT'
- class recon_gen.common.tree.filters.DefaultSliderControl(title, minimum_value, maximum_value, step_size, type='SINGLE_POINT')[source]
Bases:
objectInline default widget config for
NumericRangeFilter.- Parameters:
title (str)
minimum_value (float)
maximum_value (float)
step_size (float)
type (Literal['SINGLE_POINT', 'RANGE'])
- maximum_value: float
- minimum_value: float
- step_size: float
- title: str
- type: Literal['SINGLE_POINT', 'RANGE'] = 'SINGLE_POINT'
- class recon_gen.common.tree.filters.FilterGroup(filters, cross_dataset='SINGLE_DATASET', enabled=True, filter_group_id=AUTO)[source]
Bases:
objectTree node for one analysis-level filter group.
Construct with
FilterGroup(filter_group_id=..., filters=[...]), then attach scope by chaining.scope_visuals(sheet, [v1, v2])or.scope_sheet(sheet). Both call methods validate immediately:scope_visualsraises if any visual isn’t on the given sheet (catches the wrong-sheet bug at the call site).scope_sheetis the all-visuals-on-sheet shortcut.
Multiple scope entries are allowed — the same FilterGroup can apply to (visual subset on sheet A) plus (all visuals on sheet B). Each entry emits its own
SheetVisualScopingConfiguration.filterstakes a list of typedFilterLikewrappers (CategoryFilter/NumericRangeFilter/TimeRangeFilterabove). Each wrapper’semit()returns amodels.Filterat emission time. Parameter-bound filters (NumericRangeFilter withminimum_parameter/maximum_parameter) carry object refs toParameterDeclLikenodes — the type checker catches “filter bound to undeclared parameter” at the wiring site.filter_group_idis optional (L.1.8.5 auto-ID). When omitted, the App’s tree walker assignsfg-{n}at emit time based on the FilterGroup’s index in the analysis’s filter group list.- Parameters:
filters (list[FilterLike])
cross_dataset (Literal['SINGLE_DATASET', 'ALL_DATASETS'])
enabled (bool)
filter_group_id (FilterGroupId | Literal[_AutoSentinel.AUTO])
- cross_dataset: Literal['SINGLE_DATASET', 'ALL_DATASETS'] = 'SINGLE_DATASET'
- datasets()[source]
Datasets this group’s filters reference (object refs).
- Return type:
set[Dataset]
- enabled: bool = True
- filter_group_id: FilterGroupId | Literal[_AutoSentinel.AUTO] = 'auto'
- filters: list[FilterLike]
- scope_sheet(sheet)[source]
Scope this filter to ALL visuals on a sheet.
Equivalent to the existing
_selected_sheets_scope([sheet_id])helper — emitsScope=ALL_VISUALSon the sheet’s SheetVisualScopingConfiguration, no per-visual list.- Return type:
- Parameters:
sheet (Sheet)
- scope_visuals(sheet, visuals)[source]
Scope this filter to specific visuals on a sheet.
Construction-time check: every visual must already be registered on the given sheet via
sheet.add_visual(). Cross-sheet wiring is the bug class this catches — without the check, a scope mixing visuals from sheet A with sheet B’s identifier emits a SheetVisualScopingConfiguration that silently drops the off-sheet visual at deploy time.- Return type:
- Parameters:
sheet (Sheet)
visuals (list[VisualLike])
- class recon_gen.common.tree.filters.FilterLike(*args, **kwargs)[source]
Bases:
ProtocolStructural type for tree-level filter nodes.
Each typed wrapper (
CategoryFilter/NumericRangeFilter/TimeRangeFilter) satisfies this Protocol — exposes afilter_id, the underlyingdataset(object ref), and emits amodels.Filter. Thedatasetfield participates in the L.1.7 dependency-graph walk.filter_idisstr | Nonebecause typed wrappers default to None and letApp.resolve_auto_idsfill it.calc_field()returns the CalcField the filter references (or None if it points at a real column) — used by the dependency-graph walk and by FilterControl wrappers that need the filter_id post-resolve.- filter_id: str | Literal[_AutoSentinel.AUTO]
- class recon_gen.common.tree.filters.NumericRangeFilter(dataset, column, minimum=None, maximum=None, null_option='NON_NULLS_ONLY', include_minimum=None, include_maximum=None, default_control=None, filter_id=AUTO)[source]
Bases:
objectFilter on a numeric column. Range bounds are typed
Boundvariants —StaticBound(value)for a literal,ParameterBound( parameter)for a parameter-driven bound. The parameter-binding object ref catches “bound to a parameter that doesn’t exist” at the wiring site (the type checker resolvesparam.name).L.1.22 — the discriminated
Boundunion makes the “both static_value and parameter set” bug class structurally impossible: aStaticBoundcarries a value but no parameter, and aParameterBoundcarries a parameter but no value. Each side (min / max) is at most oneBound.- Parameters:
dataset (Dataset)
minimum (StaticBound | ParameterBound | None)
maximum (StaticBound | ParameterBound | None)
null_option (Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'])
include_minimum (bool | None)
include_maximum (bool | None)
default_control (DefaultSliderControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- default_control: DefaultSliderControl | None = None
- filter_id: str | Literal[_AutoSentinel.AUTO] = 'auto'
- include_maximum: bool | None = None
- include_minimum: bool | None = None
- maximum: StaticBound | ParameterBound | None = None
- property maximum_parameter: ParameterDeclLike | None
The parameter ref the maximum bound is bound to, or None.
- minimum: StaticBound | ParameterBound | None = None
- property minimum_parameter: ParameterDeclLike | None
The parameter ref the minimum bound is bound to, or None. Used by the parameter-references validator walk.
- null_option: Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'] = 'NON_NULLS_ONLY'
- class recon_gen.common.tree.filters.ParameterBound(parameter)[source]
Bases:
objectA parameter-driven bound — emits
Parameter(resolved fromparameter.name) in the range filter.- Parameters:
parameter (ParameterDeclLike)
- parameter: ParameterDeclLike
- class recon_gen.common.tree.filters.StaticBound(value)[source]
Bases:
objectA literal numeric bound — emits
StaticValuein the range filter.- Parameters:
value (float)
- value: float
- class recon_gen.common.tree.filters.TimeEqualityFilter(dataset, column, parameter, time_granularity=None, default_control=None, filter_id=AUTO)[source]
Bases:
objectSingle-day equality filter on a date column.
Used when paired with a SINGLE_VALUED date picker control —
TimeRangeFilterrenders broken in the QS UI when paired with a single-day picker;TimeEqualityFilteris the right shape for “show rows where the date column equals one specific day”.parameter(a typedDateTimeParamref) is the only binding mode currently exposed (the AR Daily Statement use case). Extend withrolling_date/static_valuefactories when other apps need them.- Parameters:
dataset (Dataset)
parameter (ParameterDeclLike)
time_granularity (Literal['YEAR', 'QUARTER', 'MONTH', 'WEEK', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'MILLISECOND'] | None)
default_control (DefaultDateTimePickerControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- default_control: DefaultDateTimePickerControl | None = None
- filter_id: str | Literal[_AutoSentinel.AUTO] = 'auto'
- parameter: ParameterDeclLike
- time_granularity: Literal['YEAR', 'QUARTER', 'MONTH', 'WEEK', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'MILLISECOND'] | None = None
- class recon_gen.common.tree.filters.TimeRangeFilter(dataset, column, minimum=None, maximum=None, null_option='NON_NULLS_ONLY', time_granularity=None, include_minimum=None, include_maximum=None, default_control=None, filter_id=AUTO)[source]
Bases:
objectFilter on a date / datetime column.
datasetis aDatasetobject ref (L.1.7 hard switch).columnis aColumnRef— a real column or aCalcField.minimumandmaximumare passthrough dicts for now (the existing usage takes a variety of shapes — RollingDate, StaticValue, Parameter — and lifting all of them under typed wrappers can wait for the L.2/L.3/L.4 ports to surface concrete needs).- Parameters:
dataset (Dataset)
minimum (dict[str, Any] | None)
maximum (dict[str, Any] | None)
null_option (Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'])
time_granularity (Literal['YEAR', 'QUARTER', 'MONTH', 'WEEK', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'MILLISECOND'] | None)
include_minimum (bool | None)
include_maximum (bool | None)
default_control (DefaultDateTimePickerControl | None)
filter_id (str | Literal[_AutoSentinel.AUTO])
- default_control: DefaultDateTimePickerControl | None = None
- filter_id: str | Literal[_AutoSentinel.AUTO] = 'auto'
- include_maximum: bool | None = None
- include_minimum: bool | None = None
- maximum: dict[str, Any] | None = None
- minimum: dict[str, Any] | None = None
- null_option: Literal['NON_NULLS_ONLY', 'ALL_VALUES', 'NULLS_ONLY'] = 'NON_NULLS_ONLY'
- time_granularity: Literal['YEAR', 'QUARTER', 'MONTH', 'WEEK', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'MILLISECOND'] | None = None