Skip to content

Add optional calculated_channel_metrics table with configurable KPIs - #73

Open
tombonfert wants to merge 9 commits into
mainfrom
feature/channel_metrics_of_calc_channels
Open

Add optional calculated_channel_metrics table with configurable KPIs#73
tombonfert wants to merge 9 commits into
mainfrom
feature/channel_metrics_of_calc_channels

Conversation

@tombonfert

Copy link
Copy Markdown
Collaborator

Summary

Adds an opt-in calculated_channel_metrics table for calculated channels.
Because calculated_channel_fact already matches the silver channels shape,
this companion table (silver channel_metrics shape) lets the fact + metrics
pair serve as an Impulse silver source in its own right.

Changes

  • New CalculatedChannels config section: emit_channel_metrics (default
    false), attribute_columns (default []), and kpis (default duration, min, max, mean). Unknown KPI names are rejected at config validation.
  • CalculatedChannel.determine_channel_metrics derives the table directly from
    the fact rows, grouped by (container_id, channel_id), with duration-weighted
    semantics matching SampleSeries.
  • KPIs live in a small registry (calculated_channel_kpis.KPI_BUILDERS), so
    adding a new KPI is a one-line change and is then selectable via config.
  • Dynamic output schema: fixed container_id, channel_id, type, data_type, one
    column per configured KPI, one per identity key (union across the report's
    channels), and one per configured attribute key. An identity key wins over an
    attribute key of the same name.
  • Wired into full and incremental persistence (upsert on container_id, channel_id, pruning stale rows from updated containers). Off by default, so
    existing reports are unchanged.

Tests

  • Unit: KPI derivation (duration-weighted mean, NaN handling, zero-duration
    guard via try_divide), dynamic identity/attribute columns, KPI subset and
    ordering, config default/dedupe/unknown-rejection.
  • Integration: emit flag on/off, custom KPI selection, incremental idempotency,
    and a round-trip feeding the fact + metrics pair back through DefaultSolver.

Test Plan

  • Unit tests added/updated
  • Manual testing completed
  • Documentation updated (if applicable)

Docs

  • Updated configuration, gold-layer, data-model, and channel reference pages
    plus the impulse-config, impulse-channels, and impulse-data-model skills.
  • Regenerated the pydoc-markdown API reference.

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • No new linter warnings introduced

…l_metrics

- Add `CalculatedChannel.determine_channel_metrics` to derive a dynamic-schema
  metrics DataFrame from narrow fact rows, with duration-weighted min/max/mean
  and configurable identity/attribute columns.
- Add `CalculatedChannels` config to opt in via `emit_channel_metrics` and
  select attribute columns surfaced on the metrics table.
- Persist the metrics table in full and incremental modes, upserting on
  `(container_id, channel_id)` and scoping incremental deletes to updated
  containers.
- Extend `ChannelType` and `SinkConfig` with metrics table name/URI support.
- Introduce `calculated_channel_kpis.KPI_BUILDERS`, a registry of named aggregation builders for `duration`, `min`, `max`, and `mean`.
- Refactor `CalculatedChannel.determine_channel_metrics` to accept a `kpis` list and build aggregation columns from the registry, making the output schema dynamic over the selected KPIs.
- Add `kpis` config to `CalculatedChannels` with validation and deduplication; default remains `["duration", "min", "max", "mean"]`.
- Use `F.try_divide` for the duration-weighted mean so zero total duration yields null instead of failing under ANSI mode.
- Wire the configured KPIs through `Report` and add unit/integration tests for custom selections, ordering, defaults, unknown KPI rejection, and zero-duration handling.
…API updates

- Document the optional `calculated_channel_metrics` gold table and the
  `calculated_channels` config block (`emit_channel_metrics`, `attribute_columns`,
  `kpis`) across the data model, configuration reference, channel reference, and skills.
- Add `START` and `END` to the `StatisticType` API reference.
- Update `StatsAggregator` definition-hash docs to include `channel_names` and
  cross-channel descriptor `channel_name`.
@tombonfert
tombonfert requested a review from a team as a code owner August 7, 2026 07:03
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.78882% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.83%. Comparing base (8457893) to head (8f69497).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/impulse_reporting/channels/channel_types.py 50.00% 3 Missing and 3 partials ⚠️
src/impulse_reporting/persist/report_storage.py 66.66% 2 Missing and 1 partial ⚠️
src/impulse_reporting/config/config_parser.py 96.55% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #73      +/-   ##
==========================================
+ Coverage   88.67%   88.83%   +0.16%     
==========================================
  Files          60       61       +1     
  Lines        5008     5170     +162     
  Branches      596      621      +25     
==========================================
+ Hits         4441     4593     +152     
- Misses        461      466       +5     
- Partials      106      111       +5     
Flag Coverage Δ
query_engine 84.57% <ø> (ø)
reporting 94.16% <93.78%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...c/impulse_reporting/channels/calculated_channel.py 100.00% <100.00%> (ø)
...ulse_reporting/channels/calculated_channel_kpis.py 100.00% <100.00%> (ø)
src/impulse_reporting/core/report.py 91.90% <100.00%> (+0.41%) ⬆️
src/impulse_reporting/core/report_utils.py 97.20% <100.00%> (+0.36%) ⬆️
src/impulse_reporting/config/config_parser.py 97.17% <96.55%> (-0.09%) ⬇️
src/impulse_reporting/persist/report_storage.py 83.81% <66.66%> (-0.95%) ⬇️
src/impulse_reporting/channels/channel_types.py 67.27% <50.00%> (-4.83%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Add `dispatch_calculated_channel_metrics` in `report_utils` to route
  `determine_channel_metrics` calls per channel type using the solved fact df.
- Replace the duplicated dispatch loops in `Report` with calls to the new helper
  for both changed and unchanged buckets.
- Add unit tests covering empty inputs, missing fact dfs, and correct routing
  of channels, fact df, attribute columns, and KPIs.
- Add `group_dfs_by_table` in `report_utils` to flatten per-type
  `{"changed", "unchanged"}` dicts or bare DataFrames and bucket them by
  output table name, skipping types that contribute no DataFrame.
- Replace the inline grouping logic in `persist_facts_full` with the new
  helper.
- Reuse the helper in `Report` for calculated-channel metrics persistence
  and replace the ad-hoc `reduce` + `unionByName` with
  `ReportEntityTransformer.concat_dataframes`.
- Add unit tests covering empty inputs, bare DataFrames, changed/unchanged
  dicts, shared tables, and skipped null/empty values.
- Add `persist_channel_metrics` in `report_utils` to persist optional
  calculated-channel metrics tables with dynamic schema, supporting both
  full overwrite and incremental upsert with delete-by-source scoping.
- Replace the private `_persist_channel_metrics` method in `Report` with
  calls to the new helper for full and incremental persistence.
- Add unit tests covering empty inputs, full-mode overwrite, incremental
  merge keys, and delete-condition scoping.
- Enable metrics emission in `test_incremental_changed_definition_replaces` and assert the metrics row is recomputed with the updated factor when the channel definition changes.
- Add hand-computed, duration-weighted min/max/mean assertions in `test_channel_metrics_emitted_and_usable_as_impulse_source` to independently verify KPI math end to end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant