diff --git a/Makefile b/Makefile index b62310b..178e130 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,9 @@ migrate: ## Migrate the database to the latest version @${DOCKER_EXEC} python manage.py migrate .PHONY: compilemessages -compilemessages: ## Compile translations +messages: ## Compile translations + @echo ✨ Finding translations + @${DOCKER_EXEC} python manage.py makemessages -l nl @echo ✨ Compiling translations @${DOCKER_EXEC} python manage.py compilemessages --ignore .venv @@ -50,7 +52,7 @@ _clean: @echo ✨ Stopping containers @docker compose down -v @echo ✨ Removing compiled files - @rm -f tvdt/**/locale/*/LC_MESSAGES/django.mo + @rm -f tvdt/*/locale/*/LC_MESSAGES/django.mo tvdt/locale/*/LC_MESSAGES/django.mo .PHONY: clean clean: _clean init diff --git a/tvdt/elimination/__init__.py b/tvdt/backoffice/__init__.py similarity index 100% rename from tvdt/elimination/__init__.py rename to tvdt/backoffice/__init__.py diff --git a/tvdt/elimination/apps.py b/tvdt/backoffice/apps.py similarity index 60% rename from tvdt/elimination/apps.py rename to tvdt/backoffice/apps.py index 9cb489d..e3254c3 100644 --- a/tvdt/elimination/apps.py +++ b/tvdt/backoffice/apps.py @@ -1,6 +1,6 @@ from django.apps import AppConfig -class EliminationConfig(AppConfig): +class BackofficeConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "elimination" + name = "backoffice" diff --git a/tvdt/backoffice/converters.py b/tvdt/backoffice/converters.py new file mode 100644 index 0000000..250a73a --- /dev/null +++ b/tvdt/backoffice/converters.py @@ -0,0 +1,14 @@ +from quiz.models import Quiz + + +class QuizConverter: + regex = r"\d+" + + def to_python(self, value: str) -> Quiz: + try: + return Quiz.objects.get(id=value) + except Quiz.DoesNotExist: + raise ValueError + + def to_url(self, value: Quiz | int) -> str: + return str(value.id) if isinstance(value, Quiz) else value diff --git a/tvdt/backoffice/templates/backoffice/base.html b/tvdt/backoffice/templates/backoffice/base.html new file mode 100644 index 0000000..7765e22 --- /dev/null +++ b/tvdt/backoffice/templates/backoffice/base.html @@ -0,0 +1,36 @@ +{% load i18n %} +{% load static %} + + + + + + + + + {% block title %} + {% translate "Tijd voor de test" %} + {% endblock title %} + + + + {% block nav %} + {% include "backoffice/nav.html" %} + {% endblock nav %} +
+
+ {% include "messages.html" %} + {% block body %} + {% endblock body %} +
+
+ + {% block script %} + {% endblock script %} + diff --git a/tvdt/backoffice/templates/backoffice/index.html b/tvdt/backoffice/templates/backoffice/index.html new file mode 100644 index 0000000..d61e80b --- /dev/null +++ b/tvdt/backoffice/templates/backoffice/index.html @@ -0,0 +1,45 @@ +{% extends "backoffice/base.html" %} +{% load i18n %} +{% block body %} +

{% translate "Your Seasons" %}

+ + + + + + + + + + + + {% for season in seasons %} + + + + + + + + {% empty %} + EMPTY + {% endfor %} + +
{% translate "Name" %}{% translate "Active Quiz" %}{% translate "Season Code" %}{% translate "Preregister?" %}{% translate "Manage" %}
{{ season.name }} + {% if season.active_quiz %} + {{ season.active_quiz.name }} + {% else %} + {% translate "No active quiz" %} + {% endif %} + + {{ season.season_code }} + + + + {% translate "Manage" %} +
+{% endblock body %} diff --git a/tvdt/backoffice/templates/backoffice/nav.html b/tvdt/backoffice/templates/backoffice/nav.html new file mode 100644 index 0000000..6f03737 --- /dev/null +++ b/tvdt/backoffice/templates/backoffice/nav.html @@ -0,0 +1,51 @@ +{% load i18n %} + diff --git a/tvdt/backoffice/templates/backoffice/quiz.html b/tvdt/backoffice/templates/backoffice/quiz.html new file mode 100644 index 0000000..e880f0e --- /dev/null +++ b/tvdt/backoffice/templates/backoffice/quiz.html @@ -0,0 +1,92 @@ +{% extends "backoffice/base.html" %} +{% load i18n %} +{% block body %} +

+

{% translate "Quiz" %}: {{ quiz.season.name }} - {{ quiz.name }}

+

+
+

+

{% translate "Questions" %}

+

+
+ {% for question in quiz.questions.all %} +
+

+ +

+
+
+ {% for answer in question.answers.all %} +
  • {{ answer.text }}
  • + {% empty %} + {% translate "There are no answers for this question" %} + {% endfor %} +
    +
    +
    + {% empty %} + EMPTY + {% endfor %} +
    +
    +
    +

    +

    {% translate "Score" %}

    +

    + +

    {% translate "Number of dropouts:" %} {{ quiz.dropouts }}

    + + + + + + + + + + + + {% with result=quiz.get_score %} + {% for candidate in result %} + + + + + + + + {% empty %} + {% endfor %} + +
    {% translate "Candidate" %}{% translate "Correct Answers" %}{% translate "Corrections" %}{% translate "Score" %}{% translate "Time" %}
    {{ candidate.name }}{{ candidate.correct }}{{ candidate.corrections }}{{ candidate.score }}{{ candidate.time }}
    + {% endwith %} +
    +{% endblock body %} +{% block script %} + +{% endblock script %} diff --git a/tvdt/backoffice/templates/backoffice/season.html b/tvdt/backoffice/templates/backoffice/season.html new file mode 100644 index 0000000..9b824a9 --- /dev/null +++ b/tvdt/backoffice/templates/backoffice/season.html @@ -0,0 +1,25 @@ +{% extends "backoffice/base.html" %} +{% load i18n %} +{% block body %} +

    +

    {% translate "Season" %}: {{ season.name }}

    +

    +
    +
    +

    {% translate "Quizzes" %}

    +
    + {% for quiz in season.quizzes.all %} + {{ quiz.name }} + {% empty %} + {% endfor %} +
    +
    +
    +

    {% translate "Candidates" %}

    + +
    +
    +{% endblock body %} diff --git a/tvdt/elimination/tests.py b/tvdt/backoffice/tests.py similarity index 100% rename from tvdt/elimination/tests.py rename to tvdt/backoffice/tests.py diff --git a/tvdt/backoffice/urls.py b/tvdt/backoffice/urls.py new file mode 100644 index 0000000..1190c97 --- /dev/null +++ b/tvdt/backoffice/urls.py @@ -0,0 +1,21 @@ +from django.contrib.auth.decorators import login_required +from django.urls import path, register_converter + +from tvdt.converters import SeasonCodeConverter + +from .converters import QuizConverter +from .views import BackofficeIndexView, QuizView, SeasonView + +register_converter(SeasonCodeConverter, "season") +register_converter(QuizConverter, "quiz") + +app_name = "backoffice" +urlpatterns = [ + path("", login_required(BackofficeIndexView.as_view()), name="index"), + path( + "/", + login_required(SeasonView.as_view()), + name="season", + ), + path("/", login_required(QuizView.as_view()), name="quiz"), +] diff --git a/tvdt/backoffice/views/__init__.py b/tvdt/backoffice/views/__init__.py new file mode 100644 index 0000000..6bcb9a9 --- /dev/null +++ b/tvdt/backoffice/views/__init__.py @@ -0,0 +1,3 @@ +from .home import BackofficeIndexView +from .quiz import QuizView +from .season import SeasonView diff --git a/tvdt/backoffice/views/home.py b/tvdt/backoffice/views/home.py new file mode 100644 index 0000000..23f8f51 --- /dev/null +++ b/tvdt/backoffice/views/home.py @@ -0,0 +1,10 @@ +from django.http import HttpRequest +from django.views.generic import TemplateView + + +class BackofficeIndexView(TemplateView): + template_name = "backoffice/index.html" + + def get(self, request: HttpRequest, *args, **kwargs): + seasons = request.user.seasons.all() + return self.render_to_response({"seasons": seasons}) diff --git a/tvdt/backoffice/views/quiz.py b/tvdt/backoffice/views/quiz.py new file mode 100644 index 0000000..7144121 --- /dev/null +++ b/tvdt/backoffice/views/quiz.py @@ -0,0 +1,13 @@ +from django.http import HttpRequest +from django.views import View +from django.views.generic.base import TemplateResponseMixin + +from quiz.models import Quiz + + +class QuizView(View, TemplateResponseMixin): + template_name = "backoffice/quiz.html" + + def get(self, request: HttpRequest, quiz: Quiz, *args, **kwargs): + + return self.render_to_response({"quiz": quiz}) diff --git a/tvdt/backoffice/views/season.py b/tvdt/backoffice/views/season.py new file mode 100644 index 0000000..de3c0e9 --- /dev/null +++ b/tvdt/backoffice/views/season.py @@ -0,0 +1,12 @@ +from django.http import HttpRequest +from django.views import View +from django.views.generic.base import TemplateResponseMixin + +from quiz.models import Season + + +class SeasonView(View, TemplateResponseMixin): + template_name = "backoffice/season.html" + + def get(self, request: HttpRequest, season: Season, *args, **kwargs): + return self.render_to_response({"season": season}) diff --git a/tvdt/elimination/migrations/__init__.py b/tvdt/elimination/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tvdt/elimination/templates/elimination/home.html b/tvdt/elimination/templates/elimination/home.html deleted file mode 100644 index cf701d6..0000000 --- a/tvdt/elimination/templates/elimination/home.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends 'base.html' %} - -{% block nav %} - -{% endblock %} \ No newline at end of file diff --git a/tvdt/elimination/urls.py b/tvdt/elimination/urls.py deleted file mode 100644 index f38c331..0000000 --- a/tvdt/elimination/urls.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.urls import path, register_converter - -from tvdt.converters import SeasonCodeConverter - -from .views import EliminationHomeView - -register_converter(SeasonCodeConverter, "season") -urlpatterns = [ - path("", EliminationHomeView.as_view()), - path("", EliminationHomeView.as_view()), -] diff --git a/tvdt/elimination/views/__init__.py b/tvdt/elimination/views/__init__.py deleted file mode 100644 index 6e26cad..0000000 --- a/tvdt/elimination/views/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .home import EliminationHomeView diff --git a/tvdt/elimination/views/home.py b/tvdt/elimination/views/home.py deleted file mode 100644 index 9fba12a..0000000 --- a/tvdt/elimination/views/home.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.views.generic import TemplateView - - -class EliminationHomeView(TemplateView): - template_name = "elimination/home.html" diff --git a/tvdt/locale/nl/LC_MESSAGES/django.po b/tvdt/locale/nl/LC_MESSAGES/django.po index 4fb05aa..b06f02e 100644 --- a/tvdt/locale/nl/LC_MESSAGES/django.po +++ b/tvdt/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-24 12:18+0100\n" +"POT-Creation-Date: 2024-12-11 23:22+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,10 +18,105 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: tvdt/settings.py:147 +#: backoffice/templates/backoffice/base.html:18 +msgid "Tijd voor de test" +msgstr "Tijd voor de test" + +#: backoffice/templates/backoffice/index.html:4 +msgid "Your Seasons" +msgstr "Jouw seizoenen" + +#: backoffice/templates/backoffice/index.html:8 +msgid "Name" +msgstr "Naam" + +#: backoffice/templates/backoffice/index.html:9 +msgid "Active Quiz" +msgstr "Actieve test" + +#: backoffice/templates/backoffice/index.html:10 +msgid "Season Code" +msgstr "Seizoenscode" + +#: backoffice/templates/backoffice/index.html:11 +msgid "Preregister?" +msgstr "Voorregistreren?" + +#: backoffice/templates/backoffice/index.html:12 +#: backoffice/templates/backoffice/index.html:37 +msgid "Manage" +msgstr "Beheer" + +#: backoffice/templates/backoffice/index.html:23 +msgid "No active quiz" +msgstr "Geen actieve test" + +#: backoffice/templates/backoffice/quiz.html:5 +msgid "Quiz" +msgstr "Test" + +#: backoffice/templates/backoffice/quiz.html:9 +msgid "Questions" +msgstr "Vragen" + +#: backoffice/templates/backoffice/quiz.html:36 +msgid "There are no answers for this question" +msgstr "Er zijn geen antwoorden voor deze vraag" + +#: backoffice/templates/backoffice/quiz.html:48 +#: backoffice/templates/backoffice/quiz.html:66 +msgid "Score" +msgstr "Score" + +#: backoffice/templates/backoffice/quiz.html:52 +msgid "Start Elimination" +msgstr "Start eliminatie" + +#: backoffice/templates/backoffice/quiz.html:55 +msgid "Prepare Custom Elimination" +msgstr "Aangepaste eliminatie voorbereiden" + +#: backoffice/templates/backoffice/quiz.html:56 +msgid "Load Prepared Elimination" +msgstr "Aangepaste eliminatie inladen" + +#: backoffice/templates/backoffice/quiz.html:59 +msgid "Number of dropouts:" +msgstr "Aantal afvallers:" + +#: backoffice/templates/backoffice/quiz.html:63 +msgid "Candidate" +msgstr "Kandidaat" + +#: backoffice/templates/backoffice/quiz.html:64 +msgid "Correct Answers" +msgstr "Goede antwoorden" + +#: backoffice/templates/backoffice/quiz.html:65 +msgid "Corrections" +msgstr "Jokers" + +#: backoffice/templates/backoffice/quiz.html:67 +msgid "Time" +msgstr "Tijd" + +#: backoffice/templates/backoffice/season.html:5 +msgid "Season" +msgstr "Seizoen" + +#: backoffice/templates/backoffice/season.html:9 + +msgid "Quizzes" +msgstr "Tests" + +#: backoffice/templates/backoffice/season.html:21 +msgid "Candidates" +msgstr "Kandidaten" + +#: tvdt/settings.py:173 msgid "Dutch" msgstr "Nederlands" -#: tvdt/settings.py:147 +#: tvdt/settings.py:173 msgid "English" msgstr "Engels" diff --git a/tvdt/poetry.lock b/tvdt/poetry.lock index 544ae03..dbce903 100644 --- a/tvdt/poetry.lock +++ b/tvdt/poetry.lock @@ -356,6 +356,21 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "cssbeautifier" +version = "1.15.1" +description = "CSS unobfuscator and beautifier." +optional = false +python-versions = "*" +files = [ + {file = "cssbeautifier-1.15.1.tar.gz", hash = "sha256:9f7064362aedd559c55eeecf6b6bed65e05f33488dcbe39044f0403c26e1c006"}, +] + +[package.dependencies] +editorconfig = ">=0.12.2" +jsbeautifier = "*" +six = ">=1.13.0" + [[package]] name = "dj-database-url" version = "2.3.0" @@ -490,6 +505,58 @@ files = [ django = "*" typing-extensions = "*" +[[package]] +name = "djlint" +version = "1.36.3" +description = "HTML Template Linter and Formatter" +optional = false +python-versions = ">=3.9" +files = [ + {file = "djlint-1.36.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ae7c620b58e16d6bf003bd7de3f71376a7a3daa79dc02e77f3726d5a75243f2"}, + {file = "djlint-1.36.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e155ce0970d4a28d0a2e9f2e106733a2ad05910eee90e056b056d48049e4a97b"}, + {file = "djlint-1.36.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e8bb0406e60cc696806aa6226df137618f3889c72f2dbdfa76c908c99151579"}, + {file = "djlint-1.36.3-cp310-cp310-win_amd64.whl", hash = "sha256:76d32faf988ad58ef2e7a11d04046fc984b98391761bf1b61f9a6044da53d414"}, + {file = "djlint-1.36.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:32f7a5834000fff22e94d1d35f95aaf2e06f2af2cae18af0ed2a4e215d60e730"}, + {file = "djlint-1.36.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3eb1b9c0be499e63e8822a051e7e55f188ff1ab8172a85d338a8ae21c872060e"}, + {file = "djlint-1.36.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c2e0dd1f26eb472b8c84eb70d6482877b6497a1fd031d7534864088f016d5ea"}, + {file = "djlint-1.36.3-cp311-cp311-win_amd64.whl", hash = "sha256:a06b531ab9d049c46ad4d2365d1857004a1a9dd0c23c8eae94aa0d233c6ec00d"}, + {file = "djlint-1.36.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e66361a865e5e5a4bbcb40f56af7f256fd02cbf9d48b763a40172749cc294084"}, + {file = "djlint-1.36.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:36e102b80d83e9ac2e6be9a9ded32fb925945f6dbc7a7156e4415de1b0aa0dba"}, + {file = "djlint-1.36.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ac4b7370d80bd82281e57a470de8923ac494ffb571b89d8787cef57c738c69a"}, + {file = "djlint-1.36.3-cp312-cp312-win_amd64.whl", hash = "sha256:107cc56bbef13d60cc0ae774a4d52881bf98e37c02412e573827a3e549217e3a"}, + {file = "djlint-1.36.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2a9f51971d6e63c41ea9b3831c928e1f21ae6fe57e87a3452cfe672d10232433"}, + {file = "djlint-1.36.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:080c98714b55d8f0fef5c42beaee8247ebb2e3d46b0936473bd6c47808bb6302"}, + {file = "djlint-1.36.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f65a80e0b5cb13d357ea51ca6570b34c2d9d18974c1e57142de760ea27d49ed0"}, + {file = "djlint-1.36.3-cp313-cp313-win_amd64.whl", hash = "sha256:95ef6b67ef7f2b90d9434bba37d572031079001dc8524add85c00ef0386bda1e"}, + {file = "djlint-1.36.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e2317a32094d525bc41cd11c8dc064bf38d1b442c99cc3f7c4a2616b5e6ce6e"}, + {file = "djlint-1.36.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e82266c28793cd15f97b93535d72bfbc77306eaaf6b210dd90910383a814ee6c"}, + {file = "djlint-1.36.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01b2101c2d1b079e8d545e6d9d03487fcca14d2371e44cbfdedee15b0bf4567c"}, + {file = "djlint-1.36.3-cp39-cp39-win_amd64.whl", hash = "sha256:15cde63ef28beb5194ff4137883025f125676ece1b574b64a3e1c6daed734639"}, + {file = "djlint-1.36.3-py3-none-any.whl", hash = "sha256:0c05cd5b76785de2c41a2420c06ffd112800bfc0f9c0f399cc7cea7c42557f4c"}, + {file = "djlint-1.36.3.tar.gz", hash = "sha256:d85735da34bc7ac93ad8ef9b4822cc2a23d5f0ce33f25438737b8dca1d404f78"}, +] + +[package.dependencies] +click = ">=8.0.1" +colorama = ">=0.4.4" +cssbeautifier = ">=1.14.4" +jsbeautifier = ">=1.14.4" +json5 = ">=0.9.11" +pathspec = ">=0.12" +pyyaml = ">=6" +regex = ">=2023" +tqdm = ">=4.62.2" + +[[package]] +name = "editorconfig" +version = "0.12.4" +description = "EditorConfig File Locator and Interpreter for Python" +optional = false +python-versions = "*" +files = [ + {file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"}, +] + [[package]] name = "environs" version = "11.2.1" @@ -534,17 +601,6 @@ setproctitle = ["setproctitle"] testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - [[package]] name = "idna" version = "3.10" @@ -573,6 +629,34 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[[package]] +name = "jsbeautifier" +version = "1.15.1" +description = "JavaScript unobfuscator and beautifier." +optional = false +python-versions = "*" +files = [ + {file = "jsbeautifier-1.15.1.tar.gz", hash = "sha256:ebd733b560704c602d744eafc839db60a1ee9326e30a2a80c4adb8718adc1b24"}, +] + +[package.dependencies] +editorconfig = ">=0.12.2" +six = ">=1.13.0" + +[[package]] +name = "json5" +version = "0.10.0" +description = "A Python implementation of the JSON5 data format." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, +] + +[package.extras] +dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] + [[package]] name = "marshmallow" version = "3.23.1" @@ -772,6 +856,171 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "regex" +version = "2024.11.6" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -811,6 +1060,17 @@ requests = ">=2.0.0" [package.extras] rsa = ["oauthlib[signedtoken] (>=3.0.0)"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sqlparse" version = "0.5.2" @@ -826,6 +1086,27 @@ files = [ dev = ["build", "hatch"] doc = ["sphinx"] +[[package]] +name = "tqdm" +version = "4.67.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "types-pyyaml" version = "6.0.12.20240917" @@ -876,40 +1157,7 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] -[[package]] -name = "uvicorn" -version = "0.32.1" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e"}, - {file = "uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvicorn-worker" -version = "0.2.0" -description = "Uvicorn worker for Gunicorn! ✨" -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn_worker-0.2.0-py3-none-any.whl", hash = "sha256:65dcef25ab80a62e0919640f9582216ee05b3bb1dc2f0e58b354ca0511c398fb"}, - {file = "uvicorn_worker-0.2.0.tar.gz", hash = "sha256:f6894544391796be6eeed37d48cae9d7739e5a105f7e37061eccef2eac5a0295"}, -] - -[package.dependencies] -gunicorn = ">=20.1.0" -uvicorn = ">=0.14.0" - [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "a469b765e346d3759fd0995215051afaa141e0ddf4fb967e88df5e87dabd56d2" +content-hash = "b6a1e4f297c97ec3cfa8b4e89d67034e06f3f1a67a72006f1b438770781d43d2" diff --git a/tvdt/pyproject.toml b/tvdt/pyproject.toml index 483a244..5e7c24d 100644 --- a/tvdt/pyproject.toml +++ b/tvdt/pyproject.toml @@ -16,9 +16,9 @@ psycopg2 = "^2.9.10" mypy = "^1.11.0" black = "^24.10.0" isort = "^5.13.2" +djlint = "^1.36.3" [tool.poetry.group.prod.dependencies] -uvicorn-worker = "^0.2.0" gunicorn = "^23.0.0" [tool.isort] @@ -29,3 +29,6 @@ plugins = ["mypy_django_plugin.main"] [tool.django-stubs] django_settings_module = "tvdt.settings" + +[tool.djlint] +profile="django" diff --git a/tvdt/quiz/admin.py b/tvdt/quiz/admin.py index b3ec7f3..2057a5d 100644 --- a/tvdt/quiz/admin.py +++ b/tvdt/quiz/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import Answer, Candidate, GivenAnswer, Question, Quiz, Season +from .models import Answer, Candidate, Correction, GivenAnswer, Question, Quiz, Season class CandidatesAdmin(admin.StackedInline): @@ -30,6 +30,8 @@ class AnswerInline(admin.TabularInline): @admin.register(Question) class QuestionAdmin(admin.ModelAdmin): + list_display = ["question", "quiz__season__name", "quiz__name", "_order"] + ordering = ["quiz__season", "quiz", "_order"] inlines = [AnswerInline] @@ -41,3 +43,8 @@ class CandidateAdmin(admin.ModelAdmin): @admin.register(GivenAnswer) class GivenAnswerAdmin(admin.ModelAdmin): pass + + +@admin.register(Correction) +class CorrextionAdmin(admin.ModelAdmin): + pass diff --git a/tvdt/quiz/context_processors.py b/tvdt/quiz/context_processors.py new file mode 100644 index 0000000..6c84987 --- /dev/null +++ b/tvdt/quiz/context_processors.py @@ -0,0 +1,2 @@ +def get_theme(request) -> dict: + return {"theme": "wie_is_de_mol"} diff --git a/tvdt/quiz/converters.py b/tvdt/quiz/converters.py index 00e213c..ef47a91 100644 --- a/tvdt/quiz/converters.py +++ b/tvdt/quiz/converters.py @@ -16,7 +16,7 @@ class CandidateConverter: raise ValueError try: - season = Season.objects.aget(season_code=season_code) + season = Season.objects.get(season_code=season_code) candidate = Candidate.objects.get(name=name, season=season) return candidate diff --git a/tvdt/quiz/fixtures/krtek.json b/tvdt/quiz/fixtures/krtek.json index 8419a50..4360c3b 100644 --- a/tvdt/quiz/fixtures/krtek.json +++ b/tvdt/quiz/fixtures/krtek.json @@ -890,6 +890,864 @@ "candidates": [] } }, +{ + "model": "quiz.answer", + "pk": 83, + "fields": { + "text": "Man", + "question": 16, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 84, + "fields": { + "text": "Vrouw", + "question": 16, + "is_right_answer": true, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 85, + "fields": { + "text": "Nee", + "question": 17, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 86, + "fields": { + "text": "De Krtek is vegetariër", + "question": 17, + "is_right_answer": true, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 87, + "fields": { + "text": "De Krtek is flexitariër", + "question": 17, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 88, + "fields": { + "text": "De Krtek heeft een allergie", + "question": 17, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 89, + "fields": { + "text": "De Krtek heeft een intolerantie", + "question": 17, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 90, + "fields": { + "text": "De Krtek eet geen rundvlees", + "question": 17, + "is_right_answer": false, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 91, + "fields": { + "text": "De Krtek eet geen waterdieren", + "question": 17, + "is_right_answer": false, + "_order": 6, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 92, + "fields": { + "text": "Amy, Karel en Floyd", + "question": 18, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 93, + "fields": { + "text": "Flip en Majoor", + "question": 18, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 94, + "fields": { + "text": "Benji", + "question": 18, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 95, + "fields": { + "text": "Sini", + "question": 18, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 96, + "fields": { + "text": "De huisdieren van de Krtek hebben geen naam", + "question": 18, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 97, + "fields": { + "text": "De Krtek heeft geen huisdieren", + "question": 18, + "is_right_answer": true, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 98, + "fields": { + "text": "Koffie", + "question": 19, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 99, + "fields": { + "text": "Thee", + "question": 19, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 100, + "fields": { + "text": "Water", + "question": 19, + "is_right_answer": true, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 101, + "fields": { + "text": "Melk", + "question": 19, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 102, + "fields": { + "text": "Sap", + "question": 19, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 103, + "fields": { + "text": "Niks", + "question": 19, + "is_right_answer": false, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 104, + "fields": { + "text": "Denemarken", + "question": 20, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 105, + "fields": { + "text": "Drenthe", + "question": 20, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 106, + "fields": { + "text": "Mallorca", + "question": 20, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 107, + "fields": { + "text": "Marokko", + "question": 20, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 108, + "fields": { + "text": "Oostenrijk", + "question": 20, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 109, + "fields": { + "text": "Turkije", + "question": 20, + "is_right_answer": false, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 110, + "fields": { + "text": "Zweden", + "question": 20, + "is_right_answer": true, + "_order": 6, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 111, + "fields": { + "text": "Het eerste groepje", + "question": 21, + "is_right_answer": true, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 112, + "fields": { + "text": "Het tweede groepje", + "question": 21, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 113, + "fields": { + "text": "Het derde groepje", + "question": 21, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 114, + "fields": { + "text": "Het vierde groepje", + "question": 21, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 115, + "fields": { + "text": "Het vijfde groepje", + "question": 21, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 116, + "fields": { + "text": "Nee", + "question": 22, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 117, + "fields": { + "text": "Het universum", + "question": 22, + "is_right_answer": true, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 118, + "fields": { + "text": "Toeval", + "question": 22, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 119, + "fields": { + "text": "De Krtek is hindoeïstisch", + "question": 22, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 120, + "fields": { + "text": "Ja", + "question": 23, + "is_right_answer": true, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 121, + "fields": { + "text": "Nee", + "question": 23, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 122, + "fields": { + "text": "Tussen 0:00 en 0:59 uur", + "question": 24, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 123, + "fields": { + "text": "Tussen 1:00 en 1:59 uur", + "question": 24, + "is_right_answer": true, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 124, + "fields": { + "text": "Tussen 2:00 en 2:59 uur", + "question": 24, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 125, + "fields": { + "text": "Na 3:00", + "question": 24, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 126, + "fields": { + "text": "1", + "question": 25, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 127, + "fields": { + "text": "2", + "question": 25, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 128, + "fields": { + "text": "3", + "question": 25, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 129, + "fields": { + "text": "Geen", + "question": 25, + "is_right_answer": true, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 130, + "fields": { + "text": "Digimon", + "question": 26, + "is_right_answer": true, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 131, + "fields": { + "text": "Floris", + "question": 26, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 132, + "fields": { + "text": "Het huis Anubis", + "question": 26, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 133, + "fields": { + "text": "Sesamstraat", + "question": 26, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 134, + "fields": { + "text": "Spongebob Squarepants", + "question": 26, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 135, + "fields": { + "text": "Teletubbies", + "question": 26, + "is_right_answer": false, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 136, + "fields": { + "text": "In koffer(s)", + "question": 27, + "is_right_answer": true, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 137, + "fields": { + "text": "In losse tas(sen)", + "question": 27, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 138, + "fields": { + "text": "In een rugzak", + "question": 27, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 139, + "fields": { + "text": "Een vork die door een metalen pan krast", + "question": 28, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 140, + "fields": { + "text": "Smakkende mensen", + "question": 28, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 141, + "fields": { + "text": "Een vork die over een bord schraapt", + "question": 28, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 142, + "fields": { + "text": "Schuren met schuurpapier", + "question": 28, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 143, + "fields": { + "text": "Nagels op een krijtbord", + "question": 28, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 144, + "fields": { + "text": "Servies dat tegen elkaar klettert", + "question": 28, + "is_right_answer": false, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 145, + "fields": { + "text": "Het geroekoe van een duif", + "question": 28, + "is_right_answer": true, + "_order": 6, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 146, + "fields": { + "text": "Piepschuim", + "question": 28, + "is_right_answer": false, + "_order": 7, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 147, + "fields": { + "text": "Ja", + "question": 29, + "is_right_answer": false, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 148, + "fields": { + "text": "Nee", + "question": 29, + "is_right_answer": true, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 149, + "fields": { + "text": "Claudia", + "question": 30, + "is_right_answer": true, + "_order": 0, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 150, + "fields": { + "text": "Eelco", + "question": 30, + "is_right_answer": false, + "_order": 1, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 151, + "fields": { + "text": "Elise", + "question": 30, + "is_right_answer": false, + "_order": 2, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 152, + "fields": { + "text": "Gert-Jan", + "question": 30, + "is_right_answer": false, + "_order": 3, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 153, + "fields": { + "text": "Iris", + "question": 30, + "is_right_answer": false, + "_order": 4, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 154, + "fields": { + "text": "Jari", + "question": 30, + "is_right_answer": false, + "_order": 5, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 155, + "fields": { + "text": "Lara", + "question": 30, + "is_right_answer": false, + "_order": 6, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 156, + "fields": { + "text": "Lotte", + "question": 30, + "is_right_answer": false, + "_order": 7, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 157, + "fields": { + "text": "Myrthe", + "question": 30, + "is_right_answer": false, + "_order": 8, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 158, + "fields": { + "text": "Remy", + "question": 30, + "is_right_answer": false, + "_order": 9, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 159, + "fields": { + "text": "Robbert", + "question": 30, + "is_right_answer": false, + "_order": 10, + "candidates": [] + } +}, +{ + "model": "quiz.answer", + "pk": 160, + "fields": { + "text": "Tom", + "question": 30, + "is_right_answer": false, + "_order": 11, + "candidates": [] + } +}, { "model": "quiz.question", "pk": 1, @@ -1040,6 +1898,156 @@ "_order": 14 } }, +{ + "model": "quiz.question", + "pk": 16, + "fields": { + "question": "Is de Krtek een man of een vrouw?", + "quiz": 2, + "enabled": true, + "_order": 0 + } +}, +{ + "model": "quiz.question", + "pk": 17, + "fields": { + "question": "Heeft de Krtek dieetwensen of allergieën?", + "quiz": 2, + "enabled": true, + "_order": 1 + } +}, +{ + "model": "quiz.question", + "pk": 18, + "fields": { + "question": "Hoe heet het huisdier/de huisdieren van de Krtek?", + "quiz": 2, + "enabled": true, + "_order": 2 + } +}, +{ + "model": "quiz.question", + "pk": 19, + "fields": { + "question": "Wat dronk de Krtek deze ochtend bij het ontbijt?", + "quiz": 2, + "enabled": true, + "_order": 3 + } +}, +{ + "model": "quiz.question", + "pk": 20, + "fields": { + "question": "Waar ging de eerste vakantie die de Krtek zich nog herinnert heen?", + "quiz": 2, + "enabled": true, + "_order": 4 + } +}, +{ + "model": "quiz.question", + "pk": 21, + "fields": { + "question": "Met welk groepje ging de Krtek als eerste het Douanespel in?", + "quiz": 2, + "enabled": true, + "_order": 5 + } +}, +{ + "model": "quiz.question", + "pk": 22, + "fields": { + "question": "Gelooft de Krtek ergens in?", + "quiz": 2, + "enabled": true, + "_order": 6 + } +}, +{ + "model": "quiz.question", + "pk": 23, + "fields": { + "question": "At de Krtek op vrijdagavond heksenkaas tijdens het diner?", + "quiz": 2, + "enabled": true, + "_order": 7 + } +}, +{ + "model": "quiz.question", + "pk": 24, + "fields": { + "question": "Hoe laat ging de Krtek gisteravond naar bed?", + "quiz": 2, + "enabled": true, + "_order": 8 + } +}, +{ + "model": "quiz.question", + "pk": 25, + "fields": { + "question": "Hoeveel batterijen heeft de krtek naar het bord gebracht bij het douanespel?", + "quiz": 2, + "enabled": true, + "_order": 9 + } +}, +{ + "model": "quiz.question", + "pk": 26, + "fields": { + "question": "Wat keek de Krtek als kind graag op TV?", + "quiz": 2, + "enabled": true, + "_order": 10 + } +}, +{ + "model": "quiz.question", + "pk": 27, + "fields": { + "question": "Waarin zat op de heenreis de bagage van de Krtek (voornamelijk)?", + "quiz": 2, + "enabled": true, + "_order": 11 + } +}, +{ + "model": "quiz.question", + "pk": 28, + "fields": { + "question": "Van welk geluid gaan de haren van de Krtek overeind staan?", + "quiz": 2, + "enabled": true, + "_order": 12 + } +}, +{ + "model": "quiz.question", + "pk": 29, + "fields": { + "question": "Wilde de Krtek penningmeester worden?", + "quiz": 2, + "enabled": true, + "_order": 13 + } +}, +{ + "model": "quiz.question", + "pk": 30, + "fields": { + "question": "Wie is de Krtek?", + "quiz": 2, + "enabled": true, + "_order": 14 + } +}, { "model": "quiz.candidate", "pk": 1, @@ -1168,6 +2176,14 @@ "season": 1 } }, +{ + "model": "quiz.quiz", + "pk": 2, + "fields": { + "name": "Test 2", + "season": 1 + } +}, { "model": "quiz.season", "pk": 1, @@ -1175,7 +2191,8 @@ "name": "Krtek", "active_quiz": 1, "season_code": "12345", - "preregister_candidates": true + "preregister_candidates": true, + "owner": [] } } ] diff --git a/tvdt/quiz/locale/nl/LC_MESSAGES/django.po b/tvdt/quiz/locale/nl/LC_MESSAGES/django.po index 20884a6..5f1c4f1 100644 --- a/tvdt/quiz/locale/nl/LC_MESSAGES/django.po +++ b/tvdt/quiz/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-11-25 19:18+0100\n" +"POT-Creation-Date: 2024-12-11 23:22+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -19,36 +19,36 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: quiz/apps.py:8 quiz/models/correction.py:17 quiz/models/given_answer.py:19 -#: quiz/models/question.py:20 quiz/models/quiz.py:24 +#: quiz/models/question.py:23 quiz/models/quiz.py:60 msgid "quiz" msgstr "test" -#: quiz/models/answer.py:7 +#: quiz/models/answer.py:10 msgid "text" msgstr "tekst" -#: quiz/models/answer.py:12 quiz/models/question.py:15 -#: quiz/models/question.py:28 +#: quiz/models/answer.py:15 quiz/models/question.py:18 +#: quiz/models/question.py:58 msgid "question" msgstr "vraag" -#: quiz/models/answer.py:14 +#: quiz/models/answer.py:17 msgid "is right answer" msgstr "is goede antwoord" -#: quiz/models/answer.py:16 quiz/models/candidate.py:50 +#: quiz/models/answer.py:19 quiz/models/candidate.py:50 msgid "candidates" msgstr "kandidaten" -#: quiz/models/answer.py:20 quiz/models/given_answer.py:25 +#: quiz/models/answer.py:23 quiz/models/given_answer.py:25 msgid "answer" msgstr "antwoord" -#: quiz/models/answer.py:21 +#: quiz/models/answer.py:24 msgid "answers" msgstr "antwoorden" -#: quiz/models/candidate.py:18 quiz/models/quiz.py:7 quiz/models/season.py:12 +#: quiz/models/candidate.py:18 quiz/models/quiz.py:12 quiz/models/season.py:12 msgid "name" msgstr "naam" @@ -57,11 +57,15 @@ msgstr "naam" msgid "candidate" msgstr "kandidaat" -#: quiz/models/correction.py:22 +#: quiz/models/correction.py:19 +msgid "amount" +msgstr "aantal" + +#: quiz/models/correction.py:23 msgid "correction" msgstr "joker" -#: quiz/models/correction.py:23 +#: quiz/models/correction.py:24 msgid "corrections" msgstr "jokers" @@ -73,19 +77,35 @@ msgstr "gegeven antwoord" msgid "given answers" msgstr "gegeven antwoorden" -#: quiz/models/question.py:22 +#: quiz/models/question.py:25 msgid "enabled" msgstr "actief" -#: quiz/models/question.py:29 +#: quiz/models/question.py:42 +msgid "Error: Question has no answers" +msgstr "Fout: Raar genoeg heeft deze vraag geen antwoorden..." + +#: quiz/models/question.py:47 +msgid "Error: This question has no right answer!" +msgstr "Fout: Raar genoeg heeft deze vraag geen antwoorden..." + +#: quiz/models/question.py:50 +msgid "Warning: This question has multiple correct answers" +msgstr "Waarschuwing: Raar genoeg heeft deze vraag geen antwoorden..." + +#: quiz/models/question.py:59 msgid "questions" msgstr "vraag" -#: quiz/models/quiz.py:12 quiz/models/season.py:38 +#: quiz/models/quiz.py:17 quiz/models/season.py:43 msgid "season" msgstr "seizoen" -#: quiz/models/quiz.py:25 +#: quiz/models/quiz.py:21 +msgid "dropouts" +msgstr "afvallers" + +#: quiz/models/quiz.py:61 msgid "quizzes" msgstr "tests" @@ -101,27 +121,31 @@ msgstr "seizoencode" msgid "preregister candidates" msgstr "kandidaten voorregistreren" -#: quiz/models/season.py:39 +#: quiz/models/season.py:30 +msgid "owners" +msgstr "eigenaren" + +#: quiz/models/season.py:44 msgid "seasons" msgstr "seizoenen" -#: quiz/templates/quiz/question.html:11 -msgid "Weirdly enough this question has no answers..." -msgstr "Raar genoeg heeft deze vraag geen antwoorden..." - -#: quiz/templates/quiz/select_season.html:4 +#: quiz/templates/quiz/base.html:16 msgid "Tijd voor de test" msgstr "Tijd voor de test" -#: quiz/views/enternameview.py:14 +#: quiz/templates/quiz/question.html:15 +msgid "Weirdly enough this question has no answers..." +msgstr "Raar genoeg heeft deze vraag geen antwoorden..." + +#: quiz/views/enternameview.py:15 msgid "Name" msgstr "Naam" -#: quiz/views/enternameview.py:27 +#: quiz/views/enternameview.py:28 msgid "This season has no active quiz." msgstr "Dit seizoen heeft geen actieve test." -#: quiz/views/enternameview.py:39 +#: quiz/views/enternameview.py:40 msgid "Candidate does not exist" msgstr "Kandidaat bestaat niet" diff --git a/tvdt/quiz/migrations/0002_season_owner.py b/tvdt/quiz/migrations/0002_season_owner.py new file mode 100644 index 0000000..46f11c8 --- /dev/null +++ b/tvdt/quiz/migrations/0002_season_owner.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.3 on 2024-11-30 18:21 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("quiz", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="season", + name="owner", + field=models.ManyToManyField( + related_name="seasons", + to=settings.AUTH_USER_MODEL, + verbose_name="owners", + ), + ), + ] diff --git a/tvdt/quiz/migrations/0003_correction_amount_alter_correction_candidate_and_more.py b/tvdt/quiz/migrations/0003_correction_amount_alter_correction_candidate_and_more.py new file mode 100644 index 0000000..56240a2 --- /dev/null +++ b/tvdt/quiz/migrations/0003_correction_amount_alter_correction_candidate_and_more.py @@ -0,0 +1,39 @@ +# Generated by Django 5.1.3 on 2024-12-01 14:23 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("quiz", "0002_season_owner"), + ] + + operations = [ + migrations.AddField( + model_name="correction", + name="amount", + field=models.FloatField(default=1, verbose_name="amount"), + ), + migrations.AlterField( + model_name="correction", + name="candidate", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="corrections", + to="quiz.candidate", + verbose_name="candidate", + ), + ), + migrations.AlterField( + model_name="correction", + name="quiz", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="corrections", + to="quiz.quiz", + verbose_name="quiz", + ), + ), + ] diff --git a/tvdt/quiz/migrations/0004_quiz_dropouts.py b/tvdt/quiz/migrations/0004_quiz_dropouts.py new file mode 100644 index 0000000..1503e30 --- /dev/null +++ b/tvdt/quiz/migrations/0004_quiz_dropouts.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2024-12-01 16:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("quiz", "0003_correction_amount_alter_correction_candidate_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="quiz", + name="dropouts", + field=models.PositiveSmallIntegerField(default=1, verbose_name="dropouts"), + ), + ] diff --git a/tvdt/quiz/models/answer.py b/tvdt/quiz/models/answer.py index f92af4f..5cb12a0 100644 --- a/tvdt/quiz/models/answer.py +++ b/tvdt/quiz/models/answer.py @@ -1,8 +1,11 @@ +from typing import final + from django.db import models from django.utils.translation import gettext_lazy as _ from django_stubs_ext.db.models import TypedModelMeta +@final class Answer(models.Model): text = models.CharField(max_length=64, verbose_name=_("text")) question = models.ForeignKey( diff --git a/tvdt/quiz/models/correction.py b/tvdt/quiz/models/correction.py index 3575c1d..eed4a57 100644 --- a/tvdt/quiz/models/correction.py +++ b/tvdt/quiz/models/correction.py @@ -7,15 +7,16 @@ class Correction(models.Model): candidate = models.ForeignKey( "Candidate", on_delete=models.CASCADE, - related_name="corrections_used", + related_name="corrections", verbose_name=_("candidate"), ) quiz = models.ForeignKey( "Quiz", on_delete=models.CASCADE, - related_name="corrections_used", + related_name="corrections", verbose_name=_("quiz"), ) + amount = models.FloatField(verbose_name=_("amount"), default=1) class Meta(TypedModelMeta): unique_together = ("candidate", "quiz") diff --git a/tvdt/quiz/models/question.py b/tvdt/quiz/models/question.py index 87e55e0..8b961d3 100644 --- a/tvdt/quiz/models/question.py +++ b/tvdt/quiz/models/question.py @@ -1,7 +1,10 @@ from django.db import models +from django.db.models import QuerySet from django.utils.translation import gettext_lazy as _ from django_stubs_ext.db.models import TypedModelMeta +from quiz.models import Answer + class NoActiveTestForSeason(Exception): pass @@ -21,6 +24,33 @@ class Question(models.Model): ) enabled = models.BooleanField(default=True, verbose_name=_("enabled")) + @property + def order(self): + return self._order + + @property + def right_answer(self) -> QuerySet[Answer]: + return self.answers.filter(is_right_answer=True) + + @property + def has_right_answer(self) -> bool: + return self.answers.filter(is_right_answer=True).count() > 0 + + @property + def errors(self) -> str | None: + if self.answers.count() == 0: + return _("Error: Question has no answers") + + n_correct_answers = self.answers.filter(is_right_answer=True).count() + + if n_correct_answers == 0: + return _("Error: This question has no right answer!") + + if n_correct_answers > 1: + return _("Warning: This question has multiple correct answers") + + return None + def __str__(self) -> str: return f"{self._order + 1}. {self.question} ({self.quiz}) ({self.answers.count()} answers, {self.answers.filter(is_right_answer=True).count()} correct)" diff --git a/tvdt/quiz/models/quiz.py b/tvdt/quiz/models/quiz.py index 7812fe8..ded9c1e 100644 --- a/tvdt/quiz/models/quiz.py +++ b/tvdt/quiz/models/quiz.py @@ -1,7 +1,12 @@ from django.db import models +from django.db.models import F, OuterRef, Subquery +from django.db.models.aggregates import Count, Max, Min +from django.db.models.functions import Coalesce from django.utils.translation import gettext_lazy as _ from django_stubs_ext.db.models import TypedModelMeta +from quiz.models import Candidate, Correction + class Quiz(models.Model): name = models.CharField(max_length=64, verbose_name=_("name")) @@ -12,11 +17,42 @@ class Quiz(models.Model): verbose_name=_("season"), ) + dropouts = models.PositiveSmallIntegerField( + verbose_name=_("dropouts"), + default=1, + ) + def is_valid_quiz(self) -> bool: return True # Check > 0 active questions # Check every question 1 right answer + def get_score(self): + time_query = ( + Candidate.objects.filter(id=OuterRef("id"), answers__quiz=self) + .annotate(time=Max("answers__created") - Min("answers__created")) + .values("time") + ) + corrections = Correction.objects.filter( + quiz=self, candidate=OuterRef("id") + ).values("amount") + + scores = ( + Candidate.objects.filter( + answers__answer__is_right_answer=True, + answers__quiz=self, + ) + .values("id", "name") + .annotate( + correct=Count("answers"), + corrections=Coalesce(Subquery(corrections), 0.0), + score=F("correct") + F("corrections"), + time=Subquery(time_query), + ) + .order_by("-score", "time") + ) + return scores + def __str__(self) -> str: return f"{self.season.name} - {self.name}" diff --git a/tvdt/quiz/models/season.py b/tvdt/quiz/models/season.py index 96710cb..1141d86 100644 --- a/tvdt/quiz/models/season.py +++ b/tvdt/quiz/models/season.py @@ -1,12 +1,12 @@ -import random -import string - +from django.contrib.auth import get_user_model from django.db import models from django.utils.translation import gettext_lazy as _ from django_stubs_ext.db.models import TypedModelMeta from ..helpers import generate_season_code +User = get_user_model() + class Season(models.Model): name = models.CharField(max_length=64, verbose_name=_("name")) @@ -25,6 +25,11 @@ class Season(models.Model): preregister_candidates = models.BooleanField( default=True, verbose_name=_("preregister candidates") ) + owner = models.ManyToManyField( + User, + verbose_name=_("owners"), + related_name="seasons", + ) def renew_season_code(self) -> str: self.season_code = generate_season_code() diff --git a/tvdt/quiz/static/quiz/de_mol/background.png b/tvdt/quiz/static/quiz/de_mol/background.png new file mode 100644 index 0000000..3042bd8 Binary files /dev/null and b/tvdt/quiz/static/quiz/de_mol/background.png differ diff --git a/tvdt/quiz/static/quiz/de_mol/green.png b/tvdt/quiz/static/quiz/de_mol/green.png new file mode 100644 index 0000000..b984a24 Binary files /dev/null and b/tvdt/quiz/static/quiz/de_mol/green.png differ diff --git a/tvdt/quiz/static/quiz/de_mol/red.png b/tvdt/quiz/static/quiz/de_mol/red.png new file mode 100644 index 0000000..ec37cfb Binary files /dev/null and b/tvdt/quiz/static/quiz/de_mol/red.png differ diff --git a/tvdt/quiz/static/quiz/background.png b/tvdt/quiz/static/quiz/wie_is_de_mol/background.png similarity index 100% rename from tvdt/quiz/static/quiz/background.png rename to tvdt/quiz/static/quiz/wie_is_de_mol/background.png diff --git a/tvdt/quiz/static/quiz/wie_is_de_mol/green.png b/tvdt/quiz/static/quiz/wie_is_de_mol/green.png new file mode 100644 index 0000000..ab216b5 Binary files /dev/null and b/tvdt/quiz/static/quiz/wie_is_de_mol/green.png differ diff --git a/tvdt/quiz/static/quiz/wie_is_de_mol/red.png b/tvdt/quiz/static/quiz/wie_is_de_mol/red.png new file mode 100644 index 0000000..32721a4 Binary files /dev/null and b/tvdt/quiz/static/quiz/wie_is_de_mol/red.png differ diff --git a/tvdt/quiz/templates/quiz/base.html b/tvdt/quiz/templates/quiz/base.html index 3e2095e..76fdb21 100644 --- a/tvdt/quiz/templates/quiz/base.html +++ b/tvdt/quiz/templates/quiz/base.html @@ -1,18 +1,27 @@ +{% load i18n %} {% load static %} - - - - - {% block title %}{% endblock %} - - - -
    -
    - {% if messages %} - {% for message in messages %} - - {% endfor %} - {% endif %} - {% block body %}{% endblock %} -
    - {% block script %}{% endblock %} -
    - + + + +
    +
    + {% include "messages.html" %} + {% block body %} + {% endblock body %} +
    + {% block script %} + {% endblock script %} +
    + diff --git a/tvdt/quiz/templates/quiz/enter_name.html b/tvdt/quiz/templates/quiz/enter_name.html index 044d8cb..70d639a 100644 --- a/tvdt/quiz/templates/quiz/enter_name.html +++ b/tvdt/quiz/templates/quiz/enter_name.html @@ -1,8 +1,5 @@ {% extends "quiz/base.html" %} {% load crispy_forms_tags %} - {% block body %} - {% crispy form %} - -{% endblock %} \ No newline at end of file +{% endblock body %} diff --git a/tvdt/quiz/templates/quiz/question.html b/tvdt/quiz/templates/quiz/question.html index 657833f..05f42ef 100644 --- a/tvdt/quiz/templates/quiz/question.html +++ b/tvdt/quiz/templates/quiz/question.html @@ -1,14 +1,18 @@ -{% extends 'quiz/base.html' %} +{% extends "quiz/base.html" %} {% load i18n %} - {% block body %}

    {{ question.question }}

    - {% csrf_token %} - {% for answer in question.answers.all %} -
    - {% empty %} - {% translate "Weirdly enough this question has no answers..." %} - {% endfor %} + {% csrf_token %} + {% for answer in question.answers.all %} +
    + +
    + {% empty %} + {% translate "Weirdly enough this question has no answers..." %} + {% endfor %}
    -{% endblock %} \ No newline at end of file +{% endblock body %} diff --git a/tvdt/quiz/templates/quiz/select_season.html b/tvdt/quiz/templates/quiz/select_season.html index 3921f5b..dc0facb 100644 --- a/tvdt/quiz/templates/quiz/select_season.html +++ b/tvdt/quiz/templates/quiz/select_season.html @@ -1,7 +1,6 @@ -{% extends 'quiz/base.html' %} +{% extends "quiz/base.html" %} {% load crispy_forms_tags %} {% load i18n %} -{% block title %}{% translate "Tijd voor de test" %}{% endblock %} {% block body %} {% crispy form %} -{% endblock %} +{% endblock body %} diff --git a/tvdt/quiz/urls.py b/tvdt/quiz/urls.py index 4aca62c..c4ef4bb 100644 --- a/tvdt/quiz/urls.py +++ b/tvdt/quiz/urls.py @@ -10,7 +10,8 @@ from .views.questionview import QuestionView register_converter(SeasonCodeConverter, "season") register_converter(CandidateConverter, "candidate") urlpatterns = [ - path("", SelectSeasonView.as_view(), name="home"), - path("/", EnterNameView.as_view(), name="quiz"), + path("", SelectSeasonView.as_view(), name="index"), + path("/", EnterNameView.as_view(), name="enter_name"), path("/", QuestionView.as_view(), name="question"), + # path("<>") ] diff --git a/tvdt/quiz/views/questionview.py b/tvdt/quiz/views/questionview.py index 195bcd8..e5a0bfd 100644 --- a/tvdt/quiz/views/questionview.py +++ b/tvdt/quiz/views/questionview.py @@ -26,7 +26,7 @@ class QuestionView(View, TemplateResponseMixin): if not kwargs.get("from_post"): messages.error(request, _("Quiz done")) - return redirect(reverse("quiz", kwargs={"season": candidate.season})) + return redirect(reverse("enter_name", kwargs={"season": candidate.season})) # TODO: On first question -> record time if ( diff --git a/tvdt/quiz/views/selectseasonview.py b/tvdt/quiz/views/selectseasonview.py index 1ac6b20..523245b 100644 --- a/tvdt/quiz/views/selectseasonview.py +++ b/tvdt/quiz/views/selectseasonview.py @@ -36,4 +36,4 @@ class SelectSeasonView(FormView): env.read_env() print(env.dump()) - return redirect(reverse("quiz", kwargs={"season": season})) + return redirect(reverse("enter_name", kwargs={"season": season})) diff --git a/tvdt/templates/base.html b/tvdt/templates/base.html deleted file mode 100644 index 95289f6..0000000 --- a/tvdt/templates/base.html +++ /dev/null @@ -1,33 +0,0 @@ -{% load static %} - - - - - - - {% block title %}{% endblock %} - - - -{% block nav %}{% endblock %} -
    -
    - {% if messages %} - {% for message in messages %} - - {% endfor %} - {% endif %} - {% block body %}{% endblock %} -
    - {% block script %}{% endblock %} -
    - - diff --git a/tvdt/templates/messages.html b/tvdt/templates/messages.html new file mode 100644 index 0000000..0162264 --- /dev/null +++ b/tvdt/templates/messages.html @@ -0,0 +1,13 @@ +{% if messages %} + {% for message in messages %} + + {% endfor %} +{% endif %} diff --git a/tvdt/tvdt/settings.py b/tvdt/tvdt/settings.py index 5430730..c6be8d7 100644 --- a/tvdt/tvdt/settings.py +++ b/tvdt/tvdt/settings.py @@ -1,7 +1,7 @@ """ Django settings for tvdt project. -Generated by 'django-admin startproject' using Django 5.1.2. +Generated by 'django-backoffice startproject' using Django 5.1.2. For more information on this file, see https://docs.djangoproject.com/en/5.1/topics/settings/ @@ -39,7 +39,7 @@ ALLOWED_HOSTS: list[str] = ["*"] INSTALLED_APPS = [ "quiz.apps.QuizConfig", - "elimination.apps.EliminationConfig", + "backoffice.apps.BackofficeConfig", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -84,14 +84,15 @@ CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" MIDDLEWARE = [ + "allauth.account.middleware.AccountMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", - "allauth.account.middleware.AccountMiddleware", ] ROOT_URLCONF = "tvdt.urls" @@ -109,13 +110,14 @@ TEMPLATES = [ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", + "quiz.context_processors.get_theme", ], }, }, ] AUTHENTICATION_BACKENDS = [ - # Needed to login by username in Django admin, regardless of `allauth` + # Needed to login by username in Django backoffice, regardless of `allauth` "django.contrib.auth.backends.ModelBackend", # `allauth` specific authentication methods, such as login by email "allauth.account.auth_backends.AuthenticationBackend", @@ -132,20 +134,24 @@ DATABASES = {"default": env.dj_db_url("DATABASE_URL")} # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] if not DEBUG else [] +AUTH_PASSWORD_VALIDATORS = ( + [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, + ] + if not DEBUG + else [] +) MESSAGE_TAGS = { messages.DEBUG: "alert-info", @@ -172,6 +178,7 @@ USE_I18N = True USE_TZ = True +LOCALE_PATHS = [BASE_DIR / "locale"] # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.1/howto/static-files/ diff --git a/tvdt/tvdt/urls.py b/tvdt/tvdt/urls.py index a1d6ef1..c917645 100644 --- a/tvdt/tvdt/urls.py +++ b/tvdt/tvdt/urls.py @@ -21,7 +21,7 @@ from django.urls import include, path urlpatterns = [ path("", include("quiz.urls")), - path("elimination", include("elimination.urls")), + path("backoffice/", include("backoffice.urls")), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("i18n/", include("django.conf.urls.i18n")),