Coverage for benefits/enrollment/enrollment.py: 95%
46 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-24 15:18 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-24 15:18 +0000
1from datetime import datetime, timedelta
2from enum import Enum
4from django.shortcuts import redirect
5from django.utils import timezone
6import sentry_sdk
8from benefits.routes import routes
9from benefits.core import models, session
10from . import analytics
13class Status(Enum):
14 # SUCCESS means the enrollment went through successfully
15 SUCCESS = 1
17 # SYSTEM_ERROR means the enrollment system encountered an internal error (returned a 500 HTTP status)
18 SYSTEM_ERROR = 2
20 # EXCEPTION means the enrollment system is working, but something unexpected happened
21 # because of a misconfiguration or invalid request from our side
22 EXCEPTION = 3
24 # REENROLLMENT_ERROR means that the user tried to re-enroll but is not within the reenrollment window
25 REENROLLMENT_ERROR = 4
28def _is_expired(expiry_date: datetime):
29 """Returns whether the passed in datetime is expired or not."""
30 return expiry_date <= timezone.now()
33def _is_within_reenrollment_window(expiry_date: datetime, enrollment_reenrollment_date: datetime):
34 """Returns if we are currently within the reenrollment window."""
35 return enrollment_reenrollment_date <= timezone.now() < expiry_date
38def _calculate_expiry(expiration_days: int):
39 """Returns the expiry datetime, which should be midnight in our configured timezone of the (N + 1)th day from now,
40 where N is expiration_days."""
41 default_time_zone = timezone.get_default_timezone()
42 expiry_date = timezone.localtime(timezone=default_time_zone) + timedelta(days=expiration_days + 1)
43 expiry_datetime = expiry_date.replace(hour=0, minute=0, second=0, microsecond=0)
45 return expiry_datetime
48def handle_enrollment_results(
49 request,
50 status: Status,
51 verified_by: str,
52 exception: Exception = None,
53 enrollment_method: str = models.EnrollmentMethods.DIGITAL,
54 route_reenrollment_error=routes.ENROLLMENT_REENROLLMENT_ERROR,
55 route_success=routes.ENROLLMENT_SUCCESS,
56 route_system_error=routes.ENROLLMENT_SYSTEM_ERROR,
57):
58 match (status):
59 case Status.SUCCESS:
60 agency = session.agency(request)
61 flow = session.flow(request)
62 expiry = session.enrollment_expiry(request)
63 oauth_extra_claims = session.oauth_extra_claims(request)
64 # EnrollmentEvent expects a string value for extra_claims
65 if oauth_extra_claims: 65 ↛ 68line 65 didn't jump to line 68 because the condition on line 65 was always true
66 str_extra_claims = ", ".join(oauth_extra_claims)
67 else:
68 str_extra_claims = ""
69 event = models.EnrollmentEvent.objects.create(
70 transit_agency=agency,
71 enrollment_flow=flow,
72 enrollment_method=enrollment_method,
73 verified_by=verified_by,
74 expiration_datetime=expiry,
75 extra_claims=str_extra_claims,
76 )
77 event.save()
78 analytics.returned_success(
79 request, enrollment_group=flow.group_id, enrollment_method=enrollment_method, extra_claims=oauth_extra_claims
80 )
81 return redirect(route_success)
83 case Status.SYSTEM_ERROR:
84 analytics.returned_error(request, str(exception), enrollment_method=enrollment_method)
85 sentry_sdk.capture_exception(exception)
86 return redirect(route_system_error)
88 case Status.EXCEPTION:
89 analytics.returned_error(request, str(exception), enrollment_method=enrollment_method)
90 raise exception
92 case Status.REENROLLMENT_ERROR: 92 ↛ exitline 92 didn't return from function 'handle_enrollment_results' because the pattern on line 92 always matched
93 analytics.returned_error(request, "Re-enrollment error.", enrollment_method=enrollment_method)
94 return redirect(route_reenrollment_error)