Skip to content

Commit f7411d1

Browse files
chore: decouple subsidy actions and user message dependencies
1 parent d0cfb0c commit f7411d1

File tree

5 files changed

+115
-68
lines changed

5 files changed

+115
-68
lines changed

enterprise_access/apps/api/v1/views/browse_and_request.py

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@
5151
from enterprise_access.apps.subsidy_access_policy.models import SubsidyAccessPolicy
5252
from enterprise_access.apps.subsidy_request.constants import (
5353
REUSABLE_REQUEST_STATES,
54-
LearnerCreditAdditionalActionStates,
5554
LearnerCreditRequestActionErrorReasons,
55+
LearnerCreditRequestActionTypes,
56+
LearnerCreditRequestUserMessages,
5657
SegmentEvents,
5758
SubsidyRequestStates,
5859
SubsidyTypeChoices
@@ -71,11 +72,7 @@
7172
send_learner_credit_bnr_request_approve_task,
7273
send_reminder_email_for_pending_learner_credit_request
7374
)
74-
from enterprise_access.apps.subsidy_request.utils import (
75-
get_action_choice,
76-
get_error_reason_choice,
77-
get_user_message_choice
78-
)
75+
from enterprise_access.apps.subsidy_request.utils import get_error_reason_choice
7976
from enterprise_access.apps.track.segment import track_event
8077
from enterprise_access.utils import format_traceback, get_subsidy_model
8178

@@ -898,8 +895,8 @@ def create(self, request, *args, **kwargs):
898895
self._reuse_existing_request(existing_request, course_price)
899896
LearnerCreditRequestActions.create_action(
900897
learner_credit_request=existing_request,
901-
recent_action=get_action_choice(SubsidyRequestStates.REQUESTED),
902-
status=get_user_message_choice(SubsidyRequestStates.REQUESTED),
898+
recent_action=LearnerCreditRequestActionTypes.REQUESTED,
899+
status=LearnerCreditRequestUserMessages.REQUESTED,
903900
)
904901
# Trigger admin email notification with the latest request
905902
send_learner_credit_bnr_admins_email_with_new_requests_task.delay(
@@ -941,8 +938,8 @@ def create(self, request, *args, **kwargs):
941938
lcr = LearnerCreditRequest.objects.get(uuid=lcr_uuid)
942939
LearnerCreditRequestActions.create_action(
943940
learner_credit_request=lcr,
944-
recent_action=get_action_choice(SubsidyRequestStates.REQUESTED),
945-
status=get_user_message_choice(SubsidyRequestStates.REQUESTED),
941+
recent_action=LearnerCreditRequestActionTypes.REQUESTED,
942+
status=LearnerCreditRequestUserMessages.REQUESTED,
946943
)
947944

948945
# Trigger admin email notification with the latest request
@@ -982,8 +979,8 @@ def approve(self, request, *args, **kwargs):
982979
# Log "approve" as recent action in the Request Action model.
983980
lc_request_action = LearnerCreditRequestActions.create_action(
984981
learner_credit_request=lc_request,
985-
recent_action=get_action_choice(SubsidyRequestStates.APPROVED),
986-
status=get_user_message_choice(SubsidyRequestStates.APPROVED),
982+
recent_action=LearnerCreditRequestActionTypes.APPROVED,
983+
status=LearnerCreditRequestUserMessages.APPROVED,
987984
)
988985

989986
try:
@@ -1014,7 +1011,7 @@ def approve(self, request, *args, **kwargs):
10141011
logger.exception(error_msg)
10151012

10161013
# Update approve action with error reason.
1017-
lc_request_action.status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
1014+
lc_request_action.status = LearnerCreditRequestUserMessages.REQUESTED
10181015
lc_request_action.error_reason = get_error_reason_choice(
10191016
LearnerCreditRequestActionErrorReasons.FAILED_APPROVAL
10201017
)
@@ -1044,8 +1041,8 @@ def cancel(self, request, *args, **kwargs):
10441041
error_msg = None
10451042
lc_action = LearnerCreditRequestActions.create_action(
10461043
learner_credit_request=learner_credit_request,
1047-
recent_action=get_action_choice(SubsidyRequestStates.CANCELLED),
1048-
status=get_user_message_choice(SubsidyRequestStates.CANCELLED),
1044+
recent_action=LearnerCreditRequestActionTypes.CANCELLED,
1045+
status=LearnerCreditRequestUserMessages.CANCELLED,
10491046
)
10501047

10511048
try:
@@ -1059,7 +1056,7 @@ def cancel(self, request, *args, **kwargs):
10591056
lc_action.error_reason = get_error_reason_choice(
10601057
LearnerCreditRequestActionErrorReasons.FAILED_CANCELLATION
10611058
)
1062-
lc_action.status = get_user_message_choice(SubsidyRequestStates.APPROVED)
1059+
lc_action.status = LearnerCreditRequestUserMessages.APPROVED
10631060
lc_action.traceback = error_msg
10641061
lc_action.save()
10651062
return Response(error_msg, status=status.HTTP_422_UNPROCESSABLE_ENTITY)
@@ -1081,7 +1078,7 @@ def cancel(self, request, *args, **kwargs):
10811078
lc_action.error_reason = get_error_reason_choice(
10821079
LearnerCreditRequestActionErrorReasons.FAILED_CANCELLATION
10831080
)
1084-
lc_action.status = get_user_message_choice(SubsidyRequestStates.APPROVED)
1081+
lc_action.status = LearnerCreditRequestUserMessages.APPROVED
10851082
lc_action.traceback = error_msg
10861083
lc_action.save()
10871084
return Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)
@@ -1102,17 +1099,20 @@ def remind(self, request, *args, **kwargs):
11021099

11031100
action_instance = LearnerCreditRequestActions.create_action(
11041101
learner_credit_request=learner_credit_request,
1105-
recent_action=get_action_choice(LearnerCreditAdditionalActionStates.REMINDED),
1106-
status=get_user_message_choice(LearnerCreditAdditionalActionStates.REMINDED),
1102+
recent_action=LearnerCreditRequestActionTypes.REMINDED,
1103+
status=LearnerCreditRequestUserMessages.REMINDED,
11071104
)
11081105

11091106
try:
11101107
send_reminder_email_for_pending_learner_credit_request.delay(assignment.uuid)
11111108
return Response(status=status.HTTP_200_OK)
11121109
except Exception as exc: # pylint: disable=broad-except
11131110
# Optionally log an errored action here if the task couldn't be queued
1114-
action_instance.status = get_user_message_choice(LearnerCreditRequestActionErrorReasons.EMAIL_ERROR)
1115-
action_instance.error_reason = str(exc)
1111+
action_instance.status = LearnerCreditRequestUserMessages.APPROVED
1112+
action_instance.error_reason = get_error_reason_choice(
1113+
LearnerCreditRequestActionErrorReasons.EMAIL_ERROR
1114+
)
1115+
action_instance.traceback = format_traceback(exc)
11161116
action_instance.save()
11171117
return Response(status=status.HTTP_422_UNPROCESSABLE_ENTITY)
11181118

@@ -1142,19 +1142,19 @@ def decline(self, *args, **kwargs):
11421142
# Create the action instance before attempting the decline operation
11431143
action_instance = LearnerCreditRequestActions.create_action(
11441144
learner_credit_request=learner_credit_request,
1145-
recent_action=get_action_choice(SubsidyRequestStates.DECLINED),
1146-
status=get_user_message_choice(SubsidyRequestStates.DECLINED),
1145+
recent_action=LearnerCreditRequestActionTypes.DECLINED,
1146+
status=LearnerCreditRequestUserMessages.DECLINED,
11471147
)
11481148

11491149
try:
11501150
with transaction.atomic():
11511151
learner_credit_request.decline(self.user)
11521152
except (ValidationError, IntegrityError, DatabaseError) as exc:
1153-
action_instance.status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
1153+
action_instance.status = LearnerCreditRequestActionTypes.REQUESTED
11541154
action_instance.error_reason = get_error_reason_choice(
11551155
LearnerCreditRequestActionErrorReasons.FAILED_DECLINE
11561156
)
1157-
action_instance.traceback = str(exc)
1157+
action_instance.traceback = format_traceback(exc)
11581158
action_instance.save()
11591159

11601160
logger.exception(f"Error declining learner credit request {learner_credit_request_uuid}: {exc}")
@@ -1178,11 +1178,11 @@ def decline(self, *args, **kwargs):
11781178
try:
11791179
unlink_users_from_enterprise_task.delay(enterprise_customer_uuid, [lms_user_id])
11801180
except (ConnectionError, TimeoutError, OSError) as exc:
1181-
action_instance.status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
1181+
action_instance.status = LearnerCreditRequestActionTypes.REQUESTED
11821182
action_instance.error_reason = get_error_reason_choice(
11831183
LearnerCreditRequestActionErrorReasons.FAILED_DECLINE
11841184
)
1185-
action_instance.traceback = str(exc)
1185+
action_instance.traceback = format_traceback(exc)
11861186
action_instance.save()
11871187

11881188
logger.exception(

enterprise_access/apps/subsidy_request/constants.py

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class SubsidyRequestStates:
3535
CHOICES = COMMON_STATES + LC_REQUEST_STATES
3636

3737

38+
# DEPRECATED: This class was used to add 'reminded' to the list of possible
39+
# actions. Its functionality has been consolidated into the self-contained
40+
# `LearnerCreditRequestActionTypes` class.
3841
class LearnerCreditAdditionalActionStates:
3942
""" Additional states specifically for LearnerCreditRequestActions. """
4043

@@ -44,7 +47,8 @@ class LearnerCreditAdditionalActionStates:
4447
)
4548

4649

47-
# Combined choices for LearnerCreditRequestAction model
50+
# DEPRECATED: This variable combined states from multiple classes in a way that
51+
# was confusing and brittle. Use `LearnerCreditRequestActionTypes.CHOICES` instead.
4852
LearnerCreditRequestActionChoices = SubsidyRequestStates.CHOICES + LearnerCreditAdditionalActionStates.CHOICES
4953

5054

@@ -85,20 +89,61 @@ class SubsidyTypeChoices:
8589
SUBSIDY_REQUEST_BULK_OPERATION_BATCH_SIZE = 100
8690

8791

92+
class LearnerCreditRequestActionTypes:
93+
"""
94+
Defines the set of possible values for the `recent_action` field on the
95+
`LearnerCreditRequestActions` model. This represents the specific event
96+
or operation that occurred (e.g., an approval, a reminder).
97+
"""
98+
REQUESTED = 'requested'
99+
APPROVED = 'approved'
100+
DECLINED = 'declined'
101+
ERROR = 'error'
102+
ACCEPTED = 'accepted'
103+
CANCELLED = 'cancelled'
104+
EXPIRED = 'expired'
105+
REVERSED = 'reversed'
106+
REMINDED = 'reminded'
107+
108+
CHOICES = (
109+
(REQUESTED, "Requested"),
110+
(APPROVED, "Approved"),
111+
(DECLINED, "Declined"),
112+
(ERROR, "Error"),
113+
(ACCEPTED, "Accepted"),
114+
(CANCELLED, "Cancelled"),
115+
(EXPIRED, "Expired"),
116+
(REVERSED, "Reversed"),
117+
(REMINDED, "Reminded"),
118+
)
119+
120+
88121
class LearnerCreditRequestUserMessages:
89122
"""
90-
User-facing messages for LearnerCreditRequestActions status field.
91-
Reusing the state keys from SubsidyRequestStates but with different display messages.
123+
Defines the set of possible values for the `status` field on the
124+
`LearnerCreditRequestActions` model. This represents the user-facing
125+
status label that is displayed in the UI as a result of an action.
92126
"""
127+
REQUESTED = 'requested'
128+
REMINDED = 'reminded'
129+
APPROVED = 'approved'
130+
ACCEPTED = 'accepted'
131+
DECLINED = 'declined'
132+
REVERSED = 'reversed'
133+
CANCELLED = 'cancelled'
134+
EXPIRED = 'expired'
135+
ERROR = 'error'
136+
93137
CHOICES = (
94-
(SubsidyRequestStates.REQUESTED, "Requested"),
95-
(LearnerCreditAdditionalActionStates.REMINDED, "Waiting For Learner"),
96-
(SubsidyRequestStates.APPROVED, "Waiting For Learner"),
97-
(SubsidyRequestStates.ACCEPTED, "Redeemed By Learner"),
98-
(SubsidyRequestStates.DECLINED, "Declined"),
99-
(SubsidyRequestStates.REVERSED, "Refunded"),
100-
(SubsidyRequestStates.CANCELLED, "Cancelled"),
101-
(SubsidyRequestStates.EXPIRED, "Expired"),
138+
(REQUESTED, "Requested"),
139+
(REMINDED, "Waiting For Learner"),
140+
(APPROVED, "Waiting For Learner"),
141+
(ACCEPTED, "Redeemed By Learner"),
142+
(DECLINED, "Declined"),
143+
(REVERSED, "Refunded"),
144+
(CANCELLED, "Cancelled"),
145+
(EXPIRED, "Expired"),
146+
(ERROR, "Error"),
102147
)
103148

104149

enterprise_access/apps/subsidy_request/models.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@
2121

2222
from enterprise_access.apps.subsidy_request.constants import (
2323
SUBSIDY_REQUEST_BULK_OPERATION_BATCH_SIZE,
24-
LearnerCreditAdditionalActionStates,
25-
LearnerCreditRequestActionChoices,
2624
LearnerCreditRequestActionErrorReasons,
25+
LearnerCreditRequestActionTypes,
2726
LearnerCreditRequestUserMessages,
2827
SubsidyRequestStates,
2928
SubsidyTypeChoices
@@ -487,19 +486,19 @@ def annotate_dynamic_fields_onto_queryset(cls, queryset):
487486
),
488487
When(
489488
Q(state=SubsidyRequestStates.REQUESTED),
490-
then=Value(SubsidyRequestStates.REQUESTED)
489+
then=Value(LearnerCreditRequestUserMessages.REQUESTED)
491490
),
492491
When(
493492
Q(state=SubsidyRequestStates.DECLINED),
494-
then=Value(SubsidyRequestStates.DECLINED)
493+
then=Value(LearnerCreditRequestUserMessages.DECLINED)
495494
),
496495
When(
497496
Q(state=SubsidyRequestStates.CANCELLED),
498-
then=Value(SubsidyRequestStates.CANCELLED)
497+
then=Value(LearnerCreditRequestUserMessages.CANCELLED)
499498
),
500499
When(
501500
Q(state=SubsidyRequestStates.ERROR),
502-
then=Value(SubsidyRequestStates.ERROR)
501+
then=Value(LearnerCreditRequestUserMessages.ERROR)
503502
),
504503
When(
505504
Q(state=SubsidyRequestStates.APPROVED),
@@ -560,7 +559,7 @@ class LearnerCreditRequestActions(TimeStampedModel):
560559
blank=False,
561560
null=False,
562561
db_index=True,
563-
choices=LearnerCreditRequestActionChoices,
562+
choices=LearnerCreditRequestActionTypes.CHOICES,
564563
help_text="The type of action taken on the learner credit request.",
565564
)
566565

@@ -615,7 +614,7 @@ def create_action(
615614
Args:
616615
learner_credit_request (LearnerCreditRequest): The associated learner credit request.
617616
recent_action (str): The type of action taken (must be a valid choice from
618-
LearnerCreditRequestActionChoices).
617+
LearnerCreditRequestActionTypes).
619618
status (str): The status message (must be a valid choice from LearnerCreditRequestUserMessages.CHOICES).
620619
error_reason (str, optional): The error reason if applicable (must be a valid choice
621620
from LearnerCreditRequestActionErrorReasons.CHOICES).

enterprise_access/apps/subsidy_request/tests/factories.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010

1111
from enterprise_access.apps.content_assignments.tests.factories import LearnerContentAssignmentFactory
1212
from enterprise_access.apps.core.tests.factories import UserFactory
13-
from enterprise_access.apps.subsidy_request.constants import SubsidyRequestStates, SubsidyTypeChoices
13+
from enterprise_access.apps.subsidy_request.constants import (
14+
LearnerCreditRequestActionTypes,
15+
LearnerCreditRequestUserMessages,
16+
SubsidyRequestStates,
17+
SubsidyTypeChoices
18+
)
1419
from enterprise_access.apps.subsidy_request.models import (
1520
CouponCodeRequest,
1621
LearnerCreditRequest,
@@ -105,8 +110,8 @@ class LearnerCreditRequestActionsFactory(factory.django.DjangoModelFactory):
105110
Test factory for the `LearnerCreditRequestActions` model.
106111
"""
107112
uuid = factory.LazyFunction(uuid4)
108-
recent_action = get_action_choice(SubsidyRequestStates.REQUESTED)
109-
status = get_user_message_choice(SubsidyRequestStates.REQUESTED)
113+
recent_action = LearnerCreditRequestActionTypes.REQUESTED
114+
status = LearnerCreditRequestUserMessages.REQUESTED
110115
learner_credit_request = factory.SubFactory(LearnerCreditRequestFactory)
111116
error_reason = None
112117
traceback = None

0 commit comments

Comments
 (0)