Add basic quiz functionality

This commit is contained in:
2024-11-23 22:25:24 +01:00
parent 6bf0a56b88
commit 27b8c40c1c
40 changed files with 2471 additions and 53 deletions

View File

@@ -0,0 +1,8 @@
from .answer import Answer
from .candidate import Candidate
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

View File

@@ -0,0 +1,23 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_stubs_ext.db.models import TypedModelMeta
class Answer(models.Model):
text = models.CharField(max_length=64, verbose_name=_("text"))
question = models.ForeignKey(
"Question",
on_delete=models.CASCADE,
related_name="answers",
verbose_name=_("question"),
)
is_right_answer = models.BooleanField(verbose_name=_("is right answer"))
candidates = models.ManyToManyField(
"Candidate", verbose_name=_("candidates"), blank=True
)
class Meta(TypedModelMeta):
verbose_name = _("answer")
verbose_name_plural = _("answers")
order_with_respect_to = "question"

View File

@@ -0,0 +1,48 @@
from typing import Self
from django.db import models
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
class Candidate(models.Model):
season = models.ForeignKey(
"Season",
on_delete=models.CASCADE,
related_name="candidates",
verbose_name="season",
)
name = models.CharField(max_length=16, verbose_name=_("name"))
def get_next_question(self, candidate: Self) -> "Question":
quiz = candidate.season.active_quiz
if quiz is None:
raise NoActiveTestForSeason()
question = (
Question.objects.filter(quiz=quiz)
.exclude(
id__in=GivenAnswer.objects.filter(
candidate=candidate, question__quiz=quiz
).values_list("question_id", flat=True)
)
.first()
)
if question is None:
raise QuizAlreadyFinished()
return question
def __str__(self) -> str:
return f"{self.name} ({self.season})"
class Meta(TypedModelMeta):
unique_together = ["season", "name"]
indexes = [models.Index(fields=["season", "name"])]
verbose_name = _("candidate")
verbose_name_plural = _("candidates")

View File

@@ -0,0 +1,26 @@
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(
"Candidate",
on_delete=models.CASCADE,
related_name="corrections_used",
verbose_name=_("candidate"),
)
quiz = models.ForeignKey(
"Quiz",
on_delete=models.CASCADE,
related_name="corrections_used",
verbose_name=_("quiz"),
)
class Meta(TypedModelMeta):
unique_together = ("candidate", "quiz")
verbose_name = _("correction")
verbose_name_plural = _("corrections")

View File

@@ -0,0 +1,29 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_stubs_ext.db.models import TypedModelMeta
class GivenAnswer(models.Model):
candidate = models.ForeignKey(
"Candidate",
on_delete=models.CASCADE,
related_name="answers",
verbose_name=_("candidate"),
)
question = models.ForeignKey(
"Question",
on_delete=models.CASCADE,
null=True,
related_name="given_answers",
verbose_name=_("question"),
)
answer = models.ForeignKey(
"Answer", on_delete=models.CASCADE, verbose_name=_("answer")
)
created = models.DateTimeField(auto_now_add=True)
class Meta(TypedModelMeta):
unique_together = ["candidate", "question"]
verbose_name = _("given answer")
verbose_name_plural = _("given answers")

View File

@@ -0,0 +1,31 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_stubs_ext.db.models import TypedModelMeta
class NoActiveTestForSeason(Exception):
pass
class QuizAlreadyFinished(Exception):
pass
class Question(models.Model):
question = models.CharField(max_length=256, verbose_name=_("question"))
quiz = models.ForeignKey(
"Quiz",
on_delete=models.CASCADE,
related_name="questions",
verbose_name=_("quiz"),
)
enabled = models.BooleanField(default=True, verbose_name=_("enabled"))
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)"
class Meta(TypedModelMeta):
verbose_name = _("question")
verbose_name_plural = _("questions")
order_with_respect_to = "quiz"

25
tvdt/quiz/models/quiz.py Normal file
View File

@@ -0,0 +1,25 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_stubs_ext.db.models import TypedModelMeta
class Quiz(models.Model):
name = models.CharField(max_length=64, verbose_name=_("name"))
season = models.ForeignKey(
"Season",
on_delete=models.CASCADE,
related_name="quizzes",
verbose_name=_("season"),
)
def is_valid_quiz(self) -> bool:
pass
# Check > 0 active questions
# Check every question 1 right answer
def __str__(self) -> str:
return f"{self.season.name} - {self.name}"
class Meta(TypedModelMeta):
verbose_name = _("quiz")
verbose_name_plural = _("quizzes")

View File

@@ -0,0 +1,15 @@
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")

View File

@@ -0,0 +1,39 @@
import random
import string
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
class Season(models.Model):
name = models.CharField(max_length=64, verbose_name=_("name"))
active_quiz = models.ForeignKey(
"Quiz",
on_delete=models.SET_NULL,
null=True,
blank=True,
default=None,
verbose_name=_("active quiz"),
related_name="+",
)
season_code = models.CharField(
max_length=5, default=generate_season_code, verbose_name=_("season code")
)
preregister_candidates = models.BooleanField(
default=True, verbose_name=_("preregister candidates")
)
def renew_season_code(self) -> str:
self.season_code = generate_season_code()
self.save()
return self.season_code
def __str__(self) -> str:
return self.name
class Meta(TypedModelMeta):
verbose_name = _("season")
verbose_name_plural = _("seasons")