Skip to content

Commit

Permalink
fix: update parent_content_key and is_assigned_course_run on assi…
Browse files Browse the repository at this point in the history
…gnment re-allocation (#564)
  • Loading branch information
adamstankiewicz committed Sep 19, 2024
1 parent 3fc0229 commit 63abe56
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 67 deletions.
36 changes: 33 additions & 3 deletions enterprise_access/apps/content_assignments/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
'lms_user_id', 'learner_email', 'allocation_batch_id',
'content_quantity', 'state', 'preferred_course_run_key',
'allocated_at', 'cancelled_at', 'expired_at', 'errored_at',
'parent_content_key', 'is_assigned_course_run',
]


Expand Down Expand Up @@ -329,9 +330,16 @@ def allocate_assignments(assignment_configuration, learner_emails, content_key,
existing_assignments_needs_update = set()

# This step to find and update the preferred_course_run_key is required in order
# for nudge emails to target the start date of the new run.
# for nudge emails to target the start date of the new run. For run-based assignments,
# the preferred_course_run_key is the same as the assignment's content_key.
preferred_course_run_key = _get_preferred_course_run_key(assignment_configuration, content_key)

# Determine if the assignment's content_key is a course run or a course key based
# on an associated parent content key. If the parent content key is None, then the
# assignment is for a course; otherwise, it's an assignment for a course run.
parent_content_key = _get_parent_content_key(assignment_configuration, content_key)
is_assigned_course_run = bool(parent_content_key)

# Split up the existing assignment records by state
for assignment in existing_assignments:
if not assignment.lms_user_id:
Expand All @@ -349,14 +357,28 @@ def allocate_assignments(assignment_configuration, learner_emails, content_key,
existing_assignments_needs_update.add(assignment)

if assignment.state in LearnerContentAssignmentStateChoices.REALLOCATE_STATES:
_reallocate_assignment(assignment, content_quantity, allocation_batch_id, preferred_course_run_key)
_reallocate_assignment(
assignment,
content_quantity,
allocation_batch_id,
preferred_course_run_key,
parent_content_key,
is_assigned_course_run,
)
existing_assignments_needs_update.add(assignment)
elif assignment.state == LearnerContentAssignmentStateChoices.ALLOCATED:
# For some already-allocated assignments being re-assigned, we might still need to update the preferred
# course run for nudge email purposes.
if assignment.preferred_course_run_key != preferred_course_run_key:
assignment.preferred_course_run_key = preferred_course_run_key
existing_assignments_needs_update.add(assignment)
# Update the parent_content_key and is_assigned_course_run fields if they have changed.
if assignment.parent_content_key != parent_content_key:
assignment.parent_content_key = parent_content_key
existing_assignments_needs_update.add(assignment)
if assignment.is_assigned_course_run != is_assigned_course_run:
assignment.is_assigned_course_run = is_assigned_course_run
existing_assignments_needs_update.add(assignment)

learner_emails_with_existing_assignments.add(assignment.learner_email.lower())

Expand Down Expand Up @@ -505,7 +527,13 @@ def _get_existing_assignments_for_allocation(
return existing_assignments


def _reallocate_assignment(assignment, content_quantity, allocation_batch_id, preferred_course_run_key):
def _reallocate_assignment(
assignment,
content_quantity,
allocation_batch_id,
preferred_course_run_key,
parent_content_key,
is_assigned_course_run):
"""
Modifies a ``LearnerContentAssignment`` record during the allocation flow. The record
is **not** saved.
Expand All @@ -519,6 +547,8 @@ def _reallocate_assignment(assignment, content_quantity, allocation_batch_id, pr
assignment.expired_at = None
assignment.errored_at = None
assignment.preferred_course_run_key = preferred_course_run_key
assignment.parent_content_key = parent_content_key
assignment.is_assigned_course_run = is_assigned_course_run
# Prevent invalid data from entering the database by calling the low-level full_clean() function manually.
assignment.full_clean()
return assignment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ def handle(self, *args, **options):
if datetime_start_date is not None
else False
)

if not can_send_nudge_notification_in_advance:
logger.info(
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ def setUp(self):
assignment_configuration=self.assignment_configuration,
learner_email='[email protected]',
lms_user_id=None,
content_key='edX+edXPrivacy101',
content_key='course-v1:edX+edXPrivacy101+1T2022',
parent_content_key='edX+edXPrivacy101',
is_assigned_course_run=True,
preferred_course_run_key='course-v1:edX+edXPrivacy101+1T2022',
content_title='edx: Privacy 101',
content_quantity=-123,
Expand All @@ -61,6 +63,8 @@ def setUp(self):
learner_email='[email protected]',
lms_user_id=None,
content_key='edX+edXAccessibility101',
parent_content_key=None,
is_assigned_course_run=False,
preferred_course_run_key='course-v1:edX+edXAccessibility101+1T2022',
content_title='edx: Accessibility 101',
content_quantity=-456,
Expand All @@ -72,6 +76,8 @@ def setUp(self):
learner_email='[email protected]',
lms_user_id=None,
content_key='edX+edXQuadrilateral306090',
parent_content_key=None,
is_assigned_course_run=False,
preferred_course_run_key='course-v1:edX+edXQuadrilateral306090+1T2022',
content_title='edx: Quadrilateral 306090',
content_quantity=-456,
Expand All @@ -82,6 +88,8 @@ def setUp(self):
learner_email='[email protected]',
lms_user_id=None,
content_key='edX+edXTesseract4D',
parent_content_key=None,
is_assigned_course_run=False,
preferred_course_run_key='course-v1:edX+edXTesseract4D+1T2022',
content_title='edx: Tesseract 4D',
content_quantity=-456,
Expand All @@ -93,6 +101,8 @@ def setUp(self):
learner_email='[email protected]',
lms_user_id=None,
content_key='edX+edXIsoscelesPyramid2012',
parent_content_key=None,
is_assigned_course_run=False,
preferred_course_run_key='course-v1:edX+edXIsoscelesPyramid2012+1T2022',
content_title='edx: IsoscelesPyramid 2012',
content_quantity=-456,
Expand All @@ -103,6 +113,8 @@ def setUp(self):
learner_email='[email protected]',
lms_user_id=None,
content_key='edX+edXBeeHivesAlive0220',
parent_content_key=None,
is_assigned_course_run=False,
preferred_course_run_key='course-v1:edX+edXBeeHivesAlive0220+1T2022',
content_title='edx: BeeHivesAlive 0220',
content_quantity=-456,
Expand Down Expand Up @@ -229,7 +241,9 @@ def test_command(
},
'course_type': 'executive-education-2u',
},
'edX+edXPrivacy101': {
# `self.alice_assignment` is an assignment for a course run, so its run-based content_key
# should be used as the key in this dict.
'course-v1:edX+edXPrivacy101+1T2022': {
'key': 'edX+edXPrivacy101',
'normalized_metadata': {
'start_date': start_date.strftime("%Y-%m-%dT%H:%M:%SZ"),
Expand Down Expand Up @@ -367,7 +381,9 @@ def test_command_multiple_assignment_dates(
},
'course_type': 'executive-education-2u',
},
'edX+edXPrivacy101': {
# `self.alice_assignment` is an assignment for a course run, so its run-based content_key
# should be used as the key in this dict.
'course-v1:edX+edXPrivacy101+1T2022': {
'key': 'edX+edXPrivacy101',
'course_runs': [
{
Expand Down Expand Up @@ -458,7 +474,9 @@ def test_command_multiple_assignment_course_types(
},
},
},
'edX+edXPrivacy101': {
# `self.alice_assignment` is an assignment for a course run, so its run-based content_key
# should be used as the key in this dict.
'course-v1:edX+edXPrivacy101+1T2022': {
'key': 'edX+edXPrivacy101',
'course_type': 'executive-education-2u',
'normalized_metadata': {
Expand Down Expand Up @@ -587,7 +605,9 @@ def test_command_multiple_assignment_states(
},
},
},
'edX+edXPrivacy101': {
# `self.alice_assignment` is an assignment for a course run, so its run-based content_key
# should be used as the key in this dict.
'course-v1:edX+edXPrivacy101+1T2022': {
'key': 'edX+edXPrivacy101',
'course_type': 'executive-education-2u',
'normalized_metadata': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ class Meta:
lms_user_id = factory.LazyAttribute(lambda _: FAKER.pyint())
content_key = factory.LazyAttribute(lambda _: random_content_key())
parent_content_key = factory.LazyAttribute(lambda _: random_parent_content_key())
is_assigned_course_run = True
content_title = factory.LazyAttribute(lambda _: f'{FAKER.word()}: a master class')
content_quantity = factory.LazyAttribute(lambda _: FAKER.pyfloat(positive=False, right_digits=0))
Loading

0 comments on commit 63abe56

Please sign in to comment.