diff --git a/scavenger2022/core/admin.py b/scavenger2022/core/admin.py index 44a0bd2..e0d0070 100644 --- a/scavenger2022/core/admin.py +++ b/scavenger2022/core/admin.py @@ -4,7 +4,6 @@ from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _l from django.urls import reverse -from .models import * from .forms import * @@ -19,6 +18,18 @@ class InviteInLine(admin.StackedInline): readonly_fields = ("invites",) +class LogicPuzzleAdmin(admin.ModelAdmin): + list_display = ( + "qr_index", + "hint", + ) + search_fields = ( + "hint", + "qr_index", + ) + ordering = ("qr_index",) + + class TeamAdmin(admin.ModelAdmin): readonly_fields = ("current_qr_i", "path") inlines = [ @@ -70,3 +81,4 @@ class UserAdmin(UserAdmin_): admin.site.register(User, UserAdmin) admin.site.register(Team, TeamAdmin) admin.site.register(QrCode, QrCodeAdmin) +admin.site.register(LogicPuzzleHint, LogicPuzzleAdmin) diff --git a/scavenger2022/core/context_processors.py b/scavenger2022/core/context_processors.py index 5acf991..77dad5f 100644 --- a/scavenger2022/core/context_processors.py +++ b/scavenger2022/core/context_processors.py @@ -2,12 +2,12 @@ import datetime -def cutoff(request): +def start(request): return dict( - CUTOFF=(cutoff := settings.CUTOFF), - CUTOFF_BEFORE=cutoff > datetime.datetime.utcnow(), - CUTOFF_UNTIL=cutoff - datetime.datetime.utcnow(), - CUTON=(cuton := settings.CUTON), - CUTON_BEFORE=cuton > datetime.datetime.utcnow(), - CUTON_UNTIL=cuton - datetime.datetime.utcnow(), + START=(start := settings.START), + START_BEFORE=start > datetime.datetime.utcnow(), + START_UNTIL=start - datetime.datetime.utcnow(), + END=(end := settings.END), + END_BEFORE=end > datetime.datetime.utcnow(), + END_UNTIL=end - datetime.datetime.utcnow(), ) diff --git a/scavenger2022/core/forms.py b/scavenger2022/core/forms.py index 232bd5f..2939c4f 100644 --- a/scavenger2022/core/forms.py +++ b/scavenger2022/core/forms.py @@ -11,9 +11,8 @@ class Meta: "members", "is_active", "is_open", - "completed_qr_codes", - "current_qr_code", "solo", + "current_qr_i", ) diff --git a/scavenger2022/core/migrations/0013_logicpuzzlehint_alter_qrcode_notes.py b/scavenger2022/core/migrations/0013_logicpuzzlehint_alter_qrcode_notes.py new file mode 100644 index 0000000..a12b205 --- /dev/null +++ b/scavenger2022/core/migrations/0013_logicpuzzlehint_alter_qrcode_notes.py @@ -0,0 +1,38 @@ +# Generated by Django 4.1.3 on 2022-12-12 22:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0012_remove_user_chosen"), + ] + + operations = [ + migrations.CreateModel( + name="LogicPuzzleHint", + fields=[ + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "hint", + models.TextField( + help_text="Hint for the logic puzzle", max_length=1024 + ), + ), + ("notes", models.TextField(blank=True, help_text="Internal notes")), + ( + "qr_index", + models.IntegerField( + help_text="The index of the QR code that this hint is for, that is everyone on their nth QR code will get same same hint (starting from 1)", + unique=True, + ), + ), + ], + ), + migrations.AlterField( + model_name="qrcode", + name="notes", + field=models.TextField(blank=True, help_text="Internal notes"), + ), + ] diff --git a/scavenger2022/core/migrations/0014_merge_20221212_1857.py b/scavenger2022/core/migrations/0014_merge_20221212_1857.py new file mode 100644 index 0000000..120a7de --- /dev/null +++ b/scavenger2022/core/migrations/0014_merge_20221212_1857.py @@ -0,0 +1,13 @@ +# Generated by Django 4.1.3 on 2022-12-12 23:57 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0013_logicpuzzlehint_alter_qrcode_notes"), + ("core", "0013_remove_team_completed_qr_codes_and_more"), + ] + + operations = [] diff --git a/scavenger2022/core/migrations/0015_alter_hint_options_alter_invite_team.py b/scavenger2022/core/migrations/0015_alter_hint_options_alter_invite_team.py new file mode 100644 index 0000000..d1452b0 --- /dev/null +++ b/scavenger2022/core/migrations/0015_alter_hint_options_alter_invite_team.py @@ -0,0 +1,27 @@ +# Generated by Django 4.1.3 on 2022-12-13 00:16 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0014_merge_20221212_1857"), + ] + + operations = [ + migrations.AlterModelOptions( + name="hint", + options={"permissions": [("view_before_start", "Play game before start")]}, + ), + migrations.AlterField( + model_name="invite", + name="team", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="invites", + to="core.team", + ), + ), + ] diff --git a/scavenger2022/core/models.py b/scavenger2022/core/models.py index a3d4db7..e23822c 100644 --- a/scavenger2022/core/models.py +++ b/scavenger2022/core/models.py @@ -1,9 +1,9 @@ import random import secrets +from django.conf import settings from django.contrib.auth.models import AbstractUser from django.db import models -from django.conf import settings class User(AbstractUser): @@ -13,6 +13,14 @@ class User(AbstractUser): "Team", related_name="members", on_delete=models.CASCADE, blank=True, null=True ) + @property + def in_team(self) -> bool: + try: + _ = self.team.solo + return True + except AttributeError: + return False + def generate_hint_key(): return secrets.token_urlsafe(48) @@ -28,9 +36,7 @@ class QrCode(models.Model): max_length=1024, help_text="Location of the QR code. Be specific—it's internal", ) - notes = models.TextField( - help_text="Internal notes", - ) + notes = models.TextField(help_text="Internal notes", blank=True) key = models.CharField(max_length=64, unique=True, default=generate_hint_key) def __str__(self): @@ -72,10 +78,12 @@ def __str__(self): return self.hint class Meta: - permissions = [("view_before_cutoff", "Play game before cutoff")] + permissions = [("view_before_start", "Play game before start")] class Team(models.Model): + """note, a user can currently be in multiple teams, in the future limit this to one per (class: Hunt)""" + # owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name="teams_ownership") potentially add this later id = models.AutoField(primary_key=True) name = models.CharField(max_length=64, unique=True, null=True) @@ -94,10 +102,8 @@ def update_current_qr_i(self, i: int): @property def members(self): - return User.objects.filter(team=self) - - def is_solo(self): - return self.members.count() == 1 + """Returns all members of the team, it's a related manager so to convert to queryset use .all() or filter it.""" + return User.objects.filter(team=str(self.id)) def is_full(self): return self.members.count() >= settings.MAX_TEAM_SIZE @@ -107,12 +113,17 @@ def join(self, user: User): return if self.is_full(): raise IndexError("Team is full") - self.members.add(user) - self.save() + user.team = self + user.save() def invites(self): return Invite.objects.filter(team=self) + def get_qr_nth(self): + """Get the total amount of qr codes the team has completed""" + print(int(self.current_qr_i) + 1) # todo remove. this is just for debugging + return int(self.current_qr_i) + 1 + def __str__(self): return str(self.name) @@ -125,3 +136,38 @@ class Invite(models.Model): invites = models.IntegerField(default=0) team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="invites") code = models.CharField(max_length=32, unique=True) + + +# class Hunt(models.Model): +# id = models.AutoField(primary_key=True) +# name = models.CharField(max_length=64) +# start = models.DateTimeField() +# end = models.DateTimeField() +# is_active = models.BooleanField(default=False) +# team_size = models.IntegerField(default=4, help_text="Max Team size") +# final_qr_id = models.IntegerField(null=True, blank=True) +# +# def __str__(self): +# return self.name + + +class LogicPuzzleHint(models.Model): + id = models.AutoField(primary_key=True) + hint = models.TextField( + max_length=1024, + help_text="Hint for the logic puzzle", + ) + notes = models.TextField(help_text="Internal notes", blank=True) + qr_index = models.IntegerField( + help_text="The index of the QR code that this hint is for, that is everyone on their nth QR code will get same same hint (starting from 1)", + unique=True, + ) + + # belongs_to = models.ForeignKey(Hunt, related_name="logic_puzzle_hunt", on_delete=models.CASCADE) + + def __str__(self): + return str(self.hint) + + @classmethod + def get_hint(cls, team: Team): + return cls.objects.get(qr_index=team.get_qr_nth()).hint diff --git a/scavenger2022/core/static/core/base.css b/scavenger2022/core/static/core/base.css index a3c54c0..7461d34 100644 --- a/scavenger2022/core/static/core/base.css +++ b/scavenger2022/core/static/core/base.css @@ -41,7 +41,7 @@ body > nav { padding: 4px; } -#cutoff-header { +#start-header { text-align: center; } diff --git a/scavenger2022/core/templates/core/base.html b/scavenger2022/core/templates/core/base.html index c279f1f..a28af01 100644 --- a/scavenger2022/core/templates/core/base.html +++ b/scavenger2022/core/templates/core/base.html @@ -9,20 +9,20 @@ - {% if CUTOFF_BEFORE or CUTON_BEFORE %} + {% if START_BEFORE or END_BEFORE %} {% endif %} @@ -77,10 +77,10 @@ Metropolis logo Metropolis - - + + Doodles - + Scavenger Hunt (2022) @@ -88,18 +88,18 @@ - {% if CUTOFF_BEFORE %} -