feat: Enrollment Tracks OutlineProcessor (MST-685)

NOTE: This will require a forced backfill of course outlines to update
the course content data in learning_sequences:

  python manage.py cms backfill_course_outlines --force

Without this backfill, the learning_sequences API will continue to serve
stale content data that has no user partition group data. It won't cause
errors, but it won't do the exclusions properly.

Commit summary:

* Created EnrollmentTrackPartitionGroupsOutlineProcessor to process the
  enrollment_track User Partition Group, allowing Sequences and Sections
  to be removed based on their group_access settings.
* Added user_partition_groups attribute to CourseLearningSequenceData
  and CourseSectionData in learning_sequences/data.py, along with
  backing model data.
* get_outline_from_modulestore now extracts group_access settings from
  Sections and Sequences. It also bubbles up group_access settings from
  Units, meaning that if a Sequence with no group_access setting has
  Units that are all set to show only to the Verified enrollment track,
  then the Sequence will only show to the Verified enrollment track.

This commit adds model-level support for all user partition groups by
capturing all the content group associations (group_access), but it only
implements the code checks for the enrollment track partition. It's not
clear that we want to generalize, since there's only one other partition
type (A/B testing) that is applicable at the outline level.

It's important to note that there is no way to set the group_access for
a Section or Sequence in Studio today. It's only possible by direct
editing of the OLX for import. That being said, the block structures
framework supports applying course groups at this level, and this commit
moves learning_sequences closer to feature parity.

The bubbling up from Units to the parent Sequence was done to mitigate
confusion when a Sequence is entirely composed of Units that are not
visible to the user because of content group restrictions. It's not
clear whether this is something we want to do in the long term, since it
would simplify the code to always specify group_access at the Sequence
level. This first pass is done partially to collect better data about
places in our courses where this kind of usage is already happening.

Most of the EnrollmentTrackPartitionGroupsOutlineProcessor code and its
tests were written by @schenedx.
This commit is contained in:
David Ormsbee
2021-04-14 12:26:25 -04:00
parent 3ce04b7983
commit dfb80acc11
10 changed files with 1015 additions and 39 deletions

View File

@@ -19,9 +19,9 @@ log = logging.getLogger(__name__)
FEATURES = getattr(settings, 'FEATURES', {})
def create_enrollment_track_partition(course):
def create_enrollment_track_partition_with_course_id(course_id):
"""
Create and return the dynamic enrollment track user partition.
Create and return the dynamic enrollment track user partition based only on course_id.
If it cannot be created, None is returned.
"""
if not FEATURES.get('ENABLE_ENROLLMENT_TRACK_USER_PARTITION'):
@@ -33,6 +33,21 @@ def create_enrollment_track_partition(course):
log.warning("No 'enrollment_track' scheme registered, EnrollmentTrackUserPartition will not be created.")
return None
partition = enrollment_track_scheme.create_user_partition(
id=ENROLLMENT_TRACK_PARTITION_ID,
name=_("Enrollment Track Groups"),
description=_("Partition for segmenting users by enrollment track"),
parameters={"course_id": str(course_id)}
)
return partition
def create_enrollment_track_partition(course):
"""
Create and return the dynamic enrollment track user partition.
If it cannot be created, None is returned.
"""
used_ids = {p.id for p in course.user_partitions}
if ENROLLMENT_TRACK_PARTITION_ID in used_ids:
log.warning(
@@ -44,10 +59,4 @@ def create_enrollment_track_partition(course):
)
return None
partition = enrollment_track_scheme.create_user_partition(
id=ENROLLMENT_TRACK_PARTITION_ID,
name=_("Enrollment Track Groups"),
description=_("Partition for segmenting users by enrollment track"),
parameters={"course_id": str(course.id)}
)
return partition
return create_enrollment_track_partition_with_course_id(course.id)