impulse_reporting.channels.calculated_channel
CalculatedChannel
class CalculatedChannel()
A reporting-layer calculated (derived) channel.
Orchestration counterpart to a query-engine CalculatedChannel: it wraps a
:class:TimeSeriesExpression built from the operator DSL (e.g.
q.channel(channel_name="raw_speed") * 3.6) plus an identity dict, and
is driven by :class:Report to compute the channel across containers, persist
the narrow result to a gold fact table, and update it incrementally.
Structurally parallels :class:BasicEvent (holds an aliased expression,
name-derived id, SHA-256 definition hash). Like the other entity types, its
narrow solve is batched from :class:Report (see
Report._solve_calculated_channels_batched): the channels are partitioned by
batch_size, each batch solved via QueryBuilder.solve_calculated_channels
and persisted as a temp table, then the batches are unioned into a narrow
solved_df. :meth:determine_calculated_channels shapes that already-solved
df, mirroring determine_aggregations / determine_events.
Arguments:
name(str): Name of the calculated channel (used as the entity id seed's fallback and stored on the dimension row).expr(TimeSeriesExpression): The wrapped expression; must evaluate to aSampleSeries.identity(Mapping[str, str]): Channel identity. Any non-empty set of keys; seeds the deterministicchannel_idand is stored once oncalculated_channel_dimensionas aMapType(string, string)column (joined to the fact viachannel_id, not repeated on fact rows).desc(str): Human-readable description (stored on the dimension row, excluded from the definition hash).attributes(Mapping[str, str]): Key-value metadata stored on the dimension row.
canonical_identity
def canonical_identity() -> str
Public, order-independent identity key.
Two channels with the same identity (regardless of key insertion
order) share this value and therefore the same channel_id. Used by
get_name
def get_name() -> str
Return the channel name.
set_report_id
def set_report_id(report_id: int)
Set the owning report id.
get_id
def get_id() -> int
Return the deterministic entity id (also the fact/dimension channel_id).
get_expression
def get_expression() -> TimeSeriesExpression
Return the wrapped query-engine CalculatedChannel expression.
get_expression_str
def get_expression_str() -> str
String form of the wrapped expression (identity + expr, no name/desc).
get_channel_type_str
def get_channel_type_str() -> str
Channel type string, matching the ChannelType enum member name.
determine_definition_hash
def determine_definition_hash() -> int
Hash of the computation-affecting definition (expression + identity).
as_dict
def as_dict() -> dict
Dictionary representation of the dimension metadata.
identity is a plain dict, persisted on the dimension as a
MapType(string, string) column (no fixed per-key columns).
as_spark_row
def as_spark_row() -> Row
Spark Row representation of the dimension metadata.
determine_calculated_channels
def determine_calculated_channels(
cls,
spark: SparkSession,
channels: list[CalculatedChannel],
*,
solved_df: DataFrame = None) -> DataFrame | None
Shape the already-solved narrow rows into this type's fact rows.
Mirrors determine_aggregations / determine_events: the batched
narrow solve happens in the Report (see
Report._solve_calculated_channels_batched), and this only shapes the
resulting solved_df: it selects the rows for these channels (by
channel_id) and projects to :data:CALCULATED_CHANNEL_FACT_SCHEMA.
Each channel's channel_id was fixed to its entity id at construction,
so the filter needs no join.
Arguments:
spark(SparkSession): Active Spark session (unused; kept for dispatcher symmetry).channels(list of CalculatedChannel): The channels whose rows to select fromsolved_df.solved_df(DataFrame): Narrow batched solve output (container_id, channel_id, tstart, tend, value, identity).None(no channels solved) returnsNone.
Returns:
DataFrame or None: Narrow fact DataFrame, or None when there are no channels or no
solved rows.
determine_metadata_df
def determine_metadata_df(cls, spark: SparkSession,
channels: list[CalculatedChannel])
Create the dimension DataFrame for the given channels.
identity is a self-describing MapType(string, string) column,
which createDataFrame builds directly from the plain dict returned by
determine_channel_metrics
def determine_channel_metrics(
cls,
spark: SparkSession,
channels: list[CalculatedChannel],
fact_df: DataFrame | None,
*,
attribute_columns: list[str] | None = None,
kpis: list[str] | None = None) -> DataFrame | None
Derive a silver-shaped channel_metrics DataFrame from the fact rows.
The calculated-channel fact table already matches the silver channels
table; this builds its companion channel_metrics so the pair can serve
as an Impulse silver source. Metrics are aggregated directly from the
narrow fact rows (container_id, channel_id, tstart, tend, value),
grouped by (container_id, channel_id).
The output schema is dynamic: fixed columns container_id, channel_id, value_type plus one column per configured KPI (see kpis),
one per identity key (the union across all channels), and one per
configured attribute key. Identity/attribute values are pulled from
each channel's in-memory identity / attributes dicts (null where a
channel omits a key). On an identity/attribute key collision, identity wins
and the attribute is skipped.
Arguments:
spark(SparkSession): Session used to build the per-channel metadata frame.channels(list of CalculatedChannel): The channels whose fact rows are infact_df; supply identity and attributes.fact_df(DataFrame or None): Narrow fact DataFrame (output of :meth:determine_calculated_channels).NonereturnsNone.attribute_columns(list of str): Attribute keys to surface as columns. Default/empty → no attribute columns. A key no channel defines yields an all-null column.kpis(list of str): KPI names to compute (seecalculated_channel_kpis.KPI_BUILDERS); the output carries one column per name, in order.None→ the default KPIs (duration, min, max, mean).
Returns:
DataFrame or None: The dynamic-schema metrics DataFrame, or None when fact_df is
None.