diff --git a/aleksis/apps/untis/management/commands/untis_import_mysql.py b/aleksis/apps/untis/management/commands/untis_import_mysql.py
index 2da36c8c0cb0d740a16956c448955bb577546873..f433d5fd5348bf5fe1a92efbdeebca61f6da0cf0 100644
--- a/aleksis/apps/untis/management/commands/untis_import_mysql.py
+++ b/aleksis/apps/untis/management/commands/untis_import_mysql.py
@@ -25,6 +25,6 @@ class Command(BaseCommand):
     def handle(self, *args, **options):
         command = COMMANDS_BY_NAME[options["command"]]
         background = options["background"]
-        school_id = options.get("school_id", None)
-        version = options.get("plan_version", None)
+        school_id = options.get("school_id")
+        version = options.get("plan_version")
         command.run(background=background, school_id=school_id, version=version)
diff --git a/aleksis/apps/untis/tests/util/test_mysql_util.py b/aleksis/apps/untis/tests/util/test_mysql_util.py
index 17a095295e9a698f3104b32cf2976560982cca6a..86974fb3cc382d6ffe5699d2312b2d0ad3bb34fb 100644
--- a/aleksis/apps/untis/tests/util/test_mysql_util.py
+++ b/aleksis/apps/untis/tests/util/test_mysql_util.py
@@ -4,12 +4,22 @@ from aleksis.apps.untis.util.mysql.util import (
     untis_split_third,
 )
 
-test_lesson_element = """49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;,49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;"""
+test_lesson_element = (
+    '49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, Fokus Physik"~'
+    '"Nawi"~~n~~;11;12;13;14;15;16;17;,49~0~302~7r;~0;~0~0~175608~~'
+    '~~~"Freiraum: Naturwissenschaften, Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;'
+)
 
-test_lesson_element_partial = """49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;"""
+test_lesson_element_partial = (
+    '49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften,'
+    + ' Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;'
+)
 test_lesson_element_partial_partial = ";11;12;13;14;15;16;17;"
 
-test_lesson_element_second = """46~0~92~45;~45;~12~11~0~~B~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~21700,45~0~92~45;~45;~0~0~19000~~~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~0"""
+test_lesson_element_second = (
+    '46~0~92~45;~45;~12~11~0~~B~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~'
+    + '21700,45~0~92~45;~45;~0~0~19000~~~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~0'
+)
 
 
 def test_untis_split_first():
@@ -22,14 +32,16 @@ def test_untis_split_first():
 
     assert len(untis_split_first(test_lesson_element)) == 2
     assert untis_split_first(test_lesson_element) == [
-        """49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;""",
-        """49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;""",
+        '49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, '
+        + 'Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;',
+        '49~0~302~7r;~0;~0~0~175608~~~~~"Freiraum: Naturwissenschaften, '
+        + 'Fokus Physik"~"Nawi"~~n~~;11;12;13;14;15;16;17;',
     ]
 
     assert len(untis_split_first(test_lesson_element_second)) == 2
     assert untis_split_first(test_lesson_element_second) == [
-        """46~0~92~45;~45;~12~11~0~~B~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~21700""",
-        """45~0~92~45;~45;~0~0~19000~~~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~0""",
+        '46~0~92~45;~45;~12~11~0~~B~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~21700',
+        '45~0~92~45;~45;~0~0~19000~~~~~~~~n~~;18;~"Wp_Eb"~~34~0~67900~0~0~~0',
     ]
 
 
@@ -45,7 +57,7 @@ def test_untis_split_second():
     assert untis_split_second("1~~3", int, remove_empty=False) == [1, None, 3]
     assert untis_split_second("1~~3", int) == [1, 3]
 
-    assert untis_split_second(""""asdf"~"asdf"~"asdf""") == ["asdf", "asdf", "asdf"]
+    assert untis_split_second('"asdf"~"asdf"~"asdf') == ["asdf", "asdf", "asdf"]
 
     assert len(untis_split_second(test_lesson_element_partial, remove_empty=False)) == 18
     assert untis_split_second(test_lesson_element_partial, remove_empty=False) == [
@@ -82,7 +94,7 @@ def test_untis_split_third():
     assert untis_split_third("1;;3", int, remove_empty=False) == [1, None, 3]
     assert untis_split_third("1;;3", int) == [1, 3]
 
-    assert untis_split_third(""""asdf";"asdf";"asdf""") == ["asdf", "asdf", "asdf"]
+    assert untis_split_third('"asdf";"asdf";"asdf') == ["asdf", "asdf", "asdf"]
 
     assert len(untis_split_third(test_lesson_element_partial_partial)) == 7
     assert untis_split_third(test_lesson_element_partial_partial) == [
diff --git a/aleksis/apps/untis/util/mysql/importers/common_data.py b/aleksis/apps/untis/util/mysql/importers/common_data.py
index 78eff319266b5b058a08989361ebf07138b97ff1..23d7d2786b14d7c273eeb2b933072df2a2dc8b37 100644
--- a/aleksis/apps/untis/util/mysql/importers/common_data.py
+++ b/aleksis/apps/untis/util/mysql/importers/common_data.py
@@ -2,7 +2,6 @@ import logging
 import re
 from datetime import datetime, time, timedelta
 from enum import Enum
-from typing import Dict, Tuple
 
 from django.utils.translation import gettext as _
 
@@ -34,7 +33,7 @@ class CommonDataId(Enum):
     PERIOD = 40
 
 
-def import_subjects(validity_range: ValidityRange) -> Dict[int, cursus_models.Subject]:
+def import_subjects(validity_range: ValidityRange) -> dict[int, cursus_models.Subject]:
     """Import subjects."""
     subjects_ref = {}
 
@@ -98,7 +97,7 @@ def import_subjects(validity_range: ValidityRange) -> Dict[int, cursus_models.Su
     return subjects_ref
 
 
-def import_teachers(validity_range: ValidityRange) -> Dict[int, core_models.Person]:
+def import_teachers(validity_range: ValidityRange) -> dict[int, core_models.Person]:
     """Import teachers."""
     teachers_ref = {}
 
@@ -161,8 +160,8 @@ def import_teachers(validity_range: ValidityRange) -> Dict[int, core_models.Pers
 
 
 def import_classes(
-    validity_range: ValidityRange, teachers_ref: Dict[int, core_models.Person]
-) -> Dict[int, core_models.Group]:
+    validity_range: ValidityRange, teachers_ref: dict[int, core_models.Person]
+) -> dict[int, core_models.Group]:
     """Import classes."""
     classes_ref = {}
 
@@ -271,7 +270,7 @@ def import_classes(
     return classes_ref
 
 
-def import_rooms(validity_range: ValidityRange) -> Dict[int, core_models.Room]:
+def import_rooms(validity_range: ValidityRange) -> dict[int, core_models.Room]:
     """Import rooms."""
     ref = {}
 
@@ -319,7 +318,7 @@ def import_rooms(validity_range: ValidityRange) -> Dict[int, core_models.Room]:
 
 def import_supervision_areas(
     validity_range: ValidityRange, breaks_ref, teachers_ref
-) -> Dict[int, core_models.Room]:
+) -> dict[int, core_models.Room]:
     """Import supervision areas."""
     ref = {}
 
@@ -329,9 +328,8 @@ def import_supervision_areas(
     for area in tqdm(areas, desc="Import supervision areas", **TQDM_DEFAULTS):
         if not area.name:
             raise RuntimeError(
-                "Supervision area ID {}: Cannot import supervision area without short name.".format(
-                    area.corridor_id
-                )
+                f"Supervision area ID {area.corridor_id}: "
+                "Cannot import supervision area without short name."
             )
 
         short_name = area.name
@@ -391,9 +389,8 @@ def import_supervision_areas(
                 slot = breaks_ref[weekday][before_period_number]
 
                 logger.info(
-                    "Import supervision on weekday {} before the {}. period (teacher {})".format(
-                        weekday, before_period_number, teacher
-                    )
+                    f"Import supervision on weekday {weekday} before the "
+                    f"{before_period_number}. period (teacher {teacher})"
                 )
 
                 # Get or create
@@ -461,8 +458,8 @@ def import_supervision_areas(
 
 def import_slots(
     validity_range: ValidityRange,
-) -> Tuple[
-    Dict[int, Dict[int, lesrooster_models.Slot]], Dict[int, Dict[int, lesrooster_models.BreakSlot]]
+) -> tuple[
+    dict[int, dict[int, lesrooster_models.Slot]], dict[int, dict[int, lesrooster_models.BreakSlot]]
 ]:
     """Import time periods an breaks."""
     times = (
@@ -554,7 +551,7 @@ def import_slots(
 
 def import_absence_reasons(
     validity_range: ValidityRange,
-) -> Dict[int, kolego_models.AbsenceReason]:
+) -> dict[int, kolego_models.AbsenceReason]:
     """Import absence reasons."""
     ref = {}
 
@@ -566,9 +563,8 @@ def import_absence_reasons(
     for reason in tqdm(reasons, desc="Import absence reasons", **TQDM_DEFAULTS):
         if not reason.name:
             raise RuntimeError(
-                "Absence reason ID {}: Cannot import absence reason without short name.".format(
-                    reason.absence_reason_id
-                )
+                f"Absence reason ID {reason.absence_reason_id}: "
+                "Cannot import absence reason without short name."
             )
 
         # Build values
diff --git a/aleksis/apps/untis/util/mysql/importers/holidays.py b/aleksis/apps/untis/util/mysql/importers/holidays.py
index db43a1b5a2403047fc765563efd06f78d92f9257..a8fbee630e3ea27f04d7f67009018d4f6547f252 100644
--- a/aleksis/apps/untis/util/mysql/importers/holidays.py
+++ b/aleksis/apps/untis/util/mysql/importers/holidays.py
@@ -1,5 +1,4 @@
 import logging
-from typing import Dict
 
 from tqdm import tqdm
 
@@ -14,7 +13,7 @@ logger = logging.getLogger(__name__)
 
 def import_holidays(
     validity_range: lesrooster_models.ValidityRange,
-) -> Dict[int, core_models.Holiday]:
+) -> dict[int, core_models.Holiday]:
     """Import holidays."""
     ref = {}
 
diff --git a/aleksis/apps/untis/util/mysql/importers/terms.py b/aleksis/apps/untis/util/mysql/importers/terms.py
index e71007086f5d8f9b2870d307a6d269719c9e8de0..8ce4f5fcb30078b11c6fd06e23d8b19cc74c5f9c 100644
--- a/aleksis/apps/untis/util/mysql/importers/terms.py
+++ b/aleksis/apps/untis/util/mysql/importers/terms.py
@@ -1,6 +1,6 @@
 import logging
 from datetime import date, timedelta
-from typing import Dict, Optional
+from typing import Optional
 
 from django.db.models import Max, OuterRef, Q, QuerySet, Subquery
 from django.utils import timezone
@@ -62,7 +62,7 @@ def import_terms(
     qs: Optional[QuerySet] = None,
     school_id: Optional[int] = None,
     version: Optional[int] = None,
-) -> Dict[int, lesrooster_models.ValidityRange]:
+) -> dict[int, lesrooster_models.ValidityRange]:
     """Import terms and school years as validity ranges and school terms."""
     ranges_ref = {}
 
diff --git a/aleksis/apps/untis/util/mysql/util.py b/aleksis/apps/untis/util/mysql/util.py
index e7a6d1c9fcc7c7affa6b47f991cbea3e48cbb8a7..16c78ad5bd41fbf389bf6749ece0ac6c306321cb 100644
--- a/aleksis/apps/untis/util/mysql/util.py
+++ b/aleksis/apps/untis/util/mysql/util.py
@@ -1,8 +1,9 @@
 import csv
 import logging
 import re
+from collections.abc import Sequence
 from datetime import date, datetime
-from typing import Any, Callable, Optional, Sequence, Type, Union
+from typing import Any, Callable, Optional, Union
 
 from django.db.models import Model, QuerySet
 
@@ -104,7 +105,7 @@ class UntisSemicolonDialect(UntisDialect):
     delimiter = ";"
 
 
-def split_with_csv_parser(s: str, dialect: Type[csv.Dialect]) -> Sequence:
+def split_with_csv_parser(s: str, dialect: type[csv.Dialect]) -> Sequence:
     """Split string with CSV parser."""
     parsed = csv.reader([s], dialect=dialect)
     return next(parsed)
diff --git a/pyproject.toml b/pyproject.toml
index 0ccbaf317ba11e813b3c88fd41af9e63a1f47bae..e58173fa16885ad48e7ff84fad7e5f09a084aabc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -16,7 +16,7 @@ include = [
 
 description = "AlekSIS (School Information System) — App for Untis import"
 authors = ["Dominik George <dominik.george@teckids.org>", "Julian Leucker <leuckeju@katharineum.de>", "Frank Poetzsch-Heffter <p-h@katharineum.de>", "Jonathan Weth <dev@jonathanweth.de>", "Tom Teichler <tom.teichler@teckids.org>", "mirabilos <thorsten.glaser@teckids.org>"]
-maintainers = ["Jonathan Weth <dev@jonathanweth.de>", "Dominik George <dominik.george@teckids.org>"]
+maintainers = ["Jonathan Weth <jonathan.weth@teckids.org>", "Dominik George <dominik.george@teckids.org>"]
 license = "EUPL-1.2-or-later"
 homepage = "https://aleksis.org"
 repository = "https://edugit.org/AlekSIS/official/AlekSIS-App-Untis"
@@ -39,7 +39,6 @@ priority = "primary"
 name = "gitlab"
 url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
 priority = "supplemental"
-
 [tool.poetry.dependencies]
 python = "^3.10"
 mysqlclient = "^2.0.0"
@@ -56,18 +55,16 @@ untis = "aleksis.apps.untis.apps:UntisConfig"
 django-stubs = "^4.2"
 safety = "^2.3.5"
 
-ruff = "^0.1.5"
-
-curlylint = "^0.13.0"
+ruff = "^0.8.2"
 
 [tool.poetry.group.test.dependencies]
-pytest = "^7.2"
-pytest-django = "^4.1"
+pytest = "^8.3"
+pytest-django = "^4.9"
 pytest-django-testing-postgresql = "^0.2"
-pytest-cov = "^4.0.0"
-pytest-sugar = "^0.9.2"
-selenium = "<4.10.0"
-freezegun = "^1.1.0"
+pytest-cov = "^6.0.0"
+pytest-sugar = "^1.0.0"
+selenium = "^4.27.0"
+freezegun = "^1.5.0"
 
 [tool.poetry.group.docs]
 optional = true
@@ -78,20 +75,20 @@ sphinxcontrib-django = "^2.3.0"
 sphinxcontrib-svg2pdfconverter = "^1.1.1"
 sphinx-autodoc-typehints = "^1.7"
 sphinx_material = "^0.0.35"
-
 [tool.ruff]
-exclude = ["migrations", "tests"]
+exclude = ["migrations"]
 line-length = 100
 
 [tool.ruff.lint]
 select = ["E", "F", "UP", "B", "SIM", "I", "DJ", "A", "S"]
 ignore = ["UP034", "UP015", "B028"]
-
-[tool.ruff.isort]
+[tool.ruff.lint.extend-per-file-ignores]
+"**/*/tests/**/*.py" = ["S101", "ARG", "FBT", "PLR2004", "S311", "S105"]
+[tool.ruff.lint.isort]
 known-first-party = ["aleksis"]
 section-order = ["future", "standard-library", "django", "third-party", "first-party", "local-folder"]
 
-[tool.ruff.isort.sections]
+[tool.ruff.lint.isort.sections]
 django = ["django"]
 [build-system]
 requires = ["poetry-core>=1.0.0"]