diff --git a/deployment/docker/check_psql.py b/deployment/docker/check_psql.py
index e542e5c8c075e72f633bfa0b06d768df501f5b15..1c2d2e41d8378c916d3817d322ee95af396ef697 100755
--- a/deployment/docker/check_psql.py
+++ b/deployment/docker/check_psql.py
@@ -9,17 +9,17 @@ except ImportError:
     import psycopg2 as psycopg
 
 
-url = os.getenv("DATABASE_URL")
+url = os.getenv('DATABASE_URL')
 if url is None or url == '':
     print('No DATABASE_URL specified!', file=sys.stderr)
     sys.exit(2)
 
 try:
     if url.startswith('postgis://'):
-        url = 'postgresql://' + url[len('postgis://'):]
+        url = 'postgresql://' + url[len('postgis://') :]
     psycopg.connect(url)
 
 except Exception as err:
     print('ERROR', file=sys.stderr)
     print('    ', err, sep='', file=sys.stderr)
-    exit(1)
+    sys.exit(1)
diff --git a/deployment/local_settings.docker-example.py b/deployment/local_settings.docker-example.py
index a07fb65e16c51efa78f8329dc598927ed318a20c..3a1449f286a6d71fb943f9dca0db31f24b59750a 100644
--- a/deployment/local_settings.docker-example.py
+++ b/deployment/local_settings.docker-example.py
@@ -1,5 +1,5 @@
 # Dies ist ein Beispiel für eine local_settings.py in einem Docker-Deployment.
-
+# ruff: noqa: ERA001
 IS_ADMIN = True
 IS_API = True
 IS_BACKOFFICE = True
@@ -11,10 +11,7 @@ WORKADVENTURE_URL_SCHEME = 'https://play.{assembly_slug}.at.rc3.world/'  # other
 LOGGING = {
     'version': 1,
     'disable_existing_loggers': False,
-
-    'filters': {
-    },
-
+    'filters': {},
     'formatters': {
         'verbose': {
             'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
@@ -25,7 +22,6 @@ LOGGING = {
             'style': '{',
         },
     },
-
     'handlers': {
         'file': {
             'level': 'INFO',
@@ -45,7 +41,6 @@ LOGGING = {
         #     'class': 'django.utils.log.AdminEmailHandler'
         # },
     },
-
     'loggers': {
         'django': {
             'handlers': ['file', 'console'],
diff --git a/src/api/permissions.py b/src/api/permissions.py
index 5c2c7aae635b8d844852787d8ad6f284d59dec39..431a9b1806bea49f50207cb3b42a9c941af41bec 100644
--- a/src/api/permissions.py
+++ b/src/api/permissions.py
@@ -1,6 +1,7 @@
+from rest_framework import permissions
+
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
-from rest_framework import permissions
 
 from core.models.assemblies import Assembly
 from core.models.badges import Badge
diff --git a/src/api/schedule.py b/src/api/schedule.py
index 33da86f37b2e6ac05a70756217e51c3c7d1349a0..5c9ed35a26409e65cb09a6d28667d4a239a6a887 100644
--- a/src/api/schedule.py
+++ b/src/api/schedule.py
@@ -1,40 +1,42 @@
+# ruff: noqa: PLW2901
 import json
-import re
 import logging
-
+import re
 from collections import OrderedDict
 from datetime import datetime, timedelta
+from typing import Optional
 from uuid import UUID
+
 from lxml import etree as ET
-from typing import Optional
-# from xml.etree import cElementTree as ET
 
 from django.conf import settings
 
 from core.models.conference import Conference
-from core.models.rooms import Room
 from core.models.events import Event
+from core.models.rooms import Room
 
 logger = logging.getLogger(__name__)
 
 # template for optimized attribute order
-event_template = OrderedDict({
-    'guid': None,
-    'id': None,
-    'date': None,
-    'start': None,
-    'duration': None,
-    'room': None,
-    'slug': None,
-    'url': None,
-    'title': None,
-    'subtitle': None,
-    'language': None,
-    'track': None,
-    'type': 'other',
-    'abstract': None,
-    'description': None,
-})
+event_template = OrderedDict(
+    {
+        'guid': None,
+        'id': None,
+        'date': None,
+        'start': None,
+        'duration': None,
+        'room': None,
+        'slug': None,
+        'url': None,
+        'title': None,
+        'subtitle': None,
+        'language': None,
+        'track': None,
+        'type': 'other',
+        'abstract': None,
+        'description': None,
+    }
+)
 
 
 class Day:
@@ -82,7 +84,7 @@ class ScheduleEncoder(json.JSONEncoder):
     tz = None
 
     def encode_duration(self, duration: Optional[timedelta]) -> Optional[str]:
-        """ converts a python `timedelta` to the schedule xml timedelta string that represents this timedelta. ([d:]HH:mm) """
+        """converts a python `timedelta` to the schedule xml timedelta string that represents this timedelta. ([d:]HH:mm)"""
 
         if duration is None:
             return None
@@ -91,39 +93,44 @@ class ScheduleEncoder(json.JSONEncoder):
         hours = duration.seconds // 3600
         minutes = (duration.seconds % 3600) // 60
         if days:
-            return f"{days}:{hours:02d}:{minutes:02d}"
-        return f"{hours:02d}:{minutes:02d}"
+            return f'{days}:{hours:02d}:{minutes:02d}'
+        return f'{hours:02d}:{minutes:02d}'
 
     def encode_event(self, event: Event, tz=None):
         start = event.schedule_start.astimezone(tz or self.tz) if event.schedule_start is not None else None
         additional_data = event.additional_data or {}
         legacy_id = additional_data.get('id') or int(re.sub('[^0-9]+', '', str(event.id))[0:6])
         slug = f'{event.conference.slug}-{legacy_id}-{event.slug}'
-        return OrderedDict({
-            **event_template,
-            'id': legacy_id,
-            'description': event.description,  # TODO: if the description also exists in additional_data it is overwritten due to concatination with abstract
-            **additional_data,
-            'slug': slug,
-            'url': event.get_absolute_url(),
-            'guid': event.id,
-            'date': start.isoformat() if start is not None else None,
-            'start': start.strftime('%H:%M') if start is not None else None,
-            'duration': self.encode_duration(event.schedule_duration),
-            'room': event.room.name if event.room is not None else None,
-            'title': event.name,
-            'language': event.language,
-            'track': event.track.name if event.track else None,
-        })
+        return OrderedDict(
+            {
+                **event_template,
+                'id': legacy_id,
+                # TODO: if the description also exists in additional_data it is overwritten due to concatenation with abstract
+                'description': event.description,
+                **additional_data,
+                'slug': slug,
+                'url': event.get_absolute_url(),
+                'guid': event.id,
+                'date': start.isoformat() if start is not None else None,
+                'start': start.strftime('%H:%M') if start is not None else None,
+                'duration': self.encode_duration(event.schedule_duration),
+                'room': event.room.name if event.room is not None else None,
+                'title': event.name,
+                'language': event.language,
+                'track': event.track.name if event.track else None,
+            }
+        )
 
     def encode_day(self, obj: Day):
-        return OrderedDict({
-            'index': obj.day.index,
-            'date': obj.day.start.strftime('%Y-%m-%d'),
-            'day_start': obj.day.start.isoformat(),
-            'day_end': obj.day.end.isoformat(),
-            'rooms': obj.rooms
-        })
+        return OrderedDict(
+            {
+                'index': obj.day.index,
+                'date': obj.day.start.strftime('%Y-%m-%d'),
+                'day_start': obj.day.start.isoformat(),
+                'day_end': obj.day.end.isoformat(),
+                'rooms': obj.rooms,
+            }
+        )
 
     def encode_room(self, obj: RoomDay):
         return obj.events
@@ -131,10 +138,7 @@ class ScheduleEncoder(json.JSONEncoder):
     def transform(self, obj):
         if isinstance(obj, Schedule):
             self.tz = obj.tz
-            return {
-                '$schema': 'https://c3voc.de/schedule/schema.json',
-                'schedule': obj._schedule
-            }
+            return {'$schema': 'https://c3voc.de/schedule/schema.json', 'schedule': obj._schedule}
         if isinstance(obj, Day):
             return self.encode_day(obj)
         if isinstance(obj, RoomDay):
@@ -151,9 +155,10 @@ class ScheduleEncoder(json.JSONEncoder):
 
 
 class Schedule:
-    '''
+    """
     Schedule class with import and export methods
-    '''
+    """
+
     _schedule = None
     tz = None
     conference = None
@@ -163,7 +168,7 @@ class Schedule:
         self.tz = conference.timezone
 
         self._schedule = {
-            'version': datetime.now(self.tz).strftime("%Y-%m-%d %H:%M"),
+            'version': datetime.now(self.tz).strftime('%Y-%m-%d %H:%M'),
             'base_url': settings.PLAINUI_BASE_URL,  # 'https://events.ccc.de/' + conference.slug + '/',
             'conference': {
                 'acronym': conference.slug,
@@ -174,7 +179,7 @@ class Schedule:
                 'timeslot_duration': '00:10',
                 'time_zone_name': self.tz.key if self.tz.key else datetime.now(self.tz).tzname(),
                 'days': [Day(day) for day in conference.days],
-            }
+            },
         }
 
     def __getitem__(self, key):
@@ -206,7 +211,7 @@ class Schedule:
 
     def add_events(self, events):
         for event in events:
-            try:
+            try:  # noqa: SIM105
                 self.add_event(event)
             except Warning:
                 # TODO log event and error
@@ -242,7 +247,7 @@ class Schedule:
         def _set_attrib(tag, k, v):
             if isinstance(v, str):
                 tag.set(k, v)
-            elif isinstance(v, int) or isinstance(v, UUID):
+            elif isinstance(v, (UUID, int)):
                 tag.set(k, str(v))
             elif v is not None:
                 logger.error('unknown attribute type %s=%s', k, v)
@@ -259,7 +264,7 @@ class Schedule:
             elif parent == 'person':
                 node.text = d['public_name']
                 _set_attrib(node, 'id', d['id'])
-            elif isinstance(d, dict) or isinstance(d, OrderedDict):
+            elif isinstance(d, (OrderedDict, dict)):
                 if parent == 'schedule' and 'base_url' in d:
                     d['conference']['base_url'] = d['base_url']
                     del d['base_url']
@@ -315,10 +320,7 @@ class Schedule:
                             continue
                         elif k == 'do_not_record':
                             k = 'recording'
-                            v = OrderedDict([
-                                ('license', recording_license),
-                                ('optout', 'true' if v else 'false')
-                            ])
+                            v = OrderedDict([('license', recording_license), ('optout', 'true' if v else 'false')])
                         if isinstance(v, RoomDay):
                             _set_attrib(node_, 'guid', v.room.id)
                             for event in v.events:
@@ -333,13 +335,9 @@ class Schedule:
                             _to_etree(v, ET.SubElement(node_, k), k)
             else:
                 raise Exception('unknown type', d, parent)
-        # assert isinstance(self._schedule, dict) and len(self._schedule) == 1
-        # encoder = ScheduleEncoder()
-        # schedule_dict = encoder.default(self.json())
 
         root_node = ET.Element('schedule')
-        root_node.set("{http://www.w3.org/2001/XMLSchema-instance}noNamespaceSchemaLocation", "https://c3voc.de/schedule/schema.xsd")
+        root_node.set('{http://www.w3.org/2001/XMLSchema-instance}noNamespaceSchemaLocation', 'https://c3voc.de/schedule/schema.xsd')
         _to_etree(self._schedule, root_node, 'schedule')
 
         return ET.tounicode(root_node, doctype='<?xml version="1.0"?>')
-        # return ET.tostring(root_node, xml_declaration=True, encoding='utf-8', pretty_print = True, )
diff --git a/src/api/serializers.py b/src/api/serializers.py
index fa6856eb0155276df04f4294a0f2e6e90bf050fe..15310fb7cff40c88fa716339ce59d40b034c2c0a 100644
--- a/src/api/serializers.py
+++ b/src/api/serializers.py
@@ -1,15 +1,18 @@
-from django.core.exceptions import SuspiciousOperation, ValidationError
+import contextlib
+
 from rest_framework import serializers
 from rest_framework.relations import HyperlinkedIdentityField
 from rest_framework.reverse import reverse
 
+from django.core.exceptions import SuspiciousOperation, ValidationError
+
+from core.models.assemblies import Assembly
 from core.models.badges import Badge, BadgeToken, BadgeTokenTimeConstraint
 from core.models.conference import Conference, ConferenceMember, ConferenceTrack
 from core.models.events import Event
 from core.models.metanavi import MetaNavItem
 from core.models.rooms import Room
 from core.models.users import UserTimelineEntry
-from core.models.assemblies import Assembly
 
 
 class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
@@ -21,11 +24,12 @@ class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
 
     taken from https://github.com/encode/django-rest-framework/issues/1024
     """
+
     lookup_fields = (('pk', 'pk'),)
 
     def __init__(self, *args, **kwargs):
         self.lookup_fields = kwargs.pop('lookup_fields', self.lookup_fields)
-        super(ParameterisedHyperlinkedIdentityField, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
     def get_url(self, obj, view_name, request, format):
         """
@@ -46,14 +50,8 @@ class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField):
 
 class ValidatingModelSerializer(serializers.ModelSerializer):
     def validate(self, data):
-        instance = self.Meta.model(**{
-            field: value
-            for field, value in data.items()
-            if field in self.Meta.model._meta.fields
-        })
-        for f, v in {field: value
-                     for field, value in data.items()
-                     if field in self.Meta.model._meta.fields}.items():
+        instance = self.Meta.model(**{field: value for field, value in data.items() if field in self.Meta.model._meta.fields})
+        for f, v in {field: value for field, value in data.items() if field in self.Meta.model._meta.fields}.items():
             getattr(instance, f).set(v)
         try:
             instance.clean()
@@ -81,13 +79,11 @@ class HubModelSerializer(ValidatingModelSerializer):
         self.request_user = request_user = self.context['request'].user
         self.conference_member = None
         if request_user.is_authenticated:
-            try:
+            with contextlib.suppress(ConferenceMember.DoesNotExist):
                 self.conference_member = ConferenceMember.objects.select_related('conference').get(
                     conference__slug=conference_slug,
                     user=request_user,
                 )
-            except ConferenceMember.DoesNotExist:
-                pass
 
         # store if the request's user has staff permissions in the conference (either direct or globally)
         self.is_staff = request_user.is_superuser or request_user.is_staff or (self.conference_member is not None and self.conference_member.is_staff)
@@ -104,18 +100,16 @@ class HubModelSerializer(ValidatingModelSerializer):
 
 
 class ConferenceSerializer(HubModelSerializer):
-    tracks = serializers.SlugRelatedField(
-        many=True,
-        read_only=True,
-        slug_field='slug'
-    )
+    tracks = serializers.SlugRelatedField(many=True, read_only=True, slug_field='slug')
 
     class Meta:
         model = Conference
         fields = [
-            'slug', 'name',
+            'slug',
+            'name',
             'is_public',
-            'start', 'end',
+            'start',
+            'end',
             'registration_deadline',
             'tracks',
         ]
@@ -126,27 +120,19 @@ class ConferenceTrackSerializer(HubModelSerializer):
     class Meta:
         model = ConferenceTrack
         read_only_fields = ['id']
-        fields = [
-            'conference',
-            'slug',
-            'name',
-            'is_public',
-            'id'
-        ]
+        fields = ['conference', 'slug', 'name', 'is_public', 'id']
         staff_only_fields = ['is_public']
 
 
 class AssemblySerializer(HubModelSerializer):
-    conference = serializers.SlugRelatedField(
-        read_only=True,
-        slug_field='slug'
-    )
+    conference = serializers.SlugRelatedField(read_only=True, slug_field='slug')
     parent = serializers.SlugRelatedField(slug_field='slug', queryset=Assembly.objects.none)
 
     events_url = ParameterisedHyperlinkedIdentityField(view_name='api:assembly-events', lookup_fields=(('conference_slug', 'conference'), ('slug', 'assembly')))
     rooms_url = ParameterisedHyperlinkedIdentityField(view_name='api:assembly-rooms', lookup_fields=(('conference_slug', 'conference'), ('slug', 'assembly')))
-    badges_url = ParameterisedHyperlinkedIdentityField(view_name='api:assembly-badges-list-create',
-                                                       lookup_fields=(('conference_slug', 'conference'), ('slug', 'assembly')))
+    badges_url = ParameterisedHyperlinkedIdentityField(
+        view_name='api:assembly-badges-list-create', lookup_fields=(('conference_slug', 'conference'), ('slug', 'assembly'))
+    )
 
     class Meta:
         model = Assembly
@@ -178,11 +164,11 @@ class BadgeSerializer(HubModelSerializer):
     class Meta:
         model = Badge
         exclude = ['issuing_assembly', 'conference', 'issuing_token']
-    badge_token_url = ParameterisedHyperlinkedIdentityField(view_name='api:badge-token-list-create',
-                                                            lookup_fields=(
-                                                                ('issuing_assembly.conference_slug', 'conference'),
-                                                                ('issuing_assembly.slug', 'assembly'),
-                                                                ('pk', 'pk')))
+
+    badge_token_url = ParameterisedHyperlinkedIdentityField(
+        view_name='api:badge-token-list-create',
+        lookup_fields=(('issuing_assembly.conference_slug', 'conference'), ('issuing_assembly.slug', 'assembly'), ('pk', 'pk')),
+    )
 
     def create(self, validated_data):
         issuing_assembly = Assembly.objects.filter(slug=self.context['assembly'])
@@ -223,15 +209,9 @@ class BadgeTokenUpdateSerializer(BadgeTokenSerializer):
 
 
 class RoomSerializer(HubModelSerializer):
-    conference = serializers.SlugRelatedField(
-        read_only=True,
-        slug_field='slug'
-    )
+    conference = serializers.SlugRelatedField(read_only=True, slug_field='slug')
 
-    assembly = serializers.SlugRelatedField(
-        read_only=True,
-        slug_field='slug'
-    )
+    assembly = serializers.SlugRelatedField(read_only=True, slug_field='slug')
 
     links = serializers.StringRelatedField(
         many=True,
@@ -288,7 +268,7 @@ class EventSerializer(HubModelSerializer):
     def update(self, instance, validated_data):
         # is somebody trying to change the event ID?
         if (given_id := validated_data.get('id')) is not None and given_id != instance.id:
-            raise SuspiciousOperation('You cannot update an event\'s id.')
+            raise SuspiciousOperation("You cannot update an event's id.")
 
         # prevent some fields from being updated, no matter what
         for protected_field in ['id', 'conference']:
@@ -312,10 +292,7 @@ class UserTimelineEntrySerializer(ValidatingModelSerializer):
 
 
 class MetaNavItemSerializer(HubModelSerializer):
-    conference = serializers.SlugRelatedField(
-        read_only=True,
-        slug_field='slug'
-    )
+    conference = serializers.SlugRelatedField(read_only=True, slug_field='slug')
 
     class Meta:
         model = MetaNavItem
diff --git a/src/api/tests/__init__.py b/src/api/tests/__init__.py
index e6a579db4f2421d51e9da0eec44b1afb06224fbc..8fb7973a17a79486bf8775d3b886df46eb305234 100644
--- a/src/api/tests/__init__.py
+++ b/src/api/tests/__init__.py
@@ -6,4 +6,4 @@ from .metrics import *  # noqa: F401, F403
 from .schedule import *  # noqa: F401, F403
 from .workadventure import *  # noqa: F401, F403
 
-__all__ = '*'
+__all__ = ('*',)  # noqa: F405
diff --git a/src/api/tests/badges/create_redeem_token.py b/src/api/tests/badges/create_redeem_token.py
index 9f8ea1f236142d2db6a3ce7e7e5f3135b3efccfe..125f02f8e912ca4224f3353c859fda31acdf91d6 100644
--- a/src/api/tests/badges/create_redeem_token.py
+++ b/src/api/tests/badges/create_redeem_token.py
@@ -1,11 +1,11 @@
 from datetime import datetime
+from http import HTTPStatus
 
-from django.test import TestCase
-from django.urls import reverse
 from rest_framework.authtoken.models import Token
 from zoneinfo import ZoneInfo
 
-from http import HTTPStatus
+from django.test import TestCase
+from django.urls import reverse
 
 from core.models import Assembly, Badge, BadgeToken, Conference, PlatformUser
 
@@ -14,7 +14,9 @@ class CreateRedeemTokenTests(TestCase):
     def setUp(self):
         tz = ZoneInfo('CET')
         self.conf = Conference(
-            slug='conf', name='TestConf', is_public=True,
+            slug='conf',
+            name='TestConf',
+            is_public=True,
             start=datetime(2020, 12, 27, 1, 23, 45, tzinfo=tz),
             end=datetime(2020, 12, 30, 23, 45, 00, tzinfo=tz),
         )
@@ -22,7 +24,11 @@ class CreateRedeemTokenTests(TestCase):
         self.assembly = Assembly(name='TestAssembly', slug='asmbly', conference=self.conf, state_assembly=Assembly.State.PLACED)
         self.assembly.save()
 
-        self.badge = Badge(conference=self.conf, issuing_assembly=self.assembly, name="Test Badge", )
+        self.badge = Badge(
+            conference=self.conf,
+            issuing_assembly=self.assembly,
+            name='Test Badge',
+        )
         self.badge_token = self.badge.reset_token()
 
         self.user = PlatformUser(username='bernd', is_active=True)
@@ -32,27 +38,27 @@ class CreateRedeemTokenTests(TestCase):
         self.token.save()
 
     def test_create_redeem_token_invalid_issuing_token(self):
-        url = reverse('api:badge-token-create-with-token', kwargs={'conference': self.conf.slug, 'assembly': self.assembly, "issuing_token": "invalid"})
-        resp = self.client.post(url, {"issuing_token": "test"})
+        url = reverse('api:badge-token-create-with-token', kwargs={'conference': self.conf.slug, 'assembly': self.assembly, 'issuing_token': 'invalid'})
+        resp = self.client.post(url, {'issuing_token': 'test'})
         self.assertEqual(resp.status_code, 404)
 
     def test_create_redeem_invalid_conference(self):
-        url = reverse('api:badge-token-create-with-token', kwargs={'conference': "invalid", 'assembly': self.assembly, "issuing_token": self.badge_token})
-        resp = self.client.post(url, {"issuing_token": self.badge_token, "badge_class": "single"})
+        url = reverse('api:badge-token-create-with-token', kwargs={'conference': 'invalid', 'assembly': self.assembly, 'issuing_token': self.badge_token})
+        resp = self.client.post(url, {'issuing_token': self.badge_token, 'badge_class': 'single'})
         self.assertEqual(resp.status_code, 404)
 
     def test_create_redeem_token_limited(self):
-        url = reverse('api:badge-token-create-with-token', kwargs={'conference': self.conf.slug, 'assembly': self.assembly, "issuing_token": self.badge_token})
-        resp = self.client.post(url, {"issuing_token": self.badge_token, "redeemable_count": 20})
+        url = reverse('api:badge-token-create-with-token', kwargs={'conference': self.conf.slug, 'assembly': self.assembly, 'issuing_token': self.badge_token})
+        resp = self.client.post(url, {'issuing_token': self.badge_token, 'redeemable_count': 20})
         self.assertEqual(resp.status_code, HTTPStatus.CREATED)
         badge_token = BadgeToken.objects.get(badge=self.badge)
-        self.assertEqual(str(badge_token.token), resp.json()["token"])
+        self.assertEqual(str(badge_token.token), resp.json()['token'])
         self.assertEqual(badge_token.redeemable_count, 20)
 
     def test_create_redeem_token(self):
-        url = reverse('api:badge-token-create-with-token', kwargs={'conference': self.conf.slug, 'assembly': self.assembly, "issuing_token": self.badge_token})
+        url = reverse('api:badge-token-create-with-token', kwargs={'conference': self.conf.slug, 'assembly': self.assembly, 'issuing_token': self.badge_token})
         resp = self.client.post(url, {})
         self.assertEqual(resp.status_code, HTTPStatus.CREATED)
         badge_token = BadgeToken.objects.get(badge=self.badge)
-        self.assertEqual(str(badge_token.token), resp.json()["token"])
+        self.assertEqual(str(badge_token.token), resp.json()['token'])
         self.assertEqual(badge_token.redeemable_count, 0)
diff --git a/src/api/tests/bbb.py b/src/api/tests/bbb.py
index c285cbc3e17c5e3c81d4182a18a3338cc59c47df..8ad0861591722f36198f3707725fbe20a450807d 100644
--- a/src/api/tests/bbb.py
+++ b/src/api/tests/bbb.py
@@ -1,4 +1,5 @@
 from uuid import uuid4
+
 from django.test import TestCase
 from django.urls import reverse
 
@@ -12,22 +13,32 @@ class BBBTest(TestCase):
         assembly = Assembly(name='TestAssembly', slug='asmbly', conference=conf)
         assembly.save()
         room = Room(
-            conference=conf, assembly=assembly, name='Room 1',
-            room_type=Room.RoomType.BIGBLUEBUTTON, backend_status=Room.BackendStatus.ACTIVE,
-            backend_link=str(uuid4()), backend_data={'close_secret': 'asdf'}
+            conference=conf,
+            assembly=assembly,
+            name='Room 1',
+            room_type=Room.RoomType.BIGBLUEBUTTON,
+            backend_status=Room.BackendStatus.ACTIVE,
+            backend_link=str(uuid4()),
+            backend_data={'close_secret': 'asdf'},
         )
         room.save()
 
-        self.client.get(reverse('api:bbb_meeting_end'), {
-            'meetingID': room.backend_link,
-            'close_secret': 'invalid',
-        })
+        self.client.get(
+            reverse('api:bbb_meeting_end'),
+            {
+                'meetingID': room.backend_link,
+                'close_secret': 'invalid',
+            },
+        )
         room.refresh_from_db()
         self.assertEqual(room.backend_status, Room.BackendStatus.ACTIVE)
 
-        self.client.get(reverse('api:bbb_meeting_end'), {
-            'meetingID': room.backend_link,
-            'close_secret': 'asdf',
-        })
+        self.client.get(
+            reverse('api:bbb_meeting_end'),
+            {
+                'meetingID': room.backend_link,
+                'close_secret': 'asdf',
+            },
+        )
         room.refresh_from_db()
         self.assertEqual(room.backend_status, Room.BackendStatus.INACTIVE)
diff --git a/src/api/tests/map.py b/src/api/tests/map.py
index 29667843bdeb420092648460851659006c93627c..7b30bb3c486f740c1c24a2d32ae29dfaf574332e 100644
--- a/src/api/tests/map.py
+++ b/src/api/tests/map.py
@@ -1,19 +1,22 @@
-from datetime import datetime
 import json
+from datetime import datetime
+
 from zoneinfo import ZoneInfo
 
 from django.contrib.gis.geos import Point
 from django.test import TestCase
 from django.urls import reverse
 
-from core.models import Assembly, ConferenceExportCache, Conference, PlatformUser
+from core.models import Assembly, Conference, ConferenceExportCache, PlatformUser
 
 
 class MapTest(TestCase):
     def setUp(self):
         tz = ZoneInfo('CET')
         self.conf = Conference(
-            slug='conf', name='TestConf', is_public=True,
+            slug='conf',
+            name='TestConf',
+            is_public=True,
             start=datetime(2020, 12, 27, 1, 23, 45, tzinfo=tz),
             end=datetime(2020, 12, 30, 23, 45, 00, tzinfo=tz),
         )
diff --git a/src/api/tests/metrics.py b/src/api/tests/metrics.py
index 20e03fe68acf7602ed502fe4518f1eb7b7ddea10..6f56c24324a186e8e0602d75e8b9cc4712c52afe 100644
--- a/src/api/tests/metrics.py
+++ b/src/api/tests/metrics.py
@@ -1,15 +1,14 @@
 from uuid import uuid4
 
+from django.contrib.auth import get_user_model
 from django.test import TestCase, override_settings
 from django.urls import reverse
-from django.contrib.auth import get_user_model
 
 from core.models import Assembly, Conference, Room, WorkadventureSession
 from core.models.conference import ConferenceMember
 
 
 class MetricsTest(TestCase):
-
     def test_MetricsView_allow_ip_addresses(self):
         with self.settings(METRICS_SERVER_IPS=['1.2.3.4']):
             resp = self.client.get(reverse('metrics:index'))
@@ -26,7 +25,7 @@ class MetricsTest(TestCase):
             # Our address is not 127.0.0.1 so we should get a `HTTP 200`.
             self.assertEqual(200, resp.status_code)
 
-    @override_settings(METRICS_SERVER_IPS="*")
+    @override_settings(METRICS_SERVER_IPS='*')
     def test_MetricsView(self):
         conf = Conference(slug='conf', name='TestConf', is_public=True)
         conf.save()
@@ -38,24 +37,27 @@ class MetricsTest(TestCase):
         )
         assembly.save()
         room = Room(
-            conference=conf, assembly=assembly, name='Room 1',
-            room_type=Room.RoomType.BIGBLUEBUTTON, backend_status=Room.BackendStatus.ACTIVE,
-            backend_link=str(uuid4()), backend_data={'close_secret': 'asdf'},
+            conference=conf,
+            assembly=assembly,
+            name='Room 1',
+            room_type=Room.RoomType.BIGBLUEBUTTON,
+            backend_status=Room.BackendStatus.ACTIVE,
+            backend_link=str(uuid4()),
+            backend_data={'close_secret': 'asdf'},
         )
         room.save()
-        user = get_user_model()(
-            username="testuser"
-        )
+        user = get_user_model()(username='testuser')
         user.save()
 
-        member = ConferenceMember(
-            conference=conf, active_angel=False, user=user
-        )
+        member = ConferenceMember(conference=conf, active_angel=False, user=user)
         member.save()
 
         wa_room = Room(
-            conference=conf, assembly=assembly, name='Room 2',
-            room_type=Room.RoomType.WORKADVENTURE, backend_status=Room.BackendStatus.ACTIVE,
+            conference=conf,
+            assembly=assembly,
+            name='Room 2',
+            room_type=Room.RoomType.WORKADVENTURE,
+            backend_status=Room.BackendStatus.ACTIVE,
         )
         wa_room.save()
         wa_sess1 = WorkadventureSession.create_for_conference_user(conf, user, wa_room)
diff --git a/src/api/tests/permissions.py b/src/api/tests/permissions.py
index 021da5eee95e13f429014e65bce81c2ecf503fc7..92fef7d754435b8bacbd57c7be073c0c93eb7e5b 100644
--- a/src/api/tests/permissions.py
+++ b/src/api/tests/permissions.py
@@ -1,8 +1,11 @@
 from unittest.mock import Mock
 
+from rest_framework.permissions import BasePermission
+
 from django.contrib.auth.models import AnonymousUser
 from django.test import RequestFactory, TestCase, override_settings
-from rest_framework.permissions import BasePermission
+
+from core.models import Assembly, AssemblyMember, Badge, Conference, ConferenceMember, PlatformUser
 
 from api.permissions import (
     AssemblyPermission,
@@ -16,7 +19,6 @@ from api.permissions import (
     IsReadOnly,
     IsSuperUser,
 )
-from core.models import Assembly, AssemblyMember, Badge, Conference, ConferenceMember, PlatformUser
 
 
 class PermissionTestCase(TestCase):
diff --git a/src/api/tests/schedule.py b/src/api/tests/schedule.py
index 16ce0b4a75061cb6954930d196de4af43546db21..24d394e40092eac450cddb581ba0cb9ea9240206 100644
--- a/src/api/tests/schedule.py
+++ b/src/api/tests/schedule.py
@@ -1,21 +1,24 @@
-from datetime import datetime, timedelta
 import json
 import xml.etree.ElementTree as ET
+from datetime import datetime, timedelta
+
+from rest_framework.authtoken.models import Token
 from zoneinfo import ZoneInfo
 
 from django.test import TestCase
 from django.urls import reverse
 from django.utils.timezone import now
-from rest_framework.authtoken.models import Token
 
-from core.models import Assembly, ConferenceExportCache, Conference, Event, PlatformUser, Room
+from core.models import Assembly, Conference, ConferenceExportCache, Event, PlatformUser, Room
 
 
 class ScheduleTest(TestCase):
     def setUp(self):
         tz = ZoneInfo('CET')
         self.conf = Conference(
-            slug='conf', name='TestConf', is_public=True,
+            slug='conf',
+            name='TestConf',
+            is_public=True,
             start=datetime(2020, 12, 27, 1, 23, 45, tzinfo=tz),
             end=datetime(2020, 12, 30, 23, 45, 00, tzinfo=tz),
         )
@@ -110,27 +113,27 @@ class ScheduleTest(TestCase):
         event.save()
 
         update = {
-            "url": "https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html",
-            "id": 11583,
-            "guid": str(event.id),
-            "logo": None,
-            "date": "2020-12-27T12:20:00+01:00",
-            "start": "12:20",
-            "duration": "00:30",
-            "room": "foo room",
-            "slug": "rc3-11583-rc3_eroffnung",
-            "title": "#rC3 Er\u00f6ffnung",
-            "subtitle": "",
-            "track": "Community",
-            "type": "Talk",
-            "language": "de",
-            "abstract": "Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!",
-            "description": "",
-            "recording_license": "",
-            "do_not_record": False,
-            "persons": [{"id": 14151, "public_name": "blubbel"}],
-            "links": [],
-            "attachments": []
+            'url': 'https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html',
+            'id': 11583,
+            'guid': str(event.id),
+            'logo': None,
+            'date': '2020-12-27T12:20:00+01:00',
+            'start': '12:20',
+            'duration': '00:30',
+            'room': 'foo room',
+            'slug': 'rc3-11583-rc3_eroffnung',
+            'title': '#rC3 Er\u00f6ffnung',
+            'subtitle': '',
+            'track': 'Community',
+            'type': 'Talk',
+            'language': 'de',
+            'abstract': 'Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!',
+            'description': '',
+            'recording_license': '',
+            'do_not_record': False,
+            'persons': [{'id': 14151, 'public_name': 'blubbel'}],
+            'links': [],
+            'attachments': [],
         }
 
         url = reverse('api:event-schedule', kwargs={'conference': self.conf.slug, 'pk': event.pk})
@@ -146,9 +149,7 @@ class ScheduleTest(TestCase):
         self.assertEqual(timedelta(minutes=30), event.schedule_duration)
         self.assertIsNotNone(event.schedule_end)
 
-        update = {
-            "public": True
-        }
+        update = {'public': True}
 
         with self.modify_settings(API_USERS={'append': self.user.username}):
             resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
@@ -166,35 +167,35 @@ class ScheduleTest(TestCase):
         another_room.save()
 
         update = {
-            "url": "https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html",
-            "id": 11583,
-            "guid": "d9334deb-f183-4aec-9c6c-137741f6ff73",
-            "logo": None,
-            "date": "2020-12-27T12:20:00+01:00",
-            "start": "12:20",
-            "duration": "01:30",
-            "room": "foo room",
-            "room_id": str(another_room.pk),
-            "slug": "rc3-11583-rc3_eroffnung",
-            "title": "#rC3 Er\u00f6ffnung",
-            "subtitle": "",
-            "type": "Talk",
-            "language": "de",
-            "abstract": "Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!",
-            "description": "",
-            "recording_license": "",
-            "do_not_record": False,
-            "persons": [{"id": 14151, "public_name": "blubbel"}],
-            "links": [],
-            "attachments": [],
-            "public": False
+            'url': 'https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html',
+            'id': 11583,
+            'guid': 'd9334deb-f183-4aec-9c6c-137741f6ff73',
+            'logo': None,
+            'date': '2020-12-27T12:20:00+01:00',
+            'start': '12:20',
+            'duration': '01:30',
+            'room': 'foo room',
+            'room_id': str(another_room.pk),
+            'slug': 'rc3-11583-rc3_eroffnung',
+            'title': '#rC3 Er\u00f6ffnung',
+            'subtitle': '',
+            'type': 'Talk',
+            'language': 'de',
+            'abstract': 'Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!',
+            'description': '',
+            'recording_license': '',
+            'do_not_record': False,
+            'persons': [{'id': 14151, 'public_name': 'blubbel'}],
+            'links': [],
+            'attachments': [],
+            'public': False,
         }
 
         self.assertFalse(Event.objects.filter(pk=update['guid']).exists())
 
         url = reverse('api:event-schedule', kwargs={'conference': self.conf.slug, 'pk': update['guid']})
 
-        with self.modify_settings(API_USERS={'append': self.user.username}), self.assertLogs("api.views.schedule", "WARNING"):
+        with self.modify_settings(API_USERS={'append': self.user.username}), self.assertLogs('api.views.schedule', 'WARNING'):
             resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
 
         self.assertEqual(201, resp.status_code, f'Unexpected result from POST: {resp.content}')
@@ -209,10 +210,7 @@ class ScheduleTest(TestCase):
 
         self.assertEqual(another_room.pk, event.room_id, 'Expected import to prefer "room_id" over "room".')
 
-        update = {
-            "public": False,
-            "track": "Security"
-        }
+        update = {'public': False, 'track': 'Security'}
 
         with self.modify_settings(API_USERS={'append': self.user.username}):
             resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
@@ -228,35 +226,35 @@ class ScheduleTest(TestCase):
 
     def testPushInvalidTrack(self):
         update = {
-            "url": "https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html",
-            "id": 11583,
-            "guid": "d9334deb-f183-4aec-9c6c-137741f6ff73",
-            "logo": None,
-            "date": "2020-12-27T12:20:00+01:00",
-            "start": "12:20",
-            "duration": "01:30",
-            "room": "foo room",
-            "slug": "rc3-11583-rc3_eroffnung",
-            "title": "#rC3 Er\u00f6ffnung",
-            "subtitle": "",
-            "type": "Talk",
-            "language": "de",
-            "abstract": "Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!",
-            "description": "",
-            "recording_license": "",
-            "do_not_record": False,
-            "persons": [{"id": 14151, "public_name": "blubbel"}],
-            "links": [],
-            "attachments": [],
-            "public": False,
-            "track": "Fnord"
+            'url': 'https://fahrplan.events.ccc.de/rc3/2020/Fahrplan/events/11583.html',
+            'id': 11583,
+            'guid': 'd9334deb-f183-4aec-9c6c-137741f6ff73',
+            'logo': None,
+            'date': '2020-12-27T12:20:00+01:00',
+            'start': '12:20',
+            'duration': '01:30',
+            'room': 'foo room',
+            'slug': 'rc3-11583-rc3_eroffnung',
+            'title': '#rC3 Er\u00f6ffnung',
+            'subtitle': '',
+            'type': 'Talk',
+            'language': 'de',
+            'abstract': 'Willkommen zur ersten und hoffentlich einzigen Remote Chaos Experience!',
+            'description': '',
+            'recording_license': '',
+            'do_not_record': False,
+            'persons': [{'id': 14151, 'public_name': 'blubbel'}],
+            'links': [],
+            'attachments': [],
+            'public': False,
+            'track': 'Fnord',
         }
 
         self.assertFalse(Event.objects.filter(pk=update['guid']).exists())
 
         url = reverse('api:event-schedule', kwargs={'conference': self.conf.slug, 'pk': update['guid']})
 
-        with self.modify_settings(API_USERS={'append': self.user.username}), self.assertLogs("api.views.schedule", "WARNING"):
+        with self.modify_settings(API_USERS={'append': self.user.username}), self.assertLogs('api.views.schedule', 'WARNING'):
             resp = self.client.post(url, json.dumps(update), content_type='application/json', HTTP_AUTHORIZATION=f'Token {self.token.key}')
 
         self.assertEqual(400, resp.status_code, f'Unexpected success result from POST: {resp.content}')
@@ -282,8 +280,8 @@ class ScheduleTest(TestCase):
         for remaining_day in remaining_days:
             self.assertEqual(len(remaining_day), 0)
 
-        room, = day1
-        event1, = room
+        (room,) = day1
+        (event1,) = room
         self.assertEqual(event1.findtext('title'), ev1.name)
         self.assertEqual(event1.findtext('date'), '2020-12-27T01:23:45+01:00')
         self.assertEqual(event1.findtext('start'), '01:23')
diff --git a/src/api/tests/workadventure.py b/src/api/tests/workadventure.py
index 87d0498fb04b15eee719123d57d036cd347f32fb..c329b9b1e637a3b49a679e3c9e0677eed370672b 100644
--- a/src/api/tests/workadventure.py
+++ b/src/api/tests/workadventure.py
@@ -1,9 +1,10 @@
 import json
 from pathlib import Path
 
+from rest_framework.authtoken.models import Token
+
 from django.conf import settings
 from django.test import Client, TestCase, override_settings
-from rest_framework.authtoken.models import Token
 
 from core.models import Assembly, Conference, ConferenceMember, PlatformUser, Room
 from core.sso import SSO
@@ -103,7 +104,7 @@ class WorkAdventureMapServiceTestCase(_WorkAdventureTestCase):
             self.assertTrue(all(k in x for x in endpoint_data), f'The field "{k}" is missing on at least one of returned items.')
         self.assertTrue(
             all(x['assembly_url'].startswith('http://test.localhost/') for x in endpoint_data),
-            'The assembly_url field is empty on at least one of returned items.'
+            'The assembly_url field is empty on at least one of returned items.',
         )
 
     def testPush(self):
@@ -139,7 +140,7 @@ class WorkAdventureMapServiceTestCase(_WorkAdventureTestCase):
         room5a.save()
 
         # load import data and fill in assembly and room ids
-        with Path(__file__).with_name("wa_mapservice_import.json").open() as f:
+        with Path(__file__).with_name('wa_mapservice_import.json').open() as f:
             import_json = f.read()
         import_replacements = {
             'ASSEMBLY_1_ID': self.assembly1a.id,
@@ -182,7 +183,7 @@ class WorkAdventureMapServiceTestCase(_WorkAdventureTestCase):
         room2a.refresh_from_db()
         self.assertEqual(Room.BackendStatus.ERROR, room2a.backend_status, 'the previously active room should have error state now')
         room4a.refresh_from_db()
-        self.assertEqual(Room.BackendStatus.NEW, room4a.backend_status, 'the fresh room shouldn\'t have changed status')
+        self.assertEqual(Room.BackendStatus.NEW, room4a.backend_status, "the fresh room shouldn't have changed status")
         self.assertEqual('error', room4a.director_data.get('violation', {}).get('severity', '(not set)').lower())
 
         # fetch complete list again and check what's there
@@ -191,7 +192,7 @@ class WorkAdventureMapServiceTestCase(_WorkAdventureTestCase):
         self.assertEqual('active', x['backend_status'])
 
 
-class WorkAdventureEndpointTestCase(object):  # TODO: enable again -- _WorkAdventureTestCase):
+class WorkAdventureEndpointTestCase:  # TODO: enable again -- _WorkAdventureTestCase):
     def setUp(self):
         super().setUp()
 
@@ -265,8 +266,8 @@ class WorkAdventureEndpointTestCase(object):  # TODO: enable again -- _WorkAdven
 
         self.assertEqual(
             self.room1c.reserve_capacity,
-            data["reserve_capacity"],
-            f'WA endpoint returned room_capacity {data["reserve_capacity"]} when room capacity was {self.room1c.reserve_capacity}'
+            data['reserve_capacity'],
+            f'WA endpoint returned room_capacity {data["reserve_capacity"]} when room capacity was {self.room1c.reserve_capacity}',
         )
         for k in ['room_id', 'assembly_id', 'blocked']:
             self.assertTrue(k in data, f'The field "{k}" is missing on at least one of returned items.')
@@ -300,7 +301,7 @@ class WorkAdventureEndpointTestCase(object):  # TODO: enable again -- _WorkAdven
         self.assertEqual(42, data.get('director_data', {}).get('fnord'))
 
 
-class WorkAdventureBackendTestCase(object):  # TODO: enable again -- _WorkAdventureTestCase):
+class WorkAdventureBackendTestCase:  # TODO: enable again -- _WorkAdventureTestCase):
     def setUp(self):
         super().setUp()
 
@@ -312,9 +313,11 @@ class WorkAdventureBackendTestCase(object):  # TODO: enable again -- _WorkAdvent
         self.service_token = Token(user=self.service_user)
         self.service_token.save()
         self.user_token = SSO.generate_token(self.human_user)
-        self.client.defaults.update({
-            f'HTTP_{settings.SSO_HEADER}': str(self.user_token),
-        })
+        self.client.defaults.update(
+            {
+                f'HTTP_{settings.SSO_HEADER}': str(self.user_token),
+            }
+        )
 
     def testFetch(self):
         resp = self.client.get(
diff --git a/src/api/urls.py b/src/api/urls.py
index 86996c8c6317a1b7ca354ba1d7a7b1569464c9df..a0e94b2e655525270e2aad3572d2b3695d2a0535 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -1,15 +1,13 @@
-from django.urls import path
-from rest_framework.urlpatterns import format_suffix_patterns
 from rest_framework.authtoken import views as authtoken_views
+from rest_framework.urlpatterns import format_suffix_patterns
 
-from .views import api_root
-from .views import assemblies, badges, bbb, conferencemember, conferences, events, maps, metanav, rooms, schedule, users, workadventure
+from django.urls import path
 
+from .views import api_root, assemblies, badges, bbb, conferencemember, conferences, events, maps, metanav, rooms, schedule, users, workadventure
 
 app_name = 'api'
 urlpatterns = [
     path('', api_root, name='index'),
-
     # user data/stuff
     path('me', users.profile, name='profile'),
     path('badges.zip', users.BadgeExportView.as_view(), name='badge-export'),
@@ -17,7 +15,6 @@ urlpatterns = [
     path('friends', users.friends, name='friends'),
     path('conferences', conferences.ConferenceList.as_view(), name='conference-list'),
     path('timeline', users.UserTimelineList.as_view(), name='timeline-list'),
-
     # conference-specific views
     path('c/<slug:conference>/', conferences.ConferenceDetail.as_view(), name='conference-detail'),
     path('c/<slug:conference>/metanav', metanav.MetaNavView.as_view(), name='conference-metanav'),
@@ -30,8 +27,11 @@ urlpatterns = [
     path('c/<slug:conference>/assembly/<slug:assembly>/rooms', assemblies.ConferenceAssemblyRoomList.as_view(), name='assembly-rooms'),
     path('c/<slug:conference>/assembly/<slug:assembly>/schedule', schedule.AssemblySchedule.as_view(), name='assembly-schedule'),
     path('c/<slug:conference>/assembly/<slug:assembly>/badges', badges.BadgeListCreate.as_view(), name='assembly-badges-list-create'),
-    path('c/<slug:conference>/assembly/<slug:assembly>/issue_redeem_token/<str:issuing_token>',
-         badges.BadgeTokenListCreate.as_view(), name='badge-token-create-with-token'),
+    path(
+        'c/<slug:conference>/assembly/<slug:assembly>/issue_redeem_token/<str:issuing_token>',
+        badges.BadgeTokenListCreate.as_view(),
+        name='badge-token-create-with-token',
+    ),
     path('c/<slug:conference>/assembly/<slug:assembly>/badges/<uuid:pk>', badges.BadgeTokenListCreate.as_view(), name='badge-token-list-create'),
     path('c/<slug:conference>/map/poi.json', maps.PoiExportView.as_view(), name='map-poi'),
     path('c/<slug:conference>/map/assemblies/poi.json', maps.AssembliesPoiExportView.as_view(), name='map-assemblies-poi'),
@@ -44,26 +44,20 @@ urlpatterns = [
     path('c/<slug:conference>/events', events.EventList.as_view(), name='event-list'),
     path('c/<slug:conference>/event/<uuid:pk>/', events.EventDetail.as_view(), name='event-detail'),
     path('c/<slug:conference>/event/<uuid:pk>/schedule', schedule.EventSchedule.as_view(), name='event-schedule'),
-
     # WorkAdventure integration (mapservice)
     path('c/<slug:conference>/workadventure/maps', workadventure.MapServiceView.as_view()),
     path('c/<slug:conference>/workadventure/maps/raw', workadventure.MapServiceView.as_view(), name='wa-mapservice'),
     path('c/<slug:conference>/workadventure/map/<slug:assembly>/<slug:world>', workadventure.MapDetailView.as_view(), name='wa-map-detail'),
     path('c/<slug:conference>/workadventure/map/<slug:assembly>/<slug:world>/<slug:room>', workadventure.MapDetailView.as_view(), name='wa-map-detail'),
-
     # WorkAdventure integration (exneuland)
     path('c/<slug:conference>/workadventure/maps/list', workadventure.MapBackendListView.as_view(), name='wa-backend-maps'),
     path('c/<slug:conference>/workadventure/login/<str:token>', workadventure.RegisterView.as_view(), name='wa-register'),
     path('c/<slug:conference>/workadventure/checkuser/<uuid:uid>', workadventure.UserInfoView.as_view(), name='wa-userinfo'),
-
     path('c/<slug:conference>/workadventure/redeem_badge/<str:token>', workadventure.MapBackendRedeemBadgeTokenView.as_view(), name='wa-badgeredeem'),
-
     path('c/<slug:conference>/workadventure/report/user', workadventure.MapBackendReportUserView.as_view(), name='wa-report-user'),
     path('c/<slug:conference>/workadventure/report/map', workadventure.MapBackendReportUserView.as_view(), name='wa-report-user'),
-
     # integration with other components
     path('c/<slug:conference>/is_angel/<str:username>', conferencemember.AngelView.as_view(), name='user-angel'),
-
     # BBB meeting ended callback
     path('bbb_meeting_end', bbb.MeetingEnded.as_view(), name='bbb_meeting_end'),
 ]
diff --git a/src/api/urls_sso.py b/src/api/urls_sso.py
index 294ec61ecaae8e9e052bf47d1dd6db19e5bbdb9a..d0b90462b61bc9e963c2251a9ef07f6785131de5 100644
--- a/src/api/urls_sso.py
+++ b/src/api/urls_sso.py
@@ -1,21 +1,18 @@
-from django.urls import re_path
 from oauth2_provider import views
 
-from .views import sso as sso_views
+from django.urls import re_path
 
+from .views import sso as sso_views
 
-app_name = "oauth2_provider"
+app_name = 'oauth2_provider'
 urlpatterns = [
     # General Authorization Endpoint
-    re_path(r"^authorize/$", views.AuthorizationView.as_view(), name="authorize"),
-
+    re_path(r'^authorize/$', views.AuthorizationView.as_view(), name='authorize'),
     # provide (or revoke/renew) access tokens
-    re_path(r"^token/$", views.TokenView.as_view(), name="token"),
-    re_path(r"^revoke_token/$", views.RevokeTokenView.as_view(), name="revoke-token"),
-
+    re_path(r'^token/$', views.TokenView.as_view(), name='token'),
+    re_path(r'^revoke_token/$', views.RevokeTokenView.as_view(), name='revoke-token'),
     # check an existing token, needs 'introspection' scope
-    re_path(r"^introspect/$", sso_views.IntrospectTokenView.as_view(), name="introspect"),
-
+    re_path(r'^introspect/$', sso_views.IntrospectTokenView.as_view(), name='introspect'),
     # static page for OOB token transmission
-    re_path(r"^out-of-band-display-token/$", sso_views.OutOfBandDisplayTokenView.as_view(), name="out-of-band-display-token"),
+    re_path(r'^out-of-band-display-token/$', sso_views.OutOfBandDisplayTokenView.as_view(), name='out-of-band-display-token'),
 ]
diff --git a/src/api/views/__init__.py b/src/api/views/__init__.py
index d498b70e1fd7ded5b2a86b119707b768a6f3adfa..cf012e6c0504e09c25d1b8c09fb942f41b1e892c 100644
--- a/src/api/views/__init__.py
+++ b/src/api/views/__init__.py
@@ -4,7 +4,6 @@ from rest_framework.reverse import reverse
 
 from core.models.conference import Conference
 
-
 __all__ = [
     'api_root',
 ]
diff --git a/src/api/views/assemblies.py b/src/api/views/assemblies.py
index a975e597084700164a74015305f3d25323e77f2a..98c5ec518cc2b33574ec45cdc7cc8b15bf999c61 100644
--- a/src/api/views/assemblies.py
+++ b/src/api/views/assemblies.py
@@ -1,11 +1,11 @@
 from rest_framework import generics
 
+from core.models.assemblies import Assembly
 from core.models.events import Event
 from core.models.rooms import Room
-from core.models.assemblies import Assembly
 
-from ..serializers import AssemblySerializer, RoomSerializer, EventSerializer
-from .mixins import ConferenceSlugMixin, ConferenceSlugAssemblyMixin
+from ..serializers import AssemblySerializer, EventSerializer, RoomSerializer
+from .mixins import ConferenceSlugAssemblyMixin, ConferenceSlugMixin
 
 
 class ConferenceAssemblyList(ConferenceSlugMixin, generics.ListAPIView):
diff --git a/src/api/views/badges.py b/src/api/views/badges.py
index 2e7260b154cb0b18296171dd815b7e03b72050a2..63ad4f1b5fb958d05dc9f882c90023ab0456a981 100644
--- a/src/api/views/badges.py
+++ b/src/api/views/badges.py
@@ -1,8 +1,5 @@
 import logging
 
-from django.conf import settings
-from django.http import HttpResponse
-from django.shortcuts import get_object_or_404
 from rest_framework import authentication
 from rest_framework.decorators import api_view
 from rest_framework.exceptions import NotFound
@@ -10,14 +7,19 @@ from rest_framework.generics import ListCreateAPIView
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
-from api.permissions import IsAssemblyManager, IsConferenceService, IsSuperUser, HasIssuingToken
-from api.serializers import BadgeSerializer, BadgeTokenSerializer
-from api.views.mixins import ConferenceSlugAssemblyMixin, ConferenceSlugMixin
+from django.conf import settings
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
+
 from core.models.badges import Badge, BadgeToken
 from core.models.conference import Conference
 from core.models.users import PlatformUser
 from core.sso import SSO
 
+from api.permissions import HasIssuingToken, IsAssemblyManager, IsConferenceService, IsSuperUser
+from api.serializers import BadgeSerializer, BadgeTokenSerializer
+from api.views.mixins import ConferenceSlugAssemblyMixin, ConferenceSlugMixin
+
 logger = logging.getLogger(__name__)
 
 
@@ -32,10 +34,7 @@ class BadgeListCreate(ListCreateAPIView):
 
     def get_serializer_context(self):
         context = super().get_serializer_context()
-        context.update({
-            'assembly': self.kwargs.get('assembly'),
-            'conference': self.kwargs.get('conference')
-        })
+        context.update({'assembly': self.kwargs.get('assembly'), 'conference': self.kwargs.get('conference')})
         return context
 
 
@@ -55,23 +54,21 @@ class BadgeTokenListCreate(ConferenceSlugAssemblyMixin, ListCreateAPIView):
             badge = self.kwargs.get('pk')
         else:
             try:
-                badge = Badge.objects.get(issuing_token=self.kwargs.get("issuing_token"), conference__slug=self.kwargs.get("conference")).pk
+                badge = Badge.objects.get(issuing_token=self.kwargs.get('issuing_token'), conference__slug=self.kwargs.get('conference')).pk
             except Badge.DoesNotExist:
-                raise NotFound("Badge matching issuing_token not found")
+                raise NotFound('Badge matching issuing_token not found')
         context = super().get_serializer_context()
-        context.update({
-            'badge': badge
-        })
+        context.update({'badge': badge})
         return context
 
 
 @api_view(['POST'])
 def redeem_badge_token(request, conference, **kwargs):
-    redeem_token = request.POST.get('token', request.data.get("token", None))
+    redeem_token = request.POST.get('token', request.data.get('token', None))
     if redeem_token is None:
         return HttpResponse(status=400)
 
-    username = request.POST.get('username', request.data.get("username", None))
+    username = request.POST.get('username', request.data.get('username', None))
     user = get_object_or_404(PlatformUser, username=username, is_active=True)
 
     # TODO: Proper Access Checking
@@ -107,8 +104,8 @@ class RedeemBadgeMapTokenView(ConferenceSlugMixin, APIView):
         if self.target_user is None or not self.target_user.is_in_conference(self.conference):
             return HttpResponse(status=404)
 
-        redeem_token = request.POST.get('id', request.data.get("id", None))
-        assembly = request.POST.get('assembly', request.data.get("assembly", None))
+        redeem_token = request.POST.get('id', request.data.get('id', None))
+        assembly = request.POST.get('assembly', request.data.get('assembly', None))
         if assembly is None or redeem_token is None:
             return HttpResponse(status=400)
 
@@ -119,10 +116,14 @@ class RedeemBadgeMapTokenView(ConferenceSlugMixin, APIView):
         if badge_token.map_token:
             # Redeem only MAP Tokens
             created = badge_token.redeem(self.target_user, False)
-            return Response({'badge_name': f'{badge_token.badge}',
-                             'badge_image': f'{badge_token.badge.image.url}',
-                             'issuing_assembly': f'{badge_token.badge.issuing_assembly}',
-                             'user': f'{self.target_user.username}',
-                             'created': created})
+            return Response(
+                {
+                    'badge_name': f'{badge_token.badge}',
+                    'badge_image': f'{badge_token.badge.image.url}',
+                    'issuing_assembly': f'{badge_token.badge.issuing_assembly}',
+                    'user': f'{self.target_user.username}',
+                    'created': created,
+                }
+            )
         else:
             return HttpResponse(status=415)
diff --git a/src/api/views/bbb.py b/src/api/views/bbb.py
index 79876416a5cd1c1bdb3962c1a6ca5d8ce02ec3d1..08e4f15887ab3f238fc4a5f1951897c731f03d1e 100644
--- a/src/api/views/bbb.py
+++ b/src/api/views/bbb.py
@@ -1,5 +1,5 @@
-from django.shortcuts import get_object_or_404
 from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
 from django.views import View
 
 from core.models import Room
diff --git a/src/api/views/conferencemember.py b/src/api/views/conferencemember.py
index 328a99f1faff63fe87da3bef4b757fdd4b3c2102..22eda299ab1cedea653e08b979c5c8c7d810fd26 100644
--- a/src/api/views/conferencemember.py
+++ b/src/api/views/conferencemember.py
@@ -1,10 +1,11 @@
 import logging
 
-from django.conf import settings
 from rest_framework import authentication, status
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
+from django.conf import settings
+
 from core.models.conference import ConferenceMember
 from core.sso import SSO
 
@@ -64,11 +65,13 @@ class WorkadventureView(ConferenceSlugMixin, APIView):
             return Response({'active': False}, status=status.HTTP_400_BAD_REQUEST)
 
         result = self.target_user.get_avatar_json(self.conference)
-        result.update({
-            'active': self.target_user.is_active,
-            'username': self.target_user.username,
-            'user_id': self.target_user.id,  # TODO: exchange for an ident after rC3
-        })
+        result.update(
+            {
+                'active': self.target_user.is_active,
+                'username': self.target_user.username,
+                'user_id': self.target_user.id,  # TODO: exchange for an ident after rC3
+            }
+        )
         return Response(result)
 
     def post(self, request, *get, format=None, **kwargs):
diff --git a/src/api/views/events.py b/src/api/views/events.py
index 74a2d7f12be390ce593ce533ec2d1232201afc2c..a4232805bae359e77ccc2f6517142de6adfa5701 100644
--- a/src/api/views/events.py
+++ b/src/api/views/events.py
@@ -1,7 +1,9 @@
-from core.models.events import Event
-from django.shortcuts import get_object_or_404
 from rest_framework import generics
 
+from django.shortcuts import get_object_or_404
+
+from core.models.events import Event
+
 from ..serializers import EventSerializer
 from .mixins import ConferenceSlugMixin
 
@@ -10,14 +12,12 @@ class EventList(ConferenceSlugMixin, generics.ListAPIView):
     serializer_class = EventSerializer
 
     def get_queryset(self, **kwargs):
-        return Event.objects.filter(conference=self.conference).order_by("name")
+        return Event.objects.filter(conference=self.conference).order_by('name')
 
 
 class EventDetail(ConferenceSlugMixin, generics.RetrieveAPIView):
     serializer_class = EventSerializer
 
     def get_object(self, **kwargs):
-        event_id = self.request.resolver_match.kwargs["pk"]
-        return get_object_or_404(
-            Event.objects.conference_accessible(conference=self.conference), pk=event_id
-        )
+        event_id = self.request.resolver_match.kwargs['pk']
+        return get_object_or_404(Event.objects.conference_accessible(conference=self.conference), pk=event_id)
diff --git a/src/api/views/maps.py b/src/api/views/maps.py
index ff9324649068afa2e01ee93ed576d7f152abcf75..509deb13888d41ad24139ebf3fd72948a9aa15e8 100644
--- a/src/api/views/maps.py
+++ b/src/api/views/maps.py
@@ -1,14 +1,15 @@
 import abc
 import json
 
-from django.contrib.gis.gdal import SpatialReference, CoordTransform
 from rest_framework.views import APIView
 
+from django.contrib.gis.gdal import CoordTransform, SpatialReference
+
 from core.models.assemblies import Assembly
-from core.models.map import MapPOI
 from core.models.conference import ConferenceExportCache
-from .mixins import ConferenceSlugMixin
+from core.models.map import MapPOI
 
+from .mixins import ConferenceSlugMixin
 
 _cts = {}  # cache of CoordTransforms (if needed)
 
@@ -31,7 +32,6 @@ class PoiExportView(ConferenceSlugMixin, APIView):
         features = []
         result = {
             'type': 'FeatureCollection',
-            # 'crs': {'type': 'name', 'properties': {'name': f'EPSG:{srid}'}},  # deprecated, not in RFC7946
             'features': features,
         }
 
@@ -80,7 +80,6 @@ class AssembliesExportView(ConferenceSlugMixin, APIView, metaclass=abc.ABCMeta):
         features = []
         result = {
             'type': 'FeatureCollection',
-            # 'crs': {'type': 'name', 'properties': {'name': f'EPSG:{srid}'}},  # deprecated, not in RFC7946
             'features': features,
         }
 
diff --git a/src/api/views/metanav.py b/src/api/views/metanav.py
index 84168b88d4c1bb3ffa973db3617eb2b03c58e0b7..491a16fb130268ad07245da052609ce287d35380 100644
--- a/src/api/views/metanav.py
+++ b/src/api/views/metanav.py
@@ -2,10 +2,10 @@ import logging
 
 from rest_framework.generics import ListAPIView
 
-from api.serializers import MetaNavItemSerializer
-from api.views.mixins import ConferenceSlugMixin
 from core.models.metanavi import MetaNavItem
 
+from api.serializers import MetaNavItemSerializer
+from api.views.mixins import ConferenceSlugMixin
 
 logger = logging.getLogger(__name__)
 
diff --git a/src/api/views/metrics.py b/src/api/views/metrics.py
index 3f3d06280d9f0b213bed4fa6f592e56bd1361952..4a8722c79610208e124645ee6d594606c5badd1c 100644
--- a/src/api/views/metrics.py
+++ b/src/api/views/metrics.py
@@ -1,7 +1,8 @@
 from django.conf import settings
 from django.http.response import HttpResponseForbidden
 from django.views.generic.base import TemplateView
-from core.models import Room, Conference, ConferenceMember, PlatformUser, Badge, UserBadge
+
+from core.models import Badge, Conference, ConferenceMember, PlatformUser, Room, UserBadge
 from core.models.assemblies import Assembly
 from core.models.events import Event
 from core.models.ticket import ConferenceMemberTicket
@@ -16,7 +17,7 @@ class MetricsView(TemplateView):
         """
         Only allow IP addresses that are listed in settings.METRICS_SERVER_IPS.
         """
-        remote_addr = request.META.get("REMOTE_ADDR")
+        remote_addr = request.META.get('REMOTE_ADDR')
         if remote_addr in settings.METRICS_SERVER_IPS or '*' in settings.METRICS_SERVER_IPS:
             return super().dispatch(request, *args, **kwargs)
         else:
@@ -39,73 +40,21 @@ class MetricsView(TemplateView):
                     '{user_type="service"}': PlatformUser.objects.filter(user_type=PlatformUser.Type.SERVICE).count(),
                     '{user_type="bot"}': PlatformUser.objects.filter(user_type=PlatformUser.Type.BOT).count(),
                     '{user_type="assembly"}': PlatformUser.objects.filter(user_type=PlatformUser.Type.ASSEMBLY).count(),
-                }
-            },
-            'hub_conference_members': {
-                'help': 'members in the conference',
-                'type': 'counter',
-                'values': {}
-            },
-            'hub_conference_members_staff': {
-                'help': 'staff count',
-                'type': 'counter',
-                'values': {}
-            },
-            'hub_conference_members_themes': {
-                'help': 'used themes by members in the conference',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_tickets': {
-                'help': 'registered tickets',
-                'type': 'counter',
-                'values': {}
-            },
-            'hub_conference_assemblies': {
-                'help': 'conference\'s assemblies',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_channels': {
-                'help': 'conference\'s channels',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_badges_public': {
-                'help': 'conference\'s badges (public)',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_badges_hidden': {
-                'help': 'conference\'s badges (non-public)',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_badges_accepted': {
-                'help': 'conference\'s badges (accepted/assigned)',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_badges_redeemed': {
-                'help': 'conference\'s badges (redeemed, but not accepted yet)',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_events': {
-                'help': 'conference\'s events',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_rooms': {
-                'help': 'conference\'s rooms',
-                'type': 'gauge',
-                'values': {}
-            },
-            'hub_conference_workadventure_sessions': {
-                'help': 'conference\'s workadventure session',
-                'type': 'gauge',
-                'values': {}
+                },
             },
+            'hub_conference_members': {'help': 'members in the conference', 'type': 'counter', 'values': {}},
+            'hub_conference_members_staff': {'help': 'staff count', 'type': 'counter', 'values': {}},
+            'hub_conference_members_themes': {'help': 'used themes by members in the conference', 'type': 'gauge', 'values': {}},
+            'hub_conference_tickets': {'help': 'registered tickets', 'type': 'counter', 'values': {}},
+            'hub_conference_assemblies': {'help': "conference's assemblies", 'type': 'gauge', 'values': {}},
+            'hub_conference_channels': {'help': "conference's channels", 'type': 'gauge', 'values': {}},
+            'hub_conference_badges_public': {'help': "conference's badges (public)", 'type': 'gauge', 'values': {}},
+            'hub_conference_badges_hidden': {'help': "conference's badges (non-public)", 'type': 'gauge', 'values': {}},
+            'hub_conference_badges_accepted': {'help': "conference's badges (accepted/assigned)", 'type': 'gauge', 'values': {}},
+            'hub_conference_badges_redeemed': {'help': "conference's badges (redeemed, but not accepted yet)", 'type': 'gauge', 'values': {}},
+            'hub_conference_events': {'help': "conference's events", 'type': 'gauge', 'values': {}},
+            'hub_conference_rooms': {'help': "conference's rooms", 'type': 'gauge', 'values': {}},
+            'hub_conference_workadventure_sessions': {'help': "conference's workadventure session", 'type': 'gauge', 'values': {}},
         }
 
         for conference in Conference.objects.filter(is_public=True):
@@ -129,56 +78,59 @@ class MetricsView(TemplateView):
 
             # hub_conference_rooms
             for room_type in Room.RoomType.values:
-                room_count = Room.objects.filter(
-                    conference=conference,
-                    room_type=Room.RoomType(room_type)
-                ).count()
+                room_count = Room.objects.filter(conference=conference, room_type=Room.RoomType(room_type)).count()
                 metrics['hub_conference_rooms']['values'][f'{{conference="{slug}",room_type="{room_type}"}}'] = room_count
 
             # hub_conference_events
             for kind in Event.Kind.values:
-                event_count = Event.objects.filter(
-                    conference=conference,
-                    kind=Event.Kind(kind)
-                ).count()
+                event_count = Event.objects.filter(conference=conference, kind=Event.Kind(kind)).count()
                 metrics['hub_conference_events']['values'][f'{{conference="{slug}",kind="{kind}"}}'] = event_count
 
             # hub_conference_badges_{public,private}
-            for visibility in {Badge.State.HIDDEN, Badge.State.PUBLIC}:
-                metrics['hub_conference_badges_' + visibility]['values'][f'{{conference="{slug}",badge_type="achievement"}}'] = \
-                    Badge.objects.filter(conference=conference, state=visibility).count()
-                metrics['hub_conference_badges_' + visibility]['values'][f'{{conference="{slug}",badge_type="sticker"}}'] = \
-                    Badge.objects.filter(conference=conference, state=visibility).count()
+            for visibility in (Badge.State.HIDDEN, Badge.State.PUBLIC):
+                metrics['hub_conference_badges_' + visibility]['values'][f'{{conference="{slug}",badge_type="achievement"}}'] = Badge.objects.filter(
+                    conference=conference, state=visibility
+                ).count()
+                metrics['hub_conference_badges_' + visibility]['values'][f'{{conference="{slug}",badge_type="sticker"}}'] = Badge.objects.filter(
+                    conference=conference, state=visibility
+                ).count()
 
             # hub_conference_badges_{accepted,redeemed}
             for userbadge_visibility in UserBadge.Visibility.values:
-                metrics['hub_conference_badges_accepted']['values'][f'{{conference="{slug}",visibility="{userbadge_visibility}"}}'] = \
-                    UserBadge.objects.filter(badge__conference=conference, visibility=userbadge_visibility, accepted_by_user=True).count()
-                metrics['hub_conference_badges_redeemed']['values'][f'{{conference="{slug}",visibility="{userbadge_visibility}"}}'] = \
-                    UserBadge.objects.filter(badge__conference=conference, visibility=userbadge_visibility, accepted_by_user=False).count()
+                metrics['hub_conference_badges_accepted']['values'][f'{{conference="{slug}",visibility="{userbadge_visibility}"}}'] = UserBadge.objects.filter(
+                    badge__conference=conference, visibility=userbadge_visibility, accepted_by_user=True
+                ).count()
+                metrics['hub_conference_badges_redeemed']['values'][f'{{conference="{slug}",visibility="{userbadge_visibility}"}}'] = UserBadge.objects.filter(
+                    badge__conference=conference, visibility=userbadge_visibility, accepted_by_user=False
+                ).count()
 
             # hub_conference_members{,_staff}
-            metrics['hub_conference_members']['values'][f'{{conference="{slug}",active_angel="true"}}'] = \
-                ConferenceMember.objects.filter(conference=conference, active_angel=True).count()
-            metrics['hub_conference_members']['values'][f'{{conference="{slug}",active_angel="false"}}'] = \
-                ConferenceMember.objects.filter(conference=conference, active_angel=False).count()
-            metrics['hub_conference_members_staff']['values'][f'{{conference="{slug}"}}'] = \
-                ConferenceMember.objects.filter(conference=conference, is_staff=True).count()
+            metrics['hub_conference_members']['values'][f'{{conference="{slug}",active_angel="true"}}'] = ConferenceMember.objects.filter(
+                conference=conference, active_angel=True
+            ).count()
+            metrics['hub_conference_members']['values'][f'{{conference="{slug}",active_angel="false"}}'] = ConferenceMember.objects.filter(
+                conference=conference, active_angel=False
+            ).count()
+            metrics['hub_conference_members_staff']['values'][f'{{conference="{slug}"}}'] = ConferenceMember.objects.filter(
+                conference=conference, is_staff=True
+            ).count()
 
             # hub_conference_members_themes
             for theme in PlatformUser.Theme.values:
-                metrics['hub_conference_members_themes']['values'][f'{{conference="{slug}",theme="{theme}"}}'] = \
-                    ConferenceMember.objects.filter(conference=conference, user__theme=theme).count()
+                metrics['hub_conference_members_themes']['values'][f'{{conference="{slug}",theme="{theme}"}}'] = ConferenceMember.objects.filter(
+                    conference=conference, user__theme=theme
+                ).count()
 
             # hub_conference_tickets
-            metrics['hub_conference_tickets']['values'][f'{{conference="{slug}"}}'] = \
-                ConferenceMemberTicket.objects.filter(conference=conference).count()
+            metrics['hub_conference_tickets']['values'][f'{{conference="{slug}"}}'] = ConferenceMemberTicket.objects.filter(conference=conference).count()
 
             # hub_conference_workadventure_sessions
-            metrics['hub_conference_workadventure_sessions']['values'][f'{{conference="{slug}",active="true"}}'] = \
-                WorkadventureSession.objects.filter(conference=conference, token=None).count()
-            metrics['hub_conference_workadventure_sessions']['values'][f'{{conference="{slug}",active="false"}}'] = \
+            metrics['hub_conference_workadventure_sessions']['values'][f'{{conference="{slug}",active="true"}}'] = WorkadventureSession.objects.filter(
+                conference=conference, token=None
+            ).count()
+            metrics['hub_conference_workadventure_sessions']['values'][f'{{conference="{slug}",active="false"}}'] = (
                 WorkadventureSession.objects.filter(conference=conference).exclude(token=None).count()
+            )
 
         context['metrics'] = metrics
 
diff --git a/src/api/views/mixins.py b/src/api/views/mixins.py
index a265b67c216561bb2a107e07d196b133d24be726..8b99f77aa8560d39e9a31cbe0feb61cdd0bd7f33 100644
--- a/src/api/views/mixins.py
+++ b/src/api/views/mixins.py
@@ -54,7 +54,7 @@ class ConferenceSlugAssemblyMixin(ConferenceSlugMixin):
             try:
                 self._assembly = Assembly.objects.accessible_by_user(self.request.user, self.conference).get(slug=assembly_slug)
             except Assembly.DoesNotExist:
-                if issuing_token := self.kwargs.get("issuing_token", None):
+                if issuing_token := self.kwargs.get('issuing_token', None):
                     try:
                         self._assembly = Badge.objects.get(issuing_token=issuing_token).issuing_assembly
                     except Badge.DoesNotExist:
diff --git a/src/api/views/rooms.py b/src/api/views/rooms.py
index 544c26cf13e0daade2bf902b6aa37a7bc640c70d..b55800b3871d1b73c8d5310de18e46195c3d45ae 100644
--- a/src/api/views/rooms.py
+++ b/src/api/views/rooms.py
@@ -1,12 +1,12 @@
 import logging
 
 from rest_framework import generics
+
 from core.models.rooms import Room
 
 from ..serializers import RoomSerializer
 from .mixins import ConferenceSlugMixin
 
-
 logger = logging.getLogger(__name__)
 
 
diff --git a/src/api/views/schedule.py b/src/api/views/schedule.py
index b2226234f2f27a29eb3c2910a907c5025bcbf558..d2b44ea3f989466ea00bce41763e729ddd9de2d4 100644
--- a/src/api/views/schedule.py
+++ b/src/api/views/schedule.py
@@ -1,14 +1,15 @@
 import logging
 from datetime import timedelta
 
-from django.db.models import F
-from django.http import JsonResponse, HttpResponse
-from django.utils.dateparse import parse_datetime
-from django.views.generic import View
 from rest_framework import authentication
 from rest_framework.response import Response
 from rest_framework.views import APIView
 
+from django.db.models import F
+from django.http import HttpResponse, JsonResponse
+from django.utils.dateparse import parse_datetime
+from django.views.generic import View
+
 from core.models.assemblies import Assembly
 from core.models.conference import ConferenceExportCache, ConferenceTrack
 from core.models.events import Event
@@ -58,12 +59,13 @@ class ConferenceSchedule(BaseScheduleView):
         return ''
 
     def get_events(self, **kwargs):
-        return Event.objects \
-            .conference_accessible(conference=self.conference) \
-            .exclude(schedule_duration=None) \
-            .exclude(schedule_duration__lte=timedelta(minutes=5)) \
-            .select_related('track', 'room', 'assembly') \
+        return (
+            Event.objects.conference_accessible(conference=self.conference)
+            .exclude(schedule_duration=None)
+            .exclude(schedule_duration__lte=timedelta(minutes=5))
+            .select_related('track', 'room', 'assembly')
             .order_by(F('assembly__is_official').desc(nulls_last=True), F('room__capacity').desc(nulls_last=True), 'schedule_start')
+        )
 
 
 class AssemblySchedule(BaseScheduleView):
@@ -73,11 +75,12 @@ class AssemblySchedule(BaseScheduleView):
 
     def get_events(self):
         assembly_id = self.request.resolver_match.kwargs.get('assembly')
-        return Event.objects \
-            .conference_accessible(conference=self.conference) \
-            .select_related('track', 'room') \
-            .filter(assembly_id=assembly_id) \
+        return (
+            Event.objects.conference_accessible(conference=self.conference)
+            .select_related('track', 'room')
+            .filter(assembly_id=assembly_id)
             .order_by('room__room_type', F('room__capacity').desc(nulls_last=True), 'room__name', 'schedule_start')
+        )
 
 
 class RoomSchedule(BaseScheduleView):
@@ -87,10 +90,7 @@ class RoomSchedule(BaseScheduleView):
 
     def get_events(self):
         room_id = self.request.resolver_match.kwargs.get('pk')
-        return Event.objects \
-            .conference_accessible(conference=self.conference) \
-            .filter(room_id=room_id) \
-            .order_by('schedule_start')
+        return Event.objects.conference_accessible(conference=self.conference).filter(room_id=room_id).order_by('schedule_start')
 
 
 class EventSchedule(ConferenceSlugMixin, APIView):
@@ -98,9 +98,7 @@ class EventSchedule(ConferenceSlugMixin, APIView):
     permission_classes = [IsApiUserOrReadOnly]
 
     def get(self, request, conference, pk, format=None, **kwargs):
-        event = Event.objects \
-            .accessible_by_user(conference=self.conference, user=self.request.user) \
-            .get(pk=pk)
+        event = Event.objects.accessible_by_user(conference=self.conference, user=self.request.user).get(pk=pk)
         return Response(ScheduleEncoder().encode_event(event, self.conference.timezone))
 
     def post(self, request, conference, pk, format=None, **kwargs):
diff --git a/src/api/views/sso.py b/src/api/views/sso.py
index 80c5e849d1f6f861f992de6c89cc073ddb10f08c..47543108cf693887e493baf959592a459d92759e 100644
--- a/src/api/views/sso.py
+++ b/src/api/views/sso.py
@@ -2,15 +2,14 @@ import calendar
 import json
 import logging
 
+from oauth2_provider.models import get_access_token_model
+from oauth2_provider.views import ClientProtectedScopedResourceView
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.http import HttpResponse
 from django.utils.decorators import method_decorator
-from django.views.generic import TemplateView
 from django.views.decorators.csrf import csrf_exempt
-
-from oauth2_provider.models import get_access_token_model
-from oauth2_provider.views import ClientProtectedScopedResourceView
-
+from django.views.generic import TemplateView
 
 logger = logging.getLogger(__name__)
 
@@ -19,7 +18,7 @@ class OutOfBandDisplayTokenView(TemplateView):
     template_name = 'oauth2_provider/out-of-band-display-token.html'
 
 
-@method_decorator(csrf_exempt, name="dispatch")
+@method_decorator(csrf_exempt, name='dispatch')
 class IntrospectTokenView(ClientProtectedScopedResourceView):
     """
     Implements an endpoint for token introspection based
@@ -35,7 +34,7 @@ class IntrospectTokenView(ClientProtectedScopedResourceView):
     @staticmethod
     def get_token_response(query_user, token_value=None):
         try:
-            token = get_access_token_model().objects.select_related("user", "application").get(token=token_value)
+            token = get_access_token_model().objects.select_related('user', 'application').get(token=token_value)
 
         except ObjectDoesNotExist:
             return HttpResponse(content=json.dumps({'active': False}), status=401, content_type='application/json')
@@ -57,7 +56,6 @@ class IntrospectTokenView(ClientProtectedScopedResourceView):
             'active': True,
             'client_id': token.application.client_id,
             'exp': int(calendar.timegm(token.expires.timetuple())),
-            # 'scope': token.scope,
         }
 
         # prepare user details and preferences
@@ -75,7 +73,7 @@ class IntrospectTokenView(ClientProtectedScopedResourceView):
         :param kwargs:
         :return:
         """
-        return self.get_token_response(request.user, request.GET.get("token", None))
+        return self.get_token_response(request.user, request.GET.get('token', None))
 
     def post(self, request, *args, **kwargs):
         """
@@ -86,4 +84,4 @@ class IntrospectTokenView(ClientProtectedScopedResourceView):
         :param kwargs:
         :return:
         """
-        return self.get_token_response(request.user, request.POST.get("token", None))
+        return self.get_token_response(request.user, request.POST.get('token', None))
diff --git a/src/api/views/users.py b/src/api/views/users.py
index 69699b4a4d5d6004a51d6b4040248cdb54d32bf1..5dd37ac3ee989690e8b9b05f518aad3e32c66388 100644
--- a/src/api/views/users.py
+++ b/src/api/views/users.py
@@ -2,17 +2,19 @@ import io
 import json
 import zipfile
 
-from django.contrib.auth.mixins import LoginRequiredMixin
-from django.http import HttpResponse
-from django.views.generic import View
 from rest_framework import generics, permissions
 from rest_framework.decorators import api_view
 from rest_framework.response import Response
 
-from ..serializers import UserTimelineEntrySerializer
+from django.contrib.auth.mixins import LoginRequiredMixin
+from django.http import HttpResponse
+from django.views.generic import View
+
 from core.models.badges import UserBadge
 from core.models.users import PlatformUser, UserTimelineEntry
 
+from ..serializers import UserTimelineEntrySerializer
+
 
 @api_view(['GET'])
 def profile(request, format=None):
@@ -21,11 +23,13 @@ def profile(request, format=None):
     if not u.is_authenticated:
         u = PlatformUser.get_anonymous_user()
 
-    return Response({
-        'authenticated': u.is_authenticated,
-        'username': u.username,
-        'flags': PlatformUser.get_user_flags(u),
-    })
+    return Response(
+        {
+            'authenticated': u.is_authenticated,
+            'username': u.username,
+            'flags': PlatformUser.get_user_flags(u),
+        }
+    )
 
 
 @api_view(['GET'])
@@ -34,14 +38,19 @@ def friends(request, format=None):
     if not u.is_authenticated:
         return Response([])
 
-    return Response([{
-        'user': c.contact.username,
-        'pending': c.pending,
-        # status may only be shown if that is public or the contact has a allowed sharing to us
-        'status': c.contact.status if c.contact.status_public or (c.reverse_contact_share_status(default=False) and not c.pending) else None,
-        'receive_dms': c.receive_dms,
-        'receive_dm_images': c.receive_dm_images,
-    } for c in u.contacts.all()])
+    return Response(
+        [
+            {
+                'user': c.contact.username,
+                'pending': c.pending,
+                # status may only be shown if that is public or the contact has a allowed sharing to us
+                'status': c.contact.status if c.contact.status_public or (c.reverse_contact_share_status(default=False) and not c.pending) else None,
+                'receive_dms': c.receive_dms,
+                'receive_dm_images': c.receive_dm_images,
+            }
+            for c in u.contacts.all()
+        ]
+    )
 
 
 @api_view(['GET'])
@@ -50,13 +59,18 @@ def badges(request, format=None):
     if not u.is_authenticated:
         return Response([])
 
-    return Response([{
-        'conference': ub.badge.conference.name,
-        'assembly': ub.badge.issuing_assembly.name,
-        'name': ub.badge.name,
-        'image_url': ub.badge.image.url,
-        'is_achievement': ub.badge.is_achievement,
-    } for ub in u.badges.filter(visibility=UserBadge.Visibility.PUBLIC)])
+    return Response(
+        [
+            {
+                'conference': ub.badge.conference.name,
+                'assembly': ub.badge.issuing_assembly.name,
+                'name': ub.badge.name,
+                'image_url': ub.badge.image.url,
+                'is_achievement': ub.badge.is_achievement,
+            }
+            for ub in u.badges.filter(visibility=UserBadge.Visibility.PUBLIC)
+        ]
+    )
 
 
 class BadgeExportView(LoginRequiredMixin, View):
diff --git a/src/api/views/workadventure.py b/src/api/views/workadventure.py
index 9e40b5ee61ca685643b2558ef6beb904cb645992..57110681cb813d2454c51076ebaf9b9c75245c0d 100644
--- a/src/api/views/workadventure.py
+++ b/src/api/views/workadventure.py
@@ -3,13 +3,14 @@ import logging
 from datetime import datetime
 from time import time
 
+from rest_framework.generics import get_object_or_404
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
 from django.conf import settings
 from django.http import Http404
 from django.urls import NoReverseMatch, reverse
 from django.utils import timezone
-from rest_framework.generics import get_object_or_404
-from rest_framework.response import Response
-from rest_framework.views import APIView
 
 from core.abuse import report_content
 from core.integrations import WorkAdventureIntegration
@@ -106,7 +107,9 @@ class MapServiceView(ConferenceSlugMixin, APIView):
                     skipped[room_id] = 'Non-matching assembly id, skipped.'
                     logging.warning(
                         'Got mapservice update for room id %s with non-matching assembly_id (got %s but expected %s).',
-                        room_id, a_id, room.assembly_id,
+                        room_id,
+                        a_id,
+                        room.assembly_id,
                     )
                     continue
 
@@ -126,7 +129,7 @@ class MapServiceView(ConferenceSlugMixin, APIView):
                     if has_mapinfo and is_published:
                         room.backend_status = Room.BackendStatus.ACTIVE
                         update_fields.append('backend_status')
-                        logging.info("setting WA room %s active per MapService update as we now have publishedSince>0", room_id)
+                        logging.info('setting WA room %s active per MapService update as we now have publishedSince>0', room_id)
 
                 elif room.backend_status in [Room.BackendStatus.ACTIVE]:
                     # if 'publishedSince' is not set any more, we should signal an error
@@ -164,6 +167,7 @@ class MapBackendListView(ConferenceSlugMixin, APIView):
 
     It is queried to fetch all currently active maps.
     """
+
     permission_classes = [IsConferenceService | IsSuperUser]
     required_service_classes = ['wa_backend']
 
@@ -201,15 +205,6 @@ class MapBackendReportView(ConferenceSlugMixin, APIView):
 
 class MapBackendReportUserView(MapBackendReportView):
     def post(self, request, *args, **kwargs):
-        # interface reportUser {
-        #     reportedUserUuid: string;
-        #     reportedUserIPAdress?: string;
-        #     reporterUserUuid: string;
-        #     orgSlug: string;
-        #     worldSlug: string;
-        #     roomSlug: string;
-        #     comment?: string;
-        # }
         reporting_user, data, comment, solution = self.extract_map_report_data(request)
 
         reported_session = WorkadventureSession.objects.filter(conference=self.conference, id=data.get('reportedUserUuid')).first()
@@ -231,18 +226,6 @@ class MapBackendReportUserView(MapBackendReportView):
 
 class MapBackendReportMapView(MapBackendReportView):
     def post(self, request, *args, **kwargs):
-        # interface reportMap {
-        #     mapUrl: string;
-        #     orgSlug: string;
-        #     worldSlug: string;
-        #     roomSlug: string;
-        #     reporterUserUuid: string;
-        #     comment?: string;
-        #     position: {
-        #         x: number:
-        #         y: number;
-        #     };
-        # }
         reporting_user, data, comment, solution = self.extract_map_report_data(request)
 
         report_content(
@@ -274,10 +257,13 @@ class MapBackendRedeemBadgeTokenView(ConferenceSlugMixin, APIView):
 
         created = badge_token.redeem(wa_session.user, False)
         if created:
-            return Response({
-                'msg': f'{badge_token.badge}',
-                'icon': f'{badge_token.badge.image.url}',
-            }, status=201)
+            return Response(
+                {
+                    'msg': f'{badge_token.badge}',
+                    'icon': f'{badge_token.badge.image.url}',
+                },
+                status=201,
+            )
         else:
             return Response(status=204)
 
@@ -323,7 +309,7 @@ class UserInfoView(ConferenceSlugMixin, APIView):
         try:
             wa_session = WorkadventureSession.objects.get(conference=self.conference, pk=uid)
         except WorkadventureSession.DoesNotExist:
-            raise Http404()
+            raise Http404
 
         return Response(wa_session.export_userdata())
 
@@ -354,9 +340,9 @@ class RegisterView(ConferenceSlugMixin, APIView):
             wa_session = WorkadventureSession.objects.get(conference=self.conference, token=token)
             if wa_session.token_expiry < timezone.now():
                 logger.info('WA session %s: token had expired (%s)', wa_session.id, token)
-                raise Http404()
+                raise Http404
         except WorkadventureSession.DoesNotExist:
-            raise Http404()
+            raise Http404
 
         if wa_session.additional_data is None:
             wa_session.additional_data = {}
diff --git a/src/backoffice/forms.py b/src/backoffice/forms.py
index 062b6c3f5fe8cf5b305d87f7584f15fac5e0f964..16576eca3ffb7f933a8b7793b98ebafe2faee61f 100644
--- a/src/backoffice/forms.py
+++ b/src/backoffice/forms.py
@@ -45,10 +45,14 @@ class ProfileForm(forms.ModelForm):
         fields = [
             'show_name',
             # 'description',
-            'status', 'status_public',
+            'status',
+            'status_public',
             'timezone',
-            'no_animations', 'colorblind', 'high_contrast',
-            'receive_dms', 'receive_dm_images',
+            'no_animations',
+            'colorblind',
+            'high_contrast',
+            'receive_dms',
+            'receive_dm_images',
             'autoaccept_contacts',
         ]
 
@@ -111,7 +115,7 @@ class AssemblyEditForm(TranslatedFieldsForm):
         ]
 
     def __init__(self, *args, staff_access: bool, staff_mode: bool, assembly_staff_access: bool, channel_staff_access: bool, **kwargs):
-        super(AssemblyEditForm, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
         # configure fields' widget customizations
         self.fields['assembly_link'].widget.attrs['placeholder'] = 'https://'
@@ -159,8 +163,8 @@ class AssemblyEditForm(TranslatedFieldsForm):
             raise ValidationError(_('Assembly__tags__splitwithcomma'))
 
         for tag in split_tags:
-            tag = tag.strip()
-            validate_slug(tag)
+            stripped_tag = tag.strip()
+            validate_slug(stripped_tag)
 
         return tags
 
@@ -367,14 +371,13 @@ class EditAssemblyRoomForm(TranslatedFieldsForm):
             self.fields['backend_status'] = forms.CharField(initial=self.instance.get_backend_status_display(), disabled=True)
         if self.instance.room_type not in Room.TYPES_WITH_CAPACITY:
             del self.fields['capacity']
-        else:
-            if self.instance.room_type == Room.RoomType.BIGBLUEBUTTON:
-                self.fields['capacity'].label = _('Room-bigbluebutton_capacity')
-                self.fields['capacity'].help_text = _('Room-bigbluebutton_capacity__help')
+        elif self.instance.room_type == Room.RoomType.BIGBLUEBUTTON:
+            self.fields['capacity'].label = _('Room-bigbluebutton_capacity')
+            self.fields['capacity'].help_text = _('Room-bigbluebutton_capacity__help')
 
-                if self.instance.backend_status == Room.BackendStatus.ACTIVE:
-                    self.fields['capacity'].help_text += ' ' + gettext('Room-bigbluebutton__activeroom')
-                    self.fields['capacity'].disabled = True
+            if self.instance.backend_status == Room.BackendStatus.ACTIVE:
+                self.fields['capacity'].help_text += ' ' + gettext('Room-bigbluebutton__activeroom')
+                self.fields['capacity'].disabled = True
 
         if self.instance.room_type in [Room.RoomType.HANGAR]:
             self.fields['name'].disabled = True
@@ -386,7 +389,7 @@ class EditAssemblyRoomForm(TranslatedFieldsForm):
 
     def clean_name(self):
         if Room.objects.filter(assembly=self.instance.assembly, name=self.cleaned_data['name']).exclude(pk=self.instance.pk).exists():
-            raise ValidationError(_("Room-name-assembly-unique"))
+            raise ValidationError(_('Room-name-assembly-unique'))
 
         # update slug to be based on the new name iff the name changed.
         if self.instance.name != self.cleaned_data['name']:
@@ -408,10 +411,10 @@ class EditAssemblyRoomForm(TranslatedFieldsForm):
         try:
             capacity = int(capacity)
         except ValueError:
-            raise ValidationError(_("Room-capacity-invalid"))
+            raise ValidationError(_('Room-capacity-invalid'))
 
         if capacity < 0:
-            raise ValidationError(_("Room-capacity-negative"))
+            raise ValidationError(_('Room-capacity-negative'))
         return capacity
 
     def save(self, commit=False):
@@ -466,24 +469,21 @@ class EventForm(TranslatedFieldsForm):
     class Meta:
         model = Event
         fields = ['room', 'name', 'language', 'banner_image', 'is_public', 'abstract', 'description', 'schedule_start', 'schedule_duration']
-        widgets = {
-            'abstract': forms.Textarea(attrs={'rows': 4}),
-            'is_public': forms.HiddenInput()
-        }
+        widgets = {'abstract': forms.Textarea(attrs={'rows': 4}), 'is_public': forms.HiddenInput()}
 
     def __init__(self, *args, conference, rooms=None, assembly=None, owner=None, create=False, publish=False, **kwargs):
         super().__init__(*args, **kwargs)
         self.conference = conference
         self.create = create
         self.sos = not assembly
-        self.event_type_name = _("SoS") if self.sos else _('event')
+        self.event_type_name = _('SoS') if self.sos else _('event')
         self.owner = owner
         self.publish = publish
         if not assembly:
             del self.fields['room']
             del self.fields['banner_image']
             del self.fields['abstract']
-            self.fields["is_public"].initial = True
+            self.fields['is_public'].initial = True
             self.assembly = self.conference.self_organized_sessions_assembly
             self.kind = Event.Kind.SELF_ORGANIZED
         else:
@@ -495,7 +495,7 @@ class EventForm(TranslatedFieldsForm):
         self.fields['schedule_start'].widget.attrs['placeholder'] = _('Event__schedule_start__placeholder')
 
     def clean(self):
-        if self.cleaned_data["schedule_duration"] is None:
+        if self.cleaned_data['schedule_duration'] is None:
             self.instance.schedule_end = None
         self.cleaned_data['is_public'] = not self.instance.is_public if self.publish else self.instance.is_public
         self.instance.conference = self.conference
@@ -527,6 +527,7 @@ class BadgeForm(TranslatedFieldsForm):
     class Meta:
         model = Badge
         fields = ['name', 'state', 'category', 'image', 'description', 'location']
+
     create = False
 
     def __init__(self, *args, conference=None, assembly=None, **kwargs) -> None:
@@ -570,10 +571,10 @@ class BadgeTokenTimeConstraintForm(forms.ModelForm):
         self.fields['date_time_range'].label = _('BadgeTokenTimeConstraint__date_time_range__label')
         self.fields['date_time_range'].widget.widgets[0].attrs['placeholder'] = _('BadgeTokenTimeConstraint__date_time_range__placeholder-start')
         # TODO: value bei Ausgabe richtig setzen, sonst wird existierender Wert nicht mehr angezeigt
-        # self.fields["date_time_range"].widget.widgets[0].input_type = "datetime-local"
+        # self.fields["date_time_range"].widget.widgets[0].input_type = "datetime-local" #  noqa: ERA001
         self.fields['date_time_range'].widget.widgets[1].attrs['placeholder'] = _('BadgeTokenTimeConstraint__date_time_range__placeholder-end')
         # TODO: dito, s.o.
-        # self.fields["date_time_range"].widget.widgets[1].input_type = "datetime-local"
+        # self.fields["date_time_range"].widget.widgets[1].input_type = "datetime-local" #  noqa: ERA001
 
 
 BadgeTokenTimeConstraintFormSet = inlineformset_factory(BadgeToken, BadgeTokenTimeConstraint, form=BadgeTokenTimeConstraintForm, extra=3)
diff --git a/src/backoffice/templatetags/c3assemblies.py b/src/backoffice/templatetags/c3assemblies.py
index f96c7c07ac1181ab68c4918534e4be6c6b8ae339..15e30a999f78720ff89fc769fc7c7c00f2bacb73 100644
--- a/src/backoffice/templatetags/c3assemblies.py
+++ b/src/backoffice/templatetags/c3assemblies.py
@@ -1,10 +1,10 @@
-from datetime import datetime, timedelta
 import json
+from datetime import datetime, timedelta
 
 from django.conf import settings
 from django.template.defaulttags import register
-from django.utils.translation import get_language
 from django.utils.timezone import get_current_timezone
+from django.utils.translation import get_language
 
 from core.models.assemblies import Assembly
 
diff --git a/src/backoffice/tests/__init__.py b/src/backoffice/tests/__init__.py
index 89c9be319a317f2cdacef66baa52d85628ddcd3e..3169e36fc4c077f41cdc5b8492d9da0800812f01 100644
--- a/src/backoffice/tests/__init__.py
+++ b/src/backoffice/tests/__init__.py
@@ -1,5 +1,5 @@
-from .base import *  # noqa: F401, F403
+from .base import *  # noqa: F401, F403, I001
 from .assemblies import *  # noqa: F401, F403
 from .auth import *  # noqa: F401, F403
 
-__all__ = '*'
+__all__ = ('*',)  # noqa: F405
diff --git a/src/backoffice/tests/assemblies.py b/src/backoffice/tests/assemblies.py
index f4740e5db9145613df17fdfcefee3c5b9dc9d114..a931e439e062ffeff96c5936b73b4d8c57fcebfd 100644
--- a/src/backoffice/tests/assemblies.py
+++ b/src/backoffice/tests/assemblies.py
@@ -1,20 +1,19 @@
 from django.urls import reverse
 
-from backoffice.tests import BackOfficeTestCase
 from core.models import Assembly, AssemblyLink
 
+from backoffice.tests import BackOfficeTestCase
+
 
 class AssemblyListViewTest(BackOfficeTestCase):
     def setUp(self):
         super().setUp()
-        self.assemblies = [Assembly(slug=a, name=a, is_virtual=True, conference_id=self.conf.id, state_assembly=Assembly.State.ACCEPTED)
-                           for a in ('a1', 'a2', 'a3')]
+        self.assemblies = [
+            Assembly(slug=a, name=a, is_virtual=True, conference_id=self.conf.id, state_assembly=Assembly.State.ACCEPTED) for a in ('a1', 'a2', 'a3')
+        ]
         for a in self.assemblies:
             a.save()
-        self.assembly_links = [
-            AssemblyLink(a=self.assemblies[0], b=self.assemblies[1]),
-            AssemblyLink(a=self.assemblies[0], b=self.assemblies[2])
-        ]
+        self.assembly_links = [AssemblyLink(a=self.assemblies[0], b=self.assemblies[1]), AssemblyLink(a=self.assemblies[0], b=self.assemblies[2])]
         for al in self.assembly_links:
             al.save()
 
diff --git a/src/backoffice/tests/auth.py b/src/backoffice/tests/auth.py
index e6130b06b70003c97e28039171934019c1d912c1..429cdb13ca7bc881ddee3d1fd4163d646cf97387 100644
--- a/src/backoffice/tests/auth.py
+++ b/src/backoffice/tests/auth.py
@@ -10,33 +10,35 @@ class PasswordResetTest(BackOfficeTestCase):
     @override_settings(LANGUAGE_CODE='en', AUTH_PASSWORD_VALIDATORS=[])
     def test_password_reset(self):
         resp = self.client.get(reverse('backoffice:password_reset'))
-        self.assertNotContains(resp, "Reset password link invalid")
+        self.assertNotContains(resp, 'Reset password link invalid')
 
     @override_settings(LANGUAGE_CODE='en', AUTH_PASSWORD_VALIDATORS=[])
     def test_invalid_password_reset_link(self):
-        from django.contrib.auth.tokens import default_token_generator
         from datetime import timedelta
+
+        from django.contrib.auth.tokens import default_token_generator
         from django.utils.encoding import force_bytes
         from django.utils.http import urlsafe_base64_encode
+
         uidb = urlsafe_base64_encode(force_bytes(self.user.pk))
         expired_token = default_token_generator._make_token_with_timestamp(
-            self.user, default_token_generator._num_seconds(default_token_generator._now() - timedelta(days=365)), secret=None)
-        expired_resp = self.client.get(reverse('backoffice:password_reset_confirm',
-                                               kwargs={'uidb64': uidb, 'token': expired_token}), follow=True)
+            self.user, default_token_generator._num_seconds(default_token_generator._now() - timedelta(days=365)), secret=None
+        )
+        expired_resp = self.client.get(reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': expired_token}), follow=True)
         self.assertRedirects(expired_resp, f"{reverse('backoffice:password_reset')}?retry=True")
         self.assertContains(expired_resp, 'Reset password link invalid')
 
-        invalid_session_resp = self.client.get(
-            reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': 'set-password'}))
+        invalid_session_resp = self.client.get(reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': 'set-password'}))
         self.assertRedirects(invalid_session_resp, f"{reverse('backoffice:password_reset')}?retry=True")
         self.assertNotIn(INTERNAL_RESET_SESSION_TOKEN, self.client.session)
 
     @override_settings(LANGUAGE_CODE='en', AUTH_PASSWORD_VALIDATORS=[])
     def test_PasswordResetConfirmView(self):
-        from django.utils.encoding import force_bytes
-        from django.utils.http import urlsafe_base64_encode
         from django.contrib.auth.tokens import default_token_generator
         from django.contrib.auth.views import INTERNAL_RESET_SESSION_TOKEN
+        from django.utils.encoding import force_bytes
+        from django.utils.http import urlsafe_base64_encode
+
         self.client.force_login(self.user)
         self.client.cookies = SimpleCookie()
         self.user.set_password('forgotten')
@@ -46,14 +48,15 @@ class PasswordResetTest(BackOfficeTestCase):
 
         resp = self.client.get(reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': token}))
         self.assertEqual(self.client.session[INTERNAL_RESET_SESSION_TOKEN], token)
-        self.assertRedirects(resp, reverse('backoffice:password_reset_confirm',
-                                           kwargs={'uidb64': uidb, 'token': 'set-password'}))
+        self.assertRedirects(resp, reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': 'set-password'}))
 
         resp = self.client.post(
-            reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': 'set-password'}), {
+            reverse('backoffice:password_reset_confirm', kwargs={'uidb64': uidb, 'token': 'set-password'}),
+            {
                 'new_password1': '4',  # chosen by fair dice roll
                 'new_password2': '4',  # guranteed to be random
-            })
+            },
+        )
         self.assertRedirects(resp, reverse('backoffice:password_reset_complete'))
 
         self.user.refresh_from_db()
diff --git a/src/backoffice/tests/base.py b/src/backoffice/tests/base.py
index b868f77e08c8a08217ff1a0faa10a8dd1a373e43..5d4ed2f7142ca8b472bac3224e6303e1a2806875 100644
--- a/src/backoffice/tests/base.py
+++ b/src/backoffice/tests/base.py
@@ -1,9 +1,9 @@
 import uuid
-from datetime import datetime, UTC
+from datetime import UTC, datetime
 
-from django.test import override_settings, TestCase
+from django.test import TestCase, override_settings
 
-from core.models import Conference, PlatformUser, ConferenceMember
+from core.models import Conference, ConferenceMember, PlatformUser
 
 TEST_CONF_ID = uuid.uuid4()
 
diff --git a/src/backoffice/urls.py b/src/backoffice/urls.py
index 94e89525f1795993b606415241d9cc51a971bd4e..1c885754866c0fc1cc90346c26786881e07286a7 100644
--- a/src/backoffice/urls.py
+++ b/src/backoffice/urls.py
@@ -1,66 +1,44 @@
 from django.urls import path, re_path
-
 from django.views.i18n import set_language
 
-from .views import \
-    assemblies, \
-    assemblyteam, \
-    auth, \
-    badges, \
-    channelteam, \
-    events, \
-    map, \
-    misc, \
-    profile, \
-    schedules, \
-    users, \
-    vouchers, \
-    wiki, \
-    workadventure
-
+from .views import assemblies, assemblyteam, auth, badges, channelteam, events, map, misc, profile, schedules, users, vouchers, wiki, workadventure
 
 app_name = 'backoffice'
 urlpatterns = [
     path('', misc.IndexView.as_view(), name='index'),
-
     path('accounts/profile/', profile.ProfileView.as_view(), name='profile'),
     path('accounts/signup/', auth.RegistrationView.as_view(), name='signup'),
     path('accounts/signup/done', auth.SignupDoneView.as_view(), name='account_activation_sent'),
-
     path('accounts/change-password/', auth.PasswordChangeView.as_view(), name='password_change'),
     path('accounts/change-password/done', auth.PasswordChangeDoneView.as_view(), name='password_change_done'),
-
     path('accounts/reset-password/', auth.PasswordResetView.as_view(), name='password_reset'),
     path('accounts/reset-password/done', auth.PasswordResetDoneView.as_view(), name='password_reset_done'),
     path('accounts/reset-password/confirm/<uidb64>/<token>/', auth.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
     path('accounts/reset-password/complete', auth.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
-
-    re_path(r'^accounts/activate/(?P<uid_b64>[0-9A-Za-z_\-]+)/(?P<channel_id>\d+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$', auth.RegistrationActivationView.as_view(), name='signup_activate'),  # noqa: E501
-
+    re_path(
+        r'^accounts/activate/(?P<uid_b64>[0-9A-Za-z_\-]+)/(?P<channel_id>\d+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$',
+        auth.RegistrationActivationView.as_view(),
+        name='signup_activate',
+    ),  # noqa: E501
     path('login', auth.LoginView.as_view(), name='login'),
     path('logout', auth.LogoutView.as_view(), name='logout'),
     path('auth_debug', auth.AuthDebugView.as_view()),
-
     path('conferences', misc.ConferenceSelectionView.as_view(), name='conference_selection'),
-
     path('wiki', wiki.WikiOverviewView.as_view(), name='wiki'),
     path('wiki/namespaces', wiki.NamespaceListView.as_view(), name='wiki-namespaces'),
     path('wiki/pages', wiki.PagesView.as_view(), name='wiki-pages'),
     path('wiki/page/<uuid:pk>', wiki.PageView.as_view(), name='wiki-page-detail'),
     path('wiki/page/<uuid:pk>/delete', wiki.PageDeleteView.as_view(), name='wiki-page-delete'),
     path('wiki/page/<uuid:pk>/delete-revision', wiki.PageRevisionDeleteView.as_view(), name='wiki-page-revision-delete'),
-
     path('assemblies', assemblyteam.AssembliesView.as_view(), name='assemblies'),
     path('assemblies/list/<str:variant>', assemblyteam.AssembliesListsView.as_view(), name='assemblieslist'),
     path('channels', channelteam.ChannelsView.as_view(), name='channels'),
     path('channels/list/<str:variant>', channelteam.ChannelsListView.as_view(), name='channelslist'),
-
     path('assemblyteam/<uuid:pk>', assemblyteam.AssemblyView.as_view(), name='assemblyteam-detail'),
     path('assemblyteam/<uuid:pk>/state', assemblyteam.AssemblyEditStateView.as_view(), name='assemblyteam-editstate'),
     path('assemblyteam/<uuid:pk>/hierarchy', assemblyteam.AssemblyEditHierarchyView.as_view(), name='assemblyteam-edithierarchy'),
     path('assemblyteam/<uuid:pk>/position', assemblyteam.AssemblyEditPlacementView.as_view(), name='assemblyteam-editposition'),
     path('assemblyteam/<uuid:pk>/message', assemblyteam.AssemblyMessageView.as_view(), name='assemblyteam-message'),
-
     path('assembly/create', assemblies.CreateAssemblyView.as_view(), name='assembly-create'),
     path('assembly/<uuid:pk>', assemblies.AssemblyView.as_view(), name='assembly'),
     path('assembly/<uuid:pk>/edit', assemblies.EditAssemblyView.as_view(), name='assembly-edit'),
@@ -70,43 +48,38 @@ urlpatterns = [
     path('assembly/<uuid:pk>/members/add', assemblies.MembersAddView.as_view(), name='assembly-members-add'),
     path('assembly/<uuid:pk>/members/edit/<str:uname>', assemblies.MembersEditView.as_view(), name='assembly-members-edit'),
     path('assembly/<uuid:pk>/vouchers', assemblies.VouchersView.as_view(), name='assembly-vouchers'),
-
     path('assembly/<uuid:assembly>/auth', assemblies.AuthView.as_view(), name='assembly-auth'),
     path('assembly/<uuid:assembly>/auth/app/<int:pk>', assemblies.AuthAppView.as_view(), name='assembly-auth-app'),
     path('assembly/<uuid:assembly>/auth/new_token', assemblies.AuthGetTokenView.as_view(), name='assembly-auth-gettoken'),
-
     path('assembly/<uuid:assembly>/badges', badges.BadgesView.as_view(), name='assembly-badges'),
     path('assembly/<uuid:assembly>/badge/new', badges.CreateBadgeView.as_view(), name='assembly-create-badge'),
     path('assembly/<uuid:assembly>/badge/<uuid:pk>', badges.BadgeView.as_view(), name='assembly-badge'),
     path('assembly/<uuid:assembly>/badge/<uuid:pk>/renew_token', badges.RenewBadgeIssuingTokenView.as_view(), name='assembly-badge-renew'),
     path('assembly/<uuid:assembly>/badge/<uuid:pk>/remove', badges.RemoveBadgeView.as_view(), name='assembly-badge-remove'),
     path('assembly/<uuid:assembly>/badge/<uuid:pk>/award', badges.AwardBadgeView.as_view(), name='assembly-badge-award'),
-    path('assembly/<uuid:assembly>/badge/<uuid:badge>/redeem_token/new',
-         badges.CreateBadgeTokenView.as_view(), name='assembly-badge-create-redeem-token'),
-    path('assembly/<uuid:assembly>/badge/<uuid:badge>/redeem_token/<uuid:pk>',
-         badges.BadgeTokenEditView.as_view(), name='assembly-badge-redeem-token'),
-    path('assembly/<uuid:assembly>/badge/<uuid:badge>/redeem_token/<uuid:pk>/switch',
-         badges.BadgeTokenToggleActiveView.as_view(), name='assembly-badge-toggle-active-redeem-token'),
-
+    path('assembly/<uuid:assembly>/badge/<uuid:badge>/redeem_token/new', badges.CreateBadgeTokenView.as_view(), name='assembly-badge-create-redeem-token'),
+    path('assembly/<uuid:assembly>/badge/<uuid:badge>/redeem_token/<uuid:pk>', badges.BadgeTokenEditView.as_view(), name='assembly-badge-redeem-token'),
+    path(
+        'assembly/<uuid:assembly>/badge/<uuid:badge>/redeem_token/<uuid:pk>/switch',
+        badges.BadgeTokenToggleActiveView.as_view(),
+        name='assembly-badge-toggle-active-redeem-token',
+    ),
     path('assembly/<uuid:assembly>/new_event', events.AssemblyCreateEventView.as_view(), name='assembly-create-event'),
     path('assembly/<uuid:assembly>/events', events.AssemblyEventsView.as_view(), name='assembly-events'),
     path('assembly/<uuid:assembly>/new_room', assemblies.CreateRoomView.as_view(), name='assembly-create-room'),
     path('assembly/<uuid:assembly>/new_project', assemblies.CreateProjectView.as_view(), name='assembly-create-project'),
-
     path('assembly/<uuid:assembly>/e/<uuid:pk>/', events.AssemblyEventView.as_view(), name='assembly-event'),
     path('assembly/<uuid:assembly>/e/<uuid:pk>/remove', events.AssemblyRemoveEventView.as_view(), name='assembly-event-remove'),
     path('assembly/<uuid:assembly>/r/<uuid:pk>/', assemblies.AssemblyRoomView.as_view(), name='assembly-room'),
     path('assembly/<uuid:assembly>/r/<uuid:room>/new_link', assemblies.CreateRoomLinkView.as_view(), name='roomlink-create'),
     path('assembly/<uuid:assembly>/r/<uuid:room>/remove_link', assemblies.RemoveRoomLinkView.as_view(), name='roomlink-remove'),
     path('assembly/<uuid:assembly>/r/<uuid:room>/remove', assemblies.RemoveRoomView.as_view(), name='assembly-remove-room'),
-
     path('map/floors', map.FloorListView.as_view(), name='map-floor-list'),
     path('map/floor/new', map.FloorCreateView.as_view(), name='map-floor-create'),
     path('map/floor/<uuid:pk>', map.FloorUpdateView.as_view(), name='map-floor-edit'),
     path('map/pois', map.POIListView.as_view(), name='map-poi-list'),
     path('map/poi/new', map.POICreateView.as_view(), name='map-poi-create'),
     path('map/poi/<uuid:pk>', map.POIUpdateView.as_view(), name='map-poi-edit'),
-
     path('schedule/', schedules.SchedulesIndexView.as_view(), name='schedules'),
     path('schedule/sources', schedules.ScheduleSourcesListView.as_view(), name='schedulesource-list'),
     path('schedule/source/add', schedules.ScheduleSourcesCreateView.as_view(), name='schedulesource-add'),
@@ -115,12 +88,10 @@ urlpatterns = [
     path('schedule/source/<uuid:pk>/import', schedules.ScheduleSourcesDoImportView.as_view(), name='schedulesource-import'),
     path('schedule/source/<uuid:pk>/update', schedules.ScheduleSourcesUpdateView.as_view(), name='schedulesource-edit'),
     path('schedule/import/<int:pk>', schedules.ScheduleSourceImportDetailView.as_view(), name='schedulesourceimport-detail'),
-
     path('sos/', events.SoSIndexView.as_view(), name='sos'),
     path('sos/new', events.SoSCreateView.as_view(), name='sos-create'),
     path('sos/<uuid:pk>/', events.SosEditView.as_view(), name='sos-edit'),
     path('sos/<uuid:pk>/delete', events.SosDeleteView.as_view(), name='sos-delete'),
-
     path('wa', workadventure.IndexView.as_view(), name='workadventure'),
     path('wa/maps', workadventure.MapsView.as_view(), name='workadventure-map-list'),
     path('wa/map/<uuid:pk>', workadventure.MapView.as_view(), name='workadventure-map-detail'),
@@ -140,7 +111,6 @@ urlpatterns = [
     path('wa/texture/<uuid:pk>/assembly_remove', workadventure.TextureAssemblyRemoveView.as_view(), name='workadventure-texture-assembly-remove'),
     path('wa/texture/<uuid:pk>/user_assign', workadventure.TextureUserAssignView.as_view(), name='workadventure-texture-user-assign'),
     path('wa/texture/<uuid:pk>/user_remove', workadventure.TextureUserRemoveView.as_view(), name='workadventure-texture-user-remove'),
-
     path('users', users.UsersView.as_view(), name='users'),
     path('users/<int:pk>', users.UserView.as_view(), name='user-detail'),
     path('users/<int:pk>/block', users.UserBlockView.as_view(), name='user-block'),
@@ -151,10 +121,7 @@ urlpatterns = [
     path('users/<int:user_id>/board', users.UserBoardEntries.as_view(page=1), name='user-board'),
     path('users/<int:user_id>/board/page<int:page>', users.UserBoardEntries.as_view(), name='user-board-page'),
     path('users/<int:user_id>/board-hide/', users.UserBoardEntriesHide.as_view(), name='user-board-hide'),
-
     path('vouchers', vouchers.VouchersView.as_view(), name='vouchers'),
-
     path('set_language', set_language, name='set_language'),
-
     path('_boom', misc.BoomView.as_view()),
 ]
diff --git a/src/backoffice/views/assemblies.py b/src/backoffice/views/assemblies.py
index 6e93cd3a8402a767050c3a6a976f8e23d533965c..2d7099ec3cd8acf046ebb6368e24d5d41f7b96bb 100644
--- a/src/backoffice/views/assemblies.py
+++ b/src/backoffice/views/assemblies.py
@@ -1,22 +1,25 @@
 import logging
 from datetime import date
 
+from rest_framework.authtoken.models import Token
+
 from django.conf import settings
 from django.contrib import messages
 from django.core.exceptions import PermissionDenied
-from django.http import HttpResponse, Http404
+from django.http import Http404, HttpResponse
 from django.shortcuts import get_object_or_404, redirect, render
-from django.utils.safestring import mark_safe
-from django.views.generic import TemplateView, View
-from django.views.generic.edit import CreateView, FormView, UpdateView
 from django.urls import reverse
 from django.utils import timezone
 from django.utils.html import format_html
+from django.utils.safestring import mark_safe
 from django.utils.text import format_lazy
-from django.utils.translation import get_language, gettext, gettext_lazy as _, gettext_noop
-from rest_framework.authtoken.models import Token
+from django.utils.translation import get_language, gettext, gettext_noop
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import TemplateView, View
+from django.views.generic.edit import CreateView, FormView, UpdateView
 
-from core.models.assemblies import Assembly, AssemblyMember, AssemblyLink
+from core.integrations import BigBlueButton, Hangar, IntegrationError, WorkAdventure
+from core.models.assemblies import Assembly, AssemblyLink, AssemblyMember
 from core.models.conference import ConferenceExportCache
 from core.models.events import Event
 from core.models.rooms import Room, RoomLink
@@ -24,18 +27,23 @@ from core.models.sso import Application
 from core.models.tags import ConferenceTag
 from core.models.users import PlatformUser
 from core.models.voucher import Voucher
-from core.integrations import BigBlueButton, Hangar, IntegrationError, WorkAdventure
-
-from ..forms import \
-    AssemblyAddApplicationForm, AssemblyAddMemberForm, \
-    AssemblyCreateForm, AssemblyCreateRoomGenericForm, AssemblyCreateRoomBigBlueButtonForm, AssemblyCreateRoomWorkAdventureForm, AssemblyCreateRoomHangarForm, \
-    AssemblyEditForm, \
-    AssemblyMemberEditForm, \
-    CreateAssemblyRoomLinkForm, \
-    EditAssemblyRoomForm, EditAssemblyRoomHangarForm, EditAssemblyRoomWorkAdventureForm
-
-from .mixins import ConferenceMixin, AssemblyMixin
 
+from ..forms import (
+    AssemblyAddApplicationForm,
+    AssemblyAddMemberForm,
+    AssemblyCreateForm,
+    AssemblyCreateRoomBigBlueButtonForm,
+    AssemblyCreateRoomGenericForm,
+    AssemblyCreateRoomHangarForm,
+    AssemblyCreateRoomWorkAdventureForm,
+    AssemblyEditForm,
+    AssemblyMemberEditForm,
+    CreateAssemblyRoomLinkForm,
+    EditAssemblyRoomForm,
+    EditAssemblyRoomHangarForm,
+    EditAssemblyRoomWorkAdventureForm,
+)
+from .mixins import AssemblyMixin, ConferenceMixin
 
 logger = logging.getLogger(__name__)
 
@@ -225,8 +233,8 @@ class EditAssemblyView(AssemblyMixin, UpdateView):
 
         # update tags: go through supplied list of tags
         given_tags = form.cleaned_data.get('tags').split(',')
-        for tag in given_tags:
-            tag = tag.strip()  # type: str
+        for raw_tag in given_tags:
+            tag = raw_tag.strip()  # type: str
             if len(tag) == 0:
                 # skip empty ones
                 continue
@@ -282,7 +290,7 @@ class EditAssemblyView(AssemblyMixin, UpdateView):
 
             if parent_id != assembly.parent_id:
                 if parent is not None and parent.hierarchy == Assembly.Hierarchy.CLUSTER_RESTRICTED:
-                    raise PermissionDenied()
+                    raise PermissionDenied
                 if parent is not None:
                     logger.info(
                         'Assigning assembly "%(assembly_slug)s" (%(assembly_pk)s) to "%(parent_slug)s" (%(parent_pk)s) upon request by <%(user)s>.',
@@ -328,14 +336,19 @@ class EditAssemblyView(AssemblyMixin, UpdateView):
         if settings.DEBUG:
             messages.info(
                 self.request,
-                format_html('<b>DEBUG INFO:</b> <em>changes</em> on assembly "{0}"', assembly.slug) +
-                mark_safe('<table class="table table-border table-striped table-sm"><thead><tr><th>field</th><th>value</th></tr></thead><tbody>') +  # noqa:E501
-                mark_safe(''.join(format_html(
-                    '<tr><td>{0}</td><td>{1}</td></tr>',
-                    k,
-                    format_html('<s>{}</s> {}', *v) if isinstance(v, tuple) else v,
-                ) for k, v in changes.items())) +
-                mark_safe('</tbody></table>')
+                format_html('<b>DEBUG INFO:</b> <em>changes</em> on assembly "{0}"', assembly.slug)
+                + mark_safe('<table class="table table-border table-striped table-sm"><thead><tr><th>field</th><th>value</th></tr></thead><tbody>')  # noqa:E501
+                + mark_safe(
+                    ''.join(
+                        format_html(
+                            '<tr><td>{0}</td><td>{1}</td></tr>',
+                            k,
+                            format_html('<s>{}</s> {}', *v) if isinstance(v, tuple) else v,
+                        )
+                        for k, v in changes.items()
+                    )
+                )
+                + mark_safe('</tbody></table>'),
             )
         assembly.save()
 
@@ -382,11 +395,11 @@ class AssemblyEditChildrenView(AssemblyMixin, View):
         assembly = self.assembly
         # say 'Not Found' if assembly is not a cluster or clusters aren't supported at all
         if not assembly.is_cluster or not self.conference.support_clusters:
-            raise Http404()
+            raise Http404
 
         # bail out if the current user is not associated as a contact
         if not assembly.user_can_manage(self.request.user, staff_can_manage=True):
-            raise PermissionDenied()
+            raise PermissionDenied
 
         return assembly
 
@@ -445,9 +458,11 @@ class AssemblyEditChildrenView(AssemblyMixin, View):
         return redirect('backoffice:assembly-editchildren', pk=assembly.pk)
 
     def get(self, *args, **kwargs):
-        candidates_qs = Assembly.objects.accessible_by_user(conference=self.conference, user=self.request.user). \
-            filter(hierarchy=Assembly.Hierarchy.REGULAR, parent=None). \
-            exclude(state_assembly__in=[Assembly.State.NONE, Assembly.State.REJECTED, Assembly.State.HIDDEN, Assembly.State.PLANNED])
+        candidates_qs = (
+            Assembly.objects.accessible_by_user(conference=self.conference, user=self.request.user)
+            .filter(hierarchy=Assembly.Hierarchy.REGULAR, parent=None)
+            .exclude(state_assembly__in=[Assembly.State.NONE, Assembly.State.REJECTED, Assembly.State.HIDDEN, Assembly.State.PLANNED])
+        )
         candidates = list(candidates_qs.order_by('name'))
 
         context = self.get_context_data()
@@ -464,7 +479,7 @@ class AssemblyEditLinksView(AssemblyMixin, View):
 
         # bail out if the current user is not associated as a contact
         if not assembly.user_can_manage(self.request.user, staff_can_manage=True):
-            raise PermissionDenied()
+            raise PermissionDenied
 
         return assembly
 
@@ -510,17 +525,21 @@ class AssemblyEditLinksView(AssemblyMixin, View):
                     messages.success(request, gettext('assemblyedit_removedlink').format(linked_name=linkee.name))
                     logger.info(
                         'Assembly "%s" (%s): removed link to "%s" (%s) upon request by <%s>',
-                        assembly.slug, assembly.pk,
-                        linkee.slug, linkee.pk,
+                        assembly.slug,
+                        assembly.pk,
+                        linkee.slug,
+                        linkee.pk,
                         request.user.username,
                     )
 
         return redirect('backoffice:assembly-editlinks', pk=assembly.pk)
 
     def get(self, *args, **kwargs):
-        candidates_qs = Assembly.objects.accessible_by_user(conference=self.conference, user=self.request.user). \
-            filter(hierarchy=Assembly.Hierarchy.REGULAR). \
-            exclude(state_assembly__in=[Assembly.State.NONE, Assembly.State.PLANNED, Assembly.State.HIDDEN, Assembly.State.REJECTED])
+        candidates_qs = (
+            Assembly.objects.accessible_by_user(conference=self.conference, user=self.request.user)
+            .filter(hierarchy=Assembly.Hierarchy.REGULAR)
+            .exclude(state_assembly__in=[Assembly.State.NONE, Assembly.State.PLANNED, Assembly.State.HIDDEN, Assembly.State.REJECTED])
+        )
         candidates = list(candidates_qs.order_by('name'))
         context = self.get_context_data()
         context['candidates'] = candidates
@@ -627,9 +646,9 @@ class MembersView(AssemblyMixin, TemplateView):
     assembly_management = True
 
     def get_queryset(self):
-        return AssemblyMember.objects.manageable_by_user_for_assembly(user=self.request.user,
-                                                                      assembly=self.assembly
-                                                                      ).prefetch_related('member__communication_channels')
+        return AssemblyMember.objects.manageable_by_user_for_assembly(user=self.request.user, assembly=self.assembly).prefetch_related(
+            'member__communication_channels'
+        )
 
     def get_context_data(self, *args, **kwargs):
         ctx = super().get_context_data(*args, **kwargs)
@@ -638,11 +657,11 @@ class MembersView(AssemblyMixin, TemplateView):
         return ctx
 
     def post(self, *args, **kwargs):
-        for k in self.request.POST:
-            if '-' not in k:
+        for data_pair in self.request.POST:
+            if '-' not in data_pair:
                 continue
 
-            k, v = k.split('-')
+            k, v = data_pair.split('-')
 
             if k == 'hide':
                 m = self.get_queryset().select_related('member').get(member_id=int(v))
@@ -754,7 +773,7 @@ class MembersAddView(AssemblyMixin, FormView):
                     },
                 )
 
-            else:
+            else:  # noqa: PLR5501
                 if m.member == self.request.user and not self.staff_access:
                     messages.error(self.request, format_lazy(_('Assembly__members__cant_modify_self')))
                 else:
@@ -833,11 +852,14 @@ class AuthView(AssemblyMixin, FormView):
                 authorization_grant_type=data['grant_type'],
             )
             app.save()
-            messages.success(self.request, format_html(
-                '{msg}:<br><strong><code>{secret}</code></strong>',
-                msg=_('Application__newclientsecret'),
-                secret=app.client_secret,
-            ))
+            messages.success(
+                self.request,
+                format_html(
+                    '{msg}:<br><strong><code>{secret}</code></strong>',
+                    msg=_('Application__newclientsecret'),
+                    secret=app.client_secret,
+                ),
+            )
             logger.info(
                 'New OAuth2 app "%(app_name)s" created for assembly %(assembly)s by %(user)s',
                 {'app_name': app.nam, 'assembly': self.assembly, 'user': self.request.user},
@@ -885,7 +907,7 @@ class AuthAppView(AssemblyMixin, UpdateView):
     def get_object(self, *args, **kwargs):
         obj = super().get_object(*args, **kwargs)
         if obj.assembly_id != self.assembly.id:
-            raise Application.DoesNotExist()
+            raise Application.DoesNotExist
         return obj
 
     def form_valid(self, form):
@@ -921,21 +943,21 @@ class CreateRoomView(AssemblyMixin, FormView):
         if self.room_type == Room.RoomType.BIGBLUEBUTTON:
             if BigBlueButton is None or not BigBlueButton.can_create_for_assembly(self.assembly):
                 messages.error(self.request, 'BBB not available')
-                raise RoomNotAvailableError()
+                raise RoomNotAvailableError
 
             return AssemblyCreateRoomBigBlueButtonForm(self.request.POST, assembly=self.assembly)
 
         if self.room_type == Room.RoomType.WORKADVENTURE:
             if WorkAdventure is None or not WorkAdventure.can_create_for_assembly(self.assembly):
                 messages.error(self.request, 'WA not available')
-                raise RoomNotAvailableError()
+                raise RoomNotAvailableError
 
             return AssemblyCreateRoomWorkAdventureForm(self.request.POST, assembly=self.assembly)
 
         if self.room_type == Room.RoomType.HANGAR:
             if Hangar is None or not Hangar.can_create_for_assembly(self.assembly):
                 messages.error(self.request, 'Hangar not available')
-                raise RoomNotAvailableError()
+                raise RoomNotAvailableError
 
             return AssemblyCreateRoomHangarForm(self.request.POST, assembly=self.assembly)
 
@@ -945,7 +967,7 @@ class CreateRoomView(AssemblyMixin, FormView):
         else:
             logger.warning('Unexpected room_type "%s" upon creating new room for %s.', self.room_type, self.assembly)
             messages.warning(self.request, _('internal_error_please_retry'))
-            raise RoomNotAvailableError()
+            raise RoomNotAvailableError
 
     def get_context_data(self, *args, **kwargs):
         ctx = super().get_context_data(*args, **kwargs)
@@ -953,11 +975,13 @@ class CreateRoomView(AssemblyMixin, FormView):
 
         if not self.room_type:
             ctx['rooms_available'] = rooms_available = {k[0]: True for k in Room.RoomType.choices}
-            rooms_available.update({
-                'workadventure': WorkAdventure is not None and WorkAdventure.can_create_for_assembly(self.assembly),
-                'bbb': BigBlueButton is not None and BigBlueButton.can_create_for_assembly(self.assembly),
-                'hangar': Hangar is not None and Hangar.can_create_for_assembly(self.assembly),
-            })
+            rooms_available.update(
+                {
+                    'workadventure': WorkAdventure is not None and WorkAdventure.can_create_for_assembly(self.assembly),
+                    'bbb': BigBlueButton is not None and BigBlueButton.can_create_for_assembly(self.assembly),
+                    'hangar': Hangar is not None and Hangar.can_create_for_assembly(self.assembly),
+                }
+            )
             ctx['support_bbb'] = settings.INTEGRATIONS_BBB
             ctx['support_hangar'] = settings.INTEGRATIONS_HANGAR
             ctx['support_wa'] = settings.INTEGRATIONS_WORKADVENTURE
diff --git a/src/backoffice/views/assemblyteam.py b/src/backoffice/views/assemblyteam.py
index 503704a7f60531219e0e958a570d83f1973dca9f..55affa7ab941bfc833ca380d7dcfb1be007c5a87 100644
--- a/src/backoffice/views/assemblyteam.py
+++ b/src/backoffice/views/assemblyteam.py
@@ -1,27 +1,27 @@
 import csv
-from io import StringIO
 import json
 import logging
+from io import StringIO
 
 from django.contrib import messages
-from django.contrib.gis.geos import Point, MultiPolygon, Polygon
+from django.contrib.gis.geos import MultiPolygon, Point, Polygon
 from django.contrib.postgres.aggregates import StringAgg
-from django.db.models import Q, OuterRef, Subquery
-from django.http import HttpResponse, Http404
+from django.db.models import OuterRef, Q, Subquery
+from django.http import Http404, HttpResponse
+from django.shortcuts import redirect, render
 from django.urls import reverse
-from django.shortcuts import render, redirect
 from django.utils.html import format_html
-from django.utils.translation import gettext, gettext_lazy as _
-from django.views.generic import ListView, View, DetailView
+from django.utils.translation import gettext
+from django.utils.translation import gettext_lazy as _
+from django.views.generic import DetailView, ListView, View
 
 from core.models import Room
-from core.models.assemblies import Assembly, AssemblyMember, AssemblyLink
+from core.models.assemblies import Assembly, AssemblyLink, AssemblyMember
 from core.models.conference import ConferenceExportCache
 from core.models.users import UserCommunicationChannel
 
-from .mixins import ConferenceMixin, AssemblyMixin
 from ..templatetags.c3assemblies import get_language_item
-
+from .mixins import AssemblyMixin, ConferenceMixin
 
 logger = logging.getLogger(__name__)
 
@@ -32,7 +32,7 @@ class AssemblyTeamMixin(ConferenceMixin):
     active_page = 'assemblies'
     base_view_name = 'backoffice:assemblies'
     list_view_name = 'backoffice:assemblieslist'
-    sidebar_caption = _("nav_assemblies")
+    sidebar_caption = _('nav_assemblies')
     status_field = 'state_assembly'
 
     MODES = {
@@ -53,7 +53,6 @@ class AssemblyTeamMixin(ConferenceMixin):
         lists = []
         context['sidebar'] = {
             'title': self.sidebar_caption,
-            # 'title_link': reverse(self.base_view_name),
             'items': [
                 {
                     'caption': _('Assemblys'),
@@ -69,37 +68,47 @@ class AssemblyTeamMixin(ConferenceMixin):
         }
 
         for m, (q, t) in self.MODES.items():
-            assemblies.append({
-                'mode': m,
-                'caption': t,
-                'count': self.conference.assemblies.filter(q).count(),
-                'link': reverse(self.base_view_name) + '?mode=' + m,
-            })
-
-        lists.append({
-            'caption': 'slug, name, friends & WA',
-            'link': reverse(self.list_view_name, kwargs={'variant': 'slugname'}) + '?mode=accepted',
-            'variant': 'slugname',
-        })
-
-        lists.append({
-            'caption': 'contacts',
-            'link': reverse(self.list_view_name, kwargs={'variant': 'assemblycontacts'}) + '?mode=accepted',
-            'variant': 'assemblycontacts',
-        })
-
-        lists.append({
-            'caption': 'contact mails',
-            'link': reverse(self.list_view_name, kwargs={'variant': 'contactsmail'}) + '?mode=accepted',
-            'variant': 'contactsmail',
-        })
+            assemblies.append(
+                {
+                    'mode': m,
+                    'caption': t,
+                    'count': self.conference.assemblies.filter(q).count(),
+                    'link': reverse(self.base_view_name) + '?mode=' + m,
+                }
+            )
+
+        lists.append(
+            {
+                'caption': 'slug, name, friends & WA',
+                'link': reverse(self.list_view_name, kwargs={'variant': 'slugname'}) + '?mode=accepted',
+                'variant': 'slugname',
+            }
+        )
+
+        lists.append(
+            {
+                'caption': 'contacts',
+                'link': reverse(self.list_view_name, kwargs={'variant': 'assemblycontacts'}) + '?mode=accepted',
+                'variant': 'assemblycontacts',
+            }
+        )
+
+        lists.append(
+            {
+                'caption': 'contact mails',
+                'link': reverse(self.list_view_name, kwargs={'variant': 'contactsmail'}) + '?mode=accepted',
+                'variant': 'contactsmail',
+            }
+        )
 
         if self.conference.additional_fields_schema is not None:
-            lists.append({
-                'caption': 'registration',
-                'link': reverse(self.list_view_name, kwargs={'variant': 'registration'}) + '?mode=accepted',
-                'variant': 'registration',
-            })
+            lists.append(
+                {
+                    'caption': 'registration',
+                    'link': reverse(self.list_view_name, kwargs={'variant': 'registration'}) + '?mode=accepted',
+                    'variant': 'registration',
+                }
+            )
 
         return context
 
@@ -147,9 +156,7 @@ def build_nav_from_assembly(assembly):
         }
 
         if assmbly.is_cluster:
-            me['children'] = [
-                _build_nav_from_assembly(a, way_up=False) for a in assmbly.children.order_by('slug').all()
-            ]
+            me['children'] = [_build_nav_from_assembly(a, way_up=False) for a in assmbly.children.order_by('slug').all()]
             me['expanded'] = True
 
         return [me] if way_up else me
@@ -252,43 +259,54 @@ class AssembliesListsView(AssembliesListMixin, View):
         variant_fields = None
 
         if variant == 'slugname':
+
             def wa_room_status(a):
                 try:
                     r = a.rooms.get(room_type=Room.RoomType.WORKADVENTURE)
-                    return "x" if not r.blocked else "b"
+                    return 'x' if not r.blocked else 'b'
                 except Room.DoesNotExist:
-                    return ""
+                    return ''
                 except Room.MultipleObjectsReturned:
-                    return "+"
+                    return '+'
 
             # all assemblies with slug + name + related assemblies
             qs = tuple(
-                (a.slug, a.name, a.parent, ", ".join(link.b.slug for link in AssemblyLink.objects.filter(a=a)), wa_room_status(a))
-                for a in self.get_queryset()
+                (a.slug, a.name, a.parent, ', '.join(link.b.slug for link in AssemblyLink.objects.filter(a=a)), wa_room_status(a)) for a in self.get_queryset()
             )
             variant_name = 'slug, name, parent, link, wa'
             variant_fields = [_('Assembly__slug'), _('Assembly__name'), _('Assembly__parent'), _('assembly_links'), _('Room__type-workadventure')]
 
         elif variant == 'contactsmail':
             # all assembly contacts' email addresses with duplicates removed
-            qs = UserCommunicationChannel.objects.filter(
-                channel=UserCommunicationChannel.Channel.MAIL,
-                is_verified=True,
-                user_id__in=AssemblyMember.objects.filter(assembly__in=self.get_queryset(), can_manage_assembly=True).values('member_id'),
-            ).values('address').distinct().order_by('address')
+            qs = (
+                UserCommunicationChannel.objects.filter(
+                    channel=UserCommunicationChannel.Channel.MAIL,
+                    is_verified=True,
+                    user_id__in=AssemblyMember.objects.filter(assembly__in=self.get_queryset(), can_manage_assembly=True).values('member_id'),
+                )
+                .values('address')
+                .distinct()
+                .order_by('address')
+            )
 
             variant_name = 'assembly contacts emails'
             variant_fields = [_('UserCommunicationChannel__address')]
 
         elif variant == 'assemblycontacts':
             # all assemblies with their associated contacts
-            user_mails = Subquery(UserCommunicationChannel.objects.filter(
-                user_id=OuterRef('member_id'), is_verified=True, channel=UserCommunicationChannel.Channel.MAIL,
-            ).values('address'))
-            qs = AssemblyMember.objects.filter(assembly__in=self.get_queryset(), can_manage_assembly=True) \
-                .annotate(mail=StringAgg(user_mails, '; ')) \
-                .values_list('assembly__slug', 'is_representative', 'mail') \
+            user_mails = Subquery(
+                UserCommunicationChannel.objects.filter(
+                    user_id=OuterRef('member_id'),
+                    is_verified=True,
+                    channel=UserCommunicationChannel.Channel.MAIL,
+                ).values('address')
+            )
+            qs = (
+                AssemblyMember.objects.filter(assembly__in=self.get_queryset(), can_manage_assembly=True)
+                .annotate(mail=StringAgg(user_mails, '; '))
+                .values_list('assembly__slug', 'is_representative', 'mail')
                 .order_by('assembly__slug', 'mail')
+            )
 
             variant_name = 'contact emails (assembly managers)'
             variant_fields = [_('Assembly__slug'), _('AssemblyMember__is_representative'), _('UserCommunicationChannel__address')]
@@ -431,7 +449,7 @@ class AssemblyEditHierarchyView(SingleAssemblyTeamMixin, View):
 
         # say 'Not Found' if clusters aren't supported at all
         if not self.conference.support_clusters:
-            raise Http404()
+            raise Http404
 
         return assembly
 
@@ -448,8 +466,7 @@ class AssemblyEditHierarchyView(SingleAssemblyTeamMixin, View):
         comment = request.POST.get('comment', '').strip()
 
         # don't allow changing cluster to regular if it has children
-        if value == Assembly.Hierarchy.REGULAR and \
-           assembly.is_cluster and assembly.children.exists():
+        if value == Assembly.Hierarchy.REGULAR and assembly.is_cluster and assembly.children.exists():
             messages.error(request, gettext('assemblyedit_clusterstillhaschildren'))
             return redirect(reverse('backoffice:assemblyteam-edithierarchy', kwargs={'pk': assembly.pk}) + '?value=' + value)
 
diff --git a/src/backoffice/views/auth.py b/src/backoffice/views/auth.py
index 3c5c283a150535c6759d74f3931b8aac34f319bc..e453fbed6fcd7f351f7658ae792f75c1c6e78f25 100644
--- a/src/backoffice/views/auth.py
+++ b/src/backoffice/views/auth.py
@@ -5,7 +5,7 @@ from django.http import JsonResponse
 from django.urls import reverse_lazy
 from django.views.generic import TemplateView, View
 
-from core.views import BaseLoginView, BaseRegistrationActivationView, BaseRegistrationView, BasePasswordResetView, BasePasswordResetConfirmView
+from core.views import BaseLoginView, BasePasswordResetConfirmView, BasePasswordResetView, BaseRegistrationActivationView, BaseRegistrationView
 
 from .mixins import ConferenceMixin, PasswordMixin
 
diff --git a/src/backoffice/views/badges.py b/src/backoffice/views/badges.py
index 51a4f4e833b32aad8254499e353df5d90a7962a3..a2121e519b1a21fb22f1f1a2d7230def1223bd4e 100644
--- a/src/backoffice/views/badges.py
+++ b/src/backoffice/views/badges.py
@@ -90,7 +90,7 @@ class RemoveBadgeView(AssemblyMixin, DeleteView):
     def get_object(self, *args, **kwargs):
         obj = super().get_object(*args, **kwargs)
         if obj.issuing_assembly != self.assembly:
-            raise self.model.DoesNotExist()
+            raise self.model.DoesNotExist
         return obj
 
     def delete(self, *args, **kwargs):
diff --git a/src/backoffice/views/channelteam.py b/src/backoffice/views/channelteam.py
index 1d352b968012d6406d57d605997ed90dcc16be2e..bee9d64cf481a98404e49e4a9c8f07574b9b49d5 100644
--- a/src/backoffice/views/channelteam.py
+++ b/src/backoffice/views/channelteam.py
@@ -1,13 +1,14 @@
-from django.utils.translation import gettext_lazy as _
 from django.db.models import Q
+from django.utils.translation import gettext_lazy as _
 
 from core.models.assemblies import Assembly
 
-from .assemblyteam import AssembliesView, AssembliesListsView
+from .assemblyteam import AssembliesListsView, AssembliesView
 
 
 class ChannelsMixin:
-    """ sets options that configure the Assemblies views to work in Channels mode """
+    """sets options that configure the Assemblies views to work in Channels mode"""
+
     MODES = {
         'all': (Q(), _('nav_channels_all')),
         'accepted': (Q(state_channel__in=Assembly.PUBLIC_STATES), _('nav_channels_accepted')),
@@ -19,7 +20,7 @@ class ChannelsMixin:
     active_page = 'channels'
     base_view_name = 'backoffice:channels'
     list_view_name = 'backoffice:channelslist'
-    sidebar_caption = _("nav_channels")
+    sidebar_caption = _('nav_channels')
     status_field = 'state_channel'
 
 
diff --git a/src/backoffice/views/events.py b/src/backoffice/views/events.py
index b3cf344ec8733ba6737d5e52491380905c39e661..8a0a693d089362a1d1d1af237ffc1954c8e40725 100644
--- a/src/backoffice/views/events.py
+++ b/src/backoffice/views/events.py
@@ -8,12 +8,13 @@ from django.utils.translation import gettext_lazy as _
 from django.views.generic import ListView
 from django.views.generic.edit import CreateView, DeleteView, ModelFormMixin, UpdateView
 
-from backoffice.forms import EventForm
 from core.models.assemblies import Assembly
 from core.models.conference import ConferenceExportCache
 from core.models.events import Event
 from core.models.rooms import Room
 
+from backoffice.forms import EventForm
+
 from .mixins import AssemblyMixin, ConferenceMixin
 
 logger = logging.getLogger(__name__)
@@ -46,8 +47,7 @@ class EventFormMixin(ModelFormMixin):
         result = super().form_valid(form)
         if form.publish:
             messages.success(
-                self.request,
-                _('Event__published %(event_id)s %(event_type)s') % {'event_id': form.instance.id, 'event_type': self.event_type_name}
+                self.request, _('Event__published %(event_id)s %(event_type)s') % {'event_id': form.instance.id, 'event_type': self.event_type_name}
             )
         elif form.create:
             messages.success(self.request, _('Event__created %(event_id)s %(event_type)s') % {'event_id': form.instance.id, 'event_type': self.event_type_name})
@@ -66,9 +66,7 @@ class EventPublicationMixin(ModelFormMixin):
         try:
             self.get_object().clean(True)
         except ValidationError as error:
-            context.update({
-                'publication_errors': error.message_dict
-            })
+            context.update({'publication_errors': error.message_dict})
         return context
 
     def get_form_kwargs(self, *args, **kwargs):
@@ -146,7 +144,7 @@ class AssemblyRemoveEventView(AssemblyMixin, DeleteView):
     def get_object(self, *args, **kwargs):
         obj = super().get_object(*args, **kwargs)
         if obj.assembly != self.assembly:
-            raise self.model.DoesNotExist()
+            raise self.model.DoesNotExist
         return obj
 
     def form_valid(self, *args, **kwargs):
@@ -171,14 +169,16 @@ class SoSIndexView(ConferenceMixin, ListView):
         }
 
     def get_queryset(self, *args, **kwargs):
-        return Event.objects.manageable_by_user(user=self.request.user, conference=self.conference) \
-            .filter(kind=Event.Kind.SELF_ORGANIZED).select_related('owner')
+        return (
+            Event.objects.manageable_by_user(user=self.request.user, conference=self.conference).filter(kind=Event.Kind.SELF_ORGANIZED).select_related('owner')
+        )
 
 
 class SosDeleteView(ConferenceMixin, DeleteView):
     def get_queryset(self, *args, **kwargs):
-        return Event.objects.manageable_by_user(user=self.request.user, conference=self.conference) \
-            .filter(kind=Event.Kind.SELF_ORGANIZED).select_related('owner')
+        return (
+            Event.objects.manageable_by_user(user=self.request.user, conference=self.conference).filter(kind=Event.Kind.SELF_ORGANIZED).select_related('owner')
+        )
 
     def delete(self, *args, **kwargs):
         result = super().delete(*args, **kwargs)
diff --git a/src/backoffice/views/map.py b/src/backoffice/views/map.py
index f3c0ce91ad21f50edac0d6d8bc5e9e173593e119..8ac2ed094984a7a9c2048a87e015a74d3108544b 100644
--- a/src/backoffice/views/map.py
+++ b/src/backoffice/views/map.py
@@ -6,11 +6,12 @@ from django.utils.html import format_html
 from django.utils.translation import gettext_lazy as _
 from django.views.generic import ListView
 from django.views.generic.detail import SingleObjectTemplateResponseMixin
-from django.views.generic.edit import UpdateView, CreateView, ModelFormMixin
+from django.views.generic.edit import CreateView, ModelFormMixin, UpdateView
 
 from core.models.map import MapFloor, MapPOI
-from .mixins import ConferenceMixin, guess_active_sidebar_item
+
 from ..forms import POIForm
+from .mixins import ConferenceMixin, guess_active_sidebar_item
 
 logger = logging.getLogger(__name__)
 
@@ -24,16 +25,18 @@ class MapAdminMixin(ConferenceMixin):
         ctx['active_page'] = 'map'
         ctx['uses_map'] = True
 
-        floors = [{
-            'caption': f'{floor["name"]} ({floor["index"]})',
-            'link': reverse('backoffice:map-floor-edit', kwargs={'pk': floor["pk"]}),
-        } for floor in MapFloor.objects.filter(conference=self.conference).order_by('index').values('pk', 'name', 'index')]
+        floors = [
+            {
+                'caption': f'{floor["name"]} ({floor["index"]})',
+                'link': reverse('backoffice:map-floor-edit', kwargs={'pk': floor['pk']}),
+            }
+            for floor in MapFloor.objects.filter(conference=self.conference).order_by('index').values('pk', 'name', 'index')
+        ]
 
         pois = []
         poi_count = 0
         ctx['sidebar'] = {
             'title': _('nav_map'),
-            # 'title_link': reverse(self.base_view_name),
             'items': [
                 {
                     'caption': _('MapFloors'),
@@ -51,15 +54,20 @@ class MapAdminMixin(ConferenceMixin):
 
         for poi in MapPOI.objects.filter(conference=self.conference).values('id', 'visible', 'name').iterator():
             poi_count += 1
-            pois.append({
-                'caption': poi['name'] if poi['visible'] else format_html('<s>{}</s>', poi['name']),
-                'link': reverse('backoffice:map-poi-edit', kwargs={'pk': poi['id']}),
-            })
-        pois.insert(0, {
-            'caption': format_html('<i>({all})</i>', all=_('all')),
-            'link': reverse('backoffice:map-poi-list'),
-            'count': poi_count,
-        })
+            pois.append(
+                {
+                    'caption': poi['name'] if poi['visible'] else format_html('<s>{}</s>', poi['name']),
+                    'link': reverse('backoffice:map-poi-edit', kwargs={'pk': poi['id']}),
+                }
+            )
+        pois.insert(
+            0,
+            {
+                'caption': format_html('<i>({all})</i>', all=_('all')),
+                'link': reverse('backoffice:map-poi-list'),
+                'count': poi_count,
+            },
+        )
 
         # try to guess 'active' sidebar item
         guess_active_sidebar_item(self.request, ctx['sidebar']['items'], with_query_string=False)
diff --git a/src/backoffice/views/misc.py b/src/backoffice/views/misc.py
index 788bf322e5a05070c88eebce9132aaa60658e30e..bfdc982f045a5459402a6abdd3795c61c68cfb15 100644
--- a/src/backoffice/views/misc.py
+++ b/src/backoffice/views/misc.py
@@ -1,6 +1,6 @@
+from django.shortcuts import redirect, render
 from django.views.generic import View
 from django.views.generic.edit import FormView
-from django.shortcuts import redirect, render
 
 from core.models import Assembly, Conference
 
@@ -35,14 +35,16 @@ class IndexView(ConferenceMixin, View):
             myassemblies = None
 
         ctx = self.get_context_data()
-        ctx.update({
-            'active_page': 'home',
-            'myassemblies': myassemblies,
-        })
+        ctx.update(
+            {
+                'active_page': 'home',
+                'myassemblies': myassemblies,
+            }
+        )
 
         return render(self.request, 'backoffice/index.html', ctx)
 
 
 class BoomView(View):
     def get(self, *args, **kwargs):
-        raise Exception("Bazinga! Testing the error handling, are we?")
+        raise Exception('Bazinga! Testing the error handling, are we?')
diff --git a/src/backoffice/views/mixins.py b/src/backoffice/views/mixins.py
index c3b37a8fe52a30652e8531b8438ef6e502ee866a..7151e278b49f0aec67b6d7ff9a677a99f8004af3 100644
--- a/src/backoffice/views/mixins.py
+++ b/src/backoffice/views/mixins.py
@@ -3,7 +3,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix
 from django.core.exceptions import PermissionDenied
 from django.http import HttpRequest
 from django.shortcuts import redirect
-from django.urls import reverse_lazy, reverse
+from django.urls import reverse, reverse_lazy
 from django.utils.translation import gettext_lazy as _
 
 from core.models.assemblies import Assembly
@@ -12,7 +12,6 @@ from core.models.conference import Conference, ConferenceMember
 from core.models.rooms import Room
 from core.models.sso import Application
 
-
 _UNSET = object()
 
 
@@ -66,9 +65,11 @@ class ConferenceMixin(LoginRequiredMixin, PermissionRequiredMixin):
 
     @property
     def is_channel_team(self):
-        return self.conference.support_channels and \
-               self.request.user.is_authenticated and \
-               self.request.user.has_conference_staffpermission(self.conference, 'core.channel_team')
+        return (
+            self.conference.support_channels
+            and self.request.user.is_authenticated
+            and self.request.user.has_conference_staffpermission(self.conference, 'core.channel_team')
+        )
 
     def dispatch(self, request, *args, **kwargs):
         if self.require_conference and self.conference is None:
@@ -88,35 +89,41 @@ class ConferenceMixin(LoginRequiredMixin, PermissionRequiredMixin):
 
         conference_list = Conference.objects.accessible_by_user(self.request.user).all()
 
-        context.update({
-            'LANGUAGES': settings.LANGUAGES,
-            'conference': self.conference,
-            'conferencemember': self.conferencemember,
-            'conferences': conference_list,
-        })
+        context.update(
+            {
+                'LANGUAGES': settings.LANGUAGES,
+                'conference': self.conference,
+                'conferencemember': self.conferencemember,
+                'conferences': conference_list,
+            }
+        )
 
         if self.request.user.is_authenticated:
-            context.update({
-                'has_sos': self.conferencemember is not None,
-                'has_assemblies': self.is_assembly_team,
-                'has_channel': self.is_channel_team,
-                'has_pages': self.request.user.has_conference_staffpermission(self.conference, 'core.static_pages'),
-                'has_map': self.request.user.has_conference_staffpermission(self.conference, 'core.map_edit'),
-                'has_users': self.request.user.has_conference_staffpermission(self.conference, 'core.platformusers'),
-                'has_schedules': self.request.user.has_conference_staffpermission(self.conference, 'core.scheduleadmin'),
-                'has_workadventure':
-                    settings.INTEGRATIONS_WORKADVENTURE and self.request.user.has_conference_staffpermission(self.conference, 'core.workadventure_admin'),
-            })
+            context.update(
+                {
+                    'has_sos': self.conferencemember is not None,
+                    'has_assemblies': self.is_assembly_team,
+                    'has_channel': self.is_channel_team,
+                    'has_pages': self.request.user.has_conference_staffpermission(self.conference, 'core.static_pages'),
+                    'has_map': self.request.user.has_conference_staffpermission(self.conference, 'core.map_edit'),
+                    'has_users': self.request.user.has_conference_staffpermission(self.conference, 'core.platformusers'),
+                    'has_schedules': self.request.user.has_conference_staffpermission(self.conference, 'core.scheduleadmin'),
+                    'has_workadventure': settings.INTEGRATIONS_WORKADVENTURE
+                    and self.request.user.has_conference_staffpermission(self.conference, 'core.workadventure_admin'),
+                }
+            )
         else:
-            context.update({
-                'has_assemblies': False,
-                'has_channel': False,
-                'has_pages': False,
-                'has_map': False,
-                'has_users': False,
-                'has_schedules': False,
-                'has_workadventure': False,
-            })
+            context.update(
+                {
+                    'has_assemblies': False,
+                    'has_channel': False,
+                    'has_pages': False,
+                    'has_map': False,
+                    'has_users': False,
+                    'has_schedules': False,
+                    'has_workadventure': False,
+                }
+            )
 
         return context
 
@@ -172,14 +179,16 @@ class AssemblyMixin(ConferenceMixin):
         if assembly.has_user(self.request.user):
             # don't set self._staff_mode = False here as this would prevent assembly team members to edit their own assemblies
 
-            if not self._staff_access and \
-               assembly.state_assembly in [Assembly.State.NONE, Assembly.State.HIDDEN] and \
-               assembly.state_channel in [Assembly.State.NONE, Assembly.State.HIDDEN]:
-                raise Assembly.DoesNotExist()
+            if (
+                not self._staff_access
+                and assembly.state_assembly in [Assembly.State.NONE, Assembly.State.HIDDEN]
+                and assembly.state_channel in [Assembly.State.NONE, Assembly.State.HIDDEN]
+            ):
+                raise Assembly.DoesNotExist
 
         # neither owner/manager nor assembly team? go away
         elif not self._assembly_staff_access and not self._channels_staff_access:
-            raise PermissionDenied()
+            raise PermissionDenied
 
         self._assembly = assembly
         return assembly
@@ -248,97 +257,132 @@ class AssemblyMixin(ConferenceMixin):
 
         organisation = []
         sidebar.append({'caption': _('backoffice:assembly-organisational-data'), 'children': organisation})
-        organisation.append({
-            'caption': _('backoffice:assembly-basic-data'),
-            'link': reverse('backoffice:assembly-edit', kwargs={'pk': assembly.id}),
-        })
+        organisation.append(
+            {
+                'caption': _('backoffice:assembly-basic-data'),
+                'link': reverse('backoffice:assembly-edit', kwargs={'pk': assembly.id}),
+            }
+        )
 
         if assembly.is_cluster:
-            organisation.append({
-                'caption': 'Sub-Assemblies',
-                'link': reverse('backoffice:assembly-editchildren', kwargs={'pk': assembly.id}),
-            })
-
-        organisation.append({
-            'caption': 'Links',
-            'link': reverse('backoffice:assembly-editlinks', kwargs={'pk': assembly.id}),
-        })
-
-        organisation.append({
-            'caption': _('Assembly__members'),
-            'link': reverse('backoffice:assembly-members', kwargs={'pk': assembly.id}),
-            'count': assembly.members.count(),
-        })
+            organisation.append(
+                {
+                    'caption': 'Sub-Assemblies',
+                    'link': reverse('backoffice:assembly-editchildren', kwargs={'pk': assembly.id}),
+                }
+            )
+
+        organisation.append(
+            {
+                'caption': 'Links',
+                'link': reverse('backoffice:assembly-editlinks', kwargs={'pk': assembly.id}),
+            }
+        )
+
+        organisation.append(
+            {
+                'caption': _('Assembly__members'),
+                'link': reverse('backoffice:assembly-members', kwargs={'pk': assembly.id}),
+                'count': assembly.members.count(),
+            }
+        )
 
         if can_manage:
-            apps = [{
-                'caption': f'{app["name"]}',
-                'link': reverse('backoffice:assembly-auth-app', kwargs={'assembly': assembly.id, 'pk': app['id']}),
-                'classes': [],
-            } for app in Application.objects.filter(assembly=self.assembly).values('id', 'name')]
-            sidebar.append({
-                'caption': _('Assembly__authentication'),
-                'children': apps,
-                'count': len(apps),
-                'link': reverse('backoffice:assembly-auth', kwargs={'assembly': assembly.id}),
-            })
+            apps = [
+                {
+                    'caption': f'{app["name"]}',
+                    'link': reverse('backoffice:assembly-auth-app', kwargs={'assembly': assembly.id, 'pk': app['id']}),
+                    'classes': [],
+                }
+                for app in Application.objects.filter(assembly=self.assembly).values('id', 'name')
+            ]
+            sidebar.append(
+                {
+                    'caption': _('Assembly__authentication'),
+                    'children': apps,
+                    'count': len(apps),
+                    'link': reverse('backoffice:assembly-auth', kwargs={'assembly': assembly.id}),
+                }
+            )
 
             if (voucher_count := assembly.get_voucher_count(with_always_public=True)) is not None:
-                organisation.append({
-                    'caption': _('Vouchers'),
-                    'count': voucher_count,
-                    'link': reverse('backoffice:assembly-vouchers', kwargs={'pk': assembly.id}),
-                })
-
-        rooms = [{
-            'caption': f'{room["name"]} ({room["room_type"]})',
-            'link': reverse('backoffice:assembly-room', kwargs={'assembly': assembly.id, 'pk': room['id']}),
-            'classes': ['blocked'] if room['blocked'] else [],
-        } for room in assembly.rooms.exclude(room_type=Room.RoomType.PROJECT).values('id', 'name', 'room_type', 'blocked')]
-        sidebar.append({
-            'caption': _('backoffice:assembly-rooms'),
-            'children': rooms,
-            'count': len(rooms),
-            'add_link': reverse('backoffice:assembly-create-room', kwargs={'assembly': assembly.id}) if can_manage else None,
-        })
-
-        projects = [{
-            'caption': prj["name"],
-            'link': reverse('backoffice:assembly-room', kwargs={'assembly': assembly.id, 'pk': prj['id']}),
-            'classes': ['blocked'] if prj['blocked'] else [],
-        } for prj in assembly.rooms.filter(room_type=Room.RoomType.PROJECT).values('id', 'name', 'blocked')]
-        sidebar.append({
-            'caption': _('backoffice:assembly-projects'),
-            'children': projects,
-            'count': len(projects),
-            'add_link': reverse('backoffice:assembly-create-project', kwargs={'assembly': assembly.id}) if can_manage else None,
-        })
-
-        events = [{
-            'caption': ev["name"],
-            'link': reverse('backoffice:assembly-event', kwargs={'assembly': assembly.id, 'pk': ev['id']}),
-            'classes': ['blocked'] if not ev['is_public'] else [],
-        } for ev in assembly.events.values('id', 'name', 'is_public')]
-        sidebar.append({
-            'caption': 'Events',
-            'link': reverse('backoffice:assembly-events', kwargs={'assembly': assembly.id}),
-            'children': events,
-            'count': len(events),
-            'add_link': reverse('backoffice:assembly-create-event', kwargs={'assembly': assembly.id}) if can_manage else None,
-        })
-
-        badges = [{
-            'caption': b["name"],
-            'link': reverse('backoffice:assembly-badge', kwargs={'assembly': assembly.id, 'pk': b['id']}),
-            'classes': ['blocked'] if b['state'] == Badge.State.PLANNED else [],
-        } for b in assembly.badges.values('id', 'name', 'state')]
-        sidebar.append({
-            'caption': 'Badges',
-            'link': reverse('backoffice:assembly-badges', kwargs={'assembly': assembly.id}),
-            'children': badges,
-            'count': len(badges),
-            'add_link': reverse('backoffice:assembly-create-badge', kwargs={'assembly': assembly.id}) if can_manage else None,
-        })
+                organisation.append(
+                    {
+                        'caption': _('Vouchers'),
+                        'count': voucher_count,
+                        'link': reverse('backoffice:assembly-vouchers', kwargs={'pk': assembly.id}),
+                    }
+                )
+
+        rooms = [
+            {
+                'caption': f'{room["name"]} ({room["room_type"]})',
+                'link': reverse('backoffice:assembly-room', kwargs={'assembly': assembly.id, 'pk': room['id']}),
+                'classes': ['blocked'] if room['blocked'] else [],
+            }
+            for room in assembly.rooms.exclude(room_type=Room.RoomType.PROJECT).values('id', 'name', 'room_type', 'blocked')
+        ]
+        sidebar.append(
+            {
+                'caption': _('backoffice:assembly-rooms'),
+                'children': rooms,
+                'count': len(rooms),
+                'add_link': reverse('backoffice:assembly-create-room', kwargs={'assembly': assembly.id}) if can_manage else None,
+            }
+        )
+
+        projects = [
+            {
+                'caption': prj['name'],
+                'link': reverse('backoffice:assembly-room', kwargs={'assembly': assembly.id, 'pk': prj['id']}),
+                'classes': ['blocked'] if prj['blocked'] else [],
+            }
+            for prj in assembly.rooms.filter(room_type=Room.RoomType.PROJECT).values('id', 'name', 'blocked')
+        ]
+        sidebar.append(
+            {
+                'caption': _('backoffice:assembly-projects'),
+                'children': projects,
+                'count': len(projects),
+                'add_link': reverse('backoffice:assembly-create-project', kwargs={'assembly': assembly.id}) if can_manage else None,
+            }
+        )
+
+        events = [
+            {
+                'caption': ev['name'],
+                'link': reverse('backoffice:assembly-event', kwargs={'assembly': assembly.id, 'pk': ev['id']}),
+                'classes': ['blocked'] if not ev['is_public'] else [],
+            }
+            for ev in assembly.events.values('id', 'name', 'is_public')
+        ]
+        sidebar.append(
+            {
+                'caption': 'Events',
+                'link': reverse('backoffice:assembly-events', kwargs={'assembly': assembly.id}),
+                'children': events,
+                'count': len(events),
+                'add_link': reverse('backoffice:assembly-create-event', kwargs={'assembly': assembly.id}) if can_manage else None,
+            }
+        )
+
+        badges = [
+            {
+                'caption': b['name'],
+                'link': reverse('backoffice:assembly-badge', kwargs={'assembly': assembly.id, 'pk': b['id']}),
+                'classes': ['blocked'] if b['state'] == Badge.State.PLANNED else [],
+            }
+            for b in assembly.badges.values('id', 'name', 'state')
+        ]
+        sidebar.append(
+            {
+                'caption': 'Badges',
+                'link': reverse('backoffice:assembly-badges', kwargs={'assembly': assembly.id}),
+                'children': badges,
+                'count': len(badges),
+                'add_link': reverse('backoffice:assembly-create-badge', kwargs={'assembly': assembly.id}) if can_manage else None,
+            }
+        )
 
         # try to guess 'active' sidebar item
         guess_active_sidebar_item(self.request, context['sidebar']['items'])
@@ -372,7 +416,7 @@ def guess_active_sidebar_item(request: HttpRequest, sidebar_items: dict, with_qu
             x['children'] = None
 
 
-class PasswordMixin():
+class PasswordMixin:
     def get_context_data(self, *args, **kwargs):
         try:
             context = super().get_context_data(*args, **kwargs)
@@ -380,8 +424,10 @@ class PasswordMixin():
             # super() does not have .get_context_data(), e.g. if it's a plain View
             context = {}
 
-        context.update({
-            'LANGUAGES': settings.LANGUAGES,
-        })
+        context.update(
+            {
+                'LANGUAGES': settings.LANGUAGES,
+            }
+        )
 
         return context
diff --git a/src/backoffice/views/schedules.py b/src/backoffice/views/schedules.py
index f000fc1d3927aa57bc36510da9d56a3e4137365c..ed92c9481a39d94394d53062929d8b412c1dbeb0 100644
--- a/src/backoffice/views/schedules.py
+++ b/src/backoffice/views/schedules.py
@@ -2,7 +2,7 @@ from django.contrib import messages
 from django.shortcuts import redirect
 from django.urls import reverse
 from django.views import View
-from django.views.generic import CreateView, DeleteView, DetailView, ListView, UpdateView, TemplateView
+from django.views.generic import CreateView, DeleteView, DetailView, ListView, TemplateView, UpdateView
 from django.views.generic.detail import SingleObjectMixin
 
 from core.models import ScheduleSource, ScheduleSourceImport
@@ -62,16 +62,16 @@ class ScheduleSourcesDoImportView(ScheduleAdminMixin, SingleObjectMixin, View):
         if src.assembly is not None:
             messages.info(request, f"+ ScheduleSourceImport {job.pk} for '{src.assembly.slug}' ({src.pk})")
         else:
-            messages.info(request, f"+ ScheduleSourceImport {job.pk} for wildcard assembly ({src.pk})")
+            messages.info(request, f'+ ScheduleSourceImport {job.pk} for wildcard assembly ({src.pk})')
 
         try:
             result = job.do_import()
             if result:
-                messages.success(request, f"ScheduleSourceImport {job.pk} succeeded")
+                messages.success(request, f'ScheduleSourceImport {job.pk} succeeded')
             else:
-                messages.warning(request, f"ScheduleSourceImport {job.pk} failed")
+                messages.warning(request, f'ScheduleSourceImport {job.pk} failed')
         except Exception as err:
-            messages.error(request, f"ScheduleSourceImport {job.pk} threw exception: {err}")
+            messages.error(request, f'ScheduleSourceImport {job.pk} threw exception: {err}')
 
         return redirect('backoffice:schedulesourceimport-detail', pk=str(job.pk))
 
diff --git a/src/backoffice/views/users.py b/src/backoffice/views/users.py
index c0423c445b466686e6929d220bed1dc6d318efd3..02e6976dd0e972288587ba12394314972fa5e5ab 100644
--- a/src/backoffice/views/users.py
+++ b/src/backoffice/views/users.py
@@ -1,31 +1,29 @@
 import logging
 
+from oauth2_provider.models import AccessToken
+
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.sessions.exceptions import SuspiciousSession
 from django.contrib.sessions.models import Session
-from django.core.mail import EmailMessage
 from django.core.exceptions import ObjectDoesNotExist
+from django.core.mail import EmailMessage
 from django.db.models import F
 from django.shortcuts import redirect, render
 from django.urls import reverse
 from django.utils.translation import gettext
 from django.views import View
-from django.views.generic import DetailView, TemplateView, ListView
+from django.views.generic import DetailView, ListView, TemplateView
 from django.views.generic.detail import SingleObjectMixin
 
-from oauth2_provider.models import AccessToken
-
 from core.integrations import WorkAdventureIntegration
 from core.models import BulletinBoardEntry
 from core.models.conference import ConferenceMember
 from core.models.messages import DirectMessage
 from core.models.users import PlatformUser, UserCommunicationChannel
 
-
 from .mixins import ConferenceMixin
 
-
 logger = logging.getLogger(__name__)
 MAX_ROWS = 42
 
@@ -52,13 +50,14 @@ class UsersView(ConferenceMixin, TemplateView):
             qs = qs.filter(conferences__conference=self.conference)
         qs_base = qs.annotate(is_conference_staff=F('conferences__is_staff'), active_conference_angel=F('conferences__active_angel'))
         if '@' in search_term:
-            direct_matches += list(qs.filter(communication_channels__channel=UserCommunicationChannel.Channel.MAIL,
-                                             communication_channels__address=search_term))
+            direct_matches += list(
+                qs.filter(communication_channels__channel=UserCommunicationChannel.Channel.MAIL, communication_channels__address=search_term)
+            )
         direct_matches += list(qs.filter(username__iexact=search_term))
 
         qs = qs_base.filter(username__icontains=search_term).exclude(pk__in=[direct_match.pk for direct_match in direct_matches])
         qs = qs.order_by('username')
-        results = list(direct_matches) + list(qs[:MAX_ROWS + 1])
+        results = list(direct_matches) + list(qs[: MAX_ROWS + 1])
         more_results = len(results) > MAX_ROWS
 
         if more_results:
@@ -242,7 +241,7 @@ class UserRenameView(ConferenceMixin, DetailView):
         user = self.get_object()
 
         new_name = self.request.POST.get('new_name', '').strip()
-        if new_name == '' or new_name == user.username:
+        if new_name in ('', user.username):
             return self.get(*args, **kwargs)
 
         if PlatformUser.objects.filter(username=new_name):
@@ -323,7 +322,7 @@ class UserMailView(ConferenceMixin, DetailView):
             messages.info(self.request, f'Sent mail to "{user.username}" with subject "{subject}" ({len(addresses)} recipients(s)).')
 
         except ObjectDoesNotExist:
-            messages.error(self.request, "Mail not send because channel not marked to be used for notifications or not verified!")
+            messages.error(self.request, 'Mail not send because channel not marked to be used for notifications or not verified!')
 
         except Exception as e:
             messages.error(self.request, e)
@@ -355,5 +354,5 @@ class UserBoardEntriesHide(ConferenceMixin, View):
     def post(self, request, user_id, **kwargs):
         BulletinBoardEntry.objects.filter(owner=user_id, pk=request.POST['pk']).update(hidden=True)
 
-        messages.success(request, gettext("BulletinBoardEntry--deleted"))
+        messages.success(request, gettext('BulletinBoardEntry--deleted'))
         return redirect(reverse('backoffice:user-board', kwargs={'user_id': user_id}))
diff --git a/src/backoffice/views/utils.py b/src/backoffice/views/utils.py
index af7afe2fb805f94ea70e05874ecf5993c1e40a54..08b16493d43678792b42221946f9e01cca4d560e 100644
--- a/src/backoffice/views/utils.py
+++ b/src/backoffice/views/utils.py
@@ -25,11 +25,13 @@ def extend_context(request, context, conference=None):
         conference = get_conference(request)
     conference_list = Conference.objects.accessible_by_user(request.user).all()
 
-    context.update({
-        'LANGUAGES': settings.LANGUAGES,
-        'conference': conference,
-        'conferences': conference_list,
-        'has_pages': request.user.has_pages(conference) if request.user.is_authenticated else False,
-    })
+    context.update(
+        {
+            'LANGUAGES': settings.LANGUAGES,
+            'conference': conference,
+            'conferences': conference_list,
+            'has_pages': request.user.has_pages(conference) if request.user.is_authenticated else False,
+        }
+    )
 
     return context
diff --git a/src/backoffice/views/vouchers.py b/src/backoffice/views/vouchers.py
index c49edc4770c18017d39568682ae092b88c0d0f7c..7a184b693fea3ac51362edf74796b4fe13d6c47c 100644
--- a/src/backoffice/views/vouchers.py
+++ b/src/backoffice/views/vouchers.py
@@ -8,7 +8,6 @@ from core.models import VoucherEntry
 
 from .mixins import ConferenceMixin
 
-
 logger = logging.getLogger(__name__)
 MAX_ROWS = 42
 
@@ -30,7 +29,7 @@ class VouchersView(ConferenceMixin, TemplateView):
 
         direct_matches = []
         qs = VoucherEntry.objects.filter(content__icontains=search_term)
-        results = list(direct_matches) + list(qs[:MAX_ROWS + 1])
+        results = list(direct_matches) + list(qs[: MAX_ROWS + 1])
         more_results = len(results) > MAX_ROWS
 
         if more_results:
diff --git a/src/backoffice/views/wiki.py b/src/backoffice/views/wiki.py
index ed9dda4fd777ef7de1f863116e3e57e1ce0ada65..8d0aa72682725c45daafbdd532849c07a122bc3b 100644
--- a/src/backoffice/views/wiki.py
+++ b/src/backoffice/views/wiki.py
@@ -1,17 +1,17 @@
-from datetime import datetime
 import json
+from datetime import datetime
 
 from django.contrib import messages
 from django.db import models
 from django.db.models import OuterRef
 from django.db.models.expressions import F
 from django.db.models.functions import JSONObject
-from django.views.generic import ListView, TemplateView
-from django.views.generic.edit import UpdateView, DeleteView
 from django.urls import reverse, reverse_lazy
 from django.utils.translation import gettext_lazy as _
+from django.views.generic import ListView, TemplateView
+from django.views.generic.edit import DeleteView, UpdateView
 
-from core.models import StaticPage, StaticPageRevision, StaticPageNamespace
+from core.models import StaticPage, StaticPageNamespace, StaticPageRevision
 
 from ..forms import StaticPageEditForm
 from .mixins import ConferenceMixin, guess_active_sidebar_item
@@ -89,11 +89,14 @@ class PagesView(WikiAdminMixin, ListView):
     template_name = 'backoffice/wiki_page_list.html'
 
     def get_queryset(self, *args, **kwargs):
-        last_revision_details = StaticPageRevision.objects.filter(page=OuterRef('pk'), pk=OuterRef('public_revision'))\
-            .values(data=LastRevisionJSONObject(author=F('author__username'), timestamp=F('timestamp')))[0:1]
-        return StaticPage.objects.accessible_by_user(conference=self.conference, user=self.request.user, language=None)\
-            .order_by('slug', 'language')\
+        last_revision_details = StaticPageRevision.objects.filter(page=OuterRef('pk'), pk=OuterRef('public_revision')).values(
+            data=LastRevisionJSONObject(author=F('author__username'), timestamp=F('timestamp'))
+        )[0:1]
+        return (
+            StaticPage.objects.accessible_by_user(conference=self.conference, user=self.request.user, language=None)
+            .order_by('slug', 'language')
             .annotate(last_revision_details=last_revision_details)
+        )
 
 
 class PageView(WikiAdminMixin, UpdateView):
@@ -130,7 +133,7 @@ class PageDeleteView(WikiAdminMixin, DeleteView):
 
     def form_valid(self, form):
         res = super().form_valid(form)
-        messages.success(self.request, _("StaticPage--deleted"))
+        messages.success(self.request, _('StaticPage--deleted'))
         return res
 
 
@@ -147,5 +150,5 @@ class PageRevisionDeleteView(WikiAdminMixin, DeleteView):
 
     def form_valid(self, form):
         res = super().form_valid(form)
-        messages.success(self.request, _("StaticPageRevision--deleted"))
+        messages.success(self.request, _('StaticPageRevision--deleted'))
         return res
diff --git a/src/backoffice/views/workadventure.py b/src/backoffice/views/workadventure.py
index baa65f135a617406baebba0ffb78853c4bc5ba31..8a13ebe8495ef9f0ddd6bd1bd4a4daee20c58526 100644
--- a/src/backoffice/views/workadventure.py
+++ b/src/backoffice/views/workadventure.py
@@ -5,24 +5,22 @@ from uuid import UUID
 from django.conf import settings
 from django.contrib import messages
 from django.core.exceptions import ValidationError
+from django.db.models import Q
 from django.http import HttpResponseRedirect
 from django.shortcuts import redirect, render
 from django.urls import reverse
 from django.utils.html import format_html
-from django.views.generic import CreateView, DetailView, ListView, TemplateView, UpdateView, View, DeleteView
-from django.views.generic.detail import SingleObjectMixin
-
 from django.utils.translation import gettext_lazy as _
-from django.db.models import Q
+from django.views.generic import CreateView, DeleteView, DetailView, ListView, TemplateView, UpdateView, View
+from django.views.generic.detail import SingleObjectMixin
 
 from core.integrations.workadventure import WorkAdventureIntegration
-from core.models import PlatformUser, ConferenceMember, Assembly
+from core.models import Assembly, ConferenceMember, PlatformUser
 from core.models.rooms import Room
 from core.models.workadventure import WorkadventureSession, WorkadventureTexture
 
-from .mixins import ConferenceMixin, guess_active_sidebar_item
 from ..forms import CreateWorkadventureTextureForm
-
+from .mixins import ConferenceMixin, guess_active_sidebar_item
 
 logger = logging.getLogger(__name__)
 MAX_ROWS = 42
@@ -43,7 +41,7 @@ class WorkAdventureAdminMixin(ConferenceMixin):
 
     def get_context_data(self, *args, **kwargs):
         if not settings.INTEGRATIONS_WORKADVENTURE:
-            messages.warning(self.request, "WorkAdventure integration NOT active!")
+            messages.warning(self.request, 'WorkAdventure integration NOT active!')
 
         context = super().get_context_data(*args, **kwargs)
         context['active_page'] = 'workadventure'
@@ -69,16 +67,18 @@ class WorkAdventureAdminMixin(ConferenceMixin):
                     'link': reverse('backoffice:workadventure-texture-list'),
                     'expanded': False,
                     'count': self.conference.workadventure_textures.count(),
-                }
+                },
             ],
         }
 
         for m, (q, t) in self.BACKEND_STATUS.items():
-            maps.append({
-                'caption': t,
-                'count': self.conference.rooms.filter(q).count(),
-                'link': reverse('backoffice:workadventure-map-list') + '?mode=' + m,
-            })
+            maps.append(
+                {
+                    'caption': t,
+                    'count': self.conference.rooms.filter(q).count(),
+                    'link': reverse('backoffice:workadventure-map-list') + '?mode=' + m,
+                }
+            )
 
         # try to guess 'active' sidebar item
         guess_active_sidebar_item(self.request, context['sidebar']['items'], with_query_string=True)
@@ -218,17 +218,24 @@ class MapSyncView(WorkAdventureMapMixin, SingleObjectMixin, View):
             res = WorkAdventureIntegration.trigger_map_synchronization(room, force)
             msg = format_html(
                 '<strong>sync room {id} (force={force}):</strong><br><pre class="small">{res}</pre>',
-                id=room.pk, force=force, res=json.dumps(res, indent=2),
+                id=room.pk,
+                force=force,
+                res=json.dumps(res, indent=2),
             )
             if res.get('_errors'):
                 messages.error(request, msg)
             else:
                 messages.success(request, msg)
         except Exception as err:
-            messages.error(request, format_html(
-                '<strong>sync room {id} (force={force}):</strong><br><pre class="small">{err}</pre>',
-                id=room.pk, force=force, err=err,
-            ))
+            messages.error(
+                request,
+                format_html(
+                    '<strong>sync room {id} (force={force}):</strong><br><pre class="small">{err}</pre>',
+                    id=room.pk,
+                    force=force,
+                    err=err,
+                ),
+            )
 
         return redirect('backoffice:workadventure-map-detail', pk=room.pk)
 
@@ -290,7 +297,8 @@ class SessionPushDataView(SessionView):
             res = WorkAdventureIntegration.push_userinfo_session(obj)
             msg = format_html(
                 '<strong>push userinfo {id} to backend:</strong><br><pre class="small">{res}</pre>',
-                id=obj.pk, res=json.dumps(res, indent=2),
+                id=obj.pk,
+                res=json.dumps(res, indent=2),
             )
             if res.get('_errors'):
                 messages.error(request, msg)
@@ -312,7 +320,8 @@ class SessionDeleteView(SessionView):
                     res = WorkAdventureIntegration.terminate_session(obj)
                     msg = format_html(
                         '<strong>termination of WA session {id} in backend:</strong><br><pre class="small">{res}</pre>',
-                        id=obj_id, res=json.dumps(res, indent=2),
+                        id=obj_id,
+                        res=json.dumps(res, indent=2),
                     )
                     if res.get('_errors'):
                         messages.error(request, msg)
@@ -328,10 +337,14 @@ class SessionDeleteView(SessionView):
                     messages.success(request, 'WA session deletion failure (' + obj_id + ')')
 
             except Exception as err:
-                messages.error(request, format_html(
-                    '<strong>delete session: general failure</strong> ({id})<br><pre class="small">{err}</pre>',
-                    err=err, id=obj_id,
-                ))
+                messages.error(
+                    request,
+                    format_html(
+                        '<strong>delete session: general failure</strong> ({id})<br><pre class="small">{err}</pre>',
+                        err=err,
+                        id=obj_id,
+                    ),
+                )
 
         else:
             messages.warning(request, 'session was already deleted.')
diff --git a/src/core/abuse.py b/src/core/abuse.py
index d92df660c7801d81ed2eb9f57e80bc4198c6bd52..14f4216b41fb9f7e229cd5bf5269e53d96e7e860 100644
--- a/src/core/abuse.py
+++ b/src/core/abuse.py
@@ -1,17 +1,19 @@
+import contextlib
+
 from django.contrib.sites.shortcuts import get_current_site
 from django.core.mail import send_mail
 from django.template import loader
-from django.urls import reverse, NoReverseMatch
+from django.urls import NoReverseMatch, reverse
 from django.utils.translation import gettext_lazy as _
 
-
 REPORT_CATEGORIES = {
-    'abuse': (_("abuse_report_category-abuse"), 'Abuse', 'abuse@cccv.de'),
-    'content': (_("abuse_report_category-content"), 'Content', 'report@cccv.de'),
-    'person': (_("abuse_report_category-person"), 'Person', 'report@cccv.de'),
-    'tech': (_("abuse_report_category-tech"), 'Technical', 'hub@cccv.de'),
-    # 'map': (_("abuse_report_category-map"), 'Map', 'world@cccv.de'),
-    'unknown': (_("abuse_report_category-misc"), 'Unknown', 'report@cccv.de'),
+    'abuse': (_('abuse_report_category-abuse'), 'Abuse', 'abuse@cccv.de'),
+    'content': (_('abuse_report_category-content'), 'Content', 'report@cccv.de'),
+    'person': (_('abuse_report_category-person'), 'Person', 'report@cccv.de'),
+    'tech': (_('abuse_report_category-tech'), 'Technical', 'hub@cccv.de'),
+    # TODO: Make this depended on whether WA is enabled
+    # 'map': (_("abuse_report_category-map"), 'Map', 'world@cccv.de'),# noqa: ERA001
+    'unknown': (_('abuse_report_category-misc'), 'Unknown', 'report@cccv.de'),
 }
 
 
@@ -40,12 +42,10 @@ def report_content(request, reporter, category, reported_content, problem_messag
         'message2': proposed_solution,
     }
 
-    try:
+    with contextlib.suppress(NoReverseMatch):
         context['reporter_profile'] = reverse('plainui:user_by_uuid', kwargs={'uuid': str(reporter.uuid)})
-    except NoReverseMatch:
-        pass
 
-    subject = "New %s Report" % (readable_category,)
+    subject = f'New {readable_category} Report'
     body = loader.render_to_string(EMAIL_TEMPLATE_NAME, context)
 
     send_mail(
diff --git a/src/core/admin.py b/src/core/admin.py
index 21a378a78ad4216d920dc7034d020edf3033bcc4..22348202511d24907b5720b188d8399c43638cc2 100644
--- a/src/core/admin.py
+++ b/src/core/admin.py
@@ -8,23 +8,47 @@ from django.contrib.gis.admin import GISModelAdmin
 from django.db.models import F
 from django.utils.translation import gettext_lazy as _
 
-from .models import \
-    BulletinBoardEntry, \
-    Conference, ConferenceMember, ConferenceNavigationItem, ConferenceTag, ConferenceTrack, \
-    DereferrerStats, \
-    Event, EventAttachment, EventParticipant, \
-    PlatformUser, \
-    Room, RoomLink, \
-    Assembly, AssemblyLink, AssemblyMember, AssemblyLogEntry, \
-    MapFloor, MapPOI, \
-    MetaNavItem, \
-    Badge, BadgeCategory, BadgeToken, BadgeTokenTimeConstraint, \
-    ScheduleSource, ScheduleSourceImport, ScheduleSourceMapping, \
-    StaticPage, StaticPageRevision, StaticPageNamespace, \
-    TagItem, \
-    UserCommunicationChannel, UserContact, UserBadge, UserDereferrerAllowlist, \
-    Voucher, VoucherEntry, \
-    WorkadventureSession, WorkadventureTexture
+from .models import (
+    Assembly,
+    AssemblyLink,
+    AssemblyLogEntry,
+    AssemblyMember,
+    Badge,
+    BadgeCategory,
+    BadgeToken,
+    BadgeTokenTimeConstraint,
+    BulletinBoardEntry,
+    Conference,
+    ConferenceMember,
+    ConferenceNavigationItem,
+    ConferenceTag,
+    ConferenceTrack,
+    DereferrerStats,
+    Event,
+    EventAttachment,
+    EventParticipant,
+    MapFloor,
+    MapPOI,
+    MetaNavItem,
+    PlatformUser,
+    Room,
+    RoomLink,
+    ScheduleSource,
+    ScheduleSourceImport,
+    ScheduleSourceMapping,
+    StaticPage,
+    StaticPageNamespace,
+    StaticPageRevision,
+    TagItem,
+    UserBadge,
+    UserCommunicationChannel,
+    UserContact,
+    UserDereferrerAllowlist,
+    Voucher,
+    VoucherEntry,
+    WorkadventureSession,
+    WorkadventureTexture,
+)
 
 logger = logging.getLogger(__name__)
 
@@ -57,21 +81,21 @@ class ArrayFieldEntryFilter(FieldListFilter):
         # assemble available options
         v = self.value()
         yield {
-            "selected": v is None or v == '',
-            "query_string": changelist.get_query_string(remove=[self.parameter_name]),
-            "display": _("All"),
+            'selected': v is None or v == '',
+            'query_string': changelist.get_query_string(remove=[self.parameter_name]),
+            'display': _('All'),
         }
         for value in sorted(values, key=lambda x: x.upper()):
             yield {
-                "selected": v == value,
-                "query_string": changelist.get_query_string({self.parameter_name: value}),
-                "display": value,
+                'selected': v == value,
+                'query_string': changelist.get_query_string({self.parameter_name: value}),
+                'display': value,
             }
 
     def queryset(self, request, queryset):
         if query := self.value():
             qs_filter = {
-                f"{self.field_path}__contains": query.split(','),
+                f'{self.field_path}__contains': query.split(','),
             }
             queryset = queryset.filter(**qs_filter)
         return queryset
@@ -108,22 +132,22 @@ class UserAssemblyMemberInline(admin.TabularInline):
 class UserFavoriteEventInline(admin.TabularInline):
     model = Event.favorited_by.through
     extra = 0
-    verbose_name = _("PlatformUser__favorite_event")
-    verbose_name_plural = _("PlatformUser__favorite_events")
+    verbose_name = _('PlatformUser__favorite_event')
+    verbose_name_plural = _('PlatformUser__favorite_events')
 
 
 class UserFavoriteAssemblyInline(admin.TabularInline):
     model = Assembly.favorited_by.through
     extra = 0
-    verbose_name = _("PlatformUser__favorite_assembly")
-    verbose_name_plural = _("PlatformUser__favorite_assemblies")
+    verbose_name = _('PlatformUser__favorite_assembly')
+    verbose_name_plural = _('PlatformUser__favorite_assemblies')
 
 
 class UserPersonalCalendarEventInline(admin.TabularInline):
     model = Event.in_personal_calendar.through
     extra = 0
-    verbose_name = _("PlatformUser__calendar_event")
-    verbose_name_plural = _("PlatformUser__calendar_events")
+    verbose_name = _('PlatformUser__calendar_event')
+    verbose_name_plural = _('PlatformUser__calendar_events')
 
 
 class PlatformUserAdmin(UserAdmin):
@@ -140,7 +164,7 @@ class PlatformUserAdmin(UserAdmin):
         ('Accessibility', {'fields': ('theme', 'no_animations', 'colorblind', 'high_contrast', 'tag_ignorelist')}),
         ('Disturbance Settings', {'fields': ('receive_dms', 'receive_dm_images', 'receive_audio', 'receive_video', 'autoaccept_contacts')}),
         ('Permissions', {'fields': ('is_active', 'shadow_banned', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
-        ('Notifications', {'fields': ('admin_notification', )}),
+        ('Notifications', {'fields': ('admin_notification',)}),
         ('Important dates', {'fields': ('last_login', 'date_joined')}),
         ('Security', {'fields': ('allow_reset_non_primary',)}),
     )
@@ -218,23 +242,29 @@ class ConferenceNavigationItemAdmin(admin.ModelAdmin):
         return qs.order_by('conference__name', F('parent__index').asc(nulls_first=True), 'index')
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['conference', 'parent', 'index'],
-        }),
-        ('Data', {
-            'fields': [
-                'is_visible',
-                'icon',
-                ('label_de', 'label_en'),
-                ('title_de', 'title_en'),
-                'url',
-            ],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['conference', 'parent', 'index'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': [
+                    'is_visible',
+                    'icon',
+                    ('label_de', 'label_en'),
+                    ('title_de', 'title_en'),
+                    'url',
+                ],
+            },
+        ),
     )
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
-        if db_field.name == "parent":
-            kwargs["queryset"] = ConferenceNavigationItem.objects.filter(parent=None).order_by('index')
+        if db_field.name == 'parent':
+            kwargs['queryset'] = ConferenceNavigationItem.objects.filter(parent=None).order_by('index')
 
         return super().formfield_for_foreignkey(db_field, request, **kwargs)
 
@@ -254,12 +284,18 @@ class ConferenceTrackAdmin(admin.ModelAdmin):
     readonly_fields = ['conference']
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['conference', 'is_public'],
-        }),
-        ('Data', {
-            'fields': ['slug', 'name', 'banner_image'],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['conference', 'is_public'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['slug', 'name', 'banner_image'],
+            },
+        ),
     )
 
     def get_readonly_fields(self, request, obj=None, **kwargs):
@@ -328,18 +364,30 @@ class AssemblyAdmin(GISModelAdmin):
     inlines = [TagsInline, BadgeInline, AssemblyLinkInline, AssemblyMemberInline, AssemblyLogEntryInline]
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference', 'state_assembly', 'state_channel', 'hierarchy', 'parent', 'is_official'],
-        }),
-        ('Data', {
-            'fields': ['is_physical', 'is_virtual', 'is_remote', 'slug', 'name', 'description', 'assembly_link', 'banner_image'],
-        }),
-        ('Registration', {
-            'fields': ['registration_details'],
-        }),
-        ('Location', {
-            'fields': ['assembly_location', 'location_point', 'location_boundaries'],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference', 'state_assembly', 'state_channel', 'hierarchy', 'parent', 'is_official'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['is_physical', 'is_virtual', 'is_remote', 'slug', 'name', 'description', 'assembly_link', 'banner_image'],
+            },
+        ),
+        (
+            'Registration',
+            {
+                'fields': ['registration_details'],
+            },
+        ),
+        (
+            'Location',
+            {
+                'fields': ['assembly_location', 'location_point', 'location_boundaries'],
+            },
+        ),
     )
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
@@ -356,12 +404,18 @@ class AssemblyAdmin(GISModelAdmin):
     def get_fieldsets(self, request, obj=None, **kwargs):
         if obj is None:
             return [
-                ('Organisation', {
-                    'fields': ['id', 'conference', 'state_assembly', 'state_channel', 'hierarchy', 'is_official'],
-                }),
-                ('Data', {
-                    'fields': ['is_physical', 'is_virtual', 'is_remote', 'slug', 'name'],
-                }),
+                (
+                    'Organisation',
+                    {
+                        'fields': ['id', 'conference', 'state_assembly', 'state_channel', 'hierarchy', 'is_official'],
+                    },
+                ),
+                (
+                    'Data',
+                    {
+                        'fields': ['is_physical', 'is_virtual', 'is_remote', 'slug', 'name'],
+                    },
+                ),
             ]
         return super().get_fieldsets(request, obj, **kwargs)
 
@@ -382,16 +436,25 @@ class AssemblyLogEntryAdmin(admin.ModelAdmin):
     search_fields = ['assembly__slug', 'assembly__name', 'comment', 'changes']
     readonly_fields = ['timestamp']
     fieldsets = [
-        ('Organisation', {
-            'fields': ['timestamp', 'assembly'],
-        }),
-        ('Data', {
-            'fields': [
-                ('kind', 'user',),
-                'comment',
-                'changes',
-            ],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['timestamp', 'assembly'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': [
+                    (
+                        'kind',
+                        'user',
+                    ),
+                    'comment',
+                    'changes',
+                ],
+            },
+        ),
     ]
 
 
@@ -403,12 +466,18 @@ class MapFloorAdmin(admin.ModelAdmin):
     ordering = ['conference', 'index']
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference'],
-        }),
-        ('Data', {
-            'fields': ['index', ('name_de', 'name_en')],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['index', ('name_de', 'name_en')],
+            },
+        ),
     )
 
     def get_readonly_fields(self, request, obj=None, **kwargs):
@@ -426,17 +495,24 @@ class MapPOIAdmin(GISModelAdmin):
     search_fields = ['name', 'description']
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference'],
-        }),
-        ('Data', {
-            'fields': ['visible', 'is_official',
-                       ('name_de', 'name_en'),
-                       ('description_de', 'description_en')],
-        }),
-        ('Location', {
-            'fields': ['location_floor', 'location_point'],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['visible', 'is_official', ('name_de', 'name_en'), ('description_de', 'description_en')],
+            },
+        ),
+        (
+            'Location',
+            {
+                'fields': ['location_floor', 'location_point'],
+            },
+        ),
     )
 
     def get_readonly_fields(self, request, obj=None, **kwargs):
@@ -454,7 +530,9 @@ class BadgeAdmin(admin.ModelAdmin):
     model = Badge
 
     fields = ['name', 'issuing_assembly', 'state', 'category', 'description', 'location', 'image', 'issuing_token']
-    readonly_fields = ['issuing_assembly',]
+    readonly_fields = [
+        'issuing_assembly',
+    ]
     list_display = ['__str__', 'issuing_assembly', 'category']
     list_display_links = ['__str__']
 
@@ -462,8 +540,8 @@ class BadgeAdmin(admin.ModelAdmin):
 class BadgeTokenTimeConstraintInline(admin.TabularInline):
     model = BadgeTokenTimeConstraint
     fields = ['date_time_range']
-    verbose_name = _("BadgeToken__badge_token_time_constraint")
-    verbose_name_plural = _("BadgeToken__badge_token_time_constraints")
+    verbose_name = _('BadgeToken__badge_token_time_constraint')
+    verbose_name_plural = _('BadgeToken__badge_token_time_constraints')
 
 
 class BadgeTokenAdmin(admin.ModelAdmin):
@@ -480,6 +558,7 @@ class BadgeTokenAdmin(admin.ModelAdmin):
 
     def valid(self, obj):
         return obj.valid
+
     valid.boolean = True
 
     def time_constraints_list(self, obj):
@@ -567,15 +646,24 @@ class EventAdmin(admin.ModelAdmin):
     readonly_fields = ['id', 'conference', 'get_is_imported']
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference', 'track', 'assembly', 'room', 'kind', 'is_public'],
-        }),
-        ('Schedule', {
-            'fields': ['schedule_start', 'schedule_duration', 'get_is_imported'],
-        }),
-        ('Data', {
-            'fields': ['name', 'slug', 'language', 'description', 'banner_image', 'additional_data'],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference', 'track', 'assembly', 'room', 'kind', 'is_public'],
+            },
+        ),
+        (
+            'Schedule',
+            {
+                'fields': ['schedule_start', 'schedule_duration', 'get_is_imported'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['name', 'slug', 'language', 'description', 'banner_image', 'additional_data'],
+            },
+        ),
     )
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
@@ -596,12 +684,18 @@ class EventAdmin(admin.ModelAdmin):
     def get_fieldsets(self, request, obj=None, **kwargs):
         if obj is None:
             return [
-                ('Organisation', {
-                    'fields': ['id', 'conference', 'assembly'],
-                }),
-                ('Data', {
-                    'fields': ['name'],
-                }),
+                (
+                    'Organisation',
+                    {
+                        'fields': ['id', 'conference', 'assembly'],
+                    },
+                ),
+                (
+                    'Data',
+                    {
+                        'fields': ['name'],
+                    },
+                ),
             ]
         return super().get_fieldsets(request, obj, **kwargs)
 
@@ -639,15 +733,24 @@ class RoomAdmin(admin.ModelAdmin):
     ordering = ('-conference__id', F('assembly__is_official').desc(nulls_last=True), 'assembly__name', F('capacity').desc(nulls_last=True), 'name')
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference', 'assembly', 'is_public_fahrplan', 'blocked', 'reserve_capacity'],
-        }),
-        ('Data', {
-            'fields': ['name', 'room_type', 'capacity', 'occupants', 'description'],
-        }),
-        ('Backend', {
-            'fields': ['backend_link', 'backend_link_branch', 'backend_status', 'backend_data', 'director_data'],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference', 'assembly', 'is_public_fahrplan', 'blocked', 'reserve_capacity'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['name', 'room_type', 'capacity', 'occupants', 'description'],
+            },
+        ),
+        (
+            'Backend',
+            {
+                'fields': ['backend_link', 'backend_link_branch', 'backend_status', 'backend_data', 'director_data'],
+            },
+        ),
     )
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
@@ -665,12 +768,18 @@ class RoomAdmin(admin.ModelAdmin):
     def get_fieldsets(self, request, obj=None, **kwargs):
         if obj is None:
             return [
-                ('Organisation', {
-                    'fields': ['id', 'conference', 'assembly', 'blocked'],
-                }),
-                ('Data', {
-                    'fields': ['name', 'room_type', 'backend_link', 'capacity'],
-                }),
+                (
+                    'Organisation',
+                    {
+                        'fields': ['id', 'conference', 'assembly', 'blocked'],
+                    },
+                ),
+                (
+                    'Data',
+                    {
+                        'fields': ['name', 'room_type', 'backend_link', 'capacity'],
+                    },
+                ),
             ]
         return super().get_fieldsets(request, obj, **kwargs)
 
@@ -706,15 +815,24 @@ class StaticPageAdmin(admin.ModelAdmin):
     readonly_fields = ['id', 'conference', 'language', 'body_html', 'search_content']
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference', 'slug', 'language'],
-        }),
-        ('Configuration', {
-            'fields': ['public_revision', 'protection', 'privacy', 'remove_html', 'sanitize_html'],
-        }),
-        ('Data', {
-            'fields': ['title', ('search_content', 'body_html')],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference', 'slug', 'language'],
+            },
+        ),
+        (
+            'Configuration',
+            {
+                'fields': ['public_revision', 'protection', 'privacy', 'remove_html', 'sanitize_html'],
+            },
+        ),
+        (
+            'Data',
+            {
+                'fields': ['title', ('search_content', 'body_html')],
+            },
+        ),
     )
 
     def get_inline_instances(self, request, obj=None, **kwargs):
@@ -739,15 +857,24 @@ class StaticPageNamespaceAdmin(admin.ModelAdmin):
     readonly_fields = ['id', 'conference']
 
     fieldsets = (
-        ('Organisation', {
-            'fields': ['id', 'conference'],
-        }),
-        ('Namespace', {
-            'fields': ['prefix', 'groups'],
-        }),
-        ('Upstream', {
-            'fields': ['upstream_url', 'upstream_image_base_url'],
-        }),
+        (
+            'Organisation',
+            {
+                'fields': ['id', 'conference'],
+            },
+        ),
+        (
+            'Namespace',
+            {
+                'fields': ['prefix', 'groups'],
+            },
+        ),
+        (
+            'Upstream',
+            {
+                'fields': ['upstream_url', 'upstream_image_base_url'],
+            },
+        ),
     )
 
     def get_readonly_fields(self, request, obj=None, **kwargs):
@@ -797,6 +924,7 @@ class VoucherEntryAdmin(admin.ModelAdmin):
 
     def is_assigned(self, instance):
         return instance.assigned is not None
+
     is_assigned.boolean = True
 
 
@@ -862,24 +990,33 @@ class MetaNavItemAdmin(admin.ModelAdmin):
     readonly_fields = ['pk', 'conference', 'graphic_light_current', 'graphic_dark_current']
 
     fieldsets = [
-        ('Metadata', {
-            'fields': ['pk', 'conference', 'slug'],
-        }),
-        ('Display', {
-            'fields': [
-                'index',
-                'visible',
-                'enabled',
-                ('title_de', 'title_en'),
-            ],
-        }),
-        ('Item', {
-            'fields': [
-                'url',
-                ('graphic_light_current', 'graphic_light'),
-                ('graphic_dark_current', 'graphic_dark'),
-            ],
-        }),
+        (
+            'Metadata',
+            {
+                'fields': ['pk', 'conference', 'slug'],
+            },
+        ),
+        (
+            'Display',
+            {
+                'fields': [
+                    'index',
+                    'visible',
+                    'enabled',
+                    ('title_de', 'title_en'),
+                ],
+            },
+        ),
+        (
+            'Item',
+            {
+                'fields': [
+                    'url',
+                    ('graphic_light_current', 'graphic_light'),
+                    ('graphic_dark_current', 'graphic_dark'),
+                ],
+            },
+        ),
     ]
 
     def get_readonly_fields(self, request, obj=None, **kwargs):
@@ -890,11 +1027,13 @@ class MetaNavItemAdmin(admin.ModelAdmin):
 
     def graphic_light_current(self, obj: MetaNavItem):
         return obj.get_graphic_light_as_html()
+
     graphic_light_current.allow_tags = True
     graphic_light_current.caption = _('MetaNavItem__graphic_light')
 
     def graphic_dark_current(self, obj: MetaNavItem):
         return obj.get_graphic_dark_as_html(default_to_light=False)
+
     graphic_dark_current.allow_tags = True
     graphic_dark_current.verbose_name = _('MetaNavItem__graphic_dark')
 
diff --git a/src/core/apps.py b/src/core/apps.py
index 08c8a22a972ce38b0f8a03879d5a5a0c40219dd6..9e5e1e9e87fac9035b655212bb9eeb49b76e4bae 100644
--- a/src/core/apps.py
+++ b/src/core/apps.py
@@ -2,7 +2,6 @@ import logging
 
 from django.apps import AppConfig
 
-
 logger = logging.getLogger(__name__)
 
 
diff --git a/src/core/base_forms.py b/src/core/base_forms.py
index bbea55ed3a2a35f70da15f5f47e2955fabbbc050..4c5fe68dc377a513e8ab069ec817ce38899001af 100644
--- a/src/core/base_forms.py
+++ b/src/core/base_forms.py
@@ -1,7 +1,7 @@
 from django.forms import ModelForm
 from modeltranslation.fields import build_localized_fieldname
 from modeltranslation.settings import AVAILABLE_LANGUAGES
-from modeltranslation.translator import translator, NotRegistered
+from modeltranslation.translator import NotRegistered, translator
 
 
 class TranslatedFieldsForm(ModelForm):
@@ -19,7 +19,11 @@ class TranslatedFieldsForm(ModelForm):
 
         fields_to_translate = set(translation_options.get_field_names())
 
-        for attr in ('fields', 'exclude', 'localized_fields',):
+        for attr in (
+            'fields',
+            'exclude',
+            'localized_fields',
+        ):
             fields = getattr(meta, attr, None)
             if fields:
                 new_fields = []
diff --git a/src/core/forms.py b/src/core/forms.py
index bae3f6827c34067cf1fbf86d1cacee7ee59adb90..1f5c325319d74cf1edbc6438c300ddefb1140795 100644
--- a/src/core/forms.py
+++ b/src/core/forms.py
@@ -3,14 +3,14 @@ from smtplib import SMTPException
 from typing import Any
 
 from django.conf import settings
-from django.db.models import Q
 from django.contrib.auth import forms as auth_forms
-from django.contrib.auth.tokens import default_token_generator
 from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
 from django.contrib.auth.forms import PasswordResetForm as ContribPasswordResetForm
+from django.contrib.auth.tokens import default_token_generator
 from django.contrib.sites.shortcuts import get_current_site
 from django.core.exceptions import ValidationError
 from django.core.mail import EmailMultiAlternatives
+from django.db.models import Q
 from django.forms import ChoiceField, EmailField, EmailInput
 from django.template import loader
 from django.utils.encoding import force_bytes
diff --git a/src/core/integrations/__init__.py b/src/core/integrations/__init__.py
index c6d4856c1e8d00ff2d60282b24b07bcc13b604b8..ab02f4673572fe5982890df6dacc4588717d7d35 100644
--- a/src/core/integrations/__init__.py
+++ b/src/core/integrations/__init__.py
@@ -1,7 +1,8 @@
 from django.conf import settings
+
 from .bigbluebutton import BigBlueButtonIntegration
-from .rc3hangar import HangarIntegration
 from .error import IntegrationError
+from .rc3hangar import HangarIntegration
 from .workadventure import WorkAdventureIntegration
 
 if settings.BIGBLUEBUTTON_API_URL is not None:
diff --git a/src/core/integrations/bigbluebutton.py b/src/core/integrations/bigbluebutton.py
index ffe772d112946077ada8210b3abaed61cd286619..bbc8f7c907914e85df5fdb7a4beb3d966b63c331 100644
--- a/src/core/integrations/bigbluebutton.py
+++ b/src/core/integrations/bigbluebutton.py
@@ -1,21 +1,21 @@
-from hashlib import sha1
 import logging
-from random import SystemRandom
-import requests
 import string
+from hashlib import sha1
+from random import SystemRandom
 from typing import Dict, Union
-from urllib.parse import urlencode, urljoin, quote
+from urllib.parse import quote, urlencode, urljoin
 from uuid import uuid4
 from xml.etree import ElementTree as ET
 
+import requests
+
 from django.utils.translation import gettext as _
 
+from core.models import BackendMixin, Event, PlatformUser, Room
 from core.models.assemblies import Assembly, AssemblyMember
-from core.models import BackendMixin, Event, Room, PlatformUser
 
 from .error import IntegrationError
 
-
 logger = logging.getLogger(__name__)
 PASSWORD_CHARS = string.ascii_letters + string.digits
 
@@ -24,16 +24,16 @@ def _params_to_str(data: Dict[str, Union[str, int, bool]]):
     res = {}
     for k, v in data.items():
         if isinstance(v, bool):
-            v = 'true' if v else 'false'
+            v = 'true' if v else 'false'  # noqa: PLW2901
         elif isinstance(v, int):
-            v = str(v)
+            v = str(v)  # noqa: PLW2901
         else:
             assert isinstance(v, str), f'{v!r} is no string!'
         res[k] = v
     return res
 
 
-class BigBlueButtonIntegration(object):
+class BigBlueButtonIntegration:
     """
     This class talks with a BigBlueButton server's API.
 
@@ -68,14 +68,14 @@ class BigBlueButtonIntegration(object):
             return resp
 
         if resp.status_code != 200:
-            logger.warning("Request for resource %r failed with status code %r", resource, resp.status_code)
-            raise IntegrationError(_("Request failed"))
+            logger.warning('Request for resource %r failed with status code %r', resource, resp.status_code)
+            raise IntegrationError(_('Request failed'))
 
         try:
             resp = ET.fromstring(resp.text)
         except UnicodeDecodeError:
-            logger.exception("Response decoding failed")
-            raise IntegrationError(_("Invalid Response"))
+            logger.exception('Response decoding failed')
+            raise IntegrationError(_('Invalid Response'))
 
         return resp
 
@@ -137,7 +137,8 @@ class BigBlueButtonIntegration(object):
                 f'    <module name="presentation">'
                 f'        <document url="{self._initial_presentation_url}" />'
                 f'    </module>'
-                f'</modules>')
+                f'</modules>'
+            )
 
         try:
             result = self._send_request('create', params, post_body=request_body)
@@ -153,8 +154,8 @@ class BigBlueButtonIntegration(object):
             message = result.find('message')
             if message is not None:
                 message = message.text
-            logger.warning("Request to create Room %s failed. Message: %r", room, message)
-            raise IntegrationError(_("Request failed"))
+            logger.warning('Request to create Room %s failed. Message: %r', room, message)
+            raise IntegrationError(_('Request failed'))
 
         room.backend_link = result.find('meetingID').text
         room.backend_status = Room.BackendStatus.ACTIVE
@@ -196,8 +197,8 @@ class BigBlueButtonIntegration(object):
             message = result.find('message')
             if message is not None:
                 message = message.text
-            logger.warning("Request to delete room %s failed. Message: %r", room, message)
-            raise IntegrationError(_("Request failed"))
+            logger.warning('Request to delete room %s failed. Message: %r', room, message)
+            raise IntegrationError(_('Request failed'))
 
         room.backend_status = Room.BackendStatus.INACTIVE
         room.save(update_fields=['backend_status'])
@@ -213,11 +214,11 @@ class BigBlueButtonIntegration(object):
         retcode = resp.find('returncode')
         if retcode is not None and retcode.text != 'SUCCESS':
             message_key = resp.find('messageKey')
-            if message_key is not None and (message_key.text == 'notFound' or message_key.text == 'invalidMeetingIdentifier'):
+            if message_key is not None and (message_key.text in ('notFound', 'invalidMeetingIdentifier')):
                 room.backend_status = Room.BackendStatus.INACTIVE
             else:
-                logger.warning("getMeetingInfo request for room %r failed with message %r", room, None if message_key is None else message_key.text)
-                raise IntegrationError(_("Request failed"))
+                logger.warning('getMeetingInfo request for room %r failed with message %r', room, None if message_key is None else message_key.text)
+                raise IntegrationError(_('Request failed'))
 
         try:
             room.occupants = int(resp.find('participantCount').text)
diff --git a/src/core/integrations/workadventure.py b/src/core/integrations/workadventure.py
index dd5a3c3c724df735afda38d247cdf1ad55924157..094bed765356d0f6fae4d68f81e1cc5d9ea671c9 100644
--- a/src/core/integrations/workadventure.py
+++ b/src/core/integrations/workadventure.py
@@ -1,9 +1,11 @@
+import contextlib
 import logging
 from json import JSONDecodeError
 
+import requests
+
 from django.conf import settings
 from django.utils.translation import gettext_lazy as _
-import requests
 
 from core.models.assemblies import Assembly
 from core.models.conference import Conference
@@ -96,9 +98,9 @@ class WorkAdventureIntegration:
         return {conference.slug: maps}
 
     @staticmethod
-    def _send_request_for_conference(url_template, url_auth, conference: Conference,
-                                     method='POST', content=None, data=None,
-                                     url_replacements=None, no_verify=False, expect_json=True):
+    def _send_request_for_conference(
+        url_template, url_auth, conference: Conference, method='POST', content=None, data=None, url_replacements=None, no_verify=False, expect_json=True
+    ):
         if url_template is None or url_template == '':
             logging.warning('URL is not defined.')
             return None, 'No PUSH url configured.'
@@ -117,7 +119,7 @@ class WorkAdventureIntegration:
             r = session.request(method, url, json=data, data=content.encode('utf-8') if content is not None else None)
             if not r.ok:
                 logging.error('Request to %s failed with %s: %s', url, r.status_code, r.text)
-                errmsg = 'Request got response {code}: {msg}'.format(code=r.status_code, msg=r.text)
+                errmsg = f'Request got response {r.status_code}: {r.text}'
                 try:
                     return r.json(), errmsg
                 except JSONDecodeError:
@@ -125,7 +127,7 @@ class WorkAdventureIntegration:
 
         except Exception as err:
             logging.exception('Request to %s failed.', url)
-            return None, 'Request failed: {msg}'.format(msg=str(err))
+            return None, f'Request failed: {err!s}'
 
         if r.status_code == 204:
             # server said OK but 'No Content'
@@ -142,7 +144,7 @@ class WorkAdventureIntegration:
         try:
             maps = WorkAdventureIntegration.assemble_wa_backend_maplist(conference)
         except Exception as err:
-            result['_errors'].append('Failed to assemble maps for conference {confslug}: {msg}'.format(confslug=conference.slug, msg=str(err)))
+            result['_errors'].append(f'Failed to assemble maps for conference {conference.slug}: {err!s}')
             logging.exception('WA map assembly for export failed.')
             return result
         result['maps'] = len(maps)
@@ -282,10 +284,8 @@ class WorkAdventureIntegration:
 
             # summarize violations
             violation = director_data.get('violation', {})
-            try:
+            with contextlib.suppress(ValueError):
                 linter_timestamp = unix2timestamp(int(violation.get('violationCheck')))
-            except ValueError:
-                pass
             linter_commit = violation.get('violationCommitHash')
             if not violation:
                 linter_status = 'success'
@@ -303,7 +303,7 @@ class WorkAdventureIntegration:
                 linter_results = violation.get('linterRes', {}).get('mapLints', {})
                 linter_missingassets = violation.get('linterRes', {}).get('missingAssets', [])
                 linter_missingentrypoints = violation.get('linterRes', {}).get('missingDeps', [])
-                linter_exitgraph = violation.get('linterRes', {}).get('exitGraph', "")
+                linter_exitgraph = violation.get('linterRes', {}).get('exitGraph', '')
         return {
             'wa_published': {
                 'commitHash': publish_commit if has_mapinfo else None,
@@ -320,6 +320,6 @@ class WorkAdventureIntegration:
                 'timestamp': linter_timestamp,
                 'missingAssets': linter_missingassets,
                 'missingEntrypoints': linter_missingentrypoints,
-                'exitGraph': linter_exitgraph
-            }
+                'exitGraph': linter_exitgraph,
+            },
         }
diff --git a/src/core/management/commands/assembly_contact_mails.py b/src/core/management/commands/assembly_contact_mails.py
index 361e5a03731faad2cd1f44e49567fd2b052cfbad..4caf3905b00d7898b3c109c8d21f77a356ad0632 100644
--- a/src/core/management/commands/assembly_contact_mails.py
+++ b/src/core/management/commands/assembly_contact_mails.py
@@ -6,7 +6,7 @@ from ...models.users import UserCommunicationChannel
 
 
 class Command(BaseCommand):
-    help = 'List all assemblies\' contacts\' verified e-mail addresses'
+    help = "List all assemblies' contacts' verified e-mail addresses"
 
     def add_arguments(self, parser):
         parser.add_argument('conf_slug', help='conference slug')
diff --git a/src/core/management/commands/bbb_integration_revisit.py b/src/core/management/commands/bbb_integration_revisit.py
index af4041322d03057260f4a826cda43689e924cf6e..dd92c61ce5e78c4547a964e6b358002e68f48640 100644
--- a/src/core/management/commands/bbb_integration_revisit.py
+++ b/src/core/management/commands/bbb_integration_revisit.py
@@ -3,14 +3,16 @@ import time
 from django.core.management.base import BaseCommand
 from django.db import transaction
 
-from core.models import Conference, Room
 from core.integrations import BigBlueButton, IntegrationError
+from core.models import Conference, Room
 
 
 class Command(BaseCommand):
     def add_arguments(self, parser):
         parser.add_argument(
-            '-a', '--all', action='store_true',
+            '-a',
+            '--all',
+            action='store_true',
             help='Revisit all Rooms, not just failing ones',
         )
 
@@ -30,7 +32,7 @@ class Command(BaseCommand):
                         BigBlueButton.create_room(room)
                         room.save()
                 except IntegrationError as e:
-                    print(f"Refreshing room {room!s} failed: {e!s}")
+                    print(f'Refreshing room {room!s} failed: {e!s}')
 
             elif revisit_active and room.backend_status in {Room.BackendStatus.ACTIVE, Room.BackendStatus.FULL}:
                 n_healthy += 1
@@ -39,7 +41,7 @@ class Command(BaseCommand):
                         BigBlueButton.room_status(room)
                         room.save()
                 except IntegrationError as e:
-                    print(f"Refreshing room {room!s} failed: {e!s}")
+                    print(f'Refreshing room {room!s} failed: {e!s}')
 
         run_time = time.time() - start
         print(f"Revisited {n_fail} failing rooms {f'and {n_healthy} healthy ones'} in {run_time} Seconds.")
diff --git a/src/core/management/commands/hangar_creation.py b/src/core/management/commands/hangar_creation.py
index a718cb65099925134673aa90e6697ab7fcf164d9..201952c69f9bc9d896902cd730de3354327e2c38 100644
--- a/src/core/management/commands/hangar_creation.py
+++ b/src/core/management/commands/hangar_creation.py
@@ -12,7 +12,7 @@ def create_hangar(room: Room):
     username = room.assembly.slug
     cmd = ['ssh', '-i', settings.HANGAR_KEY, settings.HANGAR_HOST, settings.HANGAR_CMD, username]
 
-    result = subprocess.run(cmd, capture_output=True, timeout=42, encoding='utf-8')
+    result = subprocess.run(cmd, capture_output=True, timeout=42, encoding='utf-8', check=False)
     if result.returncode != 0:
         room.backend_data = {
             'timestamp': timezone.now().strftime('%Y-%m-%d %H:%M:%S'),
@@ -61,7 +61,7 @@ class Command(BaseCommand):
                 contacts = create_hangar(room)
 
                 msg_subject = f'Hangar "{room.assembly.slug}"'
-                msg_text = '''Your hangar has been created. Please visit it in the Maschinenraum for access details.'''
+                msg_text = """Your hangar has been created. Please visit it in the Maschinenraum for access details."""
 
                 for c in contacts:
                     c.notify_user(msg_subject, msg_text, details_link=reverse('backoffice:assembly-room', kwargs={'assembly': room.assembly_id, 'pk': room.id}))
diff --git a/src/core/management/commands/housekeeping.py b/src/core/management/commands/housekeeping.py
index a1645d065c62615fd60e433a04d0e1b1bab8a1eb..8e7ab72ca3bd17dda15bcee4d15bc135819d8fcd 100644
--- a/src/core/management/commands/housekeeping.py
+++ b/src/core/management/commands/housekeeping.py
@@ -15,8 +15,8 @@ class Command(BaseCommand):
     def add_arguments(self, parser):
         parser.add_argument('--forever', action='store_true', help='repeat the housekeeping forever (until Ctrl+C is pressed)')
         parser.add_argument('--forever-delay', type=int, default=300, help='seconds to wait between housekeeping runs')
-        parser.add_argument('--skip-schedule-imports', action='store_true', help='don\'t do schedule imports')
-        parser.add_argument('--skip-wiki-imports', action='store_true', help='don\'t import wiki namespaces from upstream')
+        parser.add_argument('--skip-schedule-imports', action='store_true', help="don't do schedule imports")
+        parser.add_argument('--skip-wiki-imports', action='store_true', help="don't import wiki namespaces from upstream")
 
     def _housekeeping_directmessages(self):
         # clear all direct messages which are after their expiry date
diff --git a/src/core/management/commands/import_mapservice_resultfile.py b/src/core/management/commands/import_mapservice_resultfile.py
index f55d27c4bc84910ee0dc0cdb8ed610a754c888f6..4c99565217eb42e1c7c8677a26cd0f5cc563f248 100644
--- a/src/core/management/commands/import_mapservice_resultfile.py
+++ b/src/core/management/commands/import_mapservice_resultfile.py
@@ -4,11 +4,12 @@ import json
 from django.core.management.base import BaseCommand
 
 from api.views.workadventure import MapServiceView
+
 from ...models import Conference
 
 
 class Command(BaseCommand):
-    help = 'import the mapservice\'s result file directly (as it would have been sent via the maps endpoint)'
+    help = "import the mapservice's result file directly (as it would have been sent via the maps endpoint)"
 
     def add_arguments(self, parser):
         parser.add_argument('conference_slug', help='slug of the conference')
diff --git a/src/core/management/commands/rerender_markdown.py b/src/core/management/commands/rerender_markdown.py
index 77a0c86e7817a00f0d0c3ba6715c03cfecf2781c..9dce899decaceb6e9c5f28835259587daedcb3a0 100644
--- a/src/core/management/commands/rerender_markdown.py
+++ b/src/core/management/commands/rerender_markdown.py
@@ -1,7 +1,7 @@
 from django.core.management.base import BaseCommand
 
-from core.models import Event, Room, Assembly, ConferenceMember, StaticPage
 from core.markdown import render_markdown_ex, store_relationships
+from core.models import Assembly, ConferenceMember, Event, Room, StaticPage
 
 
 class Command(BaseCommand):
diff --git a/src/core/management/commands/sanitize_database.py b/src/core/management/commands/sanitize_database.py
index 23e2fffd89051a4cfb9965f47c3b4c971b1e3c46..d151568ea73c1533932960ad882a5a61c92b6014 100644
--- a/src/core/management/commands/sanitize_database.py
+++ b/src/core/management/commands/sanitize_database.py
@@ -1,16 +1,22 @@
 from django.core.management.base import BaseCommand
 
-from ...models import \
-    Assembly, AssemblyMember, \
-    ConferenceMember, ConferenceMemberTicket, ConferenceTag, ConferenceTrack, \
-    DirectMessage, \
-    Event, \
-    PlatformUser, \
-    StaticPage, \
-    TagItem, \
-    UserBadge, \
-    UserCommunicationChannel, \
-    WorkadventureSession, UserDereferrerAllowlist
+from ...models import (
+    Assembly,
+    AssemblyMember,
+    ConferenceMember,
+    ConferenceMemberTicket,
+    ConferenceTag,
+    ConferenceTrack,
+    DirectMessage,
+    Event,
+    PlatformUser,
+    StaticPage,
+    TagItem,
+    UserBadge,
+    UserCommunicationChannel,
+    UserDereferrerAllowlist,
+    WorkadventureSession,
+)
 
 
 class Command(BaseCommand):
@@ -86,10 +92,6 @@ class Command(BaseCommand):
         print('WorkadventureSession: ', end='', flush=True)
         print_delete_stat(WorkadventureSession.objects.all().delete())
 
-        # from plainui.models import BulletinBoardEntry
-        # print('BulletinBoardEntry: ', end='', flush=True)
-        # print_delete_stat(BulletinBoardEntry.objects.all().delete())  # cascade via PlatformUser
-
         print('UserDereferrerAllowlist: ', end='', flush=True)
         print_delete_stat(UserDereferrerAllowlist.objects.all().delete())
 
diff --git a/src/core/management/commands/schedule_join_rooms.py b/src/core/management/commands/schedule_join_rooms.py
index 100e10c8f7a6817b06ce47605d7171e14f127d47..a9d4b9cd6f3d77f8e017b53350c1b48cefe5552e 100644
--- a/src/core/management/commands/schedule_join_rooms.py
+++ b/src/core/management/commands/schedule_join_rooms.py
@@ -1,7 +1,7 @@
 from django.core.management.base import BaseCommand, CommandError
 from django.db import transaction
 
-from ...models import Room, Event, ScheduleSourceMapping, RoomLink
+from ...models import Event, Room, RoomLink, ScheduleSourceMapping
 
 
 class Command(BaseCommand):
diff --git a/src/core/management/commands/serviceusers.py b/src/core/management/commands/serviceusers.py
index a898830b6c5d402031c022441b291f8d05e00948..2d857bf11f74dfb7cd160da73997ccd7eb038c23 100644
--- a/src/core/management/commands/serviceusers.py
+++ b/src/core/management/commands/serviceusers.py
@@ -1,4 +1,5 @@
 from rest_framework.authtoken.models import Token
+
 from django.core.management.base import BaseCommand
 
 from ...models.users import PlatformUser
diff --git a/src/core/management/commands/stats.py b/src/core/management/commands/stats.py
index 84f09ee9b924d4dd8cf953aff028212d63ff0b50..08368fe7126796826f7f4a95de37cce8fa0c0acc 100644
--- a/src/core/management/commands/stats.py
+++ b/src/core/management/commands/stats.py
@@ -8,15 +8,21 @@ class Command(BaseCommand):
         for conf in Conference.objects.all():
             print('#', conf.slug, '(' + conf.name + ')')
 
-            print('- assemblies:',
-                  Assembly.objects.filter(conference=conf).count(), 'total,',
-                  Assembly.objects.filter(conference=conf, state__in=Assembly.PUBLIC_STATES).count(), 'accepted',
-                  )
-
-            print('- events:',
-                  Event.objects.filter(conference=conf).count(), 'total,',
-                  Event.objects.filter(conference=conf, is_public=True).count(), 'visible',
-                  )
+            print(
+                '- assemblies:',
+                Assembly.objects.filter(conference=conf).count(),
+                'total,',
+                Assembly.objects.filter(conference=conf, state__in=Assembly.PUBLIC_STATES).count(),
+                'accepted',
+            )
+
+            print(
+                '- events:',
+                Event.objects.filter(conference=conf).count(),
+                'total,',
+                Event.objects.filter(conference=conf, is_public=True).count(),
+                'visible',
+            )
 
             print('- rooms:', Room.objects.filter(conference=conf).count(), 'total')
             for rt in Room.BACKEND_ROOMTYPES:
diff --git a/src/core/management/commands/suggestion_tick.py b/src/core/management/commands/suggestion_tick.py
index 87336d54dc5fe26ace59c0a47812fb4cba4a3e6c..534ef0dbc1c5244b76b0aa5fcacfd31e32bc7067 100644
--- a/src/core/management/commands/suggestion_tick.py
+++ b/src/core/management/commands/suggestion_tick.py
@@ -1,10 +1,10 @@
+import time
+
 from django.core.management.base import BaseCommand
-from django.db import transaction, connection
+from django.db import connection, transaction
 
 from core.models import Assembly, Conference
 
-import time
-
 
 class Command(BaseCommand):
     def handle(self, *args, **options):
@@ -12,30 +12,36 @@ class Command(BaseCommand):
 
         start_time = time.time()
         with transaction.atomic(), connection.cursor() as cursor:
-            cursor.execute('''
+            cursor.execute(
+                """
                     SELECT e.id, lk.likes
                     FROM core_event AS e INNER JOIN core_eventlikecount AS lk ON e.id = lk.event1_id AND e.id=lk.event2_id
                     WHERE e.conference_id = %s AND is_public
-            ''', [conf.pk])
+            """,
+                [conf.pk],
+            )
             for row in cursor.fetchall():
                 if row[1] == 0:
-                    cursor.execute("UPDATE core_eventlikecount SET like_ratio=0 WHERE event1_id=%s", [row[0]])
+                    cursor.execute('UPDATE core_eventlikecount SET like_ratio=0 WHERE event1_id=%s', [row[0]])
                 else:
-                    cursor.execute("UPDATE core_eventlikecount SET like_ratio=likes*1000/%s WHERE event1_id=%s", [row[1], row[0]])
+                    cursor.execute('UPDATE core_eventlikecount SET like_ratio=likes*1000/%s WHERE event1_id=%s', [row[1], row[0]])
         ts = time.time() - start_time
-        print("suggestion_tick (events) run took %f Seconds for Conference %s." % (ts, conf))
+        print(f'suggestion_tick (events) run took {ts:f} Seconds for Conference {conf}.')
 
         start_time = time.time()
         with transaction.atomic(), connection.cursor() as cursor:
-            cursor.execute(f'''
+            cursor.execute(
+                f"""
                     SELECT a.id, lk.likes
                     FROM core_assembly AS a INNER JOIN core_assemblylikecount AS lk ON a.id = lk.assembly1_id AND a.id=lk.assembly2_id
                     WHERE a.conference_id = %s AND state IN ({','.join(['%s'] * len(Assembly.PUBLIC_STATES))})
-            ''', [conf.pk, *Assembly.PUBLIC_STATES])
+            """,
+                [conf.pk, *Assembly.PUBLIC_STATES],
+            )
             for row in cursor.fetchall():
                 if row[1] == 0:
-                    cursor.execute("UPDATE core_assemblylikecount SET like_ratio=0 WHERE assembly1_id=%s", [row[0]])
+                    cursor.execute('UPDATE core_assemblylikecount SET like_ratio=0 WHERE assembly1_id=%s', [row[0]])
                 else:
-                    cursor.execute("UPDATE core_assemblylikecount SET like_ratio=likes*1000/%s WHERE assembly1_id=%s", [row[1], row[0]])
+                    cursor.execute('UPDATE core_assemblylikecount SET like_ratio=likes*1000/%s WHERE assembly1_id=%s', [row[1], row[0]])
         ts = time.time() - start_time
-        print("suggestion_tick (events) run took %f Seconds for Conference %s." % (ts, conf))
+        print(f'suggestion_tick (events) run took {ts:f} Seconds for Conference {conf}.')
diff --git a/src/core/markdown.py b/src/core/markdown.py
index bd2e27b93677088c47539b5168a7fc0642635a4f..9381fb40fb5747e3bba5659c7b2a0fcee349ab5d 100644
--- a/src/core/markdown.py
+++ b/src/core/markdown.py
@@ -1,20 +1,21 @@
+import html
+import re
 from typing import Optional
-from urllib.parse import urlparse, quote
+from urllib.parse import quote, urlparse
 
 import bleach
-import html
-import re
 import mistletoe
-from mistletoe.html_renderer import HTMLRenderer
 from mistletoe.block_token import BlockToken
-from mistletoe.span_token import Link, AutoLink, SpanToken, tokenize_inner
-from modeltranslation.fields import build_localized_fieldname
-from modeltranslation.settings import AVAILABLE_LANGUAGES
+from mistletoe.html_renderer import HTMLRenderer
+from mistletoe.span_token import AutoLink, Link, SpanToken, tokenize_inner
+
 from django.conf import settings
 from django.db.models import Model
+from django.urls import NoReverseMatch, reverse
 from django.utils.safestring import mark_safe
 from django.utils.text import slugify
-from django.urls import reverse, NoReverseMatch
+from modeltranslation.fields import build_localized_fieldname
+from modeltranslation.settings import AVAILABLE_LANGUAGES
 
 from .models import conference
 from .utils import scheme_and_netloc_from_url, url_in_allowlist
@@ -70,7 +71,7 @@ class AlertBlock(BlockToken):
         self.alert_type = match[0]
         self.alert_caption = match[1].strip()[1:-1] if match[1] else None
 
-        super().__init__("".join(match[2]), tokenize_inner)
+        super().__init__(''.join(match[2]), tokenize_inner)
 
     @classmethod
     def start(cls, line):
@@ -80,7 +81,7 @@ class AlertBlock(BlockToken):
     def read(cls, lines):
         first_line = cls.pattern.match(next(lines))
         line_buffer = []
-        while (next_line := lines.peek()) is not None and next_line.startswith("  "):
+        while (next_line := lines.peek()) is not None and next_line.startswith('  '):
             line_buffer.append(next(lines))
 
         return first_line.group(1), first_line.group(2), line_buffer
@@ -96,17 +97,17 @@ class MyHtmlRenderer(HTMLRenderer):
 
     @staticmethod
     def render_html_span(token):
-        if token.content.startswith("<!--"):
+        if token.content.startswith('<!--'):
             # HTML comments can be rendered as-is
             return token.content
         return html.escape(token.content)
 
     def render_alert_block(self, token: AlertBlock) -> str:
-        result = f"<div class=\"mx-1 my-3 alert alert-{token.alert_type}\">"
+        result = f'<div class="mx-1 my-3 alert alert-{token.alert_type}">'
         if token.alert_caption:
-            result += f"<p class=\"fw-bold\">{token.alert_caption}</p>"
+            result += f'<p class="fw-bold">{token.alert_caption}</p>'
         result += self.render_inner(token)
-        result += "</div>\n"
+        result += '</div>\n'
         return result
 
     def __init__(self, conf: 'conference.Conference', result: 'RenderResult', derefer_allowlist: bool = True, *extras, **kwargs):
@@ -143,7 +144,7 @@ class MyHtmlRenderer(HTMLRenderer):
         template = '<a href="{target}"{title}{link_class}>{inner}</a>'
         target = self.escape_url(token.target)
         if token.title:
-            title = ' title="{}"'.format(html.escape(token.title))
+            title = f' title="{html.escape(token.title)}"'
         else:
             title = ''
         inner = self.render_inner(token)
@@ -158,7 +159,7 @@ class MyHtmlRenderer(HTMLRenderer):
 
         template = '<a href="{target}"{link_class}>{inner}</a>'
         if token.mailto:
-            target = 'mailto:{}'.format(token.target)
+            target = f'mailto:{token.target}'
         else:
             target = self.escape_url(token.target)
         inner = self.render_inner(token)
@@ -219,7 +220,7 @@ class MyHtmlRenderer(HTMLRenderer):
 class MySanitizedHtmlRenderer(MyHtmlRenderer):
     @staticmethod
     def render_html_block(token):
-        if token.content.startswith("<!--"):
+        if token.content.startswith('<!--'):
             # HTML comments can be rendered as-is
             return token.content
         return html.escape(token.content)
@@ -250,9 +251,9 @@ class RenderResult:
         self.linked_urls |= other_result.linked_urls
 
 
-def render_markdown_ex(conf: 'conference.Conference', markup: str,
-                       allow_embedded_html: bool = False, sanitize_html: bool = True,
-                       dont_derefer_allowlist: bool = False) -> RenderResult:
+def render_markdown_ex(
+    conf: 'conference.Conference', markup: str, allow_embedded_html: bool = False, sanitize_html: bool = True, dont_derefer_allowlist: bool = False
+) -> RenderResult:
     # remove HTML tags embedded in markdown, unless requested otherwise
     renderer = MyHtmlRenderer if allow_embedded_html else MySanitizedHtmlRenderer
 
@@ -263,17 +264,41 @@ def render_markdown_ex(conf: 'conference.Conference', markup: str,
 
     if sanitize_html:
         cleaner = bleach.Cleaner(
-            tags=[*[
-                'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'dl',
-                'table', 'thead', 'tbody', 'th', 'td', 'tr', 'hr', 'p', 'pre',
-                'img', 'code', 'div', 'span'
-            ], *list(bleach.sanitizer.ALLOWED_TAGS)],
+            tags=[
+                *[
+                    'br',
+                    'h1',
+                    'h2',
+                    'h3',
+                    'h4',
+                    'h5',
+                    'h6',
+                    'ul',
+                    'ol',
+                    'li',
+                    'dl',
+                    'table',
+                    'thead',
+                    'tbody',
+                    'th',
+                    'td',
+                    'tr',
+                    'hr',
+                    'p',
+                    'pre',
+                    'img',
+                    'code',
+                    'div',
+                    'span',
+                ],
+                *list(bleach.sanitizer.ALLOWED_TAGS),
+            ],
             attributes={
                 '*': ['class', 'id'],
                 'img': ['src', 'alt'],
                 **bleach.sanitizer.ALLOWED_ATTRIBUTES,
             },
-            protocols=['http', 'https', 'mailto', 'ftp', 'ftps']
+            protocols=['http', 'https', 'mailto', 'ftp', 'ftps'],
         )
         rendered_markup = cleaner.clean(rendered_markup)
 
@@ -281,39 +306,45 @@ def render_markdown_ex(conf: 'conference.Conference', markup: str,
     return result
 
 
-def render_markdown(conf: 'conference.Conference', markup: str,
-                    allow_embedded_html: bool = False, sanitize_html: bool = True,
-                    dont_derefer_allowlist: bool = False) -> str:
-    render_result = render_markdown_ex(conf, markup,
-                                       allow_embedded_html=allow_embedded_html, sanitize_html=sanitize_html,
-                                       dont_derefer_allowlist=dont_derefer_allowlist)
+def render_markdown(
+    conf: 'conference.Conference', markup: str, allow_embedded_html: bool = False, sanitize_html: bool = True, dont_derefer_allowlist: bool = False
+) -> str:
+    render_result = render_markdown_ex(
+        conf, markup, allow_embedded_html=allow_embedded_html, sanitize_html=sanitize_html, dont_derefer_allowlist=dont_derefer_allowlist
+    )
     return render_result.document
 
 
 def store_relationships(conf, link_source, render_result: RenderResult):
     from django.contrib.contenttypes.models import ContentType
+
     from .models import MarkdownMeta
     from .models.markdown import MetaTypes
 
     src_type = ContentType.objects.get_for_model(link_source)
     src_object_id = link_source.pk
 
-    MarkdownMeta.objects.filter(src_type=src_type, src_object_id=src_object_id) \
-        .exclude(dst_type=MetaTypes.USER_SLUG, dst_object__in=render_result.linked_user_slugs) \
-        .exclude(dst_type=MetaTypes.PAGE_SLUG, dst_object__in=render_result.linked_page_slugs)
+    MarkdownMeta.objects.filter(src_type=src_type, src_object_id=src_object_id).exclude(
+        dst_type=MetaTypes.USER_SLUG, dst_object__in=render_result.linked_user_slugs
+    ).exclude(dst_type=MetaTypes.PAGE_SLUG, dst_object__in=render_result.linked_page_slugs)
 
     MarkdownMeta.objects.bulk_create(
-        [MarkdownMeta(src_type=src_type, src_object_id=src_object_id, dst_type=MetaTypes.USER_SLUG, dst_object=linked_user_slug)
-            for linked_user_slug in render_result.linked_user_slugs] +
-        [MarkdownMeta(src_type=src_type, src_object_id=src_object_id, dst_type=MetaTypes.PAGE_SLUG, dst_object=linked_page_slug)
-            for linked_page_slug in render_result.linked_page_slugs],
-        ignore_conflicts=True
+        [
+            MarkdownMeta(src_type=src_type, src_object_id=src_object_id, dst_type=MetaTypes.USER_SLUG, dst_object=linked_user_slug)
+            for linked_user_slug in render_result.linked_user_slugs
+        ]
+        + [
+            MarkdownMeta(src_type=src_type, src_object_id=src_object_id, dst_type=MetaTypes.PAGE_SLUG, dst_object=linked_page_slug)
+            for linked_page_slug in render_result.linked_page_slugs
+        ],
+        ignore_conflicts=True,
     )
 
 
 def refresh_linking_markdown(conf: 'conference.Conference', link_target):
     from django.contrib.contenttypes.models import ContentType
-    from .models import ConferenceMember, StaticPage, Event, Room, Assembly, BulletinBoardEntry, MarkdownMeta
+
+    from .models import Assembly, BulletinBoardEntry, ConferenceMember, Event, MarkdownMeta, Room, StaticPage
     from .models.markdown import MetaTypes
 
     if isinstance(link_target, StaticPage):
@@ -323,7 +354,7 @@ def refresh_linking_markdown(conf: 'conference.Conference', link_target):
         dst_type = MetaTypes.USER_SLUG
         dst_object = link_target.user.slug
     else:
-        raise Exception("Unsupported link_target type %r" % (type(link_target)))
+        raise Exception('Unsupported link_target type %r' % (type(link_target)))
 
     relateds_by_type = {}
     for src_type_id, src_object_id in MarkdownMeta.objects.filter(dst_type=dst_type, dst_object=dst_object).values_list('src_type_id', 'src_object_id'):
@@ -333,7 +364,9 @@ def refresh_linking_markdown(conf: 'conference.Conference', link_target):
         static_page.body_html = render_markdown(conf, static_page.body)
         static_page.save()
 
-    for conference_member in ConferenceMember.objects.filter(conference=conf, uuid__in=relateds_by_type.get(ContentType.objects.get_for_model(ConferenceMember).pk, [])):  # noqa: E501
+    for conference_member in ConferenceMember.objects.filter(
+        conference=conf, uuid__in=relateds_by_type.get(ContentType.objects.get_for_model(ConferenceMember).pk, [])
+    ):  # noqa: E501
         conference_member.save(update_fields=['description'])
 
     for event in Event.objects.filter(conference=conf, pk__in=relateds_by_type.get(ContentType.objects.get_for_model(Event).pk, [])):
@@ -345,15 +378,15 @@ def refresh_linking_markdown(conf: 'conference.Conference', link_target):
     for assembly in Assembly.objects.filter(conference=conf, pk__in=relateds_by_type.get(ContentType.objects.get_for_model(Assembly).pk, [])):
         assembly.save(update_fields=['description'])
 
-    for assembly in BulletinBoardEntry.objects.filter(conference=conf, pk__in=relateds_by_type.get(ContentType.objects.get_for_model(BulletinBoardEntry).pk, [])):  # noqa: E501
+    for assembly in BulletinBoardEntry.objects.filter(
+        conference=conf, pk__in=relateds_by_type.get(ContentType.objects.get_for_model(BulletinBoardEntry).pk, [])
+    ):  # noqa: E501
         assembly.save(update_fields=['description'])
 
 
-def compile_translated_markdown_fields(obj: Model,
-                                       conf: 'conference.Conference',
-                                       field_name: str,
-                                       dst_obj: Optional[Model] = None,
-                                       dst_field_name: Optional[str] = None) -> RenderResult:
+def compile_translated_markdown_fields(
+    obj: Model, conf: 'conference.Conference', field_name: str, dst_obj: Optional[Model] = None, dst_field_name: Optional[str] = None
+) -> RenderResult:
     if dst_obj is None:
         dst_obj = obj
     if dst_field_name is None:
diff --git a/src/core/middleware.py b/src/core/middleware.py
index cd0eabb6b10ca7b5aa0f9629f59a57cf5b46a784..f7a8ff02d473ab73819f6341e3195dcd67523620 100644
--- a/src/core/middleware.py
+++ b/src/core/middleware.py
@@ -5,7 +5,6 @@ from django.conf import settings
 from django.contrib import messages
 from django.utils import timezone
 
-
 logger = logging.getLogger(__name__)
 
 
@@ -36,11 +35,11 @@ class SetRemoteAddrMiddleware:
             try:
                 real_ip = request.META[lookup_header]
             except KeyError:
-                logger.debug("Could not load client ip from %r in request from %r. Misconfigured Proxy?", lookup_header, request.META['REMOTE_ADDR'])
+                logger.debug('Could not load client ip from %r in request from %r. Misconfigured Proxy?', lookup_header, request.META['REMOTE_ADDR'])
             else:
                 # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. The
                 # client's IP will be the first one.
-                real_ip = real_ip.split(",")[0].strip()
+                real_ip = real_ip.split(',')[0].strip()
                 m = V6_WITH_V4_PREFIX_REGEX.match(real_ip)
                 if m:
                     real_ip = m.group(1)
diff --git a/src/core/migrations/0114_prepare_primary_keys_badge_badgetoken.py b/src/core/migrations/0114_prepare_primary_keys_badge_badgetoken.py
index 4242b2dfba3290b5589309f2665d7e5fe8593171..e0b2c80c16bca3685fdd9f859b8c9f32bf59ce47 100644
--- a/src/core/migrations/0114_prepare_primary_keys_badge_badgetoken.py
+++ b/src/core/migrations/0114_prepare_primary_keys_badge_badgetoken.py
@@ -26,6 +26,6 @@ class Migration(migrations.Migration):
         migrations.AlterField(
             model_name='badge',
             name='id',
-            field=models.CharField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID',  max_length=36),
+            field=models.CharField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID', max_length=36),
         ),
     ]
diff --git a/src/core/models/__init__.py b/src/core/models/__init__.py
index d5622d7ed9e4e3fc2c6d74835d6e1774a119ef75..f9086f16d9ed0e912d5966d43a13dd967687ec7c 100644
--- a/src/core/models/__init__.py
+++ b/src/core/models/__init__.py
@@ -1,43 +1,70 @@
 from .assemblies import Assembly, AssemblyLikeCount, AssemblyLink, AssemblyLogEntry, AssemblyMember
 from .badges import Badge, BadgeCategory, BadgeToken, BadgeTokenTimeConstraint, UserBadge
-from .conference import Conference, ConferenceExportCache, ConferenceMember, ConferenceNavigationItem, ConferenceTrack
-from .dereferrer import UserDereferrerAllowlist, DereferrerStats
 from .board import BulletinBoardEntry
+from .conference import Conference, ConferenceExportCache, ConferenceMember, ConferenceNavigationItem, ConferenceTrack
+from .dereferrer import DereferrerStats, UserDereferrerAllowlist
 from .events import Event, EventAttachment, EventLikeCount, EventParticipant
-from .pages import StaticPage, StaticPageRevision, StaticPageNamespace
-from .markdown import MarkdownMeta
 from .map import MapFloor, MapPOI
+from .markdown import MarkdownMeta
 from .messages import DirectMessage
 from .metanavi import MetaNavItem
+from .pages import StaticPage, StaticPageNamespace, StaticPageRevision
 from .rooms import Room, RoomLink
 from .schedules import ScheduleSource, ScheduleSourceImport, ScheduleSourceMapping
 from .shared import BackendMixin
 from .sso import Application
 from .tags import ConferenceTag, TagItem
 from .ticket import ConferenceMemberTicket
-from .users import UserCommunicationChannel, UserContact, PlatformUser
+from .users import PlatformUser, UserCommunicationChannel, UserContact
 from .voucher import Voucher, VoucherEntry
 from .workadventure import WorkadventureSession, WorkadventureTexture
 
 __all__ = [
     'Application',
-    'Assembly', 'AssemblyLikeCount', 'AssemblyLink', 'AssemblyLogEntry', 'AssemblyMember',
-    'BackendMixin', 'Badge', 'BadgeCategory', 'BadgeToken', 'BadgeTokenTimeConstraint', 'BulletinBoardEntry',
-    'Conference', 'ConferenceExportCache',
-    'ConferenceMember', 'ConferenceMemberTicket',
+    'Assembly',
+    'AssemblyLikeCount',
+    'AssemblyLink',
+    'AssemblyLogEntry',
+    'AssemblyMember',
+    'BackendMixin',
+    'Badge',
+    'BadgeCategory',
+    'BadgeToken',
+    'BadgeTokenTimeConstraint',
+    'BulletinBoardEntry',
+    'Conference',
+    'ConferenceExportCache',
+    'ConferenceMember',
+    'ConferenceMemberTicket',
     'ConferenceNavigationItem',
-    'ConferenceTag', 'ConferenceTrack',
-    'DereferrerStats', 'DirectMessage',
-    'Event', 'EventAttachment', 'EventLikeCount', 'EventParticipant',
-    'MapFloor', 'MapPOI',
+    'ConferenceTag',
+    'ConferenceTrack',
+    'DereferrerStats',
+    'DirectMessage',
+    'Event',
+    'EventAttachment',
+    'EventLikeCount',
+    'EventParticipant',
+    'MapFloor',
+    'MapPOI',
     'MetaNavItem',
     'PlatformUser',
-    'Room', 'RoomLink',
-    'ScheduleSource', 'ScheduleSourceImport', 'ScheduleSourceMapping',
-    'StaticPage', 'StaticPageRevision', 'StaticPageNamespace',
+    'Room',
+    'RoomLink',
+    'ScheduleSource',
+    'ScheduleSourceImport',
+    'ScheduleSourceMapping',
+    'StaticPage',
+    'StaticPageRevision',
+    'StaticPageNamespace',
     'MarkdownMeta',
     'TagItem',
-    'UserContact', 'UserCommunicationChannel', 'UserBadge', 'UserDereferrerAllowlist',
-    'Voucher', 'VoucherEntry',
-    'WorkadventureSession', 'WorkadventureTexture'
+    'UserContact',
+    'UserCommunicationChannel',
+    'UserBadge',
+    'UserDereferrerAllowlist',
+    'Voucher',
+    'VoucherEntry',
+    'WorkadventureSession',
+    'WorkadventureTexture',
 ]
diff --git a/src/core/models/assemblies.py b/src/core/models/assemblies.py
index 0476d886625ba4a1e2bed28d08c7ad0ed4ac0da9..54872d949eb097a803ed9403e90a194c5a2190a1 100644
--- a/src/core/models/assemblies.py
+++ b/src/core/models/assemblies.py
@@ -12,10 +12,12 @@ from django.db import models
 from django.db.models import Q
 from django.template.loader import render_to_string
 from django.urls import reverse
-from django.utils.html import escape as html_escape, format_html
 from django.utils.functional import cached_property
+from django.utils.html import escape as html_escape
+from django.utils.html import format_html
 from django.utils.safestring import mark_safe
-from django.utils.translation import gettext, gettext_lazy as _
+from django.utils.translation import gettext
+from django.utils.translation import gettext_lazy as _
 
 from core.validators import FileSizeValidator, ImageDimensionValidator
 
@@ -74,8 +76,11 @@ class AssemblyManager(models.Manager):
         return qs
 
     def conference_accessible(self, conference: Conference):
-        return self.get_queryset().filter(conference=conference) \
+        return (
+            self.get_queryset()
+            .filter(conference=conference)
             .filter(Q(state_assembly__in=self.model.PUBLIC_STATES) | Q(state_channel__in=self.model.PUBLIC_STATES))
+        )
 
     def manageable_by_user(self, user: PlatformUser, conference: Conference, staff_can_manage=True):
         assert user is not None
@@ -108,7 +113,7 @@ class AssemblyManager(models.Manager):
         return qs
 
 
-def get_banner_file_name(instance: "Assembly", filename: str):
+def get_banner_file_name(instance: 'Assembly', filename: str):
     return str(Path(str(instance.id)).joinpath('banner', filename))
 
 
@@ -141,18 +146,10 @@ class Assembly(TaggedItemMixin, models.Model):
 
     id = models.UUIDField(default=uuid4, primary_key=True, editable=False)
     conference = ConferenceReference(related_name='assemblies')
-    slug = models.SlugField(
-        help_text=_('Assembly__slug__help'),
-        verbose_name=_('Assembly__slug'))
-    name = models.CharField(
-        max_length=200,
-        help_text=_('Assembly__name__help'),
-        verbose_name=_('Assembly__name'))
-
-    is_official = models.BooleanField(
-        default=False,
-        help_text=_('Assembly__is_official__help'),
-        verbose_name=_('Assembly__is_official'))
+    slug = models.SlugField(help_text=_('Assembly__slug__help'), verbose_name=_('Assembly__slug'))
+    name = models.CharField(max_length=200, help_text=_('Assembly__name__help'), verbose_name=_('Assembly__name'))
+
+    is_official = models.BooleanField(default=False, help_text=_('Assembly__is_official__help'), verbose_name=_('Assembly__is_official'))
 
     banner_image_height = models.PositiveIntegerField(blank=True, null=True)
     banner_image_width = models.PositiveIntegerField(blank=True, null=True)
@@ -184,89 +181,71 @@ class Assembly(TaggedItemMixin, models.Model):
         ],
     )
 
-    description = models.TextField(
-        blank=True, null=True,
-        help_text=_('Assembly__description__help'),
-        verbose_name=_('Assembly__description'))
+    description = models.TextField(blank=True, null=True, help_text=_('Assembly__description__help'), verbose_name=_('Assembly__description'))
     description_html = models.TextField(blank=True, null=True)
     registration_details = models.TextField(
-        blank=True, null=True,
-        help_text=_('Assembly__registration_details__help'),
-        verbose_name=_('Assembly__registration_details'))
+        blank=True, null=True, help_text=_('Assembly__registration_details__help'), verbose_name=_('Assembly__registration_details')
+    )
 
-    registration_data = models.JSONField(
-        blank=True, null=True,
-        help_text=_('Assembly__registration_data__help'),
-        verbose_name=_('Assembly__registration_data'))
+    registration_data = models.JSONField(blank=True, null=True, help_text=_('Assembly__registration_data__help'), verbose_name=_('Assembly__registration_data'))
 
     state_assembly = models.CharField(
-        max_length=20, default=State.NONE, choices=State.choices,
-        verbose_name=_('Assembly__state_assembly'),
-        help_text=_('Assembly__state_assembly__help'))
+        max_length=20, default=State.NONE, choices=State.choices, verbose_name=_('Assembly__state_assembly'), help_text=_('Assembly__state_assembly__help')
+    )
     state_channel = models.CharField(
-        max_length=20, default=State.NONE, choices=State.choices,
-        verbose_name=_('Assembly__state_channel'),
-        help_text=_('Assembly__state_channel__help'))
+        max_length=20, default=State.NONE, choices=State.choices, verbose_name=_('Assembly__state_channel'), help_text=_('Assembly__state_channel__help')
+    )
 
     hierarchy = models.CharField(
-        max_length=20, default=Hierarchy.REGULAR, choices=Hierarchy.choices,
-        help_text=_('Assembly__hierarchy__help'),
-        verbose_name=_('Assembly__hierarchy'))
+        max_length=20, default=Hierarchy.REGULAR, choices=Hierarchy.choices, help_text=_('Assembly__hierarchy__help'), verbose_name=_('Assembly__hierarchy')
+    )
     parent = models.ForeignKey(
-        'self', blank=True, null=True, related_name='+', on_delete=models.PROTECT,
-        verbose_name=_('Assembly__parent'),
-        help_text=_('Assembly__parent__help'))
+        'self', blank=True, null=True, related_name='+', on_delete=models.PROTECT, verbose_name=_('Assembly__parent'), help_text=_('Assembly__parent__help')
+    )
 
     technical_user = models.ForeignKey(
         PlatformUser,
-        blank=True, null=True,
-        related_name='+', on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        related_name='+',
+        on_delete=models.PROTECT,
         verbose_name=_('Assembly__technical_user'),
-        help_text=_('Assembly__technical_user__help'))
-
-    is_virtual = models.BooleanField(
-        blank=True, null=True,
-        verbose_name=_('Assembly__is_virtual'),
-        help_text=_('Assembly__is_virtual__help'))
-    is_physical = models.BooleanField(
-        blank=True, null=True,
-        verbose_name=_('Assembly__is_physical'),
-        help_text=_('Assembly__is_physical__help'))
-    is_remote = models.BooleanField(
-        blank=True, null=True,
-        verbose_name=_('Assembly__is_remote'),
-        help_text=_('Assembly__is_remote__help'))
-
-    assembly_location = models.TextField(
-        blank=True, null=True,
-        verbose_name=_('Assembly__location'),
-        help_text=_('Assembly__location__help'))
-    assembly_link = models.URLField(
-        blank=True, null=True,
-        verbose_name=_('Assembly__link'),
-        help_text=_('Assembly__link__help'))
+        help_text=_('Assembly__technical_user__help'),
+    )
+
+    is_virtual = models.BooleanField(blank=True, null=True, verbose_name=_('Assembly__is_virtual'), help_text=_('Assembly__is_virtual__help'))
+    is_physical = models.BooleanField(blank=True, null=True, verbose_name=_('Assembly__is_physical'), help_text=_('Assembly__is_physical__help'))
+    is_remote = models.BooleanField(blank=True, null=True, verbose_name=_('Assembly__is_remote'), help_text=_('Assembly__is_remote__help'))
+
+    assembly_location = models.TextField(blank=True, null=True, verbose_name=_('Assembly__location'), help_text=_('Assembly__location__help'))
+    assembly_link = models.URLField(blank=True, null=True, verbose_name=_('Assembly__link'), help_text=_('Assembly__link__help'))
     public_contact = models.EmailField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Assembly__public_contact__help'),
         verbose_name=_('Assembly__public_contact'),
     )
 
     location_floor = models.ForeignKey(
         MapFloor,
-        blank=True, null=True,
-        related_name='assemblies', on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        related_name='assemblies',
+        on_delete=models.PROTECT,
         help_text=_('Assembly__location_floor__help'),
         verbose_name=_('Assembly__location_floor'),
     )
 
     location_point = gis_models.PointField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Assembly__location_point__help'),
         verbose_name=_('Assembly__location_point'),
     )
 
     location_boundaries = gis_models.MultiPolygonField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Assembly__location_boundaries__help'),
         verbose_name=_('Assembly__location_boundaries'),
     )
@@ -277,21 +256,20 @@ class Assembly(TaggedItemMixin, models.Model):
         verbose_name=_('Assembly__created'),
     )
     last_update_assembly = models.DateTimeField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Assembly__last_update_assembly__help'),
         verbose_name=_('Assembly__last_update_assembly'),
     )
     last_update_staff = models.DateTimeField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Assembly__last_update_staff__help'),
         verbose_name=_('Assembly__last_update_staff'),
     )
 
     favorited_by = models.ManyToManyField(
-        PlatformUser,
-        related_name='favorite_assemblies',
-        help_text=_('Assembly__favorited_by__help'),
-        verbose_name=_('Assembly__favorited_by')
+        PlatformUser, related_name='favorite_assemblies', help_text=_('Assembly__favorited_by__help'), verbose_name=_('Assembly__favorited_by')
     )
 
     PUBLIC_STATES = [
@@ -433,9 +411,11 @@ class Assembly(TaggedItemMixin, models.Model):
 
     @property
     def basedata_readonly(self):
-        return \
-            self.state_assembly not in [Assembly.State.NONE, Assembly.State.PLANNED, Assembly.State.REGISTERED] or \
-            self.state_channel not in [Assembly.State.NONE, Assembly.State.PLANNED, Assembly.State.REGISTERED]
+        return self.state_assembly not in [Assembly.State.NONE, Assembly.State.PLANNED, Assembly.State.REGISTERED] or self.state_channel not in [
+            Assembly.State.NONE,
+            Assembly.State.PLANNED,
+            Assembly.State.REGISTERED,
+        ]
 
     @cached_property
     def sorted_tags(self):
@@ -522,6 +502,7 @@ class Assembly(TaggedItemMixin, models.Model):
 
     def get_voucher_count(self, with_always_public: bool = True) -> Optional[int]:
         from .voucher import Voucher
+
         entries = Voucher.objects.for_assembly(self, include_public_ones=False).count()
 
         # if we shall not pay respect to Vouchers available to everyone, just return the number here
@@ -718,10 +699,7 @@ class AssemblyMember(models.Model):
         verbose_name=_('AssemblyMember__is_technical_contact'),
     )
 
-    show_public = models.BooleanField(
-        default=True,
-        help_text=_('AssemblyMember__show_public__help'),
-        verbose_name=_('AssemblyMember__show_public'))
+    show_public = models.BooleanField(default=True, help_text=_('AssemblyMember__show_public__help'), verbose_name=_('AssemblyMember__show_public'))
 
     @property
     def roles(self):
@@ -756,10 +734,9 @@ class AssemblyMember(models.Model):
 
 class AssemblyLikeCount(models.Model):
     class Meta:
-        indexes = [
-            models.Index(fields=['assembly1', 'like_ratio'])
-        ]
+        indexes = [models.Index(fields=['assembly1', 'like_ratio'])]
         unique_together = [('assembly1', 'assembly2')]
+
     assembly1 = models.ForeignKey(Assembly, related_name='suggestions', on_delete=models.CASCADE)
     assembly2 = models.ForeignKey(Assembly, related_name='+', on_delete=models.CASCADE)
     likes = models.IntegerField()
@@ -783,35 +760,45 @@ class AssemblyLogEntry(models.Model):
         """Logged activity by an assembly member."""
 
     assembly = models.ForeignKey(
-        Assembly, related_name='logentries', on_delete=models.CASCADE,
-        help_text=_('AssemblyLogEntry__assembly__help'), verbose_name=_('AssemblyLogEntry__assembly'),
+        Assembly,
+        related_name='logentries',
+        on_delete=models.CASCADE,
+        help_text=_('AssemblyLogEntry__assembly__help'),
+        verbose_name=_('AssemblyLogEntry__assembly'),
     )
     user = models.ForeignKey(
         PlatformUser,
-        blank=True, null=True, on_delete=models.SET_NULL,
-        help_text=_('AssemblyLogEntry__user__help'), verbose_name=_('AssemblyLogEntry__user'),
+        blank=True,
+        null=True,
+        on_delete=models.SET_NULL,
+        help_text=_('AssemblyLogEntry__user__help'),
+        verbose_name=_('AssemblyLogEntry__user'),
     )
     timestamp = models.DateTimeField(
-        auto_now_add=True, editable=False,
-        help_text=_('AssemblyLogEntry__timestamp__help'), verbose_name=_('AssemblyLogEntry__timestamp'),
+        auto_now_add=True,
+        editable=False,
+        help_text=_('AssemblyLogEntry__timestamp__help'),
+        verbose_name=_('AssemblyLogEntry__timestamp'),
     )
     kind = models.CharField(
-        max_length=20, choices=Kind.choices,
-        help_text=_('AssemblyLogEntry__kind__help'), verbose_name=_('AssemblyLogEntry__kind'),
+        max_length=20,
+        choices=Kind.choices,
+        help_text=_('AssemblyLogEntry__kind__help'),
+        verbose_name=_('AssemblyLogEntry__kind'),
     )
 
     changes = models.JSONField(
-        blank=True, null=True,
-        help_text=_('AssemblyLogEntry__changes__help'), verbose_name=_('AssemblyLogEntry__changes'),
+        blank=True,
+        null=True,
+        help_text=_('AssemblyLogEntry__changes__help'),
+        verbose_name=_('AssemblyLogEntry__changes'),
     )
     comment = models.TextField(
-        blank=True, null=True,
-        help_text=_('AssemblyLogEntry__comment__help'), verbose_name=_('AssemblyLogEntry__comment'),
+        blank=True,
+        null=True,
+        help_text=_('AssemblyLogEntry__comment__help'),
+        verbose_name=_('AssemblyLogEntry__comment'),
     )
 
     def __str__(self):
-        return (
-                f'{self.timestamp} [{self.kind}]' +
-                (f' @{self.user.username}' if self.user else '') +
-                (f' "{self.comment}"' if self.comment else '')
-            )
+        return f'{self.timestamp} [{self.kind}]' + (f' @{self.user.username}' if self.user else '') + (f' "{self.comment}"' if self.comment else '')
diff --git a/src/core/models/badges.py b/src/core/models/badges.py
index 0943a2ef61e98118603da76045e58b0dbfbbdca9..0dc080346091ad9ce7b9e09a3b18ffda0aa37e40 100644
--- a/src/core/models/badges.py
+++ b/src/core/models/badges.py
@@ -4,6 +4,8 @@ from pathlib import Path
 from string import ascii_lowercase, digits
 from uuid import uuid4
 
+from segno import make as segno_make
+
 from django.conf import settings
 from django.contrib.postgres.fields import DateTimeRangeField
 from django.core.exceptions import ValidationError
@@ -14,7 +16,6 @@ from django.db import models
 from django.template.loader import render_to_string
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
-from segno import make as segno_make
 
 from core.markdown import compile_translated_markdown_fields, store_relationships
 from core.validators import ImageDimensionValidator
diff --git a/src/core/models/board.py b/src/core/models/board.py
index bdf8cd79d4f61ef4534c30b17206e1e9616ee83b..f651691499b8c0a3fa27af3f4fb3ca3cedb66f49 100644
--- a/src/core/models/board.py
+++ b/src/core/models/board.py
@@ -1,8 +1,8 @@
-from django.db import models
 from django.conf import settings
+from django.db import models
 
-from core.markdown import compile_translated_markdown_fields, store_relationships
 from core.fields import ConferenceReference
+from core.markdown import compile_translated_markdown_fields, store_relationships
 
 
 class BulletinBoardEntry(models.Model):
diff --git a/src/core/models/conference.py b/src/core/models/conference.py
index 4ae3fa0ea970f084669d380c72c5ccb77868ae4f..7a28185e1cfed540343180f411509f554c35771f 100644
--- a/src/core/models/conference.py
+++ b/src/core/models/conference.py
@@ -2,10 +2,11 @@ import logging
 import typing
 import xml.etree.ElementTree as ET
 from collections import OrderedDict
+from datetime import datetime, time, timedelta
 from json import JSONEncoder
-
 from uuid import uuid4
-from datetime import datetime, time, timedelta
+
+from timezone_field import TimeZoneField
 
 from django.conf import settings
 from django.contrib.auth.models import Group
@@ -14,21 +15,20 @@ from django.core.exceptions import ValidationError
 from django.core.validators import URLValidator, validate_slug
 from django.db import models
 from django.db.models import Max
-from django.http import HttpRequest, HttpResponseNotModified, HttpResponse
-from django.urls import reverse, NoReverseMatch
+from django.http import HttpRequest, HttpResponse, HttpResponseNotModified
+from django.urls import NoReverseMatch, reverse
 from django.utils import timezone
 from django.utils.functional import cached_property
 from django.utils.html import format_html
 from django.utils.safestring import mark_safe
 from django.utils.timezone import now
-from django.utils.translation import get_language, gettext_lazy as _
-from timezone_field import TimeZoneField
+from django.utils.translation import get_language
+from django.utils.translation import gettext_lazy as _
 
 from ..fields import ConferenceReference
 from ..utils import render_markdown_as_text
 from .users import PlatformUser
 
-
 logger = logging.getLogger(__name__)
 
 
@@ -46,59 +46,41 @@ class ConferenceMember(models.Model):
     active_angel = models.BooleanField(default=False)
     roles = pg_fields.ArrayField(models.CharField(max_length=50), blank=True, null=True)
     is_staff = models.BooleanField(default=False)
-    has_ticket = models.BooleanField(
-        default=False,
-        help_text=_('ConferenceMember--has_ticket--help'),
-        verbose_name=_('ConferenceMember--has_ticket'))
+    has_ticket = models.BooleanField(default=False, help_text=_('ConferenceMember--has_ticket--help'), verbose_name=_('ConferenceMember--has_ticket'))
 
     permission_groups = models.ManyToManyField(Group, blank=True, related_name='+')
     static_page_groups = pg_fields.ArrayField(models.CharField(max_length=50), blank=True, null=True)
 
-    description = models.TextField(
-        blank=True, null=True,
-        help_text=_('PlatformUser__description__help'),
-        verbose_name=_('PlatformUser__description'))
+    description = models.TextField(blank=True, null=True, help_text=_('PlatformUser__description__help'), verbose_name=_('PlatformUser__description'))
     description_html = models.TextField(blank=True, null=True)
 
     class Meta:
         permissions = [
             ('assembly_team', _('ConferenceMember__permission-assembly_team')),
             # See all assemblies, not only the accepted ones.
-
-            ('channel_team', _("ConferenceMember__permission-channel_team")),
+            ('channel_team', _('ConferenceMember__permission-channel_team')),
             # Channelteam, Assemblyteam for Channel-Type Assemblies (Assemblies that provide their own content)
-
             ('static_pages', _('ConferenceMember__permission-static_pages')),
             # Access to static pages, can be further limited by configuring static_page_groups.
-
             ('map_edit', _('ConferenceMember__permission-map_edit')),
             # modification of the map, i.e. POIs
-
             ('platformusers', 'Orga: Users List'),
             ('rename_platformuser', 'Orga: Rename User'),
-
             ('block_platformuser', _('ConferenceMember__permission-block_platformuser')),
             # This is the right to block a misbehaving user.
-
             ('change_conferencemember__active_angel', _('ConferenceMember__permission-change_conferencemember__active_angel')),
             # Permission to set the user's is-currently-on-duty flag.
-
             ('view_platformuser__guardian', _('ConferenceMember__permission-view_platformuser__guardian')),
             # See the guardian(s) of a user.
-
             ('voucher_admin', _('ConferenceMember__permission-voucher_admin')),
             # configure conference's vouchers
-
             ('scheduleadmin', _('ConferenceMember__permission-scheduleadmin')),
             # manage/support ScheduleSupports globally, i.e. without access to an individual assembly
-
             ('workadventure_admin', _('ConferenceMember__permission-workadventure_admin')),
             # manage/support all WA rooms and backend info
         ]
 
-        constraints = [
-            models.constraints.UniqueConstraint(fields=['user', 'conference'], name='conferencemember_user_conference_unique')
-        ]
+        constraints = [models.constraints.UniqueConstraint(fields=['user', 'conference'], name='conferencemember_user_conference_unique')]
 
     def get_permission_groups_permissions(self):
         """Returns only the permission codenames which are given via the associated permission_groups of this ConferenceMember instance."""
@@ -193,88 +175,59 @@ class Conference(models.Model):
     objects = ConferenceManager()
 
     id = models.UUIDField(default=uuid4, primary_key=True, editable=False)
-    slug = models.SlugField(
-        unique=True,
-        help_text=_('Conference__slug__help'),
-        verbose_name=_('Conference__slug'))
-    name = models.CharField(
-        max_length=200,
-        help_text=_('Conference__name__help'),
-        verbose_name=_('Conference__name'))
+    slug = models.SlugField(unique=True, help_text=_('Conference__slug__help'), verbose_name=_('Conference__slug'))
+    name = models.CharField(max_length=200, help_text=_('Conference__name__help'), verbose_name=_('Conference__name'))
 
-    is_public = models.BooleanField(
-        default=False,
-        help_text=_('Conference__is_public__help'),
-        verbose_name=_('Conference__is_public'))
+    is_public = models.BooleanField(default=False, help_text=_('Conference__is_public__help'), verbose_name=_('Conference__is_public'))
     registration_deadline = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('Conference__registration_deadline__help'),
-        verbose_name=_('Conference__registration_deadline'))
-
-    start = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('Conference__start__help'),
-        verbose_name=_('Conference__start'))
-    end = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('Conference__end__help'),
-        verbose_name=_('Conference__end'))
+        blank=True, null=True, help_text=_('Conference__registration_deadline__help'), verbose_name=_('Conference__registration_deadline')
+    )
+
+    start = models.DateTimeField(blank=True, null=True, help_text=_('Conference__start__help'), verbose_name=_('Conference__start'))
+    end = models.DateTimeField(blank=True, null=True, help_text=_('Conference__end__help'), verbose_name=_('Conference__end'))
 
     timezone = TimeZoneField(
-        choices_display="WITH_GMT_OFFSET",
-        default="UTC",
-        help_text=_('Conference__timezone__help'),
-        verbose_name=_('Conference__timezone'))
+        choices_display='WITH_GMT_OFFSET', default='UTC', help_text=_('Conference__timezone__help'), verbose_name=_('Conference__timezone')
+    )
     global_notification = models.TextField(
-        default='', blank=True,
-        help_text=_('Conference__global_notification__help'),
-        verbose_name=_('Conference__global_notification'))
+        default='', blank=True, help_text=_('Conference__global_notification__help'), verbose_name=_('Conference__global_notification')
+    )
 
     logo = models.TextField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Conference__logo__help'),
         verbose_name=_('Conference__logo'),
     )
     """SVG logo"""
 
-    send_pn_disabled = models.BooleanField(
-        default=False,
-        help_text=_('Conference__send_pn_disabled__help'),
-        verbose_name=_('Conference__send_pn_disabled'))
-    board_disabled = models.BooleanField(
-        default=False,
-        help_text=_('Conference__board_disabled__help'),
-        verbose_name=_('Conference__board_disabled'))
-    require_login = models.BooleanField(
-        default=False,
-        help_text=_('Conference--require_login--help'),
-        verbose_name=_('Conference--require_login'))
-    require_ticket = models.BooleanField(
-        default=False,
-        help_text=_('Conference--require_ticket--help'),
-        verbose_name=_('Conference--require_ticket'))
+    send_pn_disabled = models.BooleanField(default=False, help_text=_('Conference__send_pn_disabled__help'), verbose_name=_('Conference__send_pn_disabled'))
+    board_disabled = models.BooleanField(default=False, help_text=_('Conference__board_disabled__help'), verbose_name=_('Conference__board_disabled'))
+    require_login = models.BooleanField(default=False, help_text=_('Conference--require_login--help'), verbose_name=_('Conference--require_login'))
+    require_ticket = models.BooleanField(default=False, help_text=_('Conference--require_ticket--help'), verbose_name=_('Conference--require_ticket'))
 
-    support_clusters = models.BooleanField(
-        default=True,
-        help_text=_('Conference__support_clusters__help'),
-        verbose_name=_('Conference__support_clusters'))
+    support_clusters = models.BooleanField(default=True, help_text=_('Conference__support_clusters__help'), verbose_name=_('Conference__support_clusters'))
     additional_fields_schema = models.JSONField(
-        blank=True, null=True, encoder=PrettyJSONEncoder,
+        blank=True,
+        null=True,
+        encoder=PrettyJSONEncoder,
         help_text=_('Conference__additional_fields_schema__help'),
-        verbose_name=_('Conference__additional_fields_schema'))
+        verbose_name=_('Conference__additional_fields_schema'),
+    )
 
     disclaimers = models.JSONField(
-        blank=True, null=True, encoder=PrettyJSONEncoder,
-        help_text=_('Conference__disclaimers__help'),
-        verbose_name=_('Conference__disclaimers'))
+        blank=True, null=True, encoder=PrettyJSONEncoder, help_text=_('Conference__disclaimers__help'), verbose_name=_('Conference__disclaimers')
+    )
 
     self_organized_sessions_assembly = models.ForeignKey(
         'Assembly',
-        blank=True, null=True,
+        blank=True,
+        null=True,
         on_delete=models.PROTECT,
         related_name='+',
         help_text=_('Conference__self_organized_sessions_assembly__help'),
-        verbose_name=_('Conference__self_organized_sessions_assembly'))
+        verbose_name=_('Conference__self_organized_sessions_assembly'),
+    )
     """
     Benutzer, die eine Self-Organized-Session (SOS) anlegen wollen,
     können dies entweder in ihrer Assembly tun, in denen sie "Member" sind oder aber in dieser hier.
@@ -305,13 +258,16 @@ class Conference(models.Model):
     )
 
     mail_footer = models.TextField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('Conference__mail_footer__help'),
         verbose_name=_('Conference__mail_footer'),
     )
 
     map_config = models.JSONField(
-        blank=True, null=True, encoder=PrettyJSONEncoder,
+        blank=True,
+        null=True,
+        encoder=PrettyJSONEncoder,
         help_text=_('Conference__map_config__help'),
         verbose_name=_('Conference__map_config'),
     )
@@ -330,11 +286,11 @@ class Conference(models.Model):
                 parsed = ET.fromstring(self.logo)
                 assert parsed.tag in ['svg', '{http://www.w3.org/2000/svg}svg'], 'Not a SVG document.'
 
-                assert 'width' not in parsed.keys() and 'height' not in parsed.keys(), 'SVG has width and/or height attribute.'
+                assert 'width' not in parsed and 'height' not in parsed, 'SVG has width and/or height attribute.'
 
                 xml_start = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
                 if self.logo.startswith(xml_start):
-                    self.logo = self.logo[len(xml_start):].lstrip()
+                    self.logo = self.logo[len(xml_start) :].lstrip()
 
             except ET.ParseError:
                 errors['logo'] = 'SVG (XML) parse error'
@@ -466,27 +422,33 @@ class Conference(models.Model):
         # iterate over the categories (parent entries)
         for parent in self.nav_items.filter(parent=None, is_visible=True).order_by('index'):
             # filter children for this parent entry (they're already sorted) and resolve the URL, if necessary
-            this_children = [{
-                'label': c.label,
-                'title': c.title,
-                'icon': c.icon,
-                'url': c.resolve_url(),
-            } for c in all_children if c.parent_id == parent.id]
+            this_children = [
+                {
+                    'label': c.label,
+                    'title': c.title,
+                    'icon': c.icon,
+                    'url': c.resolve_url(),
+                }
+                for c in all_children
+                if c.parent_id == parent.id
+            ]
 
             # append parent entry, reformatted as dict
-            result.append({
-                'label': parent.label,
-                'title': parent.title,
-                'icon': parent.icon,
-                'children': this_children,
-            })
+            result.append(
+                {
+                    'label': parent.label,
+                    'title': parent.title,
+                    'icon': parent.icon,
+                    'children': this_children,
+                }
+            )
 
         return result
 
     @cached_property
     def logo_html(self):
         if not self.logo:
-            with settings.BASE_DIR.joinpath("hub", "logos", "default.svg").open() as default_logo:
+            with settings.BASE_DIR.joinpath('hub', 'logos', 'default.svg').open() as default_logo:
                 default_logo_content = default_logo.read()
             assert default_logo_content.startswith('<svg'), 'Expected default logo to start with SVG tag.'
             return mark_safe(default_logo_content)
@@ -506,6 +468,7 @@ class Conference(models.Model):
             return None
 
         from ..markdown import render_markdown
+
         return format_html(
             '<div id="footer" style="margin-top: 1em; border-top: 2px solid #999; color: #999; font-size: 80%;">{0}</div>',
             render_markdown(conf=self, markup=self.mail_footer, dont_derefer_allowlist=True),
@@ -558,25 +521,19 @@ class ConferenceTrack(models.Model):
     objects = ConferenceTrackManager()
 
     conference = ConferenceReference(related_name='tracks')
-    slug = models.SlugField(
-        help_text=_('ConferenceTrack__slug__help'),
-        verbose_name=_('ConferenceTrack__slug'))
-    name = models.CharField(
-        max_length=200,
-        help_text=_('ConferenceTrack__name__help'),
-        verbose_name=_('ConferenceTrack__name'))
-    is_public = models.BooleanField(
-        default=True,
-        help_text=_('ConferenceTrack__is_public__help'),
-        verbose_name=_('ConferenceTrack__is_public'))
+    slug = models.SlugField(help_text=_('ConferenceTrack__slug__help'), verbose_name=_('ConferenceTrack__slug'))
+    name = models.CharField(max_length=200, help_text=_('ConferenceTrack__name__help'), verbose_name=_('ConferenceTrack__name'))
+    is_public = models.BooleanField(default=True, help_text=_('ConferenceTrack__is_public__help'), verbose_name=_('ConferenceTrack__is_public'))
     banner_image_height = models.PositiveIntegerField(blank=True, null=True)
     banner_image_width = models.PositiveIntegerField(blank=True, null=True)
     banner_image = models.ImageField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         height_field='banner_image_height',
         width_field='banner_image_width',
         help_text=_('ConferenceTrack__banner_image__help'),
-        verbose_name=_('ConferenceTrack__banner_image'))
+        verbose_name=_('ConferenceTrack__banner_image'),
+    )
 
     last_update = models.DateTimeField(auto_now=True)
 
@@ -610,7 +567,7 @@ class ConferenceExportCache(models.Model):
     last_generated = models.DateTimeField(blank=True, null=True)
     data = models.TextField(blank=True, null=True)
 
-    class Meta(object):
+    class Meta:
         unique_together = [
             ('conference', 'ident'),
         ]
@@ -632,24 +589,27 @@ class ConferenceExportCache(models.Model):
             last_room = Room.objects.filter(conference=self.conference).aggregate(Max('last_update'))['last_update__max']
 
             # if one of those is greater than our last generation, we need to update
-            if (last_track is not None and last_track > self.last_generated) or \
-               (last_event is not None and last_event > self.last_generated) or \
-               (last_room is not None and last_room > self.last_generated):
+            if (
+                (last_track is not None and last_track > self.last_generated)
+                or (last_event is not None and last_event > self.last_generated)
+                or (last_room is not None and last_room > self.last_generated)
+            ):
                 return True
 
         elif self.type == self.Type.MAP:
             from .assemblies import Assembly
+
             last_assemblies = Assembly.objects.filter(conference=self.conference).aggregate(Max('last_update_staff'))['last_update_staff__max']
             if last_assemblies is not None and last_assemblies > self.last_generated:
                 return True
 
         elif self.type == self.Type.ASSEMBLIES:
             from .assemblies import Assembly
+
             last_updates = Assembly.objects.filter(conference=self.conference).aggregate(Max('last_update_assembly'), Max('last_update_staff'))
             last_assemblies = last_updates['last_update_assembly__max']
             last_staff = last_updates['last_update_staff__max']
-            if (last_assemblies is not None and last_assemblies > self.last_generated) or \
-               (last_staff is not None and last_staff > self.last_generated):
+            if (last_assemblies is not None and last_assemblies > self.last_generated) or (last_staff is not None and last_staff > self.last_generated):
                 return True
 
         else:
@@ -668,7 +628,9 @@ class ConferenceExportCache(models.Model):
     @classmethod
     def _get_or_create_entry(cls, conference: Conference, type: Type, ident: str, result_func: typing.Callable[..., bytes]):
         cache_entry, _ = cls.objects.get_or_create(
-            conference=conference, type=type, ident=ident,
+            conference=conference,
+            type=type,
+            ident=ident,
             defaults={'needs_regeneration': True},
         )
         if cache_entry.is_dirty or settings.DEBUG:
@@ -685,9 +647,7 @@ class ConferenceExportCache(models.Model):
         return content.decode('utf-8') if as_text else content
 
     @classmethod
-    def handle_http_request(cls, request: HttpRequest,
-                            conference: Conference, type: Type, ident: str,
-                            content_type, result_func):
+    def handle_http_request(cls, request: HttpRequest, conference: Conference, type: Type, ident: str, content_type, result_func):
         cache_entry = cls._get_or_create_entry(conference, type, ident, result_func)
 
         headers = {
@@ -760,36 +720,48 @@ class ConferenceNavigationItem(models.Model):
 
     conference = ConferenceReference(related_name='nav_items')
     parent = models.ForeignKey(
-        'self', related_name='children', on_delete=models.CASCADE,
-        blank=True, null=True,
-        help_text=_('ConferenceNavigationItem__parent__help'), verbose_name=_('ConferenceNavigationItem__parent'),
+        'self',
+        related_name='children',
+        on_delete=models.CASCADE,
+        blank=True,
+        null=True,
+        help_text=_('ConferenceNavigationItem__parent__help'),
+        verbose_name=_('ConferenceNavigationItem__parent'),
     )
 
     is_visible = models.BooleanField(
         default=True,
-        help_text=_('ConferenceNavigationItem__is_visible__help'), verbose_name=_('ConferenceNavigationItem__is_visible'),
+        help_text=_('ConferenceNavigationItem__is_visible__help'),
+        verbose_name=_('ConferenceNavigationItem__is_visible'),
     )
     index = models.IntegerField(
-        help_text=_('ConferenceNavigationItem__index__help'), verbose_name=_('ConferenceNavigationItem__index'),
+        help_text=_('ConferenceNavigationItem__index__help'),
+        verbose_name=_('ConferenceNavigationItem__index'),
     )
     icon = models.CharField(
         max_length=50,
-        blank=True, null=True,
-        help_text=_('ConferenceNavigationItem__icon__help'), verbose_name=_('ConferenceNavigationItem__icon'),
+        blank=True,
+        null=True,
+        help_text=_('ConferenceNavigationItem__icon__help'),
+        verbose_name=_('ConferenceNavigationItem__icon'),
     )
 
     label = models.CharField(
         max_length=50,
-        help_text=_('ConferenceNavigationItem__label__help'), verbose_name=_('ConferenceNavigationItem__label'),
+        help_text=_('ConferenceNavigationItem__label__help'),
+        verbose_name=_('ConferenceNavigationItem__label'),
     )
     title = models.CharField(
         max_length=200,
-        help_text=_('ConferenceNavigationItem__title__help'), verbose_name=_('ConferenceNavigationItem__title'),
+        help_text=_('ConferenceNavigationItem__title__help'),
+        verbose_name=_('ConferenceNavigationItem__title'),
     )
     url = models.CharField(
         max_length=255,
-        blank=True, validators=[validate_conferencenavigationitem_url],
-        help_text=_('ConferenceNavigationItem__url__help'), verbose_name=_('ConferenceNavigationItem__url'),
+        blank=True,
+        validators=[validate_conferencenavigationitem_url],
+        help_text=_('ConferenceNavigationItem__url__help'),
+        verbose_name=_('ConferenceNavigationItem__url'),
     )
 
     def resolve_url(self):
diff --git a/src/core/models/dereferrer.py b/src/core/models/dereferrer.py
index a5f01ad1b9f8caed75304768b221d524c2d84b94..eb9d80e93f9fe2f66bb4271209bbc34b65037900 100644
--- a/src/core/models/dereferrer.py
+++ b/src/core/models/dereferrer.py
@@ -1,4 +1,5 @@
 from django.db import models
+
 from core.models.users import PlatformUser
 
 
diff --git a/src/core/models/events.py b/src/core/models/events.py
index daf213f33776ee42372e8612e397529a34cebc7d..279a3674a91b1547e0664064aa7d4fdad309834c 100644
--- a/src/core/models/events.py
+++ b/src/core/models/events.py
@@ -5,6 +5,8 @@ from re import compile
 from typing import Any
 from uuid import UUID, uuid4
 
+from ordered_set import OrderedSet
+
 from django.conf import settings
 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
 from django.db import models
@@ -16,7 +18,6 @@ from django.utils.functional import cached_property
 from django.utils.text import slugify
 from django.utils.timezone import is_aware, make_aware
 from django.utils.translation import gettext_lazy as _
-from ordered_set import OrderedSet
 
 from ..fields import ConferenceReference
 from ..markdown import compile_translated_markdown_fields, store_relationships
@@ -82,12 +83,15 @@ class EventManager(models.Manager):
         return qs.filter(is_public=True)
 
     def conference_accessible(self, conference: Conference):
-        return self.get_queryset().filter(conference=conference, is_public=True) \
+        return (
+            self.get_queryset()
+            .filter(conference=conference, is_public=True)
             .filter(
                 Q(assembly__state_assembly__in=Assembly.PUBLIC_STATES)
                 | Q(assembly__state_channel__in=Assembly.PUBLIC_STATES)
                 | Q(kind=Event.Kind.SELF_ORGANIZED)
             )
+        )
 
     def manageable_by_user(self, user: PlatformUser, conference: Conference):
         assert user is not None
@@ -112,8 +116,9 @@ class EventManager(models.Manager):
             pass
 
         # for everybody else, only show public events
-        manageable_assemblies_qs = Assembly.objects.manageable_by_user(conference=conference, user=user) \
-            .filter(members__member=user, members__can_manage_assembly=True)
+        manageable_assemblies_qs = Assembly.objects.manageable_by_user(conference=conference, user=user).filter(
+            members__member=user, members__can_manage_assembly=True
+        )
         return qs.filter(Q(kind=Event.Kind.SELF_ORGANIZED, owner=user) | Q(assembly_id__in=manageable_assemblies_qs.values_list('id', flat=True)))
 
 
@@ -129,90 +134,58 @@ class Event(TaggedItemMixin, BackendMixin, models.Model):
     class Kind(models.TextChoices):
         OFFICIAL = 'official', _('Event__kind-official')  # offizielles Event
         ASSEMBLY = 'assembly', _('Event__kind-assembly')  # von einer Assembly kuratiert
-        SELF_ORGANIZED = 'sos', _('Event__kind-sos')      # hat Einzelperson angelegt
+        SELF_ORGANIZED = 'sos', _('Event__kind-sos')  # hat Einzelperson angelegt
 
     objects = EventManager()
 
     id = models.UUIDField(default=uuid4, primary_key=True, editable=False)
     conference = ConferenceReference(related_name='events')
     # Unique together with conference
-    slug = models.SlugField(
-        max_length=150,
-        help_text=_('Event__slug__help'),
-        verbose_name=_('Event__slug'))
-    kind = models.CharField(
-        max_length=10,
-        choices=Kind.choices,
-        default=Kind.ASSEMBLY,
-        help_text=_('Event__kind__help'),
-        verbose_name=_('Event__kind'))
-
-    name = models.CharField(
-        max_length=200)
+    slug = models.SlugField(max_length=150, help_text=_('Event__slug__help'), verbose_name=_('Event__slug'))
+    kind = models.CharField(max_length=10, choices=Kind.choices, default=Kind.ASSEMBLY, help_text=_('Event__kind__help'), verbose_name=_('Event__kind'))
+
+    name = models.CharField(max_length=200)
     track = models.ForeignKey(ConferenceTrack, blank=True, null=True, on_delete=models.PROTECT)
     assembly = models.ForeignKey(Assembly, related_name='events', on_delete=models.PROTECT, blank=True, null=True)
     room = models.ForeignKey(Room, blank=True, null=True, related_name='events', on_delete=models.PROTECT)
 
-    language = models.CharField(
-        max_length=50, blank=True, null=True,
-        help_text=_('Event__language__help'),
-        verbose_name=_('Event__language'))
-    abstract = models.TextField(
-        blank=True,
-        help_text=_('Event__abstract__help'),
-        verbose_name=_('Event__abstract'))
-    description = models.TextField(
-        blank=True,
-        help_text=_('Event__description__help'),
-        verbose_name=_('Event__description'))
+    language = models.CharField(max_length=50, blank=True, null=True, help_text=_('Event__language__help'), verbose_name=_('Event__language'))
+    abstract = models.TextField(blank=True, help_text=_('Event__abstract__help'), verbose_name=_('Event__abstract'))
+    description = models.TextField(blank=True, help_text=_('Event__description__help'), verbose_name=_('Event__description'))
     description_html = models.TextField(blank=True)
     banner_image_height = models.PositiveIntegerField(blank=True, null=True)
     banner_image_width = models.PositiveIntegerField(blank=True, null=True)
     banner_image = models.ImageField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         height_field='banner_image_height',
         width_field='banner_image_width',
         help_text=_('Event__banner_image__help'),
-        verbose_name=_('Event__banner_image'))
-
-    is_public = models.BooleanField(
-        default=False,
-        help_text=_('Event__is_public__help'),
-        verbose_name=_('Event__is_public'))
-    schedule_start = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('Event__schedule_start__help'),
-        verbose_name=_('Event__schedule_start'))
-    schedule_duration = EventDurationField(
-        blank=True, null=True,
-        help_text=_('Event__schedule_duration__help'),
-        verbose_name=_('Event__schedule_duration'))
-    schedule_end = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('Event__schedule_end__help'),
-        verbose_name=_('Event__schedule_end'))
+        verbose_name=_('Event__banner_image'),
+    )
+
+    is_public = models.BooleanField(default=False, help_text=_('Event__is_public__help'), verbose_name=_('Event__is_public'))
+    schedule_start = models.DateTimeField(blank=True, null=True, help_text=_('Event__schedule_start__help'), verbose_name=_('Event__schedule_start'))
+    schedule_duration = EventDurationField(blank=True, null=True, help_text=_('Event__schedule_duration__help'), verbose_name=_('Event__schedule_duration'))
+    schedule_end = models.DateTimeField(blank=True, null=True, help_text=_('Event__schedule_end__help'), verbose_name=_('Event__schedule_end'))
 
     additional_data = models.JSONField(blank=True, null=True)
     favorited_by = models.ManyToManyField(
-        PlatformUser,
-        related_name='favorite_events',
-        help_text=_('Event__favorited_by__help'),
-        verbose_name=_('Event__favorited_by')
+        PlatformUser, related_name='favorite_events', help_text=_('Event__favorited_by__help'), verbose_name=_('Event__favorited_by')
     )
     in_personal_calendar = models.ManyToManyField(
-        PlatformUser,
-        related_name='calendar_events',
-        help_text=_('Event__in_personal_calendar__help'),
-        verbose_name=_('Event__in_personal_calendar')
+        PlatformUser, related_name='calendar_events', help_text=_('Event__in_personal_calendar__help'), verbose_name=_('Event__in_personal_calendar')
     )
 
     owner = models.ForeignKey(
         settings.AUTH_USER_MODEL,
-        blank=True, null=True,
+        blank=True,
+        null=True,
         related_name='owned_events',
         on_delete=models.PROTECT,
         help_text=_('Event__owner__help'),
-        verbose_name=_('Event__owner'))
+        verbose_name=_('Event__owner'),
+    )
     """
     Ersteller/Eigentümer des Events, dies ist insb. für Self-Organized-Sessions relevant.
     """
@@ -286,9 +259,17 @@ class Event(TaggedItemMixin, BackendMixin, models.Model):
         return self.name
 
     @classmethod
-    def from_dict(cls, data: dict, conference: Conference, assembly: Assembly = None, existing=None, pop_used_keys: bool = False,
-                  allow_kind: bool = False, allow_track: bool = False,
-                  room_lookup=None):
+    def from_dict(
+        cls,
+        data: dict,
+        conference: Conference,
+        assembly: Assembly = None,
+        existing=None,
+        pop_used_keys: bool = False,
+        allow_kind: bool = False,
+        allow_track: bool = False,
+        room_lookup=None,
+    ):
         """
         Loads an Event instance from the given dictionary.
         An existing event can be provided which's data is overwritten (in parts).
@@ -312,11 +293,11 @@ class Event(TaggedItemMixin, BackendMixin, models.Model):
         if existing:
             obj = existing
             if assembly:
-                assert obj.assembly == assembly, 'Existing event\'s assembly does not match given one.'
+                assert obj.assembly == assembly, "Existing event's assembly does not match given one."
             if conference:
-                assert obj.conference == conference, 'Existing event\'s conference does not match given one.'
+                assert obj.conference == conference, "Existing event's conference does not match given one."
             if given_uuid is not None:
-                assert obj.pk == given_uuid, f'expected existing event\'s id {obj.pk} to match the given uuid {given_uuid}'
+                assert obj.pk == given_uuid, f"expected existing event's id {obj.pk} to match the given uuid {given_uuid}"
         else:
             obj = cls(assembly=assembly, conference=conference)
             if given_uuid is not None:
@@ -324,11 +305,14 @@ class Event(TaggedItemMixin, BackendMixin, models.Model):
         assert obj.conference_id is not None
 
         direct_fields = [
-            'slug', 'name',
-            'abstract', 'description',
+            'slug',
+            'name',
+            'abstract',
+            'description',
             'language',
             'is_public',
-            'schedule_start', 'schedule_end',
+            'schedule_start',
+            'schedule_end',
         ]
         bool_fields = ['is_public']
         dt_fields = ['schedule_start', 'schedule_end']
@@ -444,7 +428,7 @@ class Event(TaggedItemMixin, BackendMixin, models.Model):
         # When setting to public we must have a duration
         if (test_for_publication or self.is_public) and not self.schedule_duration:
             publication_errors['schedule_duration'] = errors['schedule_duration'] = ValidationError(
-               _('Event__schedule_duration__non_empty %(event_type)s') % {'event_type': event_type}, code='empty'
+                _('Event__schedule_duration__non_empty %(event_type)s') % {'event_type': event_type}, code='empty'
             )
         if self.schedule_duration is not None:
             # duration must not be zero
@@ -516,7 +500,7 @@ class Event(TaggedItemMixin, BackendMixin, models.Model):
 
 def _EventAttachment_upload_path(instance, filename):
     # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
-    return 'eventattachment/{0}/{1}'.format(instance.event.id, instance.id)
+    return f'eventattachment/{instance.event.id}/{instance.id}'
 
 
 class EventAttachment(models.Model):
@@ -553,27 +537,18 @@ class EventParticipant(models.Model):
     participant = models.ForeignKey(PlatformUser, on_delete=models.CASCADE, related_name='events')
 
     role = models.CharField(
-        max_length=20, choices=Role.choices, default=Role.REGULAR,
-        help_text=_('EventParticipant__role__help'),
-        verbose_name=_('EventParticipant__role'))
-
-    is_accepted = models.BooleanField(
-        default=False,
-        help_text=_('EventParticipant__is_accepted__help'),
-        verbose_name=_('EventParticipant__is_accepted'))
-    is_public = models.BooleanField(
-        default=False,
-        help_text=_('EventParticipant__is_public__help'),
-        verbose_name=_('EventParticipant__is_public'))
+        max_length=20, choices=Role.choices, default=Role.REGULAR, help_text=_('EventParticipant__role__help'), verbose_name=_('EventParticipant__role')
+    )
+
+    is_accepted = models.BooleanField(default=False, help_text=_('EventParticipant__is_accepted__help'), verbose_name=_('EventParticipant__is_accepted'))
+    is_public = models.BooleanField(default=False, help_text=_('EventParticipant__is_public__help'), verbose_name=_('EventParticipant__is_public'))
 
     public_description = models.TextField(
-        blank=True, null=True,
-        help_text=_('EventParticipant__public_description__help'),
-        verbose_name=_('EventParticipant__public_description'))
+        blank=True, null=True, help_text=_('EventParticipant__public_description__help'), verbose_name=_('EventParticipant__public_description')
+    )
     personal_comment = models.TextField(
-        blank=True, null=True,
-        help_text=_('EventParticipant__personal_comment__help'),
-        verbose_name=_('EventParticipant__personal_comment'))
+        blank=True, null=True, help_text=_('EventParticipant__personal_comment__help'), verbose_name=_('EventParticipant__personal_comment')
+    )
 
     def clean(self, *args, **kwargs):
         # verify that the participant is a member of the event's conference
@@ -591,10 +566,9 @@ class EventParticipant(models.Model):
 
 class EventLikeCount(models.Model):
     class Meta:
-        indexes = [
-            models.Index(fields=['event1', 'like_ratio'])
-        ]
+        indexes = [models.Index(fields=['event1', 'like_ratio'])]
         unique_together = [('event1', 'event2')]
+
     event1 = models.ForeignKey(Event, related_name='suggestions', on_delete=models.CASCADE)
     event2 = models.ForeignKey(Event, related_name='+', on_delete=models.CASCADE)
     likes = models.IntegerField()
diff --git a/src/core/models/map.py b/src/core/models/map.py
index 8200d4d9606b1223dbf30ac402d0ca95ce74f67f..aa991af12997e4a00233dc6cd8c6ac14045cc786 100644
--- a/src/core/models/map.py
+++ b/src/core/models/map.py
@@ -10,7 +10,6 @@ from django.utils.translation import gettext_lazy as _
 from ..fields import ConferenceReference
 from ..markdown import compile_translated_markdown_fields, store_relationships
 
-
 logger = logging.getLogger(__name__)
 
 
@@ -42,37 +41,27 @@ class MapPOI(models.Model):
     id = models.UUIDField(default=uuid4, primary_key=True, editable=False, serialize=False)
     conference = ConferenceReference(related_name='pois')
 
-    visible = models.BooleanField(
-        default=True,
-        help_text=_('MapPOI__visible__help'),
-        verbose_name=_('MapPOI__visible'))
+    visible = models.BooleanField(default=True, help_text=_('MapPOI__visible__help'), verbose_name=_('MapPOI__visible'))
 
-    name = models.CharField(
-        max_length=200,
-        unique=True,
-        help_text=_('MapPOI__name__help'),
-        verbose_name=_('MapPOI__name'))
-    description = models.TextField(
-        blank=True, null=True,
-        help_text=_('MapPOI__description__help'),
-        verbose_name=_('MapPOI__description'))
+    name = models.CharField(max_length=200, unique=True, help_text=_('MapPOI__name__help'), verbose_name=_('MapPOI__name'))
+    description = models.TextField(blank=True, null=True, help_text=_('MapPOI__description__help'), verbose_name=_('MapPOI__description'))
     description_html = models.TextField(blank=True)
 
-    is_official = models.BooleanField(
-        default=False,
-        help_text=_('MapPOI__is_official__help'),
-        verbose_name=_('MapPOI__is_official'))
+    is_official = models.BooleanField(default=False, help_text=_('MapPOI__is_official__help'), verbose_name=_('MapPOI__is_official'))
 
     location_floor = models.ForeignKey(
         MapFloor,
-        blank=True, null=True,
-        related_name='pois', on_delete=models.PROTECT,
+        blank=True,
+        null=True,
+        related_name='pois',
+        on_delete=models.PROTECT,
         help_text=_('MapPOI__location_floor__help'),
         verbose_name=_('MapPOI__location_floor'),
     )
 
     location_point = gis_models.PointField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('MapPOI__location_point__help'),
         verbose_name=_('MapPOI__location_point'),
     )
diff --git a/src/core/models/messages.py b/src/core/models/messages.py
index ed79b3cbf9bdad854d8dfda72f02d4c744adf85e..08315dce1b48ebe5c536ab0b3ad1b01c45d1447f 100644
--- a/src/core/models/messages.py
+++ b/src/core/models/messages.py
@@ -1,8 +1,9 @@
 from uuid import uuid4
+
 from django.conf import settings
 from django.db import models
 from django.template.loader import render_to_string
-from django.urls import reverse, NoReverseMatch
+from django.urls import NoReverseMatch, reverse
 from django.utils.text import format_lazy
 from django.utils.translation import gettext_lazy as _
 
@@ -18,11 +19,13 @@ class DirectMessage(models.Model):
 
     sender = models.ForeignKey(
         settings.AUTH_USER_MODEL,
-        blank=True, null=True,
+        blank=True,
+        null=True,
         on_delete=models.CASCADE,
         related_name='sent_messages',
         help_text=_('DirectMessage__sender__help'),
-        verbose_name=_('DirectMessage__sender'))
+        verbose_name=_('DirectMessage__sender'),
+    )
     """The sender of this message or None if it is a system message (in which case the conference field is being used)."""
 
     recipient = models.ForeignKey(
@@ -30,65 +33,46 @@ class DirectMessage(models.Model):
         on_delete=models.CASCADE,
         related_name='received_messages',
         help_text=_('DirectMessage__recipient__help'),
-        verbose_name=_('DirectMessage__recipient'))
+        verbose_name=_('DirectMessage__recipient'),
+    )
     """The intended recipient of this message."""
 
-    timestamp = models.DateTimeField(
-        auto_now_add=True,
-        help_text=_('DirectMessage__timestamp__help'),
-        verbose_name=_('DirectMessage__timestamp'))
+    timestamp = models.DateTimeField(auto_now_add=True, help_text=_('DirectMessage__timestamp__help'), verbose_name=_('DirectMessage__timestamp'))
     """Timestamp when the message was sent."""
 
     autodelete_after = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('DirectMessage__autodelete_after__help'),
-        verbose_name=_('DirectMessage__autodelete_after'))
+        blank=True, null=True, help_text=_('DirectMessage__autodelete_after__help'), verbose_name=_('DirectMessage__autodelete_after')
+    )
     """Automatic purging deletes this message after this timestamp."""
 
     in_reply_to = models.ForeignKey(
-        'self',
-        blank=True, null=True, on_delete=models.SET_NULL,
-        help_text=_('DirectMessage__in_reply_to__help'),
-        verbose_name=_('DirectMessage__in_reply_to'))
+        'self', blank=True, null=True, on_delete=models.SET_NULL, help_text=_('DirectMessage__in_reply_to__help'), verbose_name=_('DirectMessage__in_reply_to')
+    )
     """Original message to which this is a reply."""
 
-    subject = models.CharField(
-        max_length=200,
-        help_text=_('DirectMessage__subject__help'),
-        verbose_name=_('DirectMessage__subject'))
+    subject = models.CharField(max_length=200, help_text=_('DirectMessage__subject__help'), verbose_name=_('DirectMessage__subject'))
     """Subject of the message, might include 'Re:' markers."""
 
-    body = models.TextField(
-        help_text=_('DirectMessage__body__help'),
-        verbose_name=_('DirectMessage__body'))
+    body = models.TextField(help_text=_('DirectMessage__body__help'), verbose_name=_('DirectMessage__body'))
     """Message with (limited) Markdown support."""
 
     deleted_by_sender = models.BooleanField(
-        default=False,
-        help_text=_('DirectMessage__deleted_by_sender__help'),
-        verbose_name=_('DirectMessage__deleted_by_sender'))
+        default=False, help_text=_('DirectMessage__deleted_by_sender__help'), verbose_name=_('DirectMessage__deleted_by_sender')
+    )
 
     deleted_by_recipient = models.BooleanField(
-        default=False,
-        help_text=_('DirectMessage__deleted_by_recipient__help'),
-        verbose_name=_('DirectMessage__deleted_by_recipient'))
-
-    was_read = models.BooleanField(
-        default=False,
-        help_text=_('DirectMessage__was_read__help'),
-        verbose_name=_('DirectMessage__was_read'))
+        default=False, help_text=_('DirectMessage__deleted_by_recipient__help'), verbose_name=_('DirectMessage__deleted_by_recipient')
+    )
+
+    was_read = models.BooleanField(default=False, help_text=_('DirectMessage__was_read__help'), verbose_name=_('DirectMessage__was_read'))
     """Signals whether the recipient has read the message, this is *not* shown to the sender!"""
 
-    has_responded = models.BooleanField(
-        default=False,
-        help_text=_('DirectMessage__has_responded__help'),
-        verbose_name=_('DirectMessage__has_responded'))
+    has_responded = models.BooleanField(default=False, help_text=_('DirectMessage__has_responded__help'), verbose_name=_('DirectMessage__has_responded'))
     """Signals whether the recipient has replied to the message. (speed optimization to circumvent EXISTS query)"""
 
     flagged_for_abuse = models.BooleanField(
-        default=False,
-        help_text=_('DirectMessage__flagged_for_abuse__help'),
-        verbose_name=_('DirectMessage__flagged_for_abuse'))
+        default=False, help_text=_('DirectMessage__flagged_for_abuse__help'), verbose_name=_('DirectMessage__flagged_for_abuse')
+    )
     """Messages by abusive users will be hidden, at least if the messages have not been read yet."""
 
     @property
diff --git a/src/core/models/pages.py b/src/core/models/pages.py
index 61f48a8ea1406896839cb484ab47bf348a392641..69f26f3687323354bfe6835881248a2889a4e695 100644
--- a/src/core/models/pages.py
+++ b/src/core/models/pages.py
@@ -1,7 +1,9 @@
 import logging
 import re
-from typing import Dict, Optional, Tuple
 import uuid
+from typing import Dict, Optional, Tuple
+
+from urllib3.util import Url, parse_url
 
 from django.conf import settings
 from django.contrib.postgres import indexes as pg_indexes
@@ -14,14 +16,13 @@ from django.utils import timezone
 from django.utils.functional import cached_property
 from django.utils.html import strip_tags
 from django.utils.translation import gettext_lazy as _
-from urllib3.util import parse_url, Url
 
 from core.markdown import render_markdown_ex, store_relationships
 
 from ..fields import ConferenceReference
+from ..utils import GitRepo
 from .conference import Conference
 from .users import PlatformUser
-from ..utils import GitRepo
 
 
 class StaticPageNamespace(models.Model):
@@ -45,13 +46,15 @@ class StaticPageNamespace(models.Model):
     )
 
     upstream_url = models.URLField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('StaticPageNamespace__upstream_url__help'),
         verbose_name=_('StaticPageNamespace__upstream_url'),
     )
 
     upstream_image_base_url = models.URLField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('StaticPageNamespace__upstream_image_base_url__help'),
         verbose_name=_('StaticPageNamespace__upstream_image_base_url'),
     )
@@ -68,7 +71,7 @@ class StaticPageNamespace(models.Model):
             metadata_raw, content = markup[4:].split('---\n', maxsplit=1)
             metadata = {}
             for line in metadata_raw.splitlines():
-                k, v = line.split(":", maxsplit=1)
+                k, v = line.split(':', maxsplit=1)
                 metadata[k.strip()] = v.strip()
         else:
             metadata = {}
@@ -85,7 +88,7 @@ class StaticPageNamespace(models.Model):
         RE_MARKDOWN_IMAGE = re.compile(r'!\[(.+?)\]\((.+?)\)')
 
         with GitRepo(url) as repo:
-            docs = repo.get_documents(glob="*.md")
+            docs = repo.get_documents(glob='*.md')
 
             if image_base_url := self.upstream_image_base_url:
                 image_base_url_url = parse_url(image_base_url)
@@ -97,10 +100,10 @@ class StaticPageNamespace(models.Model):
                         # seems to be a valid URL => don't change anything
                         return match.string
 
-                    if image_url.startswith("./"):
+                    if image_url.startswith('./'):
                         image_url = image_url[2:]
 
-                    if not image_url.startswith("/"):
+                    if not image_url.startswith('/'):
                         new_image_url = image_base_url + image_url
                     else:
                         new_image_url = Url(
@@ -111,7 +114,7 @@ class StaticPageNamespace(models.Model):
                             path=image_url,
                         )
 
-                    return f"![{match.group(1)}]({new_image_url})"
+                    return f'![{match.group(1)}]({new_image_url})'
 
                 for doc_name, doc in docs.items():
                     docs[doc_name] = RE_MARKDOWN_IMAGE.sub(derive_image_url, doc)
@@ -120,7 +123,7 @@ class StaticPageNamespace(models.Model):
         obsolete_pages = set(existing_pages.keys())
 
         for doc_filename, document_raw in docs.items():
-            page_slug = self.prefix.lower() + doc_filename.rstrip(".md")
+            page_slug = self.prefix.lower() + doc_filename.rstrip('.md')
             page = existing_pages.get(page_slug)  # type: StaticPage
 
             try:
@@ -149,7 +152,7 @@ class StaticPageNamespace(models.Model):
                     # create new page
                     page = StaticPage.objects.create(conference=self.conference, slug=page_slug)
                     page_rev = page.revisions.create(
-                        title=doc_metadata.get('title') or page_slug[len(self.prefix):].capitalize(),
+                        title=doc_metadata.get('title') or page_slug[len(self.prefix) :].capitalize(),
                         body=document,
                         is_draft=False,
                         author=None,
@@ -165,10 +168,10 @@ class StaticPageNamespace(models.Model):
 
         updated = StaticPage.objects.filter(conference=self.conference).bulk_update(
             objs=(existing_pages[page_slug] for page_slug in obsolete_pages),
-            fields={"privacy": StaticPage.Privacy.PERM},
+            fields={'privacy': StaticPage.Privacy.PERM},
         )
         if updated > 0:
-            logging.info('set %s wiki page(s) "%s" non-public (removed upstream): %s', updated, page_slug, ";".join(obsolete_pages))
+            logging.info('set %s wiki page(s) "%s" non-public (removed upstream): %s', updated, page_slug, ';'.join(obsolete_pages))
 
 
 class StaticPageManager(models.Manager):
@@ -248,21 +251,18 @@ class StaticPageManager(models.Manager):
 
     def conference_accessible(self, conference: Conference, language: str):
         # fetch conference's pages with a public revision and a language set to the given one or no language set
-        return self.get_queryset() \
-            .filter(conference=conference, public_revision__gt=0) \
-            .filter(Q(language=language) | Q(language=None))
+        return self.get_queryset().filter(conference=conference, public_revision__gt=0).filter(Q(language=language) | Q(language=None))
 
 
 class StaticPage(models.Model):
-
     class Protection(models.TextChoices):
-        NONE = 'none', _("StaticPage__protection-none")
-        PERM = 'perm', _("StaticPage__protection-perm")
+        NONE = 'none', _('StaticPage__protection-none')
+        PERM = 'perm', _('StaticPage__protection-perm')
 
     class Privacy(models.TextChoices):
-        NONE = 'none', _("StaticPage__privacy-none")
-        CONFERENCE = 'conf', _("StaticPage__privacy-conf")
-        PERM = 'perm', _("StaticPage__privacy-perm")
+        NONE = 'none', _('StaticPage__privacy-none')
+        CONFERENCE = 'conf', _('StaticPage__privacy-conf')
+        PERM = 'perm', _('StaticPage__privacy-perm')
 
     id = models.UUIDField(default=uuid.uuid4, primary_key=True)
 
@@ -270,12 +270,14 @@ class StaticPage(models.Model):
     slug = models.SlugField()
     language = models.CharField(max_length=20, blank=False, null=True, help_text=_('StaticPage__language__help'), verbose_name=_('StaticPage__language'))
     protection = models.CharField(
-        max_length=20, default=Protection.NONE, choices=Protection.choices,
-        help_text=_('StaticPage__protection__help'), verbose_name=_('StaticPage__protection')
+        max_length=20,
+        default=Protection.NONE,
+        choices=Protection.choices,
+        help_text=_('StaticPage__protection__help'),
+        verbose_name=_('StaticPage__protection'),
     )
     privacy = models.CharField(
-        max_length=20, default=Privacy.NONE, choices=Privacy.choices,
-        help_text=_('StaticPage__privacy__help'), verbose_name=_('StaticPage__privacy')
+        max_length=20, default=Privacy.NONE, choices=Privacy.choices, help_text=_('StaticPage__privacy__help'), verbose_name=_('StaticPage__privacy')
     )
 
     remove_html = models.BooleanField(
@@ -289,22 +291,10 @@ class StaticPage(models.Model):
         verbose_name=_('StaticPage__sanitize_html'),
     )
 
-    public_revision = models.PositiveIntegerField(
-        default=0,
-        help_text=_('StaticPage__public_revision__help'),
-        verbose_name=_('StaticPage__public_revision'))
-    title = models.CharField(
-        max_length=200,
-        help_text=_('StaticPage__title__help'),
-        verbose_name=_('StaticPage__title'))
-    body = models.TextField(
-        default='',
-        help_text=_('StaticPage__body__help'),
-        verbose_name=_('StaticPage__body'))
-    body_html = models.TextField(
-        blank=True, null=True, editable=False,
-        help_text=_('StaticPage__body_html__help'),
-        verbose_name=_('StaticPage__body_html'))
+    public_revision = models.PositiveIntegerField(default=0, help_text=_('StaticPage__public_revision__help'), verbose_name=_('StaticPage__public_revision'))
+    title = models.CharField(max_length=200, help_text=_('StaticPage__title__help'), verbose_name=_('StaticPage__title'))
+    body = models.TextField(default='', help_text=_('StaticPage__body__help'), verbose_name=_('StaticPage__body'))
+    body_html = models.TextField(blank=True, null=True, editable=False, help_text=_('StaticPage__body_html__help'), verbose_name=_('StaticPage__body_html'))
     """HTML of the current public revision"""
     search_content = models.TextField(blank=True, null=True, editable=False)
     """content used for searching (internal use only)"""
@@ -315,7 +305,14 @@ class StaticPage(models.Model):
 
     class Meta:
         constraints = [
-            models.constraints.UniqueConstraint(fields=['conference', 'language', 'slug',], name='unique_conferencestaticpage_slug'),
+            models.constraints.UniqueConstraint(
+                fields=[
+                    'conference',
+                    'language',
+                    'slug',
+                ],
+                name='unique_conferencestaticpage_slug',
+            ),
         ]
         indexes = [
             pg_indexes.GinIndex(fields=['search_vector'], name='staticpage_searchvector'),
@@ -325,7 +322,7 @@ class StaticPage(models.Model):
         verbose_name_plural = _('StaticPages')
 
     def __str__(self):
-        return '%s[%s]' % (self.title, self.language)
+        return f'{self.title}[{self.language}]'
 
     @cached_property
     def newest_revision(self):
@@ -353,13 +350,8 @@ class StaticPageRevision(models.Model):
     page = models.ForeignKey(StaticPage, related_name='revisions', on_delete=models.CASCADE)
     revision = models.AutoField(primary_key=True)
 
-    title = models.CharField(
-        max_length=200,
-        help_text=_('StaticPageRevision__title__help'),
-        verbose_name=_('StaticPageRevision__title'))
-    body = models.TextField(
-        help_text=_('StaticPageRevision__body__help'),
-        verbose_name=_('StaticPageRevision__body'))
+    title = models.CharField(max_length=200, help_text=_('StaticPageRevision__title__help'), verbose_name=_('StaticPageRevision__title'))
+    body = models.TextField(help_text=_('StaticPageRevision__body__help'), verbose_name=_('StaticPageRevision__body'))
 
     # `is_draft` wird momentan nicht mehr wirklich verwendet.
     # Um das wieder sinnvoll zu nutzen sollte eine Möglichkeit geschaffen zu werden, drafts
@@ -367,20 +359,17 @@ class StaticPageRevision(models.Model):
     # werden. Und dann eine Möglichkeit einen draft zu veröffentlichen.
     # das Veröffentlichen sollte jedoch eine neue Revision erzeugen,
     # so dass Revisionen, die als `is_draft` markiert sind, dauerhaft `is_draft` bleiben.
-    is_draft = models.BooleanField(
-        default=False,
-        help_text=_('StaticPageRevision__is_draft__help'),
-        verbose_name=_('StaticPageRevision__is_draft'))
+    is_draft = models.BooleanField(default=False, help_text=_('StaticPageRevision__is_draft__help'), verbose_name=_('StaticPageRevision__is_draft'))
 
     author = models.ForeignKey(
         settings.AUTH_USER_MODEL,
-        blank=True, null=True, on_delete=models.SET_NULL,
+        blank=True,
+        null=True,
+        on_delete=models.SET_NULL,
         help_text=_('StaticPageRevision__author__help'),
-        verbose_name=_('StaticPageRevision__author'))
-    timestamp = models.DateTimeField(
-        auto_now_add=True,
-        help_text=_('StaticPageRevision__timestamp__help'),
-        verbose_name=_('StaticPageRevision__timestamp'))
+        verbose_name=_('StaticPageRevision__author'),
+    )
+    timestamp = models.DateTimeField(auto_now_add=True, help_text=_('StaticPageRevision__timestamp__help'), verbose_name=_('StaticPageRevision__timestamp'))
 
     class Meta:
         constraints = [
@@ -402,9 +391,7 @@ class StaticPageRevision(models.Model):
         self.page.title = self.title
         self.page.body = self.body
         render_result = render_markdown_ex(
-            self.page.conference, self.body,
-            allow_embedded_html=not self.page.remove_html,
-            sanitize_html=self.page.sanitize_html
+            self.page.conference, self.body, allow_embedded_html=not self.page.remove_html, sanitize_html=self.page.sanitize_html
         )
         self.page.body_html = render_result.document
         store_relationships(self.page.conference, self.page, render_result)
diff --git a/src/core/models/rooms.py b/src/core/models/rooms.py
index f7fc80da6cd887fee66a2cd88c7c6b2f0032ba66..9bba968c9553d705cee1509b6590aae397df2af2 100644
--- a/src/core/models/rooms.py
+++ b/src/core/models/rooms.py
@@ -7,13 +7,13 @@ from django.db.models import Q
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
 
-from .shared import BackendMixin
 from ..fields import ConferenceReference
+from ..markdown import compile_translated_markdown_fields, store_relationships
 from ..models.conference import Conference, ConferenceMember
 from ..models.users import PlatformUser
 from ..utils import str2bool
-from ..markdown import compile_translated_markdown_fields, store_relationships
 from .assemblies import Assembly
+from .shared import BackendMixin
 
 
 class RoomManager(models.Manager):
@@ -44,8 +44,11 @@ class RoomManager(models.Manager):
         return qs.filter(blocked=False)
 
     def conference_accessible(self, conference: Conference):
-        return self.get_queryset().filter(conference=conference, blocked=False) \
+        return (
+            self.get_queryset()
+            .filter(conference=conference, blocked=False)
             .filter(Q(assembly__state_assembly__in=Assembly.PUBLIC_STATES) | Q(assembly__state_channel__in=Assembly.PUBLIC_STATES))
+        )
 
 
 class Room(BackendMixin, models.Model):
@@ -84,17 +87,14 @@ class Room(BackendMixin, models.Model):
 
     slug = models.SlugField(unique=True)
 
-    description = models.TextField(
-        blank=True, null=True,
-        help_text=_('Room__description__help'),
-        verbose_name=_('Room__description'))
+    description = models.TextField(blank=True, null=True, help_text=_('Room__description__help'), verbose_name=_('Room__description'))
     """Description of the room/project."""
     description_html = models.TextField(blank=True, null=True)
 
     is_public_fahrplan = models.BooleanField(default=False, help_text=_('Room__is_public_fahrplan__help'), verbose_name=_('Room__is_public_fahrplan'))
 
-    is_official = models.BooleanField(default=False, help_text=_("Room__is_official__help"), verbose_name=_("Room__is_official"))
-    official_room_order = models.IntegerField(default=0, help_text=_("Room__official_room_order__help"), verbose_name=_("Room__official_room_order"))
+    is_official = models.BooleanField(default=False, help_text=_('Room__is_official__help'), verbose_name=_('Room__is_official'))
+    official_room_order = models.IntegerField(default=0, help_text=_('Room__official_room_order__help'), verbose_name=_('Room__official_room_order'))
 
     room_type = models.CharField(max_length=20, choices=RoomType.choices, help_text=_('Room__type__help'), verbose_name=_('Room__type'))
     """Style of the room."""
@@ -121,21 +121,20 @@ class Room(BackendMixin, models.Model):
     """Arbitrary data necessary for room operation in the backend - used by the actual integration component (not for users!!!)."""
 
     backend_status = models.CharField(
-        max_length=20, blank=True,
+        max_length=20,
+        blank=True,
         choices=BackendMixin.BackendStatus.choices,
         default=BackendMixin.BackendStatus.NONE,
         help_text=_('Room__backend_status__help'),
-        verbose_name=_('Room__backend_status'))
+        verbose_name=_('Room__backend_status'),
+    )
     """The backend's status information."""
 
     capacity = models.PositiveIntegerField(blank=True, null=True, help_text=_('Room__capacity__help'), verbose_name=_('Room__capacity'))
     """A room's capacity, i.e. maximum of participants."""
 
     reserve_capacity = models.PositiveIntegerField(
-      default=20,
-      help_text=_('Room__reserve_capacity__help'),
-      verbose_name=_('Room__reserve_capacity'),
-      editable=False
+        default=20, help_text=_('Room__reserve_capacity__help'), verbose_name=_('Room__reserve_capacity'), editable=False
     )
     """Number of slots the deployment infrastructure should keep available for this room"""
 
@@ -209,8 +208,9 @@ class Room(BackendMixin, models.Model):
         return super().save(*args, update_fields=update_fields, **kwargs)
 
     @classmethod
-    def from_dict(cls, data: dict, conference: Conference, assembly: Assembly = None, existing=None, pop_used_keys: bool = False,
-                  allow_backend_roomtypes: bool = False):
+    def from_dict(
+        cls, data: dict, conference: Conference, assembly: Assembly = None, existing=None, pop_used_keys: bool = False, allow_backend_roomtypes: bool = False
+    ):
         """
         Loads an Room instance from the given dictionary.
         An existing event can be provided which's data is overwritten (in parts).
@@ -231,10 +231,10 @@ class Room(BackendMixin, models.Model):
         if existing:
             obj = existing
             if assembly:
-                assert obj.assembly == assembly, 'Existing room\'s assembly does not match given one.'
-                assert obj.conference == assembly.conference, 'Existing room\'s conference does not match the given assembly\'s one.'
+                assert obj.assembly == assembly, "Existing room's assembly does not match given one."
+                assert obj.conference == assembly.conference, "Existing room's conference does not match the given assembly's one."
             if conference:
-                assert obj.conference == conference, 'Existing room\'s conference does not match given one.'
+                assert obj.conference == conference, "Existing room's conference does not match given one."
         else:
             obj = cls(assembly=assembly, conference=conference)
         assert obj.conference_id is not None
@@ -291,21 +291,19 @@ class RoomLink(models.Model):
     room = models.ForeignKey(Room, related_name='links', on_delete=models.CASCADE)
     name = models.CharField(max_length=200)
 
-    link_type = models.CharField(
-        max_length=20, choices=LinkType.choices,
-        help_text=_('RoomLink__type__help'),
-        verbose_name=_('RoomLink__type'))
+    link_type = models.CharField(max_length=20, choices=LinkType.choices, help_text=_('RoomLink__type__help'), verbose_name=_('RoomLink__type'))
     link = models.CharField(max_length=255)
 
     conference_internal = models.BooleanField(
-        default=False,
-        help_text=_('RoomLink__conference_internal__help'),
-        verbose_name=_('RoomLink__conference_internal'))
+        default=False, help_text=_('RoomLink__conference_internal__help'), verbose_name=_('RoomLink__conference_internal')
+    )
     send_token = models.CharField(
-        max_length=20, choices=Token.choices,
+        max_length=20,
+        choices=Token.choices,
         default=Token.NONE,
         help_text=_('RoomLink__conference_internal__help'),
-        verbose_name=_('RoomLink__conference_internal'))
+        verbose_name=_('RoomLink__conference_internal'),
+    )
 
     def clean(self):
         if self.link_type in RoomLink.URL_LINKTYPES:
diff --git a/src/core/models/schedules.py b/src/core/models/schedules.py
index 16e39ba21f0aae9608a2a29a55182322ce93a86c..47d404ed783fa159801c46d5c62d3321a0234c4f 100644
--- a/src/core/models/schedules.py
+++ b/src/core/models/schedules.py
@@ -1,19 +1,18 @@
-from datetime import timedelta
 import logging
-from uuid import uuid4, UUID
+from datetime import timedelta
+from uuid import UUID, uuid4
 
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 
-from .assemblies import Assembly
-from .events import Event, EventAttachment, EventParticipant
-from .rooms import Room
 from ..fields import ConferenceReference
 from ..schedules import ScheduleTypeManager
 from ..utils import mask_url, str2bool
-
+from .assemblies import Assembly
+from .events import Event, EventAttachment, EventParticipant
+from .rooms import Room
 
 logger = logging.getLogger(__name__)
 
@@ -33,7 +32,8 @@ class ScheduleSource(models.Model):
     assembly = models.ForeignKey(Assembly, blank=True, null=True, related_name='schedule_sources', on_delete=models.CASCADE)
 
     import_type = models.CharField(
-        blank=False, max_length=20,
+        blank=False,
+        max_length=20,
         help_text=_('ScheduleSource__import_type__help'),
         verbose_name=_('ScheduleSource__import_type'),
     )
@@ -60,7 +60,8 @@ class ScheduleSource(models.Model):
         verbose_name=_('ScheduleSource__import_timeout'),
     )
     last_import = models.DateTimeField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('ScheduleSource__last_import__help'),
         verbose_name=_('ScheduleSource__last_import'),
     )
@@ -199,12 +200,14 @@ class ScheduleSource(models.Model):
                 item.delete()
 
             # log us skipping the entry
-            activity.append({
-                'action': 'skipped',
-                'type': item_type,
-                'source_id': item_source_id,
-                'local_id': None,
-            })
+            activity.append(
+                {
+                    'action': 'skipped',
+                    'type': item_type,
+                    'source_id': item_source_id,
+                    'local_id': None,
+                }
+            )
 
             # if we have the item locally but shall skip it, handle it as 'handled' anyway
             # so that it is not picked up again as e.g. 'missing_events'
@@ -240,13 +243,15 @@ class ScheduleSource(models.Model):
             except Exception as err:
                 # log the error
                 items[item_source_id] = None
-                activity.append({
-                    'action': 'error',
-                    'type': item_type,
-                    'source_id': item_source_id,
-                    'local_id': None,
-                    'message': str(err),
-                })
+                activity.append(
+                    {
+                        'action': 'error',
+                        'type': item_type,
+                        'source_id': item_source_id,
+                        'local_id': None,
+                        'message': str(err),
+                    }
+                )
 
                 # ... and delete the incomplete (wrong) mapping
                 mapping.delete()
@@ -255,12 +260,14 @@ class ScheduleSource(models.Model):
             else:
                 # store new item's data
                 items[item_source_id] = mapping.local_object
-                activity.append({
-                    'action': 'added',
-                    'type': item_type,
-                    'source_id': item_source_id,
-                    'local_id': str(mapping.local_id),
-                })
+                activity.append(
+                    {
+                        'action': 'added',
+                        'type': item_type,
+                        'source_id': item_source_id,
+                        'local_id': str(mapping.local_id),
+                    }
+                )
             return 'added'
 
         # note that we've seen the existing room in the imported data
@@ -273,12 +280,14 @@ class ScheduleSource(models.Model):
         if item_delete:
             mapping.local_object.delete()
             items[item_source_id] = None
-            activity.append({
-                'action': 'deleted',
-                'type': item_type,
-                'source_id': item_source_id,
-                'local_id': str(mapping.local_id),
-            })
+            activity.append(
+                {
+                    'action': 'deleted',
+                    'type': item_type,
+                    'source_id': item_source_id,
+                    'local_id': str(mapping.local_id),
+                }
+            )
             return 'deleted'
 
         # update data on existing room
@@ -297,12 +306,14 @@ class ScheduleSource(models.Model):
         mapping.local_object.save()
 
         items[item_source_id] = mapping.local_object
-        activity.append({
-            'action': 'seen',  # TODO: set to 'changed' if data was changed (returned by .from_dict()?)
-            'type': item_type,
-            'source_id': item_source_id,
-            'local_id': str(mapping.local_id),
-        })
+        activity.append(
+            {
+                'action': 'seen',  # TODO: set to 'changed' if data was changed (returned by .from_dict()?)
+                'type': item_type,
+                'source_id': item_source_id,
+                'local_id': str(mapping.local_id),
+            }
+        )
         return 'seen'
 
     def load_data(self, data):
@@ -324,18 +335,22 @@ class ScheduleSource(models.Model):
         if self.assembly:
             expected_rooms = list(self.assembly.rooms.values_list('id', flat=True))
         else:
-            expected_rooms = list(self.mappings.filter(
-                mapping_type=ScheduleSourceMapping.MappingType.ROOM,
-            ).values_list('local_id', flat=True))
-        expected_events = list(self.mappings.filter(
-            mapping_type=ScheduleSourceMapping.MappingType.EVENT,
-        ).values_list('local_id', flat=True))
+            expected_rooms = list(
+                self.mappings.filter(
+                    mapping_type=ScheduleSourceMapping.MappingType.ROOM,
+                ).values_list('local_id', flat=True)
+            )
+        expected_events = list(
+            self.mappings.filter(
+                mapping_type=ScheduleSourceMapping.MappingType.EVENT,
+            ).values_list('local_id', flat=True)
+        )
 
         # first, load the rooms (as they're needed for events)
         for r_id, r in data['rooms'].items():
             try:
                 if replace_conference_slug_prefix and r.get('slug', '').startswith(replace_conference_slug_prefix):
-                    r['slug'] = self.conference.slug + r['slug'][len(replace_conference_slug_prefix):]
+                    r['slug'] = self.conference.slug + r['slug'][len(replace_conference_slug_prefix) :]
 
                 action = self._load_dataitem(
                     activity=activity,
@@ -356,19 +371,21 @@ class ScheduleSource(models.Model):
                     rooms[r_id] = r_mapping.local_object
 
             except Exception as err:
-                activity.append({
-                    'action': 'error',
-                    'type': 'room',
-                    'source_id': r_id,
-                    'local_id': None,
-                    'message': str(err),
-                })
+                activity.append(
+                    {
+                        'action': 'error',
+                        'type': 'room',
+                        'source_id': r_id,
+                        'local_id': None,
+                        'message': str(err),
+                    }
+                )
 
         # then load events
         for e_id, e in data['events'].items():
             try:
                 if replace_conference_slug_prefix and e.get('slug', '').startswith(replace_conference_slug_prefix):
-                    e['slug'] = self.conference.slug + e['slug'][len(replace_conference_slug_prefix):]
+                    e['slug'] = self.conference.slug + e['slug'][len(replace_conference_slug_prefix) :]
 
                 self._load_dataitem(
                     activity=activity,
@@ -381,25 +398,29 @@ class ScheduleSource(models.Model):
                         'allow_kind': self.assembly.is_official if self.assembly else False,  # TODO: lookup assembly's room if not given
                         'allow_track': False,  # TODO
                         'room_lookup': lambda r_source_id: rooms.get(r_source_id),
-                    }
+                    },
                 )
             except Exception as err:
-                activity.append({
-                    'action': 'error',
-                    'type': 'event',
-                    'source_id': e_id,
-                    'local_id': e.get('uuid', None),
-                    'message': str(err),
-                })
+                activity.append(
+                    {
+                        'action': 'error',
+                        'type': 'event',
+                        'source_id': e_id,
+                        'local_id': e.get('uuid', None),
+                        'message': str(err),
+                    }
+                )
 
         # flag the non-loaded rooms as 'missing'
         for room_id in expected_rooms:
-            activity.append({
-                'action': 'missing',
-                'type': 'room',
-                'source_id': None,
-                'local_id': str(room_id),
-            })
+            activity.append(
+                {
+                    'action': 'missing',
+                    'type': 'room',
+                    'source_id': None,
+                    'local_id': str(room_id),
+                }
+            )
 
         # flag the non-loaded events as 'missing'
         for event_id in expected_events:
@@ -474,9 +495,8 @@ class ScheduleSourceMapping(models.Model):
             if room.assembly_id is not None:
                 if self.schedule_source.assembly_id is not None and room.assembly_id != self.schedule_source.assembly_id:
                     raise LocalObjectAccessViolation('Assembly of Room does not match.')
-            else:
-                if self.schedule_source.assembly_id is not None and room.conference_id != self.schedule_source.assembly.conference_id:
-                    raise LocalObjectAccessViolation('Conference of Room does not match.')
+            elif self.schedule_source.assembly_id is not None and room.conference_id != self.schedule_source.assembly.conference_id:
+                raise LocalObjectAccessViolation('Conference of Room does not match.')
             return room
 
         if self.mapping_type == self.MappingType.EVENT:
@@ -625,15 +645,18 @@ class ScheduleSourceImport(models.Model):
                 # create list of unique errors for summary
                 msgs = list({x['message'].split('\n')[0] for x in errors})
 
-            stats = ', '.join(
-                (t + '=' + str(sum(1 for x in activity if x['action'] == t))) for t in [
-                    'added', 'changed', 'seen', 'deleted', 'missing', 'error', 'skipped'
-                ]
-            ) + ' \n' + ' \n'.join(msgs)
+            stats = (
+                ', '.join(
+                    (t + '=' + str(sum(1 for x in activity if x['action'] == t)))
+                    for t in ['added', 'changed', 'seen', 'deleted', 'missing', 'error', 'skipped']
+                )
+                + ' \n'
+                + ' \n'.join(msgs)
+            )
             self.summary = ('DONE: ' + stats)[:200]
 
             if len(errors) > len(activity) / 2:
-                raise Exception("Too many errors, aborting import: " + stats)
+                raise Exception('Too many errors, aborting import: ' + stats)
 
             self.save(update_fields=['data', 'summary'])
 
diff --git a/src/core/models/shared.py b/src/core/models/shared.py
index 370c977459b0d0ee2a043f121f0056f6250222b2..77d69925df55fca9553719454a82412b1a4070de 100644
--- a/src/core/models/shared.py
+++ b/src/core/models/shared.py
@@ -64,21 +64,20 @@ class BackendMixin(models.Model):
     """Arbitrary data necessary for room operation in the backend - used by the actual integration component (not for users!!!)."""
 
     backend_status = models.CharField(
-        max_length=20, blank=True,
+        max_length=20,
+        blank=True,
         choices=BackendStatus.choices,
         default=BackendStatus.NONE,
         help_text=_('Room__backend_status__help'),
-        verbose_name=_('Room__backend_status'))
+        verbose_name=_('Room__backend_status'),
+    )
     """The backend's status information."""
 
     capacity = models.PositiveIntegerField(blank=True, null=True, help_text=_('Room__capacity__help'), verbose_name=_('Room__capacity'))
     """A room's capacity, i.e. maximum of participants."""
 
     reserve_capacity = models.PositiveIntegerField(
-      default=20,
-      help_text=_('Room__reserve_capacity__help'),
-      verbose_name=_('Room__reserve_capacity'),
-      editable=False
+        default=20, help_text=_('Room__reserve_capacity__help'), verbose_name=_('Room__reserve_capacity'), editable=False
     )
     """Number of slots the deployment infrastructure should keep available for this room"""
 
diff --git a/src/core/models/sso.py b/src/core/models/sso.py
index efa9d5ceb56137fa94107240077e483240e3e988..816e4abcd7de7238d2f6d035fc1bdb145f1ccbec 100644
--- a/src/core/models/sso.py
+++ b/src/core/models/sso.py
@@ -1,6 +1,7 @@
+from oauth2_provider.models import AbstractApplication
+
 from django.db import models
 from django.utils.translation import gettext_lazy as _
-from oauth2_provider.models import AbstractApplication
 
 from .assemblies import Assembly
 
@@ -8,6 +9,5 @@ from .assemblies import Assembly
 class Application(AbstractApplication):
     assembly = models.ForeignKey(Assembly, related_name='applications', blank=True, null=True, on_delete=models.CASCADE)
     internal_description = models.TextField(
-        blank=True, null=True,
-        help_text=_('Application__internal_description__help'),
-        verbose_name=_('Application__internal_description'))
+        blank=True, null=True, help_text=_('Application__internal_description__help'), verbose_name=_('Application__internal_description')
+    )
diff --git a/src/core/models/tags.py b/src/core/models/tags.py
index 15015992dedee70c1a033dd3bd4303194a2251f2..b1655033070b3d49d5f252ebf6efcea382549fc3 100644
--- a/src/core/models/tags.py
+++ b/src/core/models/tags.py
@@ -1,3 +1,5 @@
+import contextlib
+
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
@@ -26,11 +28,9 @@ class ConferenceTag(models.Model):
     is_public = models.BooleanField(default=False, help_text=_('ConferenceTag__is_public__help'), verbose_name=_('ConferenceTag__is_public'))
 
     value_type = models.CharField(
-        max_length=20, choices=Type.choices, default=Type.SIMPLE,
-        help_text=_('ConferenceTag__value_type__help'), verbose_name=_('ConferenceTag__value_type'))
-    description = models.TextField(
-        blank=True, null=True,
-        help_text=_('ConferenceTag__description__help'), verbose_name=_('ConferenceTag__description'))
+        max_length=20, choices=Type.choices, default=Type.SIMPLE, help_text=_('ConferenceTag__value_type__help'), verbose_name=_('ConferenceTag__value_type')
+    )
+    description = models.TextField(blank=True, null=True, help_text=_('ConferenceTag__description__help'), verbose_name=_('ConferenceTag__description'))
 
     def __str__(self):
         return self.slug
@@ -38,9 +38,7 @@ class ConferenceTag(models.Model):
 
 class TagItem(models.Model):
     class Meta:
-        constraints = [
-            models.UniqueConstraint(fields=['target_type', 'target_id', 'tag'], name='unique_target_tag')
-        ]
+        constraints = [models.UniqueConstraint(fields=['target_type', 'target_id', 'tag'], name='unique_target_tag')]
 
     tag = models.ForeignKey(ConferenceTag, related_name='+', on_delete=models.CASCADE)
 
@@ -91,7 +89,7 @@ class TagItem(models.Model):
             return
 
         if self.tag.value_type == ConferenceTag.Type.BOOL:
-            if isinstance(new_value, bool) or isinstance(new_value, int):
+            if isinstance(new_value, (bool, int)):
                 self._value_int = 0 if new_value else 1
                 return
 
@@ -119,20 +117,20 @@ class TagItem(models.Model):
 
             elif self.tag.value_type == ConferenceTag.Type.INT:
                 if not isinstance(self.value, int):
-                    raise ValidationError('Tag\'s value must be integer.')
+                    raise ValidationError("Tag's value must be integer.")
 
             elif self.tag.value_type == ConferenceTag.Type.BOOL:
                 if self.value not in [True, False]:
-                    raise ValidationError('Tag\'s value must be a boolean.')
+                    raise ValidationError("Tag's value must be a boolean.")
 
     def __str__(self):
         if self.tag.value_type == ConferenceTag.Type.SIMPLE:
             return self.tag.slug
 
-        return '{}={}'.format(self.tag.slug, self.value)
+        return f'{self.tag.slug}={self.value}'
 
 
-class TaggedItemMixin(object):
+class TaggedItemMixin:
     @property
     def tags(self):
         qs = TagItem.objects.filter(target_type=ContentType.objects.get_for_model(type(self)), target_id=self.id, tag__is_public=True).select_related('tag')
@@ -164,10 +162,8 @@ class TaggedItemMixin(object):
 
     def remove_tag(self, tag, value=None):
         if not isinstance(tag, ConferenceTag):
-            try:
+            with contextlib.suppress(ConferenceTag.DoesNotExist):
                 tag = ConferenceTag.objects.get(conference=self.conference, slug=tag, is_public=True)
-            except ConferenceTag.DoesNotExist:
-                pass
 
         # TODO: check user's permission
         TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(type(self)), target_id=self.id).delete()
diff --git a/src/core/models/ticket.py b/src/core/models/ticket.py
index f0a7c60a74afc97f0131db4e1a5a65c1b0a49583..e8804d537400a27e0d17865ea51c51e2a3c450ea 100644
--- a/src/core/models/ticket.py
+++ b/src/core/models/ticket.py
@@ -1,16 +1,16 @@
 import logging
 
+import jwt
+
 from django.conf import settings
 from django.db import models
 from django.db.utils import IntegrityError
 from django.utils.translation import gettext_lazy as _
-import jwt
 
 from ..fields import ConferenceReference
 from .conference import Conference
 from .users import PlatformUser
 
-
 logger = logging.getLogger(__name__)
 
 
diff --git a/src/core/models/users.py b/src/core/models/users.py
index 41f979c346ab2c53796984bb1473c8631316313b..f410861e519ae642c02fe9389615f0074b383257 100644
--- a/src/core/models/users.py
+++ b/src/core/models/users.py
@@ -3,7 +3,8 @@ import re
 from typing import Any
 from uuid import uuid4
 
-from django.db.models import Q
+from timezone_field import TimeZoneField
+
 from django.contrib.auth.models import AbstractUser
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.models import ContentType
@@ -12,11 +13,11 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.core.mail import send_mail
 from django.core.validators import validate_email
 from django.db import models
+from django.db.models import Q
 from django.utils.functional import cached_property
+from django.utils.text import slugify
 from django.utils.timezone import now
 from django.utils.translation import gettext_lazy as _
-from django.utils.text import slugify
-from timezone_field import TimeZoneField
 
 from ..fields import ConferenceReference
 
@@ -29,157 +30,103 @@ class PlatformUser(AbstractUser):
         BOT = ('bot', _('PlatformUser__type-bot'))
 
     class Theme(models.TextChoices):
-        DARK = ('dark', _("PlatformUser__theme-dark"))
-        LIGHT = ('light', _("PlatformUser__theme-light"))
+        DARK = ('dark', _('PlatformUser__theme-dark'))
+        LIGHT = ('light', _('PlatformUser__theme-light'))
 
     INTERACTIVE_TYPES = [Type.HUMAN, Type.BOT]
     """User types which can be interactive (and can thus have an avatar and/or modified by e.g. the Engelsystem)."""
 
     user_type = models.CharField(
-        max_length=10, choices=Type.choices, default=Type.HUMAN,
-        help_text=_('PlatformUser__type__help'),
-        verbose_name=_('PlatformUser__type'))
+        max_length=10, choices=Type.choices, default=Type.HUMAN, help_text=_('PlatformUser__type__help'), verbose_name=_('PlatformUser__type')
+    )
 
     uuid = models.UUIDField(default=uuid4, unique=True)
 
     slug = models.SlugField(blank=False, unique=True)
     # legal stuff
     accepted_speakersagreement = models.BooleanField(
-        null=True,
-        help_text=_('PlatformUser__accepted_speakersagreement__help'),
-        verbose_name=_('PlatformUser__accepted_speakersagreement'))
+        null=True, help_text=_('PlatformUser__accepted_speakersagreement__help'), verbose_name=_('PlatformUser__accepted_speakersagreement')
+    )
 
     # self portrayal
-    show_name = models.BooleanField(
-        null=True,
-        help_text=_('PlatformUser__show_name__help'),
-        verbose_name=_('PlatformUser__show_name'))
-    avatar_url = models.URLField(
-        blank=True, null=True,
-        help_text=_('PlatformUser__avatar_url__help'),
-        verbose_name=_('PlatformUser__avatar_url'))
-    avatar_config = models.JSONField(
-        blank=True, null=True,
-        help_text=_('PlatformUser__avatar_config__help'),
-        verbose_name=_('PlatformUser__avatar_config'))
-    pronouns = models.CharField(
-        max_length=50, blank=True, default='',
-        help_text=_('PlatformUser__pronouns__help'),
-        verbose_name=_('PlatformUser__pronouns'))
-
-    status = models.CharField(
-        max_length=50, blank=True, null=True,
-        help_text=_('PlatformUser__status__help'),
-        verbose_name=_('PlatformUser__status'))
-    status_public = models.BooleanField(
-        null=True,
-        help_text=_('PlatformUser__status_public__help'),
-        verbose_name=_('PlatformUser__status_public'))
+    show_name = models.BooleanField(null=True, help_text=_('PlatformUser__show_name__help'), verbose_name=_('PlatformUser__show_name'))
+    avatar_url = models.URLField(blank=True, null=True, help_text=_('PlatformUser__avatar_url__help'), verbose_name=_('PlatformUser__avatar_url'))
+    avatar_config = models.JSONField(blank=True, null=True, help_text=_('PlatformUser__avatar_config__help'), verbose_name=_('PlatformUser__avatar_config'))
+    pronouns = models.CharField(max_length=50, blank=True, default='', help_text=_('PlatformUser__pronouns__help'), verbose_name=_('PlatformUser__pronouns'))
+
+    status = models.CharField(max_length=50, blank=True, null=True, help_text=_('PlatformUser__status__help'), verbose_name=_('PlatformUser__status'))
+    status_public = models.BooleanField(null=True, help_text=_('PlatformUser__status_public__help'), verbose_name=_('PlatformUser__status_public'))
     timezone = TimeZoneField(
         help_text=_('PlatformUser__timezone__help'),
         verbose_name=_('PlatformUser__timezone'),
         default='Europe/Berlin',
-        choices_display="WITH_GMT_OFFSET",
+        choices_display='WITH_GMT_OFFSET',
     )
 
     # Accessibility Options
-    no_animations = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__no_animations__help'),
-        verbose_name=_('PlatformUser__no_animations'))
-    colorblind = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__colorblind__help'),
-        verbose_name=_('PlatformUser__colorblind'))
-    high_contrast = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__high_contrast__help'),
-        verbose_name=_('PlatformUser__high_contrast'))
+    no_animations = models.BooleanField(default=False, help_text=_('PlatformUser__no_animations__help'), verbose_name=_('PlatformUser__no_animations'))
+    colorblind = models.BooleanField(default=False, help_text=_('PlatformUser__colorblind__help'), verbose_name=_('PlatformUser__colorblind'))
+    high_contrast = models.BooleanField(default=False, help_text=_('PlatformUser__high_contrast__help'), verbose_name=_('PlatformUser__high_contrast'))
     tag_ignorelist = pg_fields.ArrayField(
-        models.SlugField(
-        ), blank=True, default=list,
-        help_text=_('PlatformUser__tag_ignorelist__help'),
-        verbose_name=_('PlatformUser__tag_ignorelist'))
+        models.SlugField(), blank=True, default=list, help_text=_('PlatformUser__tag_ignorelist__help'), verbose_name=_('PlatformUser__tag_ignorelist')
+    )
     theme = models.CharField(
-        max_length=50, choices=Theme.choices, default=Theme.DARK,
-        verbose_name=_("PlatformUser__theme"),
-        help_text=_("PlatformUser__theme__help"))
+        max_length=50, choices=Theme.choices, default=Theme.DARK, verbose_name=_('PlatformUser__theme'), help_text=_('PlatformUser__theme__help')
+    )
 
     # Disturbance Settings
-    receive_dms = models.BooleanField(
-        default=True,
-        help_text=_('PlatformUser__receive_dms__help'),
-        verbose_name=_('PlatformUser__receive_dms'))
+    receive_dms = models.BooleanField(default=True, help_text=_('PlatformUser__receive_dms__help'), verbose_name=_('PlatformUser__receive_dms'))
     receive_dm_images = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__receive_dm_images__help'),
-        verbose_name=_('PlatformUser__receive_dm_images'))
-    receive_audio = models.BooleanField(
-        default=True,
-        help_text=_('PlatformUser__receive_audio__help'),
-        verbose_name=_('PlatformUser__receive_audio'))
-    receive_video = models.BooleanField(
-        default=True,
-        help_text=_('PlatformUser__receive_video__help'),
-        verbose_name=_('PlatformUser__receive_video'))
+        default=False, help_text=_('PlatformUser__receive_dm_images__help'), verbose_name=_('PlatformUser__receive_dm_images')
+    )
+    receive_audio = models.BooleanField(default=True, help_text=_('PlatformUser__receive_audio__help'), verbose_name=_('PlatformUser__receive_audio'))
+    receive_video = models.BooleanField(default=True, help_text=_('PlatformUser__receive_video__help'), verbose_name=_('PlatformUser__receive_video'))
 
     # Administrative
-    shadow_banned = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__shadow_banned__help'),
-        verbose_name=_('PlatformUser__shadow_banned'))
+    shadow_banned = models.BooleanField(default=False, help_text=_('PlatformUser__shadow_banned__help'), verbose_name=_('PlatformUser__shadow_banned'))
     admin_notification = models.TextField(
-        default='', blank=True,
-        help_text=_('PlatformUser__admin_notification__help'),
-        verbose_name=_('PlatformUser__admin_notification'))
+        default='', blank=True, help_text=_('PlatformUser__admin_notification__help'), verbose_name=_('PlatformUser__admin_notification')
+    )
 
     audio_muted = models.BooleanField(default=False)
     audio_volume = models.FloatField(blank=True, null=True)
 
     # privacy settings
     autoaccept_contacts = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__autoaccept_contacts__help'),
-        verbose_name=_('PlatformUser__autoaccept_contacts'))
+        default=False, help_text=_('PlatformUser__autoaccept_contacts__help'), verbose_name=_('PlatformUser__autoaccept_contacts')
+    )
 
-    is_searchable = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__is_searchable__help'),
-        verbose_name=_('PlatformUser__is_searchable'))
+    is_searchable = models.BooleanField(default=False, help_text=_('PlatformUser__is_searchable__help'), verbose_name=_('PlatformUser__is_searchable'))
 
     # menu settings
-    menu = models.JSONField(
-        blank=True, default=dict,
-        help_text=_('PlatformUser__menu__help'),
-        verbose_name=_('PlatformUser__menu'))
+    menu = models.JSONField(blank=True, default=dict, help_text=_('PlatformUser__menu__help'), verbose_name=_('PlatformUser__menu'))
 
     # workadventure preferences
     wa_block_external_content = models.BooleanField(
         default=False,
-        help_text=_("PlatformUser__wa_block_external_content__help"),
-        verbose_name=_("PlatformUser__wa_block_external_content"),
+        help_text=_('PlatformUser__wa_block_external_content__help'),
+        verbose_name=_('PlatformUser__wa_block_external_content'),
     )
     wa_block_ambient_sounds = models.BooleanField(
         default=False,
-        help_text=_("PlatformUser__wa_block_ambient_sounds__help"),
-        verbose_name=_("PlatformUser__wa_block_ambient_sounds"),
+        help_text=_('PlatformUser__wa_block_ambient_sounds__help'),
+        verbose_name=_('PlatformUser__wa_block_ambient_sounds'),
     )
     wa_ignore_follow_requests = models.BooleanField(
         default=False,
-        help_text=_("PlatformUser__wa_ignore_follow_requests__help"),
-        verbose_name=_("PlatformUser__wa_ignore_follow_requests"),
+        help_text=_('PlatformUser__wa_ignore_follow_requests__help'),
+        verbose_name=_('PlatformUser__wa_ignore_follow_requests'),
     )
     wa_force_website_trigger = models.BooleanField(
         default=False,
-        help_text=_("PlatformUser__wa_force_website_trigger__help"),
-        verbose_name=_("PlatformUser__wa_force_website_trigger"),
+        help_text=_('PlatformUser__wa_force_website_trigger__help'),
+        verbose_name=_('PlatformUser__wa_force_website_trigger'),
     )
 
     # security settings
     allow_reset_non_primary = models.BooleanField(
-        default=False,
-        help_text=_('PlatformUser__allow_reset_non_primary__help'),
-        verbose_name=_('PlatformUser__allow_reset_non_primary'))
+        default=False, help_text=_('PlatformUser__allow_reset_non_primary__help'), verbose_name=_('PlatformUser__allow_reset_non_primary')
+    )
 
     @classmethod
     def get_user_flags(cls, user):
@@ -194,7 +141,7 @@ class PlatformUser(AbstractUser):
 
     @classmethod
     def get_anonymous_user(cls):
-        class AnonUser(object):
+        class AnonUser:
             def __init__(self):
                 for x in cls._meta.fields:
                     setattr(self, x.name, x.default if x.default != models.fields.NOT_PROVIDED else None)
@@ -206,7 +153,7 @@ class PlatformUser(AbstractUser):
             is_authenticated = False
 
             def save(self, *args, **kwargs):
-                raise Exception()
+                raise Exception
 
         return AnonUser()
 
@@ -231,8 +178,9 @@ class PlatformUser(AbstractUser):
         return info.get('character_layers', [])
 
     def set_character_layers(self, value):
+        # TODO: Replace both asserts with proper checks
         assert isinstance(value, list), 'character layers must be an array'
-        # assert all(isinstance(x, int) for x in value), 'character layers must be an array of integers'
+        # assert all(isinstance(x, int) for x in value), 'character layers must be an array of integers' # noqa: ERA001
 
         info = self.avatar_config or {}
         info['character_layers'] = value
@@ -281,8 +229,11 @@ class PlatformUser(AbstractUser):
 
         if (avatar_custom := update_data.get('custom', _UNSET)) != _UNSET:
             if avatar_custom is not None:
-                assert isinstance(avatar_custom, list) and len(avatar_custom) == CUSTOM_LENGTH and \
-                    all(isinstance(x, int) and MIN_CUSTOM_INDEX <= x <= MAX_CUSTOM_INDEX for x in avatar_custom)
+                assert (
+                    isinstance(avatar_custom, list)
+                    and len(avatar_custom) == CUSTOM_LENGTH
+                    and all(isinstance(x, int) and MIN_CUSTOM_INDEX <= x <= MAX_CUSTOM_INDEX for x in avatar_custom)
+                )
 
             info['custom'] = avatar_custom
 
@@ -318,9 +269,7 @@ class PlatformUser(AbstractUser):
 
     @property
     def guardians(self):
-        return self.contacts.filter(is_guardian=True) \
-            .annotate(pk=models.F('contact__pk'), username=models.F('contact__username')) \
-            .values('pk', 'username')
+        return self.contacts.filter(is_guardian=True).annotate(pk=models.F('contact__pk'), username=models.F('contact__username')).values('pk', 'username')
 
     def get_staticpage_groups(self, conference):
         """Fetches the ConferenceMember's static_page_groups."""
@@ -401,39 +350,28 @@ class PlatformUser(AbstractUser):
         ).exists()
 
     def get_verified_mail_addresses(self):
-        return list(self.communication_channels.filter(
-            channel=UserCommunicationChannel.Channel.MAIL,
-            is_verified=True,
-        ).values_list('address', flat=True))
+        return list(
+            self.communication_channels.filter(
+                channel=UserCommunicationChannel.Channel.MAIL,
+                is_verified=True,
+            ).values_list('address', flat=True)
+        )
 
 
 class UserContact(models.Model):
     user = models.ForeignKey(PlatformUser, related_name='contacts', on_delete=models.CASCADE)
     contact = models.ForeignKey(PlatformUser, related_name='+', on_delete=models.CASCADE)
 
-    pending = models.BooleanField(
-        default=True,
-        help_text=_('UserContact__pending__help'),
-        verbose_name=_('UserContact__pending'))
+    pending = models.BooleanField(default=True, help_text=_('UserContact__pending__help'), verbose_name=_('UserContact__pending'))
 
-    share_status = models.BooleanField(
-        null=True,
-        help_text=_('UserContact__share_status__help'),
-        verbose_name=_('UserContact__share_status'))
+    share_status = models.BooleanField(null=True, help_text=_('UserContact__share_status__help'), verbose_name=_('UserContact__share_status'))
 
-    receive_dms = models.BooleanField(
-        default=True,
-        help_text=_('UserContact__receive_dms__help'),
-        verbose_name=_('UserContact__receive_dms'))
+    receive_dms = models.BooleanField(default=True, help_text=_('UserContact__receive_dms__help'), verbose_name=_('UserContact__receive_dms'))
     receive_dm_images = models.BooleanField(
-        default=False,
-        help_text=_('UserContact__receive_dm_images__help'),
-        verbose_name=_('UserContact__receive_dm_images'))
+        default=False, help_text=_('UserContact__receive_dm_images__help'), verbose_name=_('UserContact__receive_dm_images')
+    )
 
-    is_guardian = models.BooleanField(
-        default=False,
-        help_text=_('UserContact__is_guardian__help'),
-        verbose_name=_('UserContact__is_guardian'))
+    is_guardian = models.BooleanField(default=False, help_text=_('UserContact__is_guardian__help'), verbose_name=_('UserContact__is_guardian'))
 
     @cached_property
     def reverse_contact(self):
@@ -454,9 +392,7 @@ _RE_VANITY = re.compile(r'^(00|\+)49[- ]?(180|[789]00)[- ]?[A-Za-z0-9 -]+$')
 
 class UserCommunicationChannel(models.Model):
     class Meta:
-        constraints = [
-            models.UniqueConstraint(fields=['user', 'channel', 'is_primary'], condition=Q(is_primary=True), name='unique_channel_primary')
-        ]
+        constraints = [models.UniqueConstraint(fields=['user', 'channel', 'is_primary'], condition=Q(is_primary=True), name='unique_channel_primary')]
 
     class Channel(models.TextChoices):
         MAIL = 'mail', _('UserCommunicationChannel__channel-mail')
@@ -469,31 +405,23 @@ class UserCommunicationChannel(models.Model):
     user = models.ForeignKey(PlatformUser, related_name='communication_channels', on_delete=models.CASCADE)
 
     channel = models.CharField(
-        max_length=20, choices=Channel.choices,
-        help_text=_('UserCommunicationChannel__channel__help'),
-        verbose_name=_('UserCommunicationChannel__channel'))
-    address = models.CharField(
-        max_length=255,
-        help_text=_('UserCommunicationChannel__address__help'),
-        verbose_name=_('UserCommunicationChannel__address'))
+        max_length=20, choices=Channel.choices, help_text=_('UserCommunicationChannel__channel__help'), verbose_name=_('UserCommunicationChannel__channel')
+    )
+    address = models.CharField(max_length=255, help_text=_('UserCommunicationChannel__address__help'), verbose_name=_('UserCommunicationChannel__address'))
 
     is_verified = models.BooleanField(
-        default=False,
-        help_text=_('UserCommunicationChannel__is_verified__help'),
-        verbose_name=_('UserCommunicationChannel__is_verified'))
+        default=False, help_text=_('UserCommunicationChannel__is_verified__help'), verbose_name=_('UserCommunicationChannel__is_verified')
+    )
     is_primary = models.BooleanField(
-        default=False,
-        help_text=_('UserCommunicationChannel__is_primary__help'),
-        verbose_name=_('UserCommunicationChannel__is_primary'))
+        default=False, help_text=_('UserCommunicationChannel__is_primary__help'), verbose_name=_('UserCommunicationChannel__is_primary')
+    )
 
     use_for_notifications = models.BooleanField(
-        default=False,
-        help_text=_('UserCommunicationChannel__use_for_notifications__help'),
-        verbose_name=_('UserCommunicationChannel__use_for_notifications'))
+        default=False, help_text=_('UserCommunicationChannel__use_for_notifications__help'), verbose_name=_('UserCommunicationChannel__use_for_notifications')
+    )
     show_public = models.BooleanField(
-        default=False,
-        help_text=_('UserCommunicationChannel__show_public__help'),
-        verbose_name=_('UserCommunicationChannel__show_public'))
+        default=False, help_text=_('UserCommunicationChannel__show_public__help'), verbose_name=_('UserCommunicationChannel__show_public')
+    )
 
     _logger = logging.getLogger(__name__)
 
@@ -584,7 +512,7 @@ class UserCommunicationChannel(models.Model):
                 )
                 return False
 
-        raise NotImplementedError(f'Don\'t know how to send to a channel of type {self.channel}')
+        raise NotImplementedError(f"Don't know how to send to a channel of type {self.channel}")
 
 
 class UserTimelineEntry(models.Model):
@@ -593,22 +521,13 @@ class UserTimelineEntry(models.Model):
     user = models.ForeignKey(PlatformUser, related_name='timeline', on_delete=models.CASCADE)
     conference = ConferenceReference(related_name='+')
 
-    timestamp = models.DateTimeField(
-        default=now,
-        help_text=_('UserTimelineEntry__timestamp__help'),
-        verbose_name=_('UserTimelineEntry__timestamp'))
+    timestamp = models.DateTimeField(default=now, help_text=_('UserTimelineEntry__timestamp__help'), verbose_name=_('UserTimelineEntry__timestamp'))
 
     target_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
     target_id = models.UUIDField()
     target = GenericForeignKey('target_type', 'target_id')
 
-    is_bookmark = models.BooleanField(
-        default=False,
-        help_text=_('UserTimelineEntry__is_bookmark__help'),
-        verbose_name=_('UserTimelineEntry__is_bookmark'))
+    is_bookmark = models.BooleanField(default=False, help_text=_('UserTimelineEntry__is_bookmark__help'), verbose_name=_('UserTimelineEntry__is_bookmark'))
     """Indicates a timeline entry 'saved for later'."""
 
-    comment = models.TextField(
-        blank=True, null=True,
-        help_text=_('UserTimelineEntry__comment__help'),
-        verbose_name=_('UserTimelineEntry__comment'))
+    comment = models.TextField(blank=True, null=True, help_text=_('UserTimelineEntry__comment__help'), verbose_name=_('UserTimelineEntry__comment'))
diff --git a/src/core/models/voucher.py b/src/core/models/voucher.py
index 1f1ae163a8a9b0e6884e0279bf6471c0cb305ea8..835d04b33f02077adc0797ad99bf99a919d16caf 100644
--- a/src/core/models/voucher.py
+++ b/src/core/models/voucher.py
@@ -85,27 +85,15 @@ class Voucher(models.Model):
 
     id = models.UUIDField(default=uuid4, primary_key=True, editable=False)
     conference = ConferenceReference(related_name='vouchers')
-    name = models.CharField(
-        max_length=200,
-        help_text=_('Voucher__name__help'),
-        verbose_name=_('Voucher__name'))
-
-    enabled = models.BooleanField(
-        default=False,
-        help_text=_('Voucher__enabled__help'),
-        verbose_name=_('Voucher__enabled'))
+    name = models.CharField(max_length=200, help_text=_('Voucher__name__help'), verbose_name=_('Voucher__name'))
+
+    enabled = models.BooleanField(default=False, help_text=_('Voucher__enabled__help'), verbose_name=_('Voucher__enabled'))
     """Mark the voucher as being available. This is intended to be used as a 'draft' marker."""
 
-    show_always = models.BooleanField(
-        default=False,
-        help_text=_('Voucher__show_always__help'),
-        verbose_name=_('Voucher__show_always'))
+    show_always = models.BooleanField(default=False, help_text=_('Voucher__show_always__help'), verbose_name=_('Voucher__show_always'))
     """Show the voucher in any case for a target, even if it hasn't been assigned yet."""
 
-    hide_from_staff = models.BooleanField(
-        default=False,
-        help_text=_('Voucher__hide_from_staff__help'),
-        verbose_name=_('Voucher__hide_from_staff'))
+    hide_from_staff = models.BooleanField(default=False, help_text=_('Voucher__hide_from_staff__help'), verbose_name=_('Voucher__hide_from_staff'))
     """Hide the values even from staff (unless they are Voucher Admin)."""
 
     assignment = models.CharField(
@@ -114,27 +102,16 @@ class Voucher(models.Model):
         choices=Assignment.choices,
         default=Assignment.MANUAL,
         help_text=_('Voucher__assignment__help'),
-        verbose_name=_('Voucher__assignment'))
+        verbose_name=_('Voucher__assignment'),
+    )
 
     data = models.CharField(
-        max_length=20,
-        blank=False,
-        choices=Data.choices,
-        default=Data.TEXT,
-        help_text=_('Voucher__data__help'),
-        verbose_name=_('Voucher__data'))
+        max_length=20, blank=False, choices=Data.choices, default=Data.TEXT, help_text=_('Voucher__data__help'), verbose_name=_('Voucher__data')
+    )
 
-    target = models.CharField(
-        max_length=20,
-        blank=False,
-        choices=Target.choices,
-        help_text=_('Voucher__target__help'),
-        verbose_name=_('Voucher__target'))
+    target = models.CharField(max_length=20, blank=False, choices=Target.choices, help_text=_('Voucher__target__help'), verbose_name=_('Voucher__target'))
 
-    description = models.TextField(
-        blank=True, null=True,
-        help_text=_('Voucher__description__help'),
-        verbose_name=_('Voucher__description'))
+    description = models.TextField(blank=True, null=True, help_text=_('Voucher__description__help'), verbose_name=_('Voucher__description'))
 
     objects = VoucherManager()
     logger = logging.getLogger(__name__)
@@ -201,8 +178,9 @@ class Voucher(models.Model):
         for assembly in missing_assignments:
             # skip non-public assembly/channel if auto-assignment is for public ones only
             if self.assignment == self.Assignment.ON_PUBLIC:
-                if (self.target == self.Target.ASSEMBLY and not assembly.is_public_assembly) or \
-                   (self.target == self.Target.CHANNEL and not assembly.is_public_channel):
+                if (self.target == self.Target.ASSEMBLY and not assembly.is_public_assembly) or (
+                    self.target == self.Target.CHANNEL and not assembly.is_public_channel
+                ):
                     continue
 
             # abort (but warn) if no more available entries are available
@@ -222,23 +200,11 @@ class Voucher(models.Model):
 
 class VoucherEntry(models.Model):
     voucher = models.ForeignKey(Voucher, on_delete=models.CASCADE, related_name='entries')
-    content = models.TextField(
-        blank=True, null=True,
-        help_text=_('VoucherEntry__content__help'),
-        verbose_name=_('VoucherEntry__content'))
-
-    created = models.DateTimeField(
-        auto_now_add=True,
-        help_text=_('VoucherEntry__created__help'),
-        verbose_name=_('VoucherEntry__created'))
-    expires = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('VoucherEntry__expires__help'),
-        verbose_name=_('VoucherEntry__expires'))
-    assigned = models.DateTimeField(
-        blank=True, null=True,
-        help_text=_('VoucherEntry__assigned__help'),
-        verbose_name=_('VoucherEntry__assigned'))
+    content = models.TextField(blank=True, null=True, help_text=_('VoucherEntry__content__help'), verbose_name=_('VoucherEntry__content'))
+
+    created = models.DateTimeField(auto_now_add=True, help_text=_('VoucherEntry__created__help'), verbose_name=_('VoucherEntry__created'))
+    expires = models.DateTimeField(blank=True, null=True, help_text=_('VoucherEntry__expires__help'), verbose_name=_('VoucherEntry__expires'))
+    assigned = models.DateTimeField(blank=True, null=True, help_text=_('VoucherEntry__assigned__help'), verbose_name=_('VoucherEntry__assigned'))
 
     assigned_assembly = models.ForeignKey(Assembly, on_delete=models.SET_NULL, related_name='vouchers', blank=True, null=True)
     assigned_user = models.ForeignKey(PlatformUser, on_delete=models.SET_NULL, related_name='vouchers', blank=True, null=True)
diff --git a/src/core/models/workadventure.py b/src/core/models/workadventure.py
index f50e1b52a3b8f111005876f01e30b1df40379e2c..5c9fc1d7aa218fcfadf5cdfa0e4e665e34a303db 100644
--- a/src/core/models/workadventure.py
+++ b/src/core/models/workadventure.py
@@ -1,5 +1,6 @@
 from datetime import timedelta
 from uuid import uuid4
+
 from django.conf import settings
 from django.contrib.auth.models import Group
 from django.contrib.postgres.fields import ArrayField
@@ -9,11 +10,11 @@ from django.db.models import Q
 from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 
+from ..fields import ConferenceReference
+from ..utils import generate_token
 from .assemblies import Assembly
 from .conference import Conference, ConferenceMember, PlatformUser
 from .rooms import Room
-from ..fields import ConferenceReference
-from ..utils import generate_token
 
 
 class WorkadventureSession(models.Model):
@@ -30,13 +31,15 @@ class WorkadventureSession(models.Model):
     )
 
     token = models.CharField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         max_length=100,
         help_text=_('WorkadventureSession__token__help'),
         verbose_name=_('WorkadventureSession__token'),
     )
     token_expiry = models.DateTimeField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('WorkadventureSession__token_expiry__help'),
         verbose_name=_('WorkadventureSession__token_expiry'),
     )
@@ -44,7 +47,8 @@ class WorkadventureSession(models.Model):
     room = models.ForeignKey(
         Room,
         on_delete=models.SET_NULL,
-        blank=True, null=True,
+        blank=True,
+        null=True,
         related_name='+',
         help_text=_('WorkadventureSession__room__help'),
         verbose_name=_('WorkadventureSession__room'),
@@ -57,7 +61,8 @@ class WorkadventureSession(models.Model):
         default=list,
     )
     additional_data = models.JSONField(
-        blank=True, null=True,
+        blank=True,
+        null=True,
         help_text=_('WorkadventureSession__additional_data__help'),
         verbose_name=_('WorkadventureSession__additional_data'),
     )
@@ -107,10 +112,9 @@ class WorkadventureSession(models.Model):
             user_tags.add('admin')
         if is_angel:
             user_tags.add('angel')
-        for slug in Assembly.objects \
-                .associated_to_user(self.user, self.conference) \
-                .filter(state_assembly__in=Assembly.PUBLIC_STATES) \
-                .values_list('slug', flat=True):
+        for slug in (
+            Assembly.objects.associated_to_user(self.user, self.conference).filter(state_assembly__in=Assembly.PUBLIC_STATES).values_list('slug', flat=True)
+        ):
             user_tags.add('assembly_' + slug)
 
         result = {
@@ -143,9 +147,9 @@ class WorkadventureSession(models.Model):
         # TODO: fehlt hier nicht ein "banned" flag? was ist mit den restlichen Flags?
         # TODO: expand textures and character layers
         # TODO: bei check-user tauchen folgende Zusatzfelder auf:
-        #         mapUrlStart: string;
+        #         mapUrlStart: string; # noqa: ERA001
         #         tags: string[];
-        #         policy_type: number;
+        #         policy_type: number; # noqa: ERA001
         return result
 
     def update_userdata(self, data):
diff --git a/src/core/schedules/__init__.py b/src/core/schedules/__init__.py
index f47a72e9b68e4b0226b8c2938cc7d3c5bcf82d8d..4f08b004bc8532d47348ea706a40f3dac7265b16 100644
--- a/src/core/schedules/__init__.py
+++ b/src/core/schedules/__init__.py
@@ -1,6 +1,5 @@
 from .base import BaseScheduleSupport, ScheduleTypeManager
 
-
 __all__ = [
     'BaseScheduleSupport',
     'ScheduleTypeManager',
diff --git a/src/core/schedules/base.py b/src/core/schedules/base.py
index eb2751279a31c9c8768e1b16c4b09d3c60b9f4f8..6d8cd6ce0b91217dc7dd7c82f24dc288a937fbfa 100644
--- a/src/core/schedules/base.py
+++ b/src/core/schedules/base.py
@@ -1,6 +1,6 @@
+import logging
 from abc import ABCMeta, abstractmethod
 from datetime import timedelta
-import logging
 
 from django.conf import settings
 from django.utils import timezone
@@ -8,9 +8,7 @@ from django.utils.module_loading import import_string
 
 
 def filter_additional_data(data: dict) -> dict:
-    return {k: v for k, v in data.items() if (v and k not in [
-        'guid', 'room', 'start', 'date', 'duration', 'title', 'abstract', 'description', 'language'
-    ])}
+    return {k: v for k, v in data.items() if (v and k not in ['guid', 'room', 'start', 'date', 'duration', 'title', 'abstract', 'description', 'language'])}
 
 
 def schedule_time_to_timedelta(s: str) -> timedelta:
@@ -135,7 +133,7 @@ class BaseScheduleSupport(metaclass=ABCMeta):
         raise NotImplementedError('push(data) was not overriden for ScheduleSupport')
 
 
-class _ScheduleTypeManager(object):
+class _ScheduleTypeManager:
     def __init__(self):
         self.types = {}
         self._initialize_from_settings()
diff --git a/src/core/schedules/schedulejson.py b/src/core/schedules/schedulejson.py
index d75d9127dc40f1b26b64f302786e3310074a551a..72c6e1584a4a0c7f0010062fc6b16ce43e34d21b 100644
--- a/src/core/schedules/schedulejson.py
+++ b/src/core/schedules/schedulejson.py
@@ -1,5 +1,6 @@
-from collections import OrderedDict
 import json
+from collections import OrderedDict
+
 import requests
 from requests_file import FileAdapter
 
@@ -37,32 +38,32 @@ class ScheduleJSONSupport(BaseScheduleSupport):
         schedule = ScheduleJSON.from_url(self.remote_url)
 
         return {
-            "rooms": {
-                r['name']: r for r in schedule.rooms()
-            },
-            "events": {
+            'rooms': {r['name']: r for r in schedule.rooms()},
+            'events': {
                 e.get('id'): {
-                    "guid": e.get('guid'),
-                    "slug": e.get('slug').split(f"{e.get('id')}-")[1][0:150].strip('-') or e.get('slug')[0:150].strip('-'),
-                    "name": e.get('title'),
-                    "language": e.get('language'),
-                    "abstract": e.get('abstract') or '',
-                    "description": e.get('description') or '',
-                    "track": e.get('track'),
-                    "room": e.get('room'),
-                    "schedule_start": e.get('date'),
-                    "schedule_duration": str(schedule_time_to_timedelta(e.get('duration'))),
-                    "is_public": True,
-                    "additional_data": filter_additional_data(e)
-                } for e in schedule.events()
-            }
+                    'guid': e.get('guid'),
+                    'slug': e.get('slug').split(f"{e.get('id')}-")[1][0:150].strip('-') or e.get('slug')[0:150].strip('-'),
+                    'name': e.get('title'),
+                    'language': e.get('language'),
+                    'abstract': e.get('abstract') or '',
+                    'description': e.get('description') or '',
+                    'track': e.get('track'),
+                    'room': e.get('room'),
+                    'schedule_start': e.get('date'),
+                    'schedule_duration': str(schedule_time_to_timedelta(e.get('duration'))),
+                    'is_public': True,
+                    'additional_data': filter_additional_data(e),
+                }
+                for e in schedule.events()
+            },
         }
 
 
 class ScheduleJSON:
-    '''
+    """
     Schedule from JSON document
-    '''
+    """
+
     _schedule = None
 
     def __init__(self, json):
@@ -73,7 +74,7 @@ class ScheduleJSON:
         r = s.get(url)
 
         if r.ok is False:
-            raise Exception('Request failed, HTTP {0}.'.format(r.status_code))
+            raise Exception(f'Request failed, HTTP {r.status_code}.')
 
         # maintain order from input file
         schedule = json.JSONDecoder(object_pairs_hook=OrderedDict).decode(r.text)
@@ -104,13 +105,12 @@ class ScheduleJSON:
         for day in self.days():
             for roomname in day.get('rooms'):
                 rooms.add(roomname)
-        return [{"name": name} for name in rooms]
+        return [{'name': name} for name in rooms]
 
     def events(self):
         for day in self.days():
             for room in day.get('rooms'):
-                for event in day.get('rooms')[room]:
-                    yield event
+                yield from day.get('rooms')[room]
 
     def __str__(self):
         return json.dumps(self._schedule, indent=2)
diff --git a/src/core/schedules/schedulexml.py b/src/core/schedules/schedulexml.py
index b38afac3b1424a0aa17d396df5070488e1136e79..679adfa6f98045788e1c66a573dda4a959795be1 100644
--- a/src/core/schedules/schedulexml.py
+++ b/src/core/schedules/schedulexml.py
@@ -1,12 +1,13 @@
 import collections
 import datetime
-from django.utils.dateparse import parse_datetime
+from xml.etree import ElementTree
+
 import requests
 from requests_file import FileAdapter
-from xml.etree import ElementTree
 
-from .base import BaseScheduleSupport, filter_additional_data, schedule_time_to_timedelta
+from django.utils.dateparse import parse_datetime
 
+from .base import BaseScheduleSupport, filter_additional_data, schedule_time_to_timedelta
 
 s = requests.Session()
 s.mount('file://', FileAdapter())
@@ -40,34 +41,34 @@ class ScheduleXMLSupport(BaseScheduleSupport):
         schedule = ScheduleXML.from_url(self.remote_url)
 
         return {
-            "rooms": {
-                r['name']: r for r in schedule.rooms()
-            },
-            "events": {
+            'rooms': {r['name']: r for r in schedule.rooms()},
+            'events': {
                 e.get('id'): {
-                    "guid": e.get('guid'),
-                    "slug": e.get('slug').split(f"{e.get('id')}-")[1][0:150].strip('-') or e.get('slug')[0:150].strip('-'),
-                    "name": e.get('title'),
-                    "language": e.get('language'),
-                    "abstract": e.get('abstract') or '',
-                    "description": e.get('description') or '',
-                    "track": e.get('track'),
-                    "room": e.get('room'),
-                    "schedule_start": e.get('date'),
-                    "schedule_duration": str(schedule_time_to_timedelta(e.get('duration'))),
-                    "is_public": True,
-                    "additional_data": filter_additional_data(e)
-                } for e in schedule.events()
-            }
+                    'guid': e.get('guid'),
+                    'slug': e.get('slug').split(f"{e.get('id')}-")[1][0:150].strip('-') or e.get('slug')[0:150].strip('-'),
+                    'name': e.get('title'),
+                    'language': e.get('language'),
+                    'abstract': e.get('abstract') or '',
+                    'description': e.get('description') or '',
+                    'track': e.get('track'),
+                    'room': e.get('room'),
+                    'schedule_start': e.get('date'),
+                    'schedule_duration': str(schedule_time_to_timedelta(e.get('duration'))),
+                    'is_public': True,
+                    'additional_data': filter_additional_data(e),
+                }
+                for e in schedule.events()
+            },
         }
 
 
 class ScheduleXML:
-    '''
+    """
     Schedule from XML document using etree, with inspirations from
       - https://github.com/pretalx/pretalx-downstream/blob/master/pretalx_downstream/tasks.py#L67
       - https://github.com/Zverik/schedule-convert/blob/master/schedule_convert/importers/frab_xml.py#L55
-    '''
+    """
+
     _schedule: ElementTree = None
     tz = None
 
@@ -79,7 +80,7 @@ class ScheduleXML:
         r = s.get(url)
 
         if r.ok is False:
-            raise Exception('Request failed, HTTP {0}.'.format(r.status_code))
+            raise Exception(f'Request failed, HTTP {r.status_code}.')
 
         schedule = ElementTree.fromstring(r.text)
         return ScheduleXML(tree=schedule)
diff --git a/src/core/search.py b/src/core/search.py
index 25062358d6397ba9736f0d7de51e443ce64d8943..baf83f2c1a72da3b955b7d3b618e94f8819563cb 100644
--- a/src/core/search.py
+++ b/src/core/search.py
@@ -10,8 +10,9 @@ from .models.tags import ConferenceTag
 from .models.users import PlatformUser
 
 
-def search(user: PlatformUser, conference: Conference, search_term: str, max_per_category: int = 10) \
-        -> Iterator[Union[ConferenceTag, ConferenceTrack, Assembly, Event]]:
+def search(
+    user: PlatformUser, conference: Conference, search_term: str, max_per_category: int = 10
+) -> Iterator[Union[ConferenceTag, ConferenceTrack, Assembly, Event]]:
     """
     Search assemblies, events, pages and tags for the search_term(s).
     Matches on the name are ranked higher than those only in the description.
@@ -47,8 +48,7 @@ def search(user: PlatformUser, conference: Conference, search_term: str, max_per
     if len(exclude_terms) > 0:
         for term in exclude_terms:
             tags = tags.exclude(slug__icontains=term)
-    for tag in tags[:max_per_category]:
-        yield tag
+    yield from tags[:max_per_category]
 
     # matching tracks are good, too
     tracks = conference.tracks.filter(is_public=True)
@@ -56,13 +56,11 @@ def search(user: PlatformUser, conference: Conference, search_term: str, max_per
         tracks = tracks.filter(name__icontains=term)
     for term in exclude_terms:
         tracks = tracks.exclude(name__icontains=term)
-    for track in tracks[:max_per_category]:
-        yield track
+    yield from tracks[:max_per_category]
 
     # search static pages
     pages = conference.pages.filter(public_revision__gte=0)
-    for page in pages.filter(search_vector=search_q):
-        yield page
+    yield from pages.filter(search_vector=search_q)
 
     # tracking which assemblies we've already seen so we don't return them twice
     assemblies_seen = []
@@ -83,11 +81,9 @@ def search(user: PlatformUser, conference: Conference, search_term: str, max_per
     # description match on Assembly
     if len(assemblies) < max_per_category:
         description_matches = Assembly.objects.conference_accessible(conference=conference).exclude(pk__in=assemblies_seen)
-        for assembly in apply_terms_description(description_matches)[:max_per_category - len(assemblies)]:
-            yield assembly
+        yield from apply_terms_description(description_matches)[: max_per_category - len(assemblies)]
 
     # description match on Event
     if len(events) < max_per_category:
         description_matches = Event.objects.conference_accessible(conference=conference).exclude(pk__in=events_seen)
-        for event in apply_terms_description(description_matches)[:max_per_category - len(events)]:
-            yield event
+        yield from apply_terms_description(description_matches)[: max_per_category - len(events)]
diff --git a/src/core/sso.py b/src/core/sso.py
index 36b8d1a3d268c95a1cd5172fb239df5c682648b3..1a70b5a9e87424e3c692f25f0e693ca126c02a30 100644
--- a/src/core/sso.py
+++ b/src/core/sso.py
@@ -1,20 +1,20 @@
-from functools import lru_cache
 import logging
+from functools import lru_cache
+
+import jwt
+from oauth2_provider.scopes import BaseScopes
 
 from django.conf import settings
 from django.utils.text import format_lazy
 from django.utils.translation import gettext_lazy as _
-import jwt
-from oauth2_provider.scopes import BaseScopes
 
 from .models.conference import Conference
 from .models.users import PlatformUser
 
-
 logger = logging.getLogger(__name__)
 
 
-class SingleSignOn(object):
+class SingleSignOn:
     def __init__(self):
         self._secret = settings.SSO_SECRET
         settings.SSO_SECRET = 'removed'
diff --git a/src/core/tests/__init__.py b/src/core/tests/__init__.py
index 851874ec76180fe2e1164064b4f5df32ad79ac11..da0a3027898f8e5c3acceff01130f82f91680280 100644
--- a/src/core/tests/__init__.py
+++ b/src/core/tests/__init__.py
@@ -4,14 +4,14 @@ from .bigbluebutton import *  # noqa: F401, F403
 from .conference import *  # noqa: F401, F403
 from .events import *  # noqa: F401, F403
 from .exportcache import *  # noqa: F401, F403
+from .markdown import *  # noqa: F401, F403
+from .schedules import *  # noqa: F401, F403
 from .search import *  # noqa: F401, F403
-from .users import *  # noqa: F401, F403
 from .tags import *  # noqa: F401, F403
 from .tickets import *  # noqa: F401, F403
-from .markdown import *  # noqa: F401, F403
-from .schedules import *  # noqa: F401, F403
+from .users import *  # noqa: F401, F403
 from .utils import *  # noqa: F401, F403
 from .validators import *  # noqa: F401, F403
 from .workadventure import *  # noqa: F401, F403
 
-__all__ = '*'
+__all__ = ('*',)  # noqa: F405
diff --git a/src/core/tests/assemblies.py b/src/core/tests/assemblies.py
index dfc6ebbacf23a5236a95b613e7e4be9243d4cdfb..faff811ac56c51e361ff85b81ad1faef34b44315 100644
--- a/src/core/tests/assemblies.py
+++ b/src/core/tests/assemblies.py
@@ -14,30 +14,40 @@ class AssembliesTests(TestCase):
         self.user_mgmt = PlatformUser(username='manager')
         self.user_mgmt.save()
         self.user_mgmt.communication_channels.create(
-            channel=UserCommunicationChannel.Channel.MAIL, address='manager@unittest.local',
-            is_verified=True, use_for_notifications=False,
+            channel=UserCommunicationChannel.Channel.MAIL,
+            address='manager@unittest.local',
+            is_verified=True,
+            use_for_notifications=False,
         )
         self.user_mgmt.communication_channels.create(
-            channel=UserCommunicationChannel.Channel.MAIL, address='notifications@unittest.local',
-            is_verified=True, use_for_notifications=True,
+            channel=UserCommunicationChannel.Channel.MAIL,
+            address='notifications@unittest.local',
+            is_verified=True,
+            use_for_notifications=True,
         )
         self.user_mgmt2 = PlatformUser(username='manager2')
         self.user_mgmt2.save()
         self.user_mgmt2.communication_channels.create(
-            channel=UserCommunicationChannel.Channel.MAIL, address='notifications2@unittest.local',
-            is_verified=True, use_for_notifications=True,
+            channel=UserCommunicationChannel.Channel.MAIL,
+            address='notifications2@unittest.local',
+            is_verified=True,
+            use_for_notifications=True,
         )
         self.user_participant = PlatformUser(username='participant')
         self.user_participant.save()
         self.user_participant.communication_channels.create(
-            channel=UserCommunicationChannel.Channel.MAIL, address='participant@unittest.local',
-            is_verified=True, use_for_notifications=False,
+            channel=UserCommunicationChannel.Channel.MAIL,
+            address='participant@unittest.local',
+            is_verified=True,
+            use_for_notifications=False,
         )
         self.user_visitor = PlatformUser(username='visitor')
         self.user_visitor.save()
         self.user_visitor.communication_channels.create(
-            channel=UserCommunicationChannel.Channel.MAIL, address='visitor@unittest.local',
-            is_verified=True, use_for_notifications=False,
+            channel=UserCommunicationChannel.Channel.MAIL,
+            address='visitor@unittest.local',
+            is_verified=True,
+            use_for_notifications=False,
         )
         ConferenceMember(conference=self.conference, user=self.user_mgmt).save()
         ConferenceMember(conference=self.conference, user=self.user_mgmt2).save()
@@ -78,8 +88,7 @@ class AssembliesTests(TestCase):
     def test_mail_htmlwithlinks(self):
         self.assembly1.send_mail_to_managers(
             subject='Hello World',
-            message='Please check [the blog](https://events.ccc.de/) and do stuff.\n'
-                    'And don\'t forget to visit [events.ccc.de](https://events.ccc.de/)!',
+            message='Please check [the blog](https://events.ccc.de/) and do stuff.\n' "And don't forget to visit [events.ccc.de](https://events.ccc.de/)!",
         )
         self.assertEqual(len(mail.outbox), 2)
 
diff --git a/src/core/tests/badges.py b/src/core/tests/badges.py
index 1abb9572fe8a0763710a12762aae609068d66022..7eb3f9ce1648992d836494a79050299acdcdafe6 100644
--- a/src/core/tests/badges.py
+++ b/src/core/tests/badges.py
@@ -2,9 +2,9 @@ from django.core.exceptions import ValidationError
 from django.test import TestCase
 
 from ..models.assemblies import Assembly
+from ..models.badges import Badge, UserBadge
 from ..models.conference import Conference, ConferenceMember
 from ..models.users import PlatformUser
-from ..models.badges import Badge, UserBadge
 
 
 class BadgeTests(TestCase):
diff --git a/src/core/tests/bigbluebutton.py b/src/core/tests/bigbluebutton.py
index fd5e5dec2b38783aa6181ea4336c392ae2320bfd..4cb0f65c1c47ae392cd50c613eb57300c732b491 100644
--- a/src/core/tests/bigbluebutton.py
+++ b/src/core/tests/bigbluebutton.py
@@ -1,15 +1,15 @@
-from django.test import TestCase
 from unittest.mock import Mock, patch
 
+from django.test import TestCase
+
 from core.integrations import BigBlueButtonIntegration, IntegrationError
 from core.models import Assembly, Conference, PlatformUser, Room
 
-
 BigBlueButton = BigBlueButtonIntegration('http://localhost/', 'asdf', 'https://localhost/end_meeting', None)
 
 
 # from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
-class Pattern(object):
+class Pattern:
     def __req__(self, lhs):
         return self.__eq__(lhs)
 
@@ -18,7 +18,7 @@ class Pattern(object):
 
 # from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
 class Any(Pattern):
-    """ Equals everything """
+    """Equals everything"""
 
     def __eq__(self, rhs):
         return True
@@ -31,8 +31,7 @@ class BigBlueButtonTest(TestCase):
         self.assembly = Assembly(name='TestAssembly', slug='asmbly', conference=self.conf)
         self.assembly.save()
         self.room = Room(
-            conference=self.conf, assembly=self.assembly, name='Room 1',
-            room_type=Room.RoomType.BIGBLUEBUTTON, backend_status=Room.BackendStatus.NEW
+            conference=self.conf, assembly=self.assembly, name='Room 1', room_type=Room.RoomType.BIGBLUEBUTTON, backend_status=Room.BackendStatus.NEW
         )
         self.room.save()
 
@@ -47,23 +46,23 @@ class BigBlueButtonTest(TestCase):
 
             get_mock.return_value.status_code = 200
             get_mock.return_value.text = (
-                    '<response>'
-                    '<returncode>SUCCESS</returncode>'
-                    '<meetingID>Test</meetingID>'
-                    '<internalMeetingID>640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613</internalMeetingID>'
-                    '<parentMeetingID>bbb-none</parentMeetingID>'
-                    '<attendeePW>ap</attendeePW>'
-                    '<moderatorPW>mp</moderatorPW>'
-                    '<createTime>1531155809613</createTime>'
-                    '<voiceBridge>70757</voiceBridge>'
-                    '<dialNumber>613-555-1234</dialNumber>'
-                    '<createDate>Mon Jul 09 17:03:29 UTC 2018</createDate>'
-                    '<hasUserJoined>false</hasUserJoined>'
-                    '<duration>0</duration>'
-                    '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
-                    '<messageKey>duplicateWarning</messageKey>'
-                    '<message>This conference was already in existence and may currently be in progress.</message>'
-                    '</response>'
+                '<response>'
+                '<returncode>SUCCESS</returncode>'
+                '<meetingID>Test</meetingID>'
+                '<internalMeetingID>640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613</internalMeetingID>'
+                '<parentMeetingID>bbb-none</parentMeetingID>'
+                '<attendeePW>ap</attendeePW>'
+                '<moderatorPW>mp</moderatorPW>'
+                '<createTime>1531155809613</createTime>'
+                '<voiceBridge>70757</voiceBridge>'
+                '<dialNumber>613-555-1234</dialNumber>'
+                '<createDate>Mon Jul 09 17:03:29 UTC 2018</createDate>'
+                '<hasUserJoined>false</hasUserJoined>'
+                '<duration>0</duration>'
+                '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
+                '<messageKey>duplicateWarning</messageKey>'
+                '<message>This conference was already in existence and may currently be in progress.</message>'
+                '</response>'
             )
             self.room.backend_status = Room.BackendStatus.NEW
             self.room.save()
@@ -72,12 +71,15 @@ class BigBlueButtonTest(TestCase):
             get_mock.assert_called_once()
             self.assertEqual(self.room.backend_status, Room.BackendStatus.ACTIVE)
             self.assertEqual(self.room.backend_link, 'Test')
-            self.assertEqual(self.room.backend_data, {
-                'attendeePW': 'ap',
-                'moderatorPW': 'mp',
-                'createTime': '1531155809613',
-                'close_secret': Any(),
-            })
+            self.assertEqual(
+                self.room.backend_data,
+                {
+                    'attendeePW': 'ap',
+                    'moderatorPW': 'mp',
+                    'createTime': '1531155809613',
+                    'close_secret': Any(),
+                },
+            )
 
     def test_remove_room(self):
         with patch.object(BigBlueButton._session, 'get') as get_mock:
@@ -202,35 +204,39 @@ class BigBlueButtonTest(TestCase):
 
         with patch.object(BigBlueButton._session, 'get') as get_mock, self.assertLogs('core.integrations.bigbluebutton'):
             get_mock.side_effect = [
-                Mock(status_code=200, text=(
-                    '<response>'
-                    '<returncode>FAILED</returncode>'
-                    '<messageKey>notFound</messageKey>'
-                    '<message>We could not find a meeting with that meeting ID</message>'
-                    '</response>'
-                )),
-                Mock(status_code=200, text=(
-                    '<response>'
-                    '<returncode>SUCCESS</returncode>'
-                    '<meetingID>Test</meetingID>'
-                    '<internalMeetingID>640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613</internalMeetingID>'
-                    '<parentMeetingID>bbb-none</parentMeetingID>'
-                    '<attendeePW>ap</attendeePW>'
-                    '<moderatorPW>mp</moderatorPW>'
-                    '<createTime>1531155809613</createTime>'
-                    '<voiceBridge>70757</voiceBridge>'
-                    '<dialNumber>613-555-1234</dialNumber>'
-                    '<createDate>Mon Jul 09 17:03:29 UTC 2018</createDate>'
-                    '<hasUserJoined>false</hasUserJoined>'
-                    '<duration>0</duration>'
-                    '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
-                    '<messageKey>duplicateWarning</messageKey>'
-                    '<message>This conference was already in existence and may currently be in progress.</message>'
-                    '</response>'
-                )),
-                Mock(status_code=302, headers={
-                    'Location': 'https://yourserver.com/client/BigBlueButton.html?sessionToken=ai1wqj8wb6s7rnk0'
-                }),
+                Mock(
+                    status_code=200,
+                    text=(
+                        '<response>'
+                        '<returncode>FAILED</returncode>'
+                        '<messageKey>notFound</messageKey>'
+                        '<message>We could not find a meeting with that meeting ID</message>'
+                        '</response>'
+                    ),
+                ),
+                Mock(
+                    status_code=200,
+                    text=(
+                        '<response>'
+                        '<returncode>SUCCESS</returncode>'
+                        '<meetingID>Test</meetingID>'
+                        '<internalMeetingID>640ab2bae07bedc4c163f679a746f7ab7fb5d1fa-1531155809613</internalMeetingID>'
+                        '<parentMeetingID>bbb-none</parentMeetingID>'
+                        '<attendeePW>ap</attendeePW>'
+                        '<moderatorPW>mp</moderatorPW>'
+                        '<createTime>1531155809613</createTime>'
+                        '<voiceBridge>70757</voiceBridge>'
+                        '<dialNumber>613-555-1234</dialNumber>'
+                        '<createDate>Mon Jul 09 17:03:29 UTC 2018</createDate>'
+                        '<hasUserJoined>false</hasUserJoined>'
+                        '<duration>0</duration>'
+                        '<hasBeenForciblyEnded>false</hasBeenForciblyEnded>'
+                        '<messageKey>duplicateWarning</messageKey>'
+                        '<message>This conference was already in existence and may currently be in progress.</message>'
+                        '</response>'
+                    ),
+                ),
+                Mock(status_code=302, headers={'Location': 'https://yourserver.com/client/BigBlueButton.html?sessionToken=ai1wqj8wb6s7rnk0'}),
             ]
             url = BigBlueButton.join_room(self.room, user)
             self.assertEqual(url, 'https://yourserver.com/client/BigBlueButton.html?sessionToken=ai1wqj8wb6s7rnk0')
diff --git a/src/core/tests/events.py b/src/core/tests/events.py
index d5efcc079c1cf6bb3f01d9fc24399b1eadadb35c..77103231650839fca1deb840156e0b004a660376 100644
--- a/src/core/tests/events.py
+++ b/src/core/tests/events.py
@@ -74,7 +74,7 @@ class EventTests(TestCase):
         with self.assertRaises(ValidationError) as cm:
             event = Event(**event_data)
             event.full_clean()
-        self.assertTrue("assembly" in cm.exception.error_dict)
+        self.assertTrue('assembly' in cm.exception.error_dict)
 
     def test_error_negative_duration(self):
         event_data = self.valid_event.copy()
@@ -82,7 +82,7 @@ class EventTests(TestCase):
         with self.assertRaises(ValidationError) as cm:
             event = Event(**event_data)
             event.full_clean()
-        self.assertTrue("schedule_duration" in cm.exception.error_dict)
+        self.assertTrue('schedule_duration' in cm.exception.error_dict)
 
     def test_error_zero_duration(self):
         event_data = self.valid_event.copy()
@@ -90,7 +90,7 @@ class EventTests(TestCase):
         with self.assertRaises(ValidationError) as cm:
             event = Event(**event_data)
             event.full_clean()
-        self.assertTrue("schedule_duration" in cm.exception.error_dict)
+        self.assertTrue('schedule_duration' in cm.exception.error_dict)
 
     def test_error_end_after_conference(self):
         event_data = self.valid_event.copy()
@@ -98,7 +98,7 @@ class EventTests(TestCase):
         with self.assertRaises(ValidationError) as cm:
             event = Event(**event_data)
             event.full_clean()
-        self.assertTrue("schedule_duration" in cm.exception.error_dict)
+        self.assertTrue('schedule_duration' in cm.exception.error_dict)
 
     def test_error_start_before_conference(self):
         event_data = self.valid_event.copy()
@@ -106,7 +106,7 @@ class EventTests(TestCase):
         with self.assertRaises(ValidationError) as cm:
             event = Event(**event_data)
             event.full_clean()
-        self.assertTrue("schedule_start" in cm.exception.error_dict)
+        self.assertTrue('schedule_start' in cm.exception.error_dict)
 
     def test_no_sos_assembly(self):
         event_data = self.valid_event.copy()
diff --git a/src/core/tests/exportcache.py b/src/core/tests/exportcache.py
index 9ea5bdb0693d51b752d94454340a5abbcb02955c..16f87c1ef11aa73da34bec95778b6cbf4803f95b 100644
--- a/src/core/tests/exportcache.py
+++ b/src/core/tests/exportcache.py
@@ -1,9 +1,9 @@
 from datetime import timedelta
 
+from django.test import TestCase, override_settings
 from django.utils import timezone
 from django.utils.datetime_safe import datetime
 from django.utils.timezone import now
-from django.test import TestCase, override_settings
 
 from ..models.assemblies import Assembly
 from ..models.conference import Conference, ConferenceExportCache
diff --git a/src/core/tests/markdown.py b/src/core/tests/markdown.py
index e313cb67dbb1f63a1c1cf03da5fb4153fb66ab1f..90e781cbc57b153893bd2bf95be0df2680e2ff25 100644
--- a/src/core/tests/markdown.py
+++ b/src/core/tests/markdown.py
@@ -17,7 +17,6 @@ TEST_CONF_ID = uuid.uuid4()
 @override_settings(ALLOWED_HOSTS=['hub.test'], PLAINUI_CONFERENCE=TEST_CONF_ID)
 class MarkdownTest(TestCase):
     def test_url_class(self):
-
         conf = Conference(name='foo', id=TEST_CONF_ID)
         conf.save()
 
diff --git a/src/core/tests/schedules.py b/src/core/tests/schedules.py
index ec618de472de2719bd0034e6a7dfa03bcfe0d67a..be0ffc95ac722c6fa7d90d5daba297e2022889de 100644
--- a/src/core/tests/schedules.py
+++ b/src/core/tests/schedules.py
@@ -1,5 +1,5 @@
-from datetime import datetime, timedelta, timezone
 import json
+from datetime import datetime, timedelta, timezone
 from pathlib import Path
 
 from django.test import TestCase, override_settings
@@ -7,10 +7,9 @@ from django.test import TestCase, override_settings
 from ..models import Event
 from ..models.assemblies import Assembly
 from ..models.conference import Conference, ConferenceMember
-from ..models.users import PlatformUser
 from ..models.schedules import ScheduleSource, ScheduleSourceImport, ScheduleSourceMapping
-from ..schedules.base import BaseScheduleSupport, ScheduleTypeManager, \
-    filter_additional_data, schedule_time_to_timedelta
+from ..models.users import PlatformUser
+from ..schedules.base import BaseScheduleSupport, ScheduleTypeManager, filter_additional_data, schedule_time_to_timedelta
 from ..schedules.schedulejson import ScheduleJSONSupport
 from ..schedules.schedulexml import ScheduleXMLSupport
 
@@ -25,7 +24,7 @@ class FileBasedScheduleSupport(BaseScheduleSupport):
     def filename(self):
         url = str(self.schedule_source.import_url)
         assert url.startswith('file://')
-        fn = url[len('file://'):]
+        fn = url[len('file://') :]
 
         base_path = Path(__file__).parent
         fn = base_path.joinpath(fn).resolve()
@@ -118,10 +117,10 @@ class ScheduleTests(TestCase):
         self.assertEqual(2, src.mappings.count())
 
         r1_m = src.mappings.get(mapping_type=ScheduleSourceMapping.MappingType.ROOM, source_id='eins')
-        self.assertEqual(r1_m.local_id, r1.id, 'room\'s mapping\'s local_id doesn\'t match room id')
+        self.assertEqual(r1_m.local_id, r1.id, "room's mapping's local_id doesn't match room id")
 
         e1_m = src.mappings.get(mapping_type=ScheduleSourceMapping.MappingType.EVENT, source_id='cej9dwoi')
-        self.assertEqual(e1_m.local_id, e1.id, 'event\'s mapping\'s local_id doesn\'t match event id')
+        self.assertEqual(e1_m.local_id, e1.id, "event's mapping's local_id doesn't match event id")
 
         # check other attributes
         self.assertEqual(e1.room, r1)
@@ -259,7 +258,7 @@ class ScheduleTests(TestCase):
             assembly=self.assembly,
             conference=self.conference,
             import_type=ScheduleXMLSupport.identifier,
-            import_url=f"file://{Path(__file__).parent.joinpath('import_data', 'schedule-2021.xml')}"
+            import_url=f"file://{Path(__file__).parent.joinpath('import_data', 'schedule-2021.xml')}",
         )
         self.assertFalse(src.has_running_import)
 
@@ -305,7 +304,7 @@ class ScheduleTests(TestCase):
             assembly=self.assembly,
             conference=self.conference,
             import_type=ScheduleJSONSupport.identifier,
-            import_url=f"file://{Path(__file__).parent.joinpath('import_data', 'schedule-2020.json')}"
+            import_url=f"file://{Path(__file__).parent.joinpath('import_data', 'schedule-2020.json')}",
         )
         self.check_json(src, rooms=1, events=1, room_name='Yellow Room')
 
@@ -315,7 +314,7 @@ class ScheduleTests(TestCase):
             assembly=self.assembly,
             conference=self.conference,
             import_type=ScheduleJSONSupport.identifier,
-            import_url=f"file://{Path(__file__).parent.joinpath('import_data', 'schedule-2021.json')}"
+            import_url=f"file://{Path(__file__).parent.joinpath('import_data', 'schedule-2021.json')}",
         )
         self.check_json(src, rooms=2, events=1, room_name='Gray Room')
 
diff --git a/src/core/tests/search.py b/src/core/tests/search.py
index ec7c0152adab7954fae7f903931dfa3278df69e5..1d9f4dadb8b4fdcb934384517fc0207b4c58c9a6 100644
--- a/src/core/tests/search.py
+++ b/src/core/tests/search.py
@@ -22,17 +22,17 @@ class SearchTests(TestCase):
         self.cocktail_page.save()
         self.cocktail_page_rev1 = self.cocktail_page.revisions.create(
             title=self.cocktail_page.title,
-            body='''Der **Pangalaktische Donnergurgler** ist der angeblich stärkste Drink im Universum.
+            body="""Der **Pangalaktische Donnergurgler** ist der angeblich stärkste Drink im Universum.
 Die Wirkung eines Pangalaktischen Donnergurglers ist in etwa so, „als ob man mit einem Goldbarren,
-der in Zitronenscheiben gehüllt ist, das Gehirn aus dem Kopf gedroschen bekommt“.''',
+der in Zitronenscheiben gehüllt ist, das Gehirn aus dem Kopf gedroschen bekommt“.""",
             author=page_user,
         )
         self.cocktail_page_rev1.save()
         self.cocktail_page_rev2 = self.cocktail_page.revisions.create(
             title=self.cocktail_page.title,
-            body='''Der **Pangalaktische Donnergurgler** ist der angeblich stärkste Drink der Galaxis.
+            body="""Der **Pangalaktische Donnergurgler** ist der angeblich stärkste Drink der Galaxis.
 Die Wirkung eines Pangalaktischen Donnergurglers ist in etwa so, „als ob man mit einem Goldbarren,
-der in Zitronenscheiben gehüllt ist, das Gehirn aus dem Kopf gedroschen bekommt“.''',
+der in Zitronenscheiben gehüllt ist, das Gehirn aus dem Kopf gedroschen bekommt“.""",
             author=page_user,
         )
         self.cocktail_page_rev2.save()
diff --git a/src/core/tests/tags.py b/src/core/tests/tags.py
index a11aaf4277e0f4b5f7ef26765df1b568ded021df..14d4d7d3d2f4ef1baff7e83e8142052c408e4f9a 100644
--- a/src/core/tests/tags.py
+++ b/src/core/tests/tags.py
@@ -119,13 +119,13 @@ class TagItemTests(TestCase):
         if value is None:
             return
         if value_type == ConferenceTag.Type.SIMPLE:
-            self.fail("Simple tag item did not return None as value")
+            self.fail('Simple tag item did not return None as value')
         if value_type == ConferenceTag.Type.STRING:
-            self.assertTrue(isinstance(value, str), "String tag item did not return None or a string as value")
+            self.assertTrue(isinstance(value, str), 'String tag item did not return None or a string as value')
         if value_type == ConferenceTag.Type.INT:
-            self.assertTrue(isinstance(value, int), "Int tag item did not return None or an int as value")
+            self.assertTrue(isinstance(value, int), 'Int tag item did not return None or an int as value')
         if value_type == ConferenceTag.Type.BOOL:
-            self.assertTrue(isinstance(value, bool), "Bool tag item did not return None or a bool as value")
+            self.assertTrue(isinstance(value, bool), 'Bool tag item did not return None or a bool as value')
 
     def test_get_value_check_type_uninitialized(self):
         for tag_item in self.tag_items.values():
@@ -135,7 +135,7 @@ class TagItemTests(TestCase):
         for tag_item in self.tag_items.values():
             value_type = tag_item.tag.value_type
             if value_type == ConferenceTag.Type.STRING:
-                tag_item.value = "Test"
+                tag_item.value = 'Test'
             if value_type == ConferenceTag.Type.INT:
                 tag_item.value = 42
             if value_type == ConferenceTag.Type.BOOL:
@@ -147,7 +147,7 @@ class TagItemTests(TestCase):
             value_type = tag_item.tag.value_type
             value = None
             if value_type == ConferenceTag.Type.STRING:
-                value = "Test"
+                value = 'Test'
             if value_type == ConferenceTag.Type.INT:
                 value = 42
             if value_type == ConferenceTag.Type.BOOL:
@@ -156,32 +156,32 @@ class TagItemTests(TestCase):
             self.assertEqual(tag_item.value, value)
 
     def test_value_check_boolean_coerce(self):
-        tag = self.tag_items["guter Geschmack"]
+        tag = self.tag_items['guter Geschmack']
         for v in [False, 0, 'n', 'no', 'nein', 'false', 'off']:
             tag.value = True
             self.assertTrue(tag.value)
             tag.value = v
-            self.assertFalse(tag.value, "Value {} did not yield false".format(v))
+            self.assertFalse(tag.value, f'Value {v} did not yield false')
 
         for v in [True, -2, -1, 1, 2, 3, 'y', 'j', 'yes', 'ja', 'true', 'on']:
             tag.value = False
             self.assertFalse(tag.value)
             tag.value = v
-            self.assertTrue(tag.value, "Value {} did not yield true".format(v))
+            self.assertTrue(tag.value, f'Value {v} did not yield true')
 
     def test_value_set_check_simple_with_wrong_argument(self):
-        tag = self.tag_items["foo"]
+        tag = self.tag_items['foo']
         with self.assertRaises(ValueError):
             tag.value = self.user
         with self.assertRaises(ValueError):
-            tag.value = "Test"
+            tag.value = 'Test'
         with self.assertRaises(ValueError):
             tag.value = 42
         with self.assertRaises(ValueError):
             tag.value = True
 
     def test_value_set_check_string_with_wrong_argument(self):
-        tag = self.tag_items["Autor des besten Gedichts"]
+        tag = self.tag_items['Autor des besten Gedichts']
         with self.assertRaises(ValueError):
             tag.value = None
         with self.assertRaises(ValueError):
@@ -192,26 +192,26 @@ class TagItemTests(TestCase):
             tag.value = True
 
     def test_value_set_check_int_with_wrong_argument(self):
-        tag = self.tag_items["michelinstars"]
+        tag = self.tag_items['michelinstars']
         with self.assertRaises(ValueError):
             tag.value = None
         with self.assertRaises(ValueError):
             tag.value = self.user
         with self.assertRaises(ValueError):
-            tag.value = "Test"
+            tag.value = 'Test'
         # Python coerces bool to int by itself
 
     def test_value_set_check_bool_with_wrong_argument(self):
-        tag = self.tag_items["guter Geschmack"]
+        tag = self.tag_items['guter Geschmack']
         with self.assertRaises(ValueError):
             tag.value = None
         with self.assertRaises(ValueError):
             tag.value = self.user
         with self.assertRaises(ValueError):
-            tag.value = "Test"
+            tag.value = 'Test'
         with self.assertRaises(ValueError):
-            tag.value = "foo"
+            tag.value = 'foo'
         with self.assertRaises(ValueError):
-            tag.value = ""
+            tag.value = ''
         with self.assertRaises(ValueError):
-            tag.value = "bar"
+            tag.value = 'bar'
diff --git a/src/core/tests/tickets.py b/src/core/tests/tickets.py
index c108a39f6017a9363f63268c46bdb95c74482c0f..1a7dfd5b1a6338588248286e8818041897de83e1 100644
--- a/src/core/tests/tickets.py
+++ b/src/core/tests/tickets.py
@@ -1,12 +1,13 @@
-from datetime import datetime, timedelta, UTC
-from django.test import TestCase, override_settings
+from datetime import UTC, datetime, timedelta
+
 import jwt
 
+from django.test import TestCase, override_settings
+
 from ..models.conference import Conference
 from ..models.ticket import ConferenceMemberTicket, TicketValidationError
 from ..models.users import PlatformUser
 
-
 _GOOD_SECRET = 'F00Preti%'
 _BAD_SECRET = 'DämlicherFlanders'
 
@@ -37,7 +38,7 @@ class PretixTests(TestCase):
         self.assertEqual(ticket.user_id, self.foo_user.id)
 
         # trying the same token again must fail
-        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', "WARNING"):
+        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', 'WARNING'):
             ConferenceMemberTicket.redeem_pretix_ticket(conference=self.conference, user=self.foo_user, token=token)
 
     def test_two_per_user(self):
@@ -46,7 +47,7 @@ class PretixTests(TestCase):
 
         # validating a second token must fail (even if the token itself is valid)
         token2 = jwt.encode({**self.pretix_jwt_basepayload, 'uid': 'jklö5678'}, _GOOD_SECRET)
-        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', "WARNING"):
+        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', 'WARNING'):
             ConferenceMemberTicket.redeem_pretix_ticket(conference=self.conference, user=self.foo_user, token=token2)
 
         # verify that the second token works on another user
@@ -57,16 +58,16 @@ class PretixTests(TestCase):
     def test_invalid_signature(self):
         token = jwt.encode({**self.pretix_jwt_basepayload, 'uid': 'asdf1234'}, _BAD_SECRET)
 
-        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', "WARNING"):
+        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', 'WARNING'):
             ConferenceMemberTicket.redeem_pretix_ticket(conference=self.conference, user=self.foo_user, token=token)
 
     def test_invalid_fields(self):
         # misspell 'uid'
         token = jwt.encode({**self.pretix_jwt_basepayload, 'userid': 'asdf1234'}, _GOOD_SECRET)
-        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', "WARNING"):
+        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', 'WARNING'):
             ConferenceMemberTicket.redeem_pretix_ticket(conference=self.conference, user=self.foo_user, token=token)
 
         # unmatching 'aud' (audience, conference slug)
         token = jwt.encode({**self.pretix_jwt_basepayload, 'aud': 'FNORD', 'userid': 'asdf1234'}, _GOOD_SECRET)
-        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', "WARNING"):
+        with self.assertRaises(TicketValidationError), self.assertLogs('core.models.ticket', 'WARNING'):
             ConferenceMemberTicket.redeem_pretix_ticket(conference=self.conference, user=self.foo_user, token=token)
diff --git a/src/core/tests/users.py b/src/core/tests/users.py
index 8956403c987ec17eb0a5e76b76d1ff8195670a2f..7d6eb76501b4713080cba57158b2eba1c02cf985 100644
--- a/src/core/tests/users.py
+++ b/src/core/tests/users.py
@@ -14,7 +14,7 @@ class UserCommunicationChannelStaticTests(TestCase):
                 self.fail(f'{g} should be accepted as {what} but raised an exception: {err}')
 
         for b in bad:
-            with self.assertRaises(ValidationError, msg=f'{b} shouldn\'t be accepted as {what}'):
+            with self.assertRaises(ValidationError, msg=f"{b} shouldn't be accepted as {what}"):
                 func(b)
 
     def test_mail_validation(self):
@@ -37,7 +37,7 @@ class UserCommunicationChannelStaticTests(TestCase):
             '#matrix:example.org',
             '!4rguxf:matrix.org',
             'https://matrix.to/#/%23somewhere%3Aexample.org',
-            'https://matrix.to/#/!somewhere%3Aexample.org'
+            'https://matrix.to/#/!somewhere%3Aexample.org',
         ]
         bad_uris = [
             '',
diff --git a/src/core/tests/utils.py b/src/core/tests/utils.py
index 89e7b171949ab36de81d482d5e5eaa6612efbc0d..1a7ff7a5353833808dfc718c0e39333343cef4b3 100644
--- a/src/core/tests/utils.py
+++ b/src/core/tests/utils.py
@@ -2,7 +2,7 @@ from datetime import timedelta
 
 from django.test import TestCase
 
-from ..utils import mask_url, str2timedelta, scheme_and_netloc_from_url, GitRepo
+from ..utils import GitRepo, mask_url, scheme_and_netloc_from_url, str2timedelta
 
 
 class UtilsTests(TestCase):
@@ -59,10 +59,10 @@ class GitRepoOfflineTests(TestCase):
         self.assertEqual(repo.repo_hash, 'develop')
 
     def test_url_params(self):
-        repo_test_baseurl = "https://git.cccv.de/hub/wiki-import-test.git"
+        repo_test_baseurl = 'https://git.cccv.de/hub/wiki-import-test.git'
 
-        repo = GitRepo(f"https://{repo_test_baseurl}")
-        self.assertEqual(f"https://{repo_test_baseurl}", repo.repo_url)
+        repo = GitRepo(f'https://{repo_test_baseurl}')
+        self.assertEqual(f'https://{repo_test_baseurl}', repo.repo_url)
         self.assertIsNone(repo.repo_branch)
         self.assertIsNone(repo.repo_path)
         self.assertIsNone(repo.repo_hash)
diff --git a/src/core/tests/validators.py b/src/core/tests/validators.py
index 5673249566c527636b5b8f74fdb0e2a0a5b7397b..cd3f4c795524730a458d21fd22882b8eb481519f 100644
--- a/src/core/tests/validators.py
+++ b/src/core/tests/validators.py
@@ -24,9 +24,7 @@ class ValidateFileSizeTests(TestCase):
 class ValidateImageSizeTests(TestCase):
     def test_does_not_raise_when_image_meets_requirements(self):
         mock_image = Mock(width=10, height=10)
-        validator = ImageDimensionValidator(
-            min_size=(10, 10), max_size=(10, 10), square=True
-        )
+        validator = ImageDimensionValidator(min_size=(10, 10), max_size=(10, 10), square=True)
 
         self.assertEqual(mock_image, validator(mock_image))
 
diff --git a/src/core/tests/workadventure.py b/src/core/tests/workadventure.py
index 7b0940c5b3a61d43f9766b4adc2e582627ff4fd0..2e91edea33863e754f8144fba6c08d38ed983cc0 100644
--- a/src/core/tests/workadventure.py
+++ b/src/core/tests/workadventure.py
@@ -58,15 +58,19 @@ class WorkadventureSessionTests(TestCase):
         self.user.refresh_from_db()
         self.assertFalse(self.user.receive_audio)
 
-        session.update_userdata({'wa_userconfig': {
-            'silentMode': False,
-            'disableCamera': False,
-            'noAnimations': True,
-            'blockExternalContent': True,
-            'blockAmbientSounds': True,
-            'ignoreFollowRequests': True,
-            'forceWebsiteTrigger': True,
-        }})
+        session.update_userdata(
+            {
+                'wa_userconfig': {
+                    'silentMode': False,
+                    'disableCamera': False,
+                    'noAnimations': True,
+                    'blockExternalContent': True,
+                    'blockAmbientSounds': True,
+                    'ignoreFollowRequests': True,
+                    'forceWebsiteTrigger': True,
+                }
+            }
+        )
         self.user.refresh_from_db()
         self.assertTrue(self.user.receive_audio)
         self.assertTrue(self.user.receive_video)
diff --git a/src/core/tokens.py b/src/core/tokens.py
index 6ee5a51559273c524f27a902c2257adefce7ea3f..635f82d2d443000b17d949f0f4db3d3887241ab9 100644
--- a/src/core/tokens.py
+++ b/src/core/tokens.py
@@ -1,17 +1,16 @@
 from django.contrib.auth.tokens import PasswordResetTokenGenerator
 
-
 # adapted from https://simpleisbetterthancomplex.com/tutorial/2017/02/18/how-to-create-user-sign-up-view.html
 
 
 class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
     def _make_hash_value(self, user, timestamp):
-        return (str(user.pk) + str(timestamp) + str(user.profile.mail_verified))
+        return str(user.pk) + str(timestamp) + str(user.profile.mail_verified)
 
 
 class ChannelActivationTokenGenerator(PasswordResetTokenGenerator):
     def _make_hash_value(self, channel, timestamp):
-        return (str(channel.user.pk) + str(timestamp) + str(channel.channel) + str(channel.address))
+        return str(channel.user.pk) + str(timestamp) + str(channel.channel) + str(channel.address)
 
 
 account_activation_token = AccountActivationTokenGenerator()
diff --git a/src/core/translation.py b/src/core/translation.py
index 8aef618ba34f35ac484181d7f0138e8222eb0f33..85d7992c175353e8686baf025c4a8cd88b123885 100644
--- a/src/core/translation.py
+++ b/src/core/translation.py
@@ -20,7 +20,10 @@ from core.models import (
 
 @register(Assembly)
 class AssemblyTranslationOptions(TranslationOptions):
-    fields = ('description', 'description_html', )
+    fields = (
+        'description',
+        'description_html',
+    )
 
 
 @register(Badge)
@@ -58,39 +61,58 @@ admin.site.register(BadgeCategory, TranslatedBadgeCategoryAdmin)
 
 @register(BulletinBoardEntry)
 class BulletinBoardEntryTranslationOptions(TranslationOptions):
-    fields = ('text', 'text_html', )
+    fields = (
+        'text',
+        'text_html',
+    )
 
 
 @register(ConferenceNavigationItem)
 class ConferenceNavigationItemTranslationOptions(TranslationOptions):
-    fields = ('label', 'title', )
+    fields = (
+        'label',
+        'title',
+    )
 
 
 @register(Event)
 class EventTranslationOptions(TranslationOptions):
-    fields = ('description', 'description_html', )
+    fields = (
+        'description',
+        'description_html',
+    )
 
 
 @register(ConferenceMember)
 class ConferenceMemberTranslationOptions(TranslationOptions):
-    fields = ('description', 'description_html', )
+    fields = (
+        'description',
+        'description_html',
+    )
 
 
 @register(Room)
 class RoomTranslationOptions(TranslationOptions):
-    fields = ('description', 'description_html', )
+    fields = (
+        'description',
+        'description_html',
+    )
 
 
 @register(MapFloor)
 class MapFloorTranslationOptions(TranslationOptions):
-    fields = ('name', )
+    fields = ('name',)
 
 
 @register(MapPOI)
 class MapPOITranslationOptions(TranslationOptions):
-    fields = ('name', 'description', 'description_html', )
+    fields = (
+        'name',
+        'description',
+        'description_html',
+    )
 
 
 @register(MetaNavItem)
 class MetaNavItemTranslationOptions(TranslationOptions):
-    fields = ('title', )
+    fields = ('title',)
diff --git a/src/core/utils.py b/src/core/utils.py
index ab233151e43fcdf6b0e1ada174c0ed8e292dc656..82ef8ba432b57a54a5fa81f6be7a1c8aeb57de5b 100644
--- a/src/core/utils.py
+++ b/src/core/utils.py
@@ -50,7 +50,7 @@ def render_markdown_as_text(markup: str):
     """
     text_only = strip_tags(markup)
     link_urls = {}
-    for (link_text, link_url) in RE_MARKDOWN_LINKS.findall(markup):
+    for link_text, link_url in RE_MARKDOWN_LINKS.findall(markup):
         if link_url not in link_urls:
             link_urls[link_url] = len(link_urls) + 1
         idx = link_urls[link_url]
@@ -73,7 +73,7 @@ def generate_token(length: int = 50, char_set: str = ''.join([ascii_letters, dig
 
 def int_to_custom_string(number: int, alphabet: str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
     if alphabet is None or len(alphabet) < 2:
-        raise ValueError("Alphabet must consist of at least two characters.")
+        raise ValueError('Alphabet must consist of at least two characters.')
 
     base = len(alphabet)
     result = []
@@ -156,7 +156,7 @@ class GitCheckoutError(Exception):
     pass
 
 
-class GitRepo(object):
+class GitRepo:
     working_copy_path: Path
 
     def __init__(self, repo_url):
@@ -178,27 +178,25 @@ class GitRepo(object):
         # note down URL without custom optional parts
         self.repo_url = parsed._replace(query=None, fragment=None).geturl()
         if '$' in self.repo_url:
-            raise ValueError('Env variables not allowed in GitRepo\'s repo_url.')
+            raise ValueError("Env variables not allowed in GitRepo's repo_url.")
 
     def __enter__(self):
         if self.working_copy_path is not None:
-            raise NotImplementedError("Accessing GitRepo twice is not supported!")
+            raise NotImplementedError('Accessing GitRepo twice is not supported!')
         self.working_copy_path = Path(tempfile.mkdtemp())
 
         # assemble git-clone call
         cmd = ['git', 'clone', '-c', 'core.symlinks=false']
         if not self.repo_hash:
             cmd.extend(
-                ['--depth', '1',]
+                [
+                    '--depth',
+                    '1',
+                ]
             )
         if self.repo_branch:
-            cmd.extend(
-                ['--branch', self.repo_branch]
-            )
-        cmd.extend([
-            self.repo_url,
-            str(self.working_copy_path)
-        ])
+            cmd.extend(['--branch', self.repo_branch])
+        cmd.extend([self.repo_url, str(self.working_copy_path)])
 
         # this fails with an exception on non-zero exitcode
         logger.info(' '.join(cmd))
@@ -223,11 +221,11 @@ class GitRepo(object):
 
     def __exit__(self, exc_type, exc_val, exc_tb):
         if not shutil.rmtree.avoids_symlink_attacks:
-            raise NotImplementedError("Your OS does not support avoiding symlink attacks.")
+            raise NotImplementedError('Your OS does not support avoiding symlink attacks.')
         try:
             shutil.rmtree(self.working_copy_path)
         except Exception:
-            logger.exception("Failed to remove temporary files.")
+            logger.exception('Failed to remove temporary files.')
 
     @cached_property
     def base_path(self) -> Path:
@@ -240,11 +238,11 @@ class GitRepo(object):
             base_path = (base_path / self.repo_path).absolute()
             # sanity check that we are not being tricked into some path traversal
             if not base_path.is_relative_to(self.working_copy_path):
-                raise ValueError(f"Provided path would result in a directory traversal: {self.repo_path}")
+                raise ValueError(f'Provided path would result in a directory traversal: {self.repo_path}')
 
         return base_path
 
-    def get_documents(self, glob: str = "*.md", encoding: str = 'utf-8') -> Dict[str, str]:
+    def get_documents(self, glob: str = '*.md', encoding: str = 'utf-8') -> Dict[str, str]:
         """
         Read all documents matching the configured glob as text.
         :param glob: a filter to use when searching the documents to read, defaults to Markdown file extension
@@ -260,7 +258,7 @@ class GitRepo(object):
             # read and store files matching the glob
             try:
                 documents[file.name] = file.read_text(encoding=encoding)
-            except (IOError, UnicodeDecodeError):
+            except (OSError, UnicodeDecodeError):
                 documents[file.name] = None
                 logger.exception('Failed to read document "%s".', file.name)
 
@@ -278,7 +276,7 @@ class GitRepo(object):
 
         # sanity check that nobody is trying to do any shenanigans
         if not file_path.is_relative_to(self.working_copy_path):
-            raise ValueError(f"Provided file path would result in a directory traversal: {filename}")
+            raise ValueError(f'Provided file path would result in a directory traversal: {filename}')
 
         # read the file (or raise if the file does not exist)
         return file_path.read_bytes()
diff --git a/src/core/validators.py b/src/core/validators.py
index 3fc396b20466d8e6f068ec8371236e9323d8b4e1..d7750f148b048c619d768f61796a184d2f1c8628 100644
--- a/src/core/validators.py
+++ b/src/core/validators.py
@@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
 
 
 @deconstructible
-class FileSizeValidator(object):
+class FileSizeValidator:
     """
     A validator class that will check a file size against the given limit
 
@@ -28,9 +28,9 @@ class FileSizeValidator(object):
     def __call__(self, file):
         if file.size > self.max_size * 1024 * 1024:
             raise ValidationError(
-                _("Validation__error_file_size_MB_exceeded %(max_size)d"),
-                code="max_file_size",
-                params={"max_size": self.max_size},
+                _('Validation__error_file_size_MB_exceeded %(max_size)d'),
+                code='max_file_size',
+                params={'max_size': self.max_size},
             )
         else:
             return file
@@ -40,7 +40,7 @@ class FileSizeValidator(object):
 
 
 @deconstructible
-class ImageDimensionValidator(object):
+class ImageDimensionValidator:
     """
     A validator class that will check a image dimension against the given limits
 
@@ -52,13 +52,7 @@ class ImageDimensionValidator(object):
     max_size: tuple[int | None, int | None]
     square: bool
 
-    def __init__(
-        self,
-        *,
-        min_size: tuple[int | None, int | None] = (None, None),
-        max_size: tuple[int | None, int | None] = (None, None),
-        square: bool = False
-    ):
+    def __init__(self, *, min_size: tuple[int | None, int | None] = (None, None), max_size: tuple[int | None, int | None] = (None, None), square: bool = False):
         """Return a model field validator for ImageFields that raises an ValidationError
             if the image has incorrect dimensions
 
@@ -76,55 +70,51 @@ class ImageDimensionValidator(object):
         if self.min_size[0] is not None and image.width < self.min_size[0]:
             errors.append(
                 ValidationError(
-                    _("Validation__error_image_width_low %(size)d"),
-                    code="min_width",
-                    params={"size": self.min_size[0]},
+                    _('Validation__error_image_width_low %(size)d'),
+                    code='min_width',
+                    params={'size': self.min_size[0]},
                 )
             )
         if self.min_size[1] is not None and image.height < self.min_size[1]:
             errors.append(
                 ValidationError(
-                    _("Validation__error_image_height_low %(size)d"),
-                    code="min_height",
-                    params={"size": self.min_size[1]},
+                    _('Validation__error_image_height_low %(size)d'),
+                    code='min_height',
+                    params={'size': self.min_size[1]},
                 )
             )
         if self.max_size[0] is not None and image.width > self.max_size[0]:
             errors.append(
                 ValidationError(
-                    _("Validation__error_image_width_high %(size)d"),
-                    code="max_width",
-                    params={"size": self.max_size[0]},
+                    _('Validation__error_image_width_high %(size)d'),
+                    code='max_width',
+                    params={'size': self.max_size[0]},
                 )
             )
         if self.max_size[1] is not None and image.height > self.max_size[1]:
             errors.append(
                 ValidationError(
-                    _("Validation__error_image_height_high %(size)d"),
-                    code="max_height",
-                    params={"size": self.max_size[1]},
+                    _('Validation__error_image_height_high %(size)d'),
+                    code='max_height',
+                    params={'size': self.max_size[1]},
                 )
             )
         if errors:
             errors.append(
                 ValidationError(
-                    _(
-                        "Validation__image_dimensions "
-                        "%(min_width)s %(min_height)s "
-                        "%(max_width)s %(max_height)s"
-                    ),
-                    code="image_dimensions",
+                    _('Validation__image_dimensions ' '%(min_width)s %(min_height)s ' '%(max_width)s %(max_height)s'),
+                    code='image_dimensions',
                     params={
-                        "min_width": self.min_size[0],
-                        "min_height": self.min_size[1],
-                        "max_width": self.max_size[0],
-                        "max_height": self.max_size[1],
+                        'min_width': self.min_size[0],
+                        'min_height': self.min_size[1],
+                        'max_width': self.max_size[0],
+                        'max_height': self.max_size[1],
                     },
                 )
             )
 
         if self.square and image.width != image.height:
-            errors.append(ValidationError(_("Validation__error_image_square")))
+            errors.append(ValidationError(_('Validation__error_image_square')))
         if errors:
             raise ValidationError(errors)
         else:
diff --git a/src/core/views/auth.py b/src/core/views/auth.py
index 8d9e469e50ad9eb8420a79a9fd91ad844a906e66..427f923adef7ae403f4c21bc1537b9776a9f8163 100644
--- a/src/core/views/auth.py
+++ b/src/core/views/auth.py
@@ -1,8 +1,9 @@
-
 import logging
 from smtplib import SMTPException
 from typing import Any
 
+from django_ratelimit.decorators import ratelimit
+
 from django.contrib import messages
 from django.contrib.auth.views import LoginView, PasswordResetConfirmView, PasswordResetView
 from django.core.exceptions import NON_FIELD_ERRORS, ImproperlyConfigured, ValidationError
@@ -16,7 +17,6 @@ from django.utils.translation import gettext_lazy as _
 from django.views.decorators.cache import never_cache
 from django.views.decorators.debug import sensitive_post_parameters
 from django.views.generic import FormView, View
-from django_ratelimit.decorators import ratelimit
 
 from core.forms import LoginForm, PasswordResetForm, RegistrationForm
 from core.models import PlatformUser, UserCommunicationChannel
diff --git a/src/hub/settings/base.py b/src/hub/settings/base.py
index a9053c10bd6faca9c71be1691c0e49e32557106f..6836235722a61d1ab1245fa536a686efbbd5f5af 100644
--- a/src/hub/settings/base.py
+++ b/src/hub/settings/base.py
@@ -10,15 +10,15 @@ For the full list of settings and their values, see
 https://docs.djangoproject.com/en/3.1/ref/settings/
 """
 
-from email.utils import getaddresses
 import json
 import os
-from pathlib import Path
 import re
+from email.utils import getaddresses
+from pathlib import Path
 
-from django.utils.translation import gettext_lazy as _
 import environ
 
+from django.utils.translation import gettext_lazy as _
 
 SCRIPT_NAME = os.getenv('SCRIPT_NAME', '')
 
@@ -38,28 +38,22 @@ env = environ.FileAwareEnv(
     CLIENT_IP_HEADER=(str, None),
     DISABLE_RATELIMIT=(bool, False),
     BADGE_RATE_LIMIT=(str, '180/h'),
-
     SENTRY_ENDPOINT=(str, None),
     SENTRY_TRACES_SAMPLE_RATE=(float, 0.05),  # create a trace for this percentage of all requests
     SENTRY_PROFILES_SAMPLE_RATE=(float, 0.50),  # do a profiling for this percentage of _traced_ requests
-
     SSO_SECRET=(str, None),
     SSO_SECRET_GENERATE=(bool, False),
     PRETIX_ISSUER=(str, 'tickets.events.ccc.de'),
     PRETIX_SECRET=(str, None),
-
     METRICS_SERVER_IPS=(list, ['*']),
     TIMEZONE=(str, 'Europe/Berlin'),
-
     BIGBLUEBUTTON=(bool, False),
     BIGBLUEBUTTON_API_URL=(str, None),
     BIGBLUEBUTTON_API_TOKEN=(str, None),
     BIGBLUEBUTTON_END_MEETING_CALLBACK=(str, None),
     BIGBLUEBUTTON_INITIAL_PRESENTATION_URL=(str, None),
-
     HANGAR=(bool, False),
     HANGAR_URL=(str, None),
-
     WORKADVENTURE=(bool, False),
     WORKADVENTURE_URL_SCHEME_GENERAL=(str, 'http://visit.at.localhost:8080/'),
     WORKADVENTURE_URL_SCHEME_ASSEMBLY=(
@@ -84,7 +78,6 @@ env = environ.FileAwareEnv(
     WORKADVENTURE_MAPSERVICE_LOGLEVEL_URL=(str, None),
     WORKADVENTURE_MAPSERVICE_LOGLEVEL_AUTHORIZATION=(str, None),
     WORKADVENTURE_MAPSERVICE_LOGLEVEL_NOVERIFY=(bool, True),
-
     ADMIN_BASE_URL=(str, '/c3admin'),
     API_BASE_URL=(str, '/api'),
     BACKOFFICE_BASE_URL=(str, '/backoffice'),
@@ -94,22 +87,17 @@ env = environ.FileAwareEnv(
     PLAINUI_DEREFERER_ALLOWLIST=(list, []),
     PLAINUI_DEREFERER_COUNTLIST=(list, []),
     PLAINUI_THEME_SUPPORT=(bool, False),
-
     SHIBBOLEET_WA_ROOM_ID=('str', None),
-
     ASSEMBLY_BANNER_FILE_SIZE_LIMIT=(int, 2),
     ASSEMBLY_BANNER_WIDTH_MINIMUM=(int, 100),
     ASSEMBLY_BANNER_HEIGHT_MINIMUM=(int, 100),
     ASSEMBLY_BANNER_WIDTH_MAXIMUM=(int, 2048),
     ASSEMBLY_BANNER_HEIGHT_MAXIMUM=(int, 1024),
-
     BADGE_IMAGE_WIDTH_MINIMUM=(int, 256),
     BADGE_IMAGE_HEIGHT_MINIMUM=(int, 256),
     BADGE_IMAGE_WIDTH_MAXIMUM=(int, 512),
     BADGE_IMAGE_HEIGHT_MAXIMUM=(int, 512),
-
     API_USERS=(list, []),
-
     DISABLE_REQUEST_LOGGING=(bool, False),
 )
 
@@ -147,9 +135,7 @@ BASE_DIR = Path(__file__).resolve().parents[2]
 
 if env('DJANGO_ENV_FILE'):
     DJANGO_ENV_FILE_PATH = Path(env('DJANGO_ENV_FILE'))
-    assert (
-        DJANGO_ENV_FILE_PATH.exists()
-    ), f'DJANGO_ENV_FILE path {DJANGO_ENV_FILE_PATH} does not exist!'
+    assert DJANGO_ENV_FILE_PATH.exists(), f'DJANGO_ENV_FILE path {DJANGO_ENV_FILE_PATH} does not exist!'
     environ.Env.read_env(DJANGO_ENV_FILE_PATH)
 
 # prepare SECRET_KEY but must be overridden, i.e. in default.py
@@ -200,7 +186,8 @@ MIDDLEWARE = [
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'core.middleware.TimezoneMiddleware',
-    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',  # TODO drüber nachdenken ob wir die brauchen (ist default an in Django)
+    # TODO drüber nachdenken ob wir die brauchen (ist default an in Django)
+    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',  # noqa: ERA001
 ]
 
 ROOT_URLCONF = 'hub.urls'
@@ -271,9 +258,7 @@ SESSION_COOKIE_HTTPONLY = True  # session cookie is unavailable to JavaScript (d
 SESSION_COOKIE_SAMESITE = 'Lax'  # set SameSite=Lax (default)
 SESSION_COOKIE_PATH = env('COOKIE_PATH') or '/'  # use configured path, SESSION_NAME or default '/'
 SESSION_COOKIE_SECURE = True  # mark session cookie as https-only
-SESSION_SAVE_EVERY_REQUEST = (
-    False  # no need to update a session on each request (default)
-)
+SESSION_SAVE_EVERY_REQUEST = False  # no need to update a session on each request (default)
 
 # CSRF Cookie configuration
 CSRF_COOKIE_NAME = env('CSRF_COOKIE_NAME', default=SESSION_COOKIE_NAME.replace('_SESSION', '_CSRF') if '_SESSION' in SESSION_COOKIE_NAME else 'HUB_CSRF')
@@ -291,7 +276,9 @@ OAUTH2_PROVIDER = {
 # https://docs.djangoproject.com/en/3.1/topics/i18n/
 
 LANGUAGE_CODE = 'en'
-LANGUAGE_COOKIE_NAME = env('LANGUAGE_COOKIE_NAME', default=SESSION_COOKIE_NAME.replace('_SESSION', '_LANG') if '_SESSION' in SESSION_COOKIE_NAME else 'HUB_LANG')  # noqa:E501
+LANGUAGE_COOKIE_NAME = env(
+    'LANGUAGE_COOKIE_NAME', default=SESSION_COOKIE_NAME.replace('_SESSION', '_LANG') if '_SESSION' in SESSION_COOKIE_NAME else 'HUB_LANG'
+)  # noqa:E501
 LANGUAGE_COOKIE_PATH = SESSION_COOKIE_PATH
 LANGUAGE_COOKIE_SECURE = SESSION_COOKIE_SECURE
 
@@ -299,8 +286,8 @@ TIME_ZONE = env('TIMEZONE')
 USE_I18N = True
 USE_TZ = True
 LANGUAGES = [
-    ('de', _("German")),
-    ('en', _("English")),
+    ('de', _('German')),
+    ('en', _('English')),
 ]
 
 MODELTRANSLATION_FALLBACK_LANGUAGES = ['en', 'de']
@@ -316,13 +303,13 @@ STATIC_ROOT = BASE_DIR / 'static.dist'
 
 # setup storage, we assume the default
 STORAGES = {
-    "staticfiles": {
-        "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
+    'staticfiles': {
+        'BACKEND': 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage',
     },
 }
 
-if env.bool("USE_PLAIN_STATICFILES", False):
-    STORAGES["staticfiles"]["BACKEND"] = "django.contrib.staticfiles.storage.StaticFilesStorage"
+if env.bool('USE_PLAIN_STATICFILES', False):
+    STORAGES['staticfiles']['BACKEND'] = 'django.contrib.staticfiles.storage.StaticFilesStorage'
 
 storage_type = env('STORAGE_TYPE').lower()
 if storage_type == 's3':
@@ -330,7 +317,7 @@ if storage_type == 's3':
         'BACKEND': 'storages.backends.s3boto3.S3Boto3Storage',
         'OPTIONS': {
             'file_overwrite': False,
-        }
+        },
     }
 
     AWS_STORAGE_BUCKET_NAME = env.str('S3_BUCKET', 'static')
@@ -351,17 +338,18 @@ elif storage_type == 'sftp':
     SFTP_STORAGE_INTERACTIVE = False
     SFTP_STORAGE_PARAMS = env.dict('SFTP_PARAMS')
 
-elif storage_type == 'local' or storage_type == '':
+elif storage_type in ('local', ''):
     if storage_type == '':
         import warnings
+
         warnings.warn('No STORAGE_TYPE selected, defaulting to "local". Verify if that is what you want!', RuntimeWarning)
 
     STORAGES['default'] = {
-        "BACKEND": "django.core.files.storage.FileSystemStorage",
+        'BACKEND': 'django.core.files.storage.FileSystemStorage',
         'OPTIONS': {
             'base_url': env('MEDIA_URL'),
             'location': env.path('MEDIA_PATH', BASE_DIR / 'media'),
-        }
+        },
     }
 
 else:
@@ -412,11 +400,11 @@ FORBIDDEN_ASSEMBLY_SLUGS = ['admin', 'visit', 'maps', 'api', 'pusher']
 
 # Logging configuration
 LOGGING = {
-    "version": 1,
-    "disable_existing_loggers": False,
-    "handlers": {
-        "console": {
-            "class": "logging.StreamHandler",
+    'version': 1,
+    'disable_existing_loggers': False,
+    'handlers': {
+        'console': {
+            'class': 'logging.StreamHandler',
         },
         'null': {
             'class': 'logging.NullHandler',
@@ -428,9 +416,9 @@ LOGGING = {
             'propagate': False,
         },
     },
-    "root": {
-        "handlers": ["console"],
-        "level": env('LOG_LEVEL'),
+    'root': {
+        'handlers': ['console'],
+        'level': env('LOG_LEVEL'),
     },
 }
 
@@ -481,7 +469,7 @@ PRETIX_SECRET_KEY = env('PRETIX_SECRET')  # the JWT shared secret with Pretix
 
 # Restrict the access to /metrics/ to certain IP addresses (e.g. allowe the Prometheus/Grafana server only).
 # You can add one or multiple IP addresse by setting the `METRICS_SERVER_IPS` env var.
-# e.g. METRICS_SERVER_IPS="127.0.0.1,80.147.140.51"
+# e.g. METRICS_SERVER_IPS="127.0.0.1,80.147.140.51" # noqa: ERA001
 # The default is to allow every IP address (METRICS_SERVER_IPS="*").
 METRICS_SERVER_IPS = env('METRICS_SERVER_IPS')
 
@@ -506,14 +494,8 @@ SCHEDULES_SUPPORT_FILE_PROTOCOL = False
 INTEGRATIONS_BBB = env('BIGBLUEBUTTON')
 BIGBLUEBUTTON_API_URL = env('BIGBLUEBUTTON_API_URL')
 BIGBLUEBUTTON_API_TOKEN = env('BIGBLUEBUTTON_API_TOKEN')
-BIGBLUEBUTTON_END_MEETING_CALLBACK = env(
-    'BIGBLUEBUTTON_END_MEETING_CALLBACK'
-)  # url bbb will call to notify us of ending meetings
-# BIGBLUEBUTTON_END_MEETING_CALLBACK = 'https://rc3.world/api/bbb_meeting_end'
-BIGBLUEBUTTON_INITIAL_PRESENTATION_URL = env(
-    'BIGBLUEBUTTON_INITIAL_PRESENTATION_URL'
-)  # url to tell bbb to use as initial presentation
-# BIGBLUEBUTTON_INITIAL_PRESENTATION_URL = 'https://rc3.world/static/plainui/bbb-background.jpg'
+BIGBLUEBUTTON_END_MEETING_CALLBACK = env('BIGBLUEBUTTON_END_MEETING_CALLBACK')  # url bbb will call to notify us of ending meetings
+BIGBLUEBUTTON_INITIAL_PRESENTATION_URL = env('BIGBLUEBUTTON_INITIAL_PRESENTATION_URL')  # url to tell bbb to use as initial presentation
 
 # Hangar
 INTEGRATIONS_HANGAR = env('HANGAR')
@@ -530,40 +512,26 @@ WORKADVENTURE_TERMINATE_OLD_SESSIONS_ON_REGISTER = False
 
 # URL (and optional Authorization-Header value) for pushing maps to exneuland, accepts "%CONFSLUG% variable
 WORKADVENTURE_BACKEND_MAP_PUSH_URL = env('WORKADVENTURE_BACKEND_MAP_PUSH_URL')
-WORKADVENTURE_BACKEND_MAP_PUSH_AUTHORIZATION = env(
-    'WORKADVENTURE_BACKEND_MAP_PUSH_AUTHORIZATION'
-)
+WORKADVENTURE_BACKEND_MAP_PUSH_AUTHORIZATION = env('WORKADVENTURE_BACKEND_MAP_PUSH_AUTHORIZATION')
 
 # URL (and optional Authorization-Header value) for pushing userinfo to exneuland, accepts "%CONFSLUG% and %USERID% variables
 WORKADVENTURE_BACKEND_USERINFO_PUSH_URL = env('WORKADVENTURE_BACKEND_USERINFO_PUSH_URL')
-WORKADVENTURE_BACKEND_USERINFO_PUSH_AUTHORIZATION = env(
-    'WORKADVENTURE_BACKEND_USERINFO_PUSH_AUTHORIZATION'
-)
+WORKADVENTURE_BACKEND_USERINFO_PUSH_AUTHORIZATION = env('WORKADVENTURE_BACKEND_USERINFO_PUSH_AUTHORIZATION')
 
 # URL (and optional Authorization-Header value) for requesting mapservice sync, accepts "%CONFSLUG% variable
 WORKADVENTURE_MAPSERVICE_SYNC_URL = env('WORKADVENTURE_MAPSERVICE_SYNC_URL')
-WORKADVENTURE_MAPSERVICE_SYNC_AUTHORIZATION = env(
-    'WORKADVENTURE_MAPSERVICE_SYNC_AUTHORIZATION'
-)
+WORKADVENTURE_MAPSERVICE_SYNC_AUTHORIZATION = env('WORKADVENTURE_MAPSERVICE_SYNC_AUTHORIZATION')
 WORKADVENTURE_MAPSERVICE_SYNC_NOVERIFY = env('WORKADVENTURE_MAPSERVICE_SYNC_NOVERIFY')
 
 # URL (and optional Authorization-Header value) for requesting mapservice sync of a single room, accepts "%CONFSLUG% and %ROOMID% variable
 WORKADVENTURE_MAPSERVICE_SYNCROOM_URL = env('WORKADVENTURE_MAPSERVICE_SYNCROOM_URL')
-WORKADVENTURE_MAPSERVICE_SYNCROOM_AUTHORIZATION = env(
-    'WORKADVENTURE_MAPSERVICE_SYNCROOM_AUTHORIZATION'
-)
-WORKADVENTURE_MAPSERVICE_SYNCROOM_NOVERIFY = env(
-    'WORKADVENTURE_MAPSERVICE_SYNCROOM_NOVERIFY'
-)
+WORKADVENTURE_MAPSERVICE_SYNCROOM_AUTHORIZATION = env('WORKADVENTURE_MAPSERVICE_SYNCROOM_AUTHORIZATION')
+WORKADVENTURE_MAPSERVICE_SYNCROOM_NOVERIFY = env('WORKADVENTURE_MAPSERVICE_SYNCROOM_NOVERIFY')
 
 # URL (and optional Authorization-Header value) for setting mapservice's loglevel, accepts "%CONFSLUG% variable
 WORKADVENTURE_MAPSERVICE_LOGLEVEL_URL = env('WORKADVENTURE_MAPSERVICE_LOGLEVEL_URL')
-WORKADVENTURE_MAPSERVICE_LOGLEVEL_AUTHORIZATION = env(
-    'WORKADVENTURE_MAPSERVICE_LOGLEVEL_AUTHORIZATION'
-)
-WORKADVENTURE_MAPSERVICE_LOGLEVEL_NOVERIFY = env(
-    'WORKADVENTURE_MAPSERVICE_LOGLEVEL_NOVERIFY'
-)
+WORKADVENTURE_MAPSERVICE_LOGLEVEL_AUTHORIZATION = env('WORKADVENTURE_MAPSERVICE_LOGLEVEL_AUTHORIZATION')
+WORKADVENTURE_MAPSERVICE_LOGLEVEL_NOVERIFY = env('WORKADVENTURE_MAPSERVICE_LOGLEVEL_NOVERIFY')
 
 # push maps to backend upon new data from mapservice?
 WORKADVENTURE_BACKEND_PUSH_ON_MAPSERVICE_DATA = True
@@ -614,15 +582,15 @@ WORKADVENTURE_LOBBY_ROOM_SLUG = 'lobby'
 # flag if newly created pages are localized by default (or not)
 STATIC_PAGE_LOCALIZED_BY_DEFAULT = env.bool('STATICPAGES_LOCALIZED_BY_DEFAULT', False)
 
-PLAINUI_THEME_SUPPORT = env("PLAINUI_THEME_SUPPORT")
+PLAINUI_THEME_SUPPORT = env('PLAINUI_THEME_SUPPORT')
 
-ASSEMBLY_BANNER_FILE_SIZE_LIMIT = env("ASSEMBLY_BANNER_FILE_SIZE_LIMIT")
-ASSEMBLY_BANNER_WIDTH_MINIMUM = env("ASSEMBLY_BANNER_WIDTH_MINIMUM")
-ASSEMBLY_BANNER_HEIGHT_MINIMUM = env("ASSEMBLY_BANNER_HEIGHT_MINIMUM")
-ASSEMBLY_BANNER_WIDTH_MAXIMUM = env("ASSEMBLY_BANNER_WIDTH_MAXIMUM")
-ASSEMBLY_BANNER_HEIGHT_MAXIMUM = env("ASSEMBLY_BANNER_HEIGHT_MAXIMUM")
+ASSEMBLY_BANNER_FILE_SIZE_LIMIT = env('ASSEMBLY_BANNER_FILE_SIZE_LIMIT')
+ASSEMBLY_BANNER_WIDTH_MINIMUM = env('ASSEMBLY_BANNER_WIDTH_MINIMUM')
+ASSEMBLY_BANNER_HEIGHT_MINIMUM = env('ASSEMBLY_BANNER_HEIGHT_MINIMUM')
+ASSEMBLY_BANNER_WIDTH_MAXIMUM = env('ASSEMBLY_BANNER_WIDTH_MAXIMUM')
+ASSEMBLY_BANNER_HEIGHT_MAXIMUM = env('ASSEMBLY_BANNER_HEIGHT_MAXIMUM')
 
-BADGE_IMAGE_WIDTH_MINIMUM = env("BADGE_IMAGE_WIDTH_MINIMUM")
-BADGE_IMAGE_HEIGHT_MINIMUM = env("BADGE_IMAGE_HEIGHT_MINIMUM")
-BADGE_IMAGE_WIDTH_MAXIMUM = env("BADGE_IMAGE_WIDTH_MAXIMUM")
-BADGE_IMAGE_HEIGHT_MAXIMUM = env("BADGE_IMAGE_HEIGHT_MAXIMUM")
+BADGE_IMAGE_WIDTH_MINIMUM = env('BADGE_IMAGE_WIDTH_MINIMUM')
+BADGE_IMAGE_HEIGHT_MINIMUM = env('BADGE_IMAGE_HEIGHT_MINIMUM')
+BADGE_IMAGE_WIDTH_MAXIMUM = env('BADGE_IMAGE_WIDTH_MAXIMUM')
+BADGE_IMAGE_HEIGHT_MAXIMUM = env('BADGE_IMAGE_HEIGHT_MAXIMUM')
diff --git a/src/hub/settings/build.py b/src/hub/settings/build.py
index aedac262f68dd34fdf618d5b075ac7f72f6d5bec..bda3e5fa7ebeb64d1d43241c888b3d226e3cbb8a 100644
--- a/src/hub/settings/build.py
+++ b/src/hub/settings/build.py
@@ -1,6 +1,6 @@
 from hub.settings.base import *  # noqa: F403
 
-SECRET_KEY = "BUILD_KEY"
+SECRET_KEY = 'BUILD_KEY'
 
 # Load apps, so static files can be loaded during build
 INSTALLED_APPS += ['backoffice', 'django_bootstrap5', 'widget_tweaks']  # noqa: F405
diff --git a/src/hub/settings/default.py b/src/hub/settings/default.py
index 84a050b1c299221b70cf6d6f2ff4cebec07be53b..75566aa502cb9e01a638dacfeadf9ea32bbd08e9 100644
--- a/src/hub/settings/default.py
+++ b/src/hub/settings/default.py
@@ -17,7 +17,7 @@ default_env = environ.FileAwareEnv(
 )
 
 
-def print_banner(message: str, filler: str = "*"):
+def print_banner(message: str, filler: str = '*'):
     print(filler * 72)
     print(filler, message)
     print(filler * 72)
@@ -25,8 +25,8 @@ def print_banner(message: str, filler: str = "*"):
 
 # configure database using environment variable "DATABASE_URL"
 DATABASES['default'] = default_env.db(default='postgis://localhost/hub')  # noqa: F405
-if DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql', 'django.contrib.gis.db.backends.postgis'] and default_env("HUB_DB_SCHEMA"):   # noqa:F405
-    DATABASES['default']['OPTIONS'] = {   # noqa:F405
+if DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql', 'django.contrib.gis.db.backends.postgis'] and default_env('HUB_DB_SCHEMA'):  # noqa:F405
+    DATABASES['default']['OPTIONS'] = {  # noqa:F405
         'options': f'-c search_path={default_env("HUB_DB_SCHEMA")}'
     }
 
@@ -36,7 +36,7 @@ if (django_host := default_env('DJANGO_HOST')) is not None:
         ALLOWED_HOSTS.append(django_host)  # noqa:F405
 
 # set flags
-SERVE_MODULE_OUTPUT = default_env("SERVE_MODULE_OUTPUT")
+SERVE_MODULE_OUTPUT = default_env('SERVE_MODULE_OUTPUT')
 IS_ADMIN = default_env('SERVE_ADMIN')
 if SERVE_MODULE_OUTPUT and IS_ADMIN:
     print_banner('Admin module has been selected and will be served', '-')
@@ -67,19 +67,19 @@ except ImportError:
 
 # read SECRET_KEY from a file not stored in git
 if DEBUG and SECRET_KEY is None:  # noqa: F405
-    SECRET_FILE = BASE_DIR.joinpath("hub", ".settings.secret")  # noqa: F405
+    SECRET_FILE = BASE_DIR.joinpath('hub', '.settings.secret')  # noqa: F405
     try:
         with SECRET_FILE.open() as secret_file:
             SECRET_KEY = secret_file.read().strip()
-    except IOError:
+    except OSError:
         try:
             SECRET_KEY = gen_secret_key()  # noqa: F405
-            with SECRET_FILE.open("w") as secret:
+            with SECRET_FILE.open('w') as secret:
                 secret.write(SECRET_KEY)
                 print('*' * 72)
                 print('*', 'Written SECRET_KEY file:', SECRET_FILE)
                 print('*' * 72)
-        except IOError:
+        except OSError:
             Exception('Please create a %s file with random characters to serve as your Django SECRET_KEY!' % SECRET_FILE)
 
 # include API django api
@@ -148,23 +148,20 @@ if SENTRY_ENDPOINT is not None:  # noqa: F405
     sentry_sdk.init(
         dsn=SENTRY_ENDPOINT,  # noqa: F405
         integrations=[DjangoIntegration()],
-
         # trace sample rate in percent
         traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE,  # noqa: F405
-
         # profiling rate (percentage of traces)
         profiles_sample_rate=SENTRY_PROFILES_SAMPLE_RATE,  # noqa: F405
-
         # do not send user information (django.contrib.auth)
-        send_default_pii=False
+        send_default_pii=False,
     )
 
 if IS_API and SSO_SECRET is None:  # noqa: F405
     SSO_SECRET = SECRET_KEY[::-1]
-    if default_env("SSO_WARNING"):
+    if default_env('SSO_WARNING'):
         print_banner('Generated a SSO_SECRET from your SECRET_KEY, this is probably not what you want!')
 
-if '*' in METRICS_SERVER_IPS:   # noqa: F405
+if '*' in METRICS_SERVER_IPS:  # noqa: F405
     print_banner('Warning: /metrics/ is accessible from every IP address ("*"). Set METRICS_SERVER_IPS="<prometheus server ip>".')
 
 TEST_RUNNER = 'core.tests.runner.PostgresSchemaTestRunner'
diff --git a/src/hub/settings/dev.py b/src/hub/settings/dev.py
index 258a2a0baf5679c4fa2c173fd71ec69e5f26d7cc..6948b660a599e51143aeeae6def6c3d0e15d4875 100644
--- a/src/hub/settings/dev.py
+++ b/src/hub/settings/dev.py
@@ -2,19 +2,17 @@ import os
 
 import environ
 
-dev_env = environ.FileAwareEnv(
-    SERVE_DEBUGPY=(bool, False)
-)
-
-if dev_env("SERVE_DEBUGPY"):
+dev_env = environ.FileAwareEnv(SERVE_DEBUGPY=(bool, False))
 
+if dev_env('SERVE_DEBUGPY'):
     # Disable debug warnings for python 3.11 see https://github.com/microsoft/debugpy/issues/861
-    os.environ.setdefault('PYDEVD_DISABLE_FILE_VALIDATION', "1")
+    os.environ.setdefault('PYDEVD_DISABLE_FILE_VALIDATION', '1')
 
     try:
         import debugpy
-        debugpy.listen(("0.0.0.0", 5678))
-        print("debugpy ready for connection at 0.0.0.0:5678")
+
+        debugpy.listen(('0.0.0.0', 5678))
+        print('debugpy ready for connection at 0.0.0.0:5678')
 
     except ImportError:
         print('!' * 72)
@@ -38,7 +36,7 @@ os.environ.setdefault('SERVE_BACKOFFICE', 'yes')
 os.environ.setdefault('SERVE_FRONTEND', 'yes')
 
 # serve metrics locally only (and silence the warning)
-os.environ.setdefault("METRICS_SERVER_IPS", "127.0.0.1")
+os.environ.setdefault('METRICS_SERVER_IPS', '127.0.0.1')
 
 # store media files locally
 os.environ.setdefault('STORAGE_TYPE', 'local')
diff --git a/src/hub/views.py b/src/hub/views.py
index e62038e622f16d0b51b53428e50450ce1902a8ce..c92cfcc1437c36da50fe2a7613de616b350b964f 100644
--- a/src/hub/views.py
+++ b/src/hub/views.py
@@ -1,11 +1,12 @@
 import json
 
+from django_ratelimit.exceptions import Ratelimited
+
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.http import HttpResponse, HttpResponseForbidden, HttpResponseServerError, JsonResponse
 from django.utils.cache import add_never_cache_headers
 from django.views.decorators.http import require_safe
-from django_ratelimit.exceptions import Ratelimited
 
 from hub.http import HttpResponseRateLimited
 
@@ -44,7 +45,7 @@ def index(request):
 def debug_headers(request):
     param = request.GET if request.method not in ['POST', 'DELETE', 'PUT', 'PATCH'] else request.POST
     token = param.get('token', '')
-    if settings.HEADERS_DEBUG_ENDPOINT_TOKEN != token:
+    if token != settings.HEADERS_DEBUG_ENDPOINT_TOKEN:
         return HttpResponse('Wrong token.', 'text/plain', status=403)
 
     request_headers = dict(request.headers)
diff --git a/src/manage.py b/src/manage.py
index b0e2fe530924fbefc4be75e185ea34f9f3330d04..1558aee21b8330e3430f36828ced8ba2e6df798a 100755
--- a/src/manage.py
+++ b/src/manage.py
@@ -14,8 +14,8 @@ def main():
     except ImportError as exc:
         raise ImportError(
             "Couldn't import Django. Are you sure it's installed and "
-            "available on your PYTHONPATH environment variable? Did you "
-            "forget to activate a virtual environment?"
+            'available on your PYTHONPATH environment variable? Did you '
+            'forget to activate a virtual environment?'
         ) from exc
     execute_from_command_line(sys.argv)
 
diff --git a/src/plainui/forms.py b/src/plainui/forms.py
index 8d4a9c34cec493fd540e424116b602723313a9bd..5d717a2e7c71f0b4fb618bf832257dfd95c6a600 100644
--- a/src/plainui/forms.py
+++ b/src/plainui/forms.py
@@ -5,12 +5,22 @@ from django.contrib.auth import forms as auth_forms
 from django.forms import ValidationError
 from django.utils.formats import localize
 from django.utils.timezone import localtime, now
-from django.utils.translation import gettext_lazy as _, gettext
+from django.utils.translation import gettext
+from django.utils.translation import gettext_lazy as _
 
 from core.abuse import REPORT_CATEGORIES, report_content
 from core.base_forms import TranslatedFieldsForm
-from core.models import BadgeToken, BulletinBoardEntry, ConferenceMember, ConferenceMemberTicket, DirectMessage, \
-    PlatformUser, StaticPage, UserCommunicationChannel, UserBadge
+from core.models import (
+    BadgeToken,
+    BulletinBoardEntry,
+    ConferenceMember,
+    ConferenceMemberTicket,
+    DirectMessage,
+    PlatformUser,
+    StaticPage,
+    UserBadge,
+    UserCommunicationChannel,
+)
 from core.models.badges import TOKEN_VALIDATOR
 
 
@@ -18,7 +28,7 @@ class UsernameField(forms.CharField):
     def to_python(self, value):
         user = PlatformUser.objects.filter(username=value).first()
         if user is None:
-            raise ValidationError(gettext("Unknown User!"), code='invalid')
+            raise ValidationError(gettext('Unknown User!'), code='invalid')
         return user
 
 
@@ -30,15 +40,15 @@ class NewDirectMessageForm(forms.Form):
         super().__init__(*args, **kwargs)
 
     in_reply_to = forms.UUIDField(required=False)
-    recipient = UsernameField(widget=forms.TextInput(attrs={'placeholder': _("Please enter the recipient name")}))
-    subject = forms.CharField(max_length=200, min_length=1, strip=True, widget=forms.TextInput(attrs={'placeholder': _("Please enter a subject")}))
+    recipient = UsernameField(widget=forms.TextInput(attrs={'placeholder': _('Please enter the recipient name')}))
+    subject = forms.CharField(max_length=200, min_length=1, strip=True, widget=forms.TextInput(attrs={'placeholder': _('Please enter a subject')}))
     body = forms.CharField(widget=forms.Textarea)
 
     def clean(self):
         if self.conf.send_pn_disabled:
-            raise ValidationError(_("Sending Messages is currently disabled"))
+            raise ValidationError(_('Sending Messages is currently disabled'))
         if self.request.limited:
-            raise ValidationError(_("rate-limited"))
+            raise ValidationError(_('rate-limited'))
 
 
 class BulletinBoardEntryForm(TranslatedFieldsForm):
@@ -52,19 +62,20 @@ class BulletinBoardEntryForm(TranslatedFieldsForm):
         self.user = user
         super().__init__(*args, **kwargs)
 
-    title = forms.CharField(max_length=200, min_length=1, strip=True, widget=forms.TextInput(attrs={'placeholder': _("Please enter a title")}))
+    title = forms.CharField(max_length=200, min_length=1, strip=True, widget=forms.TextInput(attrs={'placeholder': _('Please enter a title')}))
     is_public = forms.BooleanField(required=False)
 
     def clean(self):
         if self.conf.board_disabled:
-            raise ValidationError(_("Bulletin Board is currently disabled"))
+            raise ValidationError(_('Bulletin Board is currently disabled'))
         if self.request.limited:
-            raise ValidationError(_("rate-limited"))
+            raise ValidationError(_('rate-limited'))
 
 
 class ExampleForm(forms.Form):
-    """ used in the component gallery """
-    text = forms.CharField(min_length=1, help_text='Help Text', widget=forms.TextInput(attrs={'placeholder': _("Placeholder text")}))
+    """used in the component gallery"""
+
+    text = forms.CharField(min_length=1, help_text='Help Text', widget=forms.TextInput(attrs={'placeholder': _('Placeholder text')}))
     password = forms.CharField(help_text='Help Text', widget=forms.PasswordInput)
     checkbox = forms.BooleanField(help_text='Help Text')
     textarea = forms.CharField(widget=forms.Textarea, help_text='Help Text')
@@ -72,40 +83,39 @@ class ExampleForm(forms.Form):
     def clean(self):
         super().clean()
         if 'text' not in self.cleaned_data:
-            raise ValidationError("Some general error message of the form")
+            raise ValidationError('Some general error message of the form')
 
 
 class ProfileEditForm(TranslatedFieldsForm):
     class Meta:
         model = PlatformUser
         fields = [
-            'pronouns', 'timezone',
+            'pronouns',
+            'timezone',
         ]
 
 
 class ProfileDescriptionEditForm(TranslatedFieldsForm):
     class Meta:
         model = ConferenceMember
-        fields = [
-            'description'
-        ]
+        fields = ['description']
 
 
 class RedeemBadgeForm(forms.Form):
-    token = forms.CharField(required=True, label="", widget=forms.TextInput(attrs={'placeholder': 'Token'}), validators=[TOKEN_VALIDATOR])
-    purpose = forms.CharField(required=True, label="", widget=forms.HiddenInput(), initial="redeem_token")
+    token = forms.CharField(required=True, label='', widget=forms.TextInput(attrs={'placeholder': 'Token'}), validators=[TOKEN_VALIDATOR])
+    purpose = forms.CharField(required=True, label='', widget=forms.HiddenInput(), initial='redeem_token')
     conference = None
 
     def __init__(self, *args, **kwargs) -> None:
-        self.conference = kwargs.pop("conference", None)
+        self.conference = kwargs.pop('conference', None)
         super().__init__(*args, **kwargs)
 
     def clean_token(self):
-        token = self.cleaned_data["token"]
+        token = self.cleaned_data['token']
         try:
             badge_token = BadgeToken.objects.get(token=token, badge__conference=self.conference)
         except BadgeToken.DoesNotExist:
-            raise ValidationError(_('BadgeToken__does_not_exist %(token)s') % {"token": token})
+            raise ValidationError(_('BadgeToken__does_not_exist %(token)s') % {'token': token})
 
         return badge_token
 
@@ -117,7 +127,7 @@ class ManageBadgeForm(forms.ModelForm):
 
 
 class InputTokenForm(forms.Form):
-    jwt = forms.CharField(max_length=None, required=True, label=_("Token"))
+    jwt = forms.CharField(max_length=None, required=True, label=_('Token'))
 
     def __init__(self, conf, user, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -126,7 +136,7 @@ class InputTokenForm(forms.Form):
 
     def clean_jwt(self):
         if self.conf.end <= now():
-            raise ValidationError(gettext("The Conference is over!"))
+            raise ValidationError(gettext('The Conference is over!'))
         try:
             ConferenceMemberTicket.validate_pretix_ticket(self.conf, self.user, self.cleaned_data['jwt'])
             return self.cleaned_data['jwt']
@@ -143,9 +153,9 @@ class RedeemTokenAddToUserForm(auth_forms.AuthenticationForm):
 
     def clean(self):
         if self.conf.end <= now():
-            raise ValidationError(gettext("The Conference is over!"))
+            raise ValidationError(gettext('The Conference is over!'))
         if self.request.limited:
-            raise ValidationError(_("rate-limited"))
+            raise ValidationError(_('rate-limited'))
         return super().clean()
 
 
@@ -155,16 +165,17 @@ class WorkadventureCompatibleUsernameField(auth_forms.UsernameField):
     Django on the otherhand has a default of max_length=150 for usernames.
     (see https://git.cccv.de/hub/hub/-/issues/228)
     """
+
     def __init__(self, *args, **kwargs):
         # overwrite or set the maximum length
         kwargs['max_length'] = 20
-        return super().__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
 
 class RedeemTokenUserCreateForm(auth_forms.UserCreationForm):
     class Meta:
         model = PlatformUser
-        fields = ("username",)
+        fields = ('username',)
         # Overwrite the auth_forms.UsernameField with its extended version that
         # enforces usernames with less than 20 characters.
         field_classes = {'username': WorkadventureCompatibleUsernameField}
@@ -174,20 +185,23 @@ class RedeemTokenUserCreateForm(auth_forms.UserCreationForm):
     def __init__(self, conf, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.conf = conf
-        self['username'].help_text = \
-            gettext("Required. 20 characters or fewer. Letters, digits and @/./+/-/_ only.") + '<br><b>' \
-            + gettext("Your username is visible to others and cannot be changed later.") + '</b><br>' \
-            + gettext("The username will also be displayed next to your avatar inside the 2D world.")
+        self['username'].help_text = (
+            gettext('Required. 20 characters or fewer. Letters, digits and @/./+/-/_ only.')
+            + '<br><b>'
+            + gettext('Your username is visible to others and cannot be changed later.')
+            + '</b><br>'
+            + gettext('The username will also be displayed next to your avatar inside the 2D world.')
+        )
 
     def clean(self):
         if self.conf.end <= now():
-            raise ValidationError(gettext("The Conference is over!"))
+            raise ValidationError(gettext('The Conference is over!'))
         return super().clean()
 
 
 class TokenPasswortResetForm(auth_forms.SetPasswordForm):
     jwt = forms.CharField(max_length=None, required=True, widget=forms.HiddenInput())
-    username = forms.CharField(required=True, label=_("Username"))
+    username = forms.CharField(required=True, label=_('Username'))
 
     def __init__(self, conf, user, *args, **kwargs):
         super().__init__(user, *args, **kwargs)
@@ -205,13 +219,13 @@ class TokenPasswortResetForm(auth_forms.SetPasswordForm):
 
         user = PlatformUser.objects.filter(username=self.cleaned_data['username']).first()
         if user is None:
-            raise ValidationError(gettext("Unknown User"))
+            raise ValidationError(gettext('Unknown User'))
 
         if not user.is_active:
-            raise ValidationError(gettext("User is not active!"))
+            raise ValidationError(gettext('User is not active!'))
 
         if user.email or user.communication_channels.filter(channel=UserCommunicationChannel.Channel.MAIL, is_verified=True).exists():
-            raise ValidationError(gettext("User can use password reset by email!"))
+            raise ValidationError(gettext('User can use password reset by email!'))
 
         pretix_ident, ticket_data = ConferenceMemberTicket.validate_pretix_ticket(self.conf, self.user, self.cleaned_data['jwt'], validate_only=True)
         if not ConferenceMemberTicket.objects.filter(conference=self.conf, user=user, ident=pretix_ident).exists():
@@ -226,22 +240,25 @@ class RoomChoiceField(forms.ModelChoiceField):
 
     def label_from_instance(self, obj):
         if obj.pk in self.blocked_rooms:
-            return markupsafe.Markup('%s [%s]') % (obj, gettext("blocked"))
+            return markupsafe.Markup('%s [%s]') % (obj, gettext('blocked'))
 
         return str(obj)
 
 
 class ReportForm(forms.Form):
-    kind = forms.ChoiceField(choices=[
-        ('url', 'url'),
-        ('pn', 'pn'),
-        ('board', 'board'),
-    ], widget=forms.HiddenInput())
+    kind = forms.ChoiceField(
+        choices=[
+            ('url', 'url'),
+            ('pn', 'pn'),
+            ('board', 'board'),
+        ],
+        widget=forms.HiddenInput(),
+    )
     kind_data = forms.CharField(min_length=1, widget=forms.HiddenInput())
     next = forms.CharField(widget=forms.HiddenInput(), required=False)
     category = forms.ChoiceField(choices=[(k, v[0]) for k, v in REPORT_CATEGORIES.items()], initial='security')
-    message = forms.CharField(min_length=1, widget=forms.Textarea(), label=_("describe the problem"), required=True)
-    message2 = forms.CharField(min_length=1, widget=forms.Textarea(), label=_("describe a solution"), required=True)
+    message = forms.CharField(min_length=1, widget=forms.Textarea(), label=_('describe the problem'), required=True)
+    message2 = forms.CharField(min_length=1, widget=forms.Textarea(), label=_('describe a solution'), required=True)
 
     def __init__(self, *args, request, conf, **kwargs):
         super().__init__(*args, **kwargs)
@@ -250,7 +267,7 @@ class ReportForm(forms.Form):
 
     def clean(self):
         if self.request.limited:
-            raise ValidationError(_("rate-limited"))
+            raise ValidationError(_('rate-limited'))
         return super().clean()
 
     def send_report_mail(self, request):
@@ -288,7 +305,7 @@ class ReportForm(forms.Form):
             }
 
         else:
-            raise Exception("Unknown kind")
+            raise Exception('Unknown kind')
 
         report_content(
             request=request,
diff --git a/src/plainui/jinja2.py b/src/plainui/jinja2.py
index ae9717c94a77e790a1a5dd7da4bc98bae8c76205..8c155ff7e7399e3093581890455bb2d55ec53dff 100644
--- a/src/plainui/jinja2.py
+++ b/src/plainui/jinja2.py
@@ -1,4 +1,9 @@
 from datetime import datetime, timedelta
+
+from jinja2 import Environment, pass_context
+from jinja2.runtime import Context
+
+from django.contrib.humanize.templatetags.humanize import NaturalTimeFormatter
 from django.contrib.messages import get_messages
 from django.templatetags.static import static
 from django.urls import reverse
@@ -7,12 +12,7 @@ from django.utils.formats import localize
 from django.utils.functional import LazyObject
 from django.utils.html import json_script
 from django.utils.timezone import localdate, localtime
-from django.utils.translation import gettext, ngettext, get_language
-from django.contrib.humanize.templatetags.humanize import NaturalTimeFormatter
-
-from jinja2 import Environment, pass_context
-from jinja2.runtime import Context
-
+from django.utils.translation import get_language, gettext, ngettext
 from modeltranslation.fields import build_localized_fieldname
 from modeltranslation.settings import AVAILABLE_LANGUAGES
 
@@ -122,8 +122,8 @@ def show_vars(ctx, var=_UNSET):
             var = var.__dict__
 
         ret = ''
-        for (k, v) in var.items():
-            ret += '%r: %r\n' % (k, v)
+        for k, v in var.items():
+            ret += f'{k!r}: {v!r}\n'
         return ret
 
     except (AttributeError, TypeError):
@@ -182,29 +182,28 @@ class MyEnvironment(Environment):
 
 
 def environment(**options):
-    env = MyEnvironment(**{
-        'extensions': ["jinja2.ext.i18n", "jinja2.ext.debug"],
-        **options
-    })
-    env.globals.update({
-        'browser_type': browser_type,
-        'css_scope': css_scope,
-        'active_theme': active_theme,
-        'get_language': get_language,
-        'get_messages': get_messages,
-        'hub_absolute': hub_absolute,
-        'json_script': json_script,
-        'num_of_notifications': num_of_notifications,
-        'num_of_unread_messages': num_of_unread_messages,
-        'num_of_pending_badges': num_of_pending_badges,
-        'static': static,
-        'url': url,
-        'show_vars': show_vars,
-        'unique_id': unique_id,
-        'translated_fields_for_field': translated_fields_for_field,
-        'field_translation_languages': field_translation_languages,
-        'now': timezone.now(),
-    })
+    env = MyEnvironment(**{'extensions': ['jinja2.ext.i18n', 'jinja2.ext.debug'], **options})
+    env.globals.update(
+        {
+            'browser_type': browser_type,
+            'css_scope': css_scope,
+            'active_theme': active_theme,
+            'get_language': get_language,
+            'get_messages': get_messages,
+            'hub_absolute': hub_absolute,
+            'json_script': json_script,
+            'num_of_notifications': num_of_notifications,
+            'num_of_unread_messages': num_of_unread_messages,
+            'num_of_pending_badges': num_of_pending_badges,
+            'static': static,
+            'url': url,
+            'show_vars': show_vars,
+            'unique_id': unique_id,
+            'translated_fields_for_field': translated_fields_for_field,
+            'field_translation_languages': field_translation_languages,
+            'now': timezone.now(),
+        }
+    )
     env.filters['strftdelta'] = custom_timedelta
     env.filters['strftdelta_short'] = custom_timedelta_short
     env.filters['strftimehm'] = custom_strftimehm
diff --git a/src/plainui/management/commands/makemessages.py b/src/plainui/management/commands/makemessages.py
index a9f8a891aa66763030b416b4237fb3b65f92d50a..80aa8fe8fb42acf76e12dfb89d2f10abac010f38 100644
--- a/src/plainui/management/commands/makemessages.py
+++ b/src/plainui/management/commands/makemessages.py
@@ -1,10 +1,10 @@
 import re
+from io import StringIO
+from pathlib import Path
 
 from babel.messages.extract import extract
 from babel.messages.pofile import read_po, write_po
 from jinja2.ext import babel_extract
-from io import StringIO
-from pathlib import Path
 
 from django.core.management.commands import makemessages
 
diff --git a/src/plainui/tests/test_views.py b/src/plainui/tests/test_views.py
index 35add5230dbedecf4ce4e9a6b5b235007202c7d1..2d042352651366d44c99559c6baf0f99e521cc75 100644
--- a/src/plainui/tests/test_views.py
+++ b/src/plainui/tests/test_views.py
@@ -57,7 +57,7 @@ from plainui.views import ConferenceRequiredMixin, LandingView
 
 
 # from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
-class Pattern(object):
+class Pattern:
     def __req__(self, lhs):
         return self.__eq__(lhs)
 
@@ -66,7 +66,7 @@ class Pattern(object):
 
 # from https://github.com/Grollicus/unittest_patterns/blob/master/unittest_patterns/__init__.py
 class Any(Pattern):
-    """ Equals everything """
+    """Equals everything"""
 
     def __eq__(self, rhs):
         return True
@@ -200,7 +200,7 @@ class ViewsTestBase(TestCase):
 
     def assertSetsMessage(self, request, msg: str, level=messages.SUCCESS):
         msgs = list(messages.get_messages(request.wsgi_request))
-        self.assertEqual(len(msgs), 1, "Expected exactly one Message")
+        self.assertEqual(len(msgs), 1, 'Expected exactly one Message')
         self.assertEqual(str(msgs[0].message), msg)
         self.assertEqual(msgs[0].level, level)
 
@@ -218,20 +218,35 @@ class ViewsTest(ViewsTestBase):
         tag_hidden.save()
 
         event = Event(
-            conference=self.conf, slug='event1', assembly=assembly, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            slug='event1',
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             kind=Event.Kind.OFFICIAL,
         )
         event.save()
         suggested_event = Event(
-            conference=self.conf, slug='suggested', assembly=assembly, name='Event1_2', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 1, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            slug='suggested',
+            assembly=assembly,
+            name='Event1_2',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 1, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             kind=Event.Kind.OFFICIAL,
         )
         suggested_event.save()
         suggested_event2 = Event(
-            conference=self.conf, slug='suggested2', assembly=assembly, name='Event1_3', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 2, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            slug='suggested2',
+            assembly=assembly,
+            name='Event1_3',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 2, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             kind=Event.Kind.OFFICIAL,
         )
         suggested_event2.save()
@@ -280,13 +295,21 @@ class ViewsTest(ViewsTestBase):
         tag.save()
 
         event1 = Event(
-            conference=self.conf, assembly=assembly1, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly1,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event1.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly1, name='Event1_2', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly1,
+            name='Event1_2',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
 
@@ -319,13 +342,21 @@ class ViewsTest(ViewsTestBase):
         tag_item = TagItem(tag=tag, target_type=ContentType.objects.get_for_model(Assembly), target_id=assembly.pk)
         tag_item.save()
         event1 = Event(
-            conference=self.conf, assembly=assembly, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event1.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly, name='Event1_2', is_public=False,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_2',
+            is_public=False,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
         AssemblyLikeCount(assembly1=assembly, assembly2=assembly, likes=10, like_ratio=1).save()
@@ -390,23 +421,51 @@ class ViewsTest(ViewsTestBase):
         room_public.save()
 
         event = Event(
-            conference=self.conf, assembly=assembly, room=room, slug='Event1_1', name='Event1_1', is_public=True, kind=Event.Kind.ASSEMBLY,
-            schedule_start=datetime(2020, 1, 2, 0, 45, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            room=room,
+            slug='Event1_1',
+            name='Event1_1',
+            is_public=True,
+            kind=Event.Kind.ASSEMBLY,
+            schedule_start=datetime(2020, 1, 2, 0, 45, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly, room=room, slug='Event1_2', name='Event1_2', is_public=True, kind=Event.Kind.OFFICIAL,
-            schedule_start=datetime(2020, 1, 2, 0, 50, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            room=room,
+            slug='Event1_2',
+            name='Event1_2',
+            is_public=True,
+            kind=Event.Kind.OFFICIAL,
+            schedule_start=datetime(2020, 1, 2, 0, 50, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
         event3 = Event(
-            conference=self.conf, assembly=assembly, room=room_public, slug='Event1_3', name='Event1_3', is_public=True, kind=Event.Kind.OFFICIAL,
-            schedule_start=datetime(2020, 1, 2, 0, 55, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            room=room_public,
+            slug='Event1_3',
+            name='Event1_3',
+            is_public=True,
+            kind=Event.Kind.OFFICIAL,
+            schedule_start=datetime(2020, 1, 2, 0, 55, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event3.save()
         event4 = Event(
-            conference=self.conf, assembly=assembly, room=room, slug='Event1_4', name='Event1_4', is_public=False, kind=Event.Kind.OFFICIAL,
-            schedule_start=datetime(2020, 1, 2, 1, 0, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            room=room,
+            slug='Event1_4',
+            name='Event1_4',
+            is_public=False,
+            kind=Event.Kind.OFFICIAL,
+            schedule_start=datetime(2020, 1, 2, 1, 0, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event4.save()
 
@@ -748,9 +807,9 @@ class ViewsTest(ViewsTestBase):
         self.assertEqual(sp_de.revisions.count(), 1)
 
         # Preview
-        resp = self.client.post(reverse('plainui:static_page_edit', kwargs={'page_slug': sp.slug}), {
-                'preview': 'true', 'title': 'New Title Preview', 'body': 'New Body Preview'
-            })
+        resp = self.client.post(
+            reverse('plainui:static_page_edit', kwargs={'page_slug': sp.slug}), {'preview': 'true', 'title': 'New Title Preview', 'body': 'New Body Preview'}
+        )
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['page'], sp)
         self.assertEqual(resp.context_data['page_slug'], sp.slug)
@@ -851,8 +910,9 @@ class ViewsTest(ViewsTestBase):
         self.assertEqual(spr.author, self.user)
 
         with override_settings(STATIC_PAGE_LOCALIZED_BY_DEFAULT=True):
-            resp = self.client.post(reverse('plainui:static_page_edit', kwargs={'page_slug': 'test_new2'}),
-                                    {'title': 'Some New Page!', 'body': 'Some New Page!'})
+            resp = self.client.post(
+                reverse('plainui:static_page_edit', kwargs={'page_slug': 'test_new2'}), {'title': 'Some New Page!', 'body': 'Some New Page!'}
+            )
             self.assertRedirects(resp, reverse('plainui:static_page', kwargs={'page_slug': 'test_new2'}))
 
             sp_new2 = StaticPage.objects.get(slug='test_new2')
@@ -860,19 +920,18 @@ class ViewsTest(ViewsTestBase):
             self.assertEqual(sp_new2.language, 'en')
 
         # page links to nonexistent page, that then gets created, after which the link should be updated to refrect that the link target now exists
-        resp = self.client.post(reverse('plainui:static_page_edit', kwargs={'page_slug': 'test_linking'}), {
-            'title': 'Linking to other Pages test!',
-            'body': '[[test_linking_second_page]]'
-        })
+        resp = self.client.post(
+            reverse('plainui:static_page_edit', kwargs={'page_slug': 'test_linking'}),
+            {'title': 'Linking to other Pages test!', 'body': '[[test_linking_second_page]]'},
+        )
         self.assertRedirects(resp, reverse('plainui:static_page', kwargs={'page_slug': 'test_linking'}))
         self.assertSetsMessage(resp, 'Created Wiki Page', level=messages.SUCCESS)
         sp_new = StaticPage.objects.get(slug='test_linking')
         self.assertIn('internal-nonexist', sp_new.body_html)
         self.assertEqual(MarkdownMeta.objects.filter(src_type=ContentType.objects.get_for_model(StaticPage), src_object_id=sp_new.pk).count(), 1)
-        resp = self.client.post(reverse('plainui:static_page_edit', kwargs={'page_slug': 'test_linking_second_page'}), {
-            'title': 'Linked to test!',
-            'body': 'asdf'
-        })
+        resp = self.client.post(
+            reverse('plainui:static_page_edit', kwargs={'page_slug': 'test_linking_second_page'}), {'title': 'Linked to test!', 'body': 'asdf'}
+        )
         sp_new.refresh_from_db()
         self.assertNotIn('internal-nonexist', sp_new.body_html)
         self.assertIn('internal', sp_new.body_html)
@@ -990,8 +1049,12 @@ class ViewsTest(ViewsTestBase):
         assembly = Assembly(conference=self.conf, slug='assembly1', name='asdf', state_assembly=Assembly.State.PLACED)
         assembly.save()
         event = Event(
-            conference=self.conf, assembly=assembly, name='asdf', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='asdf',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event.save()
         tag = ConferenceTag(conference=self.conf, slug='aSdf', is_public=True)
@@ -1005,31 +1068,40 @@ class ViewsTest(ViewsTestBase):
         revision.set_public()
 
         self.assertNeedsLogin(reverse('plainui:search'), data={'q': 'asdf'}, post=True)
-        resp = self.client.post(reverse('plainui:search'), {
-            'q': 'asdf'
-        })
+        resp = self.client.post(reverse('plainui:search'), {'q': 'asdf'})
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['search_query'], 'asdf')
-        self.assertEqual(sorted(resp.context_data['search_results'], key=lambda r: r['type']), [
-            {'type': 'Assembly', 'item': assembly},
-            {'type': 'ConferenceTag', 'item': tag},
-            {'type': 'ConferenceTrack', 'item': track},
-            {'type': 'Event', 'item': event},
-            {'type': 'StaticPage', 'item': page},
-        ])
+        self.assertEqual(
+            sorted(resp.context_data['search_results'], key=lambda r: r['type']),
+            [
+                {'type': 'Assembly', 'item': assembly},
+                {'type': 'ConferenceTag', 'item': tag},
+                {'type': 'ConferenceTrack', 'item': track},
+                {'type': 'Event', 'item': event},
+                {'type': 'StaticPage', 'item': page},
+            ],
+        )
 
     @override_locale('en')
     def test_ProfileView_get(self):
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state_assembly=Assembly.State.PLACED)
         assembly.save()
         event = Event(
-            conference=self.conf, assembly=assembly, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly, name='Event1_2', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_2',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
         badge = Badge(conference=self.conf, name='badge-name', issuing_assembly=assembly)
@@ -1075,18 +1147,25 @@ class ViewsTest(ViewsTestBase):
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state_assembly=Assembly.State.PLACED)
         assembly.save()
         event = Event(
-            conference=self.conf, assembly=assembly, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event.save()
 
         self.assertNeedsLogin(reverse('plainui:userprofile'), post=True, check_user=True)
-        resp = self.client.post(reverse('plainui:userprofile'), {
-            'description_de': 'new_description de',
-            'description_en': 'new_description en',
-            'pronouns': 'they',
-            'timezone': 'Europe/Berlin',
-        })
+        resp = self.client.post(
+            reverse('plainui:userprofile'),
+            {
+                'description_de': 'new_description de',
+                'description_en': 'new_description en',
+                'pronouns': 'they',
+                'timezone': 'Europe/Berlin',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:userprofile'))
         self.user.refresh_from_db()
         self.conference_member.refresh_from_db()
@@ -1107,29 +1186,38 @@ class ViewsTest(ViewsTestBase):
         self.assertNeedsLogin(reverse('plainui:modify_theme'), post=True)
 
         # setting theme works
-        resp = self.client.post(reverse('plainui:modify_theme'), {
-            'theme': PlatformUser.Theme.LIGHT,
-            'next': reverse('plainui:landing'),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_theme'),
+            {
+                'theme': PlatformUser.Theme.LIGHT,
+                'next': reverse('plainui:landing'),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:landing'))
         self.user.refresh_from_db()
         self.assertEqual(self.user.theme, PlatformUser.Theme.LIGHT)
         self.assertEqual(self.client.session['theme'], PlatformUser.Theme.LIGHT)
 
         # invalid destination redirects to index
-        resp = self.client.post(reverse('plainui:modify_theme'), {
-            'theme': PlatformUser.Theme.LIGHT,
-            'next': 'https://www.google.de/',
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_theme'),
+            {
+                'theme': PlatformUser.Theme.LIGHT,
+                'next': 'https://www.google.de/',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.user.refresh_from_db()
         self.assertEqual(self.user.theme, PlatformUser.Theme.LIGHT)
         self.assertEqual(self.client.session['theme'], PlatformUser.Theme.LIGHT)
 
         # invalid theme does not change anything, next defaults to index
-        resp = self.client.post(reverse('plainui:modify_theme'), {
-            'theme': 'doesnotexist',
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_theme'),
+            {
+                'theme': 'doesnotexist',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.user.refresh_from_db()
         self.assertEqual(self.user.theme, PlatformUser.Theme.LIGHT)
@@ -1142,13 +1230,16 @@ class ViewsTest(ViewsTestBase):
         self.conf.start = datetime(2020, 12, 27, 0, 0, 0, tzinfo=UTC)
         self.conf.end = datetime(2020, 12, 31, 23, 59, 0, tzinfo=UTC)
         self.conf.save()
-        valid_token = jwt.encode({
-            'aud': self.conf.slug,
-            'exp': datetime.now(UTC) + timedelta(hours=1),
-            'iat': datetime.now(UTC) - timedelta(hours=1),
-            'iss': 'tickets.events.ccc.de',
-            'uid': 'blblbl123',
-        }, key='^v^')
+        valid_token = jwt.encode(
+            {
+                'aud': self.conf.slug,
+                'exp': datetime.now(UTC) + timedelta(hours=1),
+                'iat': datetime.now(UTC) - timedelta(hours=1),
+                'iss': 'tickets.events.ccc.de',
+                'uid': 'blblbl123',
+            },
+            key='^v^',
+        )
         invalid_token = (
             'fyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJzbHVnMSIsImV4cCI6MTYwODc2ODMwNywiaWF0IjoxNjA'
             '4NzYxMTA3LCJpc3MiOiJ0aWNrZXRzLmV2ZW50cy5jY2MuZGUiLCJ1aWQiOiJibGJsYmwxMjMifQ.QglU9dLHbIAOJ2e8hj_HSc6rZBpAcTrPbvY2H7HXnFw'
@@ -1211,13 +1302,16 @@ class ViewsTest(ViewsTestBase):
         self.user.set_password('test')
         self.user.save()
         ConferenceMember.objects.all().delete()
-        valid_token = jwt.encode({
-            'aud': self.conf.slug,
-            'exp': datetime.now(UTC) + timedelta(hours=1),
-            'iat': datetime.now(UTC) - timedelta(hours=1),
-            'iss': 'tickets.events.ccc.de',
-            'uid': 'blblbl123',
-        }, key='^v^')
+        valid_token = jwt.encode(
+            {
+                'aud': self.conf.slug,
+                'exp': datetime.now(UTC) + timedelta(hours=1),
+                'iat': datetime.now(UTC) - timedelta(hours=1),
+                'iss': 'tickets.events.ccc.de',
+                'uid': 'blblbl123',
+            },
+            key='^v^',
+        )
 
         # get-redirect
         resp = self.client.get(reverse('plainui:redeem_token_add_to_user'))
@@ -1228,11 +1322,7 @@ class ViewsTest(ViewsTestBase):
             'fyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJzbHVnMSIsImV4cCI6MTYwODc2ODMwNywiaWF0IjoxNjA'
             '4NzYxMTA3LCJpc3MiOiJ0aWNrZXRzLmV2ZW50cy5jY2MuZGUiLCJ1aWQiOiJibGJsYmwxMjMifQ.QglU9dLHbIAOJ2e8hj_HSc6rZBpAcTrPbvY2H7HXnFw'
         )
-        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {
-            'token': invalid_token,
-            'username': self.user.username,
-            'password': 'test'
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {'token': invalid_token, 'username': self.user.username, 'password': 'test'})
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['step'], 'user')
         self.assertEqual(list(resp.context_data['form_add'].non_field_errors()), ['Could not verify ticket. (Invalid Ticket)'])
@@ -1241,11 +1331,7 @@ class ViewsTest(ViewsTestBase):
         # conference already over
         self.conf.end = datetime(2020, 12, 28, 0, 0, 0, tzinfo=UTC)
         self.conf.save()
-        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {
-            'token': valid_token,
-            'username': self.user.username,
-            'password': 'test'
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {'token': valid_token, 'username': self.user.username, 'password': 'test'})
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['step'], 'user')
         self.assertEqual(list(resp.context_data['form_add'].non_field_errors()), ['The Conference is over!'])
@@ -1254,11 +1340,7 @@ class ViewsTest(ViewsTestBase):
         self.conf.save()
 
         # submit valid token and join conference
-        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {
-            'token': valid_token,
-            'username': self.user.username,
-            'password': 'test'
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {'token': valid_token, 'username': self.user.username, 'password': 'test'})
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertTrue(ConferenceMemberTicket.objects.filter(conference=self.conf, user=self.user, ident='blblbl123').exists())
         self.assertTrue(ConferenceMember.objects.filter(conference=self.conf, user=self.user).exists())
@@ -1266,11 +1348,7 @@ class ViewsTest(ViewsTestBase):
         # user already has a ConferenceMember but no ticket -> add ticket but don't create second ConferenceMember
         ConferenceMemberTicket.objects.all().delete()
         self.assertEqual(ConferenceMember.objects.filter(conference=self.conf, user=self.user).count(), 1)
-        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {
-            'token': valid_token,
-            'username': self.user.username,
-            'password': 'test'
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {'token': valid_token, 'username': self.user.username, 'password': 'test'})
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertTrue(ConferenceMemberTicket.objects.filter(conference=self.conf, user=self.user, ident='blblbl123').exists())
         self.assertEqual(ConferenceMember.objects.filter(conference=self.conf, user=self.user).count(), 1)
@@ -1280,20 +1358,12 @@ class ViewsTest(ViewsTestBase):
             for i in range(5):
                 ConferenceMemberTicket.objects.all().delete()
                 ConferenceMember.objects.all().delete()
-                resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {
-                    'token': valid_token,
-                    'username': self.user.username,
-                    'password': 'test'
-                })
+                resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {'token': valid_token, 'username': self.user.username, 'password': 'test'})
                 self.assertRedirects(resp, reverse('plainui:index'))
 
             ConferenceMemberTicket.objects.all().delete()
             ConferenceMember.objects.all().delete()
-            resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {
-                'token': valid_token,
-                'username': self.user.username,
-                'password': 'test'
-            })
+            resp = self.client.post(reverse('plainui:redeem_token_add_to_user'), {'token': valid_token, 'username': self.user.username, 'password': 'test'})
             self.assertIsInstance(resp, HttpResponseRateLimited)
 
     @override_settings(LANGUAGE_CODE='en', PRETIX_SECRET_KEY='^v^', PRETIX_ISSUER='tickets.events.ccc.de', AUTH_PASSWORD_VALIDATORS=[])
@@ -1308,37 +1378,34 @@ class ViewsTest(ViewsTestBase):
             'fyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJzbHVnMSIsImV4cCI6MTYwODc2ODMwNywiaWF0IjoxNjA'
             '4NzYxMTA3LCJpc3MiOiJ0aWNrZXRzLmV2ZW50cy5jY2MuZGUiLCJ1aWQiOiJibGJsYmwxMjMifQ.QglU9dLHbIAOJ2e8hj_HSc6rZBpAcTrPbvY2H7HXnFw'
         )
-        valid_token = jwt.encode({
-            'aud': self.conf.slug,
-            'exp': datetime.now(UTC) + timedelta(hours=1),
-            'iat': datetime.now(UTC) - timedelta(hours=1),
-            'iss': 'tickets.events.ccc.de',
-            'uid': 'blblbl123',
-        }, key='^v^')
+        valid_token = jwt.encode(
+            {
+                'aud': self.conf.slug,
+                'exp': datetime.now(UTC) + timedelta(hours=1),
+                'iat': datetime.now(UTC) - timedelta(hours=1),
+                'iss': 'tickets.events.ccc.de',
+                'uid': 'blblbl123',
+            },
+            key='^v^',
+        )
 
         # get-redirect
         resp = self.client.get(reverse('plainui:redeem_token_create_user'))
         self.assertRedirects(resp, reverse('plainui:redeem_token'))
 
         # invalid token
-        resp = self.client.post(reverse('plainui:redeem_token_create_user'), {
-            'token': invalid_token,
-            'username': 'testuser2',
-            'password1': 'test',
-            'password2': 'test'
-        })
+        resp = self.client.post(
+            reverse('plainui:redeem_token_create_user'), {'token': invalid_token, 'username': 'testuser2', 'password1': 'test', 'password2': 'test'}
+        )
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['step'], 'user')
         self.assertTrue(resp.context_data['form_add'])
         self.assertEqual(list(resp.context_data['form_create'].non_field_errors()), ['Could not verify ticket. (Invalid Ticket)'])
 
         # submit valid token but username exists already
-        resp = self.client.post(reverse('plainui:redeem_token_create_user'), {
-            'token': valid_token,
-            'username': self.user.username,
-            'password1': 'test',
-            'password2': 'test'
-        })
+        resp = self.client.post(
+            reverse('plainui:redeem_token_create_user'), {'token': valid_token, 'username': self.user.username, 'password1': 'test', 'password2': 'test'}
+        )
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['step'], 'user')
         self.assertTrue(resp.context_data['form_add'])
@@ -1347,12 +1414,9 @@ class ViewsTest(ViewsTestBase):
         # valid token but conference already over
         self.conf.end = datetime(2020, 12, 28, 0, 0, 0, tzinfo=UTC)
         self.conf.save()
-        resp = self.client.post(reverse('plainui:redeem_token_create_user'), {
-            'token': valid_token,
-            'username': self.user.username,
-            'password1': 'test',
-            'password2': 'test'
-        })
+        resp = self.client.post(
+            reverse('plainui:redeem_token_create_user'), {'token': valid_token, 'username': self.user.username, 'password1': 'test', 'password2': 'test'}
+        )
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['step'], 'user')
         self.assertTrue(resp.context_data['form_add'])
@@ -1361,12 +1425,9 @@ class ViewsTest(ViewsTestBase):
         self.conf.save()
 
         # submit valid token and join conference
-        resp = self.client.post(reverse('plainui:redeem_token_create_user'), {
-            'token': valid_token,
-            'username': 'testuser2',
-            'password1': 'test',
-            'password2': 'test'
-        })
+        resp = self.client.post(
+            reverse('plainui:redeem_token_create_user'), {'token': valid_token, 'username': 'testuser2', 'password1': 'test', 'password2': 'test'}
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         user = PlatformUser.objects.get(username='testuser2')
         self.assertTrue(ConferenceMemberTicket.objects.filter(conference=self.conf, user=user, ident='blblbl123').exists())
@@ -1384,13 +1445,16 @@ class ViewsTest(ViewsTestBase):
             'fyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJzbHVnMSIsImV4cCI6MTYwODc2ODMwNywiaWF0IjoxNjA'
             '4NzYxMTA3LCJpc3MiOiJ0aWNrZXRzLmV2ZW50cy5jY2MuZGUiLCJ1aWQiOiJibGJsYmwxMjMifQ.QglU9dLHbIAOJ2e8hj_HSc6rZBpAcTrPbvY2H7HXnFw'
         )
-        valid_token = jwt.encode({
-            'aud': self.conf.slug,
-            'exp': datetime.now(UTC) + timedelta(hours=1),
-            'iat': datetime.now(UTC) - timedelta(hours=1),
-            'iss': 'tickets.events.ccc.de',
-            'uid': 'blblbl123',
-        }, key='^v^')
+        valid_token = jwt.encode(
+            {
+                'aud': self.conf.slug,
+                'exp': datetime.now(UTC) + timedelta(hours=1),
+                'iat': datetime.now(UTC) - timedelta(hours=1),
+                'iss': 'tickets.events.ccc.de',
+                'uid': 'blblbl123',
+            },
+            key='^v^',
+        )
 
         self.assertNeedsLogin(reverse('plainui:redeem_token_loggedin'), check_conference_member=False, check_user=True)
         resp = self.client.get(reverse('plainui:redeem_token_loggedin'))
@@ -1399,25 +1463,19 @@ class ViewsTest(ViewsTestBase):
         # invalid token
         self.conf.end = datetime(2020, 12, 28, 0, 0, 0, tzinfo=UTC)
         self.conf.save()
-        resp = self.client.post(reverse('plainui:redeem_token_loggedin'), {
-            'token': invalid_token
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_loggedin'), {'token': invalid_token})
         self.assertRedirects(resp, reverse('plainui:redeem_token'))
         self.assertSetsMessage(resp, 'The Conference is over!', level=messages.ERROR)
         self.conf.end = datetime(2020, 12, 31, 23, 59, 0, tzinfo=UTC)
         self.conf.save()
 
         # valid token but conference over
-        resp = self.client.post(reverse('plainui:redeem_token_loggedin'), {
-            'token': invalid_token
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_loggedin'), {'token': invalid_token})
         self.assertRedirects(resp, reverse('plainui:redeem_token'))
         self.assertSetsMessage(resp, 'Could not verify ticket. (Invalid Ticket)', level=messages.ERROR)
 
         # valid token
-        resp = self.client.post(reverse('plainui:redeem_token_loggedin'), {
-            'token': valid_token
-        })
+        resp = self.client.post(reverse('plainui:redeem_token_loggedin'), {'token': valid_token})
         self.assertTrue(ConferenceMemberTicket.objects.filter(conference=self.conf, user=self.user, ident='blblbl123').exists())
         self.assertTrue(ConferenceMember.objects.filter(conference=self.conf, user=self.user).exists())
         self.assertRedirects(resp, reverse('plainui:index'))
@@ -1444,42 +1502,54 @@ class ViewsTest(ViewsTestBase):
             'fyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJzbHVnMSIsImV4cCI6MTYwODc2ODMwNywiaWF0IjoxNjA'
             '4NzYxMTA3LCJpc3MiOiJ0aWNrZXRzLmV2ZW50cy5jY2MuZGUiLCJ1aWQiOiJibGJsYmwxMjMifQ.QglU9dLHbIAOJ2e8hj_HSc6rZBpAcTrPbvY2H7HXnFw'
         )
-        resp = self.client.post(reverse('plainui:token_password_reset'), {
-            'jwt': invalid_token,
-            'username': self.user.username,
-            'new_password1': 'new',
-            'new_password2': 'new',
-        })
+        resp = self.client.post(
+            reverse('plainui:token_password_reset'),
+            {
+                'jwt': invalid_token,
+                'username': self.user.username,
+                'new_password1': 'new',
+                'new_password2': 'new',
+            },
+        )
         self.assertTrue(resp.context_data['form'].errors)
         self.user.refresh_from_db()
         self.assertFalse(self.user.check_password('new'))
 
-        resp = self.client.post(reverse('plainui:token_password_reset'), {
-            'jwt': valid_token,
-            'username': 'doesnotexist',
-            'new_password1': 'new',
-            'new_password2': 'new',
-        })
+        resp = self.client.post(
+            reverse('plainui:token_password_reset'),
+            {
+                'jwt': valid_token,
+                'username': 'doesnotexist',
+                'new_password1': 'new',
+                'new_password2': 'new',
+            },
+        )
         self.assertTrue(resp.context_data['form'].errors)
         self.user.refresh_from_db()
         self.assertFalse(self.user.check_password('new'))
 
-        resp = self.client.post(reverse('plainui:token_password_reset'), {
-            'jwt': valid_token,
-            'username': self.user.username,
-            'new_password1': 'new',
-            'new_password2': 'new2',
-        })
+        resp = self.client.post(
+            reverse('plainui:token_password_reset'),
+            {
+                'jwt': valid_token,
+                'username': self.user.username,
+                'new_password1': 'new',
+                'new_password2': 'new2',
+            },
+        )
         self.assertTrue(resp.context_data['form'].errors)
         self.user.refresh_from_db()
         self.assertFalse(self.user.check_password('new'))
 
-        resp = self.client.post(reverse('plainui:token_password_reset'), {
-            'jwt': valid_token,
-            'username': self.user.username,
-            'new_password1': 'new',
-            'new_password2': 'new',
-        })
+        resp = self.client.post(
+            reverse('plainui:token_password_reset'),
+            {
+                'jwt': valid_token,
+                'username': self.user.username,
+                'new_password1': 'new',
+                'new_password2': 'new',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:login'))
         self.user.refresh_from_db()
         self.assertTrue(self.user.check_password('new'))
@@ -1498,22 +1568,33 @@ class ViewsTest(ViewsTestBase):
         assembly2 = Assembly(conference=self.conf, slug='assembly2', name='Assembly2', state_assembly=Assembly.State.PLACED)
         assembly2.save()
         event1 = Event(
-            conference=self.conf, assembly=assembly1, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly1,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event1.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly1, name='Event1_2', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 1, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly1,
+            name='Event1_2',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 1, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
 
         self.assertNeedsLogin(reverse('plainui:modify_favorites'), post=True, check_user=True)
-        resp = self.client.post(reverse('plainui:modify_favorites'), {
-            'mode': 'add',
-            'type': 'assembly',
-            'id': str(assembly1.pk),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_favorites'),
+            {
+                'mode': 'add',
+                'type': 'assembly',
+                'id': str(assembly1.pk),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:userprofile'))
         self.assertTrue(self.user.favorite_assemblies.filter(pk=assembly1.pk).exists())
         self.assertEqual(self.client.session['fav_a'], [str(assembly1.pk)])
@@ -1522,23 +1603,29 @@ class ViewsTest(ViewsTestBase):
         self.assertEqual(lc.assembly2, assembly1)
         self.assertEqual(lc.likes, 1)
 
-        resp = self.client.post(reverse('plainui:modify_favorites'), {
-            'mode': 'add',
-            'type': 'assembly',
-            'id': str(assembly2.pk),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_favorites'),
+            {
+                'mode': 'add',
+                'type': 'assembly',
+                'id': str(assembly2.pk),
+            },
+        )
         self.assertEqual(AssemblyLikeCount.objects.count(), 4)
         self.assertEqual(AssemblyLikeCount.objects.get(assembly1=assembly1, assembly2=assembly1).likes, 1)
         self.assertEqual(AssemblyLikeCount.objects.get(assembly1=assembly2, assembly2=assembly2).likes, 1)
         self.assertEqual(AssemblyLikeCount.objects.get(assembly1=assembly1, assembly2=assembly2).likes, 1)
         self.assertEqual(AssemblyLikeCount.objects.get(assembly1=assembly2, assembly2=assembly1).likes, 1)
 
-        resp = self.client.post(reverse('plainui:modify_favorites'), {
-            'mode': 'remove',
-            'type': 'assembly',
-            'id': str(assembly1.pk),
-            'next': reverse('plainui:index'),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_favorites'),
+            {
+                'mode': 'remove',
+                'type': 'assembly',
+                'id': str(assembly1.pk),
+                'next': reverse('plainui:index'),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertFalse(self.user.favorite_assemblies.filter(pk=assembly1.pk).exists())
         self.assertEqual(self.client.session['fav_a'], [str(assembly2.pk)])
@@ -1547,12 +1634,15 @@ class ViewsTest(ViewsTestBase):
         self.assertEqual(AssemblyLikeCount.objects.get(assembly1=assembly1, assembly2=assembly2).likes, 0)
         self.assertEqual(AssemblyLikeCount.objects.get(assembly1=assembly2, assembly2=assembly1).likes, 0)
 
-        resp = self.client.post(reverse('plainui:modify_favorites'), {
-            'mode': 'add',
-            'type': 'event',
-            'id': str(event1.pk),
-            'next': reverse('plainui:index'),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_favorites'),
+            {
+                'mode': 'add',
+                'type': 'event',
+                'id': str(event1.pk),
+                'next': reverse('plainui:index'),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertTrue(self.user.favorite_events.filter(pk=event1.pk).exists())
         self.assertEqual(self.client.session['fav_e'], [str(event1.pk)])
@@ -1561,23 +1651,29 @@ class ViewsTest(ViewsTestBase):
         self.assertEqual(lc.event2, event1)
         self.assertEqual(lc.likes, 1)
 
-        resp = self.client.post(reverse('plainui:modify_favorites'), {
-            'mode': 'add',
-            'type': 'event',
-            'id': str(event2.pk),
-            'next': reverse('plainui:index'),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_favorites'),
+            {
+                'mode': 'add',
+                'type': 'event',
+                'id': str(event2.pk),
+                'next': reverse('plainui:index'),
+            },
+        )
         self.assertEqual(EventLikeCount.objects.count(), 4)
         self.assertEqual(EventLikeCount.objects.get(event1=event1, event2=event1).likes, 1)
         self.assertEqual(EventLikeCount.objects.get(event1=event2, event2=event2).likes, 1)
         self.assertEqual(EventLikeCount.objects.get(event1=event1, event2=event2).likes, 1)
         self.assertEqual(EventLikeCount.objects.get(event1=event2, event2=event1).likes, 1)
 
-        resp = self.client.post(reverse('plainui:modify_favorites'), {
-            'mode': 'remove',
-            'type': 'event',
-            'id': str(event1.pk),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_favorites'),
+            {
+                'mode': 'remove',
+                'type': 'event',
+                'id': str(event1.pk),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:userprofile'))
         self.assertFalse(self.user.favorite_events.filter(pk=event1.pk).exists())
         self.assertEqual(self.client.session['fav_e'], [str(event2.pk)])
@@ -1596,16 +1692,22 @@ class ViewsTest(ViewsTestBase):
     @override_locale('en')
     def test_ModifyLanguageView_post(self):
         self.assertNeedsLogin(reverse('plainui:modify_language'), post=True)
-        resp = self.client.post(reverse('plainui:modify_language'), {
-            'language': 'en',
-            'next': reverse('plainui:landing'),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_language'),
+            {
+                'language': 'en',
+                'next': reverse('plainui:landing'),
+            },
+        )
         self.assertRedirects(resp, translate_url(reverse('plainui:landing'), 'en'))
         self.assertEqual(self.client.cookies['language_cookie'].value, 'en')
 
-        resp = self.client.post(reverse('plainui:modify_language'), {
-            'language': 'de',
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_language'),
+            {
+                'language': 'de',
+            },
+        )
         self.assertRedirects(resp, translate_url(reverse('plainui:index'), 'de'))
         self.assertEqual(self.client.cookies['language_cookie'].value, 'de')
 
@@ -1620,25 +1722,35 @@ class ViewsTest(ViewsTestBase):
         assembly = Assembly(conference=self.conf, slug='assembly1', name='Assembly1', state_assembly=Assembly.State.PLACED)
         assembly.save()
         event = Event(
-            conference=self.conf, assembly=assembly, name='Event1_1', is_public=True,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event.save()
 
         self.assertNeedsLogin(reverse('plainui:modify_personal_calendar'), post=True, check_user=True)
-        resp = self.client.post(reverse('plainui:modify_personal_calendar'), {
-            'mode': 'add',
-            'id': str(event.pk),
-            'next': reverse('plainui:index'),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_personal_calendar'),
+            {
+                'mode': 'add',
+                'id': str(event.pk),
+                'next': reverse('plainui:index'),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertTrue(self.user.calendar_events.filter(pk=event.pk).exists())
         self.assertEqual(self.client.session['sch_e'], [str(event.pk)])
 
-        resp = self.client.post(reverse('plainui:modify_personal_calendar'), {
-            'mode': 'remove',
-            'id': str(event.pk),
-        })
+        resp = self.client.post(
+            reverse('plainui:modify_personal_calendar'),
+            {
+                'mode': 'remove',
+                'id': str(event.pk),
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:userprofile'))
         self.assertFalse(self.user.calendar_events.filter(pk=event.pk).exists())
         self.assertEqual(self.client.session['sch_e'], [])
@@ -1694,12 +1806,15 @@ class ViewsTest(ViewsTestBase):
         user2.save()
 
         self.assertNeedsLogin(reverse('plainui:personal_message_send'), post=True, check_user=True)
-        resp = self.client.post(reverse('plainui:personal_message_send'), {
-            'in_reply_to': '',
-            'recipient': 'testuser2',
-            'subject': 'Subj',
-            'body': 'Body',
-        })
+        resp = self.client.post(
+            reverse('plainui:personal_message_send'),
+            {
+                'in_reply_to': '',
+                'recipient': 'testuser2',
+                'subject': 'Subj',
+                'body': 'Body',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:personal_message'))
         new_dm = DirectMessage.objects.get()
         self.assertEqual(new_dm.conference, self.conf)
@@ -1711,12 +1826,15 @@ class ViewsTest(ViewsTestBase):
 
         received_dm = DirectMessage(conference=self.conf, sender=user2, recipient=self.user)
         received_dm.save()
-        resp = self.client.post(reverse('plainui:personal_message_send'), {
-            'in_reply_to': str(received_dm.pk),
-            'recipient': 'testuser2',
-            'subject': 'Subj2',
-            'body': 'Body2',
-        })
+        resp = self.client.post(
+            reverse('plainui:personal_message_send'),
+            {
+                'in_reply_to': str(received_dm.pk),
+                'recipient': 'testuser2',
+                'subject': 'Subj2',
+                'body': 'Body2',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:personal_message'))
         new_dm = DirectMessage.objects.get(in_reply_to=received_dm)
         self.assertEqual(new_dm.conference, self.conf)
@@ -1729,12 +1847,15 @@ class ViewsTest(ViewsTestBase):
         self.assertTrue(received_dm.has_responded)
         self.assertSetsMessage(resp, 'Message sent.')
 
-        resp = self.client.post(reverse('plainui:personal_message_send'), {
-            'in_reply_to': '',
-            'recipient': 'Unknown User',
-            'subject': '',
-            'body': '',
-        })
+        resp = self.client.post(
+            reverse('plainui:personal_message_send'),
+            {
+                'in_reply_to': '',
+                'recipient': 'Unknown User',
+                'subject': '',
+                'body': '',
+            },
+        )
         form = resp.context_data['form']
         self.assertEqual(form.errors['recipient'], ['Unknown User!'])
         self.assertEqual(form.errors['subject'], ['This field is required.'])
@@ -1743,12 +1864,15 @@ class ViewsTest(ViewsTestBase):
         DirectMessage.objects.all().delete()
         self.user.shadow_banned = True
         self.user.save()
-        resp = self.client.post(reverse('plainui:personal_message_send'), {
-            'in_reply_to': '',
-            'recipient': 'testuser2',
-            'subject': 'subject',
-            'body': 'body',
-        })
+        resp = self.client.post(
+            reverse('plainui:personal_message_send'),
+            {
+                'in_reply_to': '',
+                'recipient': 'testuser2',
+                'subject': 'subject',
+                'body': 'body',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:personal_message'))
         new_dm = DirectMessage.objects.get(subject='subject')
         self.user.refresh_from_db()
@@ -1759,20 +1883,26 @@ class ViewsTest(ViewsTestBase):
         self.user.save()
         with override_settings(RATELIMIT_ENABLE=True):
             for i in range(3):
-                resp = self.client.post(reverse('plainui:personal_message_send'), {
-                    'in_reply_to': '',
-                    'recipient': 'testuser2',
-                    'subject': 'subject' + str(i),
-                    'body': 'body',
-                })
+                resp = self.client.post(
+                    reverse('plainui:personal_message_send'),
+                    {
+                        'in_reply_to': '',
+                        'recipient': 'testuser2',
+                        'subject': 'subject' + str(i),
+                        'body': 'body',
+                    },
+                )
                 self.assertRedirects(resp, reverse('plainui:personal_message'))
 
-            resp = self.client.post(reverse('plainui:personal_message_send'), {
-                'in_reply_to': '',
-                'recipient': 'testuser2',
-                'subject': 'subject-ratelimited',
-                'body': 'body-ratelimited',
-            })
+            resp = self.client.post(
+                reverse('plainui:personal_message_send'),
+                {
+                    'in_reply_to': '',
+                    'recipient': 'testuser2',
+                    'subject': 'subject-ratelimited',
+                    'body': 'body-ratelimited',
+                },
+            )
             self.assertIsInstance(resp, HttpResponseForbidden)
 
     @override_locale('en')
@@ -1844,11 +1974,11 @@ class ViewsTest(ViewsTestBase):
         other_user = PlatformUser(username='other_user')
         other_user.save()
 
-        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title="Title1", text="some text")
+        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title='Title1', text='some text')
         e1.save()
-        e2 = BulletinBoardEntry(conference=self.conf, owner=self.user, is_public=False, title="Title2", text="other text")
+        e2 = BulletinBoardEntry(conference=self.conf, owner=self.user, is_public=False, title='Title2', text='other text')
         e2.save()
-        e3 = BulletinBoardEntry(conference=self.conf, owner=self.user, title="Title3", text="different text")
+        e3 = BulletinBoardEntry(conference=self.conf, owner=self.user, title='Title3', text='different text')
         e3.save()
 
         self.assertNeedsLogin(reverse('plainui:board'))
@@ -1862,9 +1992,9 @@ class ViewsTest(ViewsTestBase):
 
         self.user.shadow_banned = True
         self.user.save()
-        e4 = BulletinBoardEntry(conference=self.conf, owner=self.user, title="Title4", text="some text", hidden=True)
+        e4 = BulletinBoardEntry(conference=self.conf, owner=self.user, title='Title4', text='some text', hidden=True)
         e4.save()
-        e5 = BulletinBoardEntry(conference=self.conf, owner=other_user, title="Title5", text="some text", hidden=True)
+        e5 = BulletinBoardEntry(conference=self.conf, owner=other_user, title='Title5', text='some text', hidden=True)
         e5.save()
         resp = self.client.get(reverse('plainui:board'))
         self.assertEqual(list(resp.context_data['board']), [e4, e3, e1])
@@ -1873,9 +2003,9 @@ class ViewsTest(ViewsTestBase):
     def test_BoardPrivateView(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
-        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title="Title1", text="some text", hidden=True)
+        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title='Title1', text='some text', hidden=True)
         e1.save()
-        e2 = BulletinBoardEntry(conference=self.conf, owner=self.user, is_public=False, title="Title2", text="other text")
+        e2 = BulletinBoardEntry(conference=self.conf, owner=self.user, is_public=False, title='Title2', text='other text')
         e2.save()
         e_other_owner = BulletinBoardEntry(conference=self.conf, owner=user2)
         e_other_owner.save()
@@ -1888,7 +2018,7 @@ class ViewsTest(ViewsTestBase):
     @override_locale('en')
     @patch('modeltranslation.settings.AVAILABLE_LANGUAGES', ['en', 'de'])
     def test_BoardEntryEditView_get(self):
-        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, is_public=False, title="Title1", text_de="some de text", text_en="some en text")
+        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, is_public=False, title='Title1', text_de='some de text', text_en='some en text')
         e1.save()
 
         self.assertNeedsLogin(reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}), check_user=True)
@@ -1913,19 +2043,22 @@ class ViewsTest(ViewsTestBase):
     def test_BoardEntryEditView_post(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
-        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title="Title1", text="some text")
+        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title='Title1', text='some text')
         e1.save()
         e2 = BulletinBoardEntry(conference=self.conf, owner=user2)
         e2.save()
 
         # edit entry with checked is_public
         self.assertNeedsLogin(reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}), post=True, check_user=True)
-        resp = self.client.post(reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}), {
-            'title': 'Edit Title',
-            'text_de': 'blblblblbl',
-            'text_en': 'blblblbl EN',
-            'is_public': 'on',
-        })
+        resp = self.client.post(
+            reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}),
+            {
+                'title': 'Edit Title',
+                'text_de': 'blblblblbl',
+                'text_en': 'blblblbl EN',
+                'is_public': 'on',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}))
         e1.refresh_from_db()
         self.assertEqual(e1.is_public, True)
@@ -1936,30 +2069,39 @@ class ViewsTest(ViewsTestBase):
         self.assertSetsMessage(resp, 'Bulletin Board Entry updated.')
 
         # unchecked is_public
-        resp = self.client.post(reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}), {
-            'title': 'Edit Title',
-            'text_de': 'blblblblbl',
-            'text_en': 'blblblblbl ENL',
-        })
+        resp = self.client.post(
+            reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}),
+            {
+                'title': 'Edit Title',
+                'text_de': 'blblblblbl',
+                'text_en': 'blblblblbl ENL',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'id': str(e1.pk)}))
         e1.refresh_from_db()
         self.assertEqual(e1.is_public, False)
 
         # can't edit other users entries
-        resp = self.client.post(reverse('plainui:board_entry_edit', kwargs={'id': str(e2.pk)}), {
-            'title': 'Edit Title',
-            'text_de': 'blblblblbl',
-            'text_en': 'blblblblbl e',
-        })
+        resp = self.client.post(
+            reverse('plainui:board_entry_edit', kwargs={'id': str(e2.pk)}),
+            {
+                'title': 'Edit Title',
+                'text_de': 'blblblblbl',
+                'text_en': 'blblblblbl e',
+            },
+        )
         self.assertEqual(resp.status_code, 404)
 
         # create new entry
         self.assertNeedsLogin(reverse('plainui:board_entry_new'), post=True, check_user=True)
-        resp = self.client.post(reverse('plainui:board_entry_new'), {
-            'title': 'New Entry',
-            'text_de': 'some texty text de',
-            'text_en': 'some texty text',
-        })
+        resp = self.client.post(
+            reverse('plainui:board_entry_new'),
+            {
+                'title': 'New Entry',
+                'text_de': 'some texty text de',
+                'text_en': 'some texty text',
+            },
+        )
         new_entry = BulletinBoardEntry.objects.exclude(pk__in=[e1.pk, e2.pk]).get()
         self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'id': str(new_entry.pk)}))
         self.assertEqual(new_entry.conference, self.conf)
@@ -1974,11 +2116,14 @@ class ViewsTest(ViewsTestBase):
         BulletinBoardEntry.objects.all().delete()
         self.user.shadow_banned = True
         self.user.save()
-        resp = self.client.post(reverse('plainui:board_entry_new'), {
-            'title': 'New Entry',
-            'text_de': 'some texty text ed',
-            'text_en': 'some texty text ne',
-        })
+        resp = self.client.post(
+            reverse('plainui:board_entry_new'),
+            {
+                'title': 'New Entry',
+                'text_de': 'some texty text ed',
+                'text_en': 'some texty text ne',
+            },
+        )
         new_entry = BulletinBoardEntry.objects.get()
         self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'id': str(new_entry.pk)}))
         self.assertEqual(new_entry.hidden, True)
@@ -1988,17 +2133,23 @@ class ViewsTest(ViewsTestBase):
         self.user.save()
         with override_settings(RATELIMIT_ENABLE=True):
             for i in range(3):
-                resp = self.client.post(reverse('plainui:board_entry_new'), {
-                    'title': 'New Entry' + str(i),
-                    'text': 'some texty text',
-                })
+                resp = self.client.post(
+                    reverse('plainui:board_entry_new'),
+                    {
+                        'title': 'New Entry' + str(i),
+                        'text': 'some texty text',
+                    },
+                )
                 new_entry = BulletinBoardEntry.objects.get(title='New Entry' + str(i))
                 self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'id': str(new_entry.pk)}))
 
-            resp = self.client.post(reverse('plainui:board_entry_new'), {
+            resp = self.client.post(
+                reverse('plainui:board_entry_new'),
+                {
                     'title': 'New Entry-Ratelimited',
                     'text': 'some texty text-Ratelimited',
-                })
+                },
+            )
             self.assertIsInstance(resp, HttpResponseRateLimited)
 
     @override_locale('en')
@@ -2012,7 +2163,7 @@ class ViewsTest(ViewsTestBase):
     def test_BoardEntryDeleteView_post(self):
         user2 = PlatformUser(username='testuser2')
         user2.save()
-        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title="Title1", text="some text")
+        e1 = BulletinBoardEntry(conference=self.conf, owner=self.user, title='Title1', text='some text')
         e1.save()
         e2 = BulletinBoardEntry(conference=self.conf, owner=user2)
         e2.save()
@@ -2045,14 +2196,26 @@ class ViewsTest(ViewsTestBase):
         track = ConferenceTrack(conference=self.conf, is_public=True, name='Track1')
         track.save()
         event1 = Event(
-            conference=self.conf, assembly=assembly, track=track, name='Event1_1', is_public=True, room=room,
-            schedule_start=datetime(2020, 1, 2, 0, 15, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            track=track,
+            name='Event1_1',
+            is_public=True,
+            room=room,
+            schedule_start=datetime(2020, 1, 2, 0, 15, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event1.save()
         EventParticipant(is_public=True, participant=self.user, event=event1, role=EventParticipant.Role.SPEAKER).save()
         event2 = Event(
-            conference=self.conf, assembly=assembly, track=track, name='Event1_2', is_public=True, room=room,
-            schedule_start=datetime(2020, 1, 2, 1, 15, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            track=track,
+            name='Event1_2',
+            is_public=True,
+            room=room,
+            schedule_start=datetime(2020, 1, 2, 1, 15, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
 
@@ -2069,23 +2232,31 @@ class ViewsTest(ViewsTestBase):
         resp = self.client.get(reverse('plainui:fahrplan'), {'mode': 'calendar'})
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['mode'], 'calendar')
-        self.assertEqual(resp.context_data['events'], {
-            'rooms_with_events': [(room, [
-                {'type': 'space', 'minutes': 15.0},
-                {'type': 'event', 'event': event1, 'minutes': 45.0},
-                {'type': 'space', 'minutes': 15.0},
-                {'type': 'event', 'event': event2, 'minutes': 45.0}
-            ])],
-            'calendar_start': datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
-            'calendar_end': datetime(2020, 1, 2, 2, 0, 0, tzinfo=UTC),
-            'calendar_time_steps': [
-                {'newdate': True, 'ts': datetime(2020, 1, 2, 0, 0, tzinfo=UTC)},
-                {'newdate': False, 'ts': datetime(2020, 1, 2, 0, 30, tzinfo=UTC)},
-                {'newdate': False, 'ts': datetime(2020, 1, 2, 1, 0, tzinfo=UTC)},
-                {'newdate': False, 'ts': datetime(2020, 1, 2, 1, 30, tzinfo=UTC)},
-            ],
-            'calendar_step_minutes': 30,
-        })
+        self.assertEqual(
+            resp.context_data['events'],
+            {
+                'rooms_with_events': [
+                    (
+                        room,
+                        [
+                            {'type': 'space', 'minutes': 15.0},
+                            {'type': 'event', 'event': event1, 'minutes': 45.0},
+                            {'type': 'space', 'minutes': 15.0},
+                            {'type': 'event', 'event': event2, 'minutes': 45.0},
+                        ],
+                    )
+                ],
+                'calendar_start': datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+                'calendar_end': datetime(2020, 1, 2, 2, 0, 0, tzinfo=UTC),
+                'calendar_time_steps': [
+                    {'newdate': True, 'ts': datetime(2020, 1, 2, 0, 0, tzinfo=UTC)},
+                    {'newdate': False, 'ts': datetime(2020, 1, 2, 0, 30, tzinfo=UTC)},
+                    {'newdate': False, 'ts': datetime(2020, 1, 2, 1, 0, tzinfo=UTC)},
+                    {'newdate': False, 'ts': datetime(2020, 1, 2, 1, 30, tzinfo=UTC)},
+                ],
+                'calendar_step_minutes': 30,
+            },
+        )
 
         # some random requests that test building the filter content
         resp = self.client.get(reverse('plainui:fahrplan'), {'mode': 'calendar', 'show_day_filters': 'y'})
@@ -2120,20 +2291,35 @@ class ViewsTest(ViewsTestBase):
         room2 = Room(conference=self.conf, assembly=assembly, name='Some Other Room', is_public_fahrplan=False)
         room2.save()
         event = Event(
-            conference=self.conf, assembly=assembly, name='Event1_1', is_public=True, room=room,
-            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_1',
+            is_public=True,
+            room=room,
+            schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             kind=Event.Kind.OFFICIAL,
         )
         event.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly, name='Event1_2', is_public=False, room=room,
-            schedule_start=datetime(2020, 1, 2, 1, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_2',
+            is_public=False,
+            room=room,
+            schedule_start=datetime(2020, 1, 2, 1, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             kind=Event.Kind.OFFICIAL,
         )
         event2.save()
         event3 = Event(
-            conference=self.conf, assembly=assembly, name='Event1_3', is_public=True, room=room2,
-            schedule_start=datetime(2020, 1, 2, 2, 0, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            assembly=assembly,
+            name='Event1_3',
+            is_public=True,
+            room=room2,
+            schedule_start=datetime(2020, 1, 2, 2, 0, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             kind=Event.Kind.ASSEMBLY,
         )
         event3.save()
@@ -2197,23 +2383,43 @@ class ViewsTest(ViewsTestBase):
         room = Room(conference=self.conf, assembly=assembly, name='Some Room')
         room.save()
         event_pre = Event(
-            conference=self.conf, assembly=assembly, name='Event_pre', is_public=True, room=room,
-            schedule_start=now - timedelta(hours=1), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event_pre',
+            is_public=True,
+            room=room,
+            schedule_start=now - timedelta(hours=1),
+            schedule_duration=timedelta(minutes=45),
         )
         event_pre.save()
         event_still_running = Event(
-            conference=self.conf, assembly=assembly, name='Event_still_running', is_public=True, room=room,
-            schedule_start=now - timedelta(minutes=30), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event_still_running',
+            is_public=True,
+            room=room,
+            schedule_start=now - timedelta(minutes=30),
+            schedule_duration=timedelta(minutes=45),
         )
         event_still_running.save()
         event_soon = Event(
-            conference=self.conf, assembly=assembly, name='Event_soon', is_public=True, room=room,
-            schedule_start=now + timedelta(minutes=15), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event_soon',
+            is_public=True,
+            room=room,
+            schedule_start=now + timedelta(minutes=15),
+            schedule_duration=timedelta(minutes=45),
         )
         event_soon.save()
         event_post = Event(
-            conference=self.conf, assembly=assembly, name='Event_post', is_public=True, room=room,
-            schedule_start=now + timedelta(minutes=35), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            name='Event_post',
+            is_public=True,
+            room=room,
+            schedule_start=now + timedelta(minutes=35),
+            schedule_duration=timedelta(minutes=45),
         )
         event_post.save()
 
@@ -2244,19 +2450,38 @@ class ViewsTest(ViewsTestBase):
         room_link2.save()
 
         event = Event(
-            conference=self.conf, assembly=assembly, room=room1, slug='Event1_1', name='Event1_1', is_public=True, kind=Event.Kind.ASSEMBLY,
-            schedule_start=datetime(2020, 1, 1, 0, 45, 0, tzinfo=UTC), schedule_duration=timedelta(minutes=45),
+            conference=self.conf,
+            assembly=assembly,
+            room=room1,
+            slug='Event1_1',
+            name='Event1_1',
+            is_public=True,
+            kind=Event.Kind.ASSEMBLY,
+            schedule_start=datetime(2020, 1, 1, 0, 45, 0, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
             additional_data={'persons': [{'public_name': 'Gottfried Fluffke'}, {'public_name': 'Ferdinand Federviech'}]},
         )
         event.save()
         event2 = Event(
-            conference=self.conf, assembly=assembly, room=room1, slug='Event1_2', name='Event1_2', is_public=True,
-            schedule_start=datetime(2020, 1, 1, 1, 50, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            room=room1,
+            slug='Event1_2',
+            name='Event1_2',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 1, 1, 50, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event2.save()
         event_upcoming = Event(
-            conference=self.conf, assembly=assembly, room=room2, slug='Event2_1', name='Event2_1', is_public=True,
-            schedule_start=datetime(2020, 1, 1, 1, 50, 1, tzinfo=UTC), schedule_duration=timedelta(minutes=45)
+            conference=self.conf,
+            assembly=assembly,
+            room=room2,
+            slug='Event2_1',
+            name='Event2_1',
+            is_public=True,
+            schedule_start=datetime(2020, 1, 1, 1, 50, 1, tzinfo=UTC),
+            schedule_duration=timedelta(minutes=45),
         )
         event_upcoming.save()
 
@@ -2265,16 +2490,26 @@ class ViewsTest(ViewsTestBase):
         self.assertNeedsLogin(reverse('plainui:channel_events'))
         resp = self.client.get(reverse('plainui:channel_events'))
         self.assertEqual(resp.context_data['conf'], self.conf)
-        self.assertEqual(resp.context_data['rooms_active'], [{
-            'room': room1,
-            'current_event': event,
-            'next_event': event2,
-        }])
-        self.assertEqual(resp.context_data['rooms_inactive'], [{
-            'room': room2,
-            'current_event': None,
-            'next_event': event_upcoming,
-        }])
+        self.assertEqual(
+            resp.context_data['rooms_active'],
+            [
+                {
+                    'room': room1,
+                    'current_event': event,
+                    'next_event': event2,
+                }
+            ],
+        )
+        self.assertEqual(
+            resp.context_data['rooms_inactive'],
+            [
+                {
+                    'room': room2,
+                    'current_event': None,
+                    'next_event': event_upcoming,
+                }
+            ],
+        )
 
     @override_locale('en')
     def test_WorldView(self):
@@ -2310,7 +2545,8 @@ class ViewsTest(ViewsTestBase):
     @override_locale('en')
     def _dereferrer_test(self, my_endpoint):
         from django.core.signing import loads
-        my_salt = '%sdereferrer' % (self.user.pk,)
+
+        my_salt = f'{self.user.pk}dereferrer'
         anonymous_salt = '000000dereferrer'
 
         # local links are always allowed
@@ -2426,6 +2662,7 @@ class ViewsTest(ViewsTestBase):
     @override_locale('en')
     def test_ReportContentView(self):
         from plainui.forms import REPORT_CATEGORIES
+
         category_id = iter(REPORT_CATEGORIES.keys()).__next__()
 
         user2 = PlatformUser(username='testuser2')
@@ -2436,132 +2673,154 @@ class ViewsTest(ViewsTestBase):
         bbe.save()
 
         self.assertNeedsLogin(reverse('plainui:report_content'), check_user=True)
-        resp = self.client.get(reverse('plainui:report_content'), {
-            'kind': 'url', 'kind_data': reverse('plainui:index')
-        })
+        resp = self.client.get(reverse('plainui:report_content'), {'kind': 'url', 'kind_data': reverse('plainui:index')})
         self.assertEqual(resp.context_data['conf'], self.conf)
         self.assertEqual(resp.context_data['form']['kind'].value(), 'url')
         self.assertEqual(resp.context_data['form']['kind_data'].value(), reverse('plainui:index'))
 
-        resp = self.client.post(reverse('plainui:report_content'), {
-            'kind': 'url',
-            'kind_data': reverse('plainui:index'),
-            'next': '',
-            'category': category_id,
-            'message': 'Some Message',
-            'message2': 'Some Resolution',
-        })
+        resp = self.client.post(
+            reverse('plainui:report_content'),
+            {
+                'kind': 'url',
+                'kind_data': reverse('plainui:index'),
+                'next': '',
+                'category': category_id,
+                'message': 'Some Message',
+                'message2': 'Some Resolution',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertEqual(len(mail.outbox), 1)
         sent_mail = mail.outbox[0]
         self.assertEqual(sent_mail.to, [REPORT_CATEGORIES[category_id][2]])
-        self.assertEqual(sent_mail.subject, f"New {REPORT_CATEGORIES[category_id][1]} Report")
-        self.assertEqual(sent_mail.body, (
-            f"testuser ({self.user.id!s}) sent a new {REPORT_CATEGORIES[category_id][1]} Report.\n"
-            f"Profile of the Report Author: {reverse('plainui:user_by_uuid', kwargs={'uuid': str(self.user.uuid)})}\n"
-            f"\n"
-            f"Problem description:\n"
-            f"\n"
-            f"Some Message\n"
-            f"\n"
-            f"Solution description:\n"
-            f"\n"
-            f"Some Resolution\n"
-            f"\n"
-            f"Problematic content:\n"
-            f"\n"
-            f"{reverse('plainui:index')}"
-        ))
+        self.assertEqual(sent_mail.subject, f'New {REPORT_CATEGORIES[category_id][1]} Report')
+        self.assertEqual(
+            sent_mail.body,
+            (
+                f"testuser ({self.user.id!s}) sent a new {REPORT_CATEGORIES[category_id][1]} Report.\n"
+                f"Profile of the Report Author: {reverse('plainui:user_by_uuid', kwargs={'uuid': str(self.user.uuid)})}\n"
+                f"\n"
+                f"Problem description:\n"
+                f"\n"
+                f"Some Message\n"
+                f"\n"
+                f"Solution description:\n"
+                f"\n"
+                f"Some Resolution\n"
+                f"\n"
+                f"Problematic content:\n"
+                f"\n"
+                f"{reverse('plainui:index')}"
+            ),
+        )
         mail.outbox.clear()
 
-        resp = self.client.post(reverse('plainui:report_content'), {
-            'kind': 'pn',
-            'kind_data': str(dm_recv.pk),
-            'next': reverse('plainui:personal_message_show', kwargs={'msg_id': str(dm_recv.pk)}),
-            'category': category_id,
-            'message': 'Some Message',
-            'message2': 'Some Resolution',
-        })
+        resp = self.client.post(
+            reverse('plainui:report_content'),
+            {
+                'kind': 'pn',
+                'kind_data': str(dm_recv.pk),
+                'next': reverse('plainui:personal_message_show', kwargs={'msg_id': str(dm_recv.pk)}),
+                'category': category_id,
+                'message': 'Some Message',
+                'message2': 'Some Resolution',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:personal_message_show', kwargs={'msg_id': str(dm_recv.pk)}))
         self.assertEqual(len(mail.outbox), 1)
         sent_mail = mail.outbox[0]
         self.assertEqual(sent_mail.to, [REPORT_CATEGORIES[category_id][2]])
-        self.assertEqual(sent_mail.subject, f"New {REPORT_CATEGORIES[category_id][1]} Report")
-        self.assertEqual(sent_mail.body, (
-            f'testuser ({self.user.id!s}) sent a new {REPORT_CATEGORIES[category_id][1]} Report.\n'
-            f"Profile of the Report Author: {reverse('plainui:user_by_uuid', kwargs={'uuid': str(self.user.uuid)})}\n"
-            f'\n'
-            f'Problem description:\n'
-            f'\n'
-            f'Some Message\n'
-            f'\n'
-            f"Solution description:\n"
-            f"\n"
-            f"Some Resolution\n"
-            f"\n"
-            f'Problematic content:\n'
-            f'\n'
-            f'DM by testuser2 ({user2.id!s}) to testuser ({self.user.id!s}) at {localize(localtime(dm_recv.timestamp))} ({dm_recv.pk!s}).\n'
-            f'Subject: "sj1"\n'
-            f'Message: "bd1"'
-        ))
+        self.assertEqual(sent_mail.subject, f'New {REPORT_CATEGORIES[category_id][1]} Report')
+        self.assertEqual(
+            sent_mail.body,
+            (
+                f'testuser ({self.user.id!s}) sent a new {REPORT_CATEGORIES[category_id][1]} Report.\n'
+                f"Profile of the Report Author: {reverse('plainui:user_by_uuid', kwargs={'uuid': str(self.user.uuid)})}\n"
+                f'\n'
+                f'Problem description:\n'
+                f'\n'
+                f'Some Message\n'
+                f'\n'
+                f"Solution description:\n"
+                f"\n"
+                f"Some Resolution\n"
+                f"\n"
+                f'Problematic content:\n'
+                f'\n'
+                f'DM by testuser2 ({user2.id!s}) to testuser ({self.user.id!s}) at {localize(localtime(dm_recv.timestamp))} ({dm_recv.pk!s}).\n'
+                f'Subject: "sj1"\n'
+                f'Message: "bd1"'
+            ),
+        )
         mail.outbox.clear()
 
-        resp = self.client.post(reverse('plainui:report_content'), {
-            'kind': 'board',
-            'kind_data': str(bbe.pk),
-            'next': '',
-            'category': category_id,
-            'message': 'Other Message',
-            'message2': 'Other Resolution',
-        })
+        resp = self.client.post(
+            reverse('plainui:report_content'),
+            {
+                'kind': 'board',
+                'kind_data': str(bbe.pk),
+                'next': '',
+                'category': category_id,
+                'message': 'Other Message',
+                'message2': 'Other Resolution',
+            },
+        )
         self.assertRedirects(resp, reverse('plainui:index'))
         self.assertEqual(len(mail.outbox), 1)
         sent_mail = mail.outbox[0]
         self.assertEqual(sent_mail.to, [REPORT_CATEGORIES[category_id][2]])
-        self.assertEqual(sent_mail.subject, f"New {REPORT_CATEGORIES[category_id][1]} Report")
-        self.assertEqual(sent_mail.body, (
-            f'testuser ({self.user.id!s}) sent a new {REPORT_CATEGORIES[category_id][1]} Report.\n'
-            f"Profile of the Report Author: {reverse('plainui:user_by_uuid', kwargs={'uuid': str(self.user.uuid)})}\n"
-            f'\n'
-            f'Problem description:\n'
-            f'\n'
-            f'Other Message\n'
-            f'\n'
-            f"Solution description:\n"
-            f"\n"
-            f"Other Resolution\n"
-            f"\n"
-            f'Problematic content:\n'
-            f'\n'
-            f'Board Entry by testuser ({self.user.id!s}) at {localize(localtime(dm_recv.timestamp))} ({bbe.pk!s})\n'
-            f'Title: "board entry title"\n'
-            f'Text: "board entry text"'
-        ))
+        self.assertEqual(sent_mail.subject, f'New {REPORT_CATEGORIES[category_id][1]} Report')
+        self.assertEqual(
+            sent_mail.body,
+            (
+                f'testuser ({self.user.id!s}) sent a new {REPORT_CATEGORIES[category_id][1]} Report.\n'
+                f"Profile of the Report Author: {reverse('plainui:user_by_uuid', kwargs={'uuid': str(self.user.uuid)})}\n"
+                f'\n'
+                f'Problem description:\n'
+                f'\n'
+                f'Other Message\n'
+                f'\n'
+                f"Solution description:\n"
+                f"\n"
+                f"Other Resolution\n"
+                f"\n"
+                f'Problematic content:\n'
+                f'\n'
+                f'Board Entry by testuser ({self.user.id!s}) at {localize(localtime(dm_recv.timestamp))} ({bbe.pk!s})\n'
+                f'Title: "board entry title"\n'
+                f'Text: "board entry text"'
+            ),
+        )
         mail.outbox.clear()
 
         # testing the rate limiting: Will block after 1 allowed requests
         with override_settings(RATELIMIT_ENABLE=True):
-            resp = self.client.post(reverse('plainui:report_content'), {
-                'kind': 'board',
-                'kind_data': str(bbe.pk),
-                'next': '',
-                'category': category_id,
-                'message': 'Other Message',
-                'message2': 'Other Resolution',
-            })
+            resp = self.client.post(
+                reverse('plainui:report_content'),
+                {
+                    'kind': 'board',
+                    'kind_data': str(bbe.pk),
+                    'next': '',
+                    'category': category_id,
+                    'message': 'Other Message',
+                    'message2': 'Other Resolution',
+                },
+            )
             self.assertRedirects(resp, reverse('plainui:index'))
             self.assertEqual(len(mail.outbox), 1)
             mail.outbox.clear()
 
-            resp = self.client.post(reverse('plainui:report_content'), {
-                'kind': 'board',
-                'kind_data': str(bbe.pk),
-                'next': '',
-                'category': category_id,
-                'message': 'Other Message',
-                'message2': 'Other Resolution',
-            })
+            resp = self.client.post(
+                reverse('plainui:report_content'),
+                {
+                    'kind': 'board',
+                    'kind_data': str(bbe.pk),
+                    'next': '',
+                    'category': category_id,
+                    'message': 'Other Message',
+                    'message2': 'Other Resolution',
+                },
+            )
             self.assertIsInstance(resp, HttpResponseRateLimited)
             self.assertEqual(len(mail.outbox), 0)
 
@@ -2606,8 +2865,8 @@ class ViewsTest(ViewsTestBase):
         badge = Badge(conference=self.conf, name='badge-name', issuing_assembly=assembly)
         badge.save()
         badge_token = BadgeToken.objects.create(badge=badge)
-        resp = self.client.get(reverse('plainui:manage_badges'), {"redeem_token": badge_token.token})
-        self.assertRedirects(resp, reverse("plainui:manage_badges"))
+        resp = self.client.get(reverse('plainui:manage_badges'), {'redeem_token': badge_token.token})
+        self.assertRedirects(resp, reverse('plainui:manage_badges'))
         self.assertTrue(UserBadge.objects.filter(user=self.user, badge=badge).exists())
 
     @override_settings(RATELIMIT_ENABLE=False)
@@ -2617,8 +2876,8 @@ class ViewsTest(ViewsTestBase):
         badge = Badge(conference=self.conf, name='badge-name', issuing_assembly=assembly)
         badge.save()
         badge_token = BadgeToken.objects.create(badge=badge)
-        resp = self.client.post(reverse('plainui:manage_badges'), {"token": badge_token.token, 'purpose': 'redeem_token'})
-        self.assertRedirects(resp, reverse("plainui:manage_badges"))
+        resp = self.client.post(reverse('plainui:manage_badges'), {'token': badge_token.token, 'purpose': 'redeem_token'})
+        self.assertRedirects(resp, reverse('plainui:manage_badges'))
         self.assertTrue(UserBadge.objects.filter(user=self.user, badge=badge).exists())
 
     def test_badge_redeem_view_rate_limit_post(self):
@@ -2690,7 +2949,6 @@ class ViewsTest(ViewsTestBase):
 
 # @override_settings(PRETIX_SECRET_KEY=_GOOD_SECRET)
 class TestWorkadventurCompattibleUsernameField(TestCase):
-
     def setUp(self):
         class MyTestForm(forms.Form):
             username = WorkadventureCompatibleUsernameField(max_length=123)
@@ -2709,7 +2967,7 @@ class TestWorkadventurCompattibleUsernameField(TestCase):
 
 class TestConferenceDetection(TestCase):
     def setUp(self):
-        self.request = RequestFactory().get("/")
+        self.request = RequestFactory().get('/')
         self.view = LandingView()
 
     def test_no_conference_config(self):
@@ -2718,20 +2976,14 @@ class TestConferenceDetection(TestCase):
 
     @override_settings(PLAINUI_CONFERENCE=None)
     def test_valid_conference_config(self):
-        self.conf = Conference(
-            id=TEST_CONF_ID, name="conf_asdf", slug="slug1"
-        )
+        self.conf = Conference(id=TEST_CONF_ID, name='conf_asdf', slug='slug1')
         self.conf.save()
         self.view.dispatch(self.request)
 
     def setUpTwoDatabases(self):
-        self.conf = Conference(
-            id=TEST_CONF_ID, name="slug1", slug="slug1"
-        )
+        self.conf = Conference(id=TEST_CONF_ID, name='slug1', slug='slug1')
         self.conf.save()
-        self.conf2 = Conference(
-            id=TEST_CONF_ID_2, name="slug2", slug="slug2"
-        )
+        self.conf2 = Conference(id=TEST_CONF_ID_2, name='slug2', slug='slug2')
         self.conf2.save()
 
     @override_settings(PLAINUI_CONFERENCE=TEST_CONF_ID)
@@ -2751,7 +3003,5 @@ class TestConferenceDetection(TestCase):
     def test_invalid_conference_config_no_setting(self):
         self.setUpTwoDatabases()
 
-        with self.assertRaisesMessage(
-                ImproperlyConfigured,
-                expected_message="Multiple Conferences found, set PLAINUI_CONFERENCE in settings!"):
+        with self.assertRaisesMessage(ImproperlyConfigured, expected_message='Multiple Conferences found, set PLAINUI_CONFERENCE in settings!'):
             self.view.dispatch(self.request)
diff --git a/src/plainui/urls.py b/src/plainui/urls.py
index 7061a3ba66523c7c4aab66175bb5eb51506f638c..99d0911261c680ab9924e224cbe95828d3fb0cb9 100644
--- a/src/plainui/urls.py
+++ b/src/plainui/urls.py
@@ -18,7 +18,11 @@ urlpatterns = [
     path('login', views.LoginView.as_view(), name='login'),
     path('signup', views.RegistrationView.as_view(), name='signup'),
     path('signup/done', views.RegistrationDoneView.as_view(), name='signup_done'),
-    re_path(r'^signup/activate/(?P<uid_b64>[0-9A-Za-z_\-]+)/(?P<channel_id>\d+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$', views.RegistrationActivationView.as_view(), name='signup_activate'),  # noqa: E501
+    re_path(
+        r'^signup/activate/(?P<uid_b64>[0-9A-Za-z_\-]+)/(?P<channel_id>\d+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$',
+        views.RegistrationActivationView.as_view(),
+        name='signup_activate',
+    ),  # noqa: E501
     path('password_reset', views.PasswordResetView.as_view(), name='password_reset'),
     path('password_reset_sent', views.PasswordResetDoneView.as_view(), name='password_reset_done'),
     path('password_change', views.PasswordChangeView.as_view(), name='password_change'),
@@ -33,7 +37,6 @@ urlpatterns = [
     path('redeem_token_create', views.RedeemTokenUserCreateView.as_view(), name='redeem_token_create_user'),
     path('redeem_token_loggedin', views.RedeemTokenLoggedIn.as_view(), name='redeem_token_loggedin'),
     path('token_password_reset', views.TokenPasswordResetView.as_view(), name='token_password_reset'),
-
     path('', views.LandingView.as_view(), name='landing'),
     path('board', views.BoardView.as_view(), name='board'),
     path('my_board', views.BoardPrivateView.as_view(), name='board_private'),
@@ -82,7 +85,6 @@ urlpatterns = [
     path('user-by-uuid/<uuid:uuid>/', views.UserByUuidView.as_view(), name='user_by_uuid'),
     path('wa_contact', views.WorkadventureContactPage.as_view(), name='wa_contact'),
     path('world_vcard/<uuid:wa_session_id>', views.WorkadventureVCard.as_view(), name='wa_vcard'),
-
     path('shibboleet', views.ShibboleetView.as_view(), name='shibboleet'),
 ]
 
diff --git a/src/plainui/utils.py b/src/plainui/utils.py
index 6a01bb4b65814b78079636964fd327446509f8d0..25ee469623bf059a7a6b5f0ef34a54d12572f3f4 100644
--- a/src/plainui/utils.py
+++ b/src/plainui/utils.py
@@ -25,19 +25,22 @@ def check_message_content(conf, request, text, kind, kind_data):
         request.user.shadow_banned = True
         request.user.save(update_fields=['shadow_banned'])
 
-        report_form = ReportForm(conf=conf, data={
-            'kind': kind,
-            'kind_data': str(kind_data),
-            'category': 'abuse',
-            'message': 'Autoban triggered',
-            'message2': '.',
-        })
+        report_form = ReportForm(
+            conf=conf,
+            data={
+                'kind': kind,
+                'kind_data': str(kind_data),
+                'category': 'abuse',
+                'message': 'Autoban triggered',
+                'message2': '.',
+            },
+        )
         report_form.is_valid()
 
         report_form.send_report_mail(request)
         return True
     except Exception:
-        raise Exception("Boom")  # don't allow leaking eg. ValidationErrors that might be handled in a View
+        raise Exception('Boom')  # don't allow leaking eg. ValidationErrors that might be handled in a View
 
 
 class StaticPageDiff(HtmlDiff):
@@ -45,16 +48,16 @@ class StaticPageDiff(HtmlDiff):
     def _format_line(self, side, flag, linenum, text):
         try:
             linenum = '%d' % linenum
-            id = ' id="%s%s"' % (self._prefix[side], linenum)
+            id = f' id="{self._prefix[side]}{linenum}"'
         except TypeError:
             # handle blank lines where linenum is '>' or ''
             id = ''
 
         # replace those things that would get confused with HTML symbols
-        text = text.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
+        text = text.replace('&', '&amp;').replace('>', '&gt;').replace('<', '&lt;')
 
         # make space non-breakable so they don't get compressed or line wrapped
         text = text.replace(' ', '&nbsp;').rstrip()
 
         # vvv ---- removed `nowrap="nowrap"` --- vvv
-        return '<td class="diff_header"%s>%s</td><td class="diff_content">%s</td>' % (id, linenum, text)
+        return f'<td class="diff_header"{id}>{linenum}</td><td class="diff_content">{text}</td>'
diff --git a/src/plainui/views.py b/src/plainui/views.py
index 01907b4e201266ec7dd9e0a8be571204933c9834..4c264cb97a1b767604d605ade213226a576c0180 100644
--- a/src/plainui/views.py
+++ b/src/plainui/views.py
@@ -1,56 +1,93 @@
-from datetime import datetime, time, timedelta, UTC
 import logging
 import threading
-
-from typing import ClassVar, List, Optional, Dict, Any
-from urllib.parse import urlparse, ParseResult, unquote
+from datetime import UTC, datetime, time, timedelta
+from typing import Any, ClassVar, Dict, List, Optional
+from urllib.parse import ParseResult, unquote, urlparse
 from uuid import UUID  # noqa:F401 as flake8 does not properly support PEP526 yet -.-
-from modeltranslation.fields import build_localized_fieldname
-from modeltranslation.settings import AVAILABLE_LANGUAGES
+
+from django_ratelimit.decorators import ratelimit
 
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth import login
-from django.contrib.auth import views as auth_views, mixins as auth_mixins
+from django.contrib.auth import mixins as auth_mixins
+from django.contrib.auth import views as auth_views
 from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import PermissionDenied, ValidationError, ImproperlyConfigured
 from django.core import signing
-from django.core.exceptions import SuspiciousOperation
-from django.db import transaction, connection
-from django.db.models import Q, F, Prefetch
-from django.http import Http404, HttpRequest, HttpResponseRedirect, HttpResponseNotFound, HttpResponse, HttpResponseForbidden
-from django.shortcuts import redirect, get_object_or_404
-from django.urls import reverse, reverse_lazy, translate_url, NoReverseMatch
+from django.core.exceptions import ImproperlyConfigured, PermissionDenied, SuspiciousOperation, ValidationError
+from django.db import connection, transaction
+from django.db.models import F, Prefetch, Q
+from django.http import Http404, HttpRequest, HttpResponse, HttpResponseForbidden, HttpResponseNotFound, HttpResponseRedirect
+from django.shortcuts import get_object_or_404, redirect
+from django.template.response import TemplateResponse
+from django.urls import NoReverseMatch, reverse, reverse_lazy, translate_url
 from django.utils import timezone
 from django.utils.decorators import method_decorator
 from django.utils.http import url_has_allowed_host_and_scheme
 from django.utils.timezone import localtime
-from django.utils.translation import gettext, check_for_language, get_language
+from django.utils.translation import check_for_language, get_language, gettext
+from django.utils.translation import gettext_lazy as _
 from django.views.decorators.debug import sensitive_post_parameters
-from django.views.generic.base import View, TemplateView
+from django.views.generic.base import TemplateView, View
 from django.views.generic.edit import FormView, UpdateView
-from django.template.response import TemplateResponse
-from django.utils.translation import gettext_lazy as _
-from django_ratelimit.decorators import ratelimit
+from modeltranslation.fields import build_localized_fieldname
+from modeltranslation.settings import AVAILABLE_LANGUAGES
 
 from core import integrations
 from core.integrations import WorkAdventureIntegration
-from core.models import Assembly, AssemblyLikeCount, Badge, Conference, ConferenceMember, ConferenceMemberTicket, ConferenceTag, DereferrerStats, UserContact, \
-    ConferenceTrack, DirectMessage, Event, EventAttachment, EventLikeCount, EventParticipant, PlatformUser, Room, RoomLink, StaticPage, StaticPageRevision, \
-    TagItem, UserBadge, BadgeToken,  UserDereferrerAllowlist, WorkadventureSession, BulletinBoardEntry, MetaNavItem
+from core.markdown import refresh_linking_markdown, render_markdown
+from core.models import (
+    Assembly,
+    AssemblyLikeCount,
+    Badge,
+    BadgeToken,
+    BulletinBoardEntry,
+    Conference,
+    ConferenceMember,
+    ConferenceMemberTicket,
+    ConferenceTag,
+    ConferenceTrack,
+    DereferrerStats,
+    DirectMessage,
+    Event,
+    EventAttachment,
+    EventLikeCount,
+    EventParticipant,
+    MetaNavItem,
+    PlatformUser,
+    Room,
+    RoomLink,
+    StaticPage,
+    StaticPageRevision,
+    TagItem,
+    UserBadge,
+    UserContact,
+    UserDereferrerAllowlist,
+    WorkadventureSession,
+)
+from core.models.badges import TokenInvalid
 from core.models.ticket import TicketValidationError
 from core.search import search
 from core.sso import SSO
-from core.markdown import render_markdown, refresh_linking_markdown
 from core.utils import url_in_allowlist
 from core.views import BaseLoginView, BasePasswordResetConfirmView, BasePasswordResetView, BaseRegistrationActivationView, BaseRegistrationView
-from core.models.badges import TokenInvalid
 
 from plainui.utils import StaticPageDiff
 
-from .forms import BulletinBoardEntryForm, ExampleForm, InputTokenForm, NewDirectMessageForm, \
-    ProfileEditForm, ProfileDescriptionEditForm, RedeemTokenAddToUserForm, RedeemTokenUserCreateForm, ReportForm, \
-    RedeemBadgeForm, StaticPageBodyForm, TokenPasswortResetForm
+from .forms import (
+    BulletinBoardEntryForm,
+    ExampleForm,
+    InputTokenForm,
+    NewDirectMessageForm,
+    ProfileDescriptionEditForm,
+    ProfileEditForm,
+    RedeemBadgeForm,
+    RedeemTokenAddToUserForm,
+    RedeemTokenUserCreateForm,
+    ReportForm,
+    StaticPageBodyForm,
+    TokenPasswortResetForm,
+)
 
 
 def _session_refresh_favorite_assemblies(session, user) -> List[str]:
@@ -162,27 +199,26 @@ class ConferenceRequiredMixin(auth_mixins.AccessMixin):
                         ConferenceRequiredMixin._conf = conf = conferences.get()
                         self._conf_timeout = now + timedelta(300)
                     except Conference.MultipleObjectsReturned:
-                        raise ImproperlyConfigured("Multiple Conferences found, set PLAINUI_CONFERENCE in settings!")
+                        raise ImproperlyConfigured('Multiple Conferences found, set PLAINUI_CONFERENCE in settings!')
                     except Conference.DoesNotExist:
                         if Conference.objects.exists():
-                            raise ImproperlyConfigured("PLAINUI_CONFERENCE from settings not found in database!")
+                            raise ImproperlyConfigured('PLAINUI_CONFERENCE from settings not found in database!')
                         else:
-                            raise ImproperlyConfigured("No conference found in database!")
+                            raise ImproperlyConfigured('No conference found in database!')
                     except ValidationError:
-                        raise ImproperlyConfigured("Invalid value for PLAINUI_CONFERENCE in settings!")
+                        raise ImproperlyConfigured('Invalid value for PLAINUI_CONFERENCE in settings!')
                 finally:
                     self._conf_lock.release()
 
         self.conf = conf
 
-        if (self.require_login and conf.require_login
-            or conf.require_ticket and self.require_conference_member
-            or self.require_user) \
-                and not request.user.is_authenticated:
+        if (
+            self.require_login and conf.require_login or conf.require_ticket and self.require_conference_member or self.require_user
+        ) and not request.user.is_authenticated:
             return self.handle_no_permission()
 
         if conf.require_ticket and self.require_conference_member and not self.has_ticket(request):
-            messages.error(request, gettext("Please activate your Ticket to access this conference!"))
+            messages.error(request, gettext('Please activate your Ticket to access this conference!'))
             return redirect(reverse('plainui:redeem_token'))
 
         if self._test_cork:
@@ -243,13 +279,10 @@ class EventView(ConferenceRequiredMixin, TemplateView):
         context['running_state'] = running_state
 
         context['attachments'] = EventAttachment.objects.filter(
-            event=event,
-            visibility__in=[EventAttachment.Visibility.PUBLIC, EventAttachment.Visibility.CONFERENCE]
+            event=event, visibility__in=[EventAttachment.Visibility.PUBLIC, EventAttachment.Visibility.CONFERENCE]
         )
 
-        context['report_info'] = {
-            'url': reverse('plainui:event', kwargs={'event_slug': event.slug})
-        }
+        context['report_info'] = {'url': reverse('plainui:event', kwargs={'event_slug': event.slug})}
         context['share_url'] = reverse('plainui:event', kwargs={'event_slug': event.slug})
         context['schedule_info'] = {'id': event.id, 'is': str(event.id) in personal_calendar}
         context['fav_info'] = {'type': 'event', 'id': event.id, 'is': str(event.id) in favorites}
@@ -268,9 +301,11 @@ class TagView(ConferenceRequiredMixin, TemplateView):
         context['tag'] = tag
 
         # TODO other types. What should we link here?
-        context['events'] = Event.objects.conference_accessible(self.conf).filter(
-            id__in=TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(Event)).values_list('target_id')
-        ).filter(schedule_start__isnull=False, schedule_end__isnull=False)
+        context['events'] = (
+            Event.objects.conference_accessible(self.conf)
+            .filter(id__in=TagItem.objects.filter(tag=tag, target_type=ContentType.objects.get_for_model(Event)).values_list('target_id'))
+            .filter(schedule_start__isnull=False, schedule_end__isnull=False)
+        )
         context['my_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
         context['my_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
 
@@ -294,34 +329,32 @@ class SosList(ConferenceRequiredMixin, TemplateView):
         context['is_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
         context['is_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
 
-        context['report_info'] = {
-            'url': reverse('plainui:sos')
-        }
+        context['report_info'] = {'url': reverse('plainui:sos')}
         context['share_url'] = reverse('plainui:sos')
 
         return context
 
 
 class SosJoin(ConferenceRequiredMixin, View):
-    """ extra view to join BBB rooms from the Workadventure """
+    """extra view to join BBB rooms from the Workadventure"""
 
     require_user = True
 
     def get(self, request, event_slug):
         event = get_object_or_404(Event.objects.conference_accessible(self.conf), slug=event_slug)
         if not event.kind == Event.Kind.SELF_ORGANIZED:
-            raise Http404()
+            raise Http404
 
         try:
             join_url = integrations.BigBlueButton.join_room(event, self.request.user, not self.request.user.show_name)
             if not join_url:
-                error = gettext("Unspecified error, room is probably full?")
+                error = gettext('Unspecified error, room is probably full?')
             else:
                 return HttpResponseRedirect(join_url)
         except integrations.IntegrationError as e:
             error = str(e)
 
-        messages.error(request, gettext("Joining Failed: %s") % (error,))
+        messages.error(request, gettext('Joining Failed: %s') % (error,))
         return redirect(reverse('plainui:event', kwargs={'event_slug': event_slug}))
 
 
@@ -350,15 +383,11 @@ class AssemblyView(ConferenceRequiredMixin, TemplateView):
 
         suggestions = assembly.suggestions.select_related('assembly2')
         suggestions = suggestions.exclude(assembly2=assembly.pk).filter(assembly2__state_assembly__in=Assembly.PUBLIC_STATES).order_by('-like_ratio')[:5]
-        context['suggested'] = [
-            s.assembly2 for s in suggestions
-        ]
+        context['suggested'] = [s.assembly2 for s in suggestions]
 
         context['spokespeople'] = assembly.members.filter(is_representative=True, show_public=True).select_related('member')
 
-        context['report_info'] = {
-            'url': reverse('plainui:assembly', kwargs={'assembly_slug': assembly.slug})
-        }
+        context['report_info'] = {'url': reverse('plainui:assembly', kwargs={'assembly_slug': assembly.slug})}
         context['share_url'] = reverse('plainui:assembly', kwargs={'assembly_slug': assembly.slug})
         context['fav_info'] = {'type': 'assembly', 'id': assembly.id, 'is': str(assembly.id) in favorites}
 
@@ -372,16 +401,14 @@ class AssembliesView(ConferenceRequiredMixin, TemplateView):
         context = super().get_context_data(**kwargs)
         context['conf'] = self.conf
         context['events_upcoming'] = _event_filter(self.request.user, self.conf, upcoming=True)
-        # TODO: reecommended events. TODO: Implement suggestions based on liked assemblies
-        # context['events_recommended'] = _event_filter(self.request.user, self.conf, user_schedule_only=True)[0:4]
+        # TODO: recommended events.
+        # TODO: Implement suggestions based on liked assemblies
         context['is_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
         context['is_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
         context['assemblies'] = Assembly.objects.conference_accessible(self.conf).order_by('name')
         context['my_favorite_assemblies'] = _session_get_favorite_assemblies(self.request.session, self.request.user)
 
-        context['report_info'] = {
-            'url': reverse('plainui:assemblies')
-        }
+        context['report_info'] = {'url': reverse('plainui:assemblies')}
         context['share_url'] = reverse('plainui:assemblies')
 
         return context
@@ -402,9 +429,7 @@ class AssembliesAllView(ConferenceRequiredMixin, TemplateView):
             context['assemblies'] = context['assemblies'].filter(is_official=False)
         context['my_favorite_assemblies'] = _session_get_favorite_assemblies(self.request.session, self.request.user)
 
-        context['report_info'] = {
-            'url': reverse('plainui:assemblies_all')
-        }
+        context['report_info'] = {'url': reverse('plainui:assemblies_all')}
         context['share_url'] = reverse('plainui:assemblies_all')
 
         return context
@@ -419,15 +444,11 @@ class AssembliesEventsView(ConferenceRequiredMixin, TemplateView):
 
         context['events_upcoming'] = _event_filter(self.request.user, self.conf, upcoming=True)
         # not used atm
-        # assemblies = Assembly.objects.conference_accessible(self.conf)
-        # context['assemblies'] = assemblies
         context['events_from_assemblies'] = _event_filter(self.request.user, self.conf, kinds=[Event.Kind.ASSEMBLY], public_fahrplan=False)
         context['is_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
         context['is_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
 
-        context['report_info'] = {
-            'url': reverse('plainui:assemblies_events')
-        }
+        context['report_info'] = {'url': reverse('plainui:assemblies_events')}
         context['share_url'] = reverse('plainui:assemblies_events')
 
         return context
@@ -467,7 +488,7 @@ class StaticPageView(ConferenceRequiredMixin, TemplateView):
                 if not self.request.user.is_authenticated:
                     return self.handle_no_permission()
                 else:
-                    messages.error(self.request, gettext("You need an active Ticket to access this Page!"))
+                    messages.error(self.request, gettext('You need an active Ticket to access this Page!'))
                     return redirect(reverse('plainui:redeem_token'))
 
         return super().get(request, page_slug=page_slug, **kwargs)
@@ -511,13 +532,11 @@ class StaticPageView(ConferenceRequiredMixin, TemplateView):
                     sanitize_html=static_page.sanitize_html,
                 )
                 if can_edit:
-                    context["edit_url"] = reverse(
-                        "plainui:static_page_edit", kwargs={"page_slug": page_slug}
-                    ) + ("?rev=" + revision if revision else "")
+                    context['edit_url'] = reverse('plainui:static_page_edit', kwargs={'page_slug': page_slug}) + ('?rev=' + revision if revision else '')
                 else:
-                    context["edit_url"] = None
+                    context['edit_url'] = None
             except StaticPageRevision.DoesNotExist:
-                context["revision_not_found"] = True
+                context['revision_not_found'] = True
                 page_body = static_page.body_html
         else:
             page_body = static_page.body_html
@@ -552,16 +571,16 @@ class StaticPageEditView(ConferenceRequiredMixin, TemplateView):
 
         if not static_page:
             # the page does not exist and the user does not have the permission to create it
-            messages.error(request, gettext("You do not have the required permissions to create this page."))
+            messages.error(request, gettext('You do not have the required permissions to create this page.'))
             return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug}))
 
         if static_page.privacy == StaticPage.Privacy.CONFERENCE and not self.has_ticket(request):
-            messages.error(request, gettext("You do not have the required permissions to edit this page."))
+            messages.error(request, gettext('You do not have the required permissions to edit this page.'))
             # TODO: after redeem, redirect to edit view for page_slug
             return redirect(reverse('plainui:redeem_token'))
 
         if static_page.privacy == StaticPage.Privacy.PERM and not self.request.user.has_conference_staffpermission(self.conf, 'static_pages'):
-            messages.error(request, gettext("You do not have the required permissions to edit this page."))
+            messages.error(request, gettext('You do not have the required permissions to edit this page.'))
             return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug}))
 
         revision = request.GET.get('rev')
@@ -575,25 +594,31 @@ class StaticPageEditView(ConferenceRequiredMixin, TemplateView):
             page_title = static_page.title
             not_latest_revision = False
 
-        form = StaticPageBodyForm(initial={
-            'title': page_title,
-            'body': instance.body,
-        })
+        form = StaticPageBodyForm(
+            initial={
+                'title': page_title,
+                'body': instance.body,
+            }
+        )
 
         if not writeable:
             form.fields['title'].disabled = True
             form.fields['body'].disabled = True
 
-        return TemplateResponse(request, self.template_name, {
-            'page': static_page,
-            'conf': self.conf,
-            'page_slug': page_slug,
-            'static_page': static_page,
-            'writeable': writeable,
-            'not_latest_revision': not_latest_revision,
-            'revision': revision,
-            'form': form,
-        })
+        return TemplateResponse(
+            request,
+            self.template_name,
+            {
+                'page': static_page,
+                'conf': self.conf,
+                'page_slug': page_slug,
+                'static_page': static_page,
+                'writeable': writeable,
+                'not_latest_revision': not_latest_revision,
+                'revision': revision,
+                'form': form,
+            },
+        )
 
     @method_decorator(ratelimit(key='ip', rate='5/m', method=ratelimit.UNSAFE))
     @method_decorator(ratelimit(key='post:username', rate='5/m', method=ratelimit.UNSAFE))
@@ -623,16 +648,20 @@ class StaticPageEditView(ConferenceRequiredMixin, TemplateView):
             not_latest_revision = False
 
         if not form.is_valid():
-            return TemplateResponse(request, self.template_name, {
-                'page': static_page,
-                'conf': self.conf,
-                'page_slug': page_slug,
-                'static_page': static_page,
-                'writeable': True,  # otherwise, `static_page` would be `None` and we'd have raised `PermissionDenied()` earlier
-                'not_latest_revision': not_latest_revision,
-                'revision': revision,
-                'form': form,
-            })
+            return TemplateResponse(
+                request,
+                self.template_name,
+                {
+                    'page': static_page,
+                    'conf': self.conf,
+                    'page_slug': page_slug,
+                    'static_page': static_page,
+                    'writeable': True,  # otherwise, `static_page` would be `None` and we'd have raised `PermissionDenied` earlier
+                    'not_latest_revision': not_latest_revision,
+                    'revision': revision,
+                    'form': form,
+                },
+            )
 
         title = form.cleaned_data['title']
         body = form.cleaned_data['body']
@@ -645,18 +674,22 @@ class StaticPageEditView(ConferenceRequiredMixin, TemplateView):
                 sanitize_html=static_page.sanitize_html,
             )
 
-            return TemplateResponse(request, self.template_name, {
-                'page': static_page,
-                'preview_title': title,
-                'preview_body': preview_body,
-                'conf': self.conf,
-                'page_slug': page_slug,
-                'static_page': static_page,
-                'writeable': True,  # otherwise, `static_page` would be `None` and we'd error out early
-                'not_latest_revision': not_latest_revision,
-                'revision': revision,
-                'form': form,
-            })
+            return TemplateResponse(
+                request,
+                self.template_name,
+                {
+                    'page': static_page,
+                    'preview_title': title,
+                    'preview_body': preview_body,
+                    'conf': self.conf,
+                    'page_slug': page_slug,
+                    'static_page': static_page,
+                    'writeable': True,  # otherwise, `static_page` would be `None` and we'd error out early
+                    'not_latest_revision': not_latest_revision,
+                    'revision': revision,
+                    'form': form,
+                },
+            )
 
         if not page_exists:
             static_page.save()
@@ -667,9 +700,9 @@ class StaticPageEditView(ConferenceRequiredMixin, TemplateView):
         revision.save()
 
         if not page_exists:
-            messages.success(request, gettext("Created Static Page"))
+            messages.success(request, gettext('Created Static Page'))
         else:
-            messages.success(request, gettext("Updated Static Page"))
+            messages.success(request, gettext('Updated Static Page'))
         return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug}))
 
 
@@ -678,8 +711,7 @@ class StaticPageHistoryView(ConferenceRequiredMixin, TemplateView):
     require_user = True
 
     def get(self, request, page_slug, **kwargs):
-        self.static_page = StaticPage.objects.conference_accessible(conference=self.conf, language=get_language())\
-            .filter(slug=page_slug).first()
+        self.static_page = StaticPage.objects.conference_accessible(conference=self.conf, language=get_language()).filter(slug=page_slug).first()
         if not self.static_page:
             return redirect(reverse('plainui:static_page', kwargs={'page_slug': page_slug}))
 
@@ -730,13 +762,13 @@ class StaticPageDiffView(ConferenceRequiredMixin, TemplateView):
         rev2_id = self.request.GET.get('rev2')
 
         if rev1_id is None or rev2_id is None:
-            raise SuspiciousOperation("Only one revision given, need two")
+            raise SuspiciousOperation('Only one revision given, need two')
 
         try:
             rev1 = self.static_page.revisions.get(revision=rev1_id)
             rev2 = self.static_page.revisions.get(revision=rev2_id)
         except StaticPageRevision.DoesNotExist:
-            raise Http404("One of the selected revisions was not found")
+            raise Http404('One of the selected revisions was not found')
 
         differ = StaticPageDiff(tabsize=4)
         diff = differ.make_table(rev1.body.splitlines(keepends=True), rev2.body.splitlines(keepends=True), context=True, numlines=3)
@@ -770,7 +802,7 @@ class StaticPageGlobalHistoryView(ConferenceRequiredMixin, TemplateView):
 
         context = super().get_context_data(**kwargs)
         context['conf'] = self.conf
-        context['history'] = history[PAGE_SIZE * page: PAGE_SIZE * page + PAGE_SIZE]
+        context['history'] = history[PAGE_SIZE * page : PAGE_SIZE * page + PAGE_SIZE]
         return context
 
 
@@ -791,7 +823,7 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
         form1 = super().get_form(form_class)
 
         form2_kwargs = super().get_form_kwargs()
-        if hasattr(self, "object"):
+        if hasattr(self, 'object'):
             cm = self.object.conferences.filter(conference=self.conf).first()
             if cm:
                 form2_kwargs['instance'] = cm
@@ -814,11 +846,19 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
         context['badges'] = UserBadge.objects.filter(user=user, accepted_by_user=True).select_related('badge')
         context['amount_badges_not_accepted'] = len(UserBadge.objects.filter(user=user, accepted_by_user=False).select_related('badge'))
         context['is_favorite_events'] = favorite_events = _session_get_favorite_events(self.request.session, self.request.user)
-        context['my_favorite_events'] = Event.objects.conference_accessible(conference=self.conf).filter(pk__in=favorite_events).filter(schedule_start__isnull=False, schedule_end__isnull=False)  # noqa:E501
+        context['my_favorite_events'] = (
+            Event.objects.conference_accessible(conference=self.conf)
+            .filter(pk__in=favorite_events)
+            .filter(schedule_start__isnull=False, schedule_end__isnull=False)
+        )  # noqa:E501
         context['is_favorite_assemblies'] = favorite_assemblies = _session_get_favorite_assemblies(self.request.session, self.request.user)
         context['my_favorite_assemblies'] = Assembly.objects.conference_accessible(conference=self.conf).filter(pk__in=favorite_assemblies)
         context['is_fahrplan_events'] = scheduled_events = _session_get_scheduled_events(self.request.session, self.request.user)
-        context['my_fahrplan_events'] = Event.objects.conference_accessible(conference=self.conf).filter(pk__in=scheduled_events).filter(schedule_start__isnull=False, schedule_end__isnull=False)  # noqa:E501
+        context['my_fahrplan_events'] = (
+            Event.objects.conference_accessible(conference=self.conf)
+            .filter(pk__in=scheduled_events)
+            .filter(schedule_start__isnull=False, schedule_end__isnull=False)
+        )  # noqa:E501
         context['dereferrer_allowlist'] = UserDereferrerAllowlist.objects.filter(user=self.request.user)
         context['redeem_badge_form'] = RedeemBadgeForm()
         return context
@@ -848,7 +888,7 @@ class ProfileView(ConferenceRequiredMixin, UpdateView):
 
         form2.save()
         self.request.session['theme'] = form1.instance.theme
-        messages.success(self.request, gettext("Updated Profile"))
+        messages.success(self.request, gettext('Updated Profile'))
 
         try:
             WorkAdventureIntegration.push_userinfo(conference=self.conf, user=self.request.user)
@@ -881,7 +921,7 @@ class ModifyThemeView(ConferenceRequiredMixin, View):
         return redirect(redirect_to)
 
 
-@method_decorator(ratelimit(group="redeem_badge", key='user', rate=settings.BADGE_RATE_LIMIT), name='dispatch')
+@method_decorator(ratelimit(group='redeem_badge', key='user', rate=settings.BADGE_RATE_LIMIT), name='dispatch')
 class RedeemBadgeView(ConferenceRequiredMixin, FormView):
     require_user = True
     template_name = 'plainui/manage_badges.html'
@@ -889,7 +929,7 @@ class RedeemBadgeView(ConferenceRequiredMixin, FormView):
     external_context = None
 
     def __init__(self, **kwargs: Any) -> None:
-        self.external_context = kwargs.pop("external_context", {})
+        self.external_context = kwargs.pop('external_context', {})
         super().__init__(**kwargs)
 
     def get_form_kwargs(self) -> dict[str, Any]:
@@ -913,7 +953,7 @@ class RedeemBadgeView(ConferenceRequiredMixin, FormView):
         context = super().get_context_data(**kwargs)
         context['conf'] = self.conf
         context.update(self.external_context)
-        context["redeem_badge_form"] = context.pop("form")
+        context['redeem_badge_form'] = context.pop('form')
         return context
 
     def get_success_url(self):
@@ -938,11 +978,11 @@ class ManageBadgeView(ConferenceRequiredMixin, TemplateView):
     template_name = 'plainui/manage_badges.html'
 
     def get_queryset(self):
-        return UserBadge.objects.filter(user=self.request.user).values_list("badge", flat=True)
+        return UserBadge.objects.filter(user=self.request.user).values_list('badge', flat=True)
 
     def get_context_data(self, *args, **kwargs):
         context = super().get_context_data(*args, **kwargs)
-        if hasattr(self, "conf"):
+        if hasattr(self, 'conf'):
             context['conf'] = self.conf
 
         user = self.request.user
@@ -959,7 +999,7 @@ class ManageBadgeView(ConferenceRequiredMixin, TemplateView):
         return resp
 
     def dispatch(self, request, *args, **kwargs):
-        if hasattr(request, "GET") and request.GET.get('redeem_token') or hasattr(request, "GET") and request.POST.get('purpose', None) == 'redeem_token':
+        if hasattr(request, 'GET') and request.GET.get('redeem_token') or hasattr(request, 'GET') and request.POST.get('purpose', None) == 'redeem_token':
             return RedeemBadgeView.as_view(external_context=self.get_context_data())(request, *args, **kwargs)
         return super().dispatch(request, *args, **kwargs)
 
@@ -972,7 +1012,7 @@ class ManageBadgeView(ConferenceRequiredMixin, TemplateView):
             userbadge = UserBadge.objects.filter(user=user, badge=badge).first()
 
             if not userbadge:
-                raise Exception("The badge is not found.")
+                raise Exception('The badge is not found.')
             if not visibility:
                 # Change acceptanceState
                 if userbadge.accepted_by_user:
@@ -1118,7 +1158,7 @@ class RedeemTokenLoggedIn(ConferenceRequiredMixin, View):
         try:
             with transaction.atomic():
                 if self.conf.end <= self.now:
-                    raise TicketValidationError(gettext("The Conference is over!"))
+                    raise TicketValidationError(gettext('The Conference is over!'))
                 ConferenceMemberTicket.redeem_pretix_ticket(self.conf, request.user, token)
                 ConferenceMember.objects.update_or_create(conference=self.conf, user=request.user, defaults={'has_ticket': True})
                 return HttpResponseRedirect(reverse('plainui:index'))
@@ -1151,39 +1191,50 @@ class ModifyFavoritesView(ConferenceRequiredMixin, View):
                         EventLikeCount.objects.filter(event1=fav_id, event2__in=user.favorite_events.exclude(pk=fav_id)).update(likes=F('likes') - 1)
                         EventLikeCount.objects.filter(event2=fav_id, event1__in=user.favorite_events.all()).update(likes=F('likes') - 1)
                         request.user.favorite_events.remove(fav_id)
-            else:
-                if request.POST['type'] == 'assembly':
-                    assembly = Assembly.objects.conference_accessible(conference=self.conf).get(pk=fav_id)
-                    if not request.user.favorite_assemblies.filter(pk=fav_id).exists():
-                        request.user.favorite_assemblies.add(assembly)
-                        with connection.cursor() as cursor:
-                            cursor.execute("""
+            elif request.POST['type'] == 'assembly':
+                assembly = Assembly.objects.conference_accessible(conference=self.conf).get(pk=fav_id)
+                if not request.user.favorite_assemblies.filter(pk=fav_id).exists():
+                    request.user.favorite_assemblies.add(assembly)
+                    with connection.cursor() as cursor:
+                        cursor.execute(
+                            """
+                        INSERT INTO core_assemblylikecount AS lc (assembly1_id, assembly2_id, likes, like_ratio)
+                            SELECT %s, e.assembly_id, 1, 0 FROM core_assembly_favorited_by AS e WHERE e.assembly_id <> %s AND e.platformuser_id = %s
+                            ON CONFLICT (assembly1_id, assembly2_id) DO UPDATE SET likes = lc.likes + 1
+                        """,
+                            [fav_id, fav_id, user.pk],
+                        )
+
+                        cursor.execute(
+                            """
                             INSERT INTO core_assemblylikecount AS lc (assembly1_id, assembly2_id, likes, like_ratio)
-                                SELECT %s, e.assembly_id, 1, 0 FROM core_assembly_favorited_by AS e WHERE e.assembly_id <> %s AND e.platformuser_id = %s
+                                SELECT e.assembly_id, %s, 1, 0 FROM core_assembly_favorited_by AS e WHERE e.platformuser_id = %s
                                 ON CONFLICT (assembly1_id, assembly2_id) DO UPDATE SET likes = lc.likes + 1
-                            """, [fav_id, fav_id, user.pk])
-
-                            cursor.execute("""
-                                INSERT INTO core_assemblylikecount AS lc (assembly1_id, assembly2_id, likes, like_ratio)
-                                    SELECT e.assembly_id, %s, 1, 0 FROM core_assembly_favorited_by AS e WHERE e.platformuser_id = %s
-                                    ON CONFLICT (assembly1_id, assembly2_id) DO UPDATE SET likes = lc.likes + 1
-                            """, [fav_id, user.pk])
-                elif request.POST['type'] == 'event':
-                    event = Event.objects.conference_accessible(conference=self.conf).get(pk=fav_id)
-                    if not request.user.favorite_events.filter(pk=fav_id).exists():
-                        request.user.favorite_events.add(event)
-                        with connection.cursor() as cursor:
-                            cursor.execute("""
+                        """,
+                            [fav_id, user.pk],
+                        )
+            elif request.POST['type'] == 'event':
+                event = Event.objects.conference_accessible(conference=self.conf).get(pk=fav_id)
+                if not request.user.favorite_events.filter(pk=fav_id).exists():
+                    request.user.favorite_events.add(event)
+                    with connection.cursor() as cursor:
+                        cursor.execute(
+                            """
+                        INSERT INTO core_eventlikecount AS lc (event1_id, event2_id, likes, like_ratio)
+                            SELECT %s, e.event_id, 1, 0 FROM core_event_favorited_by AS e WHERE e.event_id <> %s AND e.platformuser_id = %s
+                            ON CONFLICT (event1_id, event2_id) DO UPDATE SET likes = lc.likes + 1
+                        """,
+                            [fav_id, fav_id, user.pk],
+                        )
+
+                        cursor.execute(
+                            """
                             INSERT INTO core_eventlikecount AS lc (event1_id, event2_id, likes, like_ratio)
-                                SELECT %s, e.event_id, 1, 0 FROM core_event_favorited_by AS e WHERE e.event_id <> %s AND e.platformuser_id = %s
+                                SELECT e.event_id, %s, 1, 0 FROM core_event_favorited_by AS e WHERE e.platformuser_id = %s
                                 ON CONFLICT (event1_id, event2_id) DO UPDATE SET likes = lc.likes + 1
-                            """, [fav_id, fav_id, user.pk])
-
-                            cursor.execute("""
-                                INSERT INTO core_eventlikecount AS lc (event1_id, event2_id, likes, like_ratio)
-                                    SELECT e.event_id, %s, 1, 0 FROM core_event_favorited_by AS e WHERE e.platformuser_id = %s
-                                    ON CONFLICT (event1_id, event2_id) DO UPDATE SET likes = lc.likes + 1
-                            """, [fav_id, user.pk])
+                        """,
+                            [fav_id, user.pk],
+                        )
 
         if request.POST['type'] == 'assembly':
             _session_refresh_favorite_assemblies(self.request.session, self.request.user)
@@ -1207,6 +1258,7 @@ class ModifyLanguageView(ConferenceRequiredMixin, View):
 
     def post(self, request):
         from core.templatetags.hub_absolute import hub_absolute
+
         redirect_to = request.POST.get('next') or hub_absolute('plainui:index')
 
         url_is_safe = url_has_allowed_host_and_scheme(
@@ -1278,7 +1330,7 @@ class PersonalMessageListView(ConferenceRequiredMixin, TemplateView):
 
         context['start'] = start
         context['total'] = dms.count()
-        context['msgs'] = dms[start:start + PAGE_SIZE]
+        context['msgs'] = dms[start : start + PAGE_SIZE]
         return context
 
 
@@ -1290,10 +1342,7 @@ class PersonalMessageSendView(ConferenceRequiredMixin, FormView):
     form_class = NewDirectMessageForm
 
     def get_context_data(self, **kwargs):
-        return super().get_context_data(
-            conf=self.conf,
-            **kwargs
-        )
+        return super().get_context_data(conf=self.conf, **kwargs)
 
     def get_initial(self):
         initial = super().get_initial()
@@ -1328,7 +1377,7 @@ class PersonalMessageSendView(ConferenceRequiredMixin, FormView):
         if in_reply_to:
             DirectMessage.objects.filter(id=in_reply_to, recipient=self.request.user).update(has_responded=True)
 
-        messages.success(self.request, gettext("Message sent."))
+        messages.success(self.request, gettext('Message sent.'))
         return super().form_valid(form)
 
 
@@ -1347,10 +1396,7 @@ class PersonalMessageShowView(ConferenceRequiredMixin, TemplateView):
             DirectMessage.objects.filter(pk=message.pk).update(was_read=True)
         context['msg_body'] = render_markdown(self.conf, message.body)
 
-        context['report_info'] = {
-            'kind': 'pn',
-            'url': message.id
-        }
+        context['report_info'] = {'kind': 'pn', 'url': message.id}
 
         return context
 
@@ -1366,7 +1412,7 @@ class PersonalMessageDeleteView(ConferenceRequiredMixin, View):
         DirectMessage.objects.filter(recipient=request.user, pk=request.POST['id']).update(deleted_by_recipient=True)
         DirectMessage.objects.filter(pk=request.POST['id'], deleted_by_sender=True, deleted_by_recipient=True).delete()
 
-        messages.success(self.request, gettext("Message deleted."))
+        messages.success(self.request, gettext('Message deleted.'))
         return redirect(reverse('plainui:personal_message'))
 
 
@@ -1381,7 +1427,7 @@ class IndexView(ConferenceRequiredMixin, TemplateView):
                 default_title = 'Page Missing'
 
             if not default_body:
-                url = reverse("plainui:static_page_edit", kwargs={"page_slug": slug})
+                url = reverse('plainui:static_page_edit', kwargs={'page_slug': slug})
                 default_body = 'Please configure the wiki page "{slug}" (or change slug). <a href="{url}">(edit)</a>'
 
             static_page = StaticPage(conference=self.conf, title=default_title, body_html=default_body.format(slug=slug, url=url))
@@ -1391,9 +1437,7 @@ class IndexView(ConferenceRequiredMixin, TemplateView):
         context = super().get_context_data(**kwargs)
         context['conf'] = self.conf
 
-        context['report_info'] = {
-            'url': reverse('plainui:index')
-        }
+        context['report_info'] = {'url': reverse('plainui:index')}
         context['share_url'] = reverse('plainui:index')
 
         context['start'] = self._fetch_page('start')
@@ -1461,13 +1505,10 @@ class PasswordChangeView(ConferenceRequiredMixin, auth_views.PasswordChangeView)
         return super().dispatch(*args, **kwargs)
 
     def get_context_data(self, **kwargs):
-        return super().get_context_data(
-            conf=self.conf,
-            **kwargs
-        )
+        return super().get_context_data(conf=self.conf, **kwargs)
 
     def get_success_url(self):
-        messages.success(self.request, gettext("Password changed successfully."))
+        messages.success(self.request, gettext('Password changed successfully.'))
         return reverse('plainui:userprofile')
 
 
@@ -1476,9 +1517,7 @@ class PasswordResetView(ConferenceRequiredMixin, BasePasswordResetView):
     require_login = False
     success_url = reverse_lazy('plainui:password_reset_done')
     template_name = 'plainui/registration/password_reset_form.html'
-    extra_email_context = {
-        "reset_url": 'plainui:password_reset_confirm'
-    }
+    extra_email_context = {'reset_url': 'plainui:password_reset_confirm'}
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
@@ -1544,12 +1583,12 @@ class TokenPasswordResetView(ConferenceRequiredMixin, FormView):
 
     def form_valid(self, form):
         form.save()
-        messages.success(self.request, gettext("Passwort successfully reset!"))
+        messages.success(self.request, gettext('Passwort successfully reset!'))
         return HttpResponseRedirect(reverse('plainui:login'))
 
 
 class LogoutView(auth_views.LogoutView):
-    next_page = "plainui:login"
+    next_page = 'plainui:login'
 
 
 class BoardView(ConferenceRequiredMixin, TemplateView):
@@ -1564,22 +1603,17 @@ class BoardView(ConferenceRequiredMixin, TemplateView):
             start = int(self.request.GET['start'])
         except (ValueError, KeyError):
             start = 0
-        entries = BulletinBoardEntry.objects \
-            .select_related('owner') \
-            .filter(conference=self.conf, is_public=True) \
-            .order_by('-timestamp')
+        entries = BulletinBoardEntry.objects.select_related('owner').filter(conference=self.conf, is_public=True).order_by('-timestamp')
         if self.request.user.is_authenticated and self.request.user.shadow_banned:
             entries = entries.filter(Q(owner=self.request.user) | Q(hidden=False))
         else:
             entries = entries.filter(hidden=False)
-        context['board'] = entries[start:start + PAGE_SIZE]
+        context['board'] = entries[start : start + PAGE_SIZE]
         context['current_user'] = self.request.user.id if self.request.user.is_authenticated else 0
         context['next_url'] = reverse('plainui:board') + '?start=' + str(start + PAGE_SIZE)
         context['prev_url'] = reverse('plainui:board') + '?start=' + str(start - PAGE_SIZE) if start != 0 else None
 
-        context['report_info'] = {
-            'url': reverse('plainui:board')
-        }
+        context['report_info'] = {'url': reverse('plainui:board')}
         context['share_url'] = reverse('plainui:board') if start == 0 else reverse('plainui:board') + '?start=' + str(start)
 
         return context
@@ -1593,9 +1627,7 @@ class BoardPrivateView(ConferenceRequiredMixin, TemplateView):
         context = super().get_context_data(**kwargs)
         context['conf'] = self.conf
 
-        context['board'] = BulletinBoardEntry.objects \
-            .filter(conference=self.conf, owner=self.request.user) \
-            .order_by('-timestamp')
+        context['board'] = BulletinBoardEntry.objects.filter(conference=self.conf, owner=self.request.user).order_by('-timestamp')
         return context
 
 
@@ -1609,10 +1641,7 @@ class BoardEntryView(ConferenceRequiredMixin, TemplateView):
 
         context['entry'] = get_object_or_404(BulletinBoardEntry, conference=self.conf, pk=self.kwargs['id'], is_public=True)
 
-        context['report_info'] = {
-            'kind': 'board',
-            'url': self.kwargs['id']
-        }
+        context['report_info'] = {'kind': 'board', 'url': self.kwargs['id']}
         context['share_url'] = reverse('plainui:board_entry', kwargs={'id': self.kwargs['id']})
 
         return context
@@ -1626,11 +1655,7 @@ class BoardEntryEditView(ConferenceRequiredMixin, FormView):
     form_class = BulletinBoardEntryForm
 
     def get_context_data(self, **kwargs):
-        return super().get_context_data(
-            conf=self.conf,
-            edit_mode=self.kwargs.get('id', 0) != 0,
-            **kwargs
-        )
+        return super().get_context_data(conf=self.conf, edit_mode=self.kwargs.get('id', 0) != 0, **kwargs)
 
     def get_form_kwargs(self):
         kwargs = super().get_form_kwargs()
@@ -1652,9 +1677,9 @@ class BoardEntryEditView(ConferenceRequiredMixin, FormView):
         self.board_entry.save()
 
         if 'id' in self.kwargs:
-            messages.success(self.request, gettext("Bulletin Board Entry updated."))
+            messages.success(self.request, gettext('Bulletin Board Entry updated.'))
         else:
-            messages.success(self.request, gettext("Bulletin Board Entry created."))
+            messages.success(self.request, gettext('Bulletin Board Entry created.'))
         return super().form_valid(form)
 
 
@@ -1667,7 +1692,7 @@ class BoardEntryDeleteView(ConferenceRequiredMixin, View):
     def post(self, request, **kwargs):
         BulletinBoardEntry.objects.filter(conference_id=self.conf, owner=self.request.user, id=request.POST['id']).delete()
 
-        messages.success(self.request, gettext("Bulletin Board Entry deleted."))
+        messages.success(self.request, gettext('Bulletin Board Entry deleted.'))
         return redirect(reverse('plainui:board_private'))
 
 
@@ -1696,12 +1721,12 @@ def _organize_events_for_calendar(conf, events):
     if not events_by_room:
         return None
 
-    rooms = Room.objects.conference_accessible(conf) \
-        .filter(pk__in=events_by_room.keys()) \
+    rooms = (
+        Room.objects.conference_accessible(conf)
+        .filter(pk__in=events_by_room.keys())
         .order_by('official_room_order', F('capacity').desc(nulls_last=True), 'name')
-    rooms_with_events = [
-        (room, events_by_room[room.id]) for room in rooms
-    ]
+    )
+    rooms_with_events = [(room, events_by_room[room.id]) for room in rooms]
 
     # calendar starts at first scheduled event rounded down to the full hour
     calendar_start = min(
@@ -1711,7 +1736,7 @@ def _organize_events_for_calendar(conf, events):
     calendar_start = localtime(calendar_start.replace(minute=0, second=0, microsecond=0))
 
     # calendar ends at the end of the last scheduled event rounded up to the full hour
-    calendar_end = max((room_events[-1]['event'].schedule_end for (_, room_events) in rooms_with_events))
+    calendar_end = max(room_events[-1]['event'].schedule_end for (_, room_events) in rooms_with_events)
     calendar_end = (calendar_end + timedelta(0, 3599)).replace(minute=0, second=0, microsecond=0)
     calendar_end = localtime(calendar_end)
 
@@ -1736,8 +1761,18 @@ UPCOMING_WINDOW = timedelta(minutes=30)
 
 
 def _event_filter(
-        user: PlatformUser, conf, day=None, kinds=None, assembly=None, track=None,
-        room=None, user_schedule_only=False, upcoming=False, calendar_mode=True, public_fahrplan=None):
+    user: PlatformUser,
+    conf,
+    day=None,
+    kinds=None,
+    assembly=None,
+    track=None,
+    room=None,
+    user_schedule_only=False,
+    upcoming=False,
+    calendar_mode=True,
+    public_fahrplan=None,
+):
     min_date, max_date = conf.start, conf.end
     if min_date is None or max_date is None:
         return Event.objects.none()
@@ -1766,20 +1801,11 @@ def _event_filter(
         events = events.filter(Q(schedule_start__lt=now, schedule_end__gte=now) | Q(schedule_start__gte=now, schedule_start__lt=now + UPCOMING_WINDOW))
     if calendar_mode:
         events = events.filter(room__isnull=False)
-    res = events.filter(
-            schedule_duration__isnull=False,
-            **filters
-        ).order_by('schedule_start', 'schedule_end')
+    res = events.filter(schedule_duration__isnull=False, **filters).order_by('schedule_start', 'schedule_end')
     res = res.annotate(track_name=F('track__name'))
     speakers = EventParticipant.objects.filter(is_public=True, role=EventParticipant.Role.SPEAKER).order_by('participant__username')
     speakers = speakers.annotate(speaker_name=F('participant__username'))
-    return res.prefetch_related(
-        Prefetch(
-            'participants',
-            queryset=speakers,
-            to_attr='speakers'
-        )
-    )
+    return res.prefetch_related(Prefetch('participants', queryset=speakers, to_attr='speakers'))
 
 
 class FahrplanView(ConferenceRequiredMixin, TemplateView):
@@ -1804,7 +1830,7 @@ class FahrplanView(ConferenceRequiredMixin, TemplateView):
         min_date = self.conf.start
         max_date = self.conf.end
         if min_date is None or max_date is None:
-            raise Http404()
+            raise Http404
         n_days = (max_date - min_date).days
         if (max_date - min_date) != timedelta(n_days):
             n_days += 1
@@ -1907,7 +1933,8 @@ class FahrplanView(ConferenceRequiredMixin, TemplateView):
             track=track,
             calendar_mode=mode == 'calendar',
             public_fahrplan=public_fahrplan,
-            **self.filter_opts)
+            **self.filter_opts,
+        )
 
         if mode == 'calendar':
             context['mode'] = 'calendar'
@@ -1939,20 +1966,19 @@ class PublicFahrplanView(ConferenceRequiredMixin, TemplateView):
                 context['conference_timezone'] = self.conf.timezone
 
         events = _event_filter(self.request.user, self.conf, public_fahrplan=True)
-        # events = _event_filter(self.request.user, self.conf)
         context['events'] = _organize_events_for_calendar(self.conf, events)
         return context
 
 
 class AssemblyJoinBBB(ConferenceRequiredMixin, View):
-    """ extra view to join BBB rooms from the Workadventure """
+    """extra view to join BBB rooms from the Workadventure"""
 
     require_user = True
 
     def get(self, request, assembly_slug, room_slug):
         room = get_object_or_404(Room.objects.conference_accessible(self.conf), assembly__slug=assembly_slug, slug=room_slug)
         if not room.room_type == Room.RoomType.BIGBLUEBUTTON:
-            raise Http404()
+            raise Http404
 
         try:
             return HttpResponseRedirect(integrations.BigBlueButton.join_room(room, self.request.user, not self.request.user.show_name))
@@ -1973,14 +1999,14 @@ class RoomView(ConferenceRequiredMixin, TemplateView):
                 if self.request.user.is_authenticated:
                     return HttpResponseRedirect(integrations.BigBlueButton.join_room(self.room, self.request.user, not self.request.user.show_name))
 
-                messages.error(request, gettext("RoomView--loginrequired"))
+                messages.error(request, gettext('RoomView--loginrequired'))
             elif self.room.room_type == Room.RoomType.WORKADVENTURE:
                 if self.request.user.is_authenticated:
                     wa_session = WorkadventureSession.create_for_conference_user(self.conf, self.request.user, self.room)
                     token_url = wa_session.get_token_url()
                     return redirect(token_url)
 
-                messages.error(request, gettext("RoomView--loginrequired"))
+                messages.error(request, gettext('RoomView--loginrequired'))
         except integrations.IntegrationError as e:
             messages.error(request, str(e))
 
@@ -2005,9 +2031,7 @@ class RoomView(ConferenceRequiredMixin, TemplateView):
         context['my_favorite_events'] = _session_get_favorite_events(self.request.session, self.request.user)
         context['my_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
 
-        context['report_info'] = {
-            'url': reverse('plainui:room', kwargs={'room_slug': self.room.slug})
-        }
+        context['report_info'] = {'url': reverse('plainui:room', kwargs={'room_slug': self.room.slug})}
         context['share_url'] = reverse('plainui:room', kwargs={'room_slug': self.room.slug})
 
         return context
@@ -2021,9 +2045,7 @@ class RoomsView(ConferenceRequiredMixin, TemplateView):
         context['conf'] = self.conf
         context['rooms'] = Room.objects.conference_accessible(self.conf)
 
-        context['report_info'] = {
-            'url': reverse('plainui:rooms')
-        }
+        context['report_info'] = {'url': reverse('plainui:rooms')}
         context['share_url'] = reverse('plainui:rooms')
 
         return context
@@ -2040,7 +2062,7 @@ class UpcomingView(ConferenceRequiredMixin, TemplateView):
             events=events,
             my_favorite_events=_session_get_favorite_events(self.request.session, self.request.user),
             my_scheduled_events=_session_get_scheduled_events(self.request.session, self.request.user),
-            **kwargs
+            **kwargs,
         )
 
 
@@ -2077,33 +2099,29 @@ class ChannelEventsView(ConferenceRequiredMixin, TemplateView):
             rooms.append(room)
             room_ids.append(room.pk)
 
-        current_events_qs = Event.objects.conference_accessible(self.conf)\
-            .filter(room_id__in=room_ids, schedule_start__lte=now, schedule_end__gte=now).order_by('room_id', 'schedule_start').distinct('room_id')
+        current_events_qs = (
+            Event.objects.conference_accessible(self.conf)
+            .filter(room_id__in=room_ids, schedule_start__lte=now, schedule_end__gte=now)
+            .order_by('room_id', 'schedule_start')
+            .distinct('room_id')
+        )
         speakers = EventParticipant.objects.filter(is_public=True, role=EventParticipant.Role.SPEAKER).order_by('participant__username')
         speakers = speakers.annotate(speaker_name=F('participant__username'))
-        current_events_qs = current_events_qs.prefetch_related(
-            Prefetch(
-                'participants',
-                queryset=speakers,
-                to_attr='speakers'
-            )
-        )
+        current_events_qs = current_events_qs.prefetch_related(Prefetch('participants', queryset=speakers, to_attr='speakers'))
 
         current_events = {}  # type: Dict[UUID, Event]
         for event in current_events_qs:
             current_events[event.room_id] = event
 
-        next_events_qs = Event.objects.conference_accessible(self.conf)\
-            .filter(room_id__in=room_ids, schedule_start__gt=now, schedule_end__isnull=False).order_by('room_id', 'schedule_start').distinct('room_id')
+        next_events_qs = (
+            Event.objects.conference_accessible(self.conf)
+            .filter(room_id__in=room_ids, schedule_start__gt=now, schedule_end__isnull=False)
+            .order_by('room_id', 'schedule_start')
+            .distinct('room_id')
+        )
         speakers = EventParticipant.objects.filter(is_public=True, role=EventParticipant.Role.SPEAKER).order_by('participant__username')
         speakers = speakers.annotate(speaker_name=F('participant__username'))
-        next_events_qs = next_events_qs.prefetch_related(
-            Prefetch(
-                'participants',
-                queryset=speakers,
-                to_attr='speakers'
-            )
-        )
+        next_events_qs = next_events_qs.prefetch_related(Prefetch('participants', queryset=speakers, to_attr='speakers'))
 
         next_events = {}  # type: Dict[UUID, Event]
         for event in next_events_qs:
@@ -2131,12 +2149,9 @@ class ChannelEventsView(ConferenceRequiredMixin, TemplateView):
         context['my_scheduled_events'] = _session_get_scheduled_events(self.request.session, self.request.user)
 
         events = _event_filter(self.request.user, self.conf, public_fahrplan=True)
-        # events = _event_filter(self.request.user, self.conf)
         context['events'] = _organize_events_for_calendar(self.conf, events)
 
-        context['report_info'] = {
-            'url': reverse('plainui:channel_events')
-        }
+        context['report_info'] = {'url': reverse('plainui:channel_events')}
         context['share_url'] = reverse('plainui:channel_events')
 
         return context
@@ -2181,7 +2196,7 @@ class WorkadventureEnter(ConferenceRequiredMixin, View):
 class BaseDereferrerView(ConferenceRequiredMixin, View):
     def gen_salt(self):
         if self.request.user.is_authenticated:
-            return '%sdereferrer' % (self.request.user.pk,)
+            return f'{self.request.user.pk}dereferrer'
         else:
             return '000000dereferrer'
 
@@ -2189,7 +2204,7 @@ class BaseDereferrerView(ConferenceRequiredMixin, View):
         try:
             return signing.loads(signed_payload, salt=self.gen_salt())
         except signing.BadSignature:
-            raise PermissionDenied()
+            raise PermissionDenied
 
     def sign_payload(self, payload: str) -> str:
         return signing.dumps(payload, salt=self.gen_salt())
@@ -2201,11 +2216,12 @@ class BaseDereferrerView(ConferenceRequiredMixin, View):
         if url_in_allowlist(scheme_and_netloc, settings.DEREFERRER_COUNT_ACCESS):
             with connection.cursor() as cursor:
                 cursor.execute(
-                    "INSERT INTO " + DereferrerStats._meta.db_table + " AS ds (domain, hits) VALUES (%s, 1) \
-                        ON CONFLICT (domain) DO UPDATE SET hits = ds.hits + 1",
-                    [scheme_and_netloc]
+                    'INSERT INTO '
+                    + DereferrerStats._meta.db_table
+                    + ' AS ds (domain, hits) VALUES (%s, 1) \
+                        ON CONFLICT (domain) DO UPDATE SET hits = ds.hits + 1',
+                    [scheme_and_netloc],
                 )
-            # DereferrerStats.objects.filter(domain=scheme_and_netloc).update(hits=F('hits') + 1)
 
         return HttpResponseRedirect(url.geturl())
 
@@ -2247,7 +2263,7 @@ class DereferrerView(BaseDereferrerView):
         url = urlparse(destination)
 
         # local urls are always allowed
-        allowlist_match = (not url.scheme and not url.netloc)
+        allowlist_match = not url.scheme and not url.netloc
         scheme = url.scheme if url.scheme else request.scheme
         scheme_and_netloc = scheme + '://' + url.netloc
 
@@ -2271,13 +2287,17 @@ class DereferrerView(BaseDereferrerView):
         else:
             # if the url is not allowed, show the dereferrer page
             signed_url = self.sign_payload(url.geturl())
-            return TemplateResponse(request, self.template_name, context={
-                'conf': self.conf,
-                'plain_url': url.geturl(),
-                'domain': scheme_and_netloc,
-                'signed_url': signed_url,
-                'can_allow': request.user.is_authenticated,
-            })
+            return TemplateResponse(
+                request,
+                self.template_name,
+                context={
+                    'conf': self.conf,
+                    'plain_url': url.geturl(),
+                    'domain': scheme_and_netloc,
+                    'signed_url': signed_url,
+                    'can_allow': request.user.is_authenticated,
+                },
+            )
 
 
 class WorkadventureDereferrerView(DereferrerView):
@@ -2295,7 +2315,7 @@ class DereferrerRemoveFromAllowlistView(View):
             return redirect(reverse('plainui:userprofile'))
 
         entry.delete()
-        messages.success(request, gettext("UserDereferrerAllowlist--deleted"))
+        messages.success(request, gettext('UserDereferrerAllowlist--deleted'))
         return redirect(reverse('plainui:userprofile'))
 
 
@@ -2314,11 +2334,7 @@ class ReportContentView(ConferenceRequiredMixin, FormView):
                 conference=self.conf, title='Page Missing', body_html='Please configure the static page "report_content" (or change slug... ).'
             )
 
-        return super().get_context_data(
-            conf=self.conf,
-            page=static_page,
-            **kwargs
-        )
+        return super().get_context_data(conf=self.conf, page=static_page, **kwargs)
 
     def get_form_kwargs(self):
         kwargs = super().get_form_kwargs()
@@ -2338,10 +2354,10 @@ class ReportContentView(ConferenceRequiredMixin, FormView):
         messages.success(
             self.request,
             gettext(
-                "Thank you for your help to make this plattform safer and better! "
-                "Please give us some time to find a solution and keep an eye on your Messages, we may contact you."
-            )
-         )
+                'Thank you for your help to make this plattform safer and better! '
+                'Please give us some time to find a solution and keep an eye on your Messages, we may contact you.'
+            ),
+        )
 
         redirect_to = form.cleaned_data['next']
         url_is_safe = url_has_allowed_host_and_scheme(
@@ -2375,20 +2391,26 @@ class UserView(ConferenceRequiredMixin, TemplateView):
         contact = user.is_authenticated and UserContact.objects.filter(user=display_user, contact=user).first()
         if contact and not contact.pending:
             # Show Friend Badges
-            context['badges'] = UserBadge.objects.filter(user=display_user, accepted_by_user=True) \
-                                .filter(Q(visibility__in=[UserBadge.Visibility.PUBLIC, UserBadge.Visibility.FRIENDS, UserBadge.Visibility.CLUBFRIENDS]) |
-                                        Q(badge__in=user_badges, visibility__in=[UserBadge.Visibility.CLUB, UserBadge.Visibility.CLUBFRIENDS])) \
-                                .select_related('badge')
+            context['badges'] = (
+                UserBadge.objects.filter(user=display_user, accepted_by_user=True)
+                .filter(
+                    Q(visibility__in=[UserBadge.Visibility.PUBLIC, UserBadge.Visibility.FRIENDS, UserBadge.Visibility.CLUBFRIENDS])
+                    | Q(badge__in=user_badges, visibility__in=[UserBadge.Visibility.CLUB, UserBadge.Visibility.CLUBFRIENDS])
+                )
+                .select_related('badge')
+            )
         else:
             # Show only public badges
-            context['badges'] = UserBadge.objects.filter(user=display_user, accepted_by_user=True) \
-                                .filter(Q(visibility__in=[UserBadge.Visibility.PUBLIC]) |
-                                        Q(badge__in=user_badges, visibility__in=[UserBadge.Visibility.CLUB, UserBadge.Visibility.CLUBFRIENDS])) \
-                                .select_related('badge')
+            context['badges'] = (
+                UserBadge.objects.filter(user=display_user, accepted_by_user=True)
+                .filter(
+                    Q(visibility__in=[UserBadge.Visibility.PUBLIC])
+                    | Q(badge__in=user_badges, visibility__in=[UserBadge.Visibility.CLUB, UserBadge.Visibility.CLUBFRIENDS])
+                )
+                .select_related('badge')
+            )
 
-        context['report_info'] = {
-            'url': reverse('plainui:user', kwargs={'user_slug': display_user.slug})
-        }
+        context['report_info'] = {'url': reverse('plainui:user', kwargs={'user_slug': display_user.slug})}
 
         return context
 
@@ -2411,44 +2433,34 @@ class ComponentGalleryView(TemplateView):
             conf={
                 'slug': 'sample',
                 'name': 'Example',
-                "id": "confID",
-                "get_navigation_tree": lambda: [{
-                    'label': 'Conference',
-                    'title': 'Conference',
-                    'icon': 'hammer',
-                    'url': '/',
-                }],
-                "map_config": {"frontend": {
-                    "start": {
-                        "latitude": 53.56172,
-                        "longitude": 9.98593,
-                        "zoom": 16
-                    },
-                    "style": {
-                        "version": 8,
-                        "sources": {
-                            "osm-raster": {
-                                "type": "raster",
-                                "tiles": [
-                                    "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
-                                ],
-                                "tileSize": 256,
-                                "attribution": "&copy; OpenStreetMap contributors / test use only!!!"
-                            }
+                'id': 'confID',
+                'get_navigation_tree': lambda: [
+                    {
+                        'label': 'Conference',
+                        'title': 'Conference',
+                        'icon': 'hammer',
+                        'url': '/',
+                    }
+                ],
+                'map_config': {
+                    'frontend': {
+                        'start': {'latitude': 53.56172, 'longitude': 9.98593, 'zoom': 16},
+                        'style': {
+                            'version': 8,
+                            'sources': {
+                                'osm-raster': {
+                                    'type': 'raster',
+                                    'tiles': ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
+                                    'tileSize': 256,
+                                    'attribution': '&copy; OpenStreetMap contributors / test use only!!!',
+                                }
+                            },
+                            'layers': [{'id': 'simple-tiles', 'type': 'raster', 'source': 'osm-raster', 'minzoom': 0, 'maxzoom': 19}],
                         },
-                        "layers": [
-                            {
-                                "id": "simple-tiles",
-                                "type": "raster",
-                                "source": "osm-raster",
-                                "minzoom": 0,
-                                "maxzoom": 19
-                            }
-                        ]
                     }
-                }}
+                },
             },
-            demo_map_startpos={"longitude": 9.98641, "latitude": 53.56148},
+            demo_map_startpos={'longitude': 9.98641, 'latitude': 53.56148},
             csrf_input='xss-safe',
             hide_header=True,
             time1=timezone.now(),
diff --git a/tests/utils.py b/tests/utils.py
index eec7db6e6ed349c98703c608fcb5f44988b2af7f..988e8863196903b0c85999f7a6a82de6e51126a6 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -4,7 +4,6 @@ from urllib.parse import urljoin
 
 import requests
 
-
 _DEFAULT_BASE_URL = 'http://hubapp'
 _DEFAULT_ADMIN_USERNAME = 'admin'
 _DEFAULT_ADMIN_PASSWORD = 'password'