Skip to content

Commit

Permalink
feat: persist course titles on assignments for UIUX (#296)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnagro authored Oct 20, 2023
2 parents 43f7680 + cfb97b2 commit 4d10123
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Meta:
'learner_email',
'lms_user_id',
'content_key',
'content_title',
'content_quantity',
'state',
'transaction_uuid',
Expand Down
22 changes: 22 additions & 0 deletions enterprise_access/apps/api/v1/tests/test_allocation_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def setUpTestData(cls):
super().setUpTestData()
cls.enterprise_uuid = TEST_ENTERPRISE_UUID
cls.content_key = 'course-v1:edX+edXPrivacy101+3T2020'
cls.content_title = 'edx: Privacy 101'

# Create a pair of AssignmentConfiguration + SubsidyAccessPolicy for the main test customer.
cls.assignment_configuration = AssignmentConfigurationFactory(
Expand All @@ -72,6 +73,7 @@ def setUpTestData(cls):
learner_email='alice@foo.com',
lms_user_id=None,
content_key=cls.content_key,
content_title=cls.content_title,
content_quantity=-123,
state=LearnerContentAssignmentStateChoices.ERRORED,
)
Expand All @@ -80,6 +82,7 @@ def setUpTestData(cls):
learner_email='bob@foo.com',
lms_user_id=None,
content_key=cls.content_key,
content_title=cls.content_title,
content_quantity=-456,
state=LearnerContentAssignmentStateChoices.ALLOCATED,
)
Expand All @@ -88,6 +91,7 @@ def setUpTestData(cls):
learner_email='carol@foo.com',
lms_user_id=None,
content_key=cls.content_key,
content_title=cls.content_title,
content_quantity=-789,
state=LearnerContentAssignmentStateChoices.ALLOCATED,
)
Expand Down Expand Up @@ -137,6 +141,7 @@ def test_allocate_happy_path(self, mock_allocate, mock_can_allocate):
'learner_email': 'alice@foo.com',
'lms_user_id': None,
'content_key': self.content_key,
'content_title': self.content_title,
'content_quantity': -123,
'last_notification_at': None,
'state': LearnerContentAssignmentStateChoices.ERRORED,
Expand All @@ -151,6 +156,7 @@ def test_allocate_happy_path(self, mock_allocate, mock_can_allocate):
'learner_email': 'bob@foo.com',
'lms_user_id': None,
'content_key': self.content_key,
'content_title': self.content_title,
'content_quantity': -456,
'last_notification_at': None,
'state': LearnerContentAssignmentStateChoices.ALLOCATED,
Expand All @@ -165,6 +171,7 @@ def test_allocate_happy_path(self, mock_allocate, mock_can_allocate):
'learner_email': 'carol@foo.com',
'lms_user_id': None,
'content_key': self.content_key,
'content_title': self.content_title,
'content_quantity': -789,
'last_notification_at': None,
'state': LearnerContentAssignmentStateChoices.ALLOCATED,
Expand Down Expand Up @@ -311,6 +318,7 @@ def setUpTestData(cls):
super().setUpTestData()
cls.enterprise_uuid = OTHER_TEST_ENTERPRISE_UUID
cls.content_key = 'course-v1:edX+edXPrivacy101+3T2020'
cls.content_title = 'edX: Privacy 101'

# Create a pair of AssignmentConfiguration + SubsidyAccessPolicy for the main test customer.
cls.assignment_configuration = AssignmentConfigurationFactory(
Expand Down Expand Up @@ -356,8 +364,13 @@ def delete_assignments():
@mock.patch.object(
AssignedLearnerCreditAccessPolicy, 'aggregates_for_policy', autospec=True,
)
@mock.patch(
'enterprise_access.apps.content_assignments.api.get_and_cache_content_metadata',
return_value=mock.MagicMock(),
)
def test_allocate_happy_path_e2e(
self,
mock_get_and_cache_content_metadata,
mock_aggregates_for_policy,
mock_subsidy_balance,
mock_is_subsidy_active,
Expand All @@ -367,6 +380,9 @@ def test_allocate_happy_path_e2e(
Tests that the allocate view does the underlying checks and creates
assignment records as we'd expect.
"""
mock_get_and_cache_content_metadata.return_value = {
'title': self.content_title,
}
mock_aggregates_for_policy.return_value = {
'total_quantity': -100 * 100,
}
Expand Down Expand Up @@ -395,6 +411,7 @@ def test_allocate_happy_path_e2e(
'learner_email': 'new@foo.com',
'lms_user_id': None,
'content_key': self.content_key,
'content_title': self.content_title,
'content_quantity': -123.45 * 100,
'last_notification_at': None,
'state': LearnerContentAssignmentStateChoices.ALLOCATED,
Expand Down Expand Up @@ -619,8 +636,13 @@ def test_allocate_too_much_subsidy_spend_e2e(
'enterprise_access.apps.api.v1.views.subsidy_access_policy.LmsApiClient',
return_value=mock.MagicMock(),
)
@mock.patch(
'enterprise_access.apps.content_assignments.api.get_and_cache_content_metadata',
return_value=mock.MagicMock(),
)
def test_allocate_too_much_existing_allocation_e2e(
self,
mock_get_and_cache_content_metadata, # pylint: disable=unused-argument
mock_lms_api_client,
mock_is_subsidy_active, # pylint: disable=unused-argument
mock_catalog_inclusion, # pylint: disable=unused-argument
Expand Down
2 changes: 2 additions & 0 deletions enterprise_access/apps/api/v1/tests/test_assignment_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ def test_retrieve(self, role_context_dict):
'uuid': str(self.assignment_allocated_pre_link.uuid),
'assignment_configuration': str(self.assignment_allocated_pre_link.assignment_configuration.uuid),
'content_key': self.assignment_allocated_pre_link.content_key,
'content_title': self.assignment_allocated_pre_link.content_title,
'content_quantity': self.assignment_allocated_pre_link.content_quantity,
'last_notification_at': None,
'learner_email': self.assignment_allocated_pre_link.learner_email,
Expand Down Expand Up @@ -578,6 +579,7 @@ def test_retrieve(self, role_context_dict):
'uuid': str(self.requester_assignment_accepted.uuid),
'assignment_configuration': str(self.requester_assignment_accepted.assignment_configuration.uuid),
'content_key': self.requester_assignment_accepted.content_key,
'content_title': self.requester_assignment_accepted.content_title,
'content_quantity': self.requester_assignment_accepted.content_quantity,
'last_notification_at': None,
'learner_email': self.requester_assignment_accepted.learner_email,
Expand Down
15 changes: 15 additions & 0 deletions enterprise_access/apps/content_assignments/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import CourseLocator

from enterprise_access.apps.subsidy_access_policy.content_metadata_api import get_and_cache_content_metadata

from .constants import LearnerContentAssignmentStateChoices
from .models import AssignmentConfiguration, LearnerContentAssignment

Expand Down Expand Up @@ -281,16 +283,29 @@ def _update_and_refresh_assignments(assignment_records, fields_changed):
)


def _get_content_title(assignment_configuration, content_key):
"""
Helper to retrieve (from cache) the title of a content_key'ed content_metadata
"""
content_metadata = get_and_cache_content_metadata(
assignment_configuration.enterprise_customer_uuid,
content_key,
)
return content_metadata['title']


def _create_new_assignments(assignment_configuration, learner_emails, content_key, content_quantity):
"""
Helper to bulk save new LearnerContentAssignment instances.
"""
assignments_to_create = []
for learner_email in learner_emails:
content_title = _get_content_title(assignment_configuration, content_key)
assignment = LearnerContentAssignment(
assignment_configuration=assignment_configuration,
learner_email=learner_email,
content_key=content_key,
content_title=content_title,
content_quantity=content_quantity,
state=LearnerContentAssignmentStateChoices.ALLOCATED,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.6 on 2023-10-19 18:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('content_assignments', '0008_assignment_actions'),
]

operations = [
migrations.AddField(
model_name='historicallearnercontentassignment',
name='content_title',
field=models.CharField(blank=True, help_text='The ContentMetadata.title from content corresponding to the content_key', max_length=255, null=True),
),
migrations.AddField(
model_name='learnercontentassignment',
name='content_title',
field=models.CharField(blank=True, help_text='The ContentMetadata.title from content corresponding to the content_key', max_length=255, null=True),
),
]
8 changes: 8 additions & 0 deletions enterprise_access/apps/content_assignments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ class Meta:
"ContentMetadata.content_key in enterprise-catalog."
),
)
content_title = models.CharField(
max_length=255,
blank=True,
null=True,
help_text=(
"The ContentMetadata.title from content corresponding to the content_key"
),
)
content_quantity = models.BigIntegerField(
null=False,
blank=False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ class Meta:
learner_email = factory.LazyAttribute(lambda _: FAKER.email())
lms_user_id = factory.LazyAttribute(lambda _: FAKER.pyint())
content_key = factory.LazyAttribute(lambda _: random_content_key())
content_title = factory.LazyAttribute(lambda _: f'{FAKER.word()}: a master class')
content_quantity = factory.LazyAttribute(lambda _: FAKER.pyint())
17 changes: 16 additions & 1 deletion enterprise_access/apps/content_assignments/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""
Tests for the ``api.py`` module of the content_assignments app.
"""
from unittest import mock

import ddt
from django.test import TestCase

Expand Down Expand Up @@ -208,41 +210,53 @@ def test_allocate_assignments_negative_quantity(self):
content_price_cents,
)

def test_allocate_assignments_happy_path(self):
@mock.patch(
'enterprise_access.apps.content_assignments.api.get_and_cache_content_metadata',
return_value=mock.MagicMock(),
)
def test_allocate_assignments_happy_path(self, mock_get_and_cache_content_metadata):
"""
Tests the allocation of new assignments against a given configuration.
"""
content_key = 'demoX'
content_title = 'edx: Demo 101'
content_price_cents = 100
learners_to_assign = [
f'{name}@foo.com' for name in ('alice', 'bob', 'carol', 'david', 'eugene')
]
mock_get_and_cache_content_metadata.return_value = {
'title': content_title,
}

allocated_assignment = LearnerContentAssignmentFactory.create(
assignment_configuration=self.assignment_configuration,
learner_email='alice@foo.com',
content_key=content_key,
content_title=content_title,
content_quantity=-content_price_cents,
state=LearnerContentAssignmentStateChoices.ALLOCATED,
)
accepted_assignment = LearnerContentAssignmentFactory.create(
assignment_configuration=self.assignment_configuration,
learner_email='bob@foo.com',
content_key=content_key,
content_title=content_title,
content_quantity=-content_price_cents,
state=LearnerContentAssignmentStateChoices.ACCEPTED,
)
cancelled_assignment = LearnerContentAssignmentFactory.create(
assignment_configuration=self.assignment_configuration,
learner_email='carol@foo.com',
content_key=content_key,
content_title=content_title,
content_quantity=-200,
state=LearnerContentAssignmentStateChoices.CANCELLED,
)
errored_assignment = LearnerContentAssignmentFactory.create(
assignment_configuration=self.assignment_configuration,
learner_email='david@foo.com',
content_key=content_key,
content_title=content_title,
content_quantity=-200,
state=LearnerContentAssignmentStateChoices.ERRORED,
)
Expand Down Expand Up @@ -286,6 +300,7 @@ def test_allocate_assignments_happy_path(self):
self.assertEqual(created_assignment.assignment_configuration, self.assignment_configuration)
self.assertEqual(created_assignment.learner_email, 'eugene@foo.com')
self.assertEqual(created_assignment.content_key, content_key)
self.assertEqual(created_assignment.content_title, content_title)
self.assertEqual(created_assignment.content_quantity, -1 * content_price_cents)
self.assertEqual(created_assignment.state, LearnerContentAssignmentStateChoices.ALLOCATED)

Expand Down

0 comments on commit 4d10123

Please sign in to comment.