Coverage for benefits / core / mixins.py: 96%
46 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-22 19:08 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-22 19:08 +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 return super().dispatch(request, *args, **kwargs)
58 else:
59 logger.warning("Session not configured with enrollment flow")
60 return user_error(request)
63class PageViewMixin:
64 """
65 Mixin sends an analytics event for page views.
66 """
68 def dispatch(self, request, *args, **kwargs):
69 event = analytics.ViewedPageEvent(request)
70 try:
71 analytics.send_event(event)
72 except Exception:
73 logger.warning(f"Failed to send event: {event}")
74 finally:
75 return super().dispatch(request, *args, **kwargs)
78class RecaptchaEnabledMixin:
79 """Mixin intended for use with a class-based view as the first in the MRO.
81 Configures the request with required reCAPTCHA settings."""
83 def dispatch(self, request, *args, **kwargs):
84 if settings.RECAPTCHA_ENABLED:
85 request.recaptcha = {
86 "data_field": recaptcha.DATA_FIELD,
87 "script_api": settings.RECAPTCHA_API_KEY_URL,
88 "site_key": settings.RECAPTCHA_SITE_KEY,
89 }
90 return super().dispatch(request, *args, **kwargs)
93class ValidateRecaptchaMixin:
94 """Mixin intended for use with forms where we need to verify reCAPTCHA.
96 Raises a ValidationError if it fails; otherwise continues the standard form verification.
97 """
99 def clean(self):
100 if not recaptcha.verify(self.data):
101 raise ValidationError("reCAPTCHA failed, please try again.")
102 return super().clean()