We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove the `annotated_date()` and `annotated_now()` template filters
### Proposed Changes

Remove the following template filters from `utilities.templatetags.helpers`:

- `annotated_date()`
- `annotated_now()`

### Justification

With the shift to using ISO 8601-formatted dates & times everywhere in #15735, these filters are no longer needed.

### Impact

All uses of these filters must be updated. These can generally be replaced with the new `isodatetime()` template filter.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `netbox/utilities/templatetags/helpers.py`
Content:
```
1 import datetime
2 import json
3 from typing import Dict, Any
4 from urllib.parse import quote
5 
6 from django import template
7 from django.conf import settings
8 from django.template.defaultfilters import date
9 from django.urls import NoReverseMatch, reverse
10 from django.utils import timezone
11 from django.utils.safestring import mark_safe
12 
13 from core.models import ObjectType
14 from utilities.forms import get_selected_values, TableConfigForm
15 from utilities.views import get_viewname
16 
17 __all__ = (
18     'annotated_date',
19     'annotated_now',
20     'applied_filters',
21     'as_range',
22     'divide',
23     'get_item',
24     'get_key',
25     'humanize_megabytes',
26     'humanize_speed',
27     'icon_from_status',
28     'kg_to_pounds',
29     'meters_to_feet',
30     'percentage',
31     'querystring',
32     'startswith',
33     'status_from_tag',
34     'table_config_form',
35     'utilization_graph',
36     'validated_viewname',
37     'viewname',
38 )
39 
40 register = template.Library()
41 
42 
43 #
44 # Filters
45 #
46 
47 
48 @register.filter()
49 def viewname(model, action):
50     """
51     Return the view name for the given model and action. Does not perform any validation.
52     """
53     return get_viewname(model, action)
54 
55 
56 @register.filter()
57 def validated_viewname(model, action):
58     """
59     Return the view name for the given model and action if valid, or None if invalid.
60     """
61     viewname = get_viewname(model, action)
62 
63     # Validate the view name
64     try:
65         reverse(viewname)
66         return viewname
67     except NoReverseMatch:
68         return None
69 
70 
71 @register.filter()
72 def humanize_speed(speed):
73     """
74     Humanize speeds given in Kbps. Examples:
75 
76         1544 => "1.544 Mbps"
77         100000 => "100 Mbps"
78         10000000 => "10 Gbps"
79     """
80     if not speed:
81         return ''
82     if speed >= 1000000000 and speed % 1000000000 == 0:
83         return '{} Tbps'.format(int(speed / 1000000000))
84     elif speed >= 1000000 and speed % 1000000 == 0:
85         return '{} Gbps'.format(int(speed / 1000000))
86     elif speed >= 1000 and speed % 1000 == 0:
87         return '{} Mbps'.format(int(speed / 1000))
88     elif speed >= 1000:
89         return '{} Mbps'.format(float(speed) / 1000)
90     else:
91         return '{} Kbps'.format(speed)
92 
93 
94 @register.filter()
95 def humanize_megabytes(mb):
96     """
97     Express a number of megabytes in the most suitable unit (e.g. gigabytes or terabytes).
98     """
99     if not mb:
100         return ''
101     if not mb % 1048576:  # 1024^2
102         return f'{int(mb / 1048576)} TB'
103     if not mb % 1024:
104         return f'{int(mb / 1024)} GB'
105     return f'{mb} MB'
106 
107 
108 @register.filter(expects_localtime=True)
109 def annotated_date(date_value):
110     """
111     Returns date as HTML span with short date format as the content and the
112     (long) date format as the title.
113     """
114     if not date_value:
115         return ''
116 
117     if type(date_value) is datetime.date:
118         long_ts = date(date_value, 'DATE_FORMAT')
119         short_ts = date(date_value, 'SHORT_DATE_FORMAT')
120     else:
121         long_ts = date(date_value, 'DATETIME_FORMAT')
122         short_ts = date(date_value, 'SHORT_DATETIME_FORMAT')
123 
124     return mark_safe(f'<span title="{long_ts}">{short_ts}</span>')
125 
126 
127 @register.simple_tag
128 def annotated_now():
129     """
130     Returns the current date piped through the annotated_date filter.
131     """
132     tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None
133     return annotated_date(datetime.datetime.now(tz=tzinfo))
134 
135 
136 @register.filter()
137 def divide(x, y):
138     """
139     Return x/y (rounded).
140     """
141     if x is None or y is None:
142         return None
143     return round(x / y)
144 
145 
146 @register.filter()
147 def percentage(x, y):
148     """
149     Return x/y as a percentage.
150     """
151     if x is None or y is None:
152         return None
153 
154     return round(x / y * 100, 1)
155 
156 
157 @register.filter()
158 def as_range(n):
159     """
160     Return a range of n items.
161     """
162     try:
163         int(n)
164     except TypeError:
165         return list()
166     return range(n)
167 
168 
169 @register.filter()
170 def meters_to_feet(n):
171     """
172     Convert a length from meters to feet.
173     """
174     return float(n) * 3.28084
175 
176 
177 @register.filter()
178 def kg_to_pounds(n):
179     """
180     Convert a weight from kilograms to pounds.
181     """
182     return float(n) * 2.204623
183 
184 
185 @register.filter("startswith")
186 def startswith(text: str, starts: str) -> bool:
187     """
188     Template implementation of `str.startswith()`.
189     """
190     if isinstance(text, str):
191         return text.startswith(starts)
192     return False
193 
194 
195 @register.filter
196 def get_key(value: Dict, arg: str) -> Any:
197     """
198     Template implementation of `dict.get()`, for accessing dict values
199     by key when the key is not able to be used in a template. For
200     example, `{"ui.colormode": "dark"}`.
201     """
202     return value.get(arg, None)
203 
204 
205 @register.filter
206 def get_item(value: object, attr: str) -> Any:
207     """
208     Template implementation of `__getitem__`, for accessing the `__getitem__` method
209     of a class from a template.
210     """
211     return value[attr]
212 
213 
214 @register.filter
215 def status_from_tag(tag: str = "info") -> str:
216     """
217     Determine Bootstrap theme status/level from Django's Message.level_tag.
218     """
219     status_map = {
220         'warning': 'warning',
221         'success': 'success',
222         'error': 'danger',
223         'danger': 'danger',
224         'debug': 'info',
225         'info': 'info',
226     }
227     return status_map.get(tag.lower(), 'info')
228 
229 
230 @register.filter
231 def icon_from_status(status: str = "info") -> str:
232     """
233     Determine icon class name from Bootstrap theme status/level.
234     """
235     icon_map = {
236         'warning': 'alert',
237         'success': 'check-circle',
238         'danger': 'alert',
239         'info': 'information',
240     }
241     return icon_map.get(status.lower(), 'information')
242 
243 
244 #
245 # Tags
246 #
247 
248 @register.simple_tag()
249 def querystring(request, **kwargs):
250     """
251     Append or update the page number in a querystring.
252     """
253     querydict = request.GET.copy()
254     for k, v in kwargs.items():
255         if v is not None:
256             querydict[k] = str(v)
257         elif k in querydict:
258             querydict.pop(k)
259     querystring = querydict.urlencode(safe='/')
260     if querystring:
261         return '?' + querystring
262     else:
263         return ''
264 
265 
266 @register.inclusion_tag('helpers/utilization_graph.html')
267 def utilization_graph(utilization, warning_threshold=75, danger_threshold=90):
268     """
269     Display a horizontal bar graph indicating a percentage of utilization.
270     """
271     if utilization == 100:
272         bar_class = 'bg-secondary'
273     elif danger_threshold and utilization >= danger_threshold:
274         bar_class = 'bg-danger'
275     elif warning_threshold and utilization >= warning_threshold:
276         bar_class = 'bg-warning'
277     elif warning_threshold or danger_threshold:
278         bar_class = 'bg-success'
279     else:
280         bar_class = 'bg-gray'
281     return {
282         'utilization': utilization,
283         'bar_class': bar_class,
284     }
285 
286 
287 @register.inclusion_tag('helpers/table_config_form.html')
288 def table_config_form(table, table_name=None):
289     return {
290         'table_name': table_name or table.__class__.__name__,
291         'form': TableConfigForm(table=table),
292     }
293 
294 
295 @register.inclusion_tag('helpers/applied_filters.html', takes_context=True)
296 def applied_filters(context, model, form, query_params):
297     """
298     Display the active filters for a given filter form.
299     """
300     user = context['request'].user
301     form.is_valid()  # Ensure cleaned_data has been set
302 
303     applied_filters = []
304     for filter_name in form.changed_data:
305         if filter_name not in form.cleaned_data:
306             continue
307 
308         querydict = query_params.copy()
309         if filter_name not in querydict:
310             continue
311 
312         bound_field = form.fields[filter_name].get_bound_field(form, filter_name)
313         querydict.pop(filter_name)
314         display_value = ', '.join([str(v) for v in get_selected_values(form, filter_name)])
315 
316         applied_filters.append({
317             'name': filter_name,
318             'value': form.cleaned_data[filter_name],
319             'link_url': f'?{querydict.urlencode()}',
320             'link_text': f'{bound_field.label}: {display_value}',
321         })
322 
323     save_link = None
324     if user.has_perm('extras.add_savedfilter') and 'filter_id' not in context['request'].GET:
325         object_type = ObjectType.objects.get_for_model(model).pk
326         parameters = json.dumps(dict(context['request'].GET.lists()))
327         url = reverse('extras:savedfilter_add')
328         save_link = f"{url}?object_types={object_type}&parameters={quote(parameters)}"
329 
330     return {
331         'applied_filters': applied_filters,
332         'save_link': save_link,
333     }
334
```
--- 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/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py
--- a/netbox/utilities/templatetags/helpers.py
+++ b/netbox/utilities/templatetags/helpers.py
@@ -15,8 +15,6 @@
 from utilities.views import get_viewname
 
 __all__ = (
-    'annotated_date',
-    'annotated_now',
     'applied_filters',
     'as_range',
     'divide',
@@ -105,34 +103,6 @@
     return f'{mb} MB'
 
 
-@register.filter(expects_localtime=True)
-def annotated_date(date_value):
-    """
-    Returns date as HTML span with short date format as the content and the
-    (long) date format as the title.
-    """
-    if not date_value:
-        return ''
-
-    if type(date_value) is datetime.date:
-        long_ts = date(date_value, 'DATE_FORMAT')
-        short_ts = date(date_value, 'SHORT_DATE_FORMAT')
-    else:
-        long_ts = date(date_value, 'DATETIME_FORMAT')
-        short_ts = date(date_value, 'SHORT_DATETIME_FORMAT')
-
-    return mark_safe(f'<span title="{long_ts}">{short_ts}</span>')
-
-
-@register.simple_tag
-def annotated_now():
-    """
-    Returns the current date piped through the annotated_date filter.
-    """
-    tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None
-    return annotated_date(datetime.datetime.now(tz=tzinfo))
-
-
 @register.filter()
 def divide(x, y):
     """

{"golden_diff": "diff --git a/netbox/utilities/templatetags/helpers.py b/netbox/utilities/templatetags/helpers.py\n--- a/netbox/utilities/templatetags/helpers.py\n+++ b/netbox/utilities/templatetags/helpers.py\n@@ -15,8 +15,6 @@\n from utilities.views import get_viewname\n \n __all__ = (\n-    'annotated_date',\n-    'annotated_now',\n     'applied_filters',\n     'as_range',\n     'divide',\n@@ -105,34 +103,6 @@\n     return f'{mb} MB'\n \n \n-@register.filter(expects_localtime=True)\n-def annotated_date(date_value):\n-    \"\"\"\n-    Returns date as HTML span with short date format as the content and the\n-    (long) date format as the title.\n-    \"\"\"\n-    if not date_value:\n-        return ''\n-\n-    if type(date_value) is datetime.date:\n-        long_ts = date(date_value, 'DATE_FORMAT')\n-        short_ts = date(date_value, 'SHORT_DATE_FORMAT')\n-    else:\n-        long_ts = date(date_value, 'DATETIME_FORMAT')\n-        short_ts = date(date_value, 'SHORT_DATETIME_FORMAT')\n-\n-    return mark_safe(f'<span title=\"{long_ts}\">{short_ts}</span>')\n-\n-\n-@register.simple_tag\n-def annotated_now():\n-    \"\"\"\n-    Returns the current date piped through the annotated_date filter.\n-    \"\"\"\n-    tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None\n-    return annotated_date(datetime.datetime.now(tz=tzinfo))\n-\n-\n @register.filter()\n def divide(x, y):\n     \"\"\"\n", "issue": "Remove the `annotated_date()` and `annotated_now()` template filters\n### Proposed Changes\n\nRemove the following template filters from `utilities.templatetags.helpers`:\r\n\r\n- `annotated_date()`\r\n- `annotated_now()`\n\n### Justification\n\nWith the shift to using ISO 8601-formatted dates & times everywhere in #15735, these filters are no longer needed.\n\n### Impact\n\nAll uses of these filters must be updated. These can generally be replaced with the new `isodatetime()` template filter.\n", "before_files": [{"content": "import datetime\nimport json\nfrom typing import Dict, Any\nfrom urllib.parse import quote\n\nfrom django import template\nfrom django.conf import settings\nfrom django.template.defaultfilters import date\nfrom django.urls import NoReverseMatch, reverse\nfrom django.utils import timezone\nfrom django.utils.safestring import mark_safe\n\nfrom core.models import ObjectType\nfrom utilities.forms import get_selected_values, TableConfigForm\nfrom utilities.views import get_viewname\n\n__all__ = (\n    'annotated_date',\n    'annotated_now',\n    'applied_filters',\n    'as_range',\n    'divide',\n    'get_item',\n    'get_key',\n    'humanize_megabytes',\n    'humanize_speed',\n    'icon_from_status',\n    'kg_to_pounds',\n    'meters_to_feet',\n    'percentage',\n    'querystring',\n    'startswith',\n    'status_from_tag',\n    'table_config_form',\n    'utilization_graph',\n    'validated_viewname',\n    'viewname',\n)\n\nregister = template.Library()\n\n\n#\n# Filters\n#\n\n\n@register.filter()\ndef viewname(model, action):\n    \"\"\"\n    Return the view name for the given model and action. Does not perform any validation.\n    \"\"\"\n    return get_viewname(model, action)\n\n\n@register.filter()\ndef validated_viewname(model, action):\n    \"\"\"\n    Return the view name for the given model and action if valid, or None if invalid.\n    \"\"\"\n    viewname = get_viewname(model, action)\n\n    # Validate the view name\n    try:\n        reverse(viewname)\n        return viewname\n    except NoReverseMatch:\n        return None\n\n\n@register.filter()\ndef humanize_speed(speed):\n    \"\"\"\n    Humanize speeds given in Kbps. Examples:\n\n        1544 => \"1.544 Mbps\"\n        100000 => \"100 Mbps\"\n        10000000 => \"10 Gbps\"\n    \"\"\"\n    if not speed:\n        return ''\n    if speed >= 1000000000 and speed % 1000000000 == 0:\n        return '{} Tbps'.format(int(speed / 1000000000))\n    elif speed >= 1000000 and speed % 1000000 == 0:\n        return '{} Gbps'.format(int(speed / 1000000))\n    elif speed >= 1000 and speed % 1000 == 0:\n        return '{} Mbps'.format(int(speed / 1000))\n    elif speed >= 1000:\n        return '{} Mbps'.format(float(speed) / 1000)\n    else:\n        return '{} Kbps'.format(speed)\n\n\n@register.filter()\ndef humanize_megabytes(mb):\n    \"\"\"\n    Express a number of megabytes in the most suitable unit (e.g. gigabytes or terabytes).\n    \"\"\"\n    if not mb:\n        return ''\n    if not mb % 1048576:  # 1024^2\n        return f'{int(mb / 1048576)} TB'\n    if not mb % 1024:\n        return f'{int(mb / 1024)} GB'\n    return f'{mb} MB'\n\n\n@register.filter(expects_localtime=True)\ndef annotated_date(date_value):\n    \"\"\"\n    Returns date as HTML span with short date format as the content and the\n    (long) date format as the title.\n    \"\"\"\n    if not date_value:\n        return ''\n\n    if type(date_value) is datetime.date:\n        long_ts = date(date_value, 'DATE_FORMAT')\n        short_ts = date(date_value, 'SHORT_DATE_FORMAT')\n    else:\n        long_ts = date(date_value, 'DATETIME_FORMAT')\n        short_ts = date(date_value, 'SHORT_DATETIME_FORMAT')\n\n    return mark_safe(f'<span title=\"{long_ts}\">{short_ts}</span>')\n\n\n@register.simple_tag\ndef annotated_now():\n    \"\"\"\n    Returns the current date piped through the annotated_date filter.\n    \"\"\"\n    tzinfo = timezone.get_current_timezone() if settings.USE_TZ else None\n    return annotated_date(datetime.datetime.now(tz=tzinfo))\n\n\n@register.filter()\ndef divide(x, y):\n    \"\"\"\n    Return x/y (rounded).\n    \"\"\"\n    if x is None or y is None:\n        return None\n    return round(x / y)\n\n\n@register.filter()\ndef percentage(x, y):\n    \"\"\"\n    Return x/y as a percentage.\n    \"\"\"\n    if x is None or y is None:\n        return None\n\n    return round(x / y * 100, 1)\n\n\n@register.filter()\ndef as_range(n):\n    \"\"\"\n    Return a range of n items.\n    \"\"\"\n    try:\n        int(n)\n    except TypeError:\n        return list()\n    return range(n)\n\n\n@register.filter()\ndef meters_to_feet(n):\n    \"\"\"\n    Convert a length from meters to feet.\n    \"\"\"\n    return float(n) * 3.28084\n\n\n@register.filter()\ndef kg_to_pounds(n):\n    \"\"\"\n    Convert a weight from kilograms to pounds.\n    \"\"\"\n    return float(n) * 2.204623\n\n\n@register.filter(\"startswith\")\ndef startswith(text: str, starts: str) -> bool:\n    \"\"\"\n    Template implementation of `str.startswith()`.\n    \"\"\"\n    if isinstance(text, str):\n        return text.startswith(starts)\n    return False\n\n\n@register.filter\ndef get_key(value: Dict, arg: str) -> Any:\n    \"\"\"\n    Template implementation of `dict.get()`, for accessing dict values\n    by key when the key is not able to be used in a template. For\n    example, `{\"ui.colormode\": \"dark\"}`.\n    \"\"\"\n    return value.get(arg, None)\n\n\n@register.filter\ndef get_item(value: object, attr: str) -> Any:\n    \"\"\"\n    Template implementation of `__getitem__`, for accessing the `__getitem__` method\n    of a class from a template.\n    \"\"\"\n    return value[attr]\n\n\n@register.filter\ndef status_from_tag(tag: str = \"info\") -> str:\n    \"\"\"\n    Determine Bootstrap theme status/level from Django's Message.level_tag.\n    \"\"\"\n    status_map = {\n        'warning': 'warning',\n        'success': 'success',\n        'error': 'danger',\n        'danger': 'danger',\n        'debug': 'info',\n        'info': 'info',\n    }\n    return status_map.get(tag.lower(), 'info')\n\n\n@register.filter\ndef icon_from_status(status: str = \"info\") -> str:\n    \"\"\"\n    Determine icon class name from Bootstrap theme status/level.\n    \"\"\"\n    icon_map = {\n        'warning': 'alert',\n        'success': 'check-circle',\n        'danger': 'alert',\n        'info': 'information',\n    }\n    return icon_map.get(status.lower(), 'information')\n\n\n#\n# Tags\n#\n\n@register.simple_tag()\ndef querystring(request, **kwargs):\n    \"\"\"\n    Append or update the page number in a querystring.\n    \"\"\"\n    querydict = request.GET.copy()\n    for k, v in kwargs.items():\n        if v is not None:\n            querydict[k] = str(v)\n        elif k in querydict:\n            querydict.pop(k)\n    querystring = querydict.urlencode(safe='/')\n    if querystring:\n        return '?' + querystring\n    else:\n        return ''\n\n\n@register.inclusion_tag('helpers/utilization_graph.html')\ndef utilization_graph(utilization, warning_threshold=75, danger_threshold=90):\n    \"\"\"\n    Display a horizontal bar graph indicating a percentage of utilization.\n    \"\"\"\n    if utilization == 100:\n        bar_class = 'bg-secondary'\n    elif danger_threshold and utilization >= danger_threshold:\n        bar_class = 'bg-danger'\n    elif warning_threshold and utilization >= warning_threshold:\n        bar_class = 'bg-warning'\n    elif warning_threshold or danger_threshold:\n        bar_class = 'bg-success'\n    else:\n        bar_class = 'bg-gray'\n    return {\n        'utilization': utilization,\n        'bar_class': bar_class,\n    }\n\n\n@register.inclusion_tag('helpers/table_config_form.html')\ndef table_config_form(table, table_name=None):\n    return {\n        'table_name': table_name or table.__class__.__name__,\n        'form': TableConfigForm(table=table),\n    }\n\n\n@register.inclusion_tag('helpers/applied_filters.html', takes_context=True)\ndef applied_filters(context, model, form, query_params):\n    \"\"\"\n    Display the active filters for a given filter form.\n    \"\"\"\n    user = context['request'].user\n    form.is_valid()  # Ensure cleaned_data has been set\n\n    applied_filters = []\n    for filter_name in form.changed_data:\n        if filter_name not in form.cleaned_data:\n            continue\n\n        querydict = query_params.copy()\n        if filter_name not in querydict:\n            continue\n\n        bound_field = form.fields[filter_name].get_bound_field(form, filter_name)\n        querydict.pop(filter_name)\n        display_value = ', '.join([str(v) for v in get_selected_values(form, filter_name)])\n\n        applied_filters.append({\n            'name': filter_name,\n            'value': form.cleaned_data[filter_name],\n            'link_url': f'?{querydict.urlencode()}',\n            'link_text': f'{bound_field.label}: {display_value}',\n        })\n\n    save_link = None\n    if user.has_perm('extras.add_savedfilter') and 'filter_id' not in context['request'].GET:\n        object_type = ObjectType.objects.get_for_model(model).pk\n        parameters = json.dumps(dict(context['request'].GET.lists()))\n        url = reverse('extras:savedfilter_add')\n        save_link = f\"{url}?object_types={object_type}&parameters={quote(parameters)}\"\n\n    return {\n        'applied_filters': applied_filters,\n        'save_link': save_link,\n    }\n", "path": "netbox/utilities/templatetags/helpers.py"}], "after_files": [{"content": "import datetime\nimport json\nfrom typing import Dict, Any\nfrom urllib.parse import quote\n\nfrom django import template\nfrom django.conf import settings\nfrom django.template.defaultfilters import date\nfrom django.urls import NoReverseMatch, reverse\nfrom django.utils import timezone\nfrom django.utils.safestring import mark_safe\n\nfrom core.models import ObjectType\nfrom utilities.forms import get_selected_values, TableConfigForm\nfrom utilities.views import get_viewname\n\n__all__ = (\n    'applied_filters',\n    'as_range',\n    'divide',\n    'get_item',\n    'get_key',\n    'humanize_megabytes',\n    'humanize_speed',\n    'icon_from_status',\n    'kg_to_pounds',\n    'meters_to_feet',\n    'percentage',\n    'querystring',\n    'startswith',\n    'status_from_tag',\n    'table_config_form',\n    'utilization_graph',\n    'validated_viewname',\n    'viewname',\n)\n\nregister = template.Library()\n\n\n#\n# Filters\n#\n\n\n@register.filter()\ndef viewname(model, action):\n    \"\"\"\n    Return the view name for the given model and action. Does not perform any validation.\n    \"\"\"\n    return get_viewname(model, action)\n\n\n@register.filter()\ndef validated_viewname(model, action):\n    \"\"\"\n    Return the view name for the given model and action if valid, or None if invalid.\n    \"\"\"\n    viewname = get_viewname(model, action)\n\n    # Validate the view name\n    try:\n        reverse(viewname)\n        return viewname\n    except NoReverseMatch:\n        return None\n\n\n@register.filter()\ndef humanize_speed(speed):\n    \"\"\"\n    Humanize speeds given in Kbps. Examples:\n\n        1544 => \"1.544 Mbps\"\n        100000 => \"100 Mbps\"\n        10000000 => \"10 Gbps\"\n    \"\"\"\n    if not speed:\n        return ''\n    if speed >= 1000000000 and speed % 1000000000 == 0:\n        return '{} Tbps'.format(int(speed / 1000000000))\n    elif speed >= 1000000 and speed % 1000000 == 0:\n        return '{} Gbps'.format(int(speed / 1000000))\n    elif speed >= 1000 and speed % 1000 == 0:\n        return '{} Mbps'.format(int(speed / 1000))\n    elif speed >= 1000:\n        return '{} Mbps'.format(float(speed) / 1000)\n    else:\n        return '{} Kbps'.format(speed)\n\n\n@register.filter()\ndef humanize_megabytes(mb):\n    \"\"\"\n    Express a number of megabytes in the most suitable unit (e.g. gigabytes or terabytes).\n    \"\"\"\n    if not mb:\n        return ''\n    if not mb % 1048576:  # 1024^2\n        return f'{int(mb / 1048576)} TB'\n    if not mb % 1024:\n        return f'{int(mb / 1024)} GB'\n    return f'{mb} MB'\n\n\n@register.filter()\ndef divide(x, y):\n    \"\"\"\n    Return x/y (rounded).\n    \"\"\"\n    if x is None or y is None:\n        return None\n    return round(x / y)\n\n\n@register.filter()\ndef percentage(x, y):\n    \"\"\"\n    Return x/y as a percentage.\n    \"\"\"\n    if x is None or y is None:\n        return None\n\n    return round(x / y * 100, 1)\n\n\n@register.filter()\ndef as_range(n):\n    \"\"\"\n    Return a range of n items.\n    \"\"\"\n    try:\n        int(n)\n    except TypeError:\n        return list()\n    return range(n)\n\n\n@register.filter()\ndef meters_to_feet(n):\n    \"\"\"\n    Convert a length from meters to feet.\n    \"\"\"\n    return float(n) * 3.28084\n\n\n@register.filter()\ndef kg_to_pounds(n):\n    \"\"\"\n    Convert a weight from kilograms to pounds.\n    \"\"\"\n    return float(n) * 2.204623\n\n\n@register.filter(\"startswith\")\ndef startswith(text: str, starts: str) -> bool:\n    \"\"\"\n    Template implementation of `str.startswith()`.\n    \"\"\"\n    if isinstance(text, str):\n        return text.startswith(starts)\n    return False\n\n\n@register.filter\ndef get_key(value: Dict, arg: str) -> Any:\n    \"\"\"\n    Template implementation of `dict.get()`, for accessing dict values\n    by key when the key is not able to be used in a template. For\n    example, `{\"ui.colormode\": \"dark\"}`.\n    \"\"\"\n    return value.get(arg, None)\n\n\n@register.filter\ndef get_item(value: object, attr: str) -> Any:\n    \"\"\"\n    Template implementation of `__getitem__`, for accessing the `__getitem__` method\n    of a class from a template.\n    \"\"\"\n    return value[attr]\n\n\n@register.filter\ndef status_from_tag(tag: str = \"info\") -> str:\n    \"\"\"\n    Determine Bootstrap theme status/level from Django's Message.level_tag.\n    \"\"\"\n    status_map = {\n        'warning': 'warning',\n        'success': 'success',\n        'error': 'danger',\n        'danger': 'danger',\n        'debug': 'info',\n        'info': 'info',\n    }\n    return status_map.get(tag.lower(), 'info')\n\n\n@register.filter\ndef icon_from_status(status: str = \"info\") -> str:\n    \"\"\"\n    Determine icon class name from Bootstrap theme status/level.\n    \"\"\"\n    icon_map = {\n        'warning': 'alert',\n        'success': 'check-circle',\n        'danger': 'alert',\n        'info': 'information',\n    }\n    return icon_map.get(status.lower(), 'information')\n\n\n#\n# Tags\n#\n\n@register.simple_tag()\ndef querystring(request, **kwargs):\n    \"\"\"\n    Append or update the page number in a querystring.\n    \"\"\"\n    querydict = request.GET.copy()\n    for k, v in kwargs.items():\n        if v is not None:\n            querydict[k] = str(v)\n        elif k in querydict:\n            querydict.pop(k)\n    querystring = querydict.urlencode(safe='/')\n    if querystring:\n        return '?' + querystring\n    else:\n        return ''\n\n\n@register.inclusion_tag('helpers/utilization_graph.html')\ndef utilization_graph(utilization, warning_threshold=75, danger_threshold=90):\n    \"\"\"\n    Display a horizontal bar graph indicating a percentage of utilization.\n    \"\"\"\n    if utilization == 100:\n        bar_class = 'bg-secondary'\n    elif danger_threshold and utilization >= danger_threshold:\n        bar_class = 'bg-danger'\n    elif warning_threshold and utilization >= warning_threshold:\n        bar_class = 'bg-warning'\n    elif warning_threshold or danger_threshold:\n        bar_class = 'bg-success'\n    else:\n        bar_class = 'bg-gray'\n    return {\n        'utilization': utilization,\n        'bar_class': bar_class,\n    }\n\n\n@register.inclusion_tag('helpers/table_config_form.html')\ndef table_config_form(table, table_name=None):\n    return {\n        'table_name': table_name or table.__class__.__name__,\n        'form': TableConfigForm(table=table),\n    }\n\n\n@register.inclusion_tag('helpers/applied_filters.html', takes_context=True)\ndef applied_filters(context, model, form, query_params):\n    \"\"\"\n    Display the active filters for a given filter form.\n    \"\"\"\n    user = context['request'].user\n    form.is_valid()  # Ensure cleaned_data has been set\n\n    applied_filters = []\n    for filter_name in form.changed_data:\n        if filter_name not in form.cleaned_data:\n            continue\n\n        querydict = query_params.copy()\n        if filter_name not in querydict:\n            continue\n\n        bound_field = form.fields[filter_name].get_bound_field(form, filter_name)\n        querydict.pop(filter_name)\n        display_value = ', '.join([str(v) for v in get_selected_values(form, filter_name)])\n\n        applied_filters.append({\n            'name': filter_name,\n            'value': form.cleaned_data[filter_name],\n            'link_url': f'?{querydict.urlencode()}',\n            'link_text': f'{bound_field.label}: {display_value}',\n        })\n\n    save_link = None\n    if user.has_perm('extras.add_savedfilter') and 'filter_id' not in context['request'].GET:\n        object_type = ObjectType.objects.get_for_model(model).pk\n        parameters = json.dumps(dict(context['request'].GET.lists()))\n        url = reverse('extras:savedfilter_add')\n        save_link = f\"{url}?object_types={object_type}&parameters={quote(parameters)}\"\n\n    return {\n        'applied_filters': applied_filters,\n        'save_link': save_link,\n    }\n", "path": "netbox/utilities/templatetags/helpers.py"}]}