Skip to content

feat(admin): add growth-report — cross-platform use-case growth & rates (HTML) - #25

Merged
dsblank merged 21 commits into
mainfrom
LeoRoccoBreedt/cometx-usage-report
Jul 9, 2026
Merged

feat(admin): add growth-report — cross-platform use-case growth & rates (HTML)#25
dsblank merged 21 commits into
mainfrom
LeoRoccoBreedt/cometx-usage-report

Conversation

@LeoRoccoBreedt

@LeoRoccoBreedt LeoRoccoBreedt commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

Adds a new cometx admin growth-report action: a cross-platform use-case growth & rate-of-change report — per workspace (department) — spanning Opik, Experiment Management (EM), and MPM, rendered as a self-contained, theme-aware HTML dashboard.

It is a sibling to admin usage-report (which emits experiment-count-over-time as PDF/Streamlit) and stays deliberately distinct: this one is cross-platform use-case creation growth + rates, as one self-contained HTML file (no external assets, works offline, light/dark aware).

Resolves #24.

What it does

  • Growth (primary): creation-over-time + rate of the three use-case kinds — Opik projects, EM projects, MPM monitored models — unified across platforms and broken down per department, with KPIs: Total (all-time), New in window (+N), Growth % (new / installed-base-before-window, 0-guarded), and Workspaces.
  • Adoption/usage (secondary, per product): Opik span count, EM experiment count + a model-registry engagement snapshot (registered models / versions), and MPM prediction volume — over time and per workspace/project.
  • Two independent time controls: --units {month,week,day,hour} sets chart bucket granularity (charts are always all-time); --window (default 7d) is the KPI analysis window, drawn as an "Option-A" shaded band on the charts.
cometx admin growth-report [WORKSPACE ...] \
  [--units {month,week,day,hour}] [--window 7d] \
  [--platforms em,opik,mpm] [--output growth_report.html] [--limit N] [--no-open]

Example output

The report is a single self-contained HTML file (~190 KB in the live run below): inline CSS/JS, a light/dark theme toggle, per-collector status chips, JS-rendered interactive SVG charts (data embedded as JSON), and collapsible data tables. No external assets and no secrets in the output.

Topbar + collector status
<div class="topbar">
  <div>
    <p class="eyebrow">Comet Growth Report</p>
    <h1>Comet growth report</h1>
    <div class="meta">
      <span>Window <b class="mono">Analysis window: 2026-07-02 – 2026-07-09 (7d)</b></span>
      <span>Generated <b class="mono">2026-07-09T21:51:34+00:00</b></span>
      <span>Source <b>Comet Admin API</b></span>
    </div>
  </div>
  <div class="topbar-right">
    <button class="toggle" id="themeBtn" aria-label="Toggle color theme">
      <span id="themeIcon"></span><span id="themeLbl">Theme</span>
    </button>
    <div class="collectors" aria-label="collector status">
      <span class="chip on"><span class="dot"></span>opik</span>
      <span class="chip on"><span class="dot"></span>em</span>
      <span class="chip on"><span class="dot"></span>mpm</span>
    </div>
  </div>
</div>
KPI row + chart panel (JS-rendered SVG from embedded data)
<section class="kpis">
  <div class="kpi"><span class="stripe"></span>
    <div class="label">Workspaces</div><div class="val">1</div>
    <div class="sub">proxy for teams / departments</div></div>
  <div class="kpi ok"><span class="stripe"></span>
    <div class="label">Use cases</div><div class="val">470</div></div>
  <div class="kpi"><span class="stripe"></span>
    <div class="label">New (7d)</div><div class="val">+1</div></div>
  <div class="kpi"><span class="stripe"></span>
    <div class="label">Growth (7d)</div><div class="val">0.2%</div>
    <div class="sub">vs 469 before window</div></div>
</section>

<section class="panel">
  <div class="ph"><h3>Use cases created</h3><span class="hint">by kind · monthly</span></div>
  <div class="legend">
    <span><i class="swatch" style="background:var(--accent)"></i>Opik</span>
    <span><i class="swatch" style="background:var(--sdk)"></i>EM</span>
    <span><i class="swatch" style="background:var(--ok)"></i>MPM</span>
  </div>
  <div id="chart-unified-created" class="chart-host" data-kind="stackedBars"></div>
</section>
Collapsible use-case table
<table>
  <thead><tr><th>Use case</th><th>Kind</th><th>Created</th></tr></thead>
  <tbody>
    <tr><td>ncu-rep-viewer</td><td>EM projects</td><td>2026-07-07</td></tr>
    <tr><td>backup</td><td>Opik projects</td><td>2026-06-26</td></tr>
    <tr><td>scout-manifest-test</td><td>Opik projects</td><td>2026-06-17</td></tr>
    <tr><td>scout:comet-ml/opik</td><td>Opik projects</td><td>2026-05-20</td></tr>
    <!-- … one row per use case, newest first … -->
  </tbody>
</table>

Design notes & caveats

  • EM project creation is a proxy. EM projects expose no creation timestamp, so "created" is resolved as: probe for a creation key → earliest experiment start_server_timestamplastUpdated. This affects only the precision of the EM growth line, not the use-case taxonomy. Documented in the READMEs.
  • Workspace creation is proxied by a department's earliest use-case creation.
  • Opik span-count interval: Opik has no monthly interval, so monthly --units requests weekly buckets and re-buckets them into months (no data loss).
  • MPM requires provisioning and degrades gracefully (empty section + warning) when the MPM API is unavailable.
  • Optional deps: Opik/MPM are imported lazily and guarded — pip install 'cometx[all]' enables them; without them those sections are skipped cleanly.
  • No new runtime deps: stdlib only (no pandas); the shared month/week/day/hour time-bucket helpers were relocated from admin_usage_report.py into cometx/utils.py so both reports share one copy (and the new report avoids pulling in matplotlib/reportlab).
  • Type-safe count aggregation: collector datapoints are coerced via a shared _as_float() helper before summing, so a non-numeric/mixed-type value/y from an SDK or REST response falls back to 0.0 instead of raising TypeError and aborting a workspace's collection.
  • Single summary shape: the workspace-level (project=None) summary UsageMetric is built by one _workspace_usage_metric() helper shared by all three collectors, so EM/Opik/MPM summaries can't drift.
  • Security: the report is fully self-contained (no http(s):// in the output) and the renderer only ever receives report_data — the API key never reaches the HTML (regression-tested).

Testing

  • 46 new unit tests for this feature; full suite: 127 passed, 21 skipped, flake8/black/isort clean on all touched files.
  • Live-verified against a real deployment (scout-test-leo): produced a populated ~190 KB HTML with real Opik + EM KPIs/charts/tables, MPM gracefully absent (not provisioned on the test account), and the API key confirmed absent from the output via grep.
  • MPM's populated code path is unit-tested only (no provisioned MPM account was available to live-verify); its response parsing is defensive against shape drift.

Not in scope

Three pre-existing flake8 findings in admin_usage_report.py (json unused import, weeks_apart unused var, an f-string without placeholders) are present on main and were left untouched — this branch only relocated the time helpers out of that file.

🤖 Generated with Claude Code


Generated description

Below is a concise technical summary of the changes proposed in this PR:

graph LR
admin_("admin"):::modified
generate_growth_report_("generate_growth_report"):::added
GrowthReporter_("GrowthReporter"):::added
write_growth_html_("write_growth_html"):::added
COMET_ML_API_("COMET_ML_API"):::modified
COMET_MPM_API_("COMET_MPM_API"):::added
write_html_("write_html"):::added
build_html_("build_html"):::added
admin_ -- "CLI growth-report calls generate_growth_report using workspaces, units, window." --> generate_growth_report_
generate_growth_report_ -- "GrowthReporter.build collects cross-platform events/usage and assembles report_data." --> GrowthReporter_
generate_growth_report_ -- "generate_growth_report passes report_data to write_growth_html for saving." --> write_growth_html_
GrowthReporter_ -- "Collector queries EM projects/experiments and registry models via COMET_ML_API." --> COMET_ML_API_
GrowthReporter_ -- "MPM collector fetches workspaces and prediction points using COMET_MPM_API." --> COMET_MPM_API_
write_growth_html_ -- "write_growth_html delegates report_data rendering to admin_growth_render.write_html." --> write_html_
write_html_ -- "write_html calls build_html to embed report_data into HTML." --> build_html_
classDef added stroke:#15AA7A
classDef removed stroke:#CD5270
classDef modified stroke:#EDAC4C
linkStyle default stroke:#CBD5E1,font-size:13px
Loading

Introduce the GrowthReporter orchestration and shared time-key helpers to collect Opik/EM/MPM creation and usage metrics, resolve workspaces/platforms, and assemble the report_data consumed by the new renderer so that cometx admin growth-report delivers a comprehensive, cross-platform growth narrative. Render that contract via a self-contained, theme-aware HTML dashboard that embeds inline CSS/JS/SVG without ever leaking secrets or requiring network assets while linking the new command into the CLI and documentation.

TopicDetails
Dashboard renderer Render that report_data into a standalone dashboard: inline theme-aware CSS, JS that draws interactive SVG charts with window bands/tooltips, collapsible tables, KPI/panel markup, collector chips, payload embedding, and a write helper so the output stays secret-free and offline-friendly.
Modified files (1)
  • cometx/cli/admin_growth_render.py
Latest Contributors(0)
UserCommitDate
Docs & helpers centralization Document and advertise the new command and clarify the shared time helpers by updating README/README-ADMIN, ensuring users understand argument semantics, the two time concepts, caveats, and that the new growth report differs from the usage report, plus move the time-bucketing helpers out of admin_usage_report.py so both reports rely on the shared cometx.utils utilities.
Modified files (3)
  • README-ADMIN.md
  • README.md
  • cometx/cli/admin_usage_report.py
Latest Contributors(0)
UserCommitDate
Growth orchestration Coordinate the GrowthReporter flow: parse --window, resolve workspaces/platforms (guarding optional opik/comet_mpm deps), run each collector, zero-fill time buckets via the shared utils, build KPI/chart/table payloads per section/product, emit the report_data contract, expose generate_growth_report through the CLI, and back it with exhaustive unit tests covering collectors, renderer security, and orchestration paths.
Modified files (4)
  • cometx/cli/admin.py
  • cometx/cli/admin_growth_report.py
  • cometx/utils.py
  • tests/unit/test_admin_growth_report.py
Latest Contributors(0)
UserCommitDate
Review this PR on Baz | Customize your next review

LeoRoccoBreedt and others added 13 commits July 9, 2026 12:58
…I calls)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lements

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sed imports/noqa

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nder test; polish

Removes bucket_events/workspace_creation_events/usage_by_workspace (verified
unused by the pipeline), fixes _open() to resolve relative --output paths,
drops a dead noqa line, and adds a full-chain generate_growth_report ->
build() -> build_html() integration test.
LeoRoccoBreedt and others added 5 commits July 9, 2026 20:20
The Comet EM projects endpoint returns a fallback set from another
workspace (ignoring workspaceName) when the API key isn't a member of
the requested workspace. That caused those foreign projects to be
mis-attributed to every inaccessible workspace, showing as duplicate
project names in the EM adoption table and inflating EM growth counts.

Keep only projects whose own workspaceName matches the requested one;
warn when projects are dropped, and still report the workspace at 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ult)

Wrap every report table in a native <details>/<summary> disclosure (no
JS, stays self-contained). Tables start collapsed with the title + row
count in the summary, so large tables don't dominate the page as data
grows. Also give the per-product growth breakdown tables real titles so
their summaries read meaningfully instead of a generic label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Rename 'Departments' -> 'Workspaces' everywhere, with a 'proxy for
  teams / departments' clarification (label was misleading; these are
  workspaces used as a department/group proxy).
- Cumulative (area) charts: add per-point hover tooltips, x-axis time
  labels on both edges (+ intervals), a max-value gridline label, and
  the final cumulative value — previously they showed no numbers.
- Adoption/usage sections: add usage growth KPIs (New / Growth% over the
  window) and a fastest-growing-project KPI, not just the total.
- Adoption/usage sections: add a cumulative area chart alongside the
  per-period bars.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ways show fastest-growing project

1. Replace flaky native SVG <title> tooltips with a real interactive
   tooltip (positioned div + vertical guide line) driven by per-column
   hit rects; shared across bar, area, and stacked charts.
2. Cumulative (area) charts now label only the earliest and latest dates
   on the x-axis (instead of every interval), per request.
3. Always surface the fastest-growing-project KPI for every product
   (incl. EM = most new experiments in the analysis window), showing
   '—' when nothing grew in-window so it never appears missing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…x CI)

CI installs only requirements.txt (opik present, comet_mpm not), but the
growth-report tests patched comet_mpm/opik unconditionally, erroring at
setup with ModuleNotFoundError. Guard the SDK-patching tests with
skipif(find_spec(...) is None), and make the two cross-platform assembly
tests env-independent by forcing the resolved platform set instead of
relying on which optional extras happen to be installed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LeoRoccoBreedt
LeoRoccoBreedt marked this pull request as ready for review July 9, 2026 19:10
Comment thread cometx/cli/admin_growth_report.py Outdated
Comment thread cometx/cli/admin_growth_report.py Outdated
Non-numeric or mixed-type value/y from Opik SPAN_COUNT and MPM prediction
responses could raise TypeError on `counts[...] += ...` and abort collection.
Add _as_float() to coerce datapoints (None/blank/non-numeric -> 0.0).

Addresses baz-reviewer Type Inconsistency findings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread cometx/cli/admin_growth_report.py Outdated
EM/Opik/MPM collectors each appended a near-identical workspace-level
(project=None) UsageMetric block, so the summary shape could drift.
Route all three through _workspace_usage_metric().

Addresses baz-reviewer Code Dedup finding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread cometx/cli/admin_growth_report.py Outdated
…metadata

The EM EXPERIMENT_COUNT total (and its workspace roll-up) was sourced from
`numberOfExperiments` metadata while the chart series was built from
experiments bucketed by start_server_timestamp. When those disagree, the
adoption KPI total didn't equal the cumulative chart sum. Derive `total`
from the same counts map that feeds the series so they always match.

Add a regression test covering the metadata-disagrees case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dsblank
dsblank merged commit d6ed5e0 into main Jul 9, 2026
2 checks passed
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.

[FR]: Add Opik and EM usage reporting

2 participants