We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add verifier property to Events
Soon users will have multiple pathways to verify eligibility, and it would be useful to know how many users are taking which pathway(s) in our analytics. We can get ahead of this now by adding an event property with the user's selected verifier (if any).

Let's add this to the base Event definition so that we can later filter any event type by this property, similar to the Transit Agency information attached to each event.

Linking this Amplitude reference again: https://help.amplitude.com/hc/en-us/articles/115002380567-User-properties-and-event-properties

## Acceptance Criteria

<!-- Remember to consider edge cases -->

- [ ] The base Event has an `eligibility_verifier` event property that gets its value from the user's `session.verifier()`
- [ ] The Amplitude Tracking Plan is updated to accept this event property
- [ ] The Amplitude --> Warehouse --> Metabase pipeline is updated to ingest this property

## Additional context

This task was broken out from #342 and is focused on adding the verifier information to _existing_ events. #342 is now focused on introducing _new_ events for some forthcoming UI.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `benefits/core/analytics.py`
Content:
```
1 """
2 The core application: analytics implementation.
3 """
4 import itertools
5 import json
6 import logging
7 import re
8 import time
9 import uuid
10 
11 from django.conf import settings
12 import requests
13 
14 from benefits import VERSION
15 from . import session
16 
17 
18 logger = logging.getLogger(__name__)
19 
20 
21 class Event:
22     """Base analytics event of a given type, including attributes from request's session."""
23 
24     _counter = itertools.count()
25     _domain_re = re.compile(r"^(?:https?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n?]+)", re.IGNORECASE)
26 
27     def __init__(self, request, event_type, **kwargs):
28         self.app_version = VERSION
29         # device_id is generated based on the user_id, and both are set explicitly (per session)
30         self.device_id = session.did(request)
31         self.event_properties = {}
32         self.event_type = str(event_type).lower()
33         self.insert_id = str(uuid.uuid4())
34         self.language = session.language(request)
35         # Amplitude tracks sessions using the start time as the session_id
36         self.session_id = session.start(request)
37         self.time = int(time.time() * 1000)
38         # Although Amplitude advises *against* setting user_id for anonymous users, here a value is set on anonymous
39         # users anyway, as the users never sign-in and become de-anonymized to this app / Amplitude.
40         self.user_id = session.uid(request)
41         self.user_properties = {}
42         self.__dict__.update(kwargs)
43 
44         agency = session.agency(request)
45         agency_name = agency.long_name if agency else None
46 
47         self.update_event_properties(path=request.path, transit_agency=agency_name)
48 
49         uagent = request.headers.get("user-agent")
50 
51         ref = request.headers.get("referer")
52         match = Event._domain_re.match(ref) if ref else None
53         refdom = match.group(1) if match else None
54 
55         self.update_user_properties(referrer=ref, referring_domain=refdom, user_agent=uagent, transit_agency=agency_name)
56 
57         # event is initialized, consume next counter
58         self.event_id = next(Event._counter)
59 
60     def __str__(self):
61         return json.dumps(self.__dict__)
62 
63     def update_event_properties(self, **kwargs):
64         """Merge kwargs into the self.event_properties dict."""
65         self.event_properties.update(kwargs)
66 
67     def update_user_properties(self, **kwargs):
68         """Merge kwargs into the self.user_properties dict."""
69         self.user_properties.update(kwargs)
70 
71 
72 class ViewedPageEvent(Event):
73     """Analytics event representing a single page view."""
74 
75     def __init__(self, request):
76         super().__init__(request, "viewed page")
77 
78 
79 class ChangedLanguageEvent(Event):
80     """Analytics event representing a change in the app's language."""
81 
82     def __init__(self, request, new_lang):
83         super().__init__(request, "changed language")
84         self.update_event_properties(language=new_lang)
85 
86 
87 class Client:
88     """Analytics API client"""
89 
90     def __init__(self, api_key):
91         self.api_key = api_key
92         self.headers = {"Accept": "*/*", "Content-type": "application/json"}
93         self.url = "https://api2.amplitude.com/2/httpapi"
94         logger.debug(f"Initialize Client for {self.url}")
95 
96     def _payload(self, events):
97         if not isinstance(events, list):
98             events = [events]
99         return {"api_key": self.api_key, "events": [e.__dict__ for e in events]}
100 
101     def send(self, event):
102         """Send an analytics event."""
103         if not isinstance(event, Event):
104             raise ValueError("event must be an Event instance")
105 
106         if not self.api_key:
107             logger.warning(f"api_key is not configured, cannot send event: {event}")
108             return
109 
110         try:
111             payload = self._payload(event)
112             logger.debug(f"Sending event payload: {payload}")
113 
114             r = requests.post(self.url, headers=self.headers, json=payload)
115             if r.status_code == 200:
116                 logger.debug(f"Event sent successfully: {r.json()}")
117             elif r.status_code == 400:
118                 logger.error(f"Event request was invalid: {r.json()}")
119             elif r.status_code == 413:
120                 logger.error(f"Event payload was too large: {r.json()}")
121             elif r.status_code == 429:
122                 logger.error(f"Event contained too many requests for some users: {r.json()}")
123             else:
124                 logger.error(f"Failed to send event: {r.json()}")
125 
126         except Exception:
127             logger.error(f"Failed to send event: {event}")
128 
129 
130 client = Client(settings.ANALYTICS_KEY)
131 
132 
133 def send_event(event):
134     """Send an analytics event."""
135     if isinstance(event, Event):
136         client.send(event)
137     else:
138         raise ValueError("event must be an Event instance")
139
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/benefits/core/analytics.py b/benefits/core/analytics.py
--- a/benefits/core/analytics.py
+++ b/benefits/core/analytics.py
@@ -43,8 +43,10 @@
 
         agency = session.agency(request)
         agency_name = agency.long_name if agency else None
+        verifier = session.verifier(request)
+        verifier_name = verifier.name if verifier else None
 
-        self.update_event_properties(path=request.path, transit_agency=agency_name)
+        self.update_event_properties(path=request.path, transit_agency=agency_name, eligibility_verifier=verifier_name)
 
         uagent = request.headers.get("user-agent")
 
@@ -52,7 +54,13 @@
         match = Event._domain_re.match(ref) if ref else None
         refdom = match.group(1) if match else None
 
-        self.update_user_properties(referrer=ref, referring_domain=refdom, user_agent=uagent, transit_agency=agency_name)
+        self.update_user_properties(
+            referrer=ref,
+            referring_domain=refdom,
+            user_agent=uagent,
+            transit_agency=agency_name,
+            eligibility_verifier=verifier_name,
+        )
 
         # event is initialized, consume next counter
         self.event_id = next(Event._counter)

{"golden_diff": "diff --git a/benefits/core/analytics.py b/benefits/core/analytics.py\n--- a/benefits/core/analytics.py\n+++ b/benefits/core/analytics.py\n@@ -43,8 +43,10 @@\n \n         agency = session.agency(request)\n         agency_name = agency.long_name if agency else None\n+        verifier = session.verifier(request)\n+        verifier_name = verifier.name if verifier else None\n \n-        self.update_event_properties(path=request.path, transit_agency=agency_name)\n+        self.update_event_properties(path=request.path, transit_agency=agency_name, eligibility_verifier=verifier_name)\n \n         uagent = request.headers.get(\"user-agent\")\n \n@@ -52,7 +54,13 @@\n         match = Event._domain_re.match(ref) if ref else None\n         refdom = match.group(1) if match else None\n \n-        self.update_user_properties(referrer=ref, referring_domain=refdom, user_agent=uagent, transit_agency=agency_name)\n+        self.update_user_properties(\n+            referrer=ref,\n+            referring_domain=refdom,\n+            user_agent=uagent,\n+            transit_agency=agency_name,\n+            eligibility_verifier=verifier_name,\n+        )\n \n         # event is initialized, consume next counter\n         self.event_id = next(Event._counter)\n", "issue": "Add verifier property to Events\nSoon users will have multiple pathways to verify eligibility, and it would be useful to know how many users are taking which pathway(s) in our analytics. We can get ahead of this now by adding an event property with the user's selected verifier (if any).\r\n\r\nLet's add this to the base Event definition so that we can later filter any event type by this property, similar to the Transit Agency information attached to each event.\r\n\r\nLinking this Amplitude reference again: https://help.amplitude.com/hc/en-us/articles/115002380567-User-properties-and-event-properties\r\n\r\n## Acceptance Criteria\r\n\r\n<!-- Remember to consider edge cases -->\r\n\r\n- [ ] The base Event has an `eligibility_verifier` event property that gets its value from the user's `session.verifier()`\r\n- [ ] The Amplitude Tracking Plan is updated to accept this event property\r\n- [ ] The Amplitude --> Warehouse --> Metabase pipeline is updated to ingest this property\r\n\r\n## Additional context\r\n\r\nThis task was broken out from #342 and is focused on adding the verifier information to _existing_ events. #342 is now focused on introducing _new_ events for some forthcoming UI.\r\n\n", "before_files": [{"content": "\"\"\"\nThe core application: analytics implementation.\n\"\"\"\nimport itertools\nimport json\nimport logging\nimport re\nimport time\nimport uuid\n\nfrom django.conf import settings\nimport requests\n\nfrom benefits import VERSION\nfrom . import session\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass Event:\n    \"\"\"Base analytics event of a given type, including attributes from request's session.\"\"\"\n\n    _counter = itertools.count()\n    _domain_re = re.compile(r\"^(?:https?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n?]+)\", re.IGNORECASE)\n\n    def __init__(self, request, event_type, **kwargs):\n        self.app_version = VERSION\n        # device_id is generated based on the user_id, and both are set explicitly (per session)\n        self.device_id = session.did(request)\n        self.event_properties = {}\n        self.event_type = str(event_type).lower()\n        self.insert_id = str(uuid.uuid4())\n        self.language = session.language(request)\n        # Amplitude tracks sessions using the start time as the session_id\n        self.session_id = session.start(request)\n        self.time = int(time.time() * 1000)\n        # Although Amplitude advises *against* setting user_id for anonymous users, here a value is set on anonymous\n        # users anyway, as the users never sign-in and become de-anonymized to this app / Amplitude.\n        self.user_id = session.uid(request)\n        self.user_properties = {}\n        self.__dict__.update(kwargs)\n\n        agency = session.agency(request)\n        agency_name = agency.long_name if agency else None\n\n        self.update_event_properties(path=request.path, transit_agency=agency_name)\n\n        uagent = request.headers.get(\"user-agent\")\n\n        ref = request.headers.get(\"referer\")\n        match = Event._domain_re.match(ref) if ref else None\n        refdom = match.group(1) if match else None\n\n        self.update_user_properties(referrer=ref, referring_domain=refdom, user_agent=uagent, transit_agency=agency_name)\n\n        # event is initialized, consume next counter\n        self.event_id = next(Event._counter)\n\n    def __str__(self):\n        return json.dumps(self.__dict__)\n\n    def update_event_properties(self, **kwargs):\n        \"\"\"Merge kwargs into the self.event_properties dict.\"\"\"\n        self.event_properties.update(kwargs)\n\n    def update_user_properties(self, **kwargs):\n        \"\"\"Merge kwargs into the self.user_properties dict.\"\"\"\n        self.user_properties.update(kwargs)\n\n\nclass ViewedPageEvent(Event):\n    \"\"\"Analytics event representing a single page view.\"\"\"\n\n    def __init__(self, request):\n        super().__init__(request, \"viewed page\")\n\n\nclass ChangedLanguageEvent(Event):\n    \"\"\"Analytics event representing a change in the app's language.\"\"\"\n\n    def __init__(self, request, new_lang):\n        super().__init__(request, \"changed language\")\n        self.update_event_properties(language=new_lang)\n\n\nclass Client:\n    \"\"\"Analytics API client\"\"\"\n\n    def __init__(self, api_key):\n        self.api_key = api_key\n        self.headers = {\"Accept\": \"*/*\", \"Content-type\": \"application/json\"}\n        self.url = \"https://api2.amplitude.com/2/httpapi\"\n        logger.debug(f\"Initialize Client for {self.url}\")\n\n    def _payload(self, events):\n        if not isinstance(events, list):\n            events = [events]\n        return {\"api_key\": self.api_key, \"events\": [e.__dict__ for e in events]}\n\n    def send(self, event):\n        \"\"\"Send an analytics event.\"\"\"\n        if not isinstance(event, Event):\n            raise ValueError(\"event must be an Event instance\")\n\n        if not self.api_key:\n            logger.warning(f\"api_key is not configured, cannot send event: {event}\")\n            return\n\n        try:\n            payload = self._payload(event)\n            logger.debug(f\"Sending event payload: {payload}\")\n\n            r = requests.post(self.url, headers=self.headers, json=payload)\n            if r.status_code == 200:\n                logger.debug(f\"Event sent successfully: {r.json()}\")\n            elif r.status_code == 400:\n                logger.error(f\"Event request was invalid: {r.json()}\")\n            elif r.status_code == 413:\n                logger.error(f\"Event payload was too large: {r.json()}\")\n            elif r.status_code == 429:\n                logger.error(f\"Event contained too many requests for some users: {r.json()}\")\n            else:\n                logger.error(f\"Failed to send event: {r.json()}\")\n\n        except Exception:\n            logger.error(f\"Failed to send event: {event}\")\n\n\nclient = Client(settings.ANALYTICS_KEY)\n\n\ndef send_event(event):\n    \"\"\"Send an analytics event.\"\"\"\n    if isinstance(event, Event):\n        client.send(event)\n    else:\n        raise ValueError(\"event must be an Event instance\")\n", "path": "benefits/core/analytics.py"}], "after_files": [{"content": "\"\"\"\nThe core application: analytics implementation.\n\"\"\"\nimport itertools\nimport json\nimport logging\nimport re\nimport time\nimport uuid\n\nfrom django.conf import settings\nimport requests\n\nfrom benefits import VERSION\nfrom . import session\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass Event:\n    \"\"\"Base analytics event of a given type, including attributes from request's session.\"\"\"\n\n    _counter = itertools.count()\n    _domain_re = re.compile(r\"^(?:https?:\\/\\/)?(?:[^@\\n]+@)?(?:www\\.)?([^:\\/\\n?]+)\", re.IGNORECASE)\n\n    def __init__(self, request, event_type, **kwargs):\n        self.app_version = VERSION\n        # device_id is generated based on the user_id, and both are set explicitly (per session)\n        self.device_id = session.did(request)\n        self.event_properties = {}\n        self.event_type = str(event_type).lower()\n        self.insert_id = str(uuid.uuid4())\n        self.language = session.language(request)\n        # Amplitude tracks sessions using the start time as the session_id\n        self.session_id = session.start(request)\n        self.time = int(time.time() * 1000)\n        # Although Amplitude advises *against* setting user_id for anonymous users, here a value is set on anonymous\n        # users anyway, as the users never sign-in and become de-anonymized to this app / Amplitude.\n        self.user_id = session.uid(request)\n        self.user_properties = {}\n        self.__dict__.update(kwargs)\n\n        agency = session.agency(request)\n        agency_name = agency.long_name if agency else None\n        verifier = session.verifier(request)\n        verifier_name = verifier.name if verifier else None\n\n        self.update_event_properties(path=request.path, transit_agency=agency_name, eligibility_verifier=verifier_name)\n\n        uagent = request.headers.get(\"user-agent\")\n\n        ref = request.headers.get(\"referer\")\n        match = Event._domain_re.match(ref) if ref else None\n        refdom = match.group(1) if match else None\n\n        self.update_user_properties(\n            referrer=ref,\n            referring_domain=refdom,\n            user_agent=uagent,\n            transit_agency=agency_name,\n            eligibility_verifier=verifier_name,\n        )\n\n        # event is initialized, consume next counter\n        self.event_id = next(Event._counter)\n\n    def __str__(self):\n        return json.dumps(self.__dict__)\n\n    def update_event_properties(self, **kwargs):\n        \"\"\"Merge kwargs into the self.event_properties dict.\"\"\"\n        self.event_properties.update(kwargs)\n\n    def update_user_properties(self, **kwargs):\n        \"\"\"Merge kwargs into the self.user_properties dict.\"\"\"\n        self.user_properties.update(kwargs)\n\n\nclass ViewedPageEvent(Event):\n    \"\"\"Analytics event representing a single page view.\"\"\"\n\n    def __init__(self, request):\n        super().__init__(request, \"viewed page\")\n\n\nclass ChangedLanguageEvent(Event):\n    \"\"\"Analytics event representing a change in the app's language.\"\"\"\n\n    def __init__(self, request, new_lang):\n        super().__init__(request, \"changed language\")\n        self.update_event_properties(language=new_lang)\n\n\nclass Client:\n    \"\"\"Analytics API client\"\"\"\n\n    def __init__(self, api_key):\n        self.api_key = api_key\n        self.headers = {\"Accept\": \"*/*\", \"Content-type\": \"application/json\"}\n        self.url = \"https://api2.amplitude.com/2/httpapi\"\n        logger.debug(f\"Initialize Client for {self.url}\")\n\n    def _payload(self, events):\n        if not isinstance(events, list):\n            events = [events]\n        return {\"api_key\": self.api_key, \"events\": [e.__dict__ for e in events]}\n\n    def send(self, event):\n        \"\"\"Send an analytics event.\"\"\"\n        if not isinstance(event, Event):\n            raise ValueError(\"event must be an Event instance\")\n\n        if not self.api_key:\n            logger.warning(f\"api_key is not configured, cannot send event: {event}\")\n            return\n\n        try:\n            payload = self._payload(event)\n            logger.debug(f\"Sending event payload: {payload}\")\n\n            r = requests.post(self.url, headers=self.headers, json=payload)\n            if r.status_code == 200:\n                logger.debug(f\"Event sent successfully: {r.json()}\")\n            elif r.status_code == 400:\n                logger.error(f\"Event request was invalid: {r.json()}\")\n            elif r.status_code == 413:\n                logger.error(f\"Event payload was too large: {r.json()}\")\n            elif r.status_code == 429:\n                logger.error(f\"Event contained too many requests for some users: {r.json()}\")\n            else:\n                logger.error(f\"Failed to send event: {r.json()}\")\n\n        except Exception:\n            logger.error(f\"Failed to send event: {event}\")\n\n\nclient = Client(settings.ANALYTICS_KEY)\n\n\ndef send_event(event):\n    \"\"\"Send an analytics event.\"\"\"\n    if isinstance(event, Event):\n        client.send(event)\n    else:\n        raise ValueError(\"event must be an Event instance\")\n", "path": "benefits/core/analytics.py"}]}