from django.db import models from django.contrib.auth import get_user_model from django.utils import timezone User = get_user_model() class Category(models.Model): name = models.CharField(max_length=120, unique=True) slug = models.SlugField(max_length=140, unique=True) is_active = models.BooleanField(default=True) class Meta: ordering = ["name"] def __str__(self): return self.name class Question(models.Model): category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name="questions") prompt = models.TextField() correct_answer = models.CharField(max_length=255) is_active = models.BooleanField(default=True) class Meta: indexes = [models.Index(fields=["category", "is_active"])] def __str__(self): return f"{self.category.name}: {self.prompt[:60]}" class GameSession(models.Model): class Status(models.TextChoices): LOBBY = "lobby", "Lobby" LIE = "lie", "Løgnfase" GUESS = "guess", "Gættefase" REVEAL = "reveal", "Reveal" FINISHED = "finished", "Afsluttet" host = models.ForeignKey(User, on_delete=models.PROTECT, related_name="hosted_sessions") code = models.CharField(max_length=8, unique=True) status = models.CharField(max_length=16, choices=Status.choices, default=Status.LOBBY) current_round = models.PositiveIntegerField(default=1) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["-created_at"] def __str__(self): return f"{self.code} ({self.status})" class Player(models.Model): session = models.ForeignKey(GameSession, on_delete=models.CASCADE, related_name="players") nickname = models.CharField(max_length=40) score = models.IntegerField(default=0) is_connected = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = (("session", "nickname"),) ordering = ["-score", "nickname"] def __str__(self): return f"{self.nickname} ({self.session.code})" class RoundConfig(models.Model): session = models.ForeignKey(GameSession, on_delete=models.CASCADE, related_name="rounds") number = models.PositiveIntegerField() category = models.ForeignKey(Category, on_delete=models.PROTECT) questions_count = models.PositiveIntegerField(default=3) points_correct = models.IntegerField(default=5) points_bluff = models.IntegerField(default=2) lie_seconds = models.PositiveIntegerField(default=45) guess_seconds = models.PositiveIntegerField(default=30) class Meta: unique_together = (("session", "number"),) ordering = ["number"] class RoundQuestion(models.Model): session = models.ForeignKey(GameSession, on_delete=models.CASCADE, related_name="round_questions") round_number = models.PositiveIntegerField() question = models.ForeignKey(Question, on_delete=models.PROTECT) correct_answer = models.CharField(max_length=255) shown_at = models.DateTimeField(default=timezone.now) mixed_answers = models.JSONField(default=list, blank=True) class LieAnswer(models.Model): round_question = models.ForeignKey(RoundQuestion, on_delete=models.CASCADE, related_name="lies") player = models.ForeignKey(Player, on_delete=models.CASCADE) text = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = (("round_question", "player"),) class Guess(models.Model): round_question = models.ForeignKey(RoundQuestion, on_delete=models.CASCADE, related_name="guesses") player = models.ForeignKey(Player, on_delete=models.CASCADE) selected_text = models.CharField(max_length=255) is_correct = models.BooleanField(default=False) fooled_player = models.ForeignKey(Player, on_delete=models.SET_NULL, null=True, blank=True, related_name="fooled_guesses") created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = (("round_question", "player"),) class ScoreEvent(models.Model): session = models.ForeignKey(GameSession, on_delete=models.CASCADE, related_name="score_events") player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="score_events") delta = models.IntegerField() reason = models.CharField(max_length=80) meta = models.JSONField(default=dict, blank=True) created_at = models.DateTimeField(auto_now_add=True)