diff --git a/Dockerfile b/Dockerfile
index 3eb58d43d6b60bcfa899de3df1fb11d156f60217..c2b7db5ebe8f4b8f2198ceb62fdfd984e541e897 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.9-buster AS core
+FROM debian:bullseye-slim AS core
 
 # Build arguments
 ARG EXTRAS="ldap,s3"
@@ -12,6 +12,7 @@ ENV PIP_DISABLE_PIP_VERSION_CHECK 1
 ENV PIP_NO_CACHE_DIR 1
 ENV PIP_EXTRA_INDEX_URL https://edugit.org/api/v4/projects/461/packages/pypi/simple
 ENV PIP_USE_DEPRECATED legacy-resolver
+ENV DEBIAN_FRONTEND noninteractive
 
 # Configure app settings for build and runtime
 ENV ALEKSIS_static__root /usr/share/aleksis/static
@@ -25,14 +26,18 @@ RUN apt-get -y update && \
     eatmydata apt-get -y upgrade && \
     eatmydata apt-get install -y --no-install-recommends \
         build-essential \
+    chromium \
 	dumb-init \
 	gettext \
 	libpq5 \
 	libpq-dev \
 	libssl-dev \
 	postgresql-client \
-	yarnpkg && \
-    eatmydata pip install uwsgi
+	python3-dev \
+	python3-pip \
+	uwsgi \
+	uwsgi-plugin-python3 \
+	yarnpkg
 
 # Install extra dependencies
 RUN   case ",$EXTRAS," in \
@@ -73,7 +78,8 @@ RUN set -e; \
         libpq-dev \
         libssl-dev \
         libldap2-dev \
-        libsasl2-dev; \
+        libsasl2-dev \
+        python3-dev; \
     eatmydata apt-get autoremove --purge -y; \
     apt-get clean -y; \
     rm -rf /root/.cache
diff --git a/README.rst b/README.rst
index 067f83e32bfedf6251c91b5d5e42162c1db97b9e..5946b0d4eb15331b918e95446fddbc440250bfcb 100644
--- a/README.rst
+++ b/README.rst
@@ -36,16 +36,16 @@ Licence
 
 ::
 
-  Copyright © 2021 magicfelix <felix@felix-zauberer.de>
-  Copyright © 2017, 2018, 2019, 2020 Jonathan Weth <wethjo@katharineum.de>
-  Copyright © 2017, 2018, 2019 Frank Poetzsch-Heffter <p-h@katharineum.de>
-  Copyright © 2018, 2019, 2020 Julian Leucker <leuckeju@katharineum.de>
-  Copyright © 2018, 2019, 2020 Hangzhi Yu <yuha@katharineum.de>
-  Copyright © 2019, 2020 Dominik George <dominik.george@teckids.org>
-  Copyright © 2019, 2020 Tom Teichler <tom.teichler@teckids.org>
+  Copyright © 2017, 2018, 2019, 2020, 2021 Jonathan Weth <wethjo@katharineum.de>
+  Copyright © 2017, 2018, 2019, 2020 Frank Poetzsch-Heffter <p-h@katharineum.de>
+  Copyright © 2018, 2019, 2020, 2021 Julian Leucker <leuckeju@katharineum.de>
+  Copyright © 2018, 2019, 2020, 2021 Hangzhi Yu <yuha@katharineum.de>
+  Copyright © 2019, 2020, 2021 Dominik George <dominik.george@teckids.org>
+  Copyright © 2019, 2020, 2021 Tom Teichler <tom.teichler@teckids.org>
   Copyright © 2019 mirabilos <thorsten.glaser@teckids.org>
+  Copyright © 2021 magicfelix <felix@felix-zauberer.de>
 
-  Licenced under the EUPL, version 1.2 or later
+  Licenced under the EUPL, version 1.2 or later, by Teckids e.V. (Bonn, Germany).
 
 Please see the LICENCE.rst file accompanying this distribution for the
 full licence text or on the `European Union Public Licence`_ website
diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py
index a3b9b7a9a3957b9af7ad0b9ddeb57d00a630ca73..fa48d04407a4e047ea98e6ce7b1102158ba25fbb 100644
--- a/aleksis/core/apps.py
+++ b/aleksis/core/apps.py
@@ -1,4 +1,4 @@
-from typing import Any, List, Optional, Tuple
+from typing import Any, Optional
 
 import django.apps
 from django.apps import apps
@@ -28,14 +28,14 @@ class CoreConfig(AppConfig):
     }
     licence = "EUPL-1.2+"
     copyright_info = (
-        ([2021], "magicfelix", "felix@felix-zauberer.de"),
-        ([2017, 2018, 2019, 2020], "Jonathan Weth", "wethjo@katharineum.de"),
-        ([2017, 2018, 2019], "Frank Poetzsch-Heffter", "p-h@katharineum.de"),
-        ([2018, 2019, 2020], "Julian Leucker", "leuckeju@katharineum.de"),
-        ([2018, 2019, 2020], "Hangzhi Yu", "yuha@katharineum.de"),
-        ([2019, 2020], "Dominik George", "dominik.george@teckids.org"),
-        ([2019, 2020], "Tom Teichler", "tom.teichler@teckids.org"),
+        ([2017, 2018, 2019, 2020, 2021], "Jonathan Weth", "wethjo@katharineum.de"),
+        ([2017, 2018, 2019, 2020], "Frank Poetzsch-Heffter", "p-h@katharineum.de"),
+        ([2018, 2019, 2020, 2021], "Julian Leucker", "leuckeju@katharineum.de"),
+        ([2018, 2019, 2020, 2021], "Hangzhi Yu", "yuha@katharineum.de"),
+        ([2019, 2020, 2021], "Dominik George", "dominik.george@teckids.org"),
+        ([2019, 2020, 2021], "Tom Teichler", "tom.teichler@teckids.org"),
         ([2019], "mirabilos", "thorsten.glaser@teckids.org"),
+        ([2021], "magicfelix", "felix@felix-zauberer.de"),
     )
 
     def ready(self):
@@ -107,11 +107,9 @@ class CoreConfig(AppConfig):
         verbosity: int,
         interactive: bool,
         using: str,
-        plan: List[Tuple],
-        apps: django.apps.registry.Apps,
         **kwargs,
     ) -> None:
-        super().post_migrate(app_config, verbosity, interactive, using, plan, apps)
+        super().post_migrate(app_config, verbosity, interactive, using, **kwargs)
 
         # Ensure presence of an OTP YubiKey default config
         apps.get_model("otp_yubikey", "ValidationService").objects.using(using).update_or_create(
diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
index c26e99b1bc38619b7b131cb67718516e4b6da5e1..d8a3b4e5a53a776cd9fb0433267539bde11efe99 100644
--- a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-11 21:30+0100\n"
-"PO-Revision-Date: 2021-02-17 12:13+0000\n"
+"PO-Revision-Date: 2021-03-14 11:51+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis/"
 "de/>\n"
@@ -619,7 +619,7 @@ msgstr "Menü"
 
 #: models.py:787
 msgid "Icon"
-msgstr "Icon"
+msgstr "Symbol"
 
 #: models.py:793
 msgid "Custom menu item"
diff --git a/aleksis/core/migrations/0001_initial.py b/aleksis/core/migrations/0001_initial.py
index 07e4bf433df4d8aec43814493d7dde6527d1f8f1..14041d3eca0f1830dc4da2154f318ad7f7c1d516 100644
--- a/aleksis/core/migrations/0001_initial.py
+++ b/aleksis/core/migrations/0001_initial.py
@@ -37,7 +37,7 @@ class Migration(migrations.Migration):
             name='AdditionalField',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('title', models.CharField(max_length=255, verbose_name='Title of field')),
                 ('field_type', models.CharField(choices=[('BooleanField', 'Boolean (Yes/No)'), ('CharField', 'Text (one line)'), ('DateField', 'Date'), ('DateTimeField', 'Date and time'), ('DecimalField', 'Decimal number'), ('EmailField', 'E-mail address'), ('IntegerField', 'Integer'), ('GenericIPAddressField', 'IP address'), ('NullBooleanField', 'Boolean or empty (Yes/No/Neither)'), ('TextField', 'Text (multi-line)'), ('TimeField', 'Time'), ('URLField', 'URL / Link')], max_length=50, verbose_name='Type of field')),
                 ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
@@ -54,7 +54,7 @@ class Migration(migrations.Migration):
             name='Announcement',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('title', models.CharField(max_length=150, verbose_name='Title')),
                 ('description', models.TextField(blank=True, max_length=500, verbose_name='Description')),
                 ('link', models.URLField(blank=True, verbose_name='Link to detailed view')),
@@ -71,7 +71,7 @@ class Migration(migrations.Migration):
             name='CustomMenu',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('name', models.CharField(max_length=100, unique=True, verbose_name='Menu ID')),
                 ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
             ],
@@ -87,7 +87,7 @@ class Migration(migrations.Migration):
             name='Group',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('name', models.CharField(max_length=255, unique=True, verbose_name='Long name')),
                 ('short_name', models.CharField(blank=True, max_length=255, null=True, unique=True, verbose_name='Short name')),
                 ('additional_fields', models.ManyToManyField(to='core.AdditionalField', verbose_name='Additional fields')),
@@ -106,7 +106,7 @@ class Migration(migrations.Migration):
             name='Person',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('is_active', models.BooleanField(default=True, verbose_name='Is person active?')),
                 ('first_name', models.CharField(max_length=255, verbose_name='First name')),
                 ('last_name', models.CharField(max_length=255, verbose_name='Last name')),
@@ -178,7 +178,7 @@ class Migration(migrations.Migration):
             name='PersonGroupThrough',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Group')),
                 ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Person')),
                 ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
@@ -194,7 +194,7 @@ class Migration(migrations.Migration):
             name='Notification',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('sender', models.CharField(max_length=100, verbose_name='Sender')),
                 ('title', models.CharField(max_length=150, verbose_name='Title')),
                 ('description', models.TextField(max_length=500, verbose_name='Description')),
@@ -216,7 +216,7 @@ class Migration(migrations.Migration):
             name='GroupType',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('name', models.CharField(max_length=50, verbose_name='Title of type')),
                 ('description', models.CharField(max_length=500, verbose_name='Description')),
                 ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
@@ -283,7 +283,7 @@ class Migration(migrations.Migration):
             name='CustomMenuItem',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('name', models.CharField(max_length=150, verbose_name='Name')),
                 ('url', models.URLField(verbose_name='Link')),
                 ('icon', models.CharField(blank=True, choices=[('3d_rotation', '3d_rotation'), ('ac_unit', 'ac_unit'), ('access_alarm', 'access_alarm'), ('access_alarms', 'access_alarms'), ('access_time', 'access_time'), ('accessibility', 'accessibility'), ('accessible', 'accessible'), ('account_balance', 'account_balance'), ('account_balance_wallet', 'account_balance_wallet'), ('account_box', 'account_box'), ('account_circle', 'account_circle'), ('adb', 'adb'), ('add', 'add'), ('add_a_photo', 'add_a_photo'), ('add_alarm', 'add_alarm'), ('add_alert', 'add_alert'), ('add_box', 'add_box'), ('add_circle', 'add_circle'), ('add_circle_outline', 'add_circle_outline'), ('add_location', 'add_location'), ('add_shopping_cart', 'add_shopping_cart'), ('add_to_photos', 'add_to_photos'), ('add_to_queue', 'add_to_queue'), ('adjust', 'adjust'), ('airline_seat_flat', 'airline_seat_flat'), ('airline_seat_flat_angled', 'airline_seat_flat_angled'), ('airline_seat_individual_suite', 'airline_seat_individual_suite'), ('airline_seat_legroom_extra', 'airline_seat_legroom_extra'), ('airline_seat_legroom_normal', 'airline_seat_legroom_normal'), ('airline_seat_legroom_reduced', 'airline_seat_legroom_reduced'), ('airline_seat_recline_extra', 'airline_seat_recline_extra'), ('airline_seat_recline_normal', 'airline_seat_recline_normal'), ('airplanemode_active', 'airplanemode_active'), ('airplanemode_inactive', 'airplanemode_inactive'), ('airplay', 'airplay'), ('airport_shuttle', 'airport_shuttle'), ('alarm', 'alarm'), ('alarm_add', 'alarm_add'), ('alarm_off', 'alarm_off'), ('alarm_on', 'alarm_on'), ('album', 'album'), ('all_inclusive', 'all_inclusive'), ('all_out', 'all_out'), ('android', 'android'), ('announcement', 'announcement'), ('apps', 'apps'), ('archive', 'archive'), ('arrow_back', 'arrow_back'), ('arrow_downward', 'arrow_downward'), ('arrow_drop_down', 'arrow_drop_down'), ('arrow_drop_down_circle', 'arrow_drop_down_circle'), ('arrow_drop_up', 'arrow_drop_up'), ('arrow_forward', 'arrow_forward'), ('arrow_upward', 'arrow_upward'), ('art_track', 'art_track'), ('aspect_ratio', 'aspect_ratio'), ('assessment', 'assessment'), ('assignment', 'assignment'), ('assignment_ind', 'assignment_ind'), ('assignment_late', 'assignment_late'), ('assignment_return', 'assignment_return'), ('assignment_returned', 'assignment_returned'), ('assignment_turned_in', 'assignment_turned_in'), ('assistant', 'assistant'), ('assistant_photo', 'assistant_photo'), ('attach_file', 'attach_file'), ('attach_money', 'attach_money'), ('attachment', 'attachment'), ('audiotrack', 'audiotrack'), ('autorenew', 'autorenew'), ('av_timer', 'av_timer'), ('backspace', 'backspace'), ('backup', 'backup'), ('battery_alert', 'battery_alert'), ('battery_charging_full', 'battery_charging_full'), ('battery_full', 'battery_full'), ('battery_std', 'battery_std'), ('battery_unknown', 'battery_unknown'), ('beach_access', 'beach_access'), ('beenhere', 'beenhere'), ('block', 'block'), ('bluetooth', 'bluetooth'), ('bluetooth_audio', 'bluetooth_audio'), ('bluetooth_connected', 'bluetooth_connected'), ('bluetooth_disabled', 'bluetooth_disabled'), ('bluetooth_searching', 'bluetooth_searching'), ('blur_circular', 'blur_circular'), ('blur_linear', 'blur_linear'), ('blur_off', 'blur_off'), ('blur_on', 'blur_on'), ('book', 'book'), ('bookmark', 'bookmark'), ('bookmark_border', 'bookmark_border'), ('border_all', 'border_all'), ('border_bottom', 'border_bottom'), ('border_clear', 'border_clear'), ('border_color', 'border_color'), ('border_horizontal', 'border_horizontal'), ('border_inner', 'border_inner'), ('border_left', 'border_left'), ('border_outer', 'border_outer'), ('border_right', 'border_right'), ('border_style', 'border_style'), ('border_top', 'border_top'), ('border_vertical', 'border_vertical'), ('branding_watermark', 'branding_watermark'), ('brightness_1', 'brightness_1'), ('brightness_2', 'brightness_2'), ('brightness_3', 'brightness_3'), ('brightness_4', 'brightness_4'), ('brightness_5', 'brightness_5'), ('brightness_6', 'brightness_6'), ('brightness_7', 'brightness_7'), ('brightness_auto', 'brightness_auto'), ('brightness_high', 'brightness_high'), ('brightness_low', 'brightness_low'), ('brightness_medium', 'brightness_medium'), ('broken_image', 'broken_image'), ('brush', 'brush'), ('bubble_chart', 'bubble_chart'), ('bug_report', 'bug_report'), ('build', 'build'), ('burst_mode', 'burst_mode'), ('business', 'business'), ('business_center', 'business_center'), ('cached', 'cached'), ('cake', 'cake'), ('call', 'call'), ('call_end', 'call_end'), ('call_made', 'call_made'), ('call_merge', 'call_merge'), ('call_missed', 'call_missed'), ('call_missed_outgoing', 'call_missed_outgoing'), ('call_received', 'call_received'), ('call_split', 'call_split'), ('call_to_action', 'call_to_action'), ('camera', 'camera'), ('camera_alt', 'camera_alt'), ('camera_enhance', 'camera_enhance'), ('camera_front', 'camera_front'), ('camera_rear', 'camera_rear'), ('camera_roll', 'camera_roll'), ('cancel', 'cancel'), ('card_giftcard', 'card_giftcard'), ('card_membership', 'card_membership'), ('card_travel', 'card_travel'), ('casino', 'casino'), ('cast', 'cast'), ('cast_connected', 'cast_connected'), ('center_focus_strong', 'center_focus_strong'), ('center_focus_weak', 'center_focus_weak'), ('change_history', 'change_history'), ('chat', 'chat'), ('chat_bubble', 'chat_bubble'), ('chat_bubble_outline', 'chat_bubble_outline'), ('check', 'check'), ('check_box', 'check_box'), ('check_box_outline_blank', 'check_box_outline_blank'), ('check_circle', 'check_circle'), ('chevron_left', 'chevron_left'), ('chevron_right', 'chevron_right'), ('child_care', 'child_care'), ('child_friendly', 'child_friendly'), ('chrome_reader_mode', 'chrome_reader_mode'), ('class', 'class'), ('clear', 'clear'), ('clear_all', 'clear_all'), ('close', 'close'), ('closed_caption', 'closed_caption'), ('cloud', 'cloud'), ('cloud_circle', 'cloud_circle'), ('cloud_done', 'cloud_done'), ('cloud_download', 'cloud_download'), ('cloud_off', 'cloud_off'), ('cloud_queue', 'cloud_queue'), ('cloud_upload', 'cloud_upload'), ('code', 'code'), ('collections', 'collections'), ('collections_bookmark', 'collections_bookmark'), ('color_lens', 'color_lens'), ('colorize', 'colorize'), ('comment', 'comment'), ('compare', 'compare'), ('compare_arrows', 'compare_arrows'), ('computer', 'computer'), ('confirmation_number', 'confirmation_number'), ('contact_mail', 'contact_mail'), ('contact_phone', 'contact_phone'), ('contacts', 'contacts'), ('content_copy', 'content_copy'), ('content_cut', 'content_cut'), ('content_paste', 'content_paste'), ('control_point', 'control_point'), ('control_point_duplicate', 'control_point_duplicate'), ('copyright', 'copyright'), ('create', 'create'), ('create_new_folder', 'create_new_folder'), ('credit_card', 'credit_card'), ('crop', 'crop'), ('crop_16_9', 'crop_16_9'), ('crop_3_2', 'crop_3_2'), ('crop_5_4', 'crop_5_4'), ('crop_7_5', 'crop_7_5'), ('crop_din', 'crop_din'), ('crop_free', 'crop_free'), ('crop_landscape', 'crop_landscape'), ('crop_original', 'crop_original'), ('crop_portrait', 'crop_portrait'), ('crop_rotate', 'crop_rotate'), ('crop_square', 'crop_square'), ('dashboard', 'dashboard'), ('data_usage', 'data_usage'), ('date_range', 'date_range'), ('dehaze', 'dehaze'), ('delete', 'delete'), ('delete_forever', 'delete_forever'), ('delete_sweep', 'delete_sweep'), ('description', 'description'), ('desktop_mac', 'desktop_mac'), ('desktop_windows', 'desktop_windows'), ('details', 'details'), ('developer_board', 'developer_board'), ('developer_mode', 'developer_mode'), ('device_hub', 'device_hub'), ('devices', 'devices'), ('devices_other', 'devices_other'), ('dialer_sip', 'dialer_sip'), ('dialpad', 'dialpad'), ('directions', 'directions'), ('directions_bike', 'directions_bike'), ('directions_boat', 'directions_boat'), ('directions_bus', 'directions_bus'), ('directions_car', 'directions_car'), ('directions_railway', 'directions_railway'), ('directions_run', 'directions_run'), ('directions_subway', 'directions_subway'), ('directions_transit', 'directions_transit'), ('directions_walk', 'directions_walk'), ('disc_full', 'disc_full'), ('dns', 'dns'), ('do_not_disturb', 'do_not_disturb'), ('do_not_disturb_alt', 'do_not_disturb_alt'), ('do_not_disturb_off', 'do_not_disturb_off'), ('do_not_disturb_on', 'do_not_disturb_on'), ('dock', 'dock'), ('domain', 'domain'), ('done', 'done'), ('done_all', 'done_all'), ('donut_large', 'donut_large'), ('donut_small', 'donut_small'), ('drafts', 'drafts'), ('drag_handle', 'drag_handle'), ('drive_eta', 'drive_eta'), ('dvr', 'dvr'), ('edit', 'edit'), ('edit_location', 'edit_location'), ('eject', 'eject'), ('email', 'email'), ('enhanced_encryption', 'enhanced_encryption'), ('equalizer', 'equalizer'), ('error', 'error'), ('error_outline', 'error_outline'), ('euro_symbol', 'euro_symbol'), ('ev_station', 'ev_station'), ('event', 'event'), ('event_available', 'event_available'), ('event_busy', 'event_busy'), ('event_note', 'event_note'), ('event_seat', 'event_seat'), ('exit_to_app', 'exit_to_app'), ('expand_less', 'expand_less'), ('expand_more', 'expand_more'), ('explicit', 'explicit'), ('explore', 'explore'), ('exposure', 'exposure'), ('exposure_neg_1', 'exposure_neg_1'), ('exposure_neg_2', 'exposure_neg_2'), ('exposure_plus_1', 'exposure_plus_1'), ('exposure_plus_2', 'exposure_plus_2'), ('exposure_zero', 'exposure_zero'), ('extension', 'extension'), ('face', 'face'), ('fast_forward', 'fast_forward'), ('fast_rewind', 'fast_rewind'), ('favorite', 'favorite'), ('favorite_border', 'favorite_border'), ('featured_play_list', 'featured_play_list'), ('featured_video', 'featured_video'), ('feedback', 'feedback'), ('fiber_dvr', 'fiber_dvr'), ('fiber_manual_record', 'fiber_manual_record'), ('fiber_new', 'fiber_new'), ('fiber_pin', 'fiber_pin'), ('fiber_smart_record', 'fiber_smart_record'), ('file_download', 'file_download'), ('file_upload', 'file_upload'), ('filter', 'filter'), ('filter_1', 'filter_1'), ('filter_2', 'filter_2'), ('filter_3', 'filter_3'), ('filter_4', 'filter_4'), ('filter_5', 'filter_5'), ('filter_6', 'filter_6'), ('filter_7', 'filter_7'), ('filter_8', 'filter_8'), ('filter_9', 'filter_9'), ('filter_9_plus', 'filter_9_plus'), ('filter_b_and_w', 'filter_b_and_w'), ('filter_center_focus', 'filter_center_focus'), ('filter_drama', 'filter_drama'), ('filter_frames', 'filter_frames'), ('filter_hdr', 'filter_hdr'), ('filter_list', 'filter_list'), ('filter_none', 'filter_none'), ('filter_tilt_shift', 'filter_tilt_shift'), ('filter_vintage', 'filter_vintage'), ('find_in_page', 'find_in_page'), ('find_replace', 'find_replace'), ('fingerprint', 'fingerprint'), ('first_page', 'first_page'), ('fitness_center', 'fitness_center'), ('flag', 'flag'), ('flare', 'flare'), ('flash_auto', 'flash_auto'), ('flash_off', 'flash_off'), ('flash_on', 'flash_on'), ('flight', 'flight'), ('flight_land', 'flight_land'), ('flight_takeoff', 'flight_takeoff'), ('flip', 'flip'), ('flip_to_back', 'flip_to_back'), ('flip_to_front', 'flip_to_front'), ('folder', 'folder'), ('folder_open', 'folder_open'), ('folder_shared', 'folder_shared'), ('folder_special', 'folder_special'), ('font_download', 'font_download'), ('format_align_center', 'format_align_center'), ('format_align_justify', 'format_align_justify'), ('format_align_left', 'format_align_left'), ('format_align_right', 'format_align_right'), ('format_bold', 'format_bold'), ('format_clear', 'format_clear'), ('format_color_fill', 'format_color_fill'), ('format_color_reset', 'format_color_reset'), ('format_color_text', 'format_color_text'), ('format_indent_decrease', 'format_indent_decrease'), ('format_indent_increase', 'format_indent_increase'), ('format_italic', 'format_italic'), ('format_line_spacing', 'format_line_spacing'), ('format_list_bulleted', 'format_list_bulleted'), ('format_list_numbered', 'format_list_numbered'), ('format_paint', 'format_paint'), ('format_quote', 'format_quote'), ('format_shapes', 'format_shapes'), ('format_size', 'format_size'), ('format_strikethrough', 'format_strikethrough'), ('format_textdirection_l_to_r', 'format_textdirection_l_to_r'), ('format_textdirection_r_to_l', 'format_textdirection_r_to_l'), ('format_underlined', 'format_underlined'), ('forum', 'forum'), ('forward', 'forward'), ('forward_10', 'forward_10'), ('forward_30', 'forward_30'), ('forward_5', 'forward_5'), ('free_breakfast', 'free_breakfast'), ('fullscreen', 'fullscreen'), ('fullscreen_exit', 'fullscreen_exit'), ('functions', 'functions'), ('g_translate', 'g_translate'), ('gamepad', 'gamepad'), ('games', 'games'), ('gavel', 'gavel'), ('gesture', 'gesture'), ('get_app', 'get_app'), ('gif', 'gif'), ('golf_course', 'golf_course'), ('gps_fixed', 'gps_fixed'), ('gps_not_fixed', 'gps_not_fixed'), ('gps_off', 'gps_off'), ('grade', 'grade'), ('gradient', 'gradient'), ('grain', 'grain'), ('graphic_eq', 'graphic_eq'), ('grid_off', 'grid_off'), ('grid_on', 'grid_on'), ('group', 'group'), ('group_add', 'group_add'), ('group_work', 'group_work'), ('hd', 'hd'), ('hdr_off', 'hdr_off'), ('hdr_on', 'hdr_on'), ('hdr_strong', 'hdr_strong'), ('hdr_weak', 'hdr_weak'), ('headset', 'headset'), ('headset_mic', 'headset_mic'), ('healing', 'healing'), ('hearing', 'hearing'), ('help', 'help'), ('help_outline', 'help_outline'), ('high_quality', 'high_quality'), ('highlight', 'highlight'), ('highlight_off', 'highlight_off'), ('history', 'history'), ('home', 'home'), ('hot_tub', 'hot_tub'), ('hotel', 'hotel'), ('hourglass_empty', 'hourglass_empty'), ('hourglass_full', 'hourglass_full'), ('http', 'http'), ('https', 'https'), ('image', 'image'), ('image_aspect_ratio', 'image_aspect_ratio'), ('import_contacts', 'import_contacts'), ('import_export', 'import_export'), ('important_devices', 'important_devices'), ('inbox', 'inbox'), ('indeterminate_check_box', 'indeterminate_check_box'), ('info', 'info'), ('info_outline', 'info_outline'), ('input', 'input'), ('insert_chart', 'insert_chart'), ('insert_comment', 'insert_comment'), ('insert_drive_file', 'insert_drive_file'), ('insert_emoticon', 'insert_emoticon'), ('insert_invitation', 'insert_invitation'), ('insert_link', 'insert_link'), ('insert_photo', 'insert_photo'), ('invert_colors', 'invert_colors'), ('invert_colors_off', 'invert_colors_off'), ('iso', 'iso'), ('keyboard', 'keyboard'), ('keyboard_arrow_down', 'keyboard_arrow_down'), ('keyboard_arrow_left', 'keyboard_arrow_left'), ('keyboard_arrow_right', 'keyboard_arrow_right'), ('keyboard_arrow_up', 'keyboard_arrow_up'), ('keyboard_backspace', 'keyboard_backspace'), ('keyboard_capslock', 'keyboard_capslock'), ('keyboard_hide', 'keyboard_hide'), ('keyboard_return', 'keyboard_return'), ('keyboard_tab', 'keyboard_tab'), ('keyboard_voice', 'keyboard_voice'), ('kitchen', 'kitchen'), ('label', 'label'), ('label_outline', 'label_outline'), ('landscape', 'landscape'), ('language', 'language'), ('laptop', 'laptop'), ('laptop_chromebook', 'laptop_chromebook'), ('laptop_mac', 'laptop_mac'), ('laptop_windows', 'laptop_windows'), ('last_page', 'last_page'), ('launch', 'launch'), ('layers', 'layers'), ('layers_clear', 'layers_clear'), ('leak_add', 'leak_add'), ('leak_remove', 'leak_remove'), ('lens', 'lens'), ('library_add', 'library_add'), ('library_books', 'library_books'), ('library_music', 'library_music'), ('lightbulb_outline', 'lightbulb_outline'), ('line_style', 'line_style'), ('line_weight', 'line_weight'), ('linear_scale', 'linear_scale'), ('link', 'link'), ('linked_camera', 'linked_camera'), ('list', 'list'), ('live_help', 'live_help'), ('live_tv', 'live_tv'), ('local_activity', 'local_activity'), ('local_airport', 'local_airport'), ('local_atm', 'local_atm'), ('local_bar', 'local_bar'), ('local_cafe', 'local_cafe'), ('local_car_wash', 'local_car_wash'), ('local_convenience_store', 'local_convenience_store'), ('local_dining', 'local_dining'), ('local_drink', 'local_drink'), ('local_florist', 'local_florist'), ('local_gas_station', 'local_gas_station'), ('local_grocery_store', 'local_grocery_store'), ('local_hospital', 'local_hospital'), ('local_hotel', 'local_hotel'), ('local_laundry_service', 'local_laundry_service'), ('local_library', 'local_library'), ('local_mall', 'local_mall'), ('local_movies', 'local_movies'), ('local_offer', 'local_offer'), ('local_parking', 'local_parking'), ('local_pharmacy', 'local_pharmacy'), ('local_phone', 'local_phone'), ('local_pizza', 'local_pizza'), ('local_play', 'local_play'), ('local_post_office', 'local_post_office'), ('local_printshop', 'local_printshop'), ('local_see', 'local_see'), ('local_shipping', 'local_shipping'), ('local_taxi', 'local_taxi'), ('location_city', 'location_city'), ('location_disabled', 'location_disabled'), ('location_off', 'location_off'), ('location_on', 'location_on'), ('location_searching', 'location_searching'), ('lock', 'lock'), ('lock_open', 'lock_open'), ('lock_outline', 'lock_outline'), ('looks', 'looks'), ('looks_3', 'looks_3'), ('looks_4', 'looks_4'), ('looks_5', 'looks_5'), ('looks_6', 'looks_6'), ('looks_one', 'looks_one'), ('looks_two', 'looks_two'), ('loop', 'loop'), ('loupe', 'loupe'), ('low_priority', 'low_priority'), ('loyalty', 'loyalty'), ('mail', 'mail'), ('mail_outline', 'mail_outline'), ('map', 'map'), ('markunread', 'markunread'), ('markunread_mailbox', 'markunread_mailbox'), ('memory', 'memory'), ('menu', 'menu'), ('merge_type', 'merge_type'), ('message', 'message'), ('mic', 'mic'), ('mic_none', 'mic_none'), ('mic_off', 'mic_off'), ('mms', 'mms'), ('mode_comment', 'mode_comment'), ('mode_edit', 'mode_edit'), ('monetization_on', 'monetization_on'), ('money_off', 'money_off'), ('monochrome_photos', 'monochrome_photos'), ('mood', 'mood'), ('mood_bad', 'mood_bad'), ('more', 'more'), ('more_horiz', 'more_horiz'), ('more_vert', 'more_vert'), ('motorcycle', 'motorcycle'), ('mouse', 'mouse'), ('move_to_inbox', 'move_to_inbox'), ('movie', 'movie'), ('movie_creation', 'movie_creation'), ('movie_filter', 'movie_filter'), ('multiline_chart', 'multiline_chart'), ('music_note', 'music_note'), ('music_video', 'music_video'), ('my_location', 'my_location'), ('nature', 'nature'), ('nature_people', 'nature_people'), ('navigate_before', 'navigate_before'), ('navigate_next', 'navigate_next'), ('navigation', 'navigation'), ('near_me', 'near_me'), ('network_cell', 'network_cell'), ('network_check', 'network_check'), ('network_locked', 'network_locked'), ('network_wifi', 'network_wifi'), ('new_releases', 'new_releases'), ('next_week', 'next_week'), ('nfc', 'nfc'), ('no_encryption', 'no_encryption'), ('no_sim', 'no_sim'), ('not_interested', 'not_interested'), ('note', 'note'), ('note_add', 'note_add'), ('notifications', 'notifications'), ('notifications_active', 'notifications_active'), ('notifications_none', 'notifications_none'), ('notifications_off', 'notifications_off'), ('notifications_paused', 'notifications_paused'), ('offline_pin', 'offline_pin'), ('ondemand_video', 'ondemand_video'), ('opacity', 'opacity'), ('open_in_browser', 'open_in_browser'), ('open_in_new', 'open_in_new'), ('open_with', 'open_with'), ('pages', 'pages'), ('pageview', 'pageview'), ('palette', 'palette'), ('pan_tool', 'pan_tool'), ('panorama', 'panorama'), ('panorama_fish_eye', 'panorama_fish_eye'), ('panorama_horizontal', 'panorama_horizontal'), ('panorama_vertical', 'panorama_vertical'), ('panorama_wide_angle', 'panorama_wide_angle'), ('party_mode', 'party_mode'), ('pause', 'pause'), ('pause_circle_filled', 'pause_circle_filled'), ('pause_circle_outline', 'pause_circle_outline'), ('payment', 'payment'), ('people', 'people'), ('people_outline', 'people_outline'), ('perm_camera_mic', 'perm_camera_mic'), ('perm_contact_calendar', 'perm_contact_calendar'), ('perm_data_setting', 'perm_data_setting'), ('perm_device_information', 'perm_device_information'), ('perm_identity', 'perm_identity'), ('perm_media', 'perm_media'), ('perm_phone_msg', 'perm_phone_msg'), ('perm_scan_wifi', 'perm_scan_wifi'), ('person', 'person'), ('person_add', 'person_add'), ('person_outline', 'person_outline'), ('person_pin', 'person_pin'), ('person_pin_circle', 'person_pin_circle'), ('personal_video', 'personal_video'), ('pets', 'pets'), ('phone', 'phone'), ('phone_android', 'phone_android'), ('phone_bluetooth_speaker', 'phone_bluetooth_speaker'), ('phone_forwarded', 'phone_forwarded'), ('phone_in_talk', 'phone_in_talk'), ('phone_iphone', 'phone_iphone'), ('phone_locked', 'phone_locked'), ('phone_missed', 'phone_missed'), ('phone_paused', 'phone_paused'), ('phonelink', 'phonelink'), ('phonelink_erase', 'phonelink_erase'), ('phonelink_lock', 'phonelink_lock'), ('phonelink_off', 'phonelink_off'), ('phonelink_ring', 'phonelink_ring'), ('phonelink_setup', 'phonelink_setup'), ('photo', 'photo'), ('photo_album', 'photo_album'), ('photo_camera', 'photo_camera'), ('photo_filter', 'photo_filter'), ('photo_library', 'photo_library'), ('photo_size_select_actual', 'photo_size_select_actual'), ('photo_size_select_large', 'photo_size_select_large'), ('photo_size_select_small', 'photo_size_select_small'), ('picture_as_pdf', 'picture_as_pdf'), ('picture_in_picture', 'picture_in_picture'), ('picture_in_picture_alt', 'picture_in_picture_alt'), ('pie_chart', 'pie_chart'), ('pie_chart_outlined', 'pie_chart_outlined'), ('pin_drop', 'pin_drop'), ('place', 'place'), ('play_arrow', 'play_arrow'), ('play_circle_filled', 'play_circle_filled'), ('play_circle_outline', 'play_circle_outline'), ('play_for_work', 'play_for_work'), ('playlist_add', 'playlist_add'), ('playlist_add_check', 'playlist_add_check'), ('playlist_play', 'playlist_play'), ('plus_one', 'plus_one'), ('poll', 'poll'), ('polymer', 'polymer'), ('pool', 'pool'), ('portable_wifi_off', 'portable_wifi_off'), ('portrait', 'portrait'), ('power', 'power'), ('power_input', 'power_input'), ('power_settings_new', 'power_settings_new'), ('pregnant_woman', 'pregnant_woman'), ('present_to_all', 'present_to_all'), ('print', 'print'), ('priority_high', 'priority_high'), ('public', 'public'), ('publish', 'publish'), ('query_builder', 'query_builder'), ('question_answer', 'question_answer'), ('queue', 'queue'), ('queue_music', 'queue_music'), ('queue_play_next', 'queue_play_next'), ('radio', 'radio'), ('radio_button_checked', 'radio_button_checked'), ('radio_button_unchecked', 'radio_button_unchecked'), ('rate_review', 'rate_review'), ('receipt', 'receipt'), ('recent_actors', 'recent_actors'), ('record_voice_over', 'record_voice_over'), ('redeem', 'redeem'), ('redo', 'redo'), ('refresh', 'refresh'), ('remove', 'remove'), ('remove_circle', 'remove_circle'), ('remove_circle_outline', 'remove_circle_outline'), ('remove_from_queue', 'remove_from_queue'), ('remove_red_eye', 'remove_red_eye'), ('remove_shopping_cart', 'remove_shopping_cart'), ('reorder', 'reorder'), ('repeat', 'repeat'), ('repeat_one', 'repeat_one'), ('replay', 'replay'), ('replay_10', 'replay_10'), ('replay_30', 'replay_30'), ('replay_5', 'replay_5'), ('reply', 'reply'), ('reply_all', 'reply_all'), ('report', 'report'), ('report_problem', 'report_problem'), ('restaurant', 'restaurant'), ('restaurant_menu', 'restaurant_menu'), ('restore', 'restore'), ('restore_page', 'restore_page'), ('ring_volume', 'ring_volume'), ('room', 'room'), ('room_service', 'room_service'), ('rotate_90_degrees_ccw', 'rotate_90_degrees_ccw'), ('rotate_left', 'rotate_left'), ('rotate_right', 'rotate_right'), ('rounded_corner', 'rounded_corner'), ('router', 'router'), ('rowing', 'rowing'), ('rss_feed', 'rss_feed'), ('rv_hookup', 'rv_hookup'), ('satellite', 'satellite'), ('save', 'save'), ('scanner', 'scanner'), ('schedule', 'schedule'), ('school', 'school'), ('screen_lock_landscape', 'screen_lock_landscape'), ('screen_lock_portrait', 'screen_lock_portrait'), ('screen_lock_rotation', 'screen_lock_rotation'), ('screen_rotation', 'screen_rotation'), ('screen_share', 'screen_share'), ('sd_card', 'sd_card'), ('sd_storage', 'sd_storage'), ('search', 'search'), ('security', 'security'), ('select_all', 'select_all'), ('send', 'send'), ('sentiment_dissatisfied', 'sentiment_dissatisfied'), ('sentiment_neutral', 'sentiment_neutral'), ('sentiment_satisfied', 'sentiment_satisfied'), ('sentiment_very_dissatisfied', 'sentiment_very_dissatisfied'), ('sentiment_very_satisfied', 'sentiment_very_satisfied'), ('settings', 'settings'), ('settings_applications', 'settings_applications'), ('settings_backup_restore', 'settings_backup_restore'), ('settings_bluetooth', 'settings_bluetooth'), ('settings_brightness', 'settings_brightness'), ('settings_cell', 'settings_cell'), ('settings_ethernet', 'settings_ethernet'), ('settings_input_antenna', 'settings_input_antenna'), ('settings_input_component', 'settings_input_component'), ('settings_input_composite', 'settings_input_composite'), ('settings_input_hdmi', 'settings_input_hdmi'), ('settings_input_svideo', 'settings_input_svideo'), ('settings_overscan', 'settings_overscan'), ('settings_phone', 'settings_phone'), ('settings_power', 'settings_power'), ('settings_remote', 'settings_remote'), ('settings_system_daydream', 'settings_system_daydream'), ('settings_voice', 'settings_voice'), ('share', 'share'), ('shop', 'shop'), ('shop_two', 'shop_two'), ('shopping_basket', 'shopping_basket'), ('shopping_cart', 'shopping_cart'), ('short_text', 'short_text'), ('show_chart', 'show_chart'), ('shuffle', 'shuffle'), ('signal_cellular_4_bar', 'signal_cellular_4_bar'), ('signal_cellular_connected_no_internet_4_bar', 'signal_cellular_connected_no_internet_4_bar'), ('signal_cellular_no_sim', 'signal_cellular_no_sim'), ('signal_cellular_null', 'signal_cellular_null'), ('signal_cellular_off', 'signal_cellular_off'), ('signal_wifi_4_bar', 'signal_wifi_4_bar'), ('signal_wifi_4_bar_lock', 'signal_wifi_4_bar_lock'), ('signal_wifi_off', 'signal_wifi_off'), ('sim_card', 'sim_card'), ('sim_card_alert', 'sim_card_alert'), ('skip_next', 'skip_next'), ('skip_previous', 'skip_previous'), ('slideshow', 'slideshow'), ('slow_motion_video', 'slow_motion_video'), ('smartphone', 'smartphone'), ('smoke_free', 'smoke_free'), ('smoking_rooms', 'smoking_rooms'), ('sms', 'sms'), ('sms_failed', 'sms_failed'), ('snooze', 'snooze'), ('sort', 'sort'), ('sort_by_alpha', 'sort_by_alpha'), ('spa', 'spa'), ('space_bar', 'space_bar'), ('speaker', 'speaker'), ('speaker_group', 'speaker_group'), ('speaker_notes', 'speaker_notes'), ('speaker_notes_off', 'speaker_notes_off'), ('speaker_phone', 'speaker_phone'), ('spellcheck', 'spellcheck'), ('star', 'star'), ('star_border', 'star_border'), ('star_half', 'star_half'), ('stars', 'stars'), ('stay_current_landscape', 'stay_current_landscape'), ('stay_current_portrait', 'stay_current_portrait'), ('stay_primary_landscape', 'stay_primary_landscape'), ('stay_primary_portrait', 'stay_primary_portrait'), ('stop', 'stop'), ('stop_screen_share', 'stop_screen_share'), ('storage', 'storage'), ('store', 'store'), ('store_mall_directory', 'store_mall_directory'), ('straighten', 'straighten'), ('streetview', 'streetview'), ('strikethrough_s', 'strikethrough_s'), ('style', 'style'), ('subdirectory_arrow_left', 'subdirectory_arrow_left'), ('subdirectory_arrow_right', 'subdirectory_arrow_right'), ('subject', 'subject'), ('subscriptions', 'subscriptions'), ('subtitles', 'subtitles'), ('subway', 'subway'), ('supervisor_account', 'supervisor_account'), ('surround_sound', 'surround_sound'), ('swap_calls', 'swap_calls'), ('swap_horiz', 'swap_horiz'), ('swap_vert', 'swap_vert'), ('swap_vertical_circle', 'swap_vertical_circle'), ('switch_camera', 'switch_camera'), ('switch_video', 'switch_video'), ('sync', 'sync'), ('sync_disabled', 'sync_disabled'), ('sync_problem', 'sync_problem'), ('system_update', 'system_update'), ('system_update_alt', 'system_update_alt'), ('tab', 'tab'), ('tab_unselected', 'tab_unselected'), ('tablet', 'tablet'), ('tablet_android', 'tablet_android'), ('tablet_mac', 'tablet_mac'), ('tag_faces', 'tag_faces'), ('tap_and_play', 'tap_and_play'), ('terrain', 'terrain'), ('text_fields', 'text_fields'), ('text_format', 'text_format'), ('textsms', 'textsms'), ('texture', 'texture'), ('theaters', 'theaters'), ('thumb_down', 'thumb_down'), ('thumb_up', 'thumb_up'), ('thumbs_up_down', 'thumbs_up_down'), ('time_to_leave', 'time_to_leave'), ('timelapse', 'timelapse'), ('timeline', 'timeline'), ('timer', 'timer'), ('timer_10', 'timer_10'), ('timer_3', 'timer_3'), ('timer_off', 'timer_off'), ('title', 'title'), ('toc', 'toc'), ('today', 'today'), ('toll', 'toll'), ('tonality', 'tonality'), ('touch_app', 'touch_app'), ('toys', 'toys'), ('track_changes', 'track_changes'), ('traffic', 'traffic'), ('train', 'train'), ('tram', 'tram'), ('transfer_within_a_station', 'transfer_within_a_station'), ('transform', 'transform'), ('translate', 'translate'), ('trending_down', 'trending_down'), ('trending_flat', 'trending_flat'), ('trending_up', 'trending_up'), ('tune', 'tune'), ('turned_in', 'turned_in'), ('turned_in_not', 'turned_in_not'), ('tv', 'tv'), ('unarchive', 'unarchive'), ('undo', 'undo'), ('unfold_less', 'unfold_less'), ('unfold_more', 'unfold_more'), ('update', 'update'), ('usb', 'usb'), ('verified_user', 'verified_user'), ('vertical_align_bottom', 'vertical_align_bottom'), ('vertical_align_center', 'vertical_align_center'), ('vertical_align_top', 'vertical_align_top'), ('vibration', 'vibration'), ('video_call', 'video_call'), ('video_label', 'video_label'), ('video_library', 'video_library'), ('videocam', 'videocam'), ('videocam_off', 'videocam_off'), ('videogame_asset', 'videogame_asset'), ('view_agenda', 'view_agenda'), ('view_array', 'view_array'), ('view_carousel', 'view_carousel'), ('view_column', 'view_column'), ('view_comfy', 'view_comfy'), ('view_compact', 'view_compact'), ('view_day', 'view_day'), ('view_headline', 'view_headline'), ('view_list', 'view_list'), ('view_module', 'view_module'), ('view_quilt', 'view_quilt'), ('view_stream', 'view_stream'), ('view_week', 'view_week'), ('vignette', 'vignette'), ('visibility', 'visibility'), ('visibility_off', 'visibility_off'), ('voice_chat', 'voice_chat'), ('voicemail', 'voicemail'), ('volume_down', 'volume_down'), ('volume_mute', 'volume_mute'), ('volume_off', 'volume_off'), ('volume_up', 'volume_up'), ('vpn_key', 'vpn_key'), ('vpn_lock', 'vpn_lock'), ('wallpaper', 'wallpaper'), ('warning', 'warning'), ('watch', 'watch'), ('watch_later', 'watch_later'), ('wb_auto', 'wb_auto'), ('wb_cloudy', 'wb_cloudy'), ('wb_incandescent', 'wb_incandescent'), ('wb_iridescent', 'wb_iridescent'), ('wb_sunny', 'wb_sunny'), ('wc', 'wc'), ('web', 'web'), ('web_asset', 'web_asset'), ('weekend', 'weekend'), ('whatshot', 'whatshot'), ('widgets', 'widgets'), ('wifi', 'wifi'), ('wifi_lock', 'wifi_lock'), ('wifi_tethering', 'wifi_tethering'), ('work', 'work'), ('wrap_text', 'wrap_text'), ('youtube_searched_for', 'youtube_searched_for'), ('zoom_in', 'zoom_in'), ('zoom_out', 'zoom_out'), ('zoom_out_map', 'zoom_out_map')], max_length=50, verbose_name='Icon')),
@@ -302,7 +302,7 @@ class Migration(migrations.Migration):
             name='AnnouncementRecipient',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('recipient_id', models.PositiveIntegerField()),
                 ('announcement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recipients', to='core.Announcement')),
                 ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
@@ -320,7 +320,7 @@ class Migration(migrations.Migration):
             name='Activity',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('title', models.CharField(max_length=150, verbose_name='Title')),
                 ('description', models.TextField(max_length=500, verbose_name='Description')),
                 ('app', models.CharField(max_length=100, verbose_name='Application')),
diff --git a/aleksis/core/migrations/0002_school_term.py b/aleksis/core/migrations/0002_school_term.py
index 456f78ebf7e16f32b92b954d36ad39f5d323e866..fba542ed9c4a7be9b60e94d28414ade4189a6d6b 100644
--- a/aleksis/core/migrations/0002_school_term.py
+++ b/aleksis/core/migrations/0002_school_term.py
@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
             name='SchoolTerm',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
                 ('name', models.CharField(max_length=255, unique=True, verbose_name='Name')),
                 ('date_start', models.DateField(verbose_name='Start date')),
                 ('date_end', models.DateField(verbose_name='End date')),
diff --git a/aleksis/core/migrations/0011_globalpermissions_options.py b/aleksis/core/migrations/0011_globalpermissions_options.py
new file mode 100644
index 0000000000000000000000000000000000000000..a338d910a07843804f8c7e8833e045191d743356
--- /dev/null
+++ b/aleksis/core/migrations/0011_globalpermissions_options.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.7 on 2021-04-08 19:15
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0010_external_link_widget'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='globalpermissions',
+            options={'default_permissions': (), 'managed': False, 'permissions': (('view_system_status', 'Can view system status'), ('link_persons_accounts', 'Can link persons to accounts'), ('manage_data', 'Can manage data'), ('impersonate', 'Can impersonate'), ('search', 'Can use search'), ('change_site_preferences', 'Can change site preferences'), ('change_person_preferences', 'Can change person preferences'), ('change_group_preferences', 'Can change group preferences'))},
+        ),
+    ]
diff --git a/aleksis/core/migrations/0012_valid_from_announcement.py b/aleksis/core/migrations/0012_valid_from_announcement.py
new file mode 100644
index 0000000000000000000000000000000000000000..abfc8cdeb0b9dece8f23389c3d3158d63bca13c2
--- /dev/null
+++ b/aleksis/core/migrations/0012_valid_from_announcement.py
@@ -0,0 +1,19 @@
+# Generated by Django 3.1.7 on 2021-04-08 19:15
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0011_globalpermissions_options'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='announcement',
+            name='valid_from',
+            field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date and time from when to show'),
+        ),
+    ]
diff --git a/aleksis/core/migrations/0013_pdf_file.py b/aleksis/core/migrations/0013_pdf_file.py
new file mode 100644
index 0000000000000000000000000000000000000000..63b8a95607ce306f8d14d8b4266e184e12a26838
--- /dev/null
+++ b/aleksis/core/migrations/0013_pdf_file.py
@@ -0,0 +1,46 @@
+# Generated by Django 3.2 on 2021-04-10 18:58
+
+import aleksis.core.models
+import django.contrib.sites.managers
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('sites', '0002_alter_domain_unique'),
+        ('core', '0012_valid_from_announcement'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='globalpermissions',
+            options={'default_permissions': (), 'managed': False, 'permissions': (
+            ('view_system_status', 'Can view system status'), ('link_persons_accounts', 'Can link persons to accounts'),
+            ('manage_data', 'Can manage data'), ('impersonate', 'Can impersonate'), ('search', 'Can use search'),
+            ('change_site_preferences', 'Can change site preferences'),
+            ('change_person_preferences', 'Can change person preferences'),
+            ('change_group_preferences', 'Can change group preferences'), ('test_pdf', 'Can test PDF generation'))},
+        ),
+        migrations.CreateModel(
+            name='PDFFile',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('extended_data', models.JSONField(default=dict, editable=False)),
+                ('expires_at', models.DateTimeField(default=aleksis.core.models.PDFFile._get_default_expiration, verbose_name='File expires at')),
+                ('html', models.TextField(verbose_name='Rendered HTML')),
+                ('file', models.FileField(blank=True, null=True, upload_to='pdfs/', verbose_name='Generated PDF file')),
+                ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pdf_files', to='core.person', verbose_name='Owner')),
+                ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site')),
+            ],
+            options={
+                'verbose_name': 'PDF file',
+                'verbose_name_plural': 'PDF files',
+            },
+            managers=[
+                ('objects', django.contrib.sites.managers.CurrentSiteManager()),
+            ],
+        ),
+    ]
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index 7f208aaa8e47a790a5a8bf26808095571c16f713..edad32fa014f578f5fb84aeeed08878f3426075e 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -1,7 +1,8 @@
 # flake8: noqa: DJ01
-
-from datetime import date, datetime
+import hmac
+from datetime import date, datetime, timedelta
 from typing import Iterable, List, Optional, Sequence, Union
+from urllib.parse import urlparse
 
 from django.conf import settings
 from django.contrib.auth import get_user_model
@@ -904,6 +905,7 @@ class GlobalPermissions(GlobalPermissionModel):
             ("change_site_preferences", _("Can change site preferences")),
             ("change_person_preferences", _("Can change person preferences")),
             ("change_group_preferences", _("Can change group preferences")),
+            ("test_pdf", _("Can test PDF generation")),
         )
 
 
@@ -967,3 +969,49 @@ class DataCheckResult(ExtensibleModel):
             ("run_data_checks", _("Can run data checks")),
             ("solve_data_problem", _("Can solve data check problems")),
         )
+
+
+class PDFFile(ExtensibleModel):
+    """Link to a rendered PDF file."""
+
+    def _get_default_expiration():  # noqa
+        return timezone.now() + timedelta(minutes=get_site_preferences()["general__pdf_expiration"])
+
+    def _get_upload_path(instance, filename):  # noqa
+        return f"pdfs/{instance.secret}.pdf"
+
+    person = models.ForeignKey(
+        to=Person, on_delete=models.CASCADE, verbose_name=_("Owner"), related_name="pdf_files"
+    )
+    expires_at = models.DateTimeField(
+        verbose_name=_("File expires at"), default=_get_default_expiration
+    )
+    html = models.TextField(verbose_name=_("Rendered HTML"))
+    file = models.FileField(
+        upload_to=_get_upload_path, blank=True, null=True, verbose_name=_("Generated PDF file")
+    )
+
+    def __str__(self):
+        return f"{self.person} ({self.pk})"
+
+    @property
+    def secret(self) -> str:
+        """Get secret needed for accessing the HTML page."""
+        return hmac.new(
+            bytes(settings.SECRET_KEY, "utf-8"),
+            msg=bytes(self.html + str(self.expires_at), "utf-8"),
+            digestmod="sha256",
+        ).hexdigest()
+
+    @property
+    def html_url(self) -> str:
+        """Get URL for the HTML page."""
+        return (
+            urlparse(reverse("html_for_pdf_file", args=[self.pk]))
+            ._replace(query=f"secret={self.secret}")
+            .geturl()
+        )
+
+    class Meta:
+        verbose_name = _("PDF file")
+        verbose_name_plural = _("PDF files")
diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py
index 3df1a91a21c79f40ab0894d3bbc14a97da0b90fc..ed2a73623d3e3bff97f71ef01b86d9ebc9a82222 100644
--- a/aleksis/core/preferences.py
+++ b/aleksis/core/preferences.py
@@ -9,6 +9,7 @@ from dynamic_preferences.types import (
     BooleanPreference,
     ChoicePreference,
     FilePreference,
+    IntegerPreference,
     ModelMultipleChoicePreference,
     MultipleChoicePreference,
     StringPreference,
@@ -270,3 +271,14 @@ class DashboardEditing(BooleanPreference):
     default = True
     required = False
     verbose_name = _("Allow users to edit their dashboard")
+
+
+@site_preferences_registry.register
+class PDFFileExpirationDuration(IntegerPreference):
+    """PDF file expiration duration."""
+
+    section = general
+    name = "pdf_expiration"
+    default = 3
+    verbose_name = _("PDF file expiration duration")
+    help_text = _("in minutes")
diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py
index c5902d2a21e86691b12e81f87c060836dcd1a0fc..007cfb719d5eb566939bc04b86a2974b4d081e3a 100644
--- a/aleksis/core/rules.py
+++ b/aleksis/core/rules.py
@@ -321,3 +321,6 @@ rules.add_perm("core.edit_default_dashboard", edit_default_dashboard_predicate)
 # Upload and browse files via CKEditor
 upload_files_ckeditor_predicate = has_person & has_global_perm("core.upload_files_ckeditor")
 rules.add_perm("core.upload_files_ckeditor", upload_files_ckeditor_predicate)
+
+test_pdf_generation_predicate = has_person & has_global_perm("core.test_pdf")
+rules.add_perm("core.test_pdf", test_pdf_generation_predicate)
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index 27f36211a781fbe70a8961b5f51ce4c7c1d039d5..118fb17419a2e64bc06bc31ac06e75bcc13c8f37 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -245,7 +245,7 @@ INSTALLED_APPS.append("cachalot")
 DEBUG_TOOLBAR_PANELS.append("cachalot.panels.CachalotPanel")
 CACHALOT_TIMEOUT = _settings.get("caching.cachalot.timeout", None)
 CACHALOT_DATABASES = set(["default"])
-SILENCED_SYSTEM_CHECKS.append("cachalot.W001")
+SILENCED_SYSTEM_CHECKS += ["cachalot.W001", "cachalot.E003"]
 
 SESSION_ENGINE = "django.contrib.sessions.backends.cache"
 SESSION_CACHE_ALIAS = "default"
@@ -504,6 +504,13 @@ DBBACKUP_CONNECTOR_MAPPING = {
     "django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector",
 }
 
+if _settings.get("backup.storage.type", "").lower() == "s3":
+    DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
+
+    DBBACKUP_STORAGE_OPTIONS = {
+        key: value for (key, value) in _settings.get("backup.storage.s3").items()
+    }
+
 IMPERSONATE = {"USE_HTTP_REFERER": True, "REQUIRE_SUPERUSER": True, "ALLOW_SUPERUSER": True}
 
 DJANGO_TABLES2_TEMPLATE = "django_tables2/materialize.html"
@@ -785,7 +792,7 @@ MEDIABACKUP_CHECK_SECONDS = _settings.get("backup.media.check_seconds", 7200)
 
 PROMETHEUS_EXPORT_MIGRATIONS = False
 
-if _settings.get("storage.s3.enabled", False):
+if _settings.get("storage.type", "").lower() == "s3":
     INSTALLED_APPS.append("storages")
 
     DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
@@ -797,8 +804,8 @@ if _settings.get("storage.s3.enabled", False):
             "storage.s3.static.max_age_seconds", 24 * 60 * 60
         )
 
-    AWS_REGION = _settings.get("storage.s3.region", "")
-    AWS_ACCESS_KEY_ID = _settings.get("storage.s3.access_key_id", "")
+    AWS_REGION = _settings.get("storage.s3.region_name", "")
+    AWS_ACCESS_KEY_ID = _settings.get("storage.s3.access_key", "")
     AWS_SECRET_ACCESS_KEY = _settings.get("storage.s3.secret_key", "")
     AWS_SESSION_TOKEN = _settings.get("storage.s3.session_token", "")
     AWS_STORAGE_BUCKET_NAME = _settings.get("storage.s3.bucket_name", "")
@@ -821,3 +828,6 @@ else:
     DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage"
 
 SASS_PROCESSOR_STORAGE = DEFAULT_FILE_STORAGE
+
+# Add django-cleanup after all apps to ensure that it gets all signals as last app
+INSTALLED_APPS.append("django_cleanup.apps.CleanupConfig")
diff --git a/aleksis/core/static/js/progress.js b/aleksis/core/static/js/progress.js
index 4d4c0692f39e78af60bdc621544ba7a2c2b720c4..8b897096f18c221bde8fd34c88bd93f9800ff06d 100644
--- a/aleksis/core/static/js/progress.js
+++ b/aleksis/core/static/js/progress.js
@@ -47,6 +47,10 @@ function customSuccess(progressBarElement, progressBarMessageElement) {
     $("#result-icon").text("check_circle");
     $("#result-text").text(OPTIONS.success);
     $("#result-box").show();
+    const redirect = "redirect_on_success" in OPTIONS;
+    if (redirect) {
+        window.location.replace(OPTIONS.redirect_on_success);
+    }
 }
 
 function customError(progressBarElement, progressBarMessageElement) {
diff --git a/aleksis/core/static/print.css b/aleksis/core/static/print.css
index cd3eeeea0accb97cb9e2b48054b8ad033476b41c..cda82eacab1d51a39f7f455170eb6ff12121dc30 100644
--- a/aleksis/core/static/print.css
+++ b/aleksis/core/static/print.css
@@ -5,6 +5,7 @@
 @page {
     size: A4;
     padding: 30mm;
+    margin: 0;
 }
 
 header {
diff --git a/aleksis/core/templates/core/pages/progress.html b/aleksis/core/templates/core/pages/progress.html
index 6292fb0d243b1b2ecb72ff1cec7c3b6339409c18..799f1f93648ab1de05be3f9432653b8c4e8b1466 100644
--- a/aleksis/core/templates/core/pages/progress.html
+++ b/aleksis/core/templates/core/pages/progress.html
@@ -46,6 +46,12 @@
           <i class="material-icons left">arrow_back</i>
           {% trans "Go back" %}
         </a>
+        {% if additional_button %}
+          <a class="btn waves-effect waves-light" href="{{ additional_button.href }}">
+            <i class="material-icons left">{{ additional_button.icon|default:"" }}</i>
+            {{ additional_button.caption }}
+          </a>
+        {% endif %}
       </div>
     </div>
   </div>
diff --git a/aleksis/core/templates/core/pages/test_pdf.html b/aleksis/core/templates/core/pages/test_pdf.html
new file mode 100644
index 0000000000000000000000000000000000000000..f0c0a6169c85a6f37252136b3777dc0967350e43
--- /dev/null
+++ b/aleksis/core/templates/core/pages/test_pdf.html
@@ -0,0 +1,19 @@
+{# -*- engine:django -*- #}
+
+{% extends "core/base_print.html" %}
+
+{% load i18n %}
+
+{% block browser_title %}{% blocktrans %}Test PDF generation{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}Test PDF generation{% endblocktrans %}{% endblock %}
+
+{% block content %}
+  <div class="alert primary">
+    <p>
+      <i class="material-icons left">info</i>
+      {% blocktrans %}
+        This simple view can be used to ensure the correct function of the built-in PDF generation system.
+      {% endblocktrans %}
+    </p>
+  </div>
+{% endblock %}
diff --git a/aleksis/core/tests/browser/test_selenium.py b/aleksis/core/tests/browser/test_selenium.py
index a4861d6c18797f7d3e6ed37fa221a2ea1753b96b..8afd4b1584e3b5e6f35d69b2df73625ad8da5fad 100644
--- a/aleksis/core/tests/browser/test_selenium.py
+++ b/aleksis/core/tests/browser/test_selenium.py
@@ -1,10 +1,15 @@
 import os
 
 from django.conf import settings
+from django.contrib.auth.models import User
+from django.test import override_settings
 from django.test.selenium import SeleniumTestCase, SeleniumTestCaseBase
 from django.urls import reverse
 
 import pytest
+from selenium.webdriver.support.wait import WebDriverWait
+
+from aleksis.core.models import Person
 
 pytestmark = pytest.mark.django_db
 
@@ -15,6 +20,8 @@ SeleniumTestCaseBase.browsers = list(
 SeleniumTestCaseBase.selenium_hub = os.environ.get("TEST_SELENIUM_HUB", "") or None
 
 
+@pytest.mark.usefixtures("celery_worker")
+@override_settings(CELERY_BROKER_URL="memory://localhost//")
 class SeleniumTests(SeleniumTestCase):
     serialized_rollback = True
 
@@ -29,18 +36,11 @@ class SeleniumTests(SeleniumTestCase):
         else:
             return False
 
-    def test_index(self):
-        self.selenium.get(self.live_server_url + "/")
-        assert "AlekSIS" in self.selenium.title
-        self._screenshot("index.png")
-
-    def test_login_default_superuser(self):
-        username = "admin"
-        password = "admin"
-
+    def _login(self, username="admin", password="admin", with_screenshots=False):
         # Navigate to configured login page
         self.selenium.get(self.live_server_url + reverse(settings.LOGIN_URL))
-        self._screenshot("login_default_superuser_blank.png")
+        if with_screenshots:
+            self._screenshot("login_default_superuser_blank.png")
 
         # Find login form input fields and enter defined credentials
         self.selenium.find_element_by_xpath(
@@ -49,11 +49,33 @@ class SeleniumTests(SeleniumTestCase):
         self.selenium.find_element_by_xpath(
             '//label[contains(text(), "Password")]/../input'
         ).send_keys(password)
-        self._screenshot("login_default_superuser_filled.png")
+        if with_screenshots:
+            self._screenshot("login_default_superuser_filled.png")
 
         # Submit form by clicking django-two-factor-auth's Next button
         self.selenium.find_element_by_xpath('//button[contains(text(), "Login")]').click()
-        self._screenshot("login_default_superuser_submitted.png")
+        if with_screenshots:
+            self._screenshot("login_default_superuser_submitted.png")
+
+    def _create_person(self, username="admin"):
+        user = User.objects.get(username=username)
+        person = Person.objects.create(user=user, first_name="Jane", last_name="Doe")
+        return person
+
+    def test_index(self):
+        self.selenium.get(self.live_server_url + "/")
+        assert "AlekSIS" in self.selenium.title
+        self._screenshot("index.png")
+
+    def test_login_default_superuser(self):
+        self._login("admin", "admin", with_screenshots=True)
 
         # Should redirect away from login page and not put up an alert about wrong credentials
         assert "Please enter a correct username and password." not in self.selenium.page_source
+
+    def test_pdf_generation(self):
+        self._login()
+        self._create_person()
+        self.selenium.get(self.live_server_url + reverse("test_pdf"))
+        el = WebDriverWait(self.selenium, 10).until(lambda d: ".pdf" in self.selenium.current_url)
+        self._screenshot("pdf.png")
diff --git a/aleksis/core/tests/models/test.pdf b/aleksis/core/tests/models/test.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..4e6c1457f663de82eb95a8613f4626b84146dd45
Binary files /dev/null and b/aleksis/core/tests/models/test.pdf differ
diff --git a/aleksis/core/tests/models/test_pdffile.py b/aleksis/core/tests/models/test_pdffile.py
new file mode 100644
index 0000000000000000000000000000000000000000..778ff8b5676a93279f9f9261e9c27ccbe6d85939
--- /dev/null
+++ b/aleksis/core/tests/models/test_pdffile.py
@@ -0,0 +1,102 @@
+import os
+import re
+from datetime import datetime, timedelta
+
+from django.core.files import File
+from django.core.files.storage import default_storage
+from django.template.loader import render_to_string
+from django.test import TransactionTestCase, override_settings
+from django.utils import timezone
+
+import freezegun
+import pytest
+
+from aleksis.core.models import PDFFile, Person
+from aleksis.core.util.pdf import clean_up_expired_pdf_files
+
+pytestmark = pytest.mark.django_db
+
+
+@pytest.mark.usefixtures("celery_worker")
+@override_settings(CELERY_BROKER_URL="memory://localhost//")
+class PDFFIleTest(TransactionTestCase):
+    serialized_rollback = True
+
+    _test_pdf = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test.pdf")
+
+    def _get_test_html(self):
+        return render_to_string("core/pages/test_pdf.html")
+
+    def test_pdf_file(self):
+        dummy_person = Person.objects.create(first_name="Jane", last_name="Doe")
+
+        html = self._get_test_html()
+        assert "html" in html
+        file_object = PDFFile.objects.create(person=dummy_person, html=html)
+        assert isinstance(file_object.expires_at, datetime)
+        assert file_object.expires_at > timezone.now()
+        assert not bool(file_object.file)
+
+        with open(self._test_pdf, "rb") as f:
+            file_object.file.save("print.pdf", File(f))
+        file_object.save()
+        re_base = r"pdfs/[a-zA-Z0-9]+\.pdf"
+        assert re.match(re_base, file_object.file.name)
+
+    def test_delete_signal(self):
+        dummy_person = Person.objects.create(first_name="Jane", last_name="Doe")
+        file_object = PDFFile.objects.create(person=dummy_person, html=self._get_test_html())
+        with open(self._test_pdf, "rb") as f:
+            file_object.file.save("print.pdf", File(f))
+        file_object.save()
+
+        file_path = file_object.file.path
+
+        assert default_storage.exists(file_path)
+        file_object.delete()
+        assert not default_storage.exists(file_path)
+
+    def test_delete_expired_files(self):
+        # Create test instances
+        dummy_person = Person.objects.create(first_name="Jane", last_name="Doe")
+        file_object = PDFFile.objects.create(person=dummy_person, html=self._get_test_html())
+        file_object2 = PDFFile.objects.create(
+            person=dummy_person,
+            html=self._get_test_html(),
+            expires_at=timezone.now() + timedelta(minutes=10),
+        )
+        with open(self._test_pdf, "rb") as f:
+            file_object.file.save("print.pdf", File(f))
+            file_object2.file.save("print.pdf", File(f))
+        file_object.save()
+        file_object2.save()
+
+        clean_up_expired_pdf_files()
+        assert PDFFile.objects.get(pk=file_object.pk)
+        assert PDFFile.objects.get(pk=file_object2.pk)
+
+        # Prepare times
+        test_time_before = timezone.now() + timedelta(minutes=2.5)
+        test_time_between = timezone.now() + timedelta(minutes=4)
+        test_time_after = timezone.now() + timedelta(minutes=15)
+
+        # None of the files are expired
+        with freezegun.freeze_time(test_time_before):
+            clean_up_expired_pdf_files()
+            assert PDFFile.objects.get(pk=file_object.pk)
+            assert PDFFile.objects.get(pk=file_object2.pk)
+
+        # One file is expired
+        with freezegun.freeze_time(test_time_between):
+            clean_up_expired_pdf_files()
+            with pytest.raises(PDFFile.DoesNotExist):
+                PDFFile.objects.get(pk=file_object.pk)
+            assert PDFFile.objects.get(pk=file_object2.pk)
+
+        # Both files are expired
+        with freezegun.freeze_time(test_time_after):
+            clean_up_expired_pdf_files()
+            with pytest.raises(PDFFile.DoesNotExist):
+                PDFFile.objects.get(pk=file_object.pk)
+            with pytest.raises(PDFFile.DoesNotExist):
+                PDFFile.objects.get(pk=file_object2.pk)
diff --git a/aleksis/core/tests/util/test_core_helpers.py b/aleksis/core/tests/util/test_core_helpers.py
deleted file mode 100644
index d018ddba3b610251aafcf47698f7669c49ef8fef..0000000000000000000000000000000000000000
--- a/aleksis/core/tests/util/test_core_helpers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import re
-
-from aleksis.core.util.core_helpers import path_and_rename
-
-
-def test_path_and_rename():
-    re_base = r"[a-z0-9]+"
-
-    example_1 = "sdjaasjkl.jpg"
-    re_example_1 = "files/" + re_base + r"\.jpg"
-    re2_example_1 = "images/" + re_base + r"\.jpg"
-    assert re.match(re_example_1, path_and_rename(None, example_1))
-    assert re.match(re2_example_1, path_and_rename(None, example_1, upload_to="images"))
-
-    example_2 = "sdjaasjkl"
-    re_example_2 = "files/" + re_base
-    re2_example_2 = "images/" + re_base
-    assert re.match(re_example_2, path_and_rename(None, example_2))
-    assert re.match(re2_example_2, path_and_rename(None, example_2, upload_to="images"))
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index f966c8dac14c021b606f2c1a3a3cd71645283203..ff33d92ec879052bdab33b861c2d03d1e1e8cdaa 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -167,6 +167,7 @@ urlpatterns = [
         name="preferences_group",
     ),
     path("health/", include(health_urls)),
+    path("health/pdf/", views.TestPDFGenerationView.as_view(), name="test_pdf"),
     path("data_check/", views.DataCheckView.as_view(), name="check_data",),
     path("data_check/run/", views.RunDataChecks.as_view(), name="data_check_run",),
     path(
@@ -196,6 +197,8 @@ urlpatterns = [
         {"default": True},
         name="edit_default_dashboard",
     ),
+    path("pdfs/<int:pk>/", views.RedirectToPDFFile.as_view(), name="redirect_to_pdf_file"),
+    path("pdfs/<int:pk>/html/", views.HTMLForPDFFile.as_view(), name="html_for_pdf_file"),
 ]
 
 # Add URLs for optional features
diff --git a/aleksis/core/util/apps.py b/aleksis/core/util/apps.py
index d7539c22616abd2d76013546acb15b592862ceb4..a2a6ced0e87b610dde1d74efe57b0fdf46f969c9 100644
--- a/aleksis/core/util/apps.py
+++ b/aleksis/core/util/apps.py
@@ -186,8 +186,6 @@ class AppConfig(django.apps.AppConfig):
         verbosity: int,
         interactive: bool,
         using: str,
-        plan: List[Tuple],
-        apps: django.apps.registry.Apps,
         **kwargs,
     ) -> None:
         """Call on every app instance after its models have been migrated.
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index 676894d28d62f32ee9c08de874edf6857f94a629..69350b13967b0cf1d57a6fc6307adcbc90725fe6 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -1,11 +1,9 @@
-import os
 import sys
 from datetime import datetime, timedelta
 from importlib import import_module
 from itertools import groupby
 from operator import itemgetter
 from typing import Any, Callable, Optional, Sequence, Union
-from uuid import uuid4
 
 if sys.version_info >= (3, 9):
     from importlib import metadata
@@ -183,20 +181,6 @@ def has_person(obj: Union[HttpRequest, Model]) -> bool:
         return True
 
 
-def path_and_rename(instance, filename: str, upload_to: str = "files") -> str:
-    """Update path of an uploaded file and renames it to a random UUID in Django FileField."""
-    _, ext = os.path.splitext(filename)
-
-    # set filename as random string
-    new_filename = f"{uuid4().hex}{ext}"
-
-    # Create upload directory if necessary
-    os.makedirs(os.path.join(settings.MEDIA_ROOT, upload_to), exist_ok=True)
-
-    # return the whole path to the file
-    return os.path.join(upload_to, new_filename)
-
-
 def custom_information_processor(request: HttpRequest) -> dict:
     """Provide custom information in all templates."""
     from ..models import CustomMenu
diff --git a/aleksis/core/util/pdf.py b/aleksis/core/util/pdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..18640a0e8667d91b7c31b8130b597e7992985e8d
--- /dev/null
+++ b/aleksis/core/util/pdf.py
@@ -0,0 +1,112 @@
+import os
+import subprocess  # noqa
+from tempfile import TemporaryDirectory
+from typing import Optional
+
+from django.core.files import File
+from django.http.request import HttpRequest
+from django.http.response import HttpResponse
+from django.shortcuts import get_object_or_404, render
+from django.template.loader import render_to_string
+from django.urls import reverse
+from django.utils import timezone
+from django.utils.translation import get_language
+from django.utils.translation import gettext as _
+
+from celery_progress.backend import ProgressRecorder
+
+from aleksis.core.celery import app
+from aleksis.core.models import PDFFile
+from aleksis.core.util.celery_progress import recorded_task
+
+
+@recorded_task
+def generate_pdf(
+    file_pk: int, html_url: str, recorder: ProgressRecorder, lang: Optional[str] = None
+):
+    """Generate a PDF file by rendering the HTML code using a headless Chromium."""
+    file_object = get_object_or_404(PDFFile, pk=file_pk)
+
+    recorder.set_progress(0, 1)
+
+    # Open a temporary directory
+    with TemporaryDirectory() as temp_dir:
+        pdf_path = os.path.join(temp_dir, "print.pdf")
+        lang = lang or get_language()
+
+        # Run PDF generation using a headless Chromium
+        cmd = [
+            "chromium",
+            "--headless",
+            "--no-sandbox",
+            "--run-all-compositor-stages-before-draw",
+            "--temp-profile",
+            "--disable-dev-shm-usage",
+            "--disable-gpu",
+            "--disable-setuid-sandbox",
+            "--dbus-stub",
+            f"--home-dir={temp_dir}",
+            f"--lang={lang}",
+            f"--print-to-pdf={pdf_path}",
+            html_url,
+        ]
+        res = subprocess.run(cmd)  # noqa
+
+        # Let the task fail on a non-success return code
+        res.check_returncode()
+
+        # Upload PDF file to media storage
+        with open(pdf_path, "rb") as f:
+            file_object.file.save("print.pdf", File(f))
+            file_object.save()
+
+    recorder.set_progress(1, 1)
+
+
+def render_pdf(request: HttpRequest, template_name: str, context: dict = None) -> HttpResponse:
+    """Start PDF generation and show progress page.
+
+    The progress page will redirect to the PDF after completion.
+    """
+    if not context:
+        context = {}
+
+    html_template = render_to_string(template_name, context)
+
+    file_object = PDFFile.objects.create(person=request.user.person, html=html_template)
+    html_url = request.build_absolute_uri(file_object.html_url)
+
+    result = generate_pdf.delay(file_object.pk, html_url, lang=get_language())
+
+    redirect_url = reverse("redirect_to_pdf_file", args=[file_object.pk])
+
+    progress_context = {
+        "title": _("Progress: Generate PDF file"),
+        "back_url": context.get("back_url", "index"),
+        "progress": {
+            "task_id": result.task_id,
+            "title": _("Generating PDF file …"),
+            "success": _("The PDF file has been generated successfully."),
+            "error": _("There was a problem while generating the PDF file."),
+            "redirect_on_success": redirect_url,
+        },
+        "additional_button": {
+            "href": redirect_url,
+            "caption": _("Download PDF"),
+            "icon": "picture_as_pdf",
+        },
+    }
+
+    # Render progress view
+    return render(request, "core/pages/progress.html", progress_context)
+
+
+def clean_up_expired_pdf_files() -> None:
+    """Clean up expired PDF files."""
+    PDFFile.objects.filter(expires_at__lt=timezone.now()).delete()
+
+
+@app.task
+def clean_up_expired_pdf_files_task() -> None:
+    """Clean up expired PDF files."""
+    return clean_up_expired_pdf_files()
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 0eb64c62cdc8368e664d3a2a031d7d6dc5d003f2..6e18c5683543e4f713fdf758973da6ae53961461 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -6,14 +6,14 @@ from django.core.exceptions import PermissionDenied
 from django.core.paginator import Paginator
 from django.db.models import QuerySet
 from django.forms.models import BaseModelForm, modelform_factory
-from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
+from django.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse, reverse_lazy
 from django.utils.decorators import method_decorator
 from django.utils.translation import gettext_lazy as _
 from django.views.decorators.cache import never_cache
-from django.views.generic.base import View
-from django.views.generic.detail import DetailView
+from django.views.generic.base import TemplateView, View
+from django.views.generic.detail import DetailView, SingleObjectMixin
 from django.views.generic.list import ListView
 
 import reversion
@@ -58,6 +58,7 @@ from .models import (
     Group,
     GroupType,
     Notification,
+    PDFFile,
     Person,
     SchoolTerm,
 )
@@ -78,6 +79,18 @@ from .util import messages
 from .util.apps import AppConfig
 from .util.core_helpers import has_person, objectgetter_optional
 from .util.forms import PreferenceLayout
+from .util.pdf import render_pdf
+
+
+class RenderPDFView(TemplateView):
+    """View to render a PDF file from a template.
+
+    Makes use of ``render_pdf``.
+    """
+
+    def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
+        context = self.get_context_data(**kwargs)
+        return render_pdf(request, self.template_name, context)
 
 
 @permission_required("core.view_dashboard")
@@ -429,6 +442,11 @@ class SystemStatus(PermissionRequiredMixin, MainView):
         return self.render_to_response(context, status=status_code)
 
 
+class TestPDFGenerationView(PermissionRequiredMixin, RenderPDFView):
+    template_name = "core/pages/test_pdf.html"
+    permission_required = "core.test_pdf"
+
+
 @permission_required(
     "core.mark_notification_as_read", fn=objectgetter_optional(Notification, None, False)
 )
@@ -941,3 +959,27 @@ class EditDashboardView(PermissionRequiredMixin, View):
         context = self.get_context_data(request, **kwargs)
 
         return render(request, "core/edit_dashboard.html", context=context)
+
+
+class RedirectToPDFFile(SingleObjectMixin, View):
+    """Redirect to a generated PDF file."""
+
+    model = PDFFile
+
+    def get(self, *args, **kwargs):
+        file_object = self.get_object()
+        if not file_object.file:
+            raise Http404()
+        return redirect(file_object.file.url)
+
+
+class HTMLForPDFFile(SingleObjectMixin, View):
+    """Return rendered HTML for generating a PDF file."""
+
+    model = PDFFile
+
+    def get(self, request, *args, **kwargs):
+        file_object = self.get_object()
+        if request.GET.get("secret") != file_object.secret:
+            raise PermissionDenied()
+        return HttpResponse(file_object.html)
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..2aa286cdb37550088652b10cced7bfc3d3b56aa9
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1 @@
+pytest_plugins = ("celery.contrib.pytest", )
diff --git a/docs/dev/01_setup.rst b/docs/dev/01_setup.rst
index 31c72ee6c8bcaea746291ab038318fa614f0103d..122a9baa14ce942886bd8d1a73fc5584f77cad19 100644
--- a/docs/dev/01_setup.rst
+++ b/docs/dev/01_setup.rst
@@ -28,8 +28,7 @@ Install native dependencies
 
 Some system libraries are required to install AlekSIS::
 
-  sudo apt install build-essential libpq-dev libpq5 libssl-dev python3-dev python3-pip python3-venv yarnpkg gettext
-
+  sudo apt install build-essential libpq-dev libpq5 libssl-dev python3-dev python3-pip python3-venv yarnpkg gettext chromium
 
 Get Poetry
 ----------
diff --git a/poetry.lock b/poetry.lock
index 763c045d21d7e9941576e0280cca4eeb5bb93815..84dacc77cc249c53de96386e61ba7f55f3d5074a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -8,11 +8,11 @@ python-versions = "*"
 
 [[package]]
 name = "aleksis-builddeps"
-version = "2"
+version = "3"
 description = "AlekSIS (School Information System) — Build/Dev dependencies for apps"
 category = "dev"
 optional = false
-python-versions = "*"
+python-versions = ">=3.6,<4.0"
 
 [package.dependencies]
 black = ">=19.10b0,<20.0"
@@ -27,7 +27,8 @@ flake8-docstrings = ">=1.5.0,<2.0.0"
 flake8-fixme = ">=1.1.1,<2.0.0"
 flake8-isort = ">=4.0.0,<5.0.0"
 flake8-mypy = ">=17.8.0,<18.0.0"
-flake8-rst-docstrings = ">=0.0.14,<0.0.15"
+flake8-rst-docstrings = ">=0.1.0,<0.2.0"
+freezegun = ">=1.1.0,<2.0.0"
 isort = ">=5.0.0,<6.0.0"
 pytest = ">=6.0,<7.0"
 pytest-cov = ">=2.8.1,<3.0.0"
@@ -74,14 +75,17 @@ python-versions = "*"
 
 [[package]]
 name = "asgiref"
-version = "3.3.1"
+version = "3.3.4"
 description = "ASGI specs, helper code, and adapters"
 category = "main"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
+
+[package.dependencies]
+typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
 
 [package.extras]
-tests = ["pytest", "pytest-asyncio"]
+tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
 
 [[package]]
 name = "asn1crypto"
@@ -213,20 +217,20 @@ python-versions = "*"
 
 [[package]]
 name = "boto3"
-version = "1.17.43"
+version = "1.17.53"
 description = "The AWS SDK for Python"
 category = "main"
 optional = true
 python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 
 [package.dependencies]
-botocore = ">=1.20.43,<1.21.0"
+botocore = ">=1.20.53,<1.21.0"
 jmespath = ">=0.7.1,<1.0.0"
 s3transfer = ">=0.3.0,<0.4.0"
 
 [[package]]
 name = "botocore"
-version = "1.20.43"
+version = "1.20.53"
 description = "Low-level, data-driven core of boto 3."
 category = "main"
 optional = true
@@ -465,7 +469,7 @@ dev = ["black (==19.10b0)", "flake8 (==3.8.4)", "mypy (==0.812)", "pytest (==6.2
 
 [[package]]
 name = "decorator"
-version = "5.0.1"
+version = "5.0.7"
 description = "Decorators for Humans"
 category = "main"
 optional = false
@@ -481,19 +485,19 @@ python-versions = "*"
 
 [[package]]
 name = "django"
-version = "3.1.7"
+version = "3.2"
 description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
 category = "main"
 optional = false
 python-versions = ">=3.6"
 
 [package.dependencies]
-asgiref = ">=3.2.10,<4"
+asgiref = ">=3.3.2,<4"
 pytz = "*"
 sqlparse = ">=0.2.2"
 
 [package.extras]
-argon2 = ["argon2-cffi (>=16.1.0)"]
+argon2 = ["argon2-cffi (>=19.1.0)"]
 bcrypt = ["bcrypt"]
 
 [[package]]
@@ -520,7 +524,7 @@ django = "*"
 
 [[package]]
 name = "django-auth-ldap"
-version = "2.3.0"
+version = "2.4.0"
 description = "Django LDAP authentication backend."
 category = "main"
 optional = true
@@ -624,6 +628,14 @@ python-versions = "*"
 [package.dependencies]
 django-js-asset = ">=1.2.2"
 
+[[package]]
+name = "django-cleanup"
+version = "5.1.0"
+description = "Deletes old files."
+category = "main"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "django-colorfield"
 version = "0.4.1"
@@ -647,7 +659,7 @@ six = "*"
 
 [[package]]
 name = "django-debug-toolbar"
-version = "3.2"
+version = "3.2.1"
 description = "A configurable set of panels that display various debug information about the current request/response."
 category = "main"
 optional = false
@@ -672,12 +684,15 @@ six = "*"
 
 [[package]]
 name = "django-extensions"
-version = "3.1.1"
+version = "3.1.2"
 description = "Extensions for Django"
 category = "main"
 optional = false
 python-versions = ">=3.6"
 
+[package.dependencies]
+Django = ">=2.2"
+
 [[package]]
 name = "django-favicon-plus-reloaded"
 version = "1.0.4"
@@ -858,7 +873,7 @@ Django = ">=2.0.1"
 
 [[package]]
 name = "django-otp"
-version = "1.0.2"
+version = "1.0.3"
 description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords."
 category = "main"
 optional = false
@@ -872,7 +887,7 @@ qrcode = ["qrcode"]
 
 [[package]]
 name = "django-otp-yubikey"
-version = "1.0.0"
+version = "1.0.0.post1"
 description = "A django-otp plugin that verifies YubiKey OTP tokens."
 category = "main"
 optional = false
@@ -884,11 +899,11 @@ YubiOTP = ">=0.2.2"
 
 [[package]]
 name = "django-phonenumber-field"
-version = "5.0.0"
+version = "5.1.0"
 description = "An international phone number field for django models."
 category = "main"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
 
 [package.dependencies]
 Django = ">=2.2"
@@ -967,7 +982,7 @@ django = ">=1.11"
 
 [[package]]
 name = "django-sass-processor"
-version = "1.0.0"
+version = "1.0.1"
 description = "SASS processor to compile SCSS files into *.css, while rendering, or offline."
 category = "main"
 optional = false
@@ -978,7 +993,7 @@ management_command = ["django-compressor (>=2.4)"]
 
 [[package]]
 name = "django-select2"
-version = "7.7.0"
+version = "7.7.1"
 description = "Select2 option fields for Django"
 category = "main"
 optional = false
@@ -1020,7 +1035,7 @@ sftp = ["paramiko"]
 
 [[package]]
 name = "django-stubs"
-version = "1.7.0"
+version = "1.8.0"
 description = "Mypy stubs for Django"
 category = "dev"
 optional = false
@@ -1028,9 +1043,21 @@ python-versions = ">=3.6"
 
 [package.dependencies]
 django = "*"
+django-stubs-ext = "*"
 mypy = ">=0.790"
 typing-extensions = "*"
 
+[[package]]
+name = "django-stubs-ext"
+version = "0.2.0"
+description = "Monkey-patching and extensions for django-stubs"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+django = "*"
+
 [[package]]
 name = "django-tables2"
 version = "2.3.4"
@@ -1176,7 +1203,7 @@ yaml = ["ruamel.yaml"]
 
 [[package]]
 name = "faker"
-version = "7.0.1"
+version = "8.1.0"
 description = "Faker is a Python package that generates fake data for you."
 category = "main"
 optional = false
@@ -1188,7 +1215,7 @@ text-unidecode = "1.3"
 
 [[package]]
 name = "flake8"
-version = "3.9.0"
+version = "3.9.1"
 description = "the modular source code checker: pep8 pyflakes and co"
 category = "dev"
 optional = false
@@ -1313,15 +1340,28 @@ flake8 = "*"
 
 [[package]]
 name = "flake8-rst-docstrings"
-version = "0.0.14"
+version = "0.1.2"
 description = "Python docstring reStructuredText (RST) validator"
 category = "dev"
 optional = false
-python-versions = "*"
+python-versions = ">=3.3"
 
 [package.dependencies]
 flake8 = ">=3.0.0"
-restructuredtext_lint = "*"
+pydocstyle = ">=3.0.0"
+pygments = "*"
+restructuredtext-lint = "*"
+
+[[package]]
+name = "freezegun"
+version = "1.1.0"
+description = "Let your Python tests travel through time"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+python-dateutil = ">=2.7"
 
 [[package]]
 name = "gitdb"
@@ -1371,7 +1411,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [[package]]
 name = "importlib-metadata"
-version = "3.10.0"
+version = "3.10.1"
 description = "Read metadata from Python packages"
 category = "main"
 optional = false
@@ -1640,18 +1680,18 @@ ptyprocess = ">=0.5"
 
 [[package]]
 name = "pg8000"
-version = "1.19.0"
+version = "1.19.2"
 description = "PostgreSQL interface library"
 category = "dev"
 optional = false
 python-versions = ">=3.6"
 
 [package.dependencies]
-scramp = "1.3.0"
+scramp = "1.4.0"
 
 [[package]]
 name = "phonenumbers"
-version = "8.12.20"
+version = "8.12.21"
 description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
 category = "main"
 optional = false
@@ -1689,11 +1729,11 @@ dev = ["pre-commit", "tox"]
 
 [[package]]
 name = "prometheus-client"
-version = "0.9.0"
+version = "0.10.1"
 description = "Python client for the Prometheus monitoring system."
 category = "main"
 optional = false
-python-versions = "*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [package.extras]
 twisted = ["twisted"]
@@ -1829,7 +1869,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
 
 [[package]]
 name = "pytest"
-version = "6.2.2"
+version = "6.2.3"
 description = "pytest: simple powerful testing with Python"
 category = "dev"
 optional = false
@@ -1866,7 +1906,7 @@ testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist",
 
 [[package]]
 name = "pytest-django"
-version = "4.1.0"
+version = "4.2.0"
 description = "A Django plugin for pytest."
 category = "dev"
 optional = false
@@ -1989,7 +2029,7 @@ hiredis = ["hiredis (>=0.1.3)"]
 
 [[package]]
 name = "regex"
-version = "2021.3.17"
+version = "2021.4.4"
 description = "Alternative regular expression module, to replace re."
 category = "dev"
 optional = false
@@ -2026,7 +2066,7 @@ docutils = ">=0.11,<1.0"
 
 [[package]]
 name = "ruamel.yaml"
-version = "0.17.2"
+version = "0.17.4"
 description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
 category = "main"
 optional = false
@@ -2057,7 +2097,7 @@ python-versions = "*"
 
 [[package]]
 name = "s3transfer"
-version = "0.3.6"
+version = "0.3.7"
 description = "An Amazon S3 Transfer Manager"
 category = "main"
 optional = true
@@ -2082,7 +2122,7 @@ requests = "*"
 
 [[package]]
 name = "scramp"
-version = "1.3.0"
+version = "1.4.0"
 description = "An implementation of the SCRAM protocol."
 category = "dev"
 optional = false
@@ -2144,7 +2184,7 @@ python-versions = "*"
 
 [[package]]
 name = "sphinx"
-version = "3.5.3"
+version = "3.5.4"
 description = "Python documentation generator"
 category = "dev"
 optional = false
@@ -2154,7 +2194,7 @@ python-versions = ">=3.5"
 alabaster = ">=0.7,<0.8"
 babel = ">=1.3"
 colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
-docutils = ">=0.12"
+docutils = ">=0.12,<0.17"
 imagesize = "*"
 Jinja2 = ">=2.3"
 packaging = "*"
@@ -2175,11 +2215,11 @@ test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]
 
 [[package]]
 name = "sphinx-autodoc-typehints"
-version = "1.11.1"
+version = "1.12.0"
 description = "Type hints (PEP 484) support for the Sphinx autodoc extension"
 category = "dev"
 optional = false
-python-versions = ">=3.5.2"
+python-versions = ">=3.6"
 
 [package.dependencies]
 Sphinx = ">=3.0"
@@ -2352,7 +2392,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
 
 [[package]]
 name = "tqdm"
-version = "4.59.0"
+version = "4.60.0"
 description = "Fast, Extensible Progress Meter"
 category = "main"
 optional = false
@@ -2379,7 +2419,7 @@ test = ["pytest"]
 
 [[package]]
 name = "twilio"
-version = "6.55.0"
+version = "6.56.0"
 description = "Twilio API client and TwiML generator"
 category = "main"
 optional = false
@@ -2393,7 +2433,7 @@ six = "*"
 
 [[package]]
 name = "typed-ast"
-version = "1.4.2"
+version = "1.4.3"
 description = "a fork of Python 2 and 3 ast modules with type comment support"
 category = "dev"
 optional = false
@@ -2482,7 +2522,7 @@ s3 = ["boto3", "django-storages"]
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.7"
-content-hash = "a3f02609a3a30844d85bafab55bf2ba68ae6c3a593c47be378452e1d294348d1"
+content-hash = "ee97afe83a2701ba6294b25fac3f7988908fff01dbaaab8ef2a966ed22cda388"
 
 [metadata.files]
 alabaster = [
@@ -2490,7 +2530,7 @@ alabaster = [
     {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
 ]
 aleksis-builddeps = [
-    {file = "AlekSIS-Builddeps-2.tar.gz", hash = "sha256:fdf8b230ba4a690c279d99004316e84d7d9d72962768ca6b3205df54db9abaab"},
+    {file = "AlekSIS-Builddeps-3.tar.gz", hash = "sha256:04597e29a861e576d78adc068c9f1bf85450c81dc43cb1f7db0ed43e975b64a2"},
 ]
 amqp = [
     {file = "amqp-5.0.6-py3-none-any.whl", hash = "sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"},
@@ -2505,8 +2545,8 @@ appnope = [
     {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
 ]
 asgiref = [
-    {file = "asgiref-3.3.1-py3-none-any.whl", hash = "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17"},
-    {file = "asgiref-3.3.1.tar.gz", hash = "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"},
+    {file = "asgiref-3.3.4-py3-none-any.whl", hash = "sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee"},
+    {file = "asgiref-3.3.4.tar.gz", hash = "sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"},
 ]
 asn1crypto = [
     {file = "asn1crypto-1.4.0-py2.py3-none-any.whl", hash = "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8"},
@@ -2554,12 +2594,12 @@ bleach = [
     {file = "boolean.py-3.8.tar.gz", hash = "sha256:cc24e20f985d60cd4a3a5a1c0956dd12611159d32a75081dabd0c9ab981acaa4"},
 ]
 boto3 = [
-    {file = "boto3-1.17.43-py2.py3-none-any.whl", hash = "sha256:66c5581ae0746a41b8e119522a5c2434cffca0ef2620d49f15f350d0b14e2dd0"},
-    {file = "boto3-1.17.43.tar.gz", hash = "sha256:b86aaa96ccbdba1aaca0a321dbe4be480a39b7d6fe85dcd7ee5d7c44c9b32726"},
+    {file = "boto3-1.17.53-py2.py3-none-any.whl", hash = "sha256:3bf3305571f3c8b738a53e9e7dcff59137dffe94670046c084a17f9fa4599ff3"},
+    {file = "boto3-1.17.53.tar.gz", hash = "sha256:1d26f6e7ae3c940cb07119077ac42485dcf99164350da0ab50d0f5ad345800cd"},
 ]
 botocore = [
-    {file = "botocore-1.20.43-py2.py3-none-any.whl", hash = "sha256:7d0326fd8f8d7b3b7d2e8ecc4edb5a39ecd5537072bd0f088767ca53dd34818a"},
-    {file = "botocore-1.20.43.tar.gz", hash = "sha256:7630d734260babdc34dc53c704d937f53c362bb15ccd3863ccf1b1b26be04df8"},
+    {file = "botocore-1.20.53-py2.py3-none-any.whl", hash = "sha256:d5e70d17b91c9b5867be7d6de0caa7dde9ed789bed62f03ea9b60718dc9350bf"},
+    {file = "botocore-1.20.53.tar.gz", hash = "sha256:e303500c4e80f6a706602da53daa6f751cfa8f491665c99a24ee732ab6321573"},
 ]
 bs4 = [
     {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"},
@@ -2672,16 +2712,16 @@ curlylint = [
     {file = "curlylint-0.12.2.tar.gz", hash = "sha256:76b557cf8d007bd92df2dae61a02e65f8aa2ff3e05c6398b1314d92692fbb0d8"},
 ]
 decorator = [
-    {file = "decorator-5.0.1-py2.py3-none-any.whl", hash = "sha256:2ec6e8cce2d71850b0af58ceceeab83f4bbaf60e1cc23d96db08c9d1425b7ac0"},
-    {file = "decorator-5.0.1.tar.gz", hash = "sha256:1e53162e016f317a61d93848f00e80e7109ca9ed06846c7f2930cf0ebede7c6c"},
+    {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"},
+    {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"},
 ]
 dj-database-url = [
     {file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"},
     {file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"},
 ]
 django = [
-    {file = "Django-3.1.7-py3-none-any.whl", hash = "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"},
-    {file = "Django-3.1.7.tar.gz", hash = "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7"},
+    {file = "Django-3.2-py3-none-any.whl", hash = "sha256:0604e84c4fb698a5e53e5857b5aea945b2f19a18f25f10b8748dbdf935788927"},
+    {file = "Django-3.2.tar.gz", hash = "sha256:21f0f9643722675976004eb683c55d33c05486f94506672df3d6a141546f389d"},
 ]
 django-any-js = [
     {file = "django-any-js-1.0.3.post1.tar.gz", hash = "sha256:32306643d4989b3cdbbf6a87bb43ca4d5ca35863c96ad96a8bc0d50bcf9d4ab4"},
@@ -2691,8 +2731,8 @@ django-appconf = [
     {file = "django_appconf-1.0.4-py2.py3-none-any.whl", hash = "sha256:1b1d0e1069c843ebe8ae5aa48ec52403b1440402b320c3e3a206a0907e97bb06"},
 ]
 django-auth-ldap = [
-    {file = "django-auth-ldap-2.3.0.tar.gz", hash = "sha256:5894317122a086c9955ed366562869a81459cf6b663636b152857bb5d3a0a3b7"},
-    {file = "django_auth_ldap-2.3.0-py3-none-any.whl", hash = "sha256:cbbb476eff2504b5ab4fdf1fa92d93d2d3408fd9c8bc0c426169d987d0733153"},
+    {file = "django-auth-ldap-2.4.0.tar.gz", hash = "sha256:60fcbfc3141c99c3c49d3ccd7311a3992a231c319d94b6d2c143968f63676676"},
+    {file = "django_auth_ldap-2.4.0-py3-none-any.whl", hash = "sha256:2d869955da8a0c9a4448671bd9826b9f87458f6a9fc20278e84de8a81200a2be"},
 ]
 django-bleach = [
     {file = "django-bleach-0.6.1.tar.gz", hash = "sha256:674709c26040618aff0741ce8261fd151e5ead405bd50568c2034662d69daac3"},
@@ -2726,6 +2766,10 @@ django-ckeditor = [
     {file = "django-ckeditor-6.0.0.tar.gz", hash = "sha256:29fd1a333cb9741ac2c3fd4e427a5c00115ed33a2389716a09af7656022dcdde"},
     {file = "django_ckeditor-6.0.0-py2.py3-none-any.whl", hash = "sha256:cc2d377f1bdcd4ca1540caeebe85f7e2cd006198d57328ef6c718d3eaa5a0846"},
 ]
+django-cleanup = [
+    {file = "django-cleanup-5.1.0.tar.gz", hash = "sha256:8976aec12a22913afb3d1fcb541b1aedde2f5ec243e4260c5ff78bb6aa75a089"},
+    {file = "django_cleanup-5.1.0-py2.py3-none-any.whl", hash = "sha256:71e098c7d9ac3f3da40b95cff9c0bc51218d40ef419261232f46ba3141c50acc"},
+]
 django-colorfield = [
     {file = "django-colorfield-0.4.1.tar.gz", hash = "sha256:63a542c417b72d0dac898a0f61a2a00aed3c9aabc2f5057c926efccf421f7887"},
     {file = "django_colorfield-0.4.1-py3-none-any.whl", hash = "sha256:e38f8b9dabbab48a6dab3d1eb5bd802decb92970d56a28128c9a70cdbf383e30"},
@@ -2734,16 +2778,16 @@ django-dbbackup = [
     {file = "django-dbbackup-3.3.0.tar.gz", hash = "sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8"},
 ]
 django-debug-toolbar = [
-    {file = "django-debug-toolbar-3.2.tar.gz", hash = "sha256:84e2607d900dbd571df0a2acf380b47c088efb787dce9805aefeb407341961d2"},
-    {file = "django_debug_toolbar-3.2-py3-none-any.whl", hash = "sha256:9e5a25d0c965f7e686f6a8ba23613ca9ca30184daa26487706d4829f5cfb697a"},
+    {file = "django-debug-toolbar-3.2.1.tar.gz", hash = "sha256:a5ff2a54f24bf88286f9872836081078f4baa843dc3735ee88524e89f8821e33"},
+    {file = "django_debug_toolbar-3.2.1-py3-none-any.whl", hash = "sha256:e759e63e3fe2d3110e0e519639c166816368701eab4a47fed75d7de7018467b9"},
 ]
 django-dynamic-preferences = [
     {file = "django-dynamic-preferences-1.10.1.tar.gz", hash = "sha256:e4b2bb7b2563c5064ba56dd76441c77e06b850ff1466a386a1cd308909a6c7de"},
     {file = "django_dynamic_preferences-1.10.1-py2.py3-none-any.whl", hash = "sha256:9419fa925fd2cbb665269ae72059eb3058bf080913d853419b827e4e7a141902"},
 ]
 django-extensions = [
-    {file = "django-extensions-3.1.1.tar.gz", hash = "sha256:674ad4c3b1587a884881824f40212d51829e662e52f85b012cd83d83fe1271d9"},
-    {file = "django_extensions-3.1.1-py3-none-any.whl", hash = "sha256:9507f8761ee760748938fd8af766d0608fb2738cf368adfa1b2451f61c15ae35"},
+    {file = "django-extensions-3.1.2.tar.gz", hash = "sha256:081828e985485662f62a22340c1506e37989d14b927652079a5b7cd84a82368b"},
+    {file = "django_extensions-3.1.2-py3-none-any.whl", hash = "sha256:17f85f4dcdd5eea09b8c4f0bad8f0370bf2db6d03e61b431fa7103fee29888de"},
 ]
 django-favicon-plus-reloaded = [
     {file = "django-favicon-plus-reloaded-1.0.4.tar.gz", hash = "sha256:90c761c636a338e6e9fb1d086649d82095085f92cff816c9cf074607f28c85a5"},
@@ -2809,16 +2853,16 @@ django-model-utils = [
     {file = "django_model_utils-4.1.1-py3-none-any.whl", hash = "sha256:ef7c440024e797796a3811432abdd2be8b5225ae64ef346f8bfc6de7d8e5d73c"},
 ]
 django-otp = [
-    {file = "django-otp-1.0.2.tar.gz", hash = "sha256:f523fb9dec420f28a29d3e2ad72ac06f64588956ed4f2b5b430d8e957ebb8287"},
-    {file = "django_otp-1.0.2-py3-none-any.whl", hash = "sha256:8ba5ab9bd2738c7321376c349d7cce49cf4404e79f6804e0a3cc462a91728e18"},
+    {file = "django-otp-1.0.3.tar.gz", hash = "sha256:381a15e65293b8b06d47b7d6b306e0b7af2e104137ac92f6c566d3b9b90b6244"},
+    {file = "django_otp-1.0.3-py3-none-any.whl", hash = "sha256:f4ab096b424c33ffe69453620356e1b7517f30dfb9ba13bfeaa1d1f20faddc13"},
 ]
 django-otp-yubikey = [
-    {file = "django-otp-yubikey-1.0.0.tar.gz", hash = "sha256:fbd409277892229b7e3578faa4f63ea766e242659456939164c8f71b845287b6"},
-    {file = "django_otp_yubikey-1.0.0-py2.py3-none-any.whl", hash = "sha256:07743473024900c3b7a14647039f2cf66148cf6243d6aee0853ba45516c224a4"},
+    {file = "django-otp-yubikey-1.0.0.post1.tar.gz", hash = "sha256:1da060257611d06e681848b7923fd788d878a79e8c358a373374deab13a085af"},
+    {file = "django_otp_yubikey-1.0.0.post1-py2.py3-none-any.whl", hash = "sha256:613c96be211c1267400a5a78ae63f212c722f82dffb9daef3c8b1df370abb9be"},
 ]
 django-phonenumber-field = [
-    {file = "django-phonenumber-field-5.0.0.tar.gz", hash = "sha256:1eb7af3a108744665f7c3939d38aa15b3728c57d13d45d656b0a2aa11e8cdc3c"},
-    {file = "django_phonenumber_field-5.0.0-py3-none-any.whl", hash = "sha256:adb46905cc4ecb19d8494424e1c4352f24946bb472340a2a17257d44bf8228e6"},
+    {file = "django-phonenumber-field-5.1.0.tar.gz", hash = "sha256:9eda963ac15b363393f677cc084efd45c3bd97bb5a0cdb4a06409ac99e05dd4b"},
+    {file = "django_phonenumber_field-5.1.0-py3-none-any.whl", hash = "sha256:48724ba235ee8248a474204faa0934c5baf9536f429859d05cb131fbd6b1c695"},
 ]
 django-polymorphic = [
     {file = "django-polymorphic-3.0.0.tar.gz", hash = "sha256:9d886f19f031d26bb1391c055ed9be06fb226a04a4cec1842b372c58873b3caa"},
@@ -2845,11 +2889,12 @@ django-reversion = [
     {file = "django_reversion-3.0.9-py3-none-any.whl", hash = "sha256:1b57127a136b969f4b843a915c72af271febe7f336469db6c27121f8adcad35c"},
 ]
 django-sass-processor = [
-    {file = "django-sass-processor-1.0.0.tar.gz", hash = "sha256:cb90efee38cd7b0fe727c78d8993ad7804de33f40328200dfc1a481307ef0466"},
+    {file = "django-sass-processor-1.0.1.tar.gz", hash = "sha256:dcaad47c591a2d52689c1bd209259e922e902d886293f0d5c9e0d1a4eb85eda2"},
+    {file = "django_sass_processor-1.0.1-py3-none-any.whl", hash = "sha256:1f043180c47754018e803a77da003377f5ea6558de57cd6946eb27a32e9c16a2"},
 ]
 django-select2 = [
-    {file = "django-select2-7.7.0.tar.gz", hash = "sha256:26b4c59cbeba57aea1737187b930a83c8070788286b4236b13f7873c01b32684"},
-    {file = "django_select2-7.7.0-py2.py3-none-any.whl", hash = "sha256:e56bfe3074d6b87524c5dbc139884c18c74a5e7324d66f0b93e42b6012ea0dc0"},
+    {file = "django-select2-7.7.1.tar.gz", hash = "sha256:dd091342e99436818b3fa98783ae6c24fb2a0cbc37ebd3faa0aef68422b6e416"},
+    {file = "django_select2-7.7.1-py2.py3-none-any.whl", hash = "sha256:8c54984bb931d842eab6a46d1b427c6883e5f5347529cda27dcd942fb37d87b9"},
 ]
 django-settings-context-processor = [
     {file = "django-settings-context-processor-0.2.tar.gz", hash = "sha256:d37c853d69a3069f5abbf94c7f4f6fc0fac38bbd0524190cd5a250ba800e496a"},
@@ -2859,8 +2904,12 @@ django-storages = [
     {file = "django_storages-1.11.1-py3-none-any.whl", hash = "sha256:f28765826d507a0309cfaa849bd084894bc71d81bf0d09479168d44785396f80"},
 ]
 django-stubs = [
-    {file = "django-stubs-1.7.0.tar.gz", hash = "sha256:ddd190aca5b9adb4d30760d5c64f67cb3658703f5f42c3bb0c2c71ff4d752c39"},
-    {file = "django_stubs-1.7.0-py3-none-any.whl", hash = "sha256:30a7d99c694acf79c5d93d69a5a8e4b54d2a8c11dd672aa869006789e2189fa6"},
+    {file = "django-stubs-1.8.0.tar.gz", hash = "sha256:717967d7fee0a6af0746724a0be80d72831a982a40fa8f245a6a46f4cafd157b"},
+    {file = "django_stubs-1.8.0-py3-none-any.whl", hash = "sha256:bde9e44e3c4574c2454e74a3e607cc3bc23b0441bb7d1312cd677d5e30984b74"},
+]
+django-stubs-ext = [
+    {file = "django-stubs-ext-0.2.0.tar.gz", hash = "sha256:c14f297835a42c1122421ec7e2d06579996b29d33b8016002762afa5d78863af"},
+    {file = "django_stubs_ext-0.2.0-py3-none-any.whl", hash = "sha256:bd4a1e36ef2ba0ef15801933c85c68e59b383302c873795c6ecfc25950c7ecdb"},
 ]
 django-tables2 = [
     {file = "django-tables2-2.3.4.tar.gz", hash = "sha256:50ccadbd13740a996d8a4d4f144ef80134745cd0b5ec278061537e341f5ef7a2"},
@@ -2900,12 +2949,12 @@ dynaconf = [
     {file = "dynaconf-3.1.4.tar.gz", hash = "sha256:b2f472d83052f809c5925565b8a2ba76a103d5dc1dbb9748b693ed67212781b9"},
 ]
 faker = [
-    {file = "Faker-7.0.1-py3-none-any.whl", hash = "sha256:08c4cfbfd498c0e90aff6741771c01803d894013df858db6a573182c6a47951f"},
-    {file = "Faker-7.0.1.tar.gz", hash = "sha256:20c6e4253b73ef2a783d38e085e7c8d8916295fff31c7403116d2af8f908f7ca"},
+    {file = "Faker-8.1.0-py3-none-any.whl", hash = "sha256:44eb060fad3015690ff3fec6564d7171be393021e820ad1851d96cb968fbfcd4"},
+    {file = "Faker-8.1.0.tar.gz", hash = "sha256:26c7c3df8d46f1db595a34962f8967021dd90bbd38cc6e27461a3fb16cd413ae"},
 ]
 flake8 = [
-    {file = "flake8-3.9.0-py2.py3-none-any.whl", hash = "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff"},
-    {file = "flake8-3.9.0.tar.gz", hash = "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0"},
+    {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"},
+    {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"},
 ]
 flake8-bandit = [
     {file = "flake8_bandit-2.1.2.tar.gz", hash = "sha256:687fc8da2e4a239b206af2e54a90093572a60d0954f3054e23690739b0b0de3b"},
@@ -2942,7 +2991,12 @@ flake8-polyfill = [
     {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"},
 ]
 flake8-rst-docstrings = [
-    {file = "flake8-rst-docstrings-0.0.14.tar.gz", hash = "sha256:8f8bcb18f1408b506dd8ba2c99af3eac6128f6911d4bf6ff874b94caa70182a2"},
+    {file = "flake8-rst-docstrings-0.1.2.tar.gz", hash = "sha256:7d34d2175a0cd92aba0872ade74268b2f2c12582c7267d4a0e6ef1c32a828ce3"},
+    {file = "flake8_rst_docstrings-0.1.2-py3-none-any.whl", hash = "sha256:73b5db2fd9d4d7c7e6b7767931730c4570e5a89f34a1501b837afb3b6d31537a"},
+]
+freezegun = [
+    {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"},
+    {file = "freezegun-1.1.0.tar.gz", hash = "sha256:177f9dd59861d871e27a484c3332f35a6e3f5d14626f2bf91be37891f18927f3"},
 ]
 gitdb = [
     {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"},
@@ -2965,8 +3019,8 @@ imagesize = [
     {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-3.10.0-py3-none-any.whl", hash = "sha256:d2d46ef77ffc85cbf7dac7e81dd663fde71c45326131bea8033b9bad42268ebe"},
-    {file = "importlib_metadata-3.10.0.tar.gz", hash = "sha256:c9db46394197244adf2f0b08ec5bc3cf16757e9590b02af1fca085c16c0d600a"},
+    {file = "importlib_metadata-3.10.1-py3-none-any.whl", hash = "sha256:2ec0faae539743ae6aaa84b49a169670a465f7f5d64e6add98388cc29fd1f2f6"},
+    {file = "importlib_metadata-3.10.1.tar.gz", hash = "sha256:c9356b657de65c53744046fa8f7358afe0714a1af7d570c00c3835c2d724a7c1"},
 ]
 iniconfig = [
     {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -3133,12 +3187,12 @@ pexpect = [
     {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
 ]
 pg8000 = [
-    {file = "pg8000-1.19.0-py3-none-any.whl", hash = "sha256:046095e79f7b8414acd6f38f1b069f1d9c93ab058c7b77a9f0df6c7d586cb5fc"},
-    {file = "pg8000-1.19.0.tar.gz", hash = "sha256:11ec70c0b20ea440807e2c869940f1484eea93d71b435807b63856dd82b744dd"},
+    {file = "pg8000-1.19.2-py3-none-any.whl", hash = "sha256:53924c8d7566cc66aaf4aecb3d8128703ef4054a510602290d62e607398e8d72"},
+    {file = "pg8000-1.19.2.tar.gz", hash = "sha256:44cbb4d3c912f2da167ca02bf98a0040f7e93260e4ef114c28d5d6152012ea07"},
 ]
 phonenumbers = [
-    {file = "phonenumbers-8.12.20-py2.py3-none-any.whl", hash = "sha256:7c2b26ee026f765a8032fc2a333b46fa1860445c7ce6df3b717b9f6985106084"},
-    {file = "phonenumbers-8.12.20.tar.gz", hash = "sha256:ee5a8508c4a414262abad92ec33f050347f681973ed0fb36e98b52bfe159f6b8"},
+    {file = "phonenumbers-8.12.21-py2.py3-none-any.whl", hash = "sha256:7d66d6f09541568941e51fe2275de0f2d519b3234c80c8c166c56d01d098d3fc"},
+    {file = "phonenumbers-8.12.21.tar.gz", hash = "sha256:271f26271338616460eef8b878e5879401299adfc0cd0891c48b4744afbb45b2"},
 ]
 pickleshare = [
     {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
@@ -3184,8 +3238,8 @@ pluggy = [
     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
 ]
 prometheus-client = [
-    {file = "prometheus_client-0.9.0-py2.py3-none-any.whl", hash = "sha256:b08c34c328e1bf5961f0b4352668e6c8f145b4a087e09b7296ef62cbe4693d35"},
-    {file = "prometheus_client-0.9.0.tar.gz", hash = "sha256:9da7b32f02439d8c04f7777021c304ed51d9ec180604700c1ba72a4d44dceb03"},
+    {file = "prometheus_client-0.10.1-py2.py3-none-any.whl", hash = "sha256:030e4f9df5f53db2292eec37c6255957eb76168c6f974e4176c711cf91ed34aa"},
+    {file = "prometheus_client-0.10.1.tar.gz", hash = "sha256:b6c5a9643e3545bcbfd9451766cbaa5d9c67e7303c7bc32c750b6fa70ecb107d"},
 ]
 prompt-toolkit = [
     {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"},
@@ -3333,16 +3387,16 @@ pyparsing = [
     {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
 ]
 pytest = [
-    {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
-    {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
+    {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"},
+    {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"},
 ]
 pytest-cov = [
     {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
     {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
 ]
 pytest-django = [
-    {file = "pytest-django-4.1.0.tar.gz", hash = "sha256:26f02c16d36fd4c8672390deebe3413678d89f30720c16efb8b2a6bf63b9041f"},
-    {file = "pytest_django-4.1.0-py3-none-any.whl", hash = "sha256:10e384e6b8912ded92db64c58be8139d9ae23fb8361e5fc139d8e4f8fc601bc2"},
+    {file = "pytest-django-4.2.0.tar.gz", hash = "sha256:80f8875226ec4dc0b205f0578072034563879d98d9b1bec143a80b9045716cb0"},
+    {file = "pytest_django-4.2.0-py3-none-any.whl", hash = "sha256:a51150d8962200250e850c6adcab670779b9c2aa07271471059d1fb92a843fa9"},
 ]
 pytest-django-testing-postgresql = [
     {file = "pytest-django-testing-postgresql-0.1.post0.tar.gz", hash = "sha256:78b0c58930084cb4393407b2e5a2a3b8734c627b841ecef7d62d39bbfb8e8a45"},
@@ -3405,47 +3459,47 @@ redis = [
     {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
 ]
 regex = [
-    {file = "regex-2021.3.17-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b97ec5d299c10d96617cc851b2e0f81ba5d9d6248413cd374ef7f3a8871ee4a6"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cb4ee827857a5ad9b8ae34d3c8cc51151cb4a3fe082c12ec20ec73e63cc7c6f0"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:633497504e2a485a70a3268d4fc403fe3063a50a50eed1039083e9471ad0101c"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:a59a2ee329b3de764b21495d78c92ab00b4ea79acef0f7ae8c1067f773570afa"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f85d6f41e34f6a2d1607e312820971872944f1661a73d33e1e82d35ea3305e14"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4651f839dbde0816798e698626af6a2469eee6d9964824bb5386091255a1694f"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:39c44532d0e4f1639a89e52355b949573e1e2c5116106a395642cbbae0ff9bcd"},
-    {file = "regex-2021.3.17-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3d9a7e215e02bd7646a91fb8bcba30bc55fd42a719d6b35cf80e5bae31d9134e"},
-    {file = "regex-2021.3.17-cp36-cp36m-win32.whl", hash = "sha256:159fac1a4731409c830d32913f13f68346d6b8e39650ed5d704a9ce2f9ef9cb3"},
-    {file = "regex-2021.3.17-cp36-cp36m-win_amd64.whl", hash = "sha256:13f50969028e81765ed2a1c5fcfdc246c245cf8d47986d5172e82ab1a0c42ee5"},
-    {file = "regex-2021.3.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9d8d286c53fe0cbc6d20bf3d583cabcd1499d89034524e3b94c93a5ab85ca90"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:201e2619a77b21a7780580ab7b5ce43835e242d3e20fef50f66a8df0542e437f"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d47d359545b0ccad29d572ecd52c9da945de7cd6cf9c0cfcb0269f76d3555689"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ea2f41445852c660ba7c3ebf7d70b3779b20d9ca8ba54485a17740db49f46932"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:486a5f8e11e1f5bbfcad87f7c7745eb14796642323e7e1829a331f87a713daaa"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e25e0afe1cf0f62781a150c1454b2113785401ba285c745acf10c8ca8917df"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a2ee026f4156789df8644d23ef423e6194fad0bc53575534101bb1de5d67e8ce"},
-    {file = "regex-2021.3.17-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4c0788010a93ace8a174d73e7c6c9d3e6e3b7ad99a453c8ee8c975ddd9965643"},
-    {file = "regex-2021.3.17-cp37-cp37m-win32.whl", hash = "sha256:575a832e09d237ae5fedb825a7a5bc6a116090dd57d6417d4f3b75121c73e3be"},
-    {file = "regex-2021.3.17-cp37-cp37m-win_amd64.whl", hash = "sha256:8e65e3e4c6feadf6770e2ad89ad3deb524bcb03d8dc679f381d0568c024e0deb"},
-    {file = "regex-2021.3.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0df9a0ad2aad49ea3c7f65edd2ffb3d5c59589b85992a6006354f6fb109bb18"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b98bc9db003f1079caf07b610377ed1ac2e2c11acc2bea4892e28cc5b509d8d5"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:808404898e9a765e4058bf3d7607d0629000e0a14a6782ccbb089296b76fa8fe"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5770a51180d85ea468234bc7987f5597803a4c3d7463e7323322fe4a1b181578"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:976a54d44fd043d958a69b18705a910a8376196c6b6ee5f2596ffc11bff4420d"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:63f3ca8451e5ff7133ffbec9eda641aeab2001be1a01878990f6c87e3c44b9d5"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bcd945175c29a672f13fce13a11893556cd440e37c1b643d6eeab1988c8b209c"},
-    {file = "regex-2021.3.17-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:3d9356add82cff75413bec360c1eca3e58db4a9f5dafa1f19650958a81e3249d"},
-    {file = "regex-2021.3.17-cp38-cp38-win32.whl", hash = "sha256:f5d0c921c99297354cecc5a416ee4280bd3f20fd81b9fb671ca6be71499c3fdf"},
-    {file = "regex-2021.3.17-cp38-cp38-win_amd64.whl", hash = "sha256:14de88eda0976020528efc92d0a1f8830e2fb0de2ae6005a6fc4e062553031fa"},
-    {file = "regex-2021.3.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c2e364491406b7888c2ad4428245fc56c327e34a5dfe58fd40df272b3c3dab3"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8bd4f91f3fb1c9b1380d6894bd5b4a519409135bec14c0c80151e58394a4e88a"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:882f53afe31ef0425b405a3f601c0009b44206ea7f55ee1c606aad3cc213a52c"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:07ef35301b4484bce843831e7039a84e19d8d33b3f8b2f9aab86c376813d0139"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:360a01b5fa2ad35b3113ae0c07fb544ad180603fa3b1f074f52d98c1096fa15e"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:709f65bb2fa9825f09892617d01246002097f8f9b6dde8d1bb4083cf554701ba"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c66221e947d7207457f8b6f42b12f613b09efa9669f65a587a2a71f6a0e4d106"},
-    {file = "regex-2021.3.17-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c782da0e45aff131f0bed6e66fbcfa589ff2862fc719b83a88640daa01a5aff7"},
-    {file = "regex-2021.3.17-cp39-cp39-win32.whl", hash = "sha256:dc9963aacb7da5177e40874585d7407c0f93fb9d7518ec58b86e562f633f36cd"},
-    {file = "regex-2021.3.17-cp39-cp39-win_amd64.whl", hash = "sha256:a0d04128e005142260de3733591ddf476e4902c0c23c1af237d9acf3c96e1b38"},
-    {file = "regex-2021.3.17.tar.gz", hash = "sha256:4b8a1fb724904139149a43e172850f35aa6ea97fb0545244dc0b805e0154ed68"},
+    {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"},
+    {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"},
+    {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"},
+    {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"},
+    {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"},
+    {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"},
+    {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"},
+    {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"},
+    {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"},
+    {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"},
+    {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"},
+    {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"},
+    {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"},
+    {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"},
+    {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"},
+    {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"},
+    {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"},
 ]
 requests = [
     {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
@@ -3455,8 +3509,8 @@ restructuredtext-lint = [
     {file = "restructuredtext_lint-1.3.2.tar.gz", hash = "sha256:d3b10a1fe2ecac537e51ae6d151b223b78de9fafdd50e5eb6b08c243df173c80"},
 ]
 "ruamel.yaml" = [
-    {file = "ruamel.yaml-0.17.2-py3-none-any.whl", hash = "sha256:0850def9ebca23b3a8c64c4b4115ebb6b364a10d49f89d289a26ee965e1e7d9d"},
-    {file = "ruamel.yaml-0.17.2.tar.gz", hash = "sha256:8f1e15421668b9edf30ed02899f5f81aff9808a4271935776f61a99a569a13da"},
+    {file = "ruamel.yaml-0.17.4-py3-none-any.whl", hash = "sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22"},
+    {file = "ruamel.yaml-0.17.4.tar.gz", hash = "sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28"},
 ]
 "ruamel.yaml.clib" = [
     {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc"},
@@ -3495,16 +3549,16 @@ rules = [
     {file = "rules-2.2.tar.gz", hash = "sha256:9bae429f9d4f91a375402990da1541f9e093b0ac077221d57124d06eeeca4405"},
 ]
 s3transfer = [
-    {file = "s3transfer-0.3.6-py2.py3-none-any.whl", hash = "sha256:5d48b1fd2232141a9d5fb279709117aaba506cacea7f86f11bc392f06bfa8fc2"},
-    {file = "s3transfer-0.3.6.tar.gz", hash = "sha256:c5dadf598762899d8cfaecf68eba649cd25b0ce93b6c954b156aaa3eed160547"},
+    {file = "s3transfer-0.3.7-py2.py3-none-any.whl", hash = "sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246"},
+    {file = "s3transfer-0.3.7.tar.gz", hash = "sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994"},
 ]
 safety = [
     {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"},
     {file = "safety-1.10.3.tar.gz", hash = "sha256:30e394d02a20ac49b7f65292d19d38fa927a8f9582cdfd3ad1adbbc66c641ad5"},
 ]
 scramp = [
-    {file = "scramp-1.3.0-py3-none-any.whl", hash = "sha256:6d73eae03e7a3d647a8c36ca95dc8082fe56496db6f803b561ab231627022f82"},
-    {file = "scramp-1.3.0.tar.gz", hash = "sha256:f56208b544387b98e9d39735cc054e273d060efcdf44bb4a20935180772d1ccf"},
+    {file = "scramp-1.4.0-py3-none-any.whl", hash = "sha256:27349d6839038fe3b56c641ea2a8703df065c1d605fdee67275857c0a82122b4"},
+    {file = "scramp-1.4.0.tar.gz", hash = "sha256:d27d768408c6fc025a0e567eed84325b0aaf24364c81ea5974e8334ae3c4fda3"},
 ]
 selenium = [
     {file = "selenium-3.141.0-py2.py3-none-any.whl", hash = "sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c"},
@@ -3531,12 +3585,12 @@ spdx-license-list = [
     {file = "spdx_license_list-0.5.2.tar.gz", hash = "sha256:952996f72ab807972dc2278bb9b91e5294767211e51f09aad9c0e2ff5b82a31b"},
 ]
 sphinx = [
-    {file = "Sphinx-3.5.3-py3-none-any.whl", hash = "sha256:3f01732296465648da43dec8fb40dc451ba79eb3e2cc5c6d79005fd98197107d"},
-    {file = "Sphinx-3.5.3.tar.gz", hash = "sha256:ce9c228456131bab09a3d7d10ae58474de562a6f79abb3dc811ae401cf8c1abc"},
+    {file = "Sphinx-3.5.4-py3-none-any.whl", hash = "sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8"},
+    {file = "Sphinx-3.5.4.tar.gz", hash = "sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1"},
 ]
 sphinx-autodoc-typehints = [
-    {file = "sphinx-autodoc-typehints-1.11.1.tar.gz", hash = "sha256:244ba6d3e2fdb854622f643c7763d6f95b6886eba24bec28e86edf205e4ddb20"},
-    {file = "sphinx_autodoc_typehints-1.11.1-py3-none-any.whl", hash = "sha256:da049791d719f4c9813642496ee4764203e317f0697eb75446183fa2a68e3f77"},
+    {file = "sphinx-autodoc-typehints-1.12.0.tar.gz", hash = "sha256:193617d9dbe0847281b1399d369e74e34cd959c82e02c7efde077fca908a9f52"},
+    {file = "sphinx_autodoc_typehints-1.12.0-py3-none-any.whl", hash = "sha256:5e81776ec422dd168d688ab60f034fccfafbcd94329e9537712c93003bddc04a"},
 ]
 sphinxcontrib-applehelp = [
     {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
@@ -3598,47 +3652,47 @@ toml = [
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
 ]
 tqdm = [
-    {file = "tqdm-4.59.0-py2.py3-none-any.whl", hash = "sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7"},
-    {file = "tqdm-4.59.0.tar.gz", hash = "sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"},
+    {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"},
+    {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"},
 ]
 traitlets = [
     {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"},
     {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"},
 ]
 twilio = [
-    {file = "twilio-6.55.0.tar.gz", hash = "sha256:766555e9f3bdfe9eb2fad9e2efa701f6f7644337a3f6b31a660293d2fbd54331"},
+    {file = "twilio-6.56.0.tar.gz", hash = "sha256:f3d65dcea106383e005460d1cd71703d4f106bcef1e8d289720009ef0f219f28"},
 ]
 typed-ast = [
-    {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"},
-    {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"},
-    {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"},
-    {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"},
-    {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"},
-    {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"},
-    {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"},
-    {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"},
-    {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"},
-    {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"},
-    {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"},
-    {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"},
-    {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"},
-    {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"},
-    {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"},
-    {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"},
-    {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"},
-    {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"},
-    {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"},
-    {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"},
-    {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"},
-    {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"},
-    {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"},
-    {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"},
-    {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"},
-    {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"},
-    {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"},
-    {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"},
-    {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"},
-    {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"},
+    {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
+    {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
+    {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"},
+    {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"},
+    {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"},
+    {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"},
+    {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"},
+    {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"},
+    {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"},
+    {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"},
+    {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"},
+    {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"},
+    {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"},
+    {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"},
+    {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"},
+    {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"},
+    {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"},
+    {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"},
+    {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"},
+    {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"},
+    {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"},
+    {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"},
+    {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"},
+    {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"},
+    {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"},
+    {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"},
+    {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"},
+    {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"},
+    {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"},
+    {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
 ]
 typing-extensions = [
     {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
diff --git a/pyproject.toml b/pyproject.toml
index 4e33a56e3be07176473c35e660290e433c0064e9..56bfe2998101220a938fcbb0080e40691ed45061 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,14 +34,14 @@ secondary = true
 
 [tool.poetry.dependencies]
 python = "^3.7"
-Django = "^3.1.7"
+Django = "^3.2"
 django-any-js = "^1.0"
 django-debug-toolbar = "^3.2"
 django-middleware-global-request = "^0.1.2"
 django-menu-generator-ng = "^1.2.3"
 django-tables2 = "^2.1"
 Pillow = "^8.0"
-django-phonenumber-field = {version = "<5.1", extras = ["phonenumbers"]}
+django-phonenumber-field = {version = "<5.2", extras = ["phonenumbers"]}
 django-sass-processor = "^1.0"
 libsass = "^0.20.0"
 colour = "^0.1.5"
@@ -99,6 +99,7 @@ ipython = "^7.20.0"
 django-redis = "^4.12.1"
 django-storages = {version = "^1.11.1", optional = true}
 boto3 = {version = "^1.17.33", optional = true}
+django-cleanup = "^5.1.0"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]