Skip to content

Commit fc3fa46

Browse files
authored
Merge pull request #364 from openedx/pwnage101/ENT-8105
feat: add a "redeemed" assignment action
2 parents 1e58947 + b91340a commit fc3fa46

File tree

5 files changed

+77
-4
lines changed

5 files changed

+77
-4
lines changed

enterprise_access/apps/content_assignments/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ class AssignmentActions:
3636
LEARNER_LINKED = 'learner_linked'
3737
NOTIFIED = 'notified'
3838
REMINDED = 'reminded'
39+
REDEEMED = 'redeemed'
3940
CANCELLED_NOTIFICATION = 'cancelled'
4041
AUTOMATIC_CANCELLATION_NOTIFICATION = 'automatic_cancellation'
4142

4243
CHOICES = (
4344
(LEARNER_LINKED, 'Learner linked to customer'),
4445
(NOTIFIED, 'Learner notified of assignment'),
4546
(REMINDED, 'Learner reminded about assignment'),
47+
(REDEEMED, 'Learner redeemed the assigned content'),
4648
(CANCELLED_NOTIFICATION, 'Learner assignment cancelled'),
4749
(AUTOMATIC_CANCELLATION_NOTIFICATION, 'Learner assignment cancelled automatically'),
4850
)
@@ -55,10 +57,12 @@ class AssignmentActionErrors:
5557
"""
5658
EMAIL_ERROR = 'email_error'
5759
INTERNAL_API_ERROR = 'internal_api_error'
60+
ENROLLMENT_ERROR = 'enrollment_error'
5861

5962
CHOICES = (
6063
(EMAIL_ERROR, 'Email error'),
6164
(INTERNAL_API_ERROR, 'Internal API error'),
65+
(ENROLLMENT_ERROR, 'Enrollment error'),
6266
)
6367

6468

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.2.6 on 2023-12-12 23:01
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('content_assignments', '0012_assignmentaction_add_automatic_cancellation_option'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='historicallearnercontentassignmentaction',
15+
name='action_type',
16+
field=models.CharField(choices=[('learner_linked', 'Learner linked to customer'), ('notified', 'Learner notified of assignment'), ('reminded', 'Learner reminded about assignment'), ('redeemed', 'Learner redeemed the assigned content'), ('cancelled', 'Learner assignment cancelled'), ('automatic_cancellation', 'Learner assignment cancelled automatically')], db_index=True, help_text='The type of action take on the related assignment record.', max_length=255),
17+
),
18+
migrations.AlterField(
19+
model_name='historicallearnercontentassignmentaction',
20+
name='error_reason',
21+
field=models.CharField(blank=True, choices=[('email_error', 'Email error'), ('internal_api_error', 'Internal API error'), ('enrollment_error', 'Enrollment error')], db_index=True, help_text='The type of error that occurred during the action, if any.', max_length=255, null=True),
22+
),
23+
migrations.AlterField(
24+
model_name='learnercontentassignmentaction',
25+
name='action_type',
26+
field=models.CharField(choices=[('learner_linked', 'Learner linked to customer'), ('notified', 'Learner notified of assignment'), ('reminded', 'Learner reminded about assignment'), ('redeemed', 'Learner redeemed the assigned content'), ('cancelled', 'Learner assignment cancelled'), ('automatic_cancellation', 'Learner assignment cancelled automatically')], db_index=True, help_text='The type of action take on the related assignment record.', max_length=255),
27+
),
28+
migrations.AlterField(
29+
model_name='learnercontentassignmentaction',
30+
name='error_reason',
31+
field=models.CharField(blank=True, choices=[('email_error', 'Email error'), ('internal_api_error', 'Internal API error'), ('enrollment_error', 'Enrollment error')], db_index=True, help_text='The type of error that occurred during the action, if any.', max_length=255, null=True),
32+
),
33+
]

enterprise_access/apps/content_assignments/models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,25 @@ def add_errored_expiration_action(self, exc):
372372
traceback=format_traceback(exc),
373373
)
374374

375+
def add_successful_redeemed_action(self):
376+
"""
377+
Adds a successful redeemed LearnerContentAssignmentAction for this assignment record.
378+
"""
379+
return self.actions.create(
380+
action_type=AssignmentActions.REDEEMED,
381+
completed_at=timezone.now(),
382+
)
383+
384+
def add_errored_redeemed_action(self, exc):
385+
"""
386+
Adds an errored redeemed LearnerContentAssignmentAction for this assignment record.
387+
"""
388+
return self.actions.create(
389+
action_type=AssignmentActions.REDEEMED,
390+
error_reason=AssignmentActionErrors.ENROLLMENT_ERROR,
391+
traceback=format_traceback(exc),
392+
)
393+
375394
@classmethod
376395
def annotate_dynamic_fields_onto_queryset(cls, queryset):
377396
"""

enterprise_access/apps/subsidy_access_policy/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1202,15 +1202,17 @@ def redeem(self, lms_user_id, content_key, all_transactions, metadata=None, **kw
12021202
metadata=metadata,
12031203
requested_price_cents=requested_price_cents,
12041204
)
1205-
except SubsidyAPIHTTPError:
1205+
except SubsidyAPIHTTPError as exc:
12061206
# Migrate assignment to errored if the subsidy API call errored.
12071207
found_assignment.state = LearnerContentAssignmentStateChoices.ERRORED
12081208
found_assignment.save()
1209+
found_assignment.add_errored_redeemed_action(exc)
12091210
raise
12101211
# Migrate assignment to accepted.
12111212
found_assignment.state = LearnerContentAssignmentStateChoices.ACCEPTED
12121213
found_assignment.transaction_uuid = ledger_transaction.get('uuid') # uuid should always be in the API response.
12131214
found_assignment.save()
1215+
found_assignment.add_successful_redeemed_action()
12141216
return ledger_transaction
12151217

12161218
def validate_requested_allocation_price(self, content_key, requested_price_cents):

enterprise_access/apps/subsidy_access_policy/tests/test_models.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
from django.core.exceptions import ValidationError
1414
from django.test import TestCase, override_settings
1515

16-
from enterprise_access.apps.content_assignments.constants import LearnerContentAssignmentStateChoices
16+
from enterprise_access.apps.content_assignments.constants import (
17+
AssignmentActionErrors,
18+
AssignmentActions,
19+
LearnerContentAssignmentStateChoices
20+
)
1721
from enterprise_access.apps.content_assignments.models import AssignmentConfiguration
1822
from enterprise_access.apps.content_assignments.tests.factories import LearnerContentAssignmentFactory
1923
from enterprise_access.apps.subsidy_access_policy.constants import (
@@ -1022,14 +1026,25 @@ def test_redeem(
10221026
**expected_redeem_payload,
10231027
)
10241028

1025-
# Finally, assert that the assignment object was correctly updated to reflect the success/failure.
1029+
# assert that the assignment object was correctly updated to reflect the success/failure.
10261030
if assignment:
10271031
assignment.refresh_from_db()
10281032
assert assignment.state == assignment_ending_state
1029-
# happy path should result in an updated transaction_uuid.
10301033
if not redeem_raises:
1034+
# happy path should result in an updated transaction_uuid.
10311035
assert assignment.transaction_uuid == test_transaction_uuid
10321036

1037+
# happy path should also result in a null error_reason on the redeemed action.
1038+
redeemed_action = assignment.actions.last()
1039+
assert redeemed_action.action_type == AssignmentActions.REDEEMED
1040+
assert not redeemed_action.error_reason
1041+
if fail_subsidy_create_transaction:
1042+
# sad path should generate a failed redeememd action with populated error_reason and traceback.
1043+
redeemed_action = assignment.actions.last()
1044+
assert redeemed_action.action_type == AssignmentActions.REDEEMED
1045+
assert redeemed_action.error_reason == AssignmentActionErrors.ENROLLMENT_ERROR
1046+
assert redeemed_action.traceback
1047+
10331048
def test_can_allocate_inactive_policy(self):
10341049
"""
10351050
Tests that inactive policies can't be allocated against.

0 commit comments

Comments
 (0)