Skip to content

fix(analytics): resync the hand-written types with what the API returns - #3818

Closed
blarghmatey wants to merge 1 commit into
mainfrom
fix/analytics-types-drift
Closed

blarghmatey wants to merge 1 commit into
mainfrom
fix/analytics-types-drift

Conversation

@blarghmatey

Copy link
Copy Markdown
Member

What are the relevant tickets?

Closes the analytics-types drift tracked alongside mitodl/ol-analytics-api#29 and #33. Related: mitodl/ol-analytics-api#37, which makes this file generated rather than hand-written.

Description (What does it do?)

frontends/api/src/analytics/types.ts mirrors ol-analytics-api's response models by hand. It has fallen behind them twice over.

The *_pk fields were typed number and have never been numeric. contract_pk, courserun_pk and program_pk are dbt surrogate keys — dbt_utils.generate_surrogate_key MD5-hashes its inputs into a 32-character hex string. ol-analytics-api#29 fixed the same mistake server-side, where it was not cosmetic: contract-utilization, enrollment-funnel and program-funnel were 500ing in production on a Pydantic int_parsing error (Sentry OL-ANALYTICS-API-J, 120 events in the first two minutes).

Nothing breaks on this side: every use is a React key, a Map key or Set membership, and all three behave identically for strings. But the Map<number, EnrollmentCompletionFunnel[]> in CoursePerformanceTable was only well-typed because the type it read was wrong, so it moves to Map<string, …> here.

The activity totals went nullable and seven columns were missing. ol-analytics-api#33 floors each activity total through the cohort that produced it rather than through its own magnitude, so total_videos_watched / total_problems_attempted / total_chatbot_interactions come back null when few enough learners are behind them — a large total suppresses. All three were still typed non-null. The five cohort counts that change added to MonthlyEngagementTrend (enrolling_learners, certified_learners, video_watchers, problem_attempters, chatbot_users) were missing entirely, as were video_watchers and problem_attempters on ContentEngagementDepth.

Nothing renders wrong today. EngagementTrendChart plots only new_enrollments and certificates_earned, both already nullable and already handled — it shows a suppressed indicator and maps row[series.key] ?? null. The three activity totals are deliberately not charted at all — the component's comment explains they sit on a different y-scale, and a dual-axis chart would invite comparing two things that were never on the same scale.

The live hazard was test-utils/factories.ts, which built fixtures with faker.number.int() pks. Every test asserting on one was agreeing with a shape production does not send. It now generates 32-character lowercase hex, and carries the seven added columns.

Scope

Fields were checked against models.py on ol-analytics-api@main, not against the ticket, and stop there deliberately: contract_id and the contract-grained models arrive with ol-analytics-api#34, which has not merged. This PR syncs to what is deployed.

This file is on its way out

Third time these types have been chased by hand (see also the sso_organization_id shim). ol-analytics-api#37 now publishes an OpenAPI document per tenant and commits it, and the generated typescript-axios client was verified to produce correct nullability and distinct per-model interfaces, typechecking clean under tsc --strict. Once the clients repo and the Concourse PIPELINE_CONFIGS entry land, types.ts, clients.ts and the ./analytics-types export all go away.

How can this be tested?

yarn typecheck                      # api clean; main unchanged from its baseline
yarn test src/app-pages/DashboardPage

main's typecheck carries 80 pre-existing errors in this environment (PageProps from Next's generated types, and @/public/* image module declarations) — the count is identical with and without this change, and none of them are in analytics files.

All four Analytics suites plus AnalyticsContent.test.tsx pass (50 tests). CoursewareDisplay/HomeEnrollmentsDisplay and UnenrolledCourseCard.compliance flaked on 5s timeouts in one full parallel run; both pass standalone with these changes applied, and neither file references analytics at all.

Screenshots (if appropriate)

None — types and fixtures only, no rendered output changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AVSXwars1LvgtqV1YWhrB1

`frontends/api/src/analytics/types.ts` mirrors ol-analytics-api's response
models by hand, and has fallen behind them twice over.

The `*_pk` fields are typed `number` and have never been numeric. They are dbt
surrogate keys — `dbt_utils.generate_surrogate_key` MD5-hashes its inputs into a
32-character hex string. ol-analytics-api#29 fixed the same mistake server-side,
where it was not cosmetic: `ContractUtilization`, `EnrollmentCompletionFunnel`
and `ProgramFunnel` were 500ing in production on a Pydantic `int_parsing` error
(Sentry OL-ANALYTICS-API-J, 120 events in two minutes). Nothing breaks on this
side because every use is a React key, a Map key or Set membership, all of which
behave the same for strings — but the `Map<number, …>` in CoursePerformanceTable
was only well-typed because the type it read was wrong.

Separately, ol-analytics-api#33 moved the activity totals onto the cohort that
produced them: a total is now suppressed when few enough learners are behind it,
so `total_videos_watched` and its two siblings return null even when the total
itself is large. Those three were still typed non-null here, and the five
cohort counts the change added (`enrolling_learners`, `certified_learners`,
`video_watchers`, `problem_attempters`, `chatbot_users`) were missing entirely,
as were `video_watchers`/`problem_attempters` on `ContentEngagementDepth`.

Nothing renders wrong today: EngagementTrendChart plots only `new_enrollments`
and `certificates_earned`, both already nullable and already handled. The
factories were the live hazard — they built fixtures with numeric pks, so every
test asserting on one was agreeing with a shape production does not send.

Fields were checked against `models.py` on ol-analytics-api@main rather than
against the ticket, and deliberately stop there: `contract_id` and the
contract-grained models arrive with ol-analytics-api#34, which has not merged.

This is the third hand-chase (see also the sso_organization_id shim).
ol-analytics-api#37 now publishes an OpenAPI spec per tenant and the generated
TypeScript client typechecks clean against it, so this file is on its way out —
once the clients repo and the Concourse PIPELINE_CONFIGS entry land, types.ts,
clients.ts and the `./analytics-types` export all go with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AVSXwars1LvgtqV1YWhrB1
Copilot AI balanced review requested due to automatic review settings August 21, 2026 18:14
@blarghmatey
blarghmatey requested a review from a team as a code owner August 21, 2026 18:14
@github-actions

Copy link
Copy Markdown

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Synchronizes analytics frontend types and fixtures with the deployed API models.

Changes:

  • Corrects surrogate-key types from numbers to strings.
  • Adds missing cohort fields and nullable activity totals.
  • Updates related factories, grouping, and tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
AnalyticsContent.test.tsx Updates course-run test keys to strings.
CoursePerformanceTable.tsx Uses string contract keys for grouping.
types.ts Aligns response types with API models.
factories.ts Generates representative keys and complete fixtures.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@blarghmatey

Copy link
Copy Markdown
Member Author

python-tests fails here for a reason that has nothing to do with this PR — it is broken on main itself with ModuleNotFoundError: No module named 'pypdf', and has failed on the last five main runs (most recently run 32510421346, "Update dependency tiktoken to >=0.13,<0.14" #3816). This branch changes only TypeScript types, fixtures and two call sites; it touches no Python at all.

@blarghmatey

Copy link
Copy Markdown
Member Author

Superseded — #3773 (merged 2026-08-31) already landed this exact fix on main (contract_pk/courserun_pk/program_pk number→string, the new nullable cohort fields, the CoursePerformanceTable map-key type, the docstring) as part of the larger contract-scoped analytics work in 9311a7e. Rebasing this branch leaves nothing but a would-be-empty commit, so closing rather than merging a no-op.

Closing this in favor of what's already on main.

@blarghmatey blarghmatey closed this Sep 9, 2026
@blarghmatey
blarghmatey deleted the fix/analytics-types-drift branch September 9, 2026 13:09
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.

3 participants