Coverage for benefits/eligibility/views.py: 95%
101 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 21:44 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-04 21:44 +0000
1"""
2The eligibility application: view definitions for the eligibility verification flow.
3"""
5from django.contrib import messages
6from django.shortcuts import redirect
7from django.urls import reverse
8from django.views.generic import TemplateView, FormView
10from benefits.core.context.flow import SystemName
11from benefits.routes import routes
12from benefits.core import recaptcha, session
13from benefits.core.context.agency import AgencySlug
14from benefits.core.context import formatted_gettext_lazy as _
15from benefits.core.mixins import AgencySessionRequiredMixin, FlowSessionRequiredMixin, RecaptchaEnabledMixin
16from benefits.core.models import EnrollmentFlow
17from . import analytics, forms, verify
20class EligibilityIndex:
21 def __init__(self, form_text):
22 if not isinstance(form_text, list): 22 ↛ 25line 22 didn't jump to line 25 because the condition on line 22 was always true
23 form_text = [form_text]
25 self.form_text = form_text
27 def dict(self):
28 return dict(form_text=self.form_text)
31class IndexView(AgencySessionRequiredMixin, RecaptchaEnabledMixin, FormView):
32 """View handler for the enrollment flow selection form."""
34 template_name = "eligibility/index.html"
35 form_class = forms.EnrollmentFlowSelectionForm
37 def get_form_kwargs(self):
38 """Return the keyword arguments for instantiating the form."""
39 kwargs = super().get_form_kwargs()
40 kwargs["agency"] = self.agency
41 return kwargs
43 def get_context_data(self, **kwargs):
44 """Add agency-specific context data."""
45 context = super().get_context_data(**kwargs)
47 eligiblity_index = {
48 AgencySlug.CST.value: EligibilityIndex(
49 form_text=_(
50 "Cal-ITP doesn’t save any of your information. "
51 "All CST transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
52 )
53 ),
54 AgencySlug.EDCTA.value: EligibilityIndex(
55 form_text=_(
56 "Cal-ITP doesn’t save any of your information. "
57 "All EDCTA transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
58 ),
59 ),
60 AgencySlug.MST.value: EligibilityIndex(
61 form_text=_(
62 "Cal-ITP doesn’t save any of your information. "
63 "All MST transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
64 )
65 ),
66 AgencySlug.NEVCO.value: EligibilityIndex(
67 form_text=_(
68 "Cal-ITP doesn’t save any of your information. "
69 "All Nevada County Connects transit benefits reduce fares "
70 "by 50%% for bus service on fixed routes.".replace("%%", "%")
71 )
72 ),
73 AgencySlug.RABA.value: EligibilityIndex(
74 form_text=_(
75 "Cal-ITP doesn’t save any of your information. "
76 "All RABA transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
77 )
78 ),
79 AgencySlug.SACRT.value: EligibilityIndex(
80 form_text=_(
81 "Cal-ITP doesn’t save any of your information. "
82 "All SacRT transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
83 )
84 ),
85 AgencySlug.SBMTD.value: EligibilityIndex(
86 form_text=_(
87 "Cal-ITP doesn’t save any of your information. "
88 "All SBMTD transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
89 )
90 ),
91 AgencySlug.VCTC.value: EligibilityIndex(
92 form_text=_(
93 "Cal-ITP doesn’t save any of your information. "
94 "All Ventura County Transportation Commission transit benefits "
95 "reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
96 )
97 ),
98 }
100 context.update(eligiblity_index[self.agency.slug].dict())
101 return context
103 def get(self, request, *args, **kwargs):
104 """Initialize session state before handling the request."""
106 session.update(request, eligible=False, origin=self.agency.index_url)
107 session.logout(request)
109 return super().get(request, *args, **kwargs)
111 def form_valid(self, form):
112 """If the form is valid, set enrollment flow and redirect."""
113 flow_id = form.cleaned_data.get("flow")
114 flow = EnrollmentFlow.objects.get(id=flow_id)
115 session.update(self.request, flow=flow)
117 analytics.selected_flow(self.request, flow)
118 return redirect(routes.ELIGIBILITY_START)
120 def form_invalid(self, form):
121 """If the form is invalid, display error messages."""
122 if recaptcha.has_error(form):
123 messages.error(self.request, "Recaptcha failed. Please try again.")
124 return super().form_invalid(form)
127class StartView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, TemplateView):
128 """CBV for the eligibility verification getting started screen."""
130 template_name = "eligibility/start.html"
132 def get(self, request, *args, **kwargs):
133 session.update(request, eligible=False, origin=reverse(routes.ELIGIBILITY_START))
134 return super().get(request, *args, **kwargs)
136 def get_context_data(self, **kwargs):
137 context = super().get_context_data(**kwargs)
138 context.update(self.flow.eligibility_start_context)
139 return context
142class ConfirmView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, RecaptchaEnabledMixin, FormView):
143 """View handler for Eligiblity Confirm form, used only by flows that support Eligibility API verification."""
145 template_name = "eligibility/confirm.html"
147 def get_form_class(self):
148 agency_slug = self.agency.slug
149 flow_system_name = self.flow.system_name
151 if agency_slug == AgencySlug.CST and flow_system_name == SystemName.AGENCY_CARD:
152 form_class = forms.CSTAgencyCard
153 elif agency_slug == AgencySlug.MST and flow_system_name == SystemName.COURTESY_CARD:
154 form_class = forms.MSTCourtesyCard
155 elif agency_slug == AgencySlug.SBMTD and flow_system_name == SystemName.REDUCED_FARE_MOBILITY_ID:
156 form_class = forms.SBMTDMobilityPass
157 else:
158 raise ValueError(
159 f"This agency/flow combination does not support Eligibility API verification: {agency_slug}, {flow_system_name}" # noqa
160 )
162 return form_class
164 def get(self, request, *args, **kwargs):
165 if not session.eligible(request):
166 session.update(request, origin=reverse(routes.ELIGIBILITY_CONFIRM))
167 return super().get(request, *args, **kwargs)
168 else:
169 # an already verified user, no need to verify again
170 return redirect(routes.ENROLLMENT_INDEX)
172 def post(self, request, *args, **kwargs):
173 analytics.started_eligibility(request, self.flow)
174 return super().post(request, *args, **kwargs)
176 def form_valid(self, form):
177 agency = self.agency
178 flow = self.flow
179 request = self.request
181 # make Eligibility Verification request to get the verified confirmation
182 is_verified = verify.eligibility_from_api(flow, form, agency)
184 # Eligibility API returned errors (so eligibility is unknown), allow for correction/resubmission
185 if is_verified is None:
186 analytics.returned_error(request, flow, form.errors)
187 return self.form_invalid(form)
188 # Eligibility API returned that no type was verified
189 elif not is_verified:
190 return redirect(routes.ELIGIBILITY_UNVERIFIED)
191 # Eligibility API returned that type was verified
192 else:
193 session.update(request, eligible=True)
194 analytics.returned_success(request, flow)
196 return redirect(routes.ENROLLMENT_INDEX)
198 def form_invalid(self, form):
199 if recaptcha.has_error(form):
200 messages.error(self.request, "Recaptcha failed. Please try again.")
202 return self.get(self.request)
205class UnverifiedView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, TemplateView):
206 """CBV for the unverified eligibility page."""
208 template_name = "eligibility/unverified.html"
210 def get_context_data(self, **kwargs):
211 context = super().get_context_data(**kwargs)
212 context.update(self.flow.eligibility_unverified_context)
213 return context
215 def get(self, request, *args, **kwargs):
216 analytics.returned_fail(request, self.flow)
217 return super().get(request, *args, **kwargs)