Coverage for benefits/eligibility/views.py: 95%
101 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-12 23:32 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-12 23:32 +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.SACRT.value: EligibilityIndex(
74 form_text=_(
75 "Cal-ITP doesn’t save any of your information. "
76 "All SacRT transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
77 )
78 ),
79 AgencySlug.SBMTD.value: EligibilityIndex(
80 form_text=_(
81 "Cal-ITP doesn’t save any of your information. "
82 "All SBMTD transit benefits reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
83 )
84 ),
85 AgencySlug.VCTC.value: EligibilityIndex(
86 form_text=_(
87 "Cal-ITP doesn’t save any of your information. "
88 "All Ventura County Transportation Commission transit benefits "
89 "reduce fares by 50%% for bus service on fixed routes.".replace("%%", "%")
90 )
91 ),
92 }
94 context.update(eligiblity_index[self.agency.slug].dict())
95 return context
97 def get(self, request, *args, **kwargs):
98 """Initialize session state before handling the request."""
100 session.update(request, eligible=False, origin=self.agency.index_url)
101 session.logout(request)
103 return super().get(request, *args, **kwargs)
105 def form_valid(self, form):
106 """If the form is valid, set enrollment flow and redirect."""
107 flow_id = form.cleaned_data.get("flow")
108 flow = EnrollmentFlow.objects.get(id=flow_id)
109 session.update(self.request, flow=flow)
111 analytics.selected_flow(self.request, flow)
112 return redirect(routes.ELIGIBILITY_START)
114 def form_invalid(self, form):
115 """If the form is invalid, display error messages."""
116 if recaptcha.has_error(form):
117 messages.error(self.request, "Recaptcha failed. Please try again.")
118 return super().form_invalid(form)
121class StartView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, TemplateView):
122 """CBV for the eligibility verification getting started screen."""
124 template_name = "eligibility/start.html"
126 def get(self, request, *args, **kwargs):
127 session.update(request, eligible=False, origin=reverse(routes.ELIGIBILITY_START))
128 return super().get(request, *args, **kwargs)
130 def get_context_data(self, **kwargs):
131 context = super().get_context_data(**kwargs)
132 context.update(self.flow.eligibility_start_context)
133 return context
136class ConfirmView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, RecaptchaEnabledMixin, FormView):
137 """View handler for Eligiblity Confirm form, used only by flows that support Eligibility API verification."""
139 template_name = "eligibility/confirm.html"
141 def get_form_class(self):
142 agency_slug = self.agency.slug
143 flow_system_name = self.flow.system_name
145 if agency_slug == AgencySlug.CST and flow_system_name == SystemName.AGENCY_CARD:
146 form_class = forms.CSTAgencyCard
147 elif agency_slug == AgencySlug.MST and flow_system_name == SystemName.COURTESY_CARD:
148 form_class = forms.MSTCourtesyCard
149 elif agency_slug == AgencySlug.SBMTD and flow_system_name == SystemName.REDUCED_FARE_MOBILITY_ID:
150 form_class = forms.SBMTDMobilityPass
151 else:
152 raise ValueError(
153 f"This agency/flow combination does not support Eligibility API verification: {agency_slug}, {flow_system_name}" # noqa
154 )
156 return form_class
158 def get(self, request, *args, **kwargs):
159 if not session.eligible(request):
160 session.update(request, origin=reverse(routes.ELIGIBILITY_CONFIRM))
161 return super().get(request, *args, **kwargs)
162 else:
163 # an already verified user, no need to verify again
164 return redirect(routes.ENROLLMENT_INDEX)
166 def post(self, request, *args, **kwargs):
167 analytics.started_eligibility(request, self.flow)
168 return super().post(request, *args, **kwargs)
170 def form_valid(self, form):
171 agency = self.agency
172 flow = self.flow
173 request = self.request
175 # make Eligibility Verification request to get the verified confirmation
176 is_verified = verify.eligibility_from_api(flow, form, agency)
178 # Eligibility API returned errors (so eligibility is unknown), allow for correction/resubmission
179 if is_verified is None:
180 analytics.returned_error(request, flow, form.errors)
181 return self.form_invalid(form)
182 # Eligibility API returned that no type was verified
183 elif not is_verified:
184 return redirect(routes.ELIGIBILITY_UNVERIFIED)
185 # Eligibility API returned that type was verified
186 else:
187 session.update(request, eligible=True)
188 analytics.returned_success(request, flow)
190 return redirect(routes.ENROLLMENT_INDEX)
192 def form_invalid(self, form):
193 if recaptcha.has_error(form):
194 messages.error(self.request, "Recaptcha failed. Please try again.")
196 return self.get(self.request)
199class UnverifiedView(AgencySessionRequiredMixin, FlowSessionRequiredMixin, TemplateView):
200 """CBV for the unverified eligibility page."""
202 template_name = "eligibility/unverified.html"
204 def get_context_data(self, **kwargs):
205 context = super().get_context_data(**kwargs)
206 context.update(self.flow.eligibility_unverified_context)
207 return context
209 def get(self, request, *args, **kwargs):
210 analytics.returned_fail(request, self.flow)
211 return super().get(request, *args, **kwargs)