We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Track user agent for metrics
**Describe the feature you'd like and what it will do**
In HS v2.5.4, we don't track user_agent in our metrics. This makes it difficult to tell when requests to HS are occurring via direct UI interactions, or via other tools like hsclient.


**Why is this feature important?**
We need more insight into how HS' ecosystem of tools are being used. This information should drive our continued development on existing tools and our consideration of additions for future use.

**Is your feature request related to a problem? Please describe.**
It is difficult to make decisions without information.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `hs_tracking/utils.py`
Content:
```
1 import robot_detection
2 from ipware.ip import get_ip
3 from hs_tools_resource.models import RequestUrlBase, RequestUrlBaseAggregation, RequestUrlBaseFile
4 from urllib.parse import urlparse
5 
6 
7 def get_client_ip(request):
8     return get_ip(request)
9 
10 
11 def get_user_type(session):
12     try:
13         user = session.visitor.user
14         usertype = user.userprofile.user_type
15     except AttributeError:
16         usertype = None
17     return usertype
18 
19 
20 def get_user_email_domain(session):
21     try:
22         user = session.visitor.user
23         emaildomain = user.email.split('@')[-1]
24     except AttributeError:
25         emaildomain = None
26     return emaildomain
27 
28 
29 def get_user_email_tld(session, emaildomain=None):
30     try:
31         if not emaildomain:
32             emaildomain = get_user_email_domain(session)
33         if emaildomain:
34             shortdomain = '.'.join(emaildomain.split('.')[1:])
35             return shortdomain
36     except AttributeError:
37         return None
38 
39 
40 def is_human(user_agent):
41     if robot_detection.is_robot(user_agent):
42         return False
43     return True
44 
45 
46 def get_std_log_fields(request, session=None):
47     """ returns a standard set of metadata that to each receiver function.
48     This ensures that all activities are reporting a consistent set of metrics
49     """
50     user_type = None
51     user_email_tld = None
52     full_domain = None
53     if session is not None:
54         user_type = get_user_type(session)
55         full_domain = get_user_email_domain(session)
56         user_email_tld = get_user_email_tld(session, full_domain)
57 
58     return {
59         'user_ip': get_client_ip(request),
60         'user_type': user_type,
61         'user_email_domain': user_email_tld,
62         'user_email_domain_full': full_domain
63     }
64 
65 
66 def authentic_redirect_url(url):
67     """ Validates a url scheme and netloc is in an existing web app
68     :param url: String of a url
69     :return: Boolean, True if the url exists in a web app
70     """
71     if not url:
72         return False
73     u = urlparse(url)
74     url_base = "{}://{}".format(u.scheme, u.netloc)
75     return RequestUrlBase.objects.filter(value__startswith=url_base).exists() \
76         or RequestUrlBaseAggregation.objects.filter(value__startswith=url_base).exists() \
77         or RequestUrlBaseFile.objects.filter(value__startswith=url_base).exists()
78
```
--- 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/hs_tracking/utils.py b/hs_tracking/utils.py
--- a/hs_tracking/utils.py
+++ b/hs_tracking/utils.py
@@ -47,6 +47,12 @@
     """ returns a standard set of metadata that to each receiver function.
     This ensures that all activities are reporting a consistent set of metrics
     """
+    try:
+        user_agent = request.META['HTTP_USER_AGENT']
+        human = is_human(user_agent)
+    except KeyError:
+        user_agent = None
+        human = None
     user_type = None
     user_email_tld = None
     full_domain = None
@@ -59,7 +65,9 @@
         'user_ip': get_client_ip(request),
         'user_type': user_type,
         'user_email_domain': user_email_tld,
-        'user_email_domain_full': full_domain
+        'user_email_domain_full': full_domain,
+        'is_human': human,
+        'user_agent': user_agent
     }

{"golden_diff": "diff --git a/hs_tracking/utils.py b/hs_tracking/utils.py\n--- a/hs_tracking/utils.py\n+++ b/hs_tracking/utils.py\n@@ -47,6 +47,12 @@\n     \"\"\" returns a standard set of metadata that to each receiver function.\n     This ensures that all activities are reporting a consistent set of metrics\n     \"\"\"\n+    try:\n+        user_agent = request.META['HTTP_USER_AGENT']\n+        human = is_human(user_agent)\n+    except KeyError:\n+        user_agent = None\n+        human = None\n     user_type = None\n     user_email_tld = None\n     full_domain = None\n@@ -59,7 +65,9 @@\n         'user_ip': get_client_ip(request),\n         'user_type': user_type,\n         'user_email_domain': user_email_tld,\n-        'user_email_domain_full': full_domain\n+        'user_email_domain_full': full_domain,\n+        'is_human': human,\n+        'user_agent': user_agent\n     }\n", "issue": "Track user agent for metrics\n**Describe the feature you'd like and what it will do**\r\nIn HS v2.5.4, we don't track user_agent in our metrics. This makes it difficult to tell when requests to HS are occurring via direct UI interactions, or via other tools like hsclient.\r\n\r\n\r\n**Why is this feature important?**\r\nWe need more insight into how HS' ecosystem of tools are being used. This information should drive our continued development on existing tools and our consideration of additions for future use.\r\n\r\n**Is your feature request related to a problem? Please describe.**\r\nIt is difficult to make decisions without information.\r\n\n", "before_files": [{"content": "import robot_detection\nfrom ipware.ip import get_ip\nfrom hs_tools_resource.models import RequestUrlBase, RequestUrlBaseAggregation, RequestUrlBaseFile\nfrom urllib.parse import urlparse\n\n\ndef get_client_ip(request):\n    return get_ip(request)\n\n\ndef get_user_type(session):\n    try:\n        user = session.visitor.user\n        usertype = user.userprofile.user_type\n    except AttributeError:\n        usertype = None\n    return usertype\n\n\ndef get_user_email_domain(session):\n    try:\n        user = session.visitor.user\n        emaildomain = user.email.split('@')[-1]\n    except AttributeError:\n        emaildomain = None\n    return emaildomain\n\n\ndef get_user_email_tld(session, emaildomain=None):\n    try:\n        if not emaildomain:\n            emaildomain = get_user_email_domain(session)\n        if emaildomain:\n            shortdomain = '.'.join(emaildomain.split('.')[1:])\n            return shortdomain\n    except AttributeError:\n        return None\n\n\ndef is_human(user_agent):\n    if robot_detection.is_robot(user_agent):\n        return False\n    return True\n\n\ndef get_std_log_fields(request, session=None):\n    \"\"\" returns a standard set of metadata that to each receiver function.\n    This ensures that all activities are reporting a consistent set of metrics\n    \"\"\"\n    user_type = None\n    user_email_tld = None\n    full_domain = None\n    if session is not None:\n        user_type = get_user_type(session)\n        full_domain = get_user_email_domain(session)\n        user_email_tld = get_user_email_tld(session, full_domain)\n\n    return {\n        'user_ip': get_client_ip(request),\n        'user_type': user_type,\n        'user_email_domain': user_email_tld,\n        'user_email_domain_full': full_domain\n    }\n\n\ndef authentic_redirect_url(url):\n    \"\"\" Validates a url scheme and netloc is in an existing web app\n    :param url: String of a url\n    :return: Boolean, True if the url exists in a web app\n    \"\"\"\n    if not url:\n        return False\n    u = urlparse(url)\n    url_base = \"{}://{}\".format(u.scheme, u.netloc)\n    return RequestUrlBase.objects.filter(value__startswith=url_base).exists() \\\n        or RequestUrlBaseAggregation.objects.filter(value__startswith=url_base).exists() \\\n        or RequestUrlBaseFile.objects.filter(value__startswith=url_base).exists()\n", "path": "hs_tracking/utils.py"}], "after_files": [{"content": "import robot_detection\nfrom ipware.ip import get_ip\nfrom hs_tools_resource.models import RequestUrlBase, RequestUrlBaseAggregation, RequestUrlBaseFile\nfrom urllib.parse import urlparse\n\n\ndef get_client_ip(request):\n    return get_ip(request)\n\n\ndef get_user_type(session):\n    try:\n        user = session.visitor.user\n        usertype = user.userprofile.user_type\n    except AttributeError:\n        usertype = None\n    return usertype\n\n\ndef get_user_email_domain(session):\n    try:\n        user = session.visitor.user\n        emaildomain = user.email.split('@')[-1]\n    except AttributeError:\n        emaildomain = None\n    return emaildomain\n\n\ndef get_user_email_tld(session, emaildomain=None):\n    try:\n        if not emaildomain:\n            emaildomain = get_user_email_domain(session)\n        if emaildomain:\n            shortdomain = '.'.join(emaildomain.split('.')[1:])\n            return shortdomain\n    except AttributeError:\n        return None\n\n\ndef is_human(user_agent):\n    if robot_detection.is_robot(user_agent):\n        return False\n    return True\n\n\ndef get_std_log_fields(request, session=None):\n    \"\"\" returns a standard set of metadata that to each receiver function.\n    This ensures that all activities are reporting a consistent set of metrics\n    \"\"\"\n    try:\n        user_agent = request.META['HTTP_USER_AGENT']\n        human = is_human(user_agent)\n    except KeyError:\n        user_agent = None\n        human = None\n    user_type = None\n    user_email_tld = None\n    full_domain = None\n    if session is not None:\n        user_type = get_user_type(session)\n        full_domain = get_user_email_domain(session)\n        user_email_tld = get_user_email_tld(session, full_domain)\n\n    return {\n        'user_ip': get_client_ip(request),\n        'user_type': user_type,\n        'user_email_domain': user_email_tld,\n        'user_email_domain_full': full_domain,\n        'is_human': human,\n        'user_agent': user_agent\n    }\n\n\ndef authentic_redirect_url(url):\n    \"\"\" Validates a url scheme and netloc is in an existing web app\n    :param url: String of a url\n    :return: Boolean, True if the url exists in a web app\n    \"\"\"\n    if not url:\n        return False\n    u = urlparse(url)\n    url_base = \"{}://{}\".format(u.scheme, u.netloc)\n    return RequestUrlBase.objects.filter(value__startswith=url_base).exists() \\\n        or RequestUrlBaseAggregation.objects.filter(value__startswith=url_base).exists() \\\n        or RequestUrlBaseFile.objects.filter(value__startswith=url_base).exists()\n", "path": "hs_tracking/utils.py"}]}