Skip to content

feat(b2b_dashboard): serve activity, add needs-attention count - #78

Merged
daniellefrappier18 merged 6 commits into
mainfrom
daniellef/needs-attention-aggregate
Sep 30, 2026
Merged

daniellefrappier18 merged 6 commits into
mainfrom
daniellef/needs-attention-aggregate

Conversation

@daniellefrappier18

@daniellefrappier18 daniellefrappier18 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

N/A — tracked internally as Witan project wp-needs-attention-aggregate-for-b2b-learner-progre-09344a, no GitHub issue.

Description (What does it do?)

  • Wires the learner-progress endpoint's last_active_on to the real column ol-data-platform#2693 added to mv_b2b_learner_enrollment, instead of a hardcoded NULL.
  • Updates completion_status's in_progress branch to also count tracked activity, matching the fix already shipped on the b2b_learner_records tenant in #62 — otherwise the two endpoints disagree on what counts as in-progress.
  • Adds needs_attention_count to the response envelope: a learner needs attention if they never started, or have gone quiet for 30+ days.
Implementation details

needs_attention is computed as completion_status = 'not_started' OR last_active_on <= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY), gated by the same consent check (outcomes_shared) as the other outcome aggregates. It's an overlapping count, not a new disjoint bucket — a row can be both in_progress and counted in needs_attention_count.

How can this be tested?

uv run pytest — 303 passed, including new tests asserting the needs_attention_count SQL and the updated in_progress CASE. OpenAPI spec regenerated via uv run bin/generate-openapi-spec.

Not exercised against a live StarRocks instance — these tests mock the DB layer (per the test module's own docstring) rather than executing SQL. I confirmed DATE_SUB/CURRENT_DATE() syntax against StarRocks docs, but a reviewer should sanity-check the query against a real mv_b2b_learner_enrollment before this ships to production.

Additional Context

Unblocks the "Needs attention" KPI tile on mit-learn's org/contract analytics dashboard (LearnerProgressCard.tsx), which currently omits it pending this field.

🤖 Generated with Claude Code


Post-merge correction to this description only; no code changed. The line above originally read last_active_on < DATE_SUB(...). The merged code uses <=, which is correct: the rule is "last recorded activity at least 30 days ago", so the 30th day itself counts, and test_needs_attention_boundary_is_computed_from_real_rows pins that boundary. Corrected here so the prose matches the shipped behavior. See #84, which adds the matching filter and row field and makes the intent explicit in a comment.

… on learner-progress

ol-data-platform#2693 added a real last_active_on to mv_b2b_learner_enrollment;
the learner-progress endpoint still projected it as a hardcoded NULL.  Wire it
through, match completion_status's in_progress branch to the activity-aware
definition already used by b2b_learner_records, and add needs_attention_count
to the envelope (not_started, or quiet for 30+ days) per wp-needs-attention-
aggregate-for-b2b-learner-progre-09344a.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

OpenAPI Changes

Show/hide changes
## Changes for b2b_dashboard.yaml:
1 changes: 0 error, 0 warning, 1 info
info	[response-required-property-added] at head/openapi/specs/b2b_dashboard.yaml
	in API GET /api/v1/analytics/organizations/{organization_id}/contracts/{contract_id}/learner-progress
		added the required property `needs_attention_count` to the response with the `200` status



## Changes for b2b_learner_records.yaml:
No changes detected

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

This comment was marked as outdated.

…ity buckets

Switch the needs-attention staleness check from < to <= so a learner
qualifies once 30 full days have passed with no activity, matching the
product definition, instead of requiring 31. Also update the
in_progress/not_started descriptions on CompletionStatusCounts to
mention activity, since in_progress already includes activity-only
enrollments with no grade.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

This comment was marked as outdated.

The existing test fabricated needs_attention_count via _FakePool, so it
couldn't catch a broken day-30 boundary or consent gate. Run the actual
_COMPLETION_STATUS/_NEEDS_ATTENTION strings against sqlite with
never-started, day-29 and exactly-day-30 rows, and with consent
fail-closed/open, so a regression in either fails here instead of only
passing through a mocked count.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

This comment was marked as outdated.

"No activity for 30 days or more" oversells the check: a grade-only
in_progress enrollment with no last_active_on has nothing to judge
staleness against, so it's never flagged even though it has no
recorded activity. Reword to "last recorded activity was at least 30
days ago" and regenerate the OpenAPI spec.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@daniellefrappier18

Copy link
Copy Markdown
Contributor Author

Addressed the "Clarify stale enrollment criteria and regenerate OpenAPI spec" item from Copilot's latest review (learner_models.py:227) in 3897077: reworded needs_attention_count's description from "haven't done anything in the course for 30 days or more" to "their last recorded activity was at least 30 days ago," since a grade-only in_progress enrollment with no last_active_on timestamp has nothing to judge staleness against and was never actually flagged. Applied the same wording to the module docstring and the _NEEDS_ATTENTION code comment for consistency, and regenerated openapi/specs/b2b_dashboard.yaml.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The production StarRocks query has not been exercised live, and the activity-date response still needs positive-path regression coverage.

Review effort: Balanced
Findings: None

Previously missed (1)

In code that hasn't changed since last review

Medium severity Test non-null last_active_on propagation and consent masking

tests/​test_dashboard_learner_progress.py:405

The query test checks only that last_active_on is masked when consent is off; the endpoint's shared-outcome fixture still supplies None for this field. If the new MV projection is replaced with NULL, these tests can pass even though the API never serves an activity date. Please assert that the inner query selects last_active_on from the MV and exercise a non-null date through the shared response, while checking it remains hidden without consent.

@daniellefrappier18
daniellefrappier18 merged commit 891747f into main Sep 30, 2026
6 checks passed
@daniellefrappier18
daniellefrappier18 deleted the daniellef/needs-attention-aggregate branch September 30, 2026 13:39
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