diff --git a/.gitignore b/.gitignore
index ed90f2f7a841da90717ad986c7bb57dbc86a8876..70d2c1202e645fc31260a0fdd6bac90fdd25e15a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,6 +64,8 @@ docs/_build/
 # Generated files
 aleksis/node_modules/
 aleksis/static/
+aleksis/whoosh_index/
+aleksis/xapian_index/
 
 .coverage
 .mypy_cache/
diff --git a/.gitmodules b/.gitmodules
index 33ef9e8df0ca7f341b1da2ea53faf28ddab5ba1e..0b1ff289423a93278ac8d274890a2b5ae0788e1a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,7 +1,3 @@
-[submodule "apps/official/AlekSIS-App-Alsijil"]
-	path = apps/official/AlekSIS-App-Alsijil
-	url = https://edugit.org/AlekSIS/Official/AlekSIS-App-Alsijil
-	ignore = untracked
 [submodule "apps/official/AlekSIS-App-Chronos"]
 	path = apps/official/AlekSIS-App-Chronos
 	url = https://edugit.org/AlekSIS/Official/AlekSIS-App-Chronos
diff --git a/Dockerfile b/Dockerfile
index e55220034d9f68186d4e19ea84d2cb778466b4f9..0c4f3b52ededb87de3ea36f7814d155aaa20026d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -46,7 +46,8 @@ ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
 
 # Install core extras
 FROM core AS core-extras
-ARG EXTRA_LDAP
+ARG EXTRA_LDAP=1
+ARG EXTRA_CELERY=1
 WORKDIR /usr/src/app
 
 # LDAP
@@ -58,6 +59,11 @@ RUN   if [ $EXTRA_LDAP = 1 ] ; then \
         eatmydata poetry install -E ldap; \
         fi;
 
+# Celery
+RUN   if [ $EXTRA_CELERY = 1 ] ; then \
+        eatmydata poetry install -E celery; \
+        fi;
+
 # Install official apps
 FROM core-extras AS apps
 COPY apps ./apps/
diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py
index c4e7dd73fe4239333a7584bac369a2f7c69b94a9..fd331592f24d3527c3edfb2726cce25ec83095fe 100644
--- a/aleksis/core/forms.py
+++ b/aleksis/core/forms.py
@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
 
 from django_select2.forms import ModelSelect2MultipleWidget, Select2Widget
 from material import Layout, Fieldset, Row
diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
index df3f09466923b65baf06bf60f955a0c5347e1d54..ccd9c971f5d0615838e6d81eb5b9c56f6344601a 100644
--- a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
@@ -8,9 +8,10 @@ msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2020-03-30 09:18+0000\n"
-"PO-Revision-Date: 2020-01-24 14:49+0000\n"
+"PO-Revision-Date: 2020-04-14 18:42+0000\n"
 "Last-Translator: Tom Teichler <tom.teichler@teckids.org>\n"
-"Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis/de/>\n"
+"Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis/"
+"de/>\n"
 "Language: de_DE\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -36,11 +37,11 @@ msgstr "Neues Benutzerkonto erstellen"
 
 #: forms.py:149 forms.py:152
 msgid "Date"
-msgstr ""
+msgstr "Datum"
 
 #: forms.py:150 forms.py:153
 msgid "Time"
-msgstr ""
+msgstr "Zeit"
 
 #: forms.py:155 menus.py:127 models.py:95 templates/core/persons.html:8
 #: templates/core/persons.html:9
@@ -54,27 +55,31 @@ msgstr "Gruppen"
 
 #: forms.py:160
 msgid "From when until when should the announcement be displayed?"
-msgstr ""
+msgstr "Von wann bis wann soll die Ankündigung angezeigt werden?"
 
 #: forms.py:163
 msgid "Who should see the announcement?"
-msgstr ""
+msgstr "Wer soll die Ankündigung sehen?"
 
 #: forms.py:164
 msgid "Write your announcement:"
-msgstr ""
+msgstr "Schreiben Sie ihre Ankündigung:"
 
 #: forms.py:203
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
+"Sie dürfen keine Ankündigungen erstellen, die nur für die Vergangenheit "
+"gültig sind."
 
 #: forms.py:207
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
+"Das Startdatum und die Startzeit müssen vor dem Enddatum und der Endzeit "
+"sein."
 
 #: forms.py:215
 msgid "You need at least one recipient."
-msgstr ""
+msgstr "Sie benötigen mindestens einen Empfänger."
 
 #: menus.py:7 templates/registration/login.html:21
 #: templates/two_factor/core/login.html:6
@@ -85,7 +90,7 @@ msgstr "Anmelden"
 
 #: menus.py:13
 msgid "Dashboard"
-msgstr ""
+msgstr "Dashboard"
 
 #: menus.py:19
 msgid "Account"
@@ -110,7 +115,7 @@ msgstr "Admin"
 #: menus.py:61 models.py:395 templates/core/announcement/list.html:7
 #: templates/core/announcement/list.html:8
 msgid "Announcements"
-msgstr ""
+msgstr "Ankündigungen"
 
 #: menus.py:70 templates/core/data_management.html:6
 #: templates/core/data_management.html:7
@@ -132,7 +137,7 @@ msgstr "Schulverwaltung"
 
 #: menus.py:106
 msgid "Backend Admin"
-msgstr ""
+msgstr "Backend-Administration"
 
 #: menus.py:117
 msgid "People"
@@ -168,16 +173,12 @@ msgid "School logo"
 msgstr "Schullogo"
 
 #: models.py:51
-#, fuzzy
-#| msgid "School logo"
 msgid "School"
-msgstr "Schullogo"
+msgstr "Schule"
 
 #: models.py:52
-#, fuzzy
-#| msgid "School logo"
 msgid "Schools"
-msgstr "Schullogo"
+msgstr "Schulen"
 
 #: models.py:60
 msgid "Visible caption of the term"
@@ -192,16 +193,12 @@ msgid "Effective end date of term"
 msgstr "Enddatum des Schuljahres"
 
 #: models.py:83
-#, fuzzy
-#| msgid "Edit school term"
 msgid "School term"
-msgstr "Schuljahr bearbeiten"
+msgstr "Schuljahr"
 
 #: models.py:84
-#, fuzzy
-#| msgid "Edit school term"
 msgid "School terms"
-msgstr "Schuljahr bearbeiten"
+msgstr "Schuljahre"
 
 #: models.py:94 templates/core/persons_accounts.html:36
 msgid "Person"
@@ -302,53 +299,51 @@ msgstr "Übergeordnete Gruppen"
 #: models.py:268 models.py:285 models.py:363
 #: templates/core/announcement/list.html:18
 msgid "Title"
-msgstr ""
+msgstr "Titel"
 
 #: models.py:269 models.py:286 models.py:364
 msgid "Description"
-msgstr ""
+msgstr "Beschreibung"
 
 #: models.py:271
 msgid "Application"
-msgstr ""
+msgstr "Anwendung"
 
 #: models.py:277
 msgid "Activity"
-msgstr ""
+msgstr "Aktivität"
 
 #: models.py:278
 msgid "Activities"
-msgstr ""
+msgstr "Aktivitäten"
 
 #: models.py:282
-#, fuzzy
-#| msgid "Send"
 msgid "Sender"
-msgstr "Absenden"
+msgstr "Absender"
 
 #: models.py:287 models.py:365 models.py:518
 msgid "Link"
-msgstr ""
+msgstr "Link"
 
 #: models.py:289
 msgid "Read"
-msgstr ""
+msgstr "Gelesen"
 
 #: models.py:290
 msgid "Sent"
-msgstr ""
+msgstr "Versandt"
 
 #: models.py:301
 msgid "Notification"
-msgstr ""
+msgstr "Benachrichtigung"
 
 #: models.py:302
 msgid "Notifications"
-msgstr ""
+msgstr "Benachrichtigungen"
 
 #: models.py:368
 msgid "Date and time from when to show"
-msgstr ""
+msgstr "Datum und Uhrzeit des Anzeigestarts"
 
 #: models.py:371
 msgid "Date and time until when to show"
@@ -356,63 +351,63 @@ msgstr ""
 
 #: models.py:394
 msgid "Announcement"
-msgstr ""
+msgstr "Ankündigung"
 
 #: models.py:422
 msgid "Announcement recipient"
-msgstr ""
+msgstr "Empfänger der Ankündigung"
 
 #: models.py:423
 msgid "Announcement recipients"
-msgstr ""
+msgstr "Empfänger der Ankündigung"
 
 #: models.py:473
 msgid "Widget Title"
-msgstr ""
+msgstr "Widget-Titel"
 
 #: models.py:474
 msgid "Activate Widget"
-msgstr ""
+msgstr "Widget aktivieren"
 
 #: models.py:486
 msgid "Dashboard Widget"
-msgstr ""
+msgstr "Dashboard-Widget"
 
 #: models.py:487
 msgid "Dashboard Widgets"
-msgstr ""
+msgstr "Dashboard-Widgets"
 
 #: models.py:491
 msgid "Menu ID"
-msgstr ""
+msgstr "Menü-ID"
 
 #: models.py:492
 msgid "Menu name"
-msgstr ""
+msgstr "Menü-Name"
 
 #: models.py:509
 msgid "Custom menu"
-msgstr ""
+msgstr "Benutzerdefiniertes Menü"
 
 #: models.py:510
 msgid "Custom menus"
-msgstr ""
+msgstr "Benutzerdefinierte Menüs"
 
 #: models.py:515
 msgid "Menu"
-msgstr ""
+msgstr "Menü"
 
 #: models.py:520
 msgid "Icon"
-msgstr ""
+msgstr "Icon"
 
 #: models.py:527
 msgid "Custom menu item"
-msgstr ""
+msgstr "Benutzerdefiniertes Menüelement"
 
 #: models.py:528
 msgid "Custom menu items"
-msgstr ""
+msgstr "Benutzerdefinierte Menüelemente"
 
 #: settings.py:254
 msgid "German"
@@ -424,65 +419,55 @@ msgstr "Englisch"
 
 #: settings.py:373
 msgid "Site title"
-msgstr ""
+msgstr "Seitentitel"
 
 #: settings.py:374
 msgid "Site description"
-msgstr ""
+msgstr "Seitenbeschreibung"
 
 #: settings.py:375
 msgid "Primary colour"
-msgstr ""
+msgstr "Primärfarbe"
 
 #: settings.py:376
 msgid "Secondary colour"
-msgstr ""
+msgstr "Akzentfarbe"
 
 #: settings.py:377
-#, fuzzy
-#| msgid "Last name"
 msgid "Mail out name"
-msgstr "Nachname"
+msgstr "Ausgangsmailname"
 
 #: settings.py:378
-#, fuzzy
-#| msgid "E-mail address"
 msgid "Mail out address"
-msgstr "E-Mail-Adresse"
+msgstr "E-Mail-Ausgangsadresse"
 
 #: settings.py:379
 msgid "Link to privacy policy"
-msgstr ""
+msgstr "Link zur Datenschutzerklärung"
 
 #: settings.py:380
 msgid "Link to imprint"
-msgstr ""
+msgstr "Link zum Impressum"
 
 #: settings.py:381
 msgid "Name format of adresses"
-msgstr ""
+msgstr "Namensformat von Anreden"
 
 #: settings.py:382
 msgid "Channels to allow for notifications"
-msgstr ""
+msgstr "Kanäle, welche für Benachrichtigungen erlaubt sind"
 
 #: settings.py:383
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
-msgstr ""
+msgstr "Regulärer Ausdruck um Primärgruppen zu finden, z. B.  '^Class .*'"
 
 #: templates/403.html:10
-#, fuzzy
-#| msgid "You are not allowed to access the requested page or object."
 msgid "Error (403): You are not allowed to access the requested page or object."
-msgstr "Es ist Ihnen nicht erlaubt, auf die angefragte Seite oder das angefragte Objekt zuzugreifen."
+msgstr ""
+"Fehler(403): Es ist Ihnen nicht erlaubt, auf die angefragte Seite oder das "
+"angefragte Objekt zuzugreifen."
 
 #: templates/403.html:12
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "     If you think this is an error in AlekSIS, please contact your site\n"
-#| "     administrators.\n"
-#| "    "
 msgid ""
 "\n"
 "      If you think this is an error in AlekSIS, please contact your site\n"
@@ -490,15 +475,16 @@ msgid ""
 "     "
 msgstr ""
 "\n"
-"     Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS handelt, kontaktieren Sie bitte einen Ihrer\n"
+"      Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS "
+"handelt, kontaktieren Sie bitte einen Ihrer\n"
 "     Systemadministratoren:\n"
-"    "
+"     "
 
 #: templates/404.html:10
-#, fuzzy
-#| msgid "The requested page or object was not found."
 msgid "Error (404): The requested page or object was not found."
-msgstr "Die angefragte Seite oder das angefragte Objekt wurde nicht gefunden."
+msgstr ""
+"Fehler (404): Die angefragte Seite oder das angefragte Objekt wurde nicht "
+"gefunden."
 
 #: templates/404.html:12
 #, fuzzy
@@ -514,16 +500,11 @@ msgid ""
 "     "
 msgstr ""
 "\n"
-"     Wenn Sie über einen Link auf einer externen Seite hierher gelangt sind, ist es möglich, dass dieser veraltet war.\n"
-"    "
+"      Wenn Sie über einen Link auf einer externen Seite hierher gelangt "
+"sind, ist es möglich, dass dieser veraltet war.\n"
+"     "
 
 #: templates/404.html:16
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "     If you think this is an error in AlekSIS, please contact your site\n"
-#| "     administrators.\n"
-#| "    "
 msgid ""
 "\n"
 "      If you think this is an error in AlekSIS, please contact your site\n"
@@ -531,29 +512,16 @@ msgid ""
 "     "
 msgstr ""
 "\n"
-"     Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS handelt, kontaktieren Sie bitte einen Ihrer\n"
+"      Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS "
+"handelt, kontaktieren Sie bitte einen Ihrer\n"
 "     Systemadministratoren:\n"
-"    "
+"     "
 
 #: templates/500.html:10
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "      An unexpected error has occured.\n"
-#| "     "
 msgid "Error (500): An unexpected error has occured.."
-msgstr ""
-"\n"
-"      Ein unerwarteter Fehler ist aufgetreten.\n"
-"     "
+msgstr "Error (500): Ein unerwarteter Fehler ist aufgetreten.."
 
 #: templates/500.html:12
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "     Your site administrators will automatically be notified about this\n"
-#| "     error.\n"
-#| "    "
 msgid ""
 "\n"
 "      Your site administrators will automatically be notified about this\n"
@@ -561,63 +529,59 @@ msgid ""
 "     "
 msgstr ""
 "\n"
-"     Ihre Administratoren werden automatisch über diesen Fehler informiert.\n"
-"    "
+"      Ihre Administratoren werden automatisch über diesen Fehler informiert."
+"\n"
+"     "
 
 #: templates/503.html:10
 msgid "The maintenance mode is currently enabled. Please try again later."
 msgstr "Der Wartungsmodus ist aktuell aktiviert. Bitte versuchen Sie es später erneut."
 
 #: templates/503.html:12
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "     If you think this is an error in AlekSIS, please contact your site\n"
-#| "     administrators.\n"
-#| "    "
 msgid ""
 "\n"
 "      This page is currently unavailable. If this error stays, contact your site administrators:\n"
 "     "
 msgstr ""
 "\n"
-"     Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS handelt, kontaktieren Sie bitte einen Ihrer\n"
+"      Diese Seite ist aktuell nicht erreichbar. Wenn dieser Fehler bestehen "
+"bleibt, kontaktieren Sie bitte einen Ihrer\n"
 "     Systemadministratoren:\n"
-"    "
+"     "
 
 #: templates/core/announcement/form.html:10
 #: templates/core/announcement/form.html:17
 msgid "Edit announcement"
-msgstr ""
+msgstr "Ankündigung bearbeiten"
 
 #: templates/core/announcement/form.html:12
 msgid "Publish announcement"
-msgstr ""
+msgstr "Ankündigung veröffentlichen"
 
 #: templates/core/announcement/form.html:19
 #: templates/core/announcement/list.html:13
 msgid "Publish new announcement"
-msgstr ""
+msgstr "Neue Ankündigung veröffentlichen"
 
 #: templates/core/announcement/form.html:30
 msgid "Save und publish announcement"
-msgstr ""
+msgstr "Ankündigung speichern und veröffentlichen"
 
 #: templates/core/announcement/list.html:19
 msgid "Valid from"
-msgstr ""
+msgstr "Gültig von"
 
 #: templates/core/announcement/list.html:20
 msgid "Valid until"
-msgstr ""
+msgstr "Gültig bis"
 
 #: templates/core/announcement/list.html:21
 msgid "Recipients"
-msgstr ""
+msgstr "Empfänger"
 
 #: templates/core/announcement/list.html:22
 msgid "Actions"
-msgstr ""
+msgstr "Aktionen"
 
 #: templates/core/announcement/list.html:36 templates/core/group_full.html:15
 #: templates/core/person_full.html:15
@@ -626,11 +590,11 @@ msgstr "Bearbeiten"
 
 #: templates/core/announcement/list.html:42
 msgid "Delete"
-msgstr ""
+msgstr "Löschen"
 
 #: templates/core/announcement/list.html:50
 msgid "There are no announcements."
-msgstr ""
+msgstr "Es gibt aktuell keine Ankündigungen."
 
 #: templates/core/announcements.html:9 templates/core/announcements.html:36
 #, python-format
@@ -639,6 +603,9 @@ msgid ""
 "              Valid for %(from)s\n"
 "            "
 msgstr ""
+"\n"
+"              Gültig für %(from)s\n"
+"            "
 
 #: templates/core/announcements.html:13
 #, python-format
@@ -647,6 +614,9 @@ msgid ""
 "              Valid from %(from)s until %(until)s\n"
 "            "
 msgstr ""
+"\n"
+"              Gültig von %(from)s bis %(until)s\n"
+"            "
 
 #: templates/core/announcements.html:40
 #, python-format
@@ -655,22 +625,25 @@ msgid ""
 "              Valid for %(from)s – %(until)s\n"
 "            "
 msgstr ""
+"\n"
+"              Gültig von %(from)s – %(until)s\n"
+"            "
 
 #: templates/core/base.html:54
 msgid "Logged in as"
-msgstr ""
+msgstr "Angemeldet als"
 
 #: templates/core/base.html:146
 msgid "Impress"
-msgstr ""
+msgstr "Impressum"
 
 #: templates/core/base.html:154
 msgid "Privacy Policy"
-msgstr ""
+msgstr "Datenschutzerklärung"
 
 #: templates/core/base_print.html:60
 msgid "Powered by AlekSIS"
-msgstr ""
+msgstr "Betrieben mit AlekSIS"
 
 #: templates/core/edit_group.html:6 templates/core/edit_group.html:7
 msgid "Edit group"
@@ -693,57 +666,38 @@ msgid "Members"
 msgstr "Mitglieder"
 
 #: templates/core/groups.html:14
-#, fuzzy
-#| msgid "Parent groups"
 msgid "Create group"
-msgstr "Übergeordnete Gruppen"
+msgstr "Gruppe erstellen"
 
 #: templates/core/index.html:4
 msgid "Home"
-msgstr ""
+msgstr "Startseite"
 
 #: templates/core/index.html:11
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "    AlekSIS (School Information System)\n"
-#| "  "
 msgid "AlekSIS (School Information System)"
-msgstr ""
-"\n"
-"    AlekSIS (Schulinformationssystem)\n"
-"  "
+msgstr "AlekSIS (Schulinformationssystem)"
 
 #: templates/core/index.html:43
 msgid "Last activities"
-msgstr ""
+msgstr "Letzte Aktivitäten"
 
 #: templates/core/index.html:61
 msgid "No activities available yet."
-msgstr ""
+msgstr "Aktuell keine Aktivitäten verfügbar."
 
 #: templates/core/index.html:66
 msgid "Recent notifications"
-msgstr ""
+msgstr "Letzte Benachrichtigungen"
 
 #: templates/core/index.html:82
-#, fuzzy
-#| msgid "Edit school information"
 msgid "More information →"
-msgstr "Schulinformationen bearbeiten"
+msgstr "Mehr Informationen →"
 
 #: templates/core/index.html:89
 msgid "No notifications available yet."
-msgstr ""
+msgstr "Aktuell keine Benachrichtigungen verfügbar."
 
 #: templates/core/no_person.html:11
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "        Your user account is not linked to a person. This means you\n"
-#| "        cannot access any school-related information. Please contact\n"
-#| "        the managers of AlekSIS at your school.\n"
-#| "      "
 msgid ""
 "\n"
 "          Your user account is not linked to a person. This means you\n"
@@ -752,14 +706,16 @@ msgid ""
 "        "
 msgstr ""
 "\n"
-"        Ihr Benutzerkonto ist nicht mit einer Person verknüpft. Das bedeutet, dass Sie\n"
-"       keine schulbezogenen Informationen aufrufen können. Bitte wenden Sie sich an\n"
-"       die Verwaltenden von AlekSIS an Ihrer Schule.\n"
-"      "
+"          Ihr Benutzerkonto ist nicht mit einer Person verknüpft. Das "
+"bedeutet, dass Sie\n"
+"        keine schulbezogenen Informationen aufrufen können. Bitte wenden Sie "
+"sich an\n"
+"        die Verwaltenden von AlekSIS an Ihrer Schule.\n"
+"        "
 
 #: templates/core/offline.html:6
 msgid "No internet connection."
-msgstr ""
+msgstr "Keine Internetverbindung."
 
 #: templates/core/offline.html:9
 msgid ""
@@ -767,6 +723,13 @@ msgid ""
 "        There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi or mobile data is turned on and try again. If you think you are connected, please contact the system administrators:\n"
 "      "
 msgstr ""
+"\n"
+"        Es ist ein Fehler beim Aufrufen der Seite aufgetreten. Eventuell "
+"haben Sie keine Internetverbindung. Bitte prüfen Sie, ob WLAN oder mobile "
+"Daten aktiv sind, und probieren Sie es erneut. Wenn Sie der Meinung sind, "
+"dass Sie mit dem Internet verbunden sind, kontaktieren Sie bitte einen Ihrer "
+"Systemadministratoren:\n"
+"      "
 
 #: templates/core/person_full.html:19
 msgid "Contact details"
@@ -808,7 +771,7 @@ msgstr "Neues Konto"
 
 #: templates/core/save_button.html:3
 msgid "Save"
-msgstr ""
+msgstr "Speichern"
 
 #: templates/core/school_management.html:6
 #: templates/core/school_management.html:7
@@ -824,13 +787,15 @@ msgid "Maintenance mode enabled"
 msgstr "Wartungsmodus aktiviert"
 
 #: templates/core/system_status.html:23
-#, fuzzy
-#| msgid "Only admin and visitors from internal IPs can access the site."
 msgid ""
 "\n"
 "                Only admin and visitors from internal IPs can access thesite.\n"
 "              "
-msgstr "Nur Administratoren und Besucher von internen IP-Adressen können die Seite aufrufen."
+msgstr ""
+"\n"
+"                Nur Administratoren und Besucher von internen IP-Adressen "
+"können die Seite aufrufen.\n"
+"              "
 
 #: templates/core/system_status.html:34
 msgid "Maintenance mode disabled"
@@ -845,26 +810,30 @@ msgid "Debug mode enabled"
 msgstr "Debug-Modus aktiviert"
 
 #: templates/core/system_status.html:47
-#, fuzzy
-#| msgid "The web server throws back debug information on errors. Do not use in production!"
 msgid ""
 "\n"
 "                The web server throws back debug information on errors. Do not use in production!\n"
 "              "
-msgstr "Der Server gibt Debug-Informationen bei Fehlern zurück. Nicht im Produktivbetrieb nutzen!"
+msgstr ""
+"\n"
+"                Der Server gibt Debug-Informationen bei Fehlern zurück. "
+"Nicht im Produktivbetrieb nutzen!\n"
+"              "
 
 #: templates/core/system_status.html:54
 msgid "Debug mode disabled"
 msgstr "Debug-Modus deaktivert"
 
 #: templates/core/system_status.html:56
-#, fuzzy
-#| msgid "Debug mode is disabled. Default error pages are displayed on errors."
 msgid ""
 "\n"
 "                Debug mode is disabled. Default error pages are displayed on errors.\n"
 "              "
-msgstr "Debug-Modus ist deaktiviert. Standard-Fehlerseiten werden bei Fehlern angezeigt."
+msgstr ""
+"\n"
+"                Debug-Modus ist deaktiviert. Standard-Fehlerseiten werden "
+"bei Fehlern angezeigt.\n"
+"              "
 
 #: templates/impersonate/list_users.html:8
 msgid "Impersonate user"
@@ -872,23 +841,23 @@ msgstr "Als Benutzer verkleiden"
 
 #: templates/martor/editor.html:27
 msgid "Uploading... please wait..."
-msgstr ""
+msgstr "Lädt hoch… bitte warten…"
 
 #: templates/martor/editor.html:36
 msgid "Nothing to preview"
-msgstr ""
+msgstr "Keine Vorschau"
 
 #: templates/martor/emoji.html:4
 msgid "Select Emoji to Insert"
-msgstr ""
+msgstr "Wählen Sie ein Emoji zum Einfügen aus"
 
 #: templates/martor/emoji.html:8
 msgid "Preparing emojis..."
-msgstr ""
+msgstr "Bereite Emoji vor…"
 
 #: templates/martor/guide.html:8
 msgid "Markdown Guide"
-msgstr ""
+msgstr "Markdown-Anleitung"
 
 #: templates/martor/guide.html:9
 #, python-format
@@ -897,14 +866,17 @@ msgid ""
 "            documentation,\n"
 "            <a href=\"%(doc_url)s\" target=\"_blank\">click here</a>"
 msgstr ""
+"Diese Seite wird mit Markdown betrieben. Für komplette\n"
+"            Dokumentation,\n"
+"            <a href=\"%(doc_url)s\" target=\"_blank\">hier klicken</a>"
 
 #: templates/martor/guide.html:15 templates/martor/toolbar.html:42
 msgid "Code"
-msgstr ""
+msgstr "Code"
 
 #: templates/martor/guide.html:16
 msgid "Or"
-msgstr ""
+msgstr "oder"
 
 #: templates/martor/guide.html:19
 msgid "... to Get"
@@ -912,82 +884,82 @@ msgstr ""
 
 #: templates/martor/toolbar.html:3
 msgid "Bold"
-msgstr ""
+msgstr "Fett"
 
 #: templates/martor/toolbar.html:6
 msgid "Italic"
-msgstr ""
+msgstr "kursiv"
 
 #: templates/martor/toolbar.html:10
 msgid "Horizontal Line"
-msgstr ""
+msgstr "horizontale Linie"
 
 #: templates/martor/toolbar.html:15
 msgid "Heading"
-msgstr ""
+msgstr "Überschrift"
 
 #: templates/martor/toolbar.html:20 templates/martor/toolbar.html:23
 #: templates/martor/toolbar.html:26
 msgid "H"
-msgstr ""
+msgstr "H"
 
 #: templates/martor/toolbar.html:31
 msgid "Pre or Code"
-msgstr ""
+msgstr "Pre oder Code"
 
 #: templates/martor/toolbar.html:38
 msgid "Pre"
-msgstr ""
+msgstr "Pre"
 
 #: templates/martor/toolbar.html:48
 msgid "Quote"
-msgstr ""
+msgstr "Zitat"
 
 #: templates/martor/toolbar.html:52
 msgid "Unordered List"
-msgstr ""
+msgstr "Unsortierte Liste"
 
 #: templates/martor/toolbar.html:56
 msgid "Ordered List"
-msgstr ""
+msgstr "Sortierte Liste"
 
 #: templates/martor/toolbar.html:60
 msgid "URL/Link"
-msgstr ""
+msgstr "URL/Link"
 
 #: templates/martor/toolbar.html:82
 msgid "Full Screen"
-msgstr ""
+msgstr "Vollbild"
 
 #: templates/martor/toolbar.html:86
 msgid "Markdown Guide (Help)"
-msgstr ""
+msgstr "Markdown-Anleitung (Hilfe)"
 
 #: templates/two_factor/_base_focus.html:6
 #: templates/two_factor/core/otp_required.html:22
 #: templates/two_factor/core/setup.html:5
 #: templates/two_factor/profile/profile.html:87
 msgid "Enable Two-Factor Authentication"
-msgstr ""
+msgstr "Zwei-Faktor-Authentifizierung aktivieren"
 
 #: templates/two_factor/_wizard_actions.html:6
 msgid "Cancel"
-msgstr ""
+msgstr "Abbrechen"
 
 #: templates/two_factor/_wizard_actions.html:15
 #: templates/two_factor/_wizard_actions.html:20
 msgid "Back"
-msgstr ""
+msgstr "Zurück"
 
 #: templates/two_factor/_wizard_actions.html:26
 msgid "Next"
-msgstr ""
+msgstr "Weiter"
 
 #: templates/two_factor/core/backup_tokens.html:5
 #: templates/two_factor/core/backup_tokens.html:9
 #: templates/two_factor/profile/profile.html:46
 msgid "Backup Tokens"
-msgstr ""
+msgstr "Backup-Token"
 
 #: templates/two_factor/core/backup_tokens.html:14
 msgid ""
@@ -999,55 +971,66 @@ msgid ""
 "        below will be valid.\n"
 "      "
 msgstr ""
+"\n"
+"        Backup-Token können genutzt werden, wenn Ihre primären und Backup-\n"
+"        Telefonnummern nicht verfügbar sind. Die Backup-Tokens unten können "
+"für\n"
+"        die Anmeldungsverifizierung genutzt werden. Wenn Sie alle Backup-"
+"Tokens genutzt haben,\n"
+"        müssen Sie neue generieren. Nur gültige Backup-Tokens werden "
+"angezeigt.\n"
+"      "
 
 #: templates/two_factor/core/backup_tokens.html:33
-#, fuzzy
-#| msgid ""
-#| "\n"
-#| "          There is no person with this id.\n"
-#| "        "
 msgid ""
 "\n"
 "          Print these tokens and keep them somewhere safe.\n"
 "        "
 msgstr ""
 "\n"
-"          Es existiert keine Person mit dieser ID.\n"
+"          Drucken Sie diese Tokens aus und bewahren Sie sie gut auf.\n"
 "        "
 
 #: templates/two_factor/core/backup_tokens.html:39
 msgid "You don't have any backup codes yet."
-msgstr ""
+msgstr "Sie haben aktuell keine Backup-Codes."
 
 #: templates/two_factor/core/backup_tokens.html:45
 msgid "Back to Account Security"
-msgstr ""
+msgstr "Zurück zur Kontosicherheit"
 
 #: templates/two_factor/core/backup_tokens.html:49
 msgid "Generate Tokens"
-msgstr ""
+msgstr "Tokens generieren"
 
 #: templates/two_factor/core/login.html:17
 msgid "Enter your credentials."
-msgstr ""
+msgstr "Geben Sie Ihre Zugangsdaten ein."
 
 #: templates/two_factor/core/login.html:20
 msgid ""
 "We are calling your phone right now, please enter the\n"
 "            digits you hear."
 msgstr ""
+"Wir rufen Ihr Telefon jetzt an, bitte geben Sie die\n"
+"              Zahlen ein, die Sie hören."
 
 #: templates/two_factor/core/login.html:23
 msgid ""
 "We sent you a text message, please enter the tokens we\n"
 "            sent."
 msgstr ""
+"Wir haben Ihnen eine Textnachricht geschickt. Bitte geben Sie die Tokens ein,"
+"\n"
+"            die wir Ihnen geschickt haben."
 
 #: templates/two_factor/core/login.html:26
 msgid ""
 "Please enter the tokens generated by your token\n"
 "            generator."
 msgstr ""
+"Bitte geben Sie den von Ihrem Token-Generator\n"
+"            generierten Token ein."
 
 #: templates/two_factor/core/login.html:30
 msgid ""
@@ -1058,19 +1041,19 @@ msgstr ""
 
 #: templates/two_factor/core/login.html:47
 msgid "Or, alternatively, use one of your backup phones:"
-msgstr ""
+msgstr "Oder, alternativ, nutzen Sie eins Ihrer Backup-Telefone:"
 
 #: templates/two_factor/core/login.html:57
 msgid "As a last resort, you can use a backup token:"
-msgstr ""
+msgstr "Als letzte Möglichkeit können Sie einen Backup-Token nutzen:"
 
 #: templates/two_factor/core/login.html:60
 msgid "Use Backup Token"
-msgstr ""
+msgstr "Backup-Token nutzen"
 
 #: templates/two_factor/core/otp_required.html:9
 msgid "Permission Denied"
-msgstr ""
+msgstr "Zugriff verwehrt"
 
 #: templates/two_factor/core/otp_required.html:10
 msgid ""
@@ -1088,12 +1071,12 @@ msgstr ""
 
 #: templates/two_factor/core/otp_required.html:19
 msgid "Go back"
-msgstr ""
+msgstr "Zurück"
 
 #: templates/two_factor/core/phone_register.html:5
 #: templates/two_factor/core/phone_register.html:9
 msgid "Add Backup Phone"
-msgstr ""
+msgstr "Backup-Telefon hinzufügen"
 
 #: templates/two_factor/core/phone_register.html:12
 msgid ""
@@ -1185,7 +1168,7 @@ msgstr ""
 #: templates/two_factor/core/setup_complete.html:5
 #: templates/two_factor/core/setup_complete.html:9
 msgid "Two-Factor Authentication successfully enabled"
-msgstr ""
+msgstr "Zwei-Faktor-Authentifizierung erfolgreich aktiviert"
 
 #: templates/two_factor/core/setup_complete.html:14
 msgid ""
@@ -1193,18 +1176,20 @@ msgid ""
 "        Congratulations, you've successfully enabled two-factor authentication.\n"
 "      "
 msgstr ""
+"\n"
+"        Gratulation, Sie haben die Zwei-Faktor-Authentifizierung erfolgreich "
+"aktiviert.\n"
+"      "
 
 #: templates/two_factor/core/setup_complete.html:24
 #: templates/two_factor/core/setup_complete.html:44
 msgid "Back to Profile"
-msgstr ""
+msgstr "Zurück zum Profil"
 
 #: templates/two_factor/core/setup_complete.html:28
 #: templates/two_factor/core/setup_complete.html:48
-#, fuzzy
-#| msgid "Recent backup cron jobs"
 msgid "Generate backup codes"
-msgstr "Letzte Backup-Cron-Jobs"
+msgstr "Backup-Codes generieren"
 
 #: templates/two_factor/core/setup_complete.html:34
 msgid ""
@@ -1218,46 +1203,46 @@ msgstr ""
 #: templates/two_factor/core/setup_complete.html:52
 #: templates/two_factor/profile/profile.html:41
 msgid "Add Phone Number"
-msgstr ""
+msgstr "Telefonnummer hinzufügen"
 
 #: templates/two_factor/profile/disable.html:5
 #: templates/two_factor/profile/disable.html:9
 #: templates/two_factor/profile/profile.html:63
 #: templates/two_factor/profile/profile.html:73
 msgid "Disable Two-Factor Authentication"
-msgstr ""
+msgstr "Zwei-Faktor-Authentifizierung deaktiveren"
 
 #: templates/two_factor/profile/disable.html:12
 msgid "You are about to disable two-factor authentication. This weakens your account security, are you sure?"
 msgstr ""
+"Sie sind dabei, Zwei-Faktor-Authentifizierung zu deaktivieren. Das "
+"verschlechtert Ihre Kontosicherheit. Sind Sie sicher?"
 
 #: templates/two_factor/profile/disable.html:26
 msgid "Disable"
-msgstr ""
+msgstr "Deaktivieren"
 
 #: templates/two_factor/profile/profile.html:5
 #: templates/two_factor/profile/profile.html:10
-#, fuzzy
-#| msgid "Account"
 msgid "Account Security"
-msgstr "Konto"
+msgstr "Kontosicherheit"
 
 #: templates/two_factor/profile/profile.html:15
 msgid "Tokens will be generated by your token generator."
-msgstr ""
+msgstr "Tokens werden von Ihrem Token-Generator generiert."
 
 #: templates/two_factor/profile/profile.html:17
 #, python-format
 msgid "Primary method: %(primary)s"
-msgstr ""
+msgstr "Primäre Methode: %(primary)s"
 
 #: templates/two_factor/profile/profile.html:19
 msgid "Tokens will be generated by your YubiKey."
-msgstr ""
+msgstr "Tokens werden von Ihrem YubiKey generiert."
 
 #: templates/two_factor/profile/profile.html:23
 msgid "Backup Phone Numbers"
-msgstr ""
+msgstr "Backup-Telefonnummern"
 
 #: templates/two_factor/profile/profile.html:24
 msgid ""
@@ -1267,7 +1252,7 @@ msgstr ""
 
 #: templates/two_factor/profile/profile.html:33
 msgid "Unregister"
-msgstr ""
+msgstr "Abmelden"
 
 #: templates/two_factor/profile/profile.html:48
 msgid ""
@@ -1276,11 +1261,7 @@ msgid ""
 msgstr ""
 
 #: templates/two_factor/profile/profile.html:50
-#, fuzzy, python-format
-#| msgid ""
-#| "\n"
-#| "        You are not linked to a person\n"
-#| "      "
+#, python-format
 msgid ""
 "\n"
 "        You have only one backup token remaining.\n"
@@ -1291,16 +1272,16 @@ msgid_plural ""
 "      "
 msgstr[0] ""
 "\n"
-"        Sie sind nicht mit einer Person verknüpft\n"
+"        Sie haben keinen Backup-Token übrig.\n"
 "      "
 msgstr[1] ""
 "\n"
-"        Sie sind nicht mit einer Person verknüpft\n"
+"        Sie haben %(counter)s Backup-Tokens übrig.\n"
 "      "
 
 #: templates/two_factor/profile/profile.html:59
 msgid "Show Codes"
-msgstr ""
+msgstr "Codes anzeigen"
 
 #: templates/two_factor/profile/profile.html:65
 msgid ""
@@ -1321,11 +1302,11 @@ msgstr ""
 
 #: util/notifications.py:66
 msgid "E-Mail"
-msgstr ""
+msgstr "E-Mail"
 
 #: util/notifications.py:67
 msgid "SMS"
-msgstr ""
+msgstr "SMS"
 
 #: views.py:172
 msgid "The person has been saved."
@@ -1344,22 +1325,18 @@ msgid "The term has been saved."
 msgstr "Das Schuljahr wurde gespeichert."
 
 #: views.py:272
-#, fuzzy
-#| msgid "You are not allowed to access the requested page or object."
 msgid "You are not allowed to mark notifications from other users as read!"
-msgstr "Es ist Ihnen nicht erlaubt, auf die angefragte Seite oder das angefragte Objekt zuzugreifen."
+msgstr ""
+"Es ist Ihnen nicht erlaubt, Benachrichtigungen von anderen Benutzern als "
+"gelesen zu markieren!"
 
 #: views.py:307
-#, fuzzy
-#| msgid "The term has been saved."
 msgid "The announcement has been saved."
-msgstr "Das Schuljahr wurde gespeichert."
+msgstr "Die Ankündigung wurde gespeichert."
 
 #: views.py:320
-#, fuzzy
-#| msgid "The term has been saved."
 msgid "The announcement has been deleted."
-msgstr "Das Schuljahr wurde gespeichert."
+msgstr "Ankündigung wurde gelöscht."
 
 #~ msgid "Website"
 #~ msgstr "Website"
diff --git a/aleksis/core/locale/la/LC_MESSAGES/django.po b/aleksis/core/locale/la/LC_MESSAGES/django.po
index b511f033217d5d5cb72a934b4c1275be8c9361f1..753ec75d839f32e2238bca0315c607df5559d70e 100644
--- a/aleksis/core/locale/la/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/la/LC_MESSAGES/django.po
@@ -3,20 +3,21 @@
 # This file is distributed under the same license as the PACKAGE package.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
 #
-#, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2020-03-30 09:18+0000\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
+"PO-Revision-Date: 2020-04-14 18:42+0000\n"
+"Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
+"Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis/"
+"la/>\n"
+"Language: la\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 3.8\n"
 
 #: forms.py:38 forms.py:93
 msgid "You cannot set a new username when also selecting an existing user."
@@ -36,16 +37,16 @@ msgstr ""
 
 #: forms.py:149 forms.py:152
 msgid "Date"
-msgstr ""
+msgstr "dies"
 
 #: forms.py:150 forms.py:153
 msgid "Time"
-msgstr ""
+msgstr "tempus"
 
 #: forms.py:155 menus.py:127 models.py:95 templates/core/persons.html:8
 #: templates/core/persons.html:9
 msgid "Persons"
-msgstr ""
+msgstr "personae"
 
 #: forms.py:156 menus.py:133 models.py:232 templates/core/groups.html:8
 #: templates/core/groups.html:9 templates/core/person_full.html:79
@@ -62,7 +63,7 @@ msgstr ""
 
 #: forms.py:164
 msgid "Write your announcement:"
-msgstr ""
+msgstr "Scribe nuntium:"
 
 #: forms.py:203
 msgid "You are not allowed to create announcements which are only valid in the past."
@@ -85,7 +86,7 @@ msgstr ""
 
 #: menus.py:13
 msgid "Dashboard"
-msgstr ""
+msgstr "Forum"
 
 #: menus.py:19
 msgid "Account"
@@ -105,22 +106,22 @@ msgstr ""
 
 #: menus.py:52
 msgid "Admin"
-msgstr ""
+msgstr "Administratio"
 
 #: menus.py:61 models.py:395 templates/core/announcement/list.html:7
 #: templates/core/announcement/list.html:8
 msgid "Announcements"
-msgstr ""
+msgstr "Nuntii"
 
 #: menus.py:70 templates/core/data_management.html:6
 #: templates/core/data_management.html:7
 msgid "Data management"
-msgstr ""
+msgstr "Adminstratio datarum"
 
 #: menus.py:79 templates/core/system_status.html:5
 #: templates/core/system_status.html:7
 msgid "System status"
-msgstr ""
+msgstr "Status systemae"
 
 #: menus.py:88
 msgid "Impersonation"
@@ -128,7 +129,7 @@ msgstr ""
 
 #: menus.py:97
 msgid "Manage school"
-msgstr ""
+msgstr "Administra scholam"
 
 #: menus.py:106
 msgid "Backend Admin"
@@ -136,40 +137,40 @@ msgstr ""
 
 #: menus.py:117
 msgid "People"
-msgstr ""
+msgstr "Personae"
 
 #: menus.py:139
 msgid "Persons and accounts"
-msgstr ""
+msgstr "Personae et computi"
 
 #: menus.py:152
 msgid "Edit school information"
-msgstr ""
+msgstr "Muta informationes scolae"
 
 #: menus.py:153 templates/core/edit_schoolterm.html:8
 #: templates/core/edit_schoolterm.html:9
 msgid "Edit school term"
-msgstr ""
+msgstr "Muta anum scolae"
 
 #: models.py:31 models.py:517
 msgid "Name"
-msgstr ""
+msgstr "Nomen"
 
 #: models.py:33
 msgid "Official name"
-msgstr ""
+msgstr "Officialis nomen"
 
 #: models.py:35
 msgid "Official name of the school, e.g. as given by supervisory authority"
-msgstr ""
+msgstr "Officialis nomen scolae, e. g."
 
 #: models.py:38
 msgid "School logo"
-msgstr ""
+msgstr "Imago scolae"
 
 #: models.py:51
 msgid "School"
-msgstr ""
+msgstr "Scola"
 
 #: models.py:52
 msgid "Schools"
@@ -189,23 +190,23 @@ msgstr ""
 
 #: models.py:83
 msgid "School term"
-msgstr ""
+msgstr "Anus scolae"
 
 #: models.py:84
 msgid "School terms"
-msgstr ""
+msgstr "ani scolae"
 
 #: models.py:94 templates/core/persons_accounts.html:36
 msgid "Person"
-msgstr ""
+msgstr "Persona"
 
 #: models.py:97
 msgid "female"
-msgstr ""
+msgstr "femininum"
 
 #: models.py:97
 msgid "male"
-msgstr ""
+msgstr "maskulinum"
 
 #: models.py:102
 msgid "Is person active?"
@@ -213,47 +214,47 @@ msgstr ""
 
 #: models.py:104
 msgid "First name"
-msgstr ""
+msgstr "Primus nomen"
 
 #: models.py:105
 msgid "Last name"
-msgstr ""
+msgstr "Secondus nomen"
 
 #: models.py:107
 msgid "Additional name(s)"
-msgstr ""
+msgstr "addita nomines"
 
 #: models.py:111
 msgid "Short name"
-msgstr ""
+msgstr "Breve nomen"
 
 #: models.py:114
 msgid "Street"
-msgstr ""
+msgstr "Via"
 
 #: models.py:115
 msgid "Street number"
-msgstr ""
+msgstr "Numerus domini"
 
 #: models.py:116
 msgid "Postal code"
-msgstr ""
+msgstr "Numerus directorius"
 
 #: models.py:117
 msgid "Place"
-msgstr ""
+msgstr "Urbs"
 
 #: models.py:119
 msgid "Home phone"
-msgstr ""
+msgstr "Numerus telephoni domi"
 
 #: models.py:120
 msgid "Mobile phone"
-msgstr ""
+msgstr "Numerus telephoni mobilis"
 
 #: models.py:122
 msgid "E-mail address"
-msgstr ""
+msgstr "Inscriptio electronica"
 
 #: models.py:124
 msgid "Date of birth"
diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py
index cbb53218bdb983693d87d8171350d93688fc8694..4ce208c4801a8bf9bf28a8d7f87eb769ece474f2 100644
--- a/aleksis/core/menus.py
+++ b/aleksis/core/menus.py
@@ -1,5 +1,5 @@
 from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
 
 MENUS = {
     "NAV_MENU_CORE": [
diff --git a/aleksis/core/migrations/0021_person_description_field.py b/aleksis/core/migrations/0021_person_description_field.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e8aa927c1b04a2b5812dd54b86d0a0a67397cb1
--- /dev/null
+++ b/aleksis/core/migrations/0021_person_description_field.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.5 on 2020-04-13 13:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0020_incease_length_of_fields'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='person',
+            name='description',
+            field=models.TextField(blank=True, null=True, verbose_name='Description'),
+        ),
+    ]
diff --git a/aleksis/core/migrations/0022_group_types.py b/aleksis/core/migrations/0022_group_types.py
new file mode 100644
index 0000000000000000000000000000000000000000..c521da4a6f5a5d6f6c314ee0b34e57cb374c9e7c
--- /dev/null
+++ b/aleksis/core/migrations/0022_group_types.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.0.5 on 2020-04-13 13:44
+
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0021_person_description_field'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            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)),
+                ('name', models.CharField(max_length=50, verbose_name='Title of type')),
+                ('description', models.CharField(max_length=500, verbose_name='Description')),
+            ],
+            options={
+                'verbose_name': 'Group type',
+                'verbose_name_plural': 'Group types',
+            },
+        ),
+        migrations.AddField(
+            model_name='group',
+            name='type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='type', to='core.GroupType', verbose_name='Type of group'),
+        ),
+    ]
diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index c4ec43e40839d788b9fecfeedc12403c5892c0c5..15b5c0c67c21a3cf473eda2b1c8637db849125b2 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -73,6 +73,13 @@ class ExtensibleModel(CRUDMixin):
         - Dominik George <dominik.george@teckids.org>
     """
 
+    # Defines a material design icon associated with this type of model
+    icon_ = "radio_button_unchecked"
+
+    def get_absolute_url(self) -> str:
+        """ Get the URL o a view representing this model instance """
+        pass
+
     @property
     def crud_event_create(self) -> Optional[CRUDEvent]:
         """ Return create event of this object """
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index cc4c0130e289173d7e064ca6b6d8eb3e90ff88d4..2bc80574dbcae12a15dae1e0bebc6fb31051b7a3 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -2,20 +2,22 @@ from datetime import date, datetime
 from typing import Optional, Iterable, Union, Sequence, List
 
 from django.contrib.auth import get_user_model
+from django.contrib.auth.models import Group as DjangoGroup
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
 from django.db.models import QuerySet
 from django.forms.widgets import Media
+from django.urls import reverse
 from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
 from image_cropping import ImageCropField, ImageRatioField
 from phonenumber_field.modelfields import PhoneNumberField
 from polymorphic.models import PolymorphicModel
 
 from .mixins import ExtensibleModel, PureDjangoModel
+from .tasks import send_notification
 from .util.core_helpers import now_tomorrow
-from .util.notifications import send_notification
 from .util.model_helpers import ICONS
 
 from constance import config
@@ -94,6 +96,8 @@ class Person(ExtensibleModel):
         verbose_name = _("Person")
         verbose_name_plural = _("Persons")
 
+    icon_ = "person"
+
     SEX_CHOICES = [("f", _("female")), ("m", _("male"))]
 
     user = models.OneToOneField(
@@ -133,6 +137,12 @@ class Person(ExtensibleModel):
 
     primary_group = models.ForeignKey("Group", models.SET_NULL, null=True, blank=True)
 
+    description = models.TextField(verbose_name=_("Description"), blank=True, null=True)
+
+
+    def get_absolute_url(self) -> str:
+        return reverse("person_by_id", args=[self.id])
+
     @property
     def primary_group_short_name(self) -> Optional[str]:
         """ Returns the short_name field of the primary
@@ -166,6 +176,18 @@ class Person(ExtensibleModel):
         else:
             return f"{self.first_name} {self.last_name}"
 
+    @property
+    def age(self):
+        return self.age_at(timezone.datetime.now().date())
+
+    def age_at(self, today):
+        years = today.year - self.date_of_birth.year
+        if (self.date_of_birth.month > today.month
+            or (self.date_of_birth.month == today.month
+                and self.date_of_birth.day > today.day)):
+            years -= 1
+        return years
+
     def save(self, *args, **kwargs):
         super().save(*args, **kwargs)
 
@@ -176,6 +198,10 @@ class Person(ExtensibleModel):
             self.user.email = self.email
             self.user.save()
 
+        # Save all related groups once to keep synchronisation with Django
+        for group in self.member_of.union(self.owner_of.all()).all():
+            group.save()
+
         self.auto_select_primary_group()
 
     def __str__(self) -> str:
@@ -223,6 +249,8 @@ class Group(ExtensibleModel):
         verbose_name = _("Group")
         verbose_name_plural = _("Groups")
 
+    icon_ = "group"
+
     name = models.CharField(verbose_name=_("Long name of group"), max_length=255, unique=True)
     short_name = models.CharField(verbose_name=_("Short name of group"), max_length=255, unique=True, blank=True, null=True)
 
@@ -237,6 +265,12 @@ class Group(ExtensibleModel):
         blank=True,
     )
 
+    type = models.ForeignKey("GroupType", on_delete=models.CASCADE, related_name="type", verbose_name=_("Type of group"), null=True, blank=True)
+
+
+    def get_absolute_url(self) -> str:
+        return reverse("group_by_id", args=[self.id])
+
     @property
     def announcement_recipients(self):
         return list(self.members.all()) + list(self.owners.all())
@@ -244,6 +278,20 @@ class Group(ExtensibleModel):
     def __str__(self) -> str:
         return "%s (%s)" % (self.name, self.short_name)
 
+    def save(self, *args, **kwargs):
+        super().save(*args, **kwargs)
+
+        # Synchronise group to Django group with same name
+        dj_group, _ = DjangoGroup.objects.get_or_create(name=self.name)
+        dj_group.user_set.set(
+            list(
+                self.members.filter(user__isnull=False).values_list("user", flat=True).union(
+                    self.owners.filter(user__isnull=False).values_list("user", flat=True)
+                )
+            )
+        )
+        dj_group.save()
+
 
 class Activity(ExtensibleModel):
     user = models.ForeignKey("Person", on_delete=models.CASCADE, related_name="activities")
@@ -276,7 +324,8 @@ class Notification(ExtensibleModel):
         return str(self.title)
 
     def save(self, **kwargs):
-        send_notification(self)
+        if not self.sent:
+            send_notification(self.pk, resend=True)
         self.sent = True
         super().save(**kwargs)
 
@@ -509,3 +558,11 @@ class CustomMenuItem(ExtensibleModel):
     class Meta:
         verbose_name = _("Custom menu item")
         verbose_name_plural = _("Custom menu items")
+
+class GroupType(ExtensibleModel):
+    name = models.CharField(verbose_name=_("Title of type"), max_length=50)
+    description = models.CharField(verbose_name=_("Description"), max_length=500)
+
+    class Meta:
+        verbose_name = _("Group type")
+        verbose_name_plural = _("Group types")
diff --git a/aleksis/core/search_indexes.py b/aleksis/core/search_indexes.py
new file mode 100644
index 0000000000000000000000000000000000000000..5828e0c52391423cc6dd0bad43f6a15310a85e1f
--- /dev/null
+++ b/aleksis/core/search_indexes.py
@@ -0,0 +1,10 @@
+from .models import Person, Group
+from .util.search import Indexable, SearchIndex
+
+
+class PersonIndex(SearchIndex, Indexable):
+    model = Person
+
+
+class GroupIndex(SearchIndex, Indexable):
+    model = Group
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index 94338cf8bcec2032251864bc09434767a6bf0307..463ec58009375ee57562957e8e4a45dd2bb041b5 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -55,8 +55,10 @@ INSTALLED_APPS = [
     "django.contrib.humanize",
     "guardian",
     "rules.apps.AutodiscoverRulesConfig",
+    "haystack",
     "polymorphic",
     "django_global_request",
+    "dbbackup",
     "settings_context_processor",
     "sass_processor",
     "easyaudit",
@@ -282,6 +284,7 @@ YARN_INSTALLED_APPS = [
     "materialize-css",
     "material-design-icons-iconfont",
     "select2",
+    "select2-materialize",
     "paper-css",
 ]
 
@@ -302,6 +305,7 @@ ANY_JS = {
         "css_url": JS_URL + "/material-design-icons-iconfont/dist/material-design-icons.css"
     },
     "paper-css": {"css_url": JS_URL + "/paper-css/paper.min.css"},
+    "select2-materialize": {"css_url": JS_URL + "/select2-materialize/select2-materialize.css", "js_url": JS_URL + "/select2-materialize/index.js"},
 }
 
 merge_app_settings("ANY_JS", ANY_JS, True)
@@ -407,6 +411,16 @@ MAINTENANCE_MODE_STATE_FILE_PATH = _settings.get(
     "maintenance.statefile", "maintenance_mode_state.txt"
 )
 
+DBBACKUP_STORAGE = _settings.get("backup.storage", "django.core.files.storage.FileSystemStorage")
+DBBACKUP_STORAGE_OPTIONS = {"location": _settings.get("backup.location", "/var/backups/aleksis")}
+DBBACKUP_CLEANUP_KEEP = _settings.get("backup.database.keep", 10)
+DBBACKUP_CLEANUP_KEEP_MEDIA = _settings.get("backup.media.keep", 10)
+DBBACKUP_GPG_RECIPIENT = _settings.get("backup.gpg_recipient", None)
+DBBACKUP_COMPRESS_DB = _settings.get("backup.database.compress", True)
+DBBACKUP_ENCRYPT_DB = _settings.get("backup.database.encrypt", DBBACKUP_GPG_RECIPIENT is not None)
+DBBACKUP_COMPRESS_MEDIA = _settings.get("backup.media.compress", True)
+DBBACKUP_ENCRYPT_MEDIA = _settings.get("backup.media.encrypt", DBBACKUP_GPG_RECIPIENT is not None)
+
 IMPERSONATE = {"USE_HTTP_REFERER": True, "REQUIRE_SUPERUSER": True, "ALLOW_SUPERUSER": True}
 
 DJANGO_TABLES2_TEMPLATE = "django_tables2/materialize.html"
@@ -438,7 +452,7 @@ if _settings.get("twilio.sid", None):
 
 if _settings.get("celery.enabled", False):
     INSTALLED_APPS += ("django_celery_beat", "django_celery_results")
-    CELERY_BROKER_URL = "redis://localhost"
+    CELERY_BROKER_URL = _settings.get("celery.broker", "redis://localhost")
     CELERY_RESULT_BACKEND = "django-db"
     CELERY_CACHE_BACKEND = "django-cache"
     CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
@@ -568,3 +582,34 @@ ANONYMOUS_USER_NAME = None
 
 # Append authentication backends
 AUTHENTICATION_BACKENDS.append("rules.permissions.ObjectPermissionBackend")
+
+HAYSTACK_BACKEND_SHORT = _settings.get("search.backend", "simple")
+
+if HAYSTACK_BACKEND_SHORT == "simple":
+    HAYSTACK_CONNECTIONS = {
+        'default': {
+            'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
+        },
+    }
+elif HAYSTACK_BACKEND_SHORT == "xapian":
+    HAYSTACK_CONNECTIONS = {
+        'default': {
+            'ENGINE': 'xapian_backend.XapianEngine',
+            'PATH': _settings.get("search.index", os.path.join(BASE_DIR, "xapian_index")),
+        },
+    }
+elif HAYSTACK_BACKEND_SHORT == "whoosh":
+    HAYSTACK_CONNECTIONS = {
+        'default': {
+            'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
+            'PATH': _settings.get("search.index", os.path.join(BASE_DIR, "whoosh_index")),
+        },
+    }
+
+if _settings.get("celery.enabled", False) and _settings.get("search.celery", True):
+    INSTALLED_APPS.append("celery_haystack")
+    HAYSTACK_SIGNAL_PROCESSOR = 'celery_haystack.signals.CelerySignalProcessor'
+else:
+    HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
+
+HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10
diff --git a/aleksis/core/static/js/main.js b/aleksis/core/static/js/main.js
index 27b5c5ad8fb126391f98e61b75883f965e9970e2..403837da22d351d8e97ed3cf6c27c79e30514497 100644
--- a/aleksis/core/static/js/main.js
+++ b/aleksis/core/static/js/main.js
@@ -65,6 +65,10 @@ $(document).ready( function () {
         });
     });
 
+    // Initialise auto-completion for search bar
+    window.autocomplete = new Autocomplete({});
+    window.autocomplete.setup();
+
     // Initialize text collapsibles [MAT, own work]
     $(".text-collapsible").addClass("closed").removeClass("opened");
 
diff --git a/aleksis/core/static/js/search.js b/aleksis/core/static/js/search.js
new file mode 100644
index 0000000000000000000000000000000000000000..7943d2ebfaa07123bf0174b24c055884ceb0bd36
--- /dev/null
+++ b/aleksis/core/static/js/search.js
@@ -0,0 +1,121 @@
+/*
+ * Based on: https://django-haystack.readthedocs.io/en/master/autocomplete.html
+ *
+ * © Copyright 2009-2016, Daniel Lindsley
+ * Licensed under the 3-clause BSD license
+ */
+
+var Autocomplete = function (options) {
+    this.form_selector = options.form_selector || '.autocomplete';
+    this.url = options.url || Urls.searchbarSnippets();
+    this.delay = parseInt(options.delay || 300);
+    this.minimum_length = parseInt(options.minimum_length || 3);
+    this.form_elem = null;
+    this.query_box = null;
+    this.selected_element = null;
+};
+
+Autocomplete.prototype.setup = function () {
+    var self = this;
+
+    this.form_elem = $(this.form_selector);
+    this.query_box = this.form_elem.find('input[name=q]');
+
+
+    $("#search-form").focusout(function (e) {
+        if (!$(e.relatedTarget).hasClass("search-item")) {
+            e.preventDefault();
+            $("#search-results").remove();
+        }
+    });
+
+    // Trigger the "keyup" event if input gets focused
+
+    this.query_box.focus(function () {
+        self.query_box.trigger("keydown");
+    });
+
+    // Watch the input box.
+    this.query_box.keydown(function (e) {
+        var query = self.query_box.val();
+
+        if (e.which === 38) { // Keypress Up
+            if (!self.selected_element) {
+                self.setSelectedResult($("#search-collection").children().last());
+                return false;
+            }
+
+            let prev = self.selected_element.prev();
+            if (prev.length > 0) {
+                self.setSelectedResult(prev);
+            }
+            return false;
+        }
+
+        if (e.which === 40) { // Keypress Down
+            if (!self.selected_element) {
+                self.setSelectedResult($("#search-collection").children().first());
+                return false;
+            }
+
+            let next = self.selected_element.next();
+            if (next.length > 0) {
+                self.setSelectedResult(next);
+            }
+            return false;
+        }
+
+        if (self.selected_element && e.which === 13) {
+            e.preventDefault();
+            window.location.href = self.selected_element.attr("href");
+        }
+
+        if (query.length < self.minimum_length) {
+            $("#search-results").remove();
+            return true;
+        }
+
+        self.fetch(query);
+        return true;
+    });
+
+    // // On selecting a result, remove result box
+    // this.form_elem.on('click', '#search-results', function (ev) {
+    //     $('#search-results').remove();
+    //     return true;
+    // });
+
+    // Disable browser's own autocomplete
+    // We do this here so users without JavaScript can keep it enabled
+    this.query_box.attr('autocomplete', 'off');
+};
+
+Autocomplete.prototype.fetch = function (query) {
+    var self = this;
+
+    $.ajax({
+        url: this.url
+        , data: {
+            'q': query
+        }
+        , success: function (data) {
+            self.show_results(data);
+        }
+    })
+};
+
+Autocomplete.prototype.show_results = function (data) {
+    $('#search-results').remove();
+    var results_wrapper = $('<div id="search-results">' + data + '</div>');
+    this.query_box.after(results_wrapper);
+    this.selected_element = null;
+};
+
+Autocomplete.prototype.setSelectedResult = function (element) {
+    if (this.selected_element) {
+        this.selected_element.removeClass("active");
+    }
+    element.addClass("active");
+    this.selected_element = element;
+    console.log("New element: ", element);
+};
diff --git a/aleksis/core/static/style.scss b/aleksis/core/static/style.scss
index e2cc69479f8cca34d7f2b95c2b70d61aba284174..e1edc75561fb4fe57568f1b148b8ab0f955c1b58 100644
--- a/aleksis/core/static/style.scss
+++ b/aleksis/core/static/style.scss
@@ -8,6 +8,14 @@
   color: $primary-color !important;
 }
 
+.secondary-color {
+  background-color: $secondary-color !important;
+}
+
+.secondary-color-text, .secondary-color-text a {
+  color: $secondary-color !important;
+}
+
 rect#background {
   fill: $primary-color !important;
 }
@@ -72,7 +80,7 @@ header, main, footer {
 
 #sidenav-logo {
   height: 70px;
-  width:auto;
+  width: auto;
 }
 
 @media only screen and (max-width: 993px) {
@@ -129,6 +137,66 @@ li.active > a > .sidenav-badge {
   color: $primary-color !important;
 }
 
+.sidenav li.search {
+  position: relative;
+  z-index: 2;
+}
+
+.sidenav li.search:hover {
+  background-color: #fff;
+}
+
+.sidenav li.search .search-wrapper {
+  color: #777;
+  margin-top: -1px;
+  border-top: 1px solid rgba(0, 0, 0, 0.14);
+  border-bottom: 1px solid rgba(0, 0, 0, 0.14);
+
+  -webkit-transition: margin .25s ease;
+  transition: margin .25s ease;
+}
+
+.sidenav li.search .search-wrapper input#search {
+  color: #777;
+  display: block;
+  font-size: 16px;
+  font-weight: 300;
+  width: 100%;
+  height: 62px;
+  margin: 0;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  padding: 0 45px 0 30px;
+  border: 0;
+}
+
+.sidenav li.search .search-wrapper input#search:focus {
+  outline: none;
+  -webkit-box-shadow: none;
+  box-shadow: none;
+}
+
+.sidenav li.search .search-wrapper > i.material-icons {
+  position: absolute;
+  top: 21px;
+  right: 10px;
+  cursor: pointer;
+}
+
+a.collection-item.search-item {
+  padding: 20px 10px;
+}
+
+div#search-results {
+  position: absolute;
+  width: 100%;
+}
+
+.search-result-icon {
+  position: absolute;
+  right: 10px;
+}
+
 
 // Sidenav trigger
 
@@ -204,6 +272,43 @@ form .row {
   margin-bottom: 0;
 }
 
+label.chips-checkbox {
+  &.active {
+    outline: none;
+    background-color: $chip-selected-color;
+    color: #fff;
+  }
+
+  display: inline-block;
+  height: 32px;
+  font-size: 13px;
+  font-weight: 500;
+  color: rgba(0, 0, 0, .6);
+  line-height: 32px;
+  padding: 0 12px;
+  border-radius: 16px;
+  background-color: $chip-bg-color;
+  margin-bottom: $chip-margin;
+  margin-right: $chip-margin;
+
+  > img {
+    float: left;
+    margin: 0 8px 0 -12px;
+    height: 32px;
+    width: 32px;
+    border-radius: 50%;
+  }
+}
+
+input[type="checkbox"].chips-checkbox + span {
+  padding-left: 0;
+
+  &:before {
+    display: none;
+    width: 0;
+  }
+}
+
 // Badges
 
 span.badge.new::after {
@@ -261,7 +366,7 @@ span.badge .material-icons {
 
 /* Table*/
 
-table.striped > tbody > tr:nth-child(odd), table tr.striped, table tbody.striped tr  {
+table.striped > tbody > tr:nth-child(odd), table tr.striped, table tbody.striped tr {
   background-color: rgba(208, 208, 208, 0.5);
 }
 
@@ -507,4 +612,3 @@ main .alert p:first-child, main .alert div:first-child {
   overflow: visible;
   width: 100%;
 }
-
diff --git a/aleksis/core/static/theme.scss b/aleksis/core/static/theme.scss
index 36d3e3f55a0e37595f3dcc27230947875cd2648b..907c444f5bf99ef3616ae47a4c6df8b2e82b011e 100644
--- a/aleksis/core/static/theme.scss
+++ b/aleksis/core/static/theme.scss
@@ -127,7 +127,7 @@ $collapsible-border-color: #ddd !default;
 
 $chip-bg-color: #e4e4e4 !default;
 $chip-border-color: #9e9e9e !default;
-$chip-selected-color: #26a69a !default;
+$chip-selected-color: $primary-color !default;
 $chip-margin: 5px !default;
 
 
diff --git a/aleksis/core/tasks.py b/aleksis/core/tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c4c40a8f218d95efa0c26c2e30a8388cce305dd
--- /dev/null
+++ b/aleksis/core/tasks.py
@@ -0,0 +1,18 @@
+from django.core import management
+
+from .util.core_helpers import celery_optional
+from .util.notifications import send_notification as _send_notification
+
+
+@celery_optional
+def send_notification(notification: int, resend: bool = False) -> None:
+    _send_notification(notification, resend)
+
+
+@celery_optional
+def backup_data() -> None:
+    db_options = "-z " * settings.DBBACKUP_COMPRESS_DB + "-e" * settings.DBBACKUP_ENCRYPT_DB
+    media_options = "-z " * settings.DBBACKUP_COMPRESS_MEDIA + "-e" * settings.DBBACKUP_ENCRYPT_MEDIA
+
+    management.call_command("dbbackup", db_options)
+    management.call_command("mediabackup", media_options)
diff --git a/aleksis/core/templates/core/base.html b/aleksis/core/templates/core/base.html
index b75eb9ec30f7ac9a29be5bd11a72921ca2d08ad8..8ac5ed74a195be76a558ae30e780d493ae6c2ecf 100644
--- a/aleksis/core/templates/core/base.html
+++ b/aleksis/core/templates/core/base.html
@@ -32,6 +32,8 @@
   {# Include jQuery to provide $(document).ready #}
   {% include_js "jQuery" %}
 
+  <script type="text/javascript" src="{% static 'js/search.js' %}"></script>
+
   {% block extra_head %}{% endblock %}
 </head>
 <body>
@@ -69,6 +71,16 @@
         </object>
       </a>
     </li>
+    {% if user.is_authenticated %}
+      <li class="search">
+        <form method="get" action="{% url "haystack_search" %}" id="search-form" class="autocomplete">
+          <div class="search-wrapper">
+            <input id="search" name="q" placeholder="{% trans "Search" %}">
+            <i class="material-icons">search</i>
+          </div>
+        </form>
+      </li>
+    {% endif %}
     <li class="no-padding">
       {% include "core/sidenav.html" %}
     </li>
diff --git a/aleksis/core/templates/search/indexes/core/group_text.txt b/aleksis/core/templates/search/indexes/core/group_text.txt
new file mode 100644
index 0000000000000000000000000000000000000000..165c30e8c240ddecc872520626cccb598a6ad7a0
--- /dev/null
+++ b/aleksis/core/templates/search/indexes/core/group_text.txt
@@ -0,0 +1,2 @@
+{{ object.name }}
+{{ object.short_name }}
diff --git a/aleksis/core/templates/search/indexes/core/person_text.txt b/aleksis/core/templates/search/indexes/core/person_text.txt
new file mode 100644
index 0000000000000000000000000000000000000000..210e1755e071a6ced9807790f54403ee5bc85f19
--- /dev/null
+++ b/aleksis/core/templates/search/indexes/core/person_text.txt
@@ -0,0 +1,3 @@
+{{ object.full_name }}
+{{ object.user.username }}
+{{ object.email }}
diff --git a/aleksis/core/templates/search/search.html b/aleksis/core/templates/search/search.html
new file mode 100644
index 0000000000000000000000000000000000000000..babbd70d945f26cbf777ae46ca0add15a24e442d
--- /dev/null
+++ b/aleksis/core/templates/search/search.html
@@ -0,0 +1,108 @@
+{# -*- engine:django -*- #}
+
+{% extends "core/base.html" %}
+
+{% load i18n material_form_internal %}
+
+{% block browser_title %}{% blocktrans %}Search{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}Global Search{% endblocktrans %}{% endblock %}
+
+{% block content %}
+  <form method="get">
+    {#    {% form form=form %}{% endform %}#}
+
+    <input type="text" name="{{ form.q.name }}" id="{{ form.q.id }}" value="{% firstof form.q.value "" %}"
+           placeholder="{% trans "Search Term" %}">
+
+    <h6>{{ form.models.label }}</h6>
+    <div>
+      {% for group, items in form.models|select_options %}
+        {% for choice, value, selected in items %}
+          <label class="{% if selected %} active{% endif %}">
+            <input type="checkbox"
+                   {% if value == None or value == '' %}disabled{% else %}value="{{ value }}"{% endif %}
+                    {% if selected %} checked="checked"{% endif %} name="{{ form.models.name }}">
+            <span> {{ choice }} </span>
+          </label>
+        {% endfor %}
+      {% endfor %}
+    </div>
+
+    <button type="submit" class="btn waves-effect waves-light green">
+      <i class="material-icons left">search</i>
+      {% blocktrans %}Search{% endblocktrans %}
+    </button>
+
+    <h5>{% trans "Results" %}</h5>
+
+    {% if query %}
+      <div class="collection">
+        {% for result in page.object_list %}
+          <a href="{{ result.object.get_absolute_url|default:"#" }}" class="collection-item">
+            <i class="material-icons left">{{ result.object.icon_ }}</i>
+            {{ result.object }}
+          </a>
+        {% empty %}
+          <li class="collection-item">
+            {% trans "No search results could be found to your search" %}
+          </li>
+        {% endfor %}
+      </div>
+
+      {% if page.has_other_pages %}
+        <ul class="pagination">
+          {% if page.has_previous %}
+            <li class="waves-effect">
+              <a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">
+                <i class="material-icons">chevron_left</i>
+              </a>
+            </li>
+          {% else %}
+            <li class="disabled"><a href="#"><i class="material-icons">chevron_left</i></a></li>
+          {% endif %}
+
+          {% for page_num in page.paginator.page_range %}
+            {% if page.number == page_num %}
+              <li class="active"><a href="#">{{ page_num }}</a></li>
+            {% else %}
+              <li class="waves-effect"><a href="?q={{ query }}&amp;page={{ page_num }}">{{ page_num }}</a></li>
+            {% endif %}
+          {% endfor %}
+
+          {% if page.has_next %}
+            <li class="waves-effect">
+              <a href="?q={{ query }}&amp;page={{ page.next_page_number }}">
+                <i class="material-icons">chevron_right</i>
+              </a>
+            </li>
+          {% else %}
+            <li class="disabled"><a href="#"><i class="material-icons">chevron_right</i></a></li>
+          {% endif %}
+        </ul>
+      {% endif %}
+    {% else %}
+      <div class="collection">
+        <li class="collection-item">
+          {% trans "Please enter a search term above" %}
+        </li>
+      </div>
+    {% endif %}
+
+
+  </form>
+
+  <script>
+    $(document).ready(function () {
+      $("input[type='checkbox']").each(function () {
+        $(this).addClass("chips-checkbox");
+        $(this).parent("label").addClass("chips-checkbox");
+      });
+
+      $("label.chips-checkbox > span").click(function () {
+        $(this).parent("label.chips-checkbox").toggleClass("active");
+        let input = $(this).next("input[type='checkbox']");
+        input.prop("checked", !input.prop("checked"));
+      });
+    });
+  </script>
+{% endblock %}
diff --git a/aleksis/core/templates/search/searchbar_snippet.html b/aleksis/core/templates/search/searchbar_snippet.html
new file mode 100644
index 0000000000000000000000000000000000000000..b5b7188c6a1e0381fd30f41b6fccc7e587949a30
--- /dev/null
+++ b/aleksis/core/templates/search/searchbar_snippet.html
@@ -0,0 +1,4 @@
+<a href="{{ result.object.get_absolute_url|default:"#" }}" class="collection-item search-item">
+  {{ result.object }}
+  <i class="material-icons secondary-content search-result-icon">{{ result.object.icon_ }}</i>
+</a>
diff --git a/aleksis/core/templates/search/searchbar_snippets.html b/aleksis/core/templates/search/searchbar_snippets.html
new file mode 100644
index 0000000000000000000000000000000000000000..373c93a7ec5d9311167abab0655d3444b3e57e73
--- /dev/null
+++ b/aleksis/core/templates/search/searchbar_snippets.html
@@ -0,0 +1,5 @@
+<div class="collection" id="search-collection">
+  {% for result in results %}
+    {% include "search/searchbar_snippet.html" %}
+  {% endfor %}
+</div>
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index e5320dd02f05a884a8891c7793305f8c0672d7fc..4bb7d10fbbaa6774d5f5beed3a1d7e034f020260 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -39,6 +39,8 @@ urlpatterns = [
     path("announcement/create/", views.announcement_form, name="add_announcement"),
     path("announcement/edit/<int:pk>/", views.announcement_form, name="edit_announcement"),
     path("announcement/delete/<int:pk>/", views.delete_announcement, name="delete_announcement"),
+    path("search/searchbar/", views.searchbar_snippets, name="searchbar_snippets"),
+    path("search/", include("haystack.urls")),
     path("maintenance-mode/", include("maintenance_mode.urls")),
     path("impersonate/", include("impersonate.urls")),
     path("__i18n__/", include("django.conf.urls.i18n")),
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index 67e9fe71ba5d038b9da729430a3e37bc837bc4c2..9f91154c30913ae14eff1332b5f6bea3207bfa86 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -116,11 +116,12 @@ def celery_optional(orig: Callable) -> Callable:
     and it is executed synchronously.
     """
 
+    if hasattr(settings, "CELERY_RESULT_BACKEND"):
+        from ..celery import app  # noqa
+        task = app.task(orig)
+
     def wrapped(*args, **kwargs):
         if hasattr(settings, "CELERY_RESULT_BACKEND"):
-            from ..celery import app  # noqa
-            task = app.task(orig)
-
             task.delay(*args, **kwargs)
         else:
             orig(*args, **kwargs)
diff --git a/aleksis/core/util/notifications.py b/aleksis/core/util/notifications.py
index 5f6f8fded9a00323b2695e27268d4d4a744c0525..7f309d1cc2e0a0a046334683f5e2100f28fdae2c 100644
--- a/aleksis/core/util/notifications.py
+++ b/aleksis/core/util/notifications.py
@@ -68,7 +68,6 @@ _CHANNELS_MAP = {
 }
 
 
-@celery_optional
 def send_notification(notification: Union[int, "Notification"], resend: bool = False) -> None:
     """ Send a notification through enabled channels.
 
diff --git a/aleksis/core/util/search.py b/aleksis/core/util/search.py
new file mode 100644
index 0000000000000000000000000000000000000000..6720fb6b4236f10d3bfb1932969483d4d4db6d8f
--- /dev/null
+++ b/aleksis/core/util/search.py
@@ -0,0 +1,23 @@
+from django.conf import settings
+
+from haystack import indexes
+
+# Not used here, but simplifies imports for apps
+Indexable = indexes.Indexable  # noqa
+
+if settings.HAYSTACK_SIGNAL_PROCESSOR == 'celery_haystack.signals.CelerySignalProcessor':
+    from haystack.indexes import SearchIndex as BaseSearchIndex
+else:
+    from celery_haystack.indexes import CelerySearchIndex as BaseSearchIndex
+
+class SearchIndex(BaseSearchIndex):
+    """ Base class for search indexes on AlekSIS models
+
+    It provides a default document field caleld text and exects
+    the related model in the model attribute.
+    """
+
+    text = indexes.EdgeNgramField(document=True, use_template=True)
+
+    def get_model(self):
+        return self.model
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index c927caa5185abbf1f99dd0084f945a35c27ffd6f..f64602d3b154bb493e034240fea46bf4b9fbb32d 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -4,10 +4,12 @@ from django.contrib.auth.decorators import login_required
 from django.core.exceptions import PermissionDenied
 from django.http import Http404, HttpRequest, HttpResponse
 from django.shortcuts import get_object_or_404, redirect, render
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
 
 from django_tables2 import RequestConfig
 from guardian.shortcuts import get_objects_for_user
+from haystack.inputs import AutoQuery
+from haystack.query import SearchQuerySet
 from rules.contrib.views import permission_required, objectgetter
 
 from .decorators import admin_required, person_required
@@ -86,7 +88,7 @@ def person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse:
     context["person"] = person
 
     # Get groups where person is member of
-    groups = Group.objects.filter(members=person.pk)
+    groups = Group.objects.filter(members=person)
 
     # Build table
     groups_table = GroupsTable(groups)
@@ -326,3 +328,14 @@ def delete_announcement(request: HttpRequest, pk: int) -> HttpResponse:
         messages.success(request, _("The announcement has been deleted."))
 
     return redirect("announcements")
+
+
+@login_required
+def searchbar_snippets(request: HttpRequest) -> HttpResponse:
+    query = request.GET.get('q', '')
+    limit = int(request.GET.get('limit', '5'))
+
+    results = SearchQuerySet().filter(text=AutoQuery(query))[:limit]
+    context = {"results": results}
+
+    return render(request, "search/searchbar_snippets.html", context)
diff --git a/apps/official/AlekSIS-App-Alsijil b/apps/official/AlekSIS-App-Alsijil
deleted file mode 160000
index 7419a5f0a4b9dde161a6c4e0becf7b2a9b17903f..0000000000000000000000000000000000000000
--- a/apps/official/AlekSIS-App-Alsijil
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 7419a5f0a4b9dde161a6c4e0becf7b2a9b17903f
diff --git a/apps/official/AlekSIS-App-Chronos b/apps/official/AlekSIS-App-Chronos
index d117f6c38b9d85febaf3ecec4569f32265a69cec..6b05dfc0613ff143fa292aa67eb349c90439d7ee 160000
--- a/apps/official/AlekSIS-App-Chronos
+++ b/apps/official/AlekSIS-App-Chronos
@@ -1 +1 @@
-Subproject commit d117f6c38b9d85febaf3ecec4569f32265a69cec
+Subproject commit 6b05dfc0613ff143fa292aa67eb349c90439d7ee
diff --git a/apps/official/AlekSIS-App-DashboardFeeds b/apps/official/AlekSIS-App-DashboardFeeds
index 870a206c500752985d70afaee7696899eee65dce..894dc89ac4fd4f6c33b51020288eabe9b6169256 160000
--- a/apps/official/AlekSIS-App-DashboardFeeds
+++ b/apps/official/AlekSIS-App-DashboardFeeds
@@ -1 +1 @@
-Subproject commit 870a206c500752985d70afaee7696899eee65dce
+Subproject commit 894dc89ac4fd4f6c33b51020288eabe9b6169256
diff --git a/apps/official/AlekSIS-App-Exlibris b/apps/official/AlekSIS-App-Exlibris
index 86245231cddfe4124721a4c96bf990bddf5f5779..736a7ea53be4e0fa7de7bf3dd66cd47965e36bed 160000
--- a/apps/official/AlekSIS-App-Exlibris
+++ b/apps/official/AlekSIS-App-Exlibris
@@ -1 +1 @@
-Subproject commit 86245231cddfe4124721a4c96bf990bddf5f5779
+Subproject commit 736a7ea53be4e0fa7de7bf3dd66cd47965e36bed
diff --git a/apps/official/AlekSIS-App-LDAP b/apps/official/AlekSIS-App-LDAP
index c008cd87486860fcaa3ecb98f7ba500381d01665..c0bc552d9a7e04560b54c261b5e8d690958d2968 160000
--- a/apps/official/AlekSIS-App-LDAP
+++ b/apps/official/AlekSIS-App-LDAP
@@ -1 +1 @@
-Subproject commit c008cd87486860fcaa3ecb98f7ba500381d01665
+Subproject commit c0bc552d9a7e04560b54c261b5e8d690958d2968
diff --git a/docker-compose.yml b/docker-compose.yml
index 32a73fe9576c80506bd3c826a6b7d73b7b9d37a0..56d9d1edb57546360eaf73a18d2a95097b950288 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -22,9 +22,41 @@ services:
       - ALEKSIS_caching__memcached__enabled=true
       - ALEKSIS_database__host=db
       - ALEKSIS_maintenance__debug=${ALEKSIS_maintenance__debug:-false}
+      - ALEKSIS_backup__location=/var/lib/aleksis/backups
     depends_on:
       - db
       - memcached
+  worker:
+    build: .
+    image: registry.edugit.org/aleksis/official/aleksis:${ALEKSIS_IMAGE_TAG:-latest}
+    volumes:
+      - aleksis_data:/var/lib/aleksis/
+      - aleksis_static:/usr/share/aleksis/static/
+    command: celery_worker
+    environment:
+      - ALEKSIS_http__allowed_hosts="['*']"
+      - ALEKSIS_caching__memcached__address=memcached:11211
+      - ALEKSIS_caching__memcached__enabled=true
+      - ALEKSIS_database__host=db
+      - ALEKSIS_maintenance__debug=${ALEKSIS_maintenance__debug:-false}
+      - ALEKSIS_backup__location=/var/lib/aleksis/backups
+    depends_on:
+      - app
+  scheduler:
+    build: .
+    image: registry.edugit.org/aleksis/official/aleksis:${ALEKSIS_IMAGE_TAG:-latest}
+    volumes:
+      - aleksis_data:/var/lib/aleksis/
+      - aleksis_static:/usr/share/aleksis/static/
+    command: celery_beat
+    environment:
+      - ALEKSIS_http__allowed_hosts="['*']"
+      - ALEKSIS_caching__memcached__address=memcached:11211
+      - ALEKSIS_caching__memcached__enabled=true
+      - ALEKSIS_database__host=db
+      - ALEKSIS_maintenance__debug=${ALEKSIS_maintenance__debug:-false}
+    depends_on:
+      - worker
   web:
     build: ./docker/nginx
     image: registry.edugit.org/aleksis/official/aleksis/nginx:${ALEKSIS_IMAGE_TAG:-latest}
@@ -39,3 +71,4 @@ services:
 volumes:
   postgres_data:
   aleksis_data:
+  aleksis_static:
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index d66dbdd7707d66a32f005c0f62174aebd23847e7..8c9c64e55abb848ae6d67d42f99e5434f9916a94 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -21,8 +21,12 @@ python manage.py compilescss
 python manage.py collectstatic --no-input --clear
 python manage.py migrate
 
-if [[ -n "$@" ]]; then
-    exec "$@"
+ARG=${$1:-"gunicorn"}
+
+if [ $ARG = "celery_worker" ]; then
+    exec celery -A aleksis.core worker -l info
+elif [ $ARG = "celery_beat" ]; then
+    exec celery -A aleksis.core beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
 else
     exec gunicorn aleksis.core.wsgi --bind ${GUNICORN_BIND}
 fi
diff --git a/poetry.lock b/poetry.lock
index beb17d2a016d0f4ad0e2a2d6138803de4e87ccf7..591849c3413c4492ebc30c577f706574f7c8470a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -91,10 +91,10 @@ description = "Screen-scraping library"
 name = "beautifulsoup4"
 optional = false
 python-versions = "*"
-version = "4.8.2"
+version = "4.9.0"
 
 [package.dependencies]
-soupsieve = ">=1.2"
+soupsieve = [">1.2", "<2.0"]
 
 [package.extras]
 html5lib = ["html5lib"]
@@ -213,7 +213,7 @@ description = "Python package for providing Mozilla's CA Bundle."
 name = "certifi"
 optional = false
 python-versions = "*"
-version = "2019.11.28"
+version = "2020.4.5.1"
 
 [[package]]
 category = "main"
@@ -268,7 +268,7 @@ description = "Code coverage measurement for Python"
 name = "coverage"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-version = "5.0.4"
+version = "5.1"
 
 [package.extras]
 toml = ["toml"]
@@ -502,8 +502,8 @@ category = "main"
 description = "A reusable app for cropping images easily and non-destructively in Django"
 name = "django-image-cropping"
 optional = false
-python-versions = "*"
-version = "1.3.0"
+python-versions = ">=3.5"
+version = "1.4.0"
 
 [package.dependencies]
 django-appconf = ">=1.0.2"
@@ -893,7 +893,7 @@ description = "Faker is a Python package that generates fake data for you."
 name = "faker"
 optional = false
 python-versions = ">=3.4"
-version = "4.0.2"
+version = "4.0.3"
 
 [package.dependencies]
 python-dateutil = ">=2.4"
@@ -1053,7 +1053,7 @@ description = "Git Object Database"
 name = "gitdb"
 optional = false
 python-versions = ">=3.4"
-version = "4.0.2"
+version = "4.0.4"
 
 [package.dependencies]
 smmap = ">=3.0.1,<4"
@@ -1064,7 +1064,7 @@ description = "Python Git Library"
 name = "gitpython"
 optional = false
 python-versions = ">=3.4"
-version = "3.1.0"
+version = "3.1.1"
 
 [package.dependencies]
 gitdb = ">=4.0.1,<5"
@@ -1129,7 +1129,7 @@ description = "A very fast and expressive template engine."
 name = "jinja2"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "2.11.1"
+version = "2.11.2"
 
 [package.dependencies]
 MarkupSafe = ">=0.23"
@@ -1245,7 +1245,7 @@ description = "Utility library for gitignore style pattern matching of file path
 name = "pathspec"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "0.7.0"
+version = "0.8.0"
 
 [[package]]
 category = "dev"
@@ -1253,7 +1253,7 @@ description = "Python Build Reasonableness"
 name = "pbr"
 optional = false
 python-versions = "*"
-version = "5.4.4"
+version = "5.4.5"
 
 [[package]]
 category = "dev"
@@ -1261,10 +1261,10 @@ description = "PostgreSQL interface library"
 name = "pg8000"
 optional = false
 python-versions = ">=3.5"
-version = "1.14.1"
+version = "1.15.1"
 
 [package.dependencies]
-scramp = "1.1.0"
+scramp = "1.1.1"
 
 [[package]]
 category = "main"
@@ -1280,7 +1280,7 @@ description = "Python Imaging Library (Fork)"
 name = "pillow"
 optional = false
 python-versions = ">=3.5"
-version = "7.1.0"
+version = "7.1.1"
 
 [[package]]
 category = "dev"
@@ -1319,7 +1319,7 @@ description = "psycopg2 - Python-PostgreSQL Database Adapter"
 name = "psycopg2"
 optional = false
 python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
-version = "2.8.4"
+version = "2.8.5"
 
 [[package]]
 category = "dev"
@@ -1389,7 +1389,7 @@ description = "Python parsing module"
 name = "pyparsing"
 optional = false
 python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-version = "2.4.6"
+version = "2.4.7"
 
 [[package]]
 category = "dev"
@@ -1594,7 +1594,7 @@ description = "Alternative regular expression module, to replace re."
 name = "regex"
 optional = false
 python-versions = "*"
-version = "2020.2.20"
+version = "2020.4.4"
 
 [[package]]
 category = "main"
@@ -1646,7 +1646,7 @@ description = "An implementation of the SCRAM protocol."
 name = "scramp"
 optional = false
 python-versions = ">=3.5"
-version = "1.1.0"
+version = "1.1.1"
 
 [[package]]
 category = "dev"
@@ -1673,7 +1673,7 @@ description = "A pure Python implementation of a sliding window memory map manag
 name = "smmap"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "3.0.1"
+version = "3.0.2"
 
 [[package]]
 category = "dev"
@@ -1688,8 +1688,8 @@ category = "main"
 description = "A modern CSS selector implementation for Beautiful Soup."
 name = "soupsieve"
 optional = false
-python-versions = ">=3.5"
-version = "2.0"
+python-versions = "*"
+version = "1.9.5"
 
 [[package]]
 category = "dev"
@@ -1924,7 +1924,7 @@ description = "Backported and Experimental Type Hints for Python 3.5+"
 name = "typing-extensions"
 optional = false
 python-versions = "*"
-version = "3.7.4.1"
+version = "3.7.4.2"
 
 [[package]]
 category = "main"
@@ -1953,7 +1953,7 @@ description = "Virtual Python Environment builder"
 name = "virtualenv"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
-version = "20.0.15"
+version = "20.0.17"
 
 [package.dependencies]
 appdirs = ">=1.4.3,<2"
@@ -1967,7 +1967,7 @@ version = ">=0.12,<2"
 
 [package.extras]
 docs = ["sphinx (>=2.0.0,<3)", "sphinx-argparse (>=0.2.5,<1)", "sphinx-rtd-theme (>=0.4.3,<1)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2,<1)"]
-testing = ["pytest (>=4.0.0,<6)", "coverage (>=4.5.1,<6)", "pytest-mock (>=2.0.0,<3)", "pytest-env (>=0.6.2,<1)", "pytest-timeout (>=1.3.4,<2)", "packaging (>=20.0)", "xonsh (>=0.9.13,<1)"]
+testing = ["pytest (>=4.0.0,<6)", "coverage (>=4.5.1,<6)", "pytest-mock (>=2.0.0,<3)", "pytest-env (>=0.6.2,<1)", "pytest-timeout (>=1.3.4,<2)", "packaging (>=20.0)", "xonsh (>=0.9.16,<1)"]
 
 [[package]]
 category = "dev"
@@ -2048,9 +2048,9 @@ bandit = [
     {file = "bandit-1.6.2.tar.gz", hash = "sha256:41e75315853507aa145d62a78a2a6c5e3240fe14ee7c601459d0df9418196065"},
 ]
 beautifulsoup4 = [
-    {file = "beautifulsoup4-4.8.2-py2-none-any.whl", hash = "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae"},
-    {file = "beautifulsoup4-4.8.2-py3-none-any.whl", hash = "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887"},
-    {file = "beautifulsoup4-4.8.2.tar.gz", hash = "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a"},
+    {file = "beautifulsoup4-4.9.0-py2-none-any.whl", hash = "sha256:a4bbe77fd30670455c5296242967a123ec28c37e9702a8a81bd2f20a4baf0368"},
+    {file = "beautifulsoup4-4.9.0-py3-none-any.whl", hash = "sha256:d4e96ac9b0c3a6d3f0caae2e4124e6055c5dcafde8e2f831ff194c104f0775a0"},
+    {file = "beautifulsoup4-4.9.0.tar.gz", hash = "sha256:594ca51a10d2b3443cbac41214e12dbb2a1cd57e1a7344659849e2e20ba6a8d8"},
 ]
 billiard = [
     {file = "billiard-3.6.3.0-py3-none-any.whl", hash = "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede"},
@@ -2073,8 +2073,8 @@ celery = [
     {file = "celery-4.4.2.tar.gz", hash = "sha256:108a0bf9018a871620936c33a3ee9f6336a89f8ef0a0f567a9001f4aa361415f"},
 ]
 certifi = [
-    {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"},
-    {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"},
+    {file = "certifi-2020.4.5.1-py2.py3-none-any.whl", hash = "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304"},
+    {file = "certifi-2020.4.5.1.tar.gz", hash = "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"},
 ]
 chardet = [
     {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
@@ -2096,37 +2096,37 @@ configobj = [
     {file = "configobj-5.0.6.tar.gz", hash = "sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902"},
 ]
 coverage = [
-    {file = "coverage-5.0.4-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307"},
-    {file = "coverage-5.0.4-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8"},
-    {file = "coverage-5.0.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31"},
-    {file = "coverage-5.0.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441"},
-    {file = "coverage-5.0.4-cp27-cp27m-win32.whl", hash = "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac"},
-    {file = "coverage-5.0.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435"},
-    {file = "coverage-5.0.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037"},
-    {file = "coverage-5.0.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a"},
-    {file = "coverage-5.0.4-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5"},
-    {file = "coverage-5.0.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30"},
-    {file = "coverage-5.0.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7"},
-    {file = "coverage-5.0.4-cp35-cp35m-win32.whl", hash = "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de"},
-    {file = "coverage-5.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1"},
-    {file = "coverage-5.0.4-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"},
-    {file = "coverage-5.0.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0"},
-    {file = "coverage-5.0.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd"},
-    {file = "coverage-5.0.4-cp36-cp36m-win32.whl", hash = "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0"},
-    {file = "coverage-5.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b"},
-    {file = "coverage-5.0.4-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78"},
-    {file = "coverage-5.0.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6"},
-    {file = "coverage-5.0.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014"},
-    {file = "coverage-5.0.4-cp37-cp37m-win32.whl", hash = "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732"},
-    {file = "coverage-5.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006"},
-    {file = "coverage-5.0.4-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2"},
-    {file = "coverage-5.0.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe"},
-    {file = "coverage-5.0.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9"},
-    {file = "coverage-5.0.4-cp38-cp38-win32.whl", hash = "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1"},
-    {file = "coverage-5.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0"},
-    {file = "coverage-5.0.4-cp39-cp39-win32.whl", hash = "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7"},
-    {file = "coverage-5.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892"},
-    {file = "coverage-5.0.4.tar.gz", hash = "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823"},
+    {file = "coverage-5.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"},
+    {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"},
+    {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"},
+    {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"},
+    {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"},
+    {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"},
+    {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"},
+    {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"},
+    {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"},
+    {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"},
+    {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"},
+    {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"},
+    {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"},
+    {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"},
+    {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"},
+    {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"},
+    {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"},
+    {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"},
+    {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"},
+    {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"},
+    {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"},
+    {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"},
+    {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"},
+    {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"},
+    {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"},
+    {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"},
+    {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"},
+    {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"},
+    {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"},
+    {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"},
+    {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"},
 ]
 distlib = [
     {file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"},
@@ -2202,7 +2202,8 @@ django-hattori = [
     {file = "django_hattori-0.2.1-py2.py3-none-any.whl", hash = "sha256:e529ed7af8fc34a0169c797c477672b687a205a56f3f5206f90c260acb83b7ac"},
 ]
 django-image-cropping = [
-    {file = "django-image-cropping-1.3.0.tar.gz", hash = "sha256:5c102d87bc66de025517ad06e485c100f73313ebf725e7482a728944276f6463"},
+    {file = "django-image-cropping-1.4.0.tar.gz", hash = "sha256:6cc4a6bd8901e69b710caceea29b942fdb202da26626313cd9271ae989a83a52"},
+    {file = "django_image_cropping-1.4.0-py3-none-any.whl", hash = "sha256:fe6a139c6d5dfc480f2a1d4e7e3e928d5edaefc898e17be66bc5f73140762ad9"},
 ]
 django-impersonate = [
     {file = "django-impersonate-1.5.tar.gz", hash = "sha256:2c10bcb1c42fe6495d915f4cc4cfd7c5f8375ba39a06b0f062ce6f1e2ff76585"},
@@ -2317,8 +2318,8 @@ entrypoints = [
     {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
 ]
 faker = [
-    {file = "Faker-4.0.2-py3-none-any.whl", hash = "sha256:b89aa33837498498e15c709eb40c31386408a901a53c7a5e12a425737a767976"},
-    {file = "Faker-4.0.2.tar.gz", hash = "sha256:2d3f866ef25e1a5af80e7b0ceeacc3c92dec5d0fdbad3e2cb6adf6e60b22188f"},
+    {file = "Faker-4.0.3-py3-none-any.whl", hash = "sha256:53bf2c8a2de8af271466e7b9cc2f08ecf83c4c947981680eb61080779a0adace"},
+    {file = "Faker-4.0.3.tar.gz", hash = "sha256:7292806948ed848f1bcea1e7b963bae6f398687d1da0ea096e156fea2787f454"},
 ]
 filelock = [
     {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
@@ -2365,12 +2366,12 @@ flake8-rst-docstrings = [
     {file = "flake8-rst-docstrings-0.0.13.tar.gz", hash = "sha256:b1b619d81d879b874533973ac04ee5d823fdbe8c9f3701bfe802bb41813997b4"},
 ]
 gitdb = [
-    {file = "gitdb-4.0.2-py3-none-any.whl", hash = "sha256:284a6a4554f954d6e737cddcff946404393e030b76a282c6640df8efd6b3da5e"},
-    {file = "gitdb-4.0.2.tar.gz", hash = "sha256:598e0096bb3175a0aab3a0b5aedaa18a9a25c6707e0eca0695ba1a0baf1b2150"},
+    {file = "gitdb-4.0.4-py3-none-any.whl", hash = "sha256:ba1132c0912e8c917aa8aa990bee26315064c7b7f171ceaaac0afeb1dc656c6a"},
+    {file = "gitdb-4.0.4.tar.gz", hash = "sha256:6f0ecd46f99bb4874e5678d628c3a198e2b4ef38daea2756a2bfd8df7dd5c1a5"},
 ]
 gitpython = [
-    {file = "GitPython-3.1.0-py3-none-any.whl", hash = "sha256:43da89427bdf18bf07f1164c6d415750693b4d50e28fc9b68de706245147b9dd"},
-    {file = "GitPython-3.1.0.tar.gz", hash = "sha256:e426c3b587bd58c482f0b7fe6145ff4ac7ae6c82673fc656f489719abca6f4cb"},
+    {file = "GitPython-3.1.1-py3-none-any.whl", hash = "sha256:71b8dad7409efbdae4930f2b0b646aaeccce292484ffa0bc74f1195582578b3d"},
+    {file = "GitPython-3.1.1.tar.gz", hash = "sha256:6d4f10e2aaad1864bb0f17ec06a2c2831534140e5883c350d58b4e85189dab74"},
 ]
 html2text = [
     {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"},
@@ -2393,8 +2394,8 @@ isort = [
     {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"},
 ]
 jinja2 = [
-    {file = "Jinja2-2.11.1-py2.py3-none-any.whl", hash = "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"},
-    {file = "Jinja2-2.11.1.tar.gz", hash = "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250"},
+    {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
+    {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
 ]
 kombu = [
     {file = "kombu-4.6.8-py2.py3-none-any.whl", hash = "sha256:598e7e749d6ab54f646b74b2d2df67755dee13894f73ab02a2a9feb8870c7cb2"},
@@ -2486,45 +2487,45 @@ packaging = [
     {file = "packaging-20.3.tar.gz", hash = "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3"},
 ]
 pathspec = [
-    {file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"},
-    {file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"},
+    {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"},
+    {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"},
 ]
 pbr = [
-    {file = "pbr-5.4.4-py2.py3-none-any.whl", hash = "sha256:61aa52a0f18b71c5cc58232d2cf8f8d09cd67fcad60b742a60124cb8d6951488"},
-    {file = "pbr-5.4.4.tar.gz", hash = "sha256:139d2625547dbfa5fb0b81daebb39601c478c21956dc57e2e07b74450a8c506b"},
+    {file = "pbr-5.4.5-py2.py3-none-any.whl", hash = "sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8"},
+    {file = "pbr-5.4.5.tar.gz", hash = "sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c"},
 ]
 pg8000 = [
-    {file = "pg8000-1.14.1-py3-none-any.whl", hash = "sha256:0665ddb8a3ac64d6cbce827763ef060a310f7f097df5efbc80ee87aa32217fdd"},
-    {file = "pg8000-1.14.1.tar.gz", hash = "sha256:7e87f5ec51eab2bb1f2a75a687b681152d60ec26828c93c244f00d5d085da64a"},
+    {file = "pg8000-1.15.1-py3-none-any.whl", hash = "sha256:1f17917c8e2580581f4f689ed5e6eefef4ba873e7a9550d04afcb07fd43be83d"},
+    {file = "pg8000-1.15.1.tar.gz", hash = "sha256:01033498ffb27e780f6fb9ec877655ad97ceb26a50efdd77ca3b39ab2271f37c"},
 ]
 phonenumbers = [
     {file = "phonenumbers-8.12.1-py2.py3-none-any.whl", hash = "sha256:bebf881ef0e775b93062fbd107bf164b5baef877a7b8f702e93a9a5d24ae4065"},
     {file = "phonenumbers-8.12.1.tar.gz", hash = "sha256:59ae9cb25fb03027c9f2bf5584098e699be7eca12c443838b83752956be15cda"},
 ]
 pillow = [
-    {file = "Pillow-7.1.0-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:0011ec16bcab9f2f07afa95081ee025b3f0fe428611a000df0fbcb51dd873ca0"},
-    {file = "Pillow-7.1.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e0fbed3f29291c0e4b83c7f21809a709f5a46eefdad236ec2a11096ebb76c6ae"},
-    {file = "Pillow-7.1.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:75513b87a441c093a26a75974cd8373d8d40476cdb178309e8c2f2a534033ea3"},
-    {file = "Pillow-7.1.0-cp35-cp35m-win32.whl", hash = "sha256:ecabe2323e4ed3ce24eaea2120aca395723dd301e9fb03d7e0912a61343fc7f3"},
-    {file = "Pillow-7.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:7dacf81a6b6b724a263c27a364030f81eef97398acda67d1ac50e722ad3cec59"},
-    {file = "Pillow-7.1.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1e1ed97d93676ba6e19124f8054ed439825292cecb490370729f90a27585f180"},
-    {file = "Pillow-7.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:dd4c3cfa93626ed08005abf3aa52b50dae28bf0bc4cb5a4626e9ce6878e7f700"},
-    {file = "Pillow-7.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:65422a17c9d08bf56d8eb0ce5c83863e050cec5f15e8b2042c955aa724ce515d"},
-    {file = "Pillow-7.1.0-cp36-cp36m-win32.whl", hash = "sha256:81655ad8b10acf81a644ad88faa9cd19ab2930f1b83ff6d32217f00de602b6e5"},
-    {file = "Pillow-7.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:87562ac8e35b873eabed6b02259dcec05d7c776fa6c478865fcb147f17ec7530"},
-    {file = "Pillow-7.1.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:0e5aedc62a78525fcfbec417d8db0073dff5de9d8544047f619d208e2cd3d323"},
-    {file = "Pillow-7.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:196d9ff5d0b070b50cd3a3c59ffa3b41232ca34dbd09e262bfa32047229d4b16"},
-    {file = "Pillow-7.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7ccf6e86bd9f7301b883c6ba180309ca6bd566b0324eb66566b0b841c974558e"},
-    {file = "Pillow-7.1.0-cp37-cp37m-win32.whl", hash = "sha256:32facad1f01111505c0fa3f15b2acac13af231f6dc0e68927c8d2d23df8c25c5"},
-    {file = "Pillow-7.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d071c33320c6f66730d1adea0bfd468ab5453627ff8974a6e56f43a48c60bce5"},
-    {file = "Pillow-7.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:f8e26f68b5f7b15ad9d1cc2c28ea57fe8c2f9dfee77f4e0519e833fdbe2feb8d"},
-    {file = "Pillow-7.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:83ab411759b9e1f6a695bea321e835bed448f767711da2630d597dabc69d0350"},
-    {file = "Pillow-7.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fb1f9faa2984918cd7a3b18db1192463e0030500cb060c974b5fd98c13276a8e"},
-    {file = "Pillow-7.1.0-cp38-cp38-win32.whl", hash = "sha256:3d4e8b208346374e820bc5f88e753a4a2d35b6ead0aa1f45dcc7b9206780b983"},
-    {file = "Pillow-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2f44dc402c643a88b9453c6fce84e20b16352df42a98ab013aaebe07e1a1c478"},
-    {file = "Pillow-7.1.0-pp373-pypy36_pp73-win32.whl", hash = "sha256:c85cce00769e162cf50fc21451be9e627f60a9be22172d684c05dc7d0141c55b"},
-    {file = "Pillow-7.1.0-py3.8-macosx-10.9-x86_64.egg", hash = "sha256:832412d607006e79e2eb08fe46af910da2eae8b44f6cab5462566c71982e2e47"},
-    {file = "Pillow-7.1.0.tar.gz", hash = "sha256:fb13e40bd17615a03192f03cab35df0fbfb61ab58ef08ece42884a6bd314faf4"},
+    {file = "Pillow-7.1.1-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:b7453750cf911785009423789d2e4e5393aae9cbb8b3f471dab854b85a26cb89"},
+    {file = "Pillow-7.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4510c6b33277970b1af83c987277f9a08ec2b02cc20ac0f9234e4026136bb137"},
+    {file = "Pillow-7.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b99b2607b6cd58396f363b448cbe71d3c35e28f03e442ab00806463439629c2c"},
+    {file = "Pillow-7.1.1-cp35-cp35m-win32.whl", hash = "sha256:cd47793f7bc9285a88c2b5551d3f16a2ddd005789614a34c5f4a598c2a162383"},
+    {file = "Pillow-7.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:04a10558320eba9137d6a78ca6fc8f4a5801f1b971152938851dc4629d903579"},
+    {file = "Pillow-7.1.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:50a10b048f4dd81c092adad99fa5f7ba941edaf2f9590510109ac2a15e706695"},
+    {file = "Pillow-7.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:721c04d3c77c38086f1f95d1cd8df87f2f9a505a780acf8575912b3206479da1"},
+    {file = "Pillow-7.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:a5dc9f28c0239ec2742d4273bd85b2aa84655be2564db7ad1eb8f64b1efcdc4c"},
+    {file = "Pillow-7.1.1-cp36-cp36m-win32.whl", hash = "sha256:d6bf085f6f9ec6a1724c187083b37b58a8048f86036d42d21802ed5d1fae4853"},
+    {file = "Pillow-7.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:251e5618125ec12ac800265d7048f5857a8f8f1979db9ea3e11382e159d17f68"},
+    {file = "Pillow-7.1.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:433bbc2469a2351bea53666d97bb1eb30f0d56461735be02ea6b27654569f80f"},
+    {file = "Pillow-7.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:eb84e7e5b07ff3725ab05977ac56d5eeb0c510795aeb48e8b691491be3c5745b"},
+    {file = "Pillow-7.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3713386d1e9e79cea1c5e6aaac042841d7eef838cc577a3ca153c8bedf570287"},
+    {file = "Pillow-7.1.1-cp37-cp37m-win32.whl", hash = "sha256:291bad7097b06d648222b769bbfcd61e40d0abdfe10df686d20ede36eb8162b6"},
+    {file = "Pillow-7.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6c1924ed7dbc6ad0636907693bbbdd3fdae1d73072963e71f5644b864bb10b4d"},
+    {file = "Pillow-7.1.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:670e58d3643971f4afd79191abd21623761c2ebe61db1c2cb4797d817c4ba1a7"},
+    {file = "Pillow-7.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8d5799243050c2833c2662b824dfb16aa98e408d2092805edea4300a408490e7"},
+    {file = "Pillow-7.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:da737ab273f4d60ae552f82ad83f7cbd0e173ca30ca20b160f708c92742ee212"},
+    {file = "Pillow-7.1.1-cp38-cp38-win32.whl", hash = "sha256:b2f3e8cc52ecd259b94ca880fea0d15f4ebc6da2cd3db515389bb878d800270f"},
+    {file = "Pillow-7.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:2f0b52a08d175f10c8ea36685115681a484c55d24d0933f9fd911e4111c04144"},
+    {file = "Pillow-7.1.1-pp373-pypy36_pp73-win32.whl", hash = "sha256:90cd441a1638ae176eab4d8b6b94ab4ec24b212ed4c3fbee2a6e74672481d4f8"},
+    {file = "Pillow-7.1.1-py3.8-macosx-10.9-x86_64.egg", hash = "sha256:5eef904c82b5f8e4256e8d420c971357da2884c0b812ba4efa15a7ad2ec66247"},
+    {file = "Pillow-7.1.1.tar.gz", hash = "sha256:0f89ddc77cf421b8cd34ae852309501458942bf370831b4a9b406156b599a14e"},
 ]
 pipenv = [
     {file = "pipenv-2018.11.26-py2-none-any.whl", hash = "sha256:7df8e33a2387de6f537836f48ac6fcd94eda6ed9ba3d5e3fd52e35b5bc7ff49e"},
@@ -2536,19 +2537,19 @@ pluggy = [
     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
 ]
 psycopg2 = [
-    {file = "psycopg2-2.8.4-cp27-cp27m-win32.whl", hash = "sha256:72772181d9bad1fa349792a1e7384dde56742c14af2b9986013eb94a240f005b"},
-    {file = "psycopg2-2.8.4-cp27-cp27m-win_amd64.whl", hash = "sha256:893c11064b347b24ecdd277a094413e1954f8a4e8cdaf7ffbe7ca3db87c103f0"},
-    {file = "psycopg2-2.8.4-cp34-cp34m-win32.whl", hash = "sha256:9ab75e0b2820880ae24b7136c4d230383e07db014456a476d096591172569c38"},
-    {file = "psycopg2-2.8.4-cp34-cp34m-win_amd64.whl", hash = "sha256:b0845e3bdd4aa18dc2f9b6fb78fbd3d9d371ad167fd6d1b7ad01c0a6cdad4fc6"},
-    {file = "psycopg2-2.8.4-cp35-cp35m-win32.whl", hash = "sha256:ef6df7e14698e79c59c7ee7cf94cd62e5b869db369ed4b1b8f7b729ea825712a"},
-    {file = "psycopg2-2.8.4-cp35-cp35m-win_amd64.whl", hash = "sha256:965c4c93e33e6984d8031f74e51227bd755376a9df6993774fd5b6fb3288b1f4"},
-    {file = "psycopg2-2.8.4-cp36-cp36m-win32.whl", hash = "sha256:ed686e5926929887e2c7ae0a700e32c6129abb798b4ad2b846e933de21508151"},
-    {file = "psycopg2-2.8.4-cp36-cp36m-win_amd64.whl", hash = "sha256:dca2d7203f0dfce8ea4b3efd668f8ea65cd2b35112638e488a4c12594015f67b"},
-    {file = "psycopg2-2.8.4-cp37-cp37m-win32.whl", hash = "sha256:8396be6e5ff844282d4d49b81631772f80dabae5658d432202faf101f5283b7c"},
-    {file = "psycopg2-2.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:47fc642bf6f427805daf52d6e52619fe0637648fe27017062d898f3bf891419d"},
-    {file = "psycopg2-2.8.4-cp38-cp38-win32.whl", hash = "sha256:4212ca404c4445dc5746c0d68db27d2cbfb87b523fe233dc84ecd24062e35677"},
-    {file = "psycopg2-2.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:92a07dfd4d7c325dd177548c4134052d4842222833576c8391aab6f74038fc3f"},
-    {file = "psycopg2-2.8.4.tar.gz", hash = "sha256:f898e5cc0a662a9e12bde6f931263a1bbd350cfb18e1d5336a12927851825bb6"},
+    {file = "psycopg2-2.8.5-cp27-cp27m-win32.whl", hash = "sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf"},
+    {file = "psycopg2-2.8.5-cp27-cp27m-win_amd64.whl", hash = "sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb"},
+    {file = "psycopg2-2.8.5-cp34-cp34m-win32.whl", hash = "sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72"},
+    {file = "psycopg2-2.8.5-cp34-cp34m-win_amd64.whl", hash = "sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e"},
+    {file = "psycopg2-2.8.5-cp35-cp35m-win32.whl", hash = "sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055"},
+    {file = "psycopg2-2.8.5-cp35-cp35m-win_amd64.whl", hash = "sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52"},
+    {file = "psycopg2-2.8.5-cp36-cp36m-win32.whl", hash = "sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c"},
+    {file = "psycopg2-2.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81"},
+    {file = "psycopg2-2.8.5-cp37-cp37m-win32.whl", hash = "sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9"},
+    {file = "psycopg2-2.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080"},
+    {file = "psycopg2-2.8.5-cp38-cp38-win32.whl", hash = "sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7"},
+    {file = "psycopg2-2.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535"},
+    {file = "psycopg2-2.8.5.tar.gz", hash = "sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818"},
 ]
 py = [
     {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"},
@@ -2601,8 +2602,8 @@ pygments = [
     {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
 ]
 pyparsing = [
-    {file = "pyparsing-2.4.6-py2.py3-none-any.whl", hash = "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"},
-    {file = "pyparsing-2.4.6.tar.gz", hash = "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f"},
+    {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
+    {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
 ]
 pytest = [
     {file = "pytest-5.4.1-py3-none-any.whl", hash = "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172"},
@@ -2672,27 +2673,27 @@ redis = [
     {file = "redis-3.4.1.tar.gz", hash = "sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f"},
 ]
 regex = [
-    {file = "regex-2020.2.20-cp27-cp27m-win32.whl", hash = "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb"},
-    {file = "regex-2020.2.20-cp27-cp27m-win_amd64.whl", hash = "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74"},
-    {file = "regex-2020.2.20-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400"},
-    {file = "regex-2020.2.20-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0"},
-    {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc"},
-    {file = "regex-2020.2.20-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"},
-    {file = "regex-2020.2.20-cp36-cp36m-win32.whl", hash = "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69"},
-    {file = "regex-2020.2.20-cp36-cp36m-win_amd64.whl", hash = "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b"},
-    {file = "regex-2020.2.20-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e"},
-    {file = "regex-2020.2.20-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242"},
-    {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce"},
-    {file = "regex-2020.2.20-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab"},
-    {file = "regex-2020.2.20-cp37-cp37m-win32.whl", hash = "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431"},
-    {file = "regex-2020.2.20-cp37-cp37m-win_amd64.whl", hash = "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1"},
-    {file = "regex-2020.2.20-cp38-cp38-manylinux1_i686.whl", hash = "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045"},
-    {file = "regex-2020.2.20-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26"},
-    {file = "regex-2020.2.20-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2"},
-    {file = "regex-2020.2.20-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70"},
-    {file = "regex-2020.2.20-cp38-cp38-win32.whl", hash = "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d"},
-    {file = "regex-2020.2.20-cp38-cp38-win_amd64.whl", hash = "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa"},
-    {file = "regex-2020.2.20.tar.gz", hash = "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5"},
+    {file = "regex-2020.4.4-cp27-cp27m-win32.whl", hash = "sha256:90742c6ff121a9c5b261b9b215cb476eea97df98ea82037ec8ac95d1be7a034f"},
+    {file = "regex-2020.4.4-cp27-cp27m-win_amd64.whl", hash = "sha256:24f4f4062eb16c5bbfff6a22312e8eab92c2c99c51a02e39b4eae54ce8255cd1"},
+    {file = "regex-2020.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:08119f707f0ebf2da60d2f24c2f39ca616277bb67ef6c92b72cbf90cbe3a556b"},
+    {file = "regex-2020.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c9423a150d3a4fc0f3f2aae897a59919acd293f4cb397429b120a5fcd96ea3db"},
+    {file = "regex-2020.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c087bff162158536387c53647411db09b6ee3f9603c334c90943e97b1052a156"},
+    {file = "regex-2020.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1cbe0fa0b7f673400eb29e9ef41d4f53638f65f9a2143854de6b1ce2899185c3"},
+    {file = "regex-2020.4.4-cp36-cp36m-win32.whl", hash = "sha256:0ce9537396d8f556bcfc317c65b6a0705320701e5ce511f05fc04421ba05b8a8"},
+    {file = "regex-2020.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:7e1037073b1b7053ee74c3c6c0ada80f3501ec29d5f46e42669378eae6d4405a"},
+    {file = "regex-2020.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4385f12aa289d79419fede43f979e372f527892ac44a541b5446617e4406c468"},
+    {file = "regex-2020.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a58dd45cb865be0ce1d5ecc4cfc85cd8c6867bea66733623e54bd95131f473b6"},
+    {file = "regex-2020.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ccccdd84912875e34c5ad2d06e1989d890d43af6c2242c6fcfa51556997af6cd"},
+    {file = "regex-2020.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ea4adf02d23b437684cd388d557bf76e3afa72f7fed5bbc013482cc00c816948"},
+    {file = "regex-2020.4.4-cp37-cp37m-win32.whl", hash = "sha256:2294f8b70e058a2553cd009df003a20802ef75b3c629506be20687df0908177e"},
+    {file = "regex-2020.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:e91ba11da11cf770f389e47c3f5c30473e6d85e06d7fd9dcba0017d2867aab4a"},
+    {file = "regex-2020.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5635cd1ed0a12b4c42cce18a8d2fb53ff13ff537f09de5fd791e97de27b6400e"},
+    {file = "regex-2020.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:23069d9c07e115537f37270d1d5faea3e0bdded8279081c4d4d607a2ad393683"},
+    {file = "regex-2020.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c162a21e0da33eb3d31a3ac17a51db5e634fc347f650d271f0305d96601dc15b"},
+    {file = "regex-2020.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:fb95debbd1a824b2c4376932f2216cc186912e389bdb0e27147778cf6acb3f89"},
+    {file = "regex-2020.4.4-cp38-cp38-win32.whl", hash = "sha256:2a3bf8b48f8e37c3a40bb3f854bf0121c194e69a650b209628d951190b862de3"},
+    {file = "regex-2020.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bfed051dbff32fd8945eccca70f5e22b55e4148d2a8a45141a3b053d6455ae3"},
+    {file = "regex-2020.4.4.tar.gz", hash = "sha256:295badf61a51add2d428a46b8580309c520d8b26e769868b922750cf3ce67142"},
 ]
 requests = [
     {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"},
@@ -2706,8 +2707,8 @@ safety = [
     {file = "safety-1.8.7.tar.gz", hash = "sha256:3016631e0dd17193d6cf12e8ed1af92df399585e8ee0e4b1300d9e7e32b54903"},
 ]
 scramp = [
-    {file = "scramp-1.1.0-py3-none-any.whl", hash = "sha256:e09d2a9be5adeb94cbeb56fc54a61fc5f5b6e140e679b2b60d1f7a8d6478d906"},
-    {file = "scramp-1.1.0.tar.gz", hash = "sha256:475aa6296deb2737b86e9df9098e8eca0f30c8ad1cc0a8adadb99ef012a5ceba"},
+    {file = "scramp-1.1.1-py3-none-any.whl", hash = "sha256:a2c740624642de84f77327da8f56b2f030c5afd10deccaedbb8eb6108a66dfc1"},
+    {file = "scramp-1.1.1.tar.gz", hash = "sha256:b57eb0ae2f9240b15b5d0dab2ea8e40b43eef13ac66d3f627a79ef85a6da0927"},
 ]
 selenium = [
     {file = "selenium-3.141.0-py2.py3-none-any.whl", hash = "sha256:2d7131d7bc5a5b99a2d9b04aaf2612c411b03b8ca1b1ee8d3de5845a9be2cb3c"},
@@ -2718,16 +2719,16 @@ six = [
     {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"},
 ]
 smmap = [
-    {file = "smmap-3.0.1-py2.py3-none-any.whl", hash = "sha256:5fead614cf2de17ee0707a8c6a5f2aa5a2fc6c698c70993ba42f515485ffda78"},
-    {file = "smmap-3.0.1.tar.gz", hash = "sha256:171484fe62793e3626c8b05dd752eb2ca01854b0c55a1efc0dc4210fccb65446"},
+    {file = "smmap-3.0.2-py2.py3-none-any.whl", hash = "sha256:52ea78b3e708d2c2b0cfe93b6fc3fbeec53db913345c26be6ed84c11ed8bebc1"},
+    {file = "smmap-3.0.2.tar.gz", hash = "sha256:b46d3fc69ba5f367df96d91f8271e8ad667a198d5a28e215a6c3d9acd133a911"},
 ]
 snowballstemmer = [
     {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"},
     {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"},
 ]
 soupsieve = [
-    {file = "soupsieve-2.0-py2.py3-none-any.whl", hash = "sha256:fcd71e08c0aee99aca1b73f45478549ee7e7fc006d51b37bec9e9def7dc22b69"},
-    {file = "soupsieve-2.0.tar.gz", hash = "sha256:e914534802d7ffd233242b785229d5ba0766a7f487385e3f714446a07bf540ae"},
+    {file = "soupsieve-1.9.5-py2.py3-none-any.whl", hash = "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5"},
+    {file = "soupsieve-1.9.5.tar.gz", hash = "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda"},
 ]
 sphinx = [
     {file = "Sphinx-2.4.4-py3-none-any.whl", hash = "sha256:fc312670b56cb54920d6cc2ced455a22a547910de10b3142276495ced49231cb"},
@@ -2825,9 +2826,9 @@ typed-ast = [
     {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
 ]
 typing-extensions = [
-    {file = "typing_extensions-3.7.4.1-py2-none-any.whl", hash = "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d"},
-    {file = "typing_extensions-3.7.4.1-py3-none-any.whl", hash = "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"},
-    {file = "typing_extensions-3.7.4.1.tar.gz", hash = "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2"},
+    {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"},
+    {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"},
+    {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"},
 ]
 urllib3 = [
     {file = "urllib3-1.25.8-py2.py3-none-any.whl", hash = "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc"},
@@ -2838,8 +2839,8 @@ vine = [
     {file = "vine-1.3.0.tar.gz", hash = "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87"},
 ]
 virtualenv = [
-    {file = "virtualenv-20.0.15-py2.py3-none-any.whl", hash = "sha256:4e399f48c6b71228bf79f5febd27e3bbb753d9d5905776a86667bc61ab628a25"},
-    {file = "virtualenv-20.0.15.tar.gz", hash = "sha256:9e81279f4a9d16d1c0654a127c2c86e5bca2073585341691882c1e66e31ef8a5"},
+    {file = "virtualenv-20.0.17-py2.py3-none-any.whl", hash = "sha256:00cfe8605fb97f5a59d52baab78e6070e72c12ca64f51151695407cc0eb8a431"},
+    {file = "virtualenv-20.0.17.tar.gz", hash = "sha256:c8364ec469084046c779c9a11ae6340094e8a0bf1d844330fc55c1cefe67c172"},
 ]
 virtualenv-clone = [
     {file = "virtualenv-clone-0.5.4.tar.gz", hash = "sha256:665e48dd54c84b98b71a657acb49104c54e7652bce9c1c4f6c6976ed4c827a29"},
diff --git a/pyproject.toml b/pyproject.toml
index 9d4b5c90cc54ccb37b044417bd9bdd681d521327..cc3967bcd613a1616e9cfef5e6eb7c0f21ce3588 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -70,10 +70,13 @@ django-bleach = "^0.6.1"
 django-memoize = "^2.2.1"
 django-guardian = "^2.2.0"
 rules = "^2.2"
+django-haystack = "^3.0"
+celery-haystack = {version="^0.3.1", optional=true}
+django-dbbackup = "^3.3.0"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]
-celery = ["Celery", "django-celery-results", "django-celery-beat", "django-celery-email"]
+celery = ["Celery", "django-celery-results", "django-celery-beat", "django-celery-email", "celery-haystack"]
 
 [tool.poetry.dev-dependencies]
 sphinx = "^2.1"