From 6ad9b465436aef949bbe22cf5cd228ad69314b47 Mon Sep 17 00:00:00 2001 From: Marijn Doeve Date: Mon, 25 Nov 2024 19:21:44 +0100 Subject: [PATCH] progress --- .gitignore | 4 +- README.md | 1 + tvdt/locale/nl/LC_MESSAGES/django.po | 6 +- tvdt/poetry.lock | 20 +-- tvdt/pyproject.toml | 1 - tvdt/quiz/admin.py | 2 +- tvdt/quiz/converters.py | 2 +- tvdt/quiz/fixtures/krtek.json | 24 +++ tvdt/quiz/locale/nl/LC_MESSAGES/django.po | 142 ++++++++++-------- tvdt/quiz/migrations/0001_initial.py | 91 ++++------- ..._season_preregister_candidates_and_more.py | 56 ------- ...swer_created_alter_givenanswer_question.py | 31 ---- ...alter_question_unique_together_and_more.py | 29 ---- tvdt/quiz/models/__init__.py | 1 - tvdt/quiz/models/candidate.py | 10 +- tvdt/quiz/models/correction.py | 3 - tvdt/quiz/models/given_answer.py | 22 ++- tvdt/quiz/models/quiz_time.py | 15 -- tvdt/quiz/templates/quiz/base.html | 28 +++- tvdt/quiz/templates/quiz/enter_name.html | 1 - tvdt/quiz/urls.py | 2 +- tvdt/quiz/views/enternameview.py | 40 ++--- tvdt/quiz/views/questionview.py | 43 ++++-- tvdt/quiz/views/selectseasonview.py | 8 +- tvdt/tvdt/settings.py | 14 ++ tvdt/tvdt/urls.py | 2 +- 26 files changed, 260 insertions(+), 338 deletions(-) delete mode 100644 tvdt/quiz/migrations/0002_season_preregister_candidates_and_more.py delete mode 100644 tvdt/quiz/migrations/0003_givenanswer_created_alter_givenanswer_question.py delete mode 100644 tvdt/quiz/migrations/0004_alter_question_unique_together_and_more.py delete mode 100644 tvdt/quiz/models/quiz_time.py diff --git a/.gitignore b/.gitignore index 6a15543..54cab8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea/ ### Generated by gibo (https://github.com/simonwhitaker/gibo) ### https://raw.github.com/github/gitignore/76739a38b56907118c5a880d63250c99d5690a5a/Python.gitignore @@ -172,7 +173,8 @@ cython_debug/ .LSOverride # Icon must end with two \r -Icon +Icon + # Thumbnails ._* diff --git a/README.md b/README.md index 2fed854..cba7a1b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ kunnen alleen vooraf ingevoerde namen gebruikt worden in een test. - Vanaf het moment dat de kandidaat op start klikt na het intypen van hun naam gaat de tijd lopen. Deze stopt na het aanklikken van een antwoord op de laatste vraag van de test. +- Achtergrondmuziek ### Schermen kijken diff --git a/tvdt/locale/nl/LC_MESSAGES/django.po b/tvdt/locale/nl/LC_MESSAGES/django.po index 310f18b..4fb05aa 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-10-20 00:06+0200\n" +"POT-Creation-Date: 2024-11-24 12:18+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,10 +18,10 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: tvdt/settings.py:109 +#: tvdt/settings.py:147 msgid "Dutch" msgstr "Nederlands" -#: tvdt/settings.py:109 +#: tvdt/settings.py:147 msgid "English" msgstr "Engels" diff --git a/tvdt/poetry.lock b/tvdt/poetry.lock index f3abf74..7a8547e 100644 --- a/tvdt/poetry.lock +++ b/tvdt/poetry.lock @@ -101,24 +101,6 @@ django-crispy-forms = ">=2.3" [package.extras] test = ["pytest", "pytest-django"] -[[package]] -name = "crispy-tailwind" -version = "1.0.3" -description = "Tailwind CSS for Django Crispy Forms" -optional = false -python-versions = ">=3.8" -files = [ - {file = "crispy-tailwind-1.0.3.tar.gz", hash = "sha256:2bc9f616d406e4b003f25d46fcb0079f1c2522719d97adb107667271d849459a"}, - {file = "crispy_tailwind-1.0.3-py3-none-any.whl", hash = "sha256:31427f66b1c4fd0d6fb040f4197cfb97d104cdbe7641ea2dea940c0057c4db4b"}, -] - -[package.dependencies] -django = ">=4.2" -django-crispy-forms = ">=2.0" - -[package.extras] -test = ["pytest", "pytest-django"] - [[package]] name = "django" version = "5.1.3" @@ -379,4 +361,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "95199cfe52c42cc9e58c63f0f09684bff9a129c583fbbeb74e9c2ffa9c531813" +content-hash = "c7f81b41e492344223fddfa4c37985437e3f8d0012c90b968f9dd3fdd0c67833" diff --git a/tvdt/pyproject.toml b/tvdt/pyproject.toml index c1539fc..882f633 100644 --- a/tvdt/pyproject.toml +++ b/tvdt/pyproject.toml @@ -6,7 +6,6 @@ package-mode = false python = "^3.12" Django = "^5.1.2" django-crispy-forms = "^2.3" -crispy-tailwind = "^1.0.3" crispy-bootstrap5 = "^2024.10" django-allauth = "^65.1.0" diff --git a/tvdt/quiz/admin.py b/tvdt/quiz/admin.py index 2fc25b1..b3ec7f3 100644 --- a/tvdt/quiz/admin.py +++ b/tvdt/quiz/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from .models import Question, Answer, Candidate, Quiz, Season, GivenAnswer +from .models import Answer, Candidate, GivenAnswer, Question, Quiz, Season class CandidatesAdmin(admin.StackedInline): diff --git a/tvdt/quiz/converters.py b/tvdt/quiz/converters.py index 531e9bc..8e2cd39 100644 --- a/tvdt/quiz/converters.py +++ b/tvdt/quiz/converters.py @@ -1,7 +1,7 @@ import base64 import binascii -from .models import Season, Candidate +from .models import Candidate, Season class SeasonCodeConverter: diff --git a/tvdt/quiz/fixtures/krtek.json b/tvdt/quiz/fixtures/krtek.json index c705540..8419a50 100644 --- a/tvdt/quiz/fixtures/krtek.json +++ b/tvdt/quiz/fixtures/krtek.json @@ -1136,6 +1136,30 @@ "name": "Tom" } }, +{ + "model": "quiz.candidate", + "pk": 14, + "fields": { + "season": 1, + "name": "Marijn" + } +}, +{ + "model": "quiz.candidate", + "pk": 15, + "fields": { + "season": 1, + "name": "Renske" + } +}, +{ + "model": "quiz.candidate", + "pk": 16, + "fields": { + "season": 1, + "name": "Philine" + } +}, { "model": "quiz.quiz", "pk": 1, diff --git a/tvdt/quiz/locale/nl/LC_MESSAGES/django.po b/tvdt/quiz/locale/nl/LC_MESSAGES/django.po index c0177c4..20884a6 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-10-20 00:23+0200\n" +"POT-Creation-Date: 2024-11-25 19:18+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,93 +18,77 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: quiz/models/answer.py:10 -msgid "text" -msgstr "tekst" - -#: quiz/models/answer.py:15 quiz/models/given_answer.py:21 -#: quiz/models/question.py:9 quiz/models/question.py:21 -msgid "question" -msgstr "vraag" - -#: quiz/models/answer.py:17 -msgid "is right answer" -msgstr "is goede antwoord" - -#: quiz/models/answer.py:19 quiz/models/candidate.py:25 -msgid "candidates" -msgstr "kandidaten" - -#: quiz/models/answer.py:23 quiz/models/given_answer.py:24 -msgid "answer" -msgstr "antwoord" - -#: quiz/models/answer.py:24 -msgid "answers" -msgstr "antwoorden" - -#: quiz/models/candidate.py:15 quiz/models/quiz.py:9 quiz/models/season.py:12 -msgid "name" -msgstr "naam" - -#: quiz/models/candidate.py:24 quiz/models/correction.py:14 -#: quiz/models/given_answer.py:15 quiz/models/quiz_time.py:11 -msgid "candidate" -msgstr "kandidaat" - -#: quiz/models/correction.py:20 quiz/models/question.py:11 -#: quiz/models/quiz.py:26 quiz/models/quiz_time.py:13 +#: 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 msgid "quiz" msgstr "test" -#: quiz/models/correction.py:25 +#: quiz/models/answer.py:7 +msgid "text" +msgstr "tekst" + +#: quiz/models/answer.py:12 quiz/models/question.py:15 +#: quiz/models/question.py:28 +msgid "question" +msgstr "vraag" + +#: quiz/models/answer.py:14 +msgid "is right answer" +msgstr "is goede antwoord" + +#: quiz/models/answer.py:16 quiz/models/candidate.py:50 +msgid "candidates" +msgstr "kandidaten" + +#: quiz/models/answer.py:20 quiz/models/given_answer.py:25 +msgid "answer" +msgstr "antwoord" + +#: quiz/models/answer.py:21 +msgid "answers" +msgstr "antwoorden" + +#: quiz/models/candidate.py:18 quiz/models/quiz.py:7 quiz/models/season.py:12 +msgid "name" +msgstr "naam" + +#: quiz/models/candidate.py:49 quiz/models/correction.py:11 +#: quiz/models/given_answer.py:11 +msgid "candidate" +msgstr "kandidaat" + +#: quiz/models/correction.py:22 msgid "correction" msgstr "joker" -#: quiz/models/correction.py:26 +#: quiz/models/correction.py:23 msgid "corrections" msgstr "jokers" -#: quiz/models/given_answer.py:30 +#: quiz/models/given_answer.py:36 msgid "given answer" msgstr "gegeven antwoord" -#: quiz/models/given_answer.py:31 +#: quiz/models/given_answer.py:37 msgid "given answers" msgstr "gegeven antwoorden" -#: quiz/models/question.py:13 -msgid "number" -msgstr "nummer" - -#: quiz/models/question.py:14 -msgid "enabled" -msgstr "ingeschakeld" - #: quiz/models/question.py:22 +msgid "enabled" +msgstr "actief" + +#: quiz/models/question.py:29 msgid "questions" msgstr "vraag" -#: quiz/models/quiz.py:14 quiz/models/season.py:38 +#: quiz/models/quiz.py:12 quiz/models/season.py:38 msgid "season" msgstr "seizoen" -#: quiz/models/quiz.py:27 +#: quiz/models/quiz.py:25 msgid "quizzes" msgstr "tests" -#: quiz/models/quiz_time.py:14 -msgid "seconds" -msgstr "seconden" - -#: quiz/models/quiz_time.py:17 -msgid "quiz time" -msgstr "testtijd" - -#: quiz/models/quiz_time.py:18 -msgid "quiz times" -msgstr "testtijden" - #: quiz/models/season.py:19 msgid "active quiz" msgstr "actieve test" @@ -120,3 +104,35 @@ msgstr "kandidaten voorregistreren" #: quiz/models/season.py:39 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 +msgid "Tijd voor de test" +msgstr "Tijd voor de test" + +#: quiz/views/enternameview.py:14 +msgid "Name" +msgstr "Naam" + +#: quiz/views/enternameview.py:27 +msgid "This season has no active quiz." +msgstr "Dit seizoen heeft geen actieve test." + +#: quiz/views/enternameview.py:39 +msgid "Candidate does not exist" +msgstr "Kandidaat bestaat niet" + +#: quiz/views/questionview.py:23 +msgid "No active quiz for season" +msgstr "Geen active test voor seizoen" + +#: quiz/views/questionview.py:27 +msgid "Quiz done" +msgstr "Test klaar" + +#: quiz/views/selectseasonview.py:30 +msgid "Invalid season code" +msgstr "Ongeldige seizoencode" diff --git a/tvdt/quiz/migrations/0001_initial.py b/tvdt/quiz/migrations/0001_initial.py index 98915f7..070f3af 100644 --- a/tvdt/quiz/migrations/0001_initial.py +++ b/tvdt/quiz/migrations/0001_initial.py @@ -1,9 +1,10 @@ -# Generated by Django 5.1.2 on 2024-10-19 21:54 +# Generated by Django 5.1.3 on 2024-11-25 18:17 import django.db.models.deletion -import quiz.helpers from django.db import migrations, models +import quiz.helpers + class Migration(migrations.Migration): @@ -24,7 +25,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("name", models.CharField(max_length=16, verbose_name="naam")), + ("name", models.CharField(max_length=16, verbose_name="name")), ], options={ "verbose_name": "candidate", @@ -44,7 +45,6 @@ class Migration(migrations.Migration): ), ), ("question", models.CharField(max_length=256, verbose_name="question")), - ("number", models.PositiveSmallIntegerField(verbose_name="number")), ("enabled", models.BooleanField(default=True, verbose_name="enabled")), ], options={ @@ -64,7 +64,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("name", models.CharField(max_length=64, verbose_name="naam")), + ("name", models.CharField(max_length=64, verbose_name="name")), ], options={ "verbose_name": "quiz", @@ -91,7 +91,7 @@ class Migration(migrations.Migration): ( "candidates", models.ManyToManyField( - to="quiz.candidate", verbose_name="candidates" + blank=True, to="quiz.candidate", verbose_name="candidates" ), ), ( @@ -107,6 +107,7 @@ class Migration(migrations.Migration): options={ "verbose_name": "answer", "verbose_name_plural": "answers", + "order_with_respect_to": "question", }, ), migrations.AddField( @@ -120,7 +121,7 @@ class Migration(migrations.Migration): ), ), migrations.CreateModel( - name="QuizTime", + name="GivenAnswer", fields=[ ( "id", @@ -131,11 +132,21 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("seconds", models.PositiveIntegerField(verbose_name="seconds")), + ("created", models.DateTimeField(auto_now_add=True)), + ( + "answer", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="quiz.answer", + verbose_name="answer", + ), + ), ( "candidate", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, + related_name="answers", to="quiz.candidate", verbose_name="candidate", ), @@ -144,14 +155,16 @@ class Migration(migrations.Migration): "quiz", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, + related_name="+", to="quiz.quiz", verbose_name="quiz", ), ), ], options={ - "verbose_name": "quiz time", - "verbose_name_plural": "quiz times", + "verbose_name": "given answer", + "verbose_name_plural": "given answers", + "ordering": ("quiz", "candidate"), }, ), migrations.CreateModel( @@ -166,7 +179,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("name", models.CharField(max_length=64, verbose_name="naam")), + ("name", models.CharField(max_length=64, verbose_name="name")), ( "season_code", models.CharField( @@ -175,9 +188,16 @@ class Migration(migrations.Migration): verbose_name="season code", ), ), + ( + "preregister_candidates", + models.BooleanField( + default=True, verbose_name="preregister candidates" + ), + ), ( "active_quiz", models.ForeignKey( + blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, @@ -212,54 +232,9 @@ class Migration(migrations.Migration): verbose_name="season", ), ), - migrations.CreateModel( - name="GivenAnswer", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "answer", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="quiz.answer", - verbose_name="answer", - ), - ), - ( - "candidate", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="answers", - to="quiz.candidate", - verbose_name="candidate", - ), - ), - ( - "question", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="given_answers", - to="quiz.question", - verbose_name="question", - ), - ), - ], - options={ - "verbose_name": "given answer", - "verbose_name_plural": "given answers", - "unique_together": {("candidate", "question")}, - }, - ), - migrations.AlterUniqueTogether( + migrations.AlterOrderWithRespectTo( name="question", - unique_together={("quiz", "number")}, + order_with_respect_to="quiz", ), migrations.CreateModel( name="Correction", diff --git a/tvdt/quiz/migrations/0002_season_preregister_candidates_and_more.py b/tvdt/quiz/migrations/0002_season_preregister_candidates_and_more.py deleted file mode 100644 index 5f6e76b..0000000 --- a/tvdt/quiz/migrations/0002_season_preregister_candidates_and_more.py +++ /dev/null @@ -1,56 +0,0 @@ -# Generated by Django 5.1.2 on 2024-10-19 22:17 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("quiz", "0001_initial"), - ] - - operations = [ - migrations.AddField( - model_name="season", - name="preregister_candidates", - field=models.BooleanField( - default=True, verbose_name="preregister candidates" - ), - ), - migrations.AlterField( - model_name="answer", - name="candidates", - field=models.ManyToManyField( - blank=True, to="quiz.candidate", verbose_name="candidates" - ), - ), - migrations.AlterField( - model_name="candidate", - name="name", - field=models.CharField(max_length=16, verbose_name="name"), - ), - migrations.AlterField( - model_name="quiz", - name="name", - field=models.CharField(max_length=64, verbose_name="name"), - ), - migrations.AlterField( - model_name="season", - name="active_quiz", - field=models.ForeignKey( - blank=True, - default=None, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to="quiz.quiz", - verbose_name="active quiz", - ), - ), - migrations.AlterField( - model_name="season", - name="name", - field=models.CharField(max_length=64, verbose_name="name"), - ), - ] diff --git a/tvdt/quiz/migrations/0003_givenanswer_created_alter_givenanswer_question.py b/tvdt/quiz/migrations/0003_givenanswer_created_alter_givenanswer_question.py deleted file mode 100644 index 1473ea5..0000000 --- a/tvdt/quiz/migrations/0003_givenanswer_created_alter_givenanswer_question.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 5.1.2 on 2024-11-03 19:21 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("quiz", "0002_season_preregister_candidates_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="givenanswer", - name="created", - field=models.DateTimeField(auto_now_add=True, default=None), - preserve_default=False, - ), - migrations.AlterField( - model_name="givenanswer", - name="question", - field=models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="given_answers", - to="quiz.question", - verbose_name="question", - ), - ), - ] diff --git a/tvdt/quiz/migrations/0004_alter_question_unique_together_and_more.py b/tvdt/quiz/migrations/0004_alter_question_unique_together_and_more.py deleted file mode 100644 index 969f4f4..0000000 --- a/tvdt/quiz/migrations/0004_alter_question_unique_together_and_more.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-23 16:15 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("quiz", "0003_givenanswer_created_alter_givenanswer_question"), - ] - - operations = [ - migrations.AlterUniqueTogether( - name="question", - unique_together=set(), - ), - migrations.AlterOrderWithRespectTo( - name="question", - order_with_respect_to="quiz", - ), - migrations.AlterOrderWithRespectTo( - name="answer", - order_with_respect_to="question", - ), - migrations.RemoveField( - model_name="question", - name="number", - ), - ] diff --git a/tvdt/quiz/models/__init__.py b/tvdt/quiz/models/__init__.py index d35b3c4..6b1752d 100644 --- a/tvdt/quiz/models/__init__.py +++ b/tvdt/quiz/models/__init__.py @@ -4,5 +4,4 @@ from .correction import Correction from .given_answer import GivenAnswer from .question import Question from .quiz import Quiz -from .quiz_time import QuizTime from .season import Season diff --git a/tvdt/quiz/models/candidate.py b/tvdt/quiz/models/candidate.py index faee14c..863e7d6 100644 --- a/tvdt/quiz/models/candidate.py +++ b/tvdt/quiz/models/candidate.py @@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _ from django_stubs_ext.db.models import TypedModelMeta from .given_answer import GivenAnswer -from .question import NoActiveTestForSeason, QuizAlreadyFinished, Question +from .question import NoActiveTestForSeason, Question, QuizAlreadyFinished class Candidate(models.Model): @@ -23,11 +23,13 @@ class Candidate(models.Model): raise NoActiveTestForSeason() question = ( - Question.objects.filter(quiz=quiz) + Question.objects.filter(quiz=quiz, enabled=True) .exclude( id__in=GivenAnswer.objects.filter( - candidate=candidate, question__quiz=quiz - ).values_list("question_id", flat=True) + candidate=candidate, + quiz=quiz, + answer__isnull=False, + ).values_list("answer__question_id", flat=True) ) .first() ) diff --git a/tvdt/quiz/models/correction.py b/tvdt/quiz/models/correction.py index 07cb610..3575c1d 100644 --- a/tvdt/quiz/models/correction.py +++ b/tvdt/quiz/models/correction.py @@ -2,9 +2,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from django_stubs_ext.db.models import TypedModelMeta -from .candidate import Candidate -from .quiz import Quiz - class Correction(models.Model): candidate = models.ForeignKey( diff --git a/tvdt/quiz/models/given_answer.py b/tvdt/quiz/models/given_answer.py index dbe98a0..b6863e7 100644 --- a/tvdt/quiz/models/given_answer.py +++ b/tvdt/quiz/models/given_answer.py @@ -10,20 +10,28 @@ class GivenAnswer(models.Model): related_name="answers", verbose_name=_("candidate"), ) - question = models.ForeignKey( - "Question", + + quiz = models.ForeignKey( + "Quiz", on_delete=models.CASCADE, - null=True, - related_name="given_answers", - verbose_name=_("question"), + null=False, + related_name="+", + verbose_name=_("quiz"), ) + answer = models.ForeignKey( - "Answer", on_delete=models.CASCADE, verbose_name=_("answer") + "Answer", + on_delete=models.CASCADE, + verbose_name=_("answer"), + null=True, ) created = models.DateTimeField(auto_now_add=True) + def __str__(self): + return f"{self.quiz} - {self.candidate.name} {self.answer}" + class Meta(TypedModelMeta): - unique_together = ["candidate", "question"] + ordering = ("quiz", "candidate") verbose_name = _("given answer") verbose_name_plural = _("given answers") diff --git a/tvdt/quiz/models/quiz_time.py b/tvdt/quiz/models/quiz_time.py deleted file mode 100644 index 7db0247..0000000 --- a/tvdt/quiz/models/quiz_time.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.db import models -from django.utils.translation import gettext_lazy as _ -from django_stubs_ext.db.models import TypedModelMeta - - -class QuizTime(models.Model): - candidate = models.ForeignKey( - "Candidate", on_delete=models.CASCADE, verbose_name=_("candidate") - ) - quiz = models.ForeignKey("Quiz", on_delete=models.CASCADE, verbose_name=_("quiz")) - seconds = models.PositiveIntegerField(verbose_name=_("seconds")) - - class Meta(TypedModelMeta): - verbose_name = _("quiz time") - verbose_name_plural = _("quiz times") diff --git a/tvdt/quiz/templates/quiz/base.html b/tvdt/quiz/templates/quiz/base.html index 7f93b6f..1ce8e08 100644 --- a/tvdt/quiz/templates/quiz/base.html +++ b/tvdt/quiz/templates/quiz/base.html @@ -1,6 +1,6 @@ {% load static %} - + {# #} @@ -17,17 +17,33 @@ background-position: center center; background-repeat: no-repeat; background-color: black; + color: white; + display: grid; align-items: center; justify-self: center; - color: white; + } + + .asteriskField { + display: none; } -
-{% block body %}{% endblock %} -
-{% block script %}{% endblock %} +
+
+ {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} + {% block body %}{% endblock %} +
+ {% block script %}{% endblock %} +
diff --git a/tvdt/quiz/templates/quiz/enter_name.html b/tvdt/quiz/templates/quiz/enter_name.html index 1af6abf..044d8cb 100644 --- a/tvdt/quiz/templates/quiz/enter_name.html +++ b/tvdt/quiz/templates/quiz/enter_name.html @@ -3,7 +3,6 @@ {% block body %} -

{{ season.name }} ({{ season.season_code }})

{% crispy form %} {% endblock %} \ No newline at end of file diff --git a/tvdt/quiz/urls.py b/tvdt/quiz/urls.py index fbd8a67..cea1c13 100644 --- a/tvdt/quiz/urls.py +++ b/tvdt/quiz/urls.py @@ -2,8 +2,8 @@ from django.urls import path, register_converter from .converters import CandidateConverter, SeasonCodeConverter from .views import SelectSeasonView -from .views.questionview import QuestionView from .views.enternameview import EnterNameView +from .views.questionview import QuestionView register_converter(SeasonCodeConverter, "season") register_converter(CandidateConverter, "candidate") diff --git a/tvdt/quiz/views/enternameview.py b/tvdt/quiz/views/enternameview.py index 3ea4b3f..95aa77f 100644 --- a/tvdt/quiz/views/enternameview.py +++ b/tvdt/quiz/views/enternameview.py @@ -1,47 +1,47 @@ from crispy_forms.helper import FormHelper from django import forms -from django.http import Http404 -from django.shortcuts import render, redirect +from django.contrib import messages +from django.shortcuts import redirect from django.urls import reverse +from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy from django.views import View +from django.views.generic.base import TemplateResponseMixin -from ..models import Season, Candidate +from ..models import Candidate, Season class EnterNameForm(forms.Form): - name = forms.CharField() + name = forms.CharField(label=_("Name")) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() -class EnterNameView(View): +class EnterNameView(View, TemplateResponseMixin): template_name = "quiz/enter_name.html" forms_class = EnterNameForm def get(self, request, season: Season, *args, **kwargs): if season.active_quiz == None: - raise Http404("No quiz active") + messages.info(request, _("This season has no active quiz.")) + return redirect("home") - return render( - request, - self.template_name, - {"form": self.forms_class(), "season": season}, - ) + return self.render_to_response({"form": self.forms_class()}) def post(self, request, season: Season, *args, **kwargs): name = request.POST.get("name") - if season.preregister_candidates: - try: - candidate = Candidate.objects.get(season=season, name=name) - except Candidate.DoesNotExist: - raise Http404("Candidate not found") - else: - candidate, created = Candidate.objects.get_or_create( - season=season, name=name - ) + try: + candidate = Candidate.objects.get(season=season, name__iexact=name) + except Candidate.DoesNotExist: + if season.preregister_candidates: + messages.warning(request, _("Candidate does not exist")) + + return redirect(reverse("quiz", kwargs={"season": season})) + + candidate = Candidate.objects.create(season=season, name=name) return redirect( reverse( diff --git a/tvdt/quiz/views/questionview.py b/tvdt/quiz/views/questionview.py index a24ecd2..195bcd8 100644 --- a/tvdt/quiz/views/questionview.py +++ b/tvdt/quiz/views/questionview.py @@ -1,15 +1,17 @@ from django.contrib import messages from django.core.exceptions import BadRequest from django.http import Http404, HttpRequest, HttpResponse -from django.shortcuts import render +from django.shortcuts import redirect +from django.urls import reverse from django.utils.translation import gettext as _ from django.views import View +from django.views.generic.base import TemplateResponseMixin -from ..models import Candidate, Answer, GivenAnswer -from ..models.question import NoActiveTestForSeason +from ..models import Answer, Candidate, GivenAnswer +from ..models.question import NoActiveTestForSeason, QuizAlreadyFinished -class QuestionView(View): +class QuestionView(View, TemplateResponseMixin): template_name = "quiz/question.html" def get( @@ -18,14 +20,26 @@ class QuestionView(View): try: question = candidate.get_next_question(candidate) except NoActiveTestForSeason: - messages.error(request, _("No active Quiz for season")) - raise Http404("No active Quiz for seaon") + messages.error(request, _("No active quiz for season")) + return redirect("home") + except QuizAlreadyFinished: + if not kwargs.get("from_post"): + messages.error(request, _("Quiz done")) - return render( - request, - "quiz/question.html", - {"candidate": candidate, "question": question}, - ) + return redirect(reverse("quiz", kwargs={"season": candidate.season})) + + # TODO: On first question -> record time + if ( + GivenAnswer.objects.filter( + candidate=candidate, quiz=candidate.season.active_quiz + ).count() + == 0 + ): + GivenAnswer.objects.create( + candidate=candidate, quiz=question.quiz, answer=None + ) + + return self.render_to_response({"candidate": candidate, "question": question}) def post(self, request: HttpRequest, candidate: Candidate, *args, **kwargs): answer_id = request.POST.get("answer") @@ -38,7 +52,8 @@ class QuestionView(View): raise BadRequest GivenAnswer.objects.create( - candidate=candidate, question=answer.question, answer=answer + candidate=candidate, + quiz=answer.question.quiz, + answer=answer, ) - - return self.get(request, candidate, args, kwargs) + return self.get(request, candidate, from_post=True) diff --git a/tvdt/quiz/views/selectseasonview.py b/tvdt/quiz/views/selectseasonview.py index cce0f53..e9ed058 100644 --- a/tvdt/quiz/views/selectseasonview.py +++ b/tvdt/quiz/views/selectseasonview.py @@ -1,9 +1,12 @@ from crispy_forms.helper import FormHelper +from django import forms +from django.contrib import messages from django.http import Http404 from django.shortcuts import redirect from django.urls import reverse +from django.utils.translation import gettext as _ from django.views.generic.edit import FormView -from django import forms +from mypy.dmypy.client import request from ..models import Season @@ -24,6 +27,7 @@ class SelectSeasonView(FormView): try: season = Season.objects.get(season_code=form.cleaned_data["code"].upper()) except Season.DoesNotExist: - raise Http404("Season does not exist") + messages.warning(self.request, _("Invalid season code")) + return redirect("home") return redirect(reverse("quiz", kwargs={"season": season})) diff --git a/tvdt/tvdt/settings.py b/tvdt/tvdt/settings.py index 33581d1..1ab74b3 100644 --- a/tvdt/tvdt/settings.py +++ b/tvdt/tvdt/settings.py @@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/5.1/ref/settings/ """ from pathlib import Path + +from django.contrib import messages from django.utils.translation import gettext_lazy as _ # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -125,6 +127,18 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] +MESSAGE_TAGS = { + messages.DEBUG: "alert-info", + messages.INFO: "alert-info", + messages.SUCCESS: "alert-success", + messages.WARNING: "alert-warning", + messages.ERROR: "alert-danger", + 100: "alert-primary", + 110: "alert-secondary", + 120: "alert-light", + 130: "alert-dark", +} + # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ diff --git a/tvdt/tvdt/urls.py b/tvdt/tvdt/urls.py index e01c529..e5a0122 100644 --- a/tvdt/tvdt/urls.py +++ b/tvdt/tvdt/urls.py @@ -18,7 +18,7 @@ Including another URLconf from django.conf import settings from django.conf.urls.static import static from django.contrib import admin -from django.urls import path, include +from django.urls import include, path urlpatterns = [ path("", include("quiz.urls")),