Skip to content

fix: record open-course enrollments and correct dashboard metrics - #74

Open
ccantillo wants to merge 1 commit into
mainfrom
crls/fix/dashboard-metrics-backend
Open

fix: record open-course enrollments and correct dashboard metrics#74
ccantillo wants to merge 1 commit into
mainfrom
crls/fix/dashboard-metrics-backend

Conversation

@ccantillo

Copy link
Copy Markdown
Contributor

Fix dashboard metrics not updating — backend

Refs nau-technical#1021

Related MFE PRs: frontend-app-corporate, frontend-app-partner-catalogs
(cache-invalidation fixes; they work without this PR but the metrics they
refresh are only correct with it).

Summary

The corporate dashboards (general partner dashboard, catalog dashboard, and
course cards) never reflected enrollments, certified learners, or completion
activity. End-to-end testing against the real DRF viewsets showed the root
causes are in this repo, not in the MFEs:

  1. Enrollments in open (free/audit) courses never created a
    CatalogCourseEnrollment record
    , and that record is what every metric
    counts — so dashboards and course cards stayed at 0.
  2. The certified_count SQL annotation always returned 0 due to two bugs.
  3. The partner serializer mapped the Enrollments column to the learner
    count
    , so the general dashboard showed how many learners exist instead of
    course enrollments.
  4. Catalogs containing a course run with no modulestore content crashed the
    manage endpoint with a 500 while computing completion rates.

What changed

partner_catalog/services/enrollments.py

create_or_activate_course_enrollment returned early for open courses after
syncing the LMS enrollment, without persisting the CatalogCourseEnrollment.
The create-or-activate block (previously only reachable by the paid path) is
extracted into _persist_enrollment_record() and now runs for open courses
too. Open enrollments still do not consume the paid enrollment bag:
can_consume_course_limit only counts enrollments in paid courses.

partner_catalog/services/certificates.py

Two bugs in annotate_certified_count:

  • users_qs / courses_qs were built with OuterRef("pk") but used at two
    different nesting depths — inside Exists() (1 level, correct) and inside
    the count Subquery (2 levels). At depth 2, Django resolved the ref against
    the GeneratedCertificate queryset, silently matching nothing.
  • .values() with no arguments grouped by every model field (per certificate
    row), so the count subquery could only ever return 1.

The four annotators now pass factory callables so the helper builds each
subquery at the correct depth (OuterRef("pk") for Exists,
OuterRef(OuterRef("pk")) for the count subquery), and the count groups by a
constant to return a single distinct-user count.

partner_catalog/api/v1/views.py / serializers.py

PartnerSerializer.enrollments pointed at learners_count. Added a real
enrollments_count annotation to PartnerViewset (active enrollments across
the partner's catalogs) and pointed the serializer at it.

partner_catalog/services/progress.py + edxapp_wrapper/courseware_module*

compute_progress_percent_by_user raised an unhandled
xmodule.modulestore.exceptions.ItemNotFoundError when a course run existed
only as a CourseOverview stub, turning the whole manage-catalogs request
into a 500. The exception class is now exposed through the existing
edxapp_wrapper pattern (item_not_found_error()) and caught narrowly,
returning 0.0 progress for that run.

tests/test_catalog_course_enrollment_service.py

test_new_enrollment_open_course_does_not_consume_bag_and_no_create asserted
the buggy behavior. Renamed to ..._and_creates_record; it now asserts the
record is created with active=True while the paid bag is still never
consulted.

@ccantillo
ccantillo marked this pull request as ready for review September 1, 2026 22:23
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