We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
URL's from Events API are not always full URL's
### Describe the bug
The `google_maps_url` from the Events API is not always a full URL.

### How to reproduce
Steps to reproduce the behaviour:
1. On staging, go to `https://staging.thalia.nu/api/v1/events/147/` and see that the `google_maps_url` is not a full URL but a relative one.

### Expected behaviour
The `google_maps_url` should always be a full URL.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `website/utils/snippets.py`
Content:
```
1 """Provides various utilities that are useful across the project."""
2 import datetime
3 import hmac
4 from base64 import urlsafe_b64decode, urlsafe_b64encode
5 from collections import namedtuple
6 from _sha1 import sha1
7 
8 from django.conf import settings
9 from django.template.defaultfilters import urlencode
10 from django.templatetags.static import static
11 from django.utils import timezone, dateparse
12 from rest_framework.exceptions import ParseError
13 
14 
15 def dict2obj(d, name="Object"):
16     return namedtuple(name, d.keys())(*d.values())
17 
18 
19 def datetime_to_lectureyear(date):
20     """Convert a :class:`~datetime.date` to the start of the lectureyear.
21 
22     >>> from datetime import date, datetime, timezone
23     >>> nov_23 = date(1990, 11, 7)
24     >>> datetime_to_lectureyear(nov_23)
25     1990
26     >>> mar_2 = date(1993, 3, 2)
27     >>> datetime_to_lectureyear(mar_2)
28     1992
29 
30     Also works on :class:`~datetime.datetime`, but they need to be tz-aware:
31 
32     >>> new_year = datetime(2000, 1, 1, tzinfo=timezone.utc)
33     >>> datetime_to_lectureyear(new_year)
34     1999
35     """
36     if isinstance(date, timezone.datetime):
37         date = timezone.localtime(date).date()
38     sept_1 = timezone.make_aware(timezone.datetime(date.year, 9, 1))
39     if date < sept_1.date():
40         return date.year - 1
41     return date.year
42 
43 
44 def create_google_maps_url(location, zoom, size):
45     if location.lower().strip() == "online":
46         return static("img/locations/online.png")
47     if location.lower().strip() == "discord":
48         return static("img/locations/discord.png")
49 
50     maps_url = (
51         f"/maps/api/staticmap?"
52         f"center={ urlencode(location) }&"
53         f"zoom={ zoom }&size={ size }&"
54         f"markers={ urlencode(location) }&"
55         f"key={ settings.GOOGLE_MAPS_API_KEY }"
56     )
57 
58     decoded_key = urlsafe_b64decode(settings.GOOGLE_MAPS_API_SECRET)
59 
60     signature = hmac.new(decoded_key, maps_url.encode(), sha1)
61 
62     encoded_signature = urlsafe_b64encode(signature.digest())
63 
64     maps_url += f"&signature={encoded_signature.decode('utf-8')}"
65 
66     return "https://maps.googleapis.com" + maps_url
67 
68 
69 def _extract_date(param):
70     """Extract the date from an arbitrary string."""
71     if param is None:
72         return None
73     try:
74         return dateparse.parse_datetime(param)
75     except ValueError:
76         return dateparse.parse_date(param)
77 
78 
79 def extract_date_range(request, allow_empty=False):
80     """Extract a date range from an arbitrary string."""
81     default_value = None
82 
83     start = request.query_params.get("start", default_value)
84     if start or not allow_empty:
85         try:
86             start = dateparse.parse_datetime(start)
87             if not timezone.is_aware(start):
88                 start = timezone.make_aware(start)
89         except (ValueError, AttributeError, TypeError) as e:
90             raise ParseError(detail="start query parameter invalid") from e
91 
92     end = request.query_params.get("end", default_value)
93     if end or not allow_empty:
94         try:
95             end = dateparse.parse_datetime(end)
96             if not timezone.is_aware(end):
97                 end = timezone.make_aware(end)
98         except (ValueError, AttributeError, TypeError) as e:
99             raise ParseError(detail="end query parameter invalid") from e
100 
101     return start, end
102 
103 
104 def overlaps(check, others, can_equal=True):
105     """Check for overlapping date ranges.
106 
107     This works by checking the maximum of the two `since` times, and the minimum of
108     the two `until` times. Because there are no infinite dates, the value date_max
109     is created for when the `until` value is None; this signifies a timespan that
110     has not ended yet and is the maximum possible date in Python's datetime.
111 
112     The ranges overlap when the maximum start time is smaller than the minimum
113     end time, as can be seen in this example of two integer ranges:
114 
115     check: . . . .[4]. . . . 9
116     other: . . 2 . .[5]. . . .
117 
118     check: . . . .[4]. . . . 9
119     other: . . 2 . . . . . . . [date_max]
120 
121     And when non overlapping:
122     check: . . . . . .[6] . . 9
123     other: . . 2 . .[5]. . . .
124 
125     4 < 5 == True so these intervals overlap, while 6 < 5 == False so these intervals
126     don't overlap
127 
128     The can_equal argument is used for boards, where the end date can't be the same
129     as the start date.
130 
131     >>> overlaps( \
132     dict2obj({ \
133         'pk': 1 \
134         , 'since': datetime.date(2018, 12, 1) \
135         , 'until': datetime.date(2019, 1, 1) \
136     }) \
137     , [dict2obj({ \
138     'pk': 2 \
139     , 'since': datetime.date(2019, 1, 1) \
140     , 'until': datetime.date(2019, 1, 31) \
141     })])
142     False
143 
144     >>> overlaps( \
145     dict2obj({ \
146         'pk': 1 \
147         , 'since': datetime.date(2018, 12, 1) \
148         , 'until': datetime.date(2019, 1, 1) \
149     }) \
150     , [dict2obj({ \
151     'pk': 2 \
152     , 'since': datetime.date(2019, 1, 1) \
153     , 'until': datetime.date(2019, 1, 31) \
154     })], False)
155     True
156 
157     >>> overlaps( \
158     dict2obj({ \
159         'pk': 1 \
160         , 'since': datetime.date(2018, 12, 1) \
161         , 'until': datetime.date(2019, 1, 2) \
162     }) \
163     , [dict2obj({ \
164     'pk': 2 \
165     , 'since': datetime.date(2019, 1, 1) \
166     , 'until': datetime.date(2019, 1, 31) \
167     })])
168     True
169     """
170     date_max = datetime.date(datetime.MAXYEAR, 12, 31)
171     for other in others:
172         if check.pk == other.pk:
173             # No checks for the object we're validating
174             continue
175 
176         max_start = max(check.since, other.since)
177         min_end = min(check.until or date_max, other.until or date_max)
178 
179         if max_start == min_end and not can_equal:
180             return True
181         if max_start < min_end:
182             return True
183 
184     return False
185
```
--- 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/website/utils/snippets.py b/website/utils/snippets.py
--- a/website/utils/snippets.py
+++ b/website/utils/snippets.py
@@ -43,9 +43,9 @@
 
 def create_google_maps_url(location, zoom, size):
     if location.lower().strip() == "online":
-        return static("img/locations/online.png")
+        return settings.BASE_URL + static("img/locations/online.png")
     if location.lower().strip() == "discord":
-        return static("img/locations/discord.png")
+        return settings.BASE_URL + static("img/locations/discord.png")
 
     maps_url = (
         f"/maps/api/staticmap?"

{"golden_diff": "diff --git a/website/utils/snippets.py b/website/utils/snippets.py\n--- a/website/utils/snippets.py\n+++ b/website/utils/snippets.py\n@@ -43,9 +43,9 @@\n \n def create_google_maps_url(location, zoom, size):\n     if location.lower().strip() == \"online\":\n-        return static(\"img/locations/online.png\")\n+        return settings.BASE_URL + static(\"img/locations/online.png\")\n     if location.lower().strip() == \"discord\":\n-        return static(\"img/locations/discord.png\")\n+        return settings.BASE_URL + static(\"img/locations/discord.png\")\n \n     maps_url = (\n         f\"/maps/api/staticmap?\"\n", "issue": "URL's from Events API are not always full URL's\n### Describe the bug\r\nThe `google_maps_url` from the Events API is not always a full URL.\r\n\r\n### How to reproduce\r\nSteps to reproduce the behaviour:\r\n1. On staging, go to `https://staging.thalia.nu/api/v1/events/147/` and see that the `google_maps_url` is not a full URL but a relative one.\r\n\r\n### Expected behaviour\r\nThe `google_maps_url` should always be a full URL.\r\n\r\n\n", "before_files": [{"content": "\"\"\"Provides various utilities that are useful across the project.\"\"\"\nimport datetime\nimport hmac\nfrom base64 import urlsafe_b64decode, urlsafe_b64encode\nfrom collections import namedtuple\nfrom _sha1 import sha1\n\nfrom django.conf import settings\nfrom django.template.defaultfilters import urlencode\nfrom django.templatetags.static import static\nfrom django.utils import timezone, dateparse\nfrom rest_framework.exceptions import ParseError\n\n\ndef dict2obj(d, name=\"Object\"):\n    return namedtuple(name, d.keys())(*d.values())\n\n\ndef datetime_to_lectureyear(date):\n    \"\"\"Convert a :class:`~datetime.date` to the start of the lectureyear.\n\n    >>> from datetime import date, datetime, timezone\n    >>> nov_23 = date(1990, 11, 7)\n    >>> datetime_to_lectureyear(nov_23)\n    1990\n    >>> mar_2 = date(1993, 3, 2)\n    >>> datetime_to_lectureyear(mar_2)\n    1992\n\n    Also works on :class:`~datetime.datetime`, but they need to be tz-aware:\n\n    >>> new_year = datetime(2000, 1, 1, tzinfo=timezone.utc)\n    >>> datetime_to_lectureyear(new_year)\n    1999\n    \"\"\"\n    if isinstance(date, timezone.datetime):\n        date = timezone.localtime(date).date()\n    sept_1 = timezone.make_aware(timezone.datetime(date.year, 9, 1))\n    if date < sept_1.date():\n        return date.year - 1\n    return date.year\n\n\ndef create_google_maps_url(location, zoom, size):\n    if location.lower().strip() == \"online\":\n        return static(\"img/locations/online.png\")\n    if location.lower().strip() == \"discord\":\n        return static(\"img/locations/discord.png\")\n\n    maps_url = (\n        f\"/maps/api/staticmap?\"\n        f\"center={ urlencode(location) }&\"\n        f\"zoom={ zoom }&size={ size }&\"\n        f\"markers={ urlencode(location) }&\"\n        f\"key={ settings.GOOGLE_MAPS_API_KEY }\"\n    )\n\n    decoded_key = urlsafe_b64decode(settings.GOOGLE_MAPS_API_SECRET)\n\n    signature = hmac.new(decoded_key, maps_url.encode(), sha1)\n\n    encoded_signature = urlsafe_b64encode(signature.digest())\n\n    maps_url += f\"&signature={encoded_signature.decode('utf-8')}\"\n\n    return \"https://maps.googleapis.com\" + maps_url\n\n\ndef _extract_date(param):\n    \"\"\"Extract the date from an arbitrary string.\"\"\"\n    if param is None:\n        return None\n    try:\n        return dateparse.parse_datetime(param)\n    except ValueError:\n        return dateparse.parse_date(param)\n\n\ndef extract_date_range(request, allow_empty=False):\n    \"\"\"Extract a date range from an arbitrary string.\"\"\"\n    default_value = None\n\n    start = request.query_params.get(\"start\", default_value)\n    if start or not allow_empty:\n        try:\n            start = dateparse.parse_datetime(start)\n            if not timezone.is_aware(start):\n                start = timezone.make_aware(start)\n        except (ValueError, AttributeError, TypeError) as e:\n            raise ParseError(detail=\"start query parameter invalid\") from e\n\n    end = request.query_params.get(\"end\", default_value)\n    if end or not allow_empty:\n        try:\n            end = dateparse.parse_datetime(end)\n            if not timezone.is_aware(end):\n                end = timezone.make_aware(end)\n        except (ValueError, AttributeError, TypeError) as e:\n            raise ParseError(detail=\"end query parameter invalid\") from e\n\n    return start, end\n\n\ndef overlaps(check, others, can_equal=True):\n    \"\"\"Check for overlapping date ranges.\n\n    This works by checking the maximum of the two `since` times, and the minimum of\n    the two `until` times. Because there are no infinite dates, the value date_max\n    is created for when the `until` value is None; this signifies a timespan that\n    has not ended yet and is the maximum possible date in Python's datetime.\n\n    The ranges overlap when the maximum start time is smaller than the minimum\n    end time, as can be seen in this example of two integer ranges:\n\n    check: . . . .[4]. . . . 9\n    other: . . 2 . .[5]. . . .\n\n    check: . . . .[4]. . . . 9\n    other: . . 2 . . . . . . . [date_max]\n\n    And when non overlapping:\n    check: . . . . . .[6] . . 9\n    other: . . 2 . .[5]. . . .\n\n    4 < 5 == True so these intervals overlap, while 6 < 5 == False so these intervals\n    don't overlap\n\n    The can_equal argument is used for boards, where the end date can't be the same\n    as the start date.\n\n    >>> overlaps( \\\n    dict2obj({ \\\n        'pk': 1 \\\n        , 'since': datetime.date(2018, 12, 1) \\\n        , 'until': datetime.date(2019, 1, 1) \\\n    }) \\\n    , [dict2obj({ \\\n    'pk': 2 \\\n    , 'since': datetime.date(2019, 1, 1) \\\n    , 'until': datetime.date(2019, 1, 31) \\\n    })])\n    False\n\n    >>> overlaps( \\\n    dict2obj({ \\\n        'pk': 1 \\\n        , 'since': datetime.date(2018, 12, 1) \\\n        , 'until': datetime.date(2019, 1, 1) \\\n    }) \\\n    , [dict2obj({ \\\n    'pk': 2 \\\n    , 'since': datetime.date(2019, 1, 1) \\\n    , 'until': datetime.date(2019, 1, 31) \\\n    })], False)\n    True\n\n    >>> overlaps( \\\n    dict2obj({ \\\n        'pk': 1 \\\n        , 'since': datetime.date(2018, 12, 1) \\\n        , 'until': datetime.date(2019, 1, 2) \\\n    }) \\\n    , [dict2obj({ \\\n    'pk': 2 \\\n    , 'since': datetime.date(2019, 1, 1) \\\n    , 'until': datetime.date(2019, 1, 31) \\\n    })])\n    True\n    \"\"\"\n    date_max = datetime.date(datetime.MAXYEAR, 12, 31)\n    for other in others:\n        if check.pk == other.pk:\n            # No checks for the object we're validating\n            continue\n\n        max_start = max(check.since, other.since)\n        min_end = min(check.until or date_max, other.until or date_max)\n\n        if max_start == min_end and not can_equal:\n            return True\n        if max_start < min_end:\n            return True\n\n    return False\n", "path": "website/utils/snippets.py"}], "after_files": [{"content": "\"\"\"Provides various utilities that are useful across the project.\"\"\"\nimport datetime\nimport hmac\nfrom base64 import urlsafe_b64decode, urlsafe_b64encode\nfrom collections import namedtuple\nfrom _sha1 import sha1\n\nfrom django.conf import settings\nfrom django.template.defaultfilters import urlencode\nfrom django.templatetags.static import static\nfrom django.utils import timezone, dateparse\nfrom rest_framework.exceptions import ParseError\n\n\ndef dict2obj(d, name=\"Object\"):\n    return namedtuple(name, d.keys())(*d.values())\n\n\ndef datetime_to_lectureyear(date):\n    \"\"\"Convert a :class:`~datetime.date` to the start of the lectureyear.\n\n    >>> from datetime import date, datetime, timezone\n    >>> nov_23 = date(1990, 11, 7)\n    >>> datetime_to_lectureyear(nov_23)\n    1990\n    >>> mar_2 = date(1993, 3, 2)\n    >>> datetime_to_lectureyear(mar_2)\n    1992\n\n    Also works on :class:`~datetime.datetime`, but they need to be tz-aware:\n\n    >>> new_year = datetime(2000, 1, 1, tzinfo=timezone.utc)\n    >>> datetime_to_lectureyear(new_year)\n    1999\n    \"\"\"\n    if isinstance(date, timezone.datetime):\n        date = timezone.localtime(date).date()\n    sept_1 = timezone.make_aware(timezone.datetime(date.year, 9, 1))\n    if date < sept_1.date():\n        return date.year - 1\n    return date.year\n\n\ndef create_google_maps_url(location, zoom, size):\n    if location.lower().strip() == \"online\":\n        return settings.BASE_URL + static(\"img/locations/online.png\")\n    if location.lower().strip() == \"discord\":\n        return settings.BASE_URL + static(\"img/locations/discord.png\")\n\n    maps_url = (\n        f\"/maps/api/staticmap?\"\n        f\"center={ urlencode(location) }&\"\n        f\"zoom={ zoom }&size={ size }&\"\n        f\"markers={ urlencode(location) }&\"\n        f\"key={ settings.GOOGLE_MAPS_API_KEY }\"\n    )\n\n    decoded_key = urlsafe_b64decode(settings.GOOGLE_MAPS_API_SECRET)\n\n    signature = hmac.new(decoded_key, maps_url.encode(), sha1)\n\n    encoded_signature = urlsafe_b64encode(signature.digest())\n\n    maps_url += f\"&signature={encoded_signature.decode('utf-8')}\"\n\n    return \"https://maps.googleapis.com\" + maps_url\n\n\ndef _extract_date(param):\n    \"\"\"Extract the date from an arbitrary string.\"\"\"\n    if param is None:\n        return None\n    try:\n        return dateparse.parse_datetime(param)\n    except ValueError:\n        return dateparse.parse_date(param)\n\n\ndef extract_date_range(request, allow_empty=False):\n    \"\"\"Extract a date range from an arbitrary string.\"\"\"\n    default_value = None\n\n    start = request.query_params.get(\"start\", default_value)\n    if start or not allow_empty:\n        try:\n            start = dateparse.parse_datetime(start)\n            if not timezone.is_aware(start):\n                start = timezone.make_aware(start)\n        except (ValueError, AttributeError, TypeError) as e:\n            raise ParseError(detail=\"start query parameter invalid\") from e\n\n    end = request.query_params.get(\"end\", default_value)\n    if end or not allow_empty:\n        try:\n            end = dateparse.parse_datetime(end)\n            if not timezone.is_aware(end):\n                end = timezone.make_aware(end)\n        except (ValueError, AttributeError, TypeError) as e:\n            raise ParseError(detail=\"end query parameter invalid\") from e\n\n    return start, end\n\n\ndef overlaps(check, others, can_equal=True):\n    \"\"\"Check for overlapping date ranges.\n\n    This works by checking the maximum of the two `since` times, and the minimum of\n    the two `until` times. Because there are no infinite dates, the value date_max\n    is created for when the `until` value is None; this signifies a timespan that\n    has not ended yet and is the maximum possible date in Python's datetime.\n\n    The ranges overlap when the maximum start time is smaller than the minimum\n    end time, as can be seen in this example of two integer ranges:\n\n    check: . . . .[4]. . . . 9\n    other: . . 2 . .[5]. . . .\n\n    check: . . . .[4]. . . . 9\n    other: . . 2 . . . . . . . [date_max]\n\n    And when non overlapping:\n    check: . . . . . .[6] . . 9\n    other: . . 2 . .[5]. . . .\n\n    4 < 5 == True so these intervals overlap, while 6 < 5 == False so these intervals\n    don't overlap\n\n    The can_equal argument is used for boards, where the end date can't be the same\n    as the start date.\n\n    >>> overlaps( \\\n    dict2obj({ \\\n        'pk': 1 \\\n        , 'since': datetime.date(2018, 12, 1) \\\n        , 'until': datetime.date(2019, 1, 1) \\\n    }) \\\n    , [dict2obj({ \\\n    'pk': 2 \\\n    , 'since': datetime.date(2019, 1, 1) \\\n    , 'until': datetime.date(2019, 1, 31) \\\n    })])\n    False\n\n    >>> overlaps( \\\n    dict2obj({ \\\n        'pk': 1 \\\n        , 'since': datetime.date(2018, 12, 1) \\\n        , 'until': datetime.date(2019, 1, 1) \\\n    }) \\\n    , [dict2obj({ \\\n    'pk': 2 \\\n    , 'since': datetime.date(2019, 1, 1) \\\n    , 'until': datetime.date(2019, 1, 31) \\\n    })], False)\n    True\n\n    >>> overlaps( \\\n    dict2obj({ \\\n        'pk': 1 \\\n        , 'since': datetime.date(2018, 12, 1) \\\n        , 'until': datetime.date(2019, 1, 2) \\\n    }) \\\n    , [dict2obj({ \\\n    'pk': 2 \\\n    , 'since': datetime.date(2019, 1, 1) \\\n    , 'until': datetime.date(2019, 1, 31) \\\n    })])\n    True\n    \"\"\"\n    date_max = datetime.date(datetime.MAXYEAR, 12, 31)\n    for other in others:\n        if check.pk == other.pk:\n            # No checks for the object we're validating\n            continue\n\n        max_start = max(check.since, other.since)\n        min_end = min(check.until or date_max, other.until or date_max)\n\n        if max_start == min_end and not can_equal:\n            return True\n        if max_start < min_end:\n            return True\n\n    return False\n", "path": "website/utils/snippets.py"}]}