Coverage for benefits / core / mixins.py: 97%
48 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-01 15:39 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-01 15:39 +0000
1import logging
3from django.conf import settings
4from django.forms import ValidationError
6from benefits.core import analytics
8from . import recaptcha, session
9from .middleware import user_error
11logger = logging.getLogger(__name__)
14class AgencySessionRequiredMixin:
15 """Mixin intended for use with a class-based view as the first in the MRO.
17 Gets the active `TransitAgency` out of session and sets an attribute on `self`.
19 If the session is not configured with an agency, return a user error.
20 """
22 def dispatch(self, request, *args, **kwargs):
23 if session.active_agency(request):
24 self.agency = session.agency(request)
25 return super().dispatch(request, *args, **kwargs)
26 else:
27 logger.warning("Session not configured with an active agency")
28 return user_error(request)
31class EligibleSessionRequiredMixin:
32 """Mixin intended for use with a class-based view as the first in the MRO.
34 If the session is not marked as eligible (e.g. the user has verified their eligibility), return a user error.
35 """
37 def dispatch(self, request, *args, **kwargs):
38 if session.eligible(request):
39 return super().dispatch(request, *args, **kwargs)
40 else:
41 logger.warning("Session does not have verified eligibility")
42 return user_error(request)
45class FlowSessionRequiredMixin:
46 """Mixin intended for use with a class-based view as the first in the MRO.
48 Gets the current `EnrollmentFlow` out of session and sets an attribute on `self`.
50 If the session is not configured with a flow, return a user error.
51 """
53 def dispatch(self, request, *args, **kwargs):
54 flow = session.flow(request)
55 if flow:
56 self.flow = flow
57 self.agency = session.agency(request)
58 self.group = session.group(request)
59 return super().dispatch(request, *args, **kwargs)
60 else:
61 logger.warning("Session not configured with enrollment flow")
62 return user_error(request)
65class PageViewMixin:
66 """
67 Mixin sends an analytics event for page views.
68 """
70 def dispatch(self, request, *args, **kwargs):
71 event = analytics.ViewedPageEvent(request)
72 try:
73 analytics.send_event(event)
74 except Exception:
75 logger.warning(f"Failed to send event: {event}")
76 finally:
77 return super().dispatch(request, *args, **kwargs)
80class RecaptchaEnabledMixin:
81 """Mixin intended for use with a class-based view as the first in the MRO.
83 Configures the request with required reCAPTCHA settings."""
85 def dispatch(self, request, *args, **kwargs):
86 if settings.RECAPTCHA_ENABLED:
87 request.recaptcha = {
88 "data_field": recaptcha.DATA_FIELD,
89 "script_api": settings.RECAPTCHA_API_KEY_URL,
90 "site_key": settings.RECAPTCHA_SITE_KEY,
91 }
92 return super().dispatch(request, *args, **kwargs)
95class ValidateRecaptchaMixin:
96 """Mixin intended for use with forms where we need to verify reCAPTCHA.
98 Raises a ValidationError if it fails; otherwise continues the standard form verification.
99 """
101 def clean(self):
102 if not recaptcha.verify(self.data):
103 raise ValidationError("reCAPTCHA failed, please try again.")
104 return super().clean()