From 0b49c39af5df9400c32db42bdee26c8f5a10ceac Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 2 Dec 2023 23:45:46 -0500 Subject: [PATCH 01/36] Added start/end via current hunt --- core/context_processors.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/core/context_processors.py b/core/context_processors.py index 7e1c569..3417a86 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -1,14 +1,16 @@ import datetime +from core.models import Hunt from django.conf import settings def start(request): - return dict( - 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(), - ) + hunt = Hunt.current_hunt() + return dict( + START=(start := hunt.start), + START_BEFORE=start > datetime.datetime.utcnow(), + START_UNTIL=start - datetime.datetime.utcnow(), + END=(end := hunt.end), + END_BEFORE=end > datetime.datetime.utcnow(), + END_UNTIL=end - datetime.datetime.utcnow(), + ) From 0e577092b3115c96bf6e1a7efba9eca2f4a906d9 Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 2 Dec 2023 23:55:09 -0500 Subject: [PATCH 02/36] I'm clownin --- core/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/models.py b/core/models.py index 8f84743..c7039a4 100644 --- a/core/models.py +++ b/core/models.py @@ -204,6 +204,12 @@ class Hunt(models.Model): ending_location = models.ForeignKey( QrCode, on_delete=models.PROTECT, related_name="ending_location" ) + middle_locations = models.ManyToManyField( + QrCode, + related_name="hunt", + help_text="Possible locations that are not the start or end", + blank=True, + ) early_access_users = models.ManyToManyField( User, related_name="early_access_users", From 701f97cf32e396ad89537c365c2441eccd6f8af1 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 3 Dec 2023 00:03:17 -0500 Subject: [PATCH 03/36] switched all references of settings.start or end to Hunt start/end --- .idea/workspace.xml | 86 ++++++------ core/context_processors.py | 18 +-- core/views/qr.py | 252 ++++++++++++++++++++---------------- core/views/team.py | 6 +- wlmac-scavenger/settings.py | 2 - 5 files changed, 195 insertions(+), 169 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 97adc6f..c4e80ac 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,13 +4,8 @@ - - @@ -447,7 +449,9 @@ - diff --git a/core/context_processors.py b/core/context_processors.py index 3417a86..ace58e2 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -5,12 +5,12 @@ def start(request): - hunt = Hunt.current_hunt() - return dict( - START=(start := hunt.start), - START_BEFORE=start > datetime.datetime.utcnow(), - START_UNTIL=start - datetime.datetime.utcnow(), - END=(end := hunt.end), - END_BEFORE=end > datetime.datetime.utcnow(), - END_UNTIL=end - datetime.datetime.utcnow(), - ) + hunt = Hunt.current_hunt() + return dict( + START=(start := hunt.start), + START_BEFORE=start > datetime.datetime.utcnow(), + START_UNTIL=start - datetime.datetime.utcnow(), + END=(end := hunt.end), + END_BEFORE=end > datetime.datetime.utcnow(), + END_UNTIL=end - datetime.datetime.utcnow(), + ) diff --git a/core/views/qr.py b/core/views/qr.py index 4a7c1fa..e5ab6df 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -1,56 +1,77 @@ import datetime +from functools import wraps from queue import LifoQueue -from django.conf import settings -from functools import wraps from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.views.decorators.http import require_http_methods -from django.shortcuts import render, redirect, get_object_or_404 -from django.urls import reverse -from django.utils.translation import gettext as _ -from ..models import QrCode, LogicPuzzleHint, Team from django.db.models import signals from django.dispatch import Signal, receiver from django.http import StreamingHttpResponse +from django.shortcuts import render, redirect, get_object_or_404 +from django.urls import reverse +from django.utils.translation import gettext as _ +from django.views.decorators.http import require_http_methods + +from ..models import QrCode, LogicPuzzleHint, Team, Hunt # NOTE: some of these GET routes are not idempotent, but that should be fine def team_required(f): - @wraps(f) - def wrapped(*args, **kwargs): - request = args[0] - if request.user.team is None: - messages.error( - request, - _("Please join a team or choose to go solo before getting a hint."), - ) - return redirect(reverse("index")) - return f(*args, **kwargs) - - return wrapped + @wraps(f) + def wrapped(*args, **kwargs): + request = args[0] + if request.user.team is None: + messages.error( + request, + _("Please join a team or choose to go solo before getting a hint."), + ) + return redirect(reverse("index")) + return f(*args, **kwargs) + + return wrapped def after_start(f): - @wraps(f) - def wrapped(*args, **kwargs): - request = args[0] - if ( - not request.user.has_perm( - "core.view_before_start" - ) # or current_hunt. todo impl - and settings.START > datetime.datetime.now() - ): - messages.error( - request, - _("Contest has not started yet."), - ) - return redirect(reverse("index")) - return f(*args, **kwargs) - - return wrapped + hunt_ = Hunt.current_hunt() + """ + Decorator for views that checks that the hunt has started. + + User can access the view if they meet ANY of the following conditions: + - They have the view_before_start permission + - The hunt has started + - They are on the early access list for that hunt + - They are a superuser + """ + + @wraps(f) + def wrapped(*args, **kwargs): + request = args[0] + if any( + [ + request.user.has_perm("core.view_before_start"), + (hunt_.start < datetime.datetime.now() < hunt_.end), + hunt_.early_access_users.filter(user=request.user).exists(), + request.user.is_superuser, + ] + ): + return f(*args, **kwargs) + + # if they DON'T have perms and the hunt hasn't started + if hunt_.start > datetime.datetime.now(): + messages.error( + request, + _("Contest has not started yet."), + ) + else: + messages.error( + request, + _("Contest has ended."), + ) + return redirect(reverse("index")) + + return wrapped @login_required @@ -58,21 +79,22 @@ def wrapped(*args, **kwargs): @team_required @after_start def qr(request, key): - context = dict(first=False) - context["qr"] = qr = get_object_or_404(QrCode, key=key) - context["hunt"] = qr.hunt - codes = QrCode.code_pks(request.user.team) - if qr.id not in codes: - context["offpath"] = True - return render(request, "core/qr.html", context=context) - i = codes.index(qr.id) - context["nextqr"] = ( - None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) - ) - context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - # TODO: check if they skipped? - request.user.team.update_current_qr_i(i) - return render(request, "core/qr.html", context=context) + context = dict(first=False) + context["qr"] = qr = get_object_or_404(QrCode, key=key) + context["qr"]: QrCode + context["hunt"]: Hunt = qr.hunt + codes = QrCode.code_pks(request.user.team) + if qr.id not in codes: + context["offpath"] = True + return render(request, "core/qr.html", context=context) + i = codes.index(qr.id) + context["nextqr"] = ( + None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) + ) + context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) + # TODO: check if they skipped? + request.user.team.update_current_qr_i(i) + return render(request, "core/qr.html", context=context) @login_required @@ -80,16 +102,16 @@ def qr(request, key): @team_required @after_start def qr_first(request): - context = dict(first=True) - # check if the user is on the first qr code - # if request.user.team.current_qr_i != 0: - # messages.error(request, _("You are not on the first QR code.")) - # return redirect(reverse("qr_current")) - context["qr"] = QrCode.codes(request.user.team)[0] - codes = QrCode.code_pks(request.user.team) - context["nextqr"] = QrCode.objects.get(id=codes[0]) - context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - return render(request, "core/qr.html", context=context) + context = dict(first=True) + # check if the user is on the first qr code + # if request.user.team.current_qr_i != 0: + # messages.error(request, _("You are not on the first QR code.")) + # return redirect(reverse("qr_current")) + context["qr"] = QrCode.codes(request.user.team)[0] + codes = QrCode.code_pks(request.user.team) + context["nextqr"] = QrCode.objects.get(id=codes[0]) + context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) + return render(request, "core/qr.html", context=context) @login_required @@ -97,15 +119,15 @@ def qr_first(request): @team_required @after_start def qr_current(request): - i = request.user.team.current_qr_i - context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[request.user.team.current_qr_i] - codes = QrCode.code_pks(request.user.team) - context["nextqr"] = ( - None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) - ) - context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - return render(request, "core/qr.html", context=context) + i = request.user.team.current_qr_i + context = dict(first=i == 0, current=True) + context["qr"] = QrCode.codes(request.user.team)[request.user.team.current_qr_i] + codes = QrCode.code_pks(request.user.team) + context["nextqr"] = ( + None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) + ) + context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) + return render(request, "core/qr.html", context=context) @login_required @@ -113,10 +135,10 @@ def qr_current(request): @team_required @after_start def qr_catalog(request): - i = request.user.team.current_qr_i - context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[: request.user.team.current_qr_i] - return render(request, "core/qr_catalog.html", context=context) + i = request.user.team.current_qr_i + context = dict(first=i == 0, current=True) + context["qr"] = QrCode.codes(request.user.team)[: request.user.team.current_qr_i] + return render(request, "core/qr_catalog.html", context=context) global_notifs = Signal() @@ -124,44 +146,44 @@ def qr_catalog(request): @receiver(signals.post_save, sender=Team) def team_change(sender, **kwargs): - global_notifs.send("team_change", orig_sender=sender, kwargs=kwargs) + global_notifs.send("team_change", orig_sender=sender, kwargs=kwargs) class SignalStream: - def __init__(self, signal, pk: int): - self.signal = signal - self.pk = pk - self.q = LifoQueue() - self.end = False - self.__setup() - - def __setup(self): - self.signal.connect(self.__receive) - self.q.put(("init", {})) - - def __del__(self): - self.signal.disconnect(self.__receive) - - def __receive(self, sender, **kwargs): - self.q.put((sender, kwargs)) - - def __iter__(self): - return self - - def __next__(self): - while 1: - if self.end: - raise StopIteration - sender, kwargs = self.q.get() - if sender == "team_change": - team = kwargs["kwargs"]["instance"] - if self.pk == team.id: - self.end = True - return f"event: {sender}\ndata: null\n" - else: - continue - else: - return f"event: {sender}\ndata: null\n" + def __init__(self, signal, pk: int): + self.signal = signal + self.pk = pk + self.q = LifoQueue() + self.end = False + self.__setup() + + def __setup(self): + self.signal.connect(self.__receive) + self.q.put(("init", {})) + + def __del__(self): + self.signal.disconnect(self.__receive) + + def __receive(self, sender, **kwargs): + self.q.put((sender, kwargs)) + + def __iter__(self): + return self + + def __next__(self): + while 1: + if self.end: + raise StopIteration + sender, kwargs = self.q.get() + if sender == "team_change": + team = kwargs["kwargs"]["instance"] + if self.pk == team.id: + self.end = True + return f"event: {sender}\ndata: null\n" + else: + continue + else: + return f"event: {sender}\ndata: null\n" @login_required @@ -169,9 +191,9 @@ def __next__(self): @team_required @after_start def qr_signal(request): - s = StreamingHttpResponse( - SignalStream(signal=global_notifs, pk=request.user.team.id), - content_type="text/event-stream", - ) - s["Cache-Control"] = "no-cache" - return s + s = StreamingHttpResponse( + SignalStream(signal=global_notifs, pk=request.user.team.id), + content_type="text/event-stream", + ) + s["Cache-Control"] = "no-cache" + return s diff --git a/core/views/team.py b/core/views/team.py index 2ea014d..ba737a2 100644 --- a/core/views/team.py +++ b/core/views/team.py @@ -17,7 +17,8 @@ @login_required @require_http_methods(["GET", "POST"]) def join(request): - if settings.START < datetime.datetime.now() and request.user.team is not None: + hunt_ = Hunt.current_hunt() + if hunt_.start < datetime.datetime.now() and request.user.team is not None: messages.error( request, _( @@ -61,7 +62,8 @@ def join(request): @login_required @require_http_methods(["GET", "POST"]) def make(request): - if settings.START < datetime.datetime.now() and request.user.team is not None: + hunt_ = Hunt.current_hunt() + if hunt_.start < datetime.datetime.now() and request.user.team is not None: messages.error( request, _("Since the hunt has already begun, making new teams is disallowed."), diff --git a/wlmac-scavenger/settings.py b/wlmac-scavenger/settings.py index 87b2aec..d99f813 100644 --- a/wlmac-scavenger/settings.py +++ b/wlmac-scavenger/settings.py @@ -124,5 +124,3 @@ raise TypeError("local_settings.py not found") SECRET_KEY # type: ignore -START # type: ignore -END # type: ignore From c4c3745beaa264c302c42a25c1ac3eb0d97cd563 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 3 Dec 2023 05:04:17 +0000 Subject: [PATCH 04/36] format --- core/views/qr.py | 244 +++++++++++++++++++++++------------------------ 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/core/views/qr.py b/core/views/qr.py index e5ab6df..b5499bb 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -19,23 +19,23 @@ def team_required(f): - @wraps(f) - def wrapped(*args, **kwargs): - request = args[0] - if request.user.team is None: - messages.error( - request, - _("Please join a team or choose to go solo before getting a hint."), - ) - return redirect(reverse("index")) - return f(*args, **kwargs) - - return wrapped + @wraps(f) + def wrapped(*args, **kwargs): + request = args[0] + if request.user.team is None: + messages.error( + request, + _("Please join a team or choose to go solo before getting a hint."), + ) + return redirect(reverse("index")) + return f(*args, **kwargs) + + return wrapped def after_start(f): - hunt_ = Hunt.current_hunt() - """ + hunt_ = Hunt.current_hunt() + """ Decorator for views that checks that the hunt has started. User can access the view if they meet ANY of the following conditions: @@ -44,34 +44,34 @@ def after_start(f): - They are on the early access list for that hunt - They are a superuser """ - - @wraps(f) - def wrapped(*args, **kwargs): - request = args[0] - if any( - [ - request.user.has_perm("core.view_before_start"), - (hunt_.start < datetime.datetime.now() < hunt_.end), - hunt_.early_access_users.filter(user=request.user).exists(), - request.user.is_superuser, - ] - ): - return f(*args, **kwargs) - - # if they DON'T have perms and the hunt hasn't started - if hunt_.start > datetime.datetime.now(): - messages.error( - request, - _("Contest has not started yet."), - ) - else: - messages.error( - request, - _("Contest has ended."), - ) - return redirect(reverse("index")) - - return wrapped + + @wraps(f) + def wrapped(*args, **kwargs): + request = args[0] + if any( + [ + request.user.has_perm("core.view_before_start"), + (hunt_.start < datetime.datetime.now() < hunt_.end), + hunt_.early_access_users.filter(user=request.user).exists(), + request.user.is_superuser, + ] + ): + return f(*args, **kwargs) + + # if they DON'T have perms and the hunt hasn't started + if hunt_.start > datetime.datetime.now(): + messages.error( + request, + _("Contest has not started yet."), + ) + else: + messages.error( + request, + _("Contest has ended."), + ) + return redirect(reverse("index")) + + return wrapped @login_required @@ -79,22 +79,22 @@ def wrapped(*args, **kwargs): @team_required @after_start def qr(request, key): - context = dict(first=False) - context["qr"] = qr = get_object_or_404(QrCode, key=key) - context["qr"]: QrCode - context["hunt"]: Hunt = qr.hunt - codes = QrCode.code_pks(request.user.team) - if qr.id not in codes: - context["offpath"] = True - return render(request, "core/qr.html", context=context) - i = codes.index(qr.id) - context["nextqr"] = ( - None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) - ) - context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - # TODO: check if they skipped? - request.user.team.update_current_qr_i(i) - return render(request, "core/qr.html", context=context) + context = dict(first=False) + context["qr"] = qr = get_object_or_404(QrCode, key=key) + context["qr"]: QrCode + context["hunt"]: Hunt = qr.hunt + codes = QrCode.code_pks(request.user.team) + if qr.id not in codes: + context["offpath"] = True + return render(request, "core/qr.html", context=context) + i = codes.index(qr.id) + context["nextqr"] = ( + None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) + ) + context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) + # TODO: check if they skipped? + request.user.team.update_current_qr_i(i) + return render(request, "core/qr.html", context=context) @login_required @@ -102,16 +102,16 @@ def qr(request, key): @team_required @after_start def qr_first(request): - context = dict(first=True) - # check if the user is on the first qr code - # if request.user.team.current_qr_i != 0: - # messages.error(request, _("You are not on the first QR code.")) - # return redirect(reverse("qr_current")) - context["qr"] = QrCode.codes(request.user.team)[0] - codes = QrCode.code_pks(request.user.team) - context["nextqr"] = QrCode.objects.get(id=codes[0]) - context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - return render(request, "core/qr.html", context=context) + context = dict(first=True) + # check if the user is on the first qr code + # if request.user.team.current_qr_i != 0: + # messages.error(request, _("You are not on the first QR code.")) + # return redirect(reverse("qr_current")) + context["qr"] = QrCode.codes(request.user.team)[0] + codes = QrCode.code_pks(request.user.team) + context["nextqr"] = QrCode.objects.get(id=codes[0]) + context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) + return render(request, "core/qr.html", context=context) @login_required @@ -119,15 +119,15 @@ def qr_first(request): @team_required @after_start def qr_current(request): - i = request.user.team.current_qr_i - context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[request.user.team.current_qr_i] - codes = QrCode.code_pks(request.user.team) - context["nextqr"] = ( - None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) - ) - context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - return render(request, "core/qr.html", context=context) + i = request.user.team.current_qr_i + context = dict(first=i == 0, current=True) + context["qr"] = QrCode.codes(request.user.team)[request.user.team.current_qr_i] + codes = QrCode.code_pks(request.user.team) + context["nextqr"] = ( + None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) + ) + context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) + return render(request, "core/qr.html", context=context) @login_required @@ -135,10 +135,10 @@ def qr_current(request): @team_required @after_start def qr_catalog(request): - i = request.user.team.current_qr_i - context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[: request.user.team.current_qr_i] - return render(request, "core/qr_catalog.html", context=context) + i = request.user.team.current_qr_i + context = dict(first=i == 0, current=True) + context["qr"] = QrCode.codes(request.user.team)[: request.user.team.current_qr_i] + return render(request, "core/qr_catalog.html", context=context) global_notifs = Signal() @@ -146,44 +146,44 @@ def qr_catalog(request): @receiver(signals.post_save, sender=Team) def team_change(sender, **kwargs): - global_notifs.send("team_change", orig_sender=sender, kwargs=kwargs) + global_notifs.send("team_change", orig_sender=sender, kwargs=kwargs) class SignalStream: - def __init__(self, signal, pk: int): - self.signal = signal - self.pk = pk - self.q = LifoQueue() - self.end = False - self.__setup() - - def __setup(self): - self.signal.connect(self.__receive) - self.q.put(("init", {})) - - def __del__(self): - self.signal.disconnect(self.__receive) - - def __receive(self, sender, **kwargs): - self.q.put((sender, kwargs)) - - def __iter__(self): - return self - - def __next__(self): - while 1: - if self.end: - raise StopIteration - sender, kwargs = self.q.get() - if sender == "team_change": - team = kwargs["kwargs"]["instance"] - if self.pk == team.id: - self.end = True - return f"event: {sender}\ndata: null\n" - else: - continue - else: - return f"event: {sender}\ndata: null\n" + def __init__(self, signal, pk: int): + self.signal = signal + self.pk = pk + self.q = LifoQueue() + self.end = False + self.__setup() + + def __setup(self): + self.signal.connect(self.__receive) + self.q.put(("init", {})) + + def __del__(self): + self.signal.disconnect(self.__receive) + + def __receive(self, sender, **kwargs): + self.q.put((sender, kwargs)) + + def __iter__(self): + return self + + def __next__(self): + while 1: + if self.end: + raise StopIteration + sender, kwargs = self.q.get() + if sender == "team_change": + team = kwargs["kwargs"]["instance"] + if self.pk == team.id: + self.end = True + return f"event: {sender}\ndata: null\n" + else: + continue + else: + return f"event: {sender}\ndata: null\n" @login_required @@ -191,9 +191,9 @@ def __next__(self): @team_required @after_start def qr_signal(request): - s = StreamingHttpResponse( - SignalStream(signal=global_notifs, pk=request.user.team.id), - content_type="text/event-stream", - ) - s["Cache-Control"] = "no-cache" - return s + s = StreamingHttpResponse( + SignalStream(signal=global_notifs, pk=request.user.team.id), + content_type="text/event-stream", + ) + s["Cache-Control"] = "no-cache" + return s From b2889e9211911ee3445fe5452f1f3aaf4fd53cb6 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 3 Dec 2023 00:05:47 -0500 Subject: [PATCH 05/36] .idea then I sleep --- .idea/misc.xml | 4 +- .idea/scavenger.iml | 2 +- .idea/workspace.xml | 100 +++++++++++++++++++++++++++----------------- 3 files changed, 64 insertions(+), 42 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index b1ea2f1..995cec4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - - + + \ No newline at end of file diff --git a/.idea/scavenger.iml b/.idea/scavenger.iml index dbb060e..d1a04da 100644 --- a/.idea/scavenger.iml +++ b/.idea/scavenger.iml @@ -14,7 +14,7 @@ - + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c4e80ac..a7d0ce4 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,9 +4,7 @@ - - - + - { + "keyToString": { + "NIXITCH_NIXPKGS_CONFIG": "", + "NIXITCH_NIX_CONF_DIR": "", + "NIXITCH_NIX_OTHER_STORES": "", + "NIXITCH_NIX_PATH": "", + "NIXITCH_NIX_PROFILES": "", + "NIXITCH_NIX_REMOTE": "", + "NIXITCH_NIX_USER_PROFILE_DIR": "", + "RunOnceActivity.OpenProjectViewOnStart": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "SHARE_PROJECT_CONFIGURATION_FILES": "true", + "WebServerToolWindowFactoryState": "true", + "WebServerToolWindowPanel.toolwindow.highlight.mappings": "true", + "WebServerToolWindowPanel.toolwindow.highlight.symlinks": "true", + "WebServerToolWindowPanel.toolwindow.show.date": "false", + "WebServerToolWindowPanel.toolwindow.show.permissions": "false", + "WebServerToolWindowPanel.toolwindow.show.size": "false", + "git-widget-placeholder": "main", + "ignore.virus.scanning.warn.message": "true", + "last_opened_file_path": "D:/projects/metropolis/backend", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "settings.editor.selected.configurable": "Errors", + "ts.external.directory.path": "C:\\Users\\jason\\AppData\\Local\\Programs\\PyCharm Professional\\plugins\\javascript-impl\\jsLanguageServicesImpl\\external", + "vue.rearranger.settings.migration": "true" } -}]]> +} @@ -185,6 +182,7 @@ + - @@ -426,9 +448,6 @@ - - - @@ -451,7 +470,10 @@ - From 22e80302f76ce1e3936c623fb194b3f167cc0110 Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 3 Dec 2023 00:42:47 -0500 Subject: [PATCH 06/36] time sensitive + cleaner --- core/context_processors.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/context_processors.py b/core/context_processors.py index ace58e2..a2eeb47 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -2,15 +2,17 @@ from core.models import Hunt from django.conf import settings +from django.utils import timezone def start(request): hunt = Hunt.current_hunt() + now = timezone.now() return dict( START=(start := hunt.start), - START_BEFORE=start > datetime.datetime.utcnow(), - START_UNTIL=start - datetime.datetime.utcnow(), + START_BEFORE=start > now, + START_UNTIL=start - now, END=(end := hunt.end), - END_BEFORE=end > datetime.datetime.utcnow(), - END_UNTIL=end - datetime.datetime.utcnow(), + END_BEFORE=end > now, + END_UNTIL=end - now, ) From 5b189c0f451bba00dcafda289744aa6cef50d200 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 13:31:27 -0500 Subject: [PATCH 07/36] updated .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bc58bf4..66b2122 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ __pycache__ .direnv /db.sqlite3 /venv +.idea/workspace.xml From 775a010c7ad8e38ddd7ad7238e5dfbc2dfb274a9 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 13:39:34 -0500 Subject: [PATCH 08/36] updated .gitignore removes two .idea files --- .gitignore | 3 + .idea/dataSources.local.xml | 7 +- .idea/misc.xml | 7 - .idea/workspace.xml | 483 ------------------------------------ 4 files changed, 9 insertions(+), 491 deletions(-) delete mode 100644 .idea/misc.xml delete mode 100644 .idea/workspace.xml diff --git a/.gitignore b/.gitignore index 66b2122..098cd58 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ __pycache__ /db.sqlite3 /venv .idea/workspace.xml +.idea/misc.xml + +``` diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml index aed4b13..4daa519 100644 --- a/.idea/dataSources.local.xml +++ b/.idea/dataSources.local.xml @@ -2,7 +2,12 @@ - + + " + + + master_key + no-auth diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 995cec4..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index a7d0ce4..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,483 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { - "associatedIndex": 8 -} - - - - - { - "keyToString": { - "NIXITCH_NIXPKGS_CONFIG": "", - "NIXITCH_NIX_CONF_DIR": "", - "NIXITCH_NIX_OTHER_STORES": "", - "NIXITCH_NIX_PATH": "", - "NIXITCH_NIX_PROFILES": "", - "NIXITCH_NIX_REMOTE": "", - "NIXITCH_NIX_USER_PROFILE_DIR": "", - "RunOnceActivity.OpenProjectViewOnStart": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "SHARE_PROJECT_CONFIGURATION_FILES": "true", - "WebServerToolWindowFactoryState": "true", - "WebServerToolWindowPanel.toolwindow.highlight.mappings": "true", - "WebServerToolWindowPanel.toolwindow.highlight.symlinks": "true", - "WebServerToolWindowPanel.toolwindow.show.date": "false", - "WebServerToolWindowPanel.toolwindow.show.permissions": "false", - "WebServerToolWindowPanel.toolwindow.show.size": "false", - "git-widget-placeholder": "main", - "ignore.virus.scanning.warn.message": "true", - "last_opened_file_path": "D:/projects/metropolis/backend", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "settings.editor.selected.configurable": "Errors", - "ts.external.directory.path": "C:\\Users\\jason\\AppData\\Local\\Programs\\PyCharm Professional\\plugins\\javascript-impl\\jsLanguageServicesImpl\\external", - "vue.rearranger.settings.migration": "true" - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1701109954551 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From cfca466faaebbd15c7590807bfd4512667ec5262 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 13:45:22 -0500 Subject: [PATCH 09/36] Updates credits --- .idea/scavenger.iml | 4 +- core/templates/core/credits.html | 80 ++++++++++++++++---------------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/.idea/scavenger.iml b/.idea/scavenger.iml index d1a04da..533dad0 100644 --- a/.idea/scavenger.iml +++ b/.idea/scavenger.iml @@ -14,7 +14,7 @@ - + @@ -22,7 +22,7 @@ - - + diff --git a/core/migrations/0019_alter_qrcode_key_hunt_logicpuzzlehint_belongs_to_and_more.py b/core/migrations/0019_alter_qrcode_key_hunt_logicpuzzlehint_belongs_to_and_more.py index c61d716..695ff3e 100644 --- a/core/migrations/0019_alter_qrcode_key_hunt_logicpuzzlehint_belongs_to_and_more.py +++ b/core/migrations/0019_alter_qrcode_key_hunt_logicpuzzlehint_belongs_to_and_more.py @@ -31,7 +31,7 @@ def undo_create_hunt(apps, schema_editor): try: Hunt.objects.get(name="First Hunt").delete() - except Hunt.DoesNotExist: + except (Hunt.DoesNotExist, django.db.utils.OperationalError): pass diff --git a/core/migrations/0021_hunt_middle_locations.py b/core/migrations/0021_hunt_middle_locations.py new file mode 100644 index 0000000..7d37aa9 --- /dev/null +++ b/core/migrations/0021_hunt_middle_locations.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.13 on 2023-12-03 19:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0020_remove_hunt_form_in_ending_text_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='hunt', + name='middle_locations', + field=models.ManyToManyField(blank=True, help_text='Possible locations that are not the start or end', related_name='hunt', to='core.qrcode'), + ), + ] diff --git a/core/views/qr.py b/core/views/qr.py index b5499bb..2cb0e51 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -34,7 +34,6 @@ def wrapped(*args, **kwargs): def after_start(f): - hunt_ = Hunt.current_hunt() """ Decorator for views that checks that the hunt has started. @@ -47,6 +46,8 @@ def after_start(f): @wraps(f) def wrapped(*args, **kwargs): + hunt_ = Hunt.current_hunt() + request = args[0] if any( [ From e47586ae6a2f4cf818b9d56856bbc3fc31b930d1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 3 Dec 2023 19:08:38 +0000 Subject: [PATCH 11/36] format --- core/migrations/0021_hunt_middle_locations.py | 14 +++++++++----- core/views/qr.py | 18 +++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core/migrations/0021_hunt_middle_locations.py b/core/migrations/0021_hunt_middle_locations.py index 7d37aa9..9ca1f56 100644 --- a/core/migrations/0021_hunt_middle_locations.py +++ b/core/migrations/0021_hunt_middle_locations.py @@ -4,15 +4,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0020_remove_hunt_form_in_ending_text_and_more'), + ("core", "0020_remove_hunt_form_in_ending_text_and_more"), ] operations = [ migrations.AddField( - model_name='hunt', - name='middle_locations', - field=models.ManyToManyField(blank=True, help_text='Possible locations that are not the start or end', related_name='hunt', to='core.qrcode'), + model_name="hunt", + name="middle_locations", + field=models.ManyToManyField( + blank=True, + help_text="Possible locations that are not the start or end", + related_name="hunt", + to="core.qrcode", + ), ), ] diff --git a/core/views/qr.py b/core/views/qr.py index 2cb0e51..b050b75 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -35,19 +35,19 @@ def wrapped(*args, **kwargs): def after_start(f): """ - Decorator for views that checks that the hunt has started. - - User can access the view if they meet ANY of the following conditions: - - They have the view_before_start permission - - The hunt has started - - They are on the early access list for that hunt - - They are a superuser - """ + Decorator for views that checks that the hunt has started. + + User can access the view if they meet ANY of the following conditions: + - They have the view_before_start permission + - The hunt has started + - They are on the early access list for that hunt + - They are a superuser + """ @wraps(f) def wrapped(*args, **kwargs): hunt_ = Hunt.current_hunt() - + request = args[0] if any( [ From c1df39b58a9b77f3c9850953f03a11afa8566daf Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 15:32:18 -0500 Subject: [PATCH 12/36] Added some helper fields to Hunt formatted --- core/locale/en_CA/LC_MESSAGES/django.mo | Bin 337 -> 434 bytes core/locale/en_CA/LC_MESSAGES/django.po | 185 +++++++++++------- core/locale/ja_JP/LC_MESSAGES/django.mo | Bin 4248 -> 2914 bytes core/migrations/0021_hunt_middle_locations.py | 14 +- core/models.py | 30 ++- core/templates/core/base.html | 4 +- core/views/qr.py | 10 +- core/views/team.py | 2 +- 8 files changed, 156 insertions(+), 89 deletions(-) diff --git a/core/locale/en_CA/LC_MESSAGES/django.mo b/core/locale/en_CA/LC_MESSAGES/django.mo index 6c5906d1cd061dff54de8b533942893de34efc9e..5d3a7323185aeecba4ba0580f73a852a46437121 100644 GIT binary patch delta 200 zcmcb}w28U?o)F7a1|VPtVi_Pd0b*7l_5orLNC09kAWj5gP9V+!ViiUPhB6?{1H`j| zY>@a0APq!d05S^%m_UR=e0*wNN^w9@Dnn6fZenI$W?ni&Kv8~HYI2FL=R~J$Rj`2C0F8iKV3yZlOLt3Vx2h Yt_n7;zK)(g4vsD^L9W5Ulld8Y0R6ZPga7~l diff --git a/core/locale/en_CA/LC_MESSAGES/django.po b/core/locale/en_CA/LC_MESSAGES/django.po index b4692ff..68ce15a 100644 --- a/core/locale/en_CA/LC_MESSAGES/django.po +++ b/core/locale/en_CA/LC_MESSAGES/django.po @@ -8,89 +8,122 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-12-17 01:39+0000\n" +"POT-Creation-Date: 2023-12-03 14:46-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" +"Last-Translator: Jason Cameron \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: core/templates/core/base.html:22 -msgid "starts in:" +#: .\core\admin.py:14 +msgid "Set selected users as a Location Setter" msgstr "" -#: core/templates/core/base.html:23 -msgid "ends in:" +#: .\core\admin.py:25 +msgid "Set selected users as a Logic Puzzle Setter" msgstr "" -#: core/templates/core/base.html:24 +#: .\core\admin.py:117 +msgid "Link to Hint Page" +msgstr "" + +#: .\core\templates\core\base.html:22 +msgid "starts in" +msgstr "" + +#: .\core\templates\core\base.html:23 +msgid "__startsPost" +msgstr "" + +#: .\core\templates\core\base.html:24 +msgid " __endsPre" +msgstr "Hurry Hurry!" + +#: .\core\templates\core\base.html:25 +msgid "remaining" +msgstr "remaining" + +#: .\core\templates\core\base.html:26 msgid "scavenger hunt ended!" msgstr "" -#: core/templates/core/base.html:145 +#: .\core\templates\core\base.html:105 msgid "Home" msgstr "" -#: core/templates/core/base.html:152 +#: .\core\templates\core\base.html:112 msgid "Admin" msgstr "" -#: core/templates/core/base.html:158 +#: .\core\templates\core\base.html:117 msgid "current" msgstr "" -#: core/templates/core/base.html:163 core/templates/core/logout.html:6 +#: .\core\templates\core\base.html:121 .\core\templates\core\logout.html:6 msgid "Logout" msgstr "" -#: core/templates/core/credits.html:6 +#: .\core\templates\core\base.html:128 +msgid "Login" +msgstr "" + +#: .\core\templates\core\credits.html:6 msgid "Credits" msgstr "" -#: core/templates/core/credits.html:10 +#: .\core\templates\core\credits.html:10 msgid "credits" msgstr "" -#: core/templates/core/credits.html:14 -msgid "" -"\n" -"project manager: ApocalypseCalculator
\n" -"programming: nyiyui, JasonLovesDoggo, Jimmy Liu, cheollie, and Misheel " -"Batkhuu
\n" -"UI/UX design: cheollie
\n" -"support: project metropolis and " -"SAC
\n" +#: .\core\templates\core\credits.html:14 +msgid "project manager" msgstr "" -#: core/templates/core/credits.html:20 +#: .\core\templates\core\credits.html:19 +msgid "programming" +msgstr "" + +#: .\core\templates\core\credits.html:28 +msgid "content" +msgstr "" + +#: .\core\templates\core\credits.html:34 +msgid "UI/UX design" +msgstr "" + +#: .\core\templates\core\credits.html:41 msgid "hints" msgstr "" -#: core/templates/core/gate.html:6 core/templates/core/index.html:6 +#: .\core\templates\core\credits.html:48 +msgid "support" +msgstr "" + +#: .\core\templates\core\gate.html:6 .\core\templates\core\index.html:6 msgid "Scavenger Hunt" msgstr "" -#: core/templates/core/gate.html:11 +#: .\core\templates\core\gate.html:11 msgid "The hunt has not begun yet." msgstr "" -#: core/templates/core/gate.html:14 +#: .\core\templates\core\gate.html:14 #, python-format msgid "" "\n" "Please login.\n" msgstr "" -#: core/templates/core/index.html:11 +#: .\core\templates\core\index.html:10 #, python-format msgid "" "\n" "welcome, %(username)s!\n" msgstr "" -#: core/templates/core/index.html:23 +#: .\core\templates\core\index.html:23 #, python-format msgid "" "\n" @@ -99,23 +132,23 @@ msgid "" " " msgstr "" -#: core/templates/core/index.html:29 +#: .\core\templates\core\index.html:29 #, python-format msgid "" "\n" -" or, do you still want to invite team " +" or, would you like to invite more team " "members?\n" " " msgstr "" -#: core/templates/core/index.html:36 +#: .\core\templates\core\index.html:37 msgid "" "\n" " you're going solo\n" " " msgstr "" -#: core/templates/core/index.html:40 +#: .\core\templates\core\index.html:41 #, python-format msgid "" "\n" @@ -123,125 +156,127 @@ msgid "" " " msgstr "" -#: core/templates/core/index.html:48 core/templates/core/team_join.html:10 +#: .\core\templates\core\index.html:50 .\core\templates\core\team_join.html:10 msgid "join a team" msgstr "" -#: core/templates/core/index.html:51 core/templates/core/team_new.html:10 +#: .\core\templates\core\index.html:53 .\core\templates\core\team_new.html:10 msgid "make a team" msgstr "" -#: core/templates/core/index.html:57 +#: .\core\templates\core\index.html:60 msgid "go solo " msgstr "" -#: core/templates/core/logic_hints.html:8 -msgid "Logic Clues" -msgstr "" - -#: core/templates/core/logic_hints.html:13 -msgid "All of your logic clues so far" -msgstr "" - -#: core/templates/core/logout.html:10 core/templates/core/logout.html:16 +#: .\core\templates\core\logout.html:10 .\core\templates\core\logout.html:16 msgid "logout" msgstr "" -#: core/templates/core/qr.html:10 +#: .\core\templates\core\qr.html:10 msgid "Hint" msgstr "" -#: core/templates/core/qr.html:32 +#: .\core\templates\core\qr.html:33 msgid "your team found:" msgstr "" -#: core/templates/core/qr.html:36 +#: .\core\templates\core\qr.html:37 msgid "change" msgstr "" -#: core/templates/core/qr.html:44 +#: .\core\templates\core\qr.html:46 msgid "your first hint is:" msgstr "" -#: core/templates/core/qr.html:46 +#: .\core\templates\core\qr.html:48 msgid "your next hint is:" msgstr "" -#: core/templates/core/qr.html:68 -msgid "thank you for playing!" +#: .\core\templates\core\qr.html:72 +msgid "" +"Oh ho? What lieth there? Tis the light at the end of the tunnel!
" +"Congratulations valiant scavenger and thank you for playing!" msgstr "" -#: core/templates/core/team_invite.html:7 +#: .\core\templates\core\team_invite.html:7 msgid "Invite Members" msgstr "" -#: core/templates/core/team_invite.html:11 +#: .\core\templates\core\team_invite.html:11 msgid "invite members" msgstr "" -#: core/templates/core/team_invite.html:28 +#: .\core\templates\core\team_invite.html:33 #, python-format msgid "" "\n" -" either you can\n" -" ,\n" -" or let them scan the QR code below:\n" -" " +" you may either\n" +" ,\n" +" share via invite code %(code)s,\n" +" or allow them to scan the QR code below:\n" +" " msgstr "" -#: core/templates/core/team_join.html:6 +#: .\core\templates\core\team_join.html:6 msgid "Join a Team" msgstr "" -#: core/templates/core/team_join.html:14 +#: .\core\templates\core\team_join.html:14 msgid "either enter your team's join code" msgstr "" -#: core/templates/core/team_join.html:18 +#: .\core\templates\core\team_join.html:18 msgid "Join" msgstr "" -#: core/templates/core/team_join.html:20 +#: .\core\templates\core\team_join.html:20 msgid "or, scan your team's QR code" msgstr "" -#: core/templates/core/team_new.html:6 +#: .\core\templates\core\team_new.html:6 msgid "Make a Team" msgstr "" -#: core/templates/core/team_new.html:27 -msgid "make" +#: .\core\templates\core\team_new.html:24 +msgid "create" msgstr "" -#: core/views/qr.py:28 +#: .\core\views\qr.py:28 msgid "Please join a team or choose to go solo before getting a hint." msgstr "" -#: core/views/qr.py:46 +#: .\core\views\qr.py:66 msgid "Contest has not started yet." msgstr "" -#: core/views/team.py:23 -msgid "Since the hunt has already begun, switching teams is disallowed." +#: .\core\views\qr.py:71 +msgid "Contest has ended." +msgstr "" + +#: .\core\views\team.py:25 +msgid "" +"Since the hunt has already begun, switching teams is disallowed. If you need " +"to switch teams, please contact an admin." msgstr "" -#: core/views/team.py:40 +#: .\core\views\team.py:45 #, python-format msgid "Joined team %(team_name)s" msgstr "" -#: core/views/team.py:46 +#: .\core\views\team.py:51 #, python-format msgid "Team %(team_name)s is full." msgstr "" -#: core/views/team.py:63 +#: .\core\views\team.py:69 msgid "Since the hunt has already begun, making new teams is disallowed." msgstr "" -#: core/views/team.py:76 +#: .\core\views\team.py:85 #, python-format msgid "Made team %(team_name)s" msgstr "" diff --git a/core/locale/ja_JP/LC_MESSAGES/django.mo b/core/locale/ja_JP/LC_MESSAGES/django.mo index e894a2b456603d28f691cc44c80869ab9d0dca01..d9e955c4addfd8c766916811e71e52d85a6fcbcf 100644 GIT binary patch delta 1050 zcmXZaO-NKx6u|K_sWW5Rj9K|zX?~K>BC;$fidwWtQo=<=P1882eT*YSF5o=f|cfXpix#b0F>EB`yzuW!_o}%Bv;G;N-`rZfBaeq+r z{ImViwIV6~qmqVBT#K5p1-E0n?MIL?l0Z#x)jDpyk9&DOhx*_?Z*LEEj=hBV!R$a(e z+b**f%8Avz&;zHT`>3I-Y~<3f)u`gqQ>zDwD!T`E9cpOhIGe)9x=~>!@{XH8!SL*m zx7uSq`)cY#p=hip(HW0QD3nNsaIU%F^R| zOXFiQ|2nsr9XAERBj#lApqUDGG&t!gCzEwXae-pq zTB&Q?+1SjVk!HX=Fh=9F#e?;zX{%r z@mnB%?63G+1O5~21@D58K5!J=0v-iF1-=Q={J((sv43dZbSq=+7~c-!$1<8<0?EED zAnAV?yd4|`$v#7m$3e2+13wNP0#o1%;9cN(@T1@_z&pX;fgc9{46Z>;-_heWSac8G zcYwbIw}IQib|@9#1b9FAE0E%N>jxO);NL*<>+TOG>p4jNJqX?gZqwWelKyXiG~WR? zfFbxHa6ykRfM3V>Es*qYg7Z7U2&DPH0crg^AnCaUi)sBG_*)O&qsN1qUjxYw3ncx% z<_t)3=Jfa!_+^YQf^dob3B-^6UB6EuXjC^_L5lM?!Ow#}h%mCx;6~{wn>a`^Xg=kT z>a%`*77uInfJzzRRBnj@^BAUqCHJZCuiV~^#O!hi?mlGxln76yUqwDU>B4l&HRr837F z{y5Zf+j1t_xn*u{_X~z6(qjQ;nnqxxlW`!P2=uqri_|OJ#GtFQxHY&!$>7)=PsUeO~J>6?M`LY%(~mI*Tqd&x#S0yZSyI^!E%@P7`)(# zaZ+VDlU5-1hMo;wy~#)giWfz3On81zx6%7xi&*lw5JFDD@VOrtULedigr1us=aowV zsqa(3Bud{a1?oLg+~Z8#zV&n>yOr2?%K@9yi@@>pt0*q9EB)^>!x z@EoHkI{mg3>oegN>vx?%_(>`pH>jtBmqn0a{hlzbz-QYLY_=Uj*r3WaA5Id>9zm6< zKVc>rPda07qJ#~(c{dE$uz}*Dj?s-%|E#m7ir+{kpgwa2*F`Q-W2gw~MAYTDOT8(A zfVvZv2YWJXx8>w8ueF#A+Y^Ra#)^FCbRoqPw7?NlO1jT2pPQC%*r;=1{x=f*DJ#f9 zUH;t#j9ljwsTdD!JHv7X!^sPl(=p72o+l7Sy-DCP*(#DfWu2ew0n~>~I3`SWvaB8e zMh^Q-x0dcI))F@cPlftJbSTn{8do*A>Lq#w6}r(GK1>Q2=Ot|jeP}?YIx$fk!1f8% z^@uz#*+v;5Xk$uNv;W|0HhJe@Yf@QhzEb%fcSFa_vK>jwr3cOQ9_$B}i@M*@*Z-Bi zZ3BGIz^>hck3E{&C8+Mx!+zc}(+`DtKRx1Rd1}XF>0M&dqBZHQMj*0$i!G0`GfiZqEfrj(ITTc z8PCdSR!&D(UYmXUrDHNWdu8dV+L?vweq;bPz@dZ6o}b7V-abzjMn`3|NLh+2aFL~I z^DoQj)D4dnSy`Y}m8T)CHhaAK`hMEaWgJ((y;M8#0$C6rkkc_j7(FH9gEBfKqm$Q; z&&W7}Vb_k#R$rW5z7(}6SaGV<=BKL%PQ0t-iK4^R%g-o_7a;|iXr6y7jv7j5d>*-k zwQ8aE^HtdPe|Y$`o;gSZ?Hn{oQPpx_BAA*mzmHutde$?23q?z7emM zxWHa0I0(_TBj;5Q?Q58NYi1y8X~Hv(KrWIOKHeT zbVpRnH;NEcD^<0n>Dq~Ni2?O2*N;N<8d=|x%ti{&>fE&K8rA*iZnQLQG3@FO<10%u zwYm7(k!NVzSQTJc)vS<)(0W}o+-%eUOJde^1}VJm)&IDgX&dGAJZ%)o#?xx>zIuTl zBDW~pv5cRjT`PJC$;P6N^=}+oe(gtZ99!R+S-!lG$*%w7+yZ(;?@$dNk!OklHd^dk TH+b6;NUBY$-tQ)*OH2O+HEIRX diff --git a/core/migrations/0021_hunt_middle_locations.py b/core/migrations/0021_hunt_middle_locations.py index 7d37aa9..9ca1f56 100644 --- a/core/migrations/0021_hunt_middle_locations.py +++ b/core/migrations/0021_hunt_middle_locations.py @@ -4,15 +4,19 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0020_remove_hunt_form_in_ending_text_and_more'), + ("core", "0020_remove_hunt_form_in_ending_text_and_more"), ] operations = [ migrations.AddField( - model_name='hunt', - name='middle_locations', - field=models.ManyToManyField(blank=True, help_text='Possible locations that are not the start or end', related_name='hunt', to='core.qrcode'), + model_name="hunt", + name="middle_locations", + field=models.ManyToManyField( + blank=True, + help_text="Possible locations that are not the start or end", + related_name="hunt", + to="core.qrcode", + ), ), ] diff --git a/core/models.py b/core/models.py index c7039a4..57f3984 100644 --- a/core/models.py +++ b/core/models.py @@ -230,12 +230,40 @@ def __str__(self): return self.name @classmethod - def current_hunt(cls): + def lastest_hunt(cls) -> Hunt | None: + """ + Returns the latest hunt, ended or not. + :return: Hunt or None + """ + try: + Hunt.objects.filter(start__lte=timezone.now()).order_by("start").last() + except IndexError: + return None + + @classmethod + def current_hunt(cls) -> Hunt | None: try: return cls.objects.get(start__lte=timezone.now(), end__gte=timezone.now()) except cls.DoesNotExist: return None + @classmethod + def next_hunt(cls) -> Hunt | None: + try: + return ( + cls.objects.filter(start__gte=timezone.now()).order_by("start").first() + ) + except IndexError: + return None + + @property + def started(self): + return self.start < timezone.now() + + @property + def ended(self): + return self.end < timezone.now() + def clean(self): """ Due to how this was designed, it is not possible to have multiple hunts running at the same time. diff --git a/core/templates/core/base.html b/core/templates/core/base.html index f0e069e..b5a8a29 100644 --- a/core/templates/core/base.html +++ b/core/templates/core/base.html @@ -20,8 +20,8 @@ {{ START.timestamp }} * 1000, {{ END.timestamp }} * 1000, "{% translate 'starts in' %}", - "{% translate '__startsPost' %}", - "{% translate '__endsPre' %}", + "{% translate '' %}", + "{% translate '' %}", "{% translate 'remaining' %}", "{% translate 'scavenger hunt ended!' %}", ) diff --git a/core/views/qr.py b/core/views/qr.py index 2cb0e51..16856c4 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -51,24 +51,24 @@ def wrapped(*args, **kwargs): request = args[0] if any( [ + hunt_.started and not hunt_.ended, request.user.has_perm("core.view_before_start"), - (hunt_.start < datetime.datetime.now() < hunt_.end), - hunt_.early_access_users.filter(user=request.user).exists(), + hunt_.early_access_users.contains(request.user), request.user.is_superuser, ] ): return f(*args, **kwargs) # if they DON'T have perms and the hunt hasn't started - if hunt_.start > datetime.datetime.now(): + if hunt_.ended: messages.error( request, - _("Contest has not started yet."), + _("Contest has ended."), ) else: messages.error( request, - _("Contest has ended."), + _("Contest has not started yet."), ) return redirect(reverse("index")) diff --git a/core/views/team.py b/core/views/team.py index ba737a2..3d820c0 100644 --- a/core/views/team.py +++ b/core/views/team.py @@ -18,7 +18,7 @@ @require_http_methods(["GET", "POST"]) def join(request): hunt_ = Hunt.current_hunt() - if hunt_.start < datetime.datetime.now() and request.user.team is not None: + if hunt_.started and request.user.team is not None: messages.error( request, _( From 9c653749c89dc9a4c92a13fe6903eb6b38a715d1 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 15:36:18 -0500 Subject: [PATCH 13/36] reamed after_start to during_hunt to reflect it's internal changes --- core/views/puzzle.py | 4 ++-- core/views/qr.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/views/puzzle.py b/core/views/puzzle.py index 6c2defb..c352d84 100644 --- a/core/views/puzzle.py +++ b/core/views/puzzle.py @@ -2,14 +2,14 @@ from django.shortcuts import render from django.views.decorators.http import require_http_methods -from .qr import team_required, after_start +from .qr import team_required, during_hunt from ..models import LogicPuzzleHint @login_required @require_http_methods(["GET", "POST"]) @team_required -@after_start +@during_hunt def logic_clues(request): context = dict(logic_clues=LogicPuzzleHint.get_clues(request.user.team)) return render(request, "core/logic_hints.html", context=context) diff --git a/core/views/qr.py b/core/views/qr.py index 16856c4..bafbbbb 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -33,7 +33,7 @@ def wrapped(*args, **kwargs): return wrapped -def after_start(f): +def during_hunt(f): """ Decorator for views that checks that the hunt has started. @@ -78,7 +78,7 @@ def wrapped(*args, **kwargs): @login_required @require_http_methods(["GET", "POST"]) @team_required -@after_start +@during_hunt def qr(request, key): context = dict(first=False) context["qr"] = qr = get_object_or_404(QrCode, key=key) @@ -101,7 +101,7 @@ def qr(request, key): @login_required @require_http_methods(["GET", "POST"]) @team_required -@after_start +@during_hunt def qr_first(request): context = dict(first=True) # check if the user is on the first qr code @@ -118,7 +118,7 @@ def qr_first(request): @login_required @require_http_methods(["GET", "POST"]) @team_required -@after_start +@during_hunt def qr_current(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) @@ -134,7 +134,7 @@ def qr_current(request): @login_required @require_http_methods(["GET", "POST"]) @team_required -@after_start +@during_hunt def qr_catalog(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) @@ -190,7 +190,7 @@ def __next__(self): @login_required @require_http_methods(["GET"]) @team_required -@after_start +@during_hunt def qr_signal(request): s = StreamingHttpResponse( SignalStream(signal=global_notifs, pk=request.user.team.id), From f643162b13c40b4804b8d3b9f61ae118cc2ecfad Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 15:39:25 -0500 Subject: [PATCH 14/36] renamed Hunt.early_access_users to Hunt.testers --- core/models.py | 6 +++--- core/views/qr.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/models.py b/core/models.py index 57f3984..5b9b252 100644 --- a/core/models.py +++ b/core/models.py @@ -210,10 +210,10 @@ class Hunt(models.Model): help_text="Possible locations that are not the start or end", blank=True, ) - early_access_users = models.ManyToManyField( + testers = models.ManyToManyField( User, - related_name="early_access_users", - help_text="Users that can access this hunt before it starts", + related_name="testers", + help_text="Users that can access this hunt before it starts as well as after", blank=True, ) form_url = models.URLField( diff --git a/core/views/qr.py b/core/views/qr.py index bafbbbb..25bf01a 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -52,8 +52,8 @@ def wrapped(*args, **kwargs): if any( [ hunt_.started and not hunt_.ended, - request.user.has_perm("core.view_before_start"), - hunt_.early_access_users.contains(request.user), + request.user.has_perm("core.view_before_start") and not hunt_.ended, + hunt_.testers.contains(request.user), request.user.is_superuser, ] ): From 1aec86c111ac1a1ceccf94d692212bf480b29074 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 15:46:43 -0500 Subject: [PATCH 15/36] add manual migration (untested) fix docs. --- ...ve_hunt_early_access_users_hunt_testers.py | 28 +++++++++++++++++++ core/views/qr.py | 8 +++--- 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 core/migrations/0022_remove_hunt_early_access_users_hunt_testers.py diff --git a/core/migrations/0022_remove_hunt_early_access_users_hunt_testers.py b/core/migrations/0022_remove_hunt_early_access_users_hunt_testers.py new file mode 100644 index 0000000..f830316 --- /dev/null +++ b/core/migrations/0022_remove_hunt_early_access_users_hunt_testers.py @@ -0,0 +1,28 @@ +# Generated by Django 4.1.13 on 2023-12-03 20:44 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0021_hunt_middle_locations"), + ] + + operations = [ + migrations.RenameField( + model_name="hunt", + old_name="early_access_users", + new_name="testers", + ), + migrations.AlterField( + model_name="hunt", + name="testers", + field=models.ManyToManyField( + blank=True, + help_text="Users that can access this hunt before it starts as well as after", + related_name="testers", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/core/views/qr.py b/core/views/qr.py index 1d4fe15..2337bbe 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -35,12 +35,12 @@ def wrapped(*args, **kwargs): def during_hunt(f): """ - Decorator for views that checks that the hunt has started. + Decorator for views that checks that the hunt is currently active. User can access the view if they meet ANY of the following conditions: - - They have the view_before_start permission - - The hunt has started - - They are on the early access list for that hunt + - They have the view_before_start permission and the hunt hasn't ended + - The hunt has started and hasn't ended + - They are on the testers list for that hunt - They are a superuser """ From 86ca83373a3dcd2f202ce3ab3d3d0486ccd2fa35 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 22:05:29 -0600 Subject: [PATCH 16/36] add scavenger.iml to gitignore since I have different configs on win/linux --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 098cd58..4f9dc78 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,5 @@ __pycache__ /venv .idea/workspace.xml .idea/misc.xml - +.idea/scavenger.iml ``` From 29a39ea8c11d531531b6a368abd57920e42679e6 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 22:19:19 -0600 Subject: [PATCH 17/36] Fixed incorrect ternary op added the logic for hunts in context_processors.py add scavenger.iml to gitignore since I have different configs on win/linux --- .idea/scavenger.iml | 2 +- core/context_processors.py | 26 +++++++++++++++++++------- core/static/core/countdown.js | 4 ++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.idea/scavenger.iml b/.idea/scavenger.iml index c370e70..7a335b4 100644 --- a/.idea/scavenger.iml +++ b/.idea/scavenger.iml @@ -14,7 +14,7 @@
- + diff --git a/core/context_processors.py b/core/context_processors.py index a2eeb47..dc99249 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -1,18 +1,30 @@ import datetime from core.models import Hunt -from django.conf import settings from django.utils import timezone def start(request): hunt = Hunt.current_hunt() now = timezone.now() + if hunt is not None: + event_start = hunt.start + event_end = hunt.end + elif Hunt.next_hunt() is not None: + hunt = Hunt.next_hunt() + event_start = hunt.start + event_end = hunt.end + else: + print("WARNING: No hunt is scheduled") + print("Please add a hunt in the future to the database") + event_start = timezone.now() + datetime.timedelta(weeks=75) + event_end = event_start + datetime.timedelta(hours=3) + return dict( - START=(start := hunt.start), - START_BEFORE=start > now, - START_UNTIL=start - now, - END=(end := hunt.end), - END_BEFORE=end > now, - END_UNTIL=end - now, + START=event_start, + START_BEFORE=event_start > now, + START_UNTIL=event_start - now, + END=event_end, + END_BEFORE=event_end > now, + END_UNTIL=event_end - now, ) diff --git a/core/static/core/countdown.js b/core/static/core/countdown.js index 1af3968..5373540 100644 --- a/core/static/core/countdown.js +++ b/core/static/core/countdown.js @@ -49,8 +49,8 @@ const newCalculateTime = (start, end, startsPre, startsPost, endsPre, endsPost, startsPost = startsPost.startsWith('__') ? '' : startsPost endsPre = endsPre.endsWith('__') ? '' : endsPre endsPost = endsPost.endsWith('__') ? '' : endsPost - eTextPre.innerText = started ? (finished ? ended : endsPre) : startsPre - eTextPost.innerText = started ? (finished ? ended : endsPost) : startsPost + eTextPre.innerText = started ? startsPre : (finished ? ended : endsPre) + eTextPost.innerText = started ? startsPost : (finished ? ended : endsPost) } export default newCalculateTime From 2c4ac15383c676efdecdf9afc0ed4b13c966ccb6 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 22:22:08 -0600 Subject: [PATCH 18/36] adds black. --- poetry.lock | 131 +++++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 1 + 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 339a307..aaacb38 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. [[package]] name = "asgiref" @@ -59,6 +59,48 @@ files = [ [package.extras] tzdata = ["tzdata"] +[[package]] +name = "black" +version = "23.11.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, + {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, + {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, + {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, + {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, + {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, + {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, + {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, + {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, + {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, + {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, + {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, + {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, + {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, + {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, + {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, + {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, + {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "certifi" version = "2023.11.17" @@ -233,6 +275,31 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + [[package]] name = "cryptography" version = "41.0.7" @@ -373,6 +440,54 @@ files = [ {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, ] +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pathspec" +version = "0.11.2" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, +] + +[[package]] +name = "platformdirs" +version = "4.0.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, + {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + [[package]] name = "pycparser" version = "2.21" @@ -421,6 +536,17 @@ dev = ["build", "flake8"] doc = ["sphinx"] test = ["pytest", "pytest-cov"] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + [[package]] name = "typing-extensions" version = "4.8.0" @@ -462,5 +588,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "9f7ca6a088c6b910bc55cc7a9368ce083a9279353d61dffbfb277e44472113bf" - +content-hash = "94092c6d189cacb49a082d696195c49bcb804654f76ed0f434eaf6b8cbaebb9b" diff --git a/pyproject.toml b/pyproject.toml index af8bb4f..d963f5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ fontawesomefree = "^6.2.1" mistune = "^3.0.2" django-ckeditor = "^6.0.0" django-impersonate = "^1.9.2" +black = "^23.11.0" [tool.poetry.dev-dependencies] [build-system] From 4e0506e1f04870a5f7c34e155687c8f4e113f289 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Sun, 3 Dec 2023 23:52:28 -0600 Subject: [PATCH 19/36] added dynamic hunt naming (dw i'll clean up chunky context) Slight changes to translations --- core/templates/core/base.html | 2 +- core/templates/core/index.html | 2 +- core/views/index.py | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/templates/core/base.html b/core/templates/core/base.html index b5a8a29..59e9775 100644 --- a/core/templates/core/base.html +++ b/core/templates/core/base.html @@ -21,7 +21,7 @@ {{ END.timestamp }} * 1000, "{% translate 'starts in' %}", "{% translate '' %}", - "{% translate '' %}", + "{% translate 'Only' %}", "{% translate 'remaining' %}", "{% translate 'scavenger hunt ended!' %}", ) diff --git a/core/templates/core/index.html b/core/templates/core/index.html index 9afc798..e5c0b3c 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -21,7 +21,7 @@ {% if request.user.in_team and not START_BEFORE %} {% url 'qr_first' as first_url %} {% blocktranslate %} - the hunt has started! go decrypt your first hint! + {{ hunt_name }} has started! go decrypt your first hint! {% endblocktranslate %}
{% if not request.user.team.solo %} diff --git a/core/views/index.py b/core/views/index.py index db5a8f2..5df8582 100644 --- a/core/views/index.py +++ b/core/views/index.py @@ -3,15 +3,18 @@ from django.contrib.auth.models import Group from django.views.decorators.http import require_http_methods -from ..models import User +from ..models import User, Hunt @require_http_methods(["GET"]) def index(q): + hunt = Hunt.current_hunt() or Hunt.next_hunt() + context = dict(first=False) + context["hunt_name"] = hunt or "The hunt" return render( q, "core/index.html" if q.user.is_authenticated else "core/gate.html", - {}, + context, ) From 5dc0256e22981b4aa1af534162e082d02a1dcae4 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 12:21:43 -0500 Subject: [PATCH 20/36] Updated usages to support joining a team before the event started feat: added a @upcoming_hunt_required decorator that confirms .upcoming or .current exists (todo cache?) --- core/context_processors.py | 5 ++--- core/views/qr.py | 24 ++++++++++++++++++++++-- core/views/team.py | 17 +++++++++++------ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/core/context_processors.py b/core/context_processors.py index dc99249..ade5f7f 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -5,9 +5,8 @@ def start(request): - hunt = Hunt.current_hunt() now = timezone.now() - if hunt is not None: + if hunt := Hunt.current_hunt() is not None: event_start = hunt.start event_end = hunt.end elif Hunt.next_hunt() is not None: @@ -17,7 +16,7 @@ def start(request): else: print("WARNING: No hunt is scheduled") print("Please add a hunt in the future to the database") - event_start = timezone.now() + datetime.timedelta(weeks=75) + event_start = now + datetime.timedelta(weeks=75) event_end = event_start + datetime.timedelta(hours=3) return dict( diff --git a/core/views/qr.py b/core/views/qr.py index 2337bbe..c47b004 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -32,6 +32,19 @@ def wrapped(*args, **kwargs): return wrapped +def upcoming_hunt_required(f): + @wraps(f) + def wrapped(*args, **kwargs): + request = args[0] + if Hunt.current_hunt() is None and Hunt.next_hunt() is None: + messages.error( + request, + _("No hunts are in the database, please contact an admin.") + ) + return redirect(reverse("index")) + return f(*args, **kwargs) # todo: maybe return the hunt object to be used in the view? (more efficient) + + return wrapped def during_hunt(f): """ @@ -45,10 +58,17 @@ def during_hunt(f): """ @wraps(f) + @upcoming_hunt_required def wrapped(*args, **kwargs): - hunt_ = Hunt.current_hunt() - + hunt_ = Hunt.current_hunt() or Hunt.next_hunt() request = args[0] + if hunt_ is None: + messages.error( + request, + _("No hunt is currently active."), + ) + return redirect(reverse("index")) + if any( [ hunt_.started and not hunt_.ended, diff --git a/core/views/team.py b/core/views/team.py index 3d820c0..091a971 100644 --- a/core/views/team.py +++ b/core/views/team.py @@ -11,13 +11,14 @@ from ..models import Team, Invite, generate_invite_code, Hunt from ..forms import TeamJoinForm, TeamMakeForm -from .qr import team_required +from .qr import team_required, upcoming_hunt_required @login_required @require_http_methods(["GET", "POST"]) +@upcoming_hunt_required def join(request): - hunt_ = Hunt.current_hunt() + hunt_ = Hunt.current_hunt() or Hunt.next_hunt() if hunt_.started and request.user.team is not None: messages.error( request, @@ -61,9 +62,10 @@ def join(request): @login_required @require_http_methods(["GET", "POST"]) +@upcoming_hunt_required def make(request): - hunt_ = Hunt.current_hunt() - if hunt_.start < datetime.datetime.now() and request.user.team is not None: + hunt_ = Hunt.current_hunt() or Hunt.next_hunt() + if hunt_.started and request.user.team is not None: messages.error( request, _("Since the hunt has already begun, making new teams is disallowed."), @@ -73,7 +75,7 @@ def make(request): form = TeamMakeForm(request.POST) if form.is_valid(): raw: Team = form.save(commit=False) - raw.hunt = Hunt.current_hunt() + raw.hunt = Hunt.current_hunt() or Hunt.next_hunt() raw.save() request.user.team = raw request.user.save() @@ -92,9 +94,11 @@ def make(request): @login_required +@upcoming_hunt_required def solo(q: HttpRequest): + hunt_ = Hunt.current_hunt() or Hunt.next_hunt() team = Team.objects.create( - solo=True, hunt=Hunt.current_hunt(), name=f"{q.user.username}'s Solo Team" + solo=True, hunt=hunt_, name=f"{q.user.username}'s Solo Team" ) q.user.team = team q.user.save() @@ -104,6 +108,7 @@ def solo(q: HttpRequest): @login_required @require_http_methods(["GET"]) @team_required +@upcoming_hunt_required # redundant def invite(q): invites = Invite.objects.filter(team=q.user.team).values_list("code", flat=True) if invites.count() == 0: From c3c008a6bd7384930f046cb348c479a1dcf5f2ba Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Dec 2023 17:22:53 +0000 Subject: [PATCH 21/36] format --- core/views/qr.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/views/qr.py b/core/views/qr.py index c47b004..b8b1805 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -32,20 +32,23 @@ def wrapped(*args, **kwargs): return wrapped + def upcoming_hunt_required(f): @wraps(f) def wrapped(*args, **kwargs): request = args[0] if Hunt.current_hunt() is None and Hunt.next_hunt() is None: messages.error( - request, - _("No hunts are in the database, please contact an admin.") + request, _("No hunts are in the database, please contact an admin.") ) return redirect(reverse("index")) - return f(*args, **kwargs) # todo: maybe return the hunt object to be used in the view? (more efficient) + return f( + *args, **kwargs + ) # todo: maybe return the hunt object to be used in the view? (more efficient) return wrapped + def during_hunt(f): """ Decorator for views that checks that the hunt is currently active. From 33b493c371c86abae2011a5da970ce8af0d1b974 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 13:35:43 -0500 Subject: [PATCH 22/36] docs: improved docs for starting, middle and ending locations. docs: added docs for how alg works based fixed alg for the new Hunt system. added some warnings so ppl (admins) don't get confused allows start/end to be optional --- core/admin.py | 24 ++++++++++++++++++++++-- core/models.py | 45 ++++++++++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/core/admin.py b/core/admin.py index 960cf81..215583b 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,4 +1,4 @@ -from django.contrib import admin +from django.contrib import admin, messages from django.contrib.auth.admin import UserAdmin as UserAdmin_ from django.contrib.auth.models import Group from django.utils.safestring import mark_safe @@ -120,6 +120,26 @@ def url(self, qr): return "" +class HuntAdmin(admin.ModelAdmin): + def save_model(self, request, obj, form, change): + + # todo: add warning if admin attempts to change path length or middle locations when hunt has started (will basically re-randomize the hunt (I THINK)) + + #extras = 1 if obj.starting_location else 0 + #extras += 1 if obj.ending_location else 0 + + if obj.path_length > obj.middle_locations.count(): + messages.warning( + request, + "The path length is longer than the amount of locations. " + "If you do not increase middle locations or decrease path length, " + "the path length will be reduced to the amount of locations.", + ) + # raise ValidationError( + # "The path length is longer than the amount of locations. Please increase the amount of locations or decrease the path length." + # ) + super().save_model(request, obj, form, change) + class UserAdmin(UserAdmin_): readonly_fields = ( "username", @@ -148,5 +168,5 @@ class UserAdmin(UserAdmin_): admin.site.register(Team, TeamAdmin) admin.site.register(QrCode, QrCodeAdmin) admin.site.register(LogicPuzzleHint, LogicPuzzleAdmin) -admin.site.register(Hunt) +admin.site.register(Hunt, HuntAdmin) admin.site.register(Invite) diff --git a/core/models.py b/core/models.py index 5b9b252..3d08c79 100644 --- a/core/models.py +++ b/core/models.py @@ -81,16 +81,26 @@ def codes(cls, team: "Team"): @classmethod def code_pks(cls, team: "Team"): - r = random.Random(team.id) - pks = [a["pk"] for a in QrCode.objects.all().values("pk")] - pks = pks[: team.hunt.path_length] + """ + Returns the QR codes that the team has to find in order. + The algorithm behind this system is as follows: + 1. Get the hunt that the team belongs to + 2. Get the QR codes that are in the middle of the path for the specified hunt + 3. Shuffle QR codes (using the team id appended to hunt id as the key) + 4. Get the first n QR codes (n being the path length) + 5. Add the starting and ending QR codes to list + 6. Return the list + """ + hunt: Hunt = team.hunt + r = random.Random(str(hunt.id) + str(team.id)) + hunt_codes = hunt.middle_locations.all() + pks = [a["pk"] for a in hunt_codes.values("pk")] r.shuffle(pks) - if isinstance((pk := team.hunt.ending_location_id), int): - i = pks.index(pk) if pk in pks else r.randrange(0, len(pks)) - pks = pks[:i] + pks[i + 1 :] + [pk] - if isinstance((pk := team.hunt.starting_location_id), int): - i = pks.index(pk) if pk in pks else r.randrange(0, len(pks)) - pks = [pk] + pks[:i] + pks[i + 1 :] + pks = pks[: hunt.path_length] + if hunt_end := hunt.ending_location: + pks.append(hunt_end.id) + if hunt_start := hunt.starting_location: + pks.insert(0, hunt_start.id) return pks def hint(self, team: "Team"): @@ -195,14 +205,23 @@ class Hunt(models.Model): default=4, help_text="Max Team size" ) path_length = models.PositiveSmallIntegerField( - default=15, - help_text="Length of the path: The amount of codes each time will have to find before the end.", + default=13, + help_text="Length of the path: The amount of codes each time will have to find before the end. (not including start/end)", ) starting_location = models.ForeignKey( - QrCode, on_delete=models.PROTECT, related_name="starting_location" + QrCode, + on_delete=models.PROTECT, + related_name="starting_location", + blank=True, + null=True, + help_text="(Optional) A specified starting location for the hunt. All teams will have to scan this QR code as their first.", ) ending_location = models.ForeignKey( - QrCode, on_delete=models.PROTECT, related_name="ending_location" + QrCode, + on_delete=models.PROTECT, + related_name="ending_location", + blank=True, + help_text="(Optional) A specified ending location for the hunt. All teams will get as their last location.", ) middle_locations = models.ManyToManyField( QrCode, From 665450313ec563c1444ca380764a62dd7469cc7b Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Dec 2023 18:36:40 +0000 Subject: [PATCH 23/36] format --- core/admin.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/admin.py b/core/admin.py index 215583b..3fb86e3 100644 --- a/core/admin.py +++ b/core/admin.py @@ -122,12 +122,11 @@ def url(self, qr): class HuntAdmin(admin.ModelAdmin): def save_model(self, request, obj, form, change): - # todo: add warning if admin attempts to change path length or middle locations when hunt has started (will basically re-randomize the hunt (I THINK)) - - #extras = 1 if obj.starting_location else 0 - #extras += 1 if obj.ending_location else 0 - + + # extras = 1 if obj.starting_location else 0 + # extras += 1 if obj.ending_location else 0 + if obj.path_length > obj.middle_locations.count(): messages.warning( request, @@ -140,6 +139,7 @@ def save_model(self, request, obj, form, change): # ) super().save_model(request, obj, form, change) + class UserAdmin(UserAdmin_): readonly_fields = ( "username", From a42184a0fb52bc033544afc1f6b3307968e08630 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 13:49:45 -0500 Subject: [PATCH 24/36] fix: assigning hunt as bool --- core/context_processors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/context_processors.py b/core/context_processors.py index ade5f7f..db16049 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -6,7 +6,7 @@ def start(request): now = timezone.now() - if hunt := Hunt.current_hunt() is not None: + if (hunt := Hunt.current_hunt()) is not None: event_start = hunt.start event_end = hunt.end elif Hunt.next_hunt() is not None: From 29f0dae09913c4466735962d27b59ef5675e2a9d Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 13:52:52 -0500 Subject: [PATCH 25/36] remove useless assignment --- core/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/admin.py b/core/admin.py index 3fb86e3..c42e9eb 100644 --- a/core/admin.py +++ b/core/admin.py @@ -113,7 +113,7 @@ def url(self, qr): if qr.id: return format_html( mark_safe('{}'), - (url := reverse("qr", kwargs=dict(key=qr.key))), + reverse("qr", kwargs=dict(key=qr.key)), _("Link to Hint Page"), ) else: From 49d7986e0c6025a2cebd96bbfe67dd1d03b72b63 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 14:57:22 -0500 Subject: [PATCH 26/36] remove useless assignment --- core/templates/core/qr.html | 2 +- core/views/qr.py | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/templates/core/qr.html b/core/templates/core/qr.html index 55d4dac..5f38983 100644 --- a/core/templates/core/qr.html +++ b/core/templates/core/qr.html @@ -65,7 +65,7 @@ {% endif %} {% else %} - {% ending_block qr.hunt %} + {% ending_block request.user.team.hunt %} You found Derek! Now, solve the entirety of the logic puzzle to find which of the 5 SAC members in question this locker belongs to here! Oh, and close the locker, a dead Derek smells bad. {% translate 'Oh ho? What lieth there? Tis the light at the end of the tunnel!
Congratulations valiant scavenger and thank you for playing!' %} {% endif %} diff --git a/core/views/qr.py b/core/views/qr.py index b8b1805..fa9f01b 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -106,7 +106,6 @@ def qr(request, key): context = dict(first=False) context["qr"] = qr = get_object_or_404(QrCode, key=key) context["qr"]: QrCode - context["hunt"]: Hunt = qr.hunt codes = QrCode.code_pks(request.user.team) if qr.id not in codes: context["offpath"] = True @@ -131,7 +130,7 @@ def qr_first(request): # if request.user.team.current_qr_i != 0: # messages.error(request, _("You are not on the first QR code.")) # return redirect(reverse("qr_current")) - context["qr"] = QrCode.codes(request.user.team)[0] + context["qr"] = QrCode.codes(request.user.team).first() codes = QrCode.code_pks(request.user.team) context["nextqr"] = QrCode.objects.get(id=codes[0]) context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) @@ -145,10 +144,9 @@ def qr_first(request): def qr_current(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[request.user.team.current_qr_i] - codes = QrCode.code_pks(request.user.team) + context["qr"] = codes = QrCode.codes(request.user.team)[request.user.team.current_qr_i] context["nextqr"] = ( - None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) + None if codes.count() <= (j := i + 1) else codes[j] ) context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) return render(request, "core/qr.html", context=context) From 13dc668397b1f89184a7de09772bc556e73430d3 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 15:14:10 -0500 Subject: [PATCH 27/36] fix typo --- core/templates/core/qr.html | 1 - 1 file changed, 1 deletion(-) diff --git a/core/templates/core/qr.html b/core/templates/core/qr.html index 5f38983..e3a8077 100644 --- a/core/templates/core/qr.html +++ b/core/templates/core/qr.html @@ -66,7 +66,6 @@ {% endif %} {% else %} {% ending_block request.user.team.hunt %} - You found Derek! Now, solve the entirety of the logic puzzle to find which of the 5 SAC members in question this locker belongs to here! Oh, and close the locker, a dead Derek smells bad. {% translate 'Oh ho? What lieth there? Tis the light at the end of the tunnel!
Congratulations valiant scavenger and thank you for playing!' %} {% endif %} From 56ec5cdcd049f8de091d2f543d1d0c9cc945b9d8 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 15:30:08 -0500 Subject: [PATCH 28/36] fix typo --- core/templatetags/qr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templatetags/qr.py b/core/templatetags/qr.py index 21b5fd7..9a7fda2 100644 --- a/core/templatetags/qr.py +++ b/core/templatetags/qr.py @@ -27,7 +27,7 @@ def ending_block(hunt): return format_html( hunt.ending_text.replace( match.group(0), - '{}'.format(hunt.ending_form, ending_text), + '{}'.format(hunt.form_url, ending_text), ) ) return hunt.ending_text From a479ae5b8e99ed0a748eccb07a9189e27bac1ca7 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 15:31:29 -0500 Subject: [PATCH 29/36] fix change to list --- core/views/qr.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/views/qr.py b/core/views/qr.py index fa9f01b..e74cc00 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -144,9 +144,10 @@ def qr_first(request): def qr_current(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) - context["qr"] = codes = QrCode.codes(request.user.team)[request.user.team.current_qr_i] + codes = QrCode.codes(request.user.team) + context["qr"] = codes[i] context["nextqr"] = ( - None if codes.count() <= (j := i + 1) else codes[j] + None if len(codes) <= (j := i + 1) else codes[j] ) context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) return render(request, "core/qr.html", context=context) @@ -159,7 +160,7 @@ def qr_current(request): def qr_catalog(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[: request.user.team.current_qr_i] + context["qr"] = QrCode.codes(request.user.team).all()[: request.user.team.current_qr_i] return render(request, "core/qr_catalog.html", context=context) From 5114158853901bc0f702b44f6e30e61e7161c133 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Dec 2023 21:09:12 +0000 Subject: [PATCH 30/36] format --- core/views/qr.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/views/qr.py b/core/views/qr.py index e74cc00..8e2535a 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -146,9 +146,7 @@ def qr_current(request): context = dict(first=i == 0, current=True) codes = QrCode.codes(request.user.team) context["qr"] = codes[i] - context["nextqr"] = ( - None if len(codes) <= (j := i + 1) else codes[j] - ) + context["nextqr"] = None if len(codes) <= (j := i + 1) else codes[j] context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) return render(request, "core/qr.html", context=context) @@ -160,7 +158,9 @@ def qr_current(request): def qr_catalog(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team).all()[: request.user.team.current_qr_i] + context["qr"] = QrCode.codes(request.user.team).all()[ + : request.user.team.current_qr_i + ] return render(request, "core/qr_catalog.html", context=context) From 3fc3d801d0d19fe7b06366e881ce00b60e7aa838 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 16:40:55 -0500 Subject: [PATCH 31/36] what --- core/templates/core/qr.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/core/qr.html b/core/templates/core/qr.html index e3a8077..5e2d5c4 100644 --- a/core/templates/core/qr.html +++ b/core/templates/core/qr.html @@ -68,6 +68,6 @@ {% ending_block request.user.team.hunt %} {% translate 'Oh ho? What lieth there? Tis the light at the end of the tunnel!
Congratulations valiant scavenger and thank you for playing!' %} {% endif %} - + {% endif %} {% endblock %} From a6f64fc619f583d813611cfc189bc6468954202d Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 16:41:53 -0500 Subject: [PATCH 32/36] typehinting --- core/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/models.py b/core/models.py index 3d08c79..38d0483 100644 --- a/core/models.py +++ b/core/models.py @@ -2,10 +2,12 @@ import random import secrets +from typing import List from django.contrib.auth.models import AbstractUser from django.core.exceptions import ValidationError from django.db import models +from django.db.models import QuerySet from django.db.models.signals import pre_save from django.dispatch import receiver from django.utils import timezone @@ -75,7 +77,7 @@ def __str__(self): return f"{self.id} {self.short or self.location}" @classmethod - def codes(cls, team: "Team"): + def codes(cls, team: "Team") -> List[QrCode]: pks = QrCode.code_pks(team) return [QrCode.objects.get(id=a) for a in pks] From 8de9654bcaa325cd4a39e82412a117e33f08b448 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 16:45:03 -0500 Subject: [PATCH 33/36] fix reference to old/new Queryset --- core/views/qr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/views/qr.py b/core/views/qr.py index 8e2535a..177bcac 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -158,7 +158,7 @@ def qr_current(request): def qr_catalog(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team).all()[ + context["qr"] = QrCode.codes(request.user.team)[ : request.user.team.current_qr_i ] return render(request, "core/qr_catalog.html", context=context) From bfa56b2a4317d9262ba2e14d454489b051badb84 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 16:46:25 -0500 Subject: [PATCH 34/36] little TODO --- core/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/models.py b/core/models.py index 38d0483..589c1b3 100644 --- a/core/models.py +++ b/core/models.py @@ -77,7 +77,7 @@ def __str__(self): return f"{self.id} {self.short or self.location}" @classmethod - def codes(cls, team: "Team") -> List[QrCode]: + def codes(cls, team: "Team") -> List[QrCode]: # todo: have this return an ordered QuerySet instead of a list pks = QrCode.code_pks(team) return [QrCode.objects.get(id=a) for a in pks] From 54118710d9dc6433abab59903669d6b48c1ba6a6 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Dec 2023 21:47:26 +0000 Subject: [PATCH 35/36] format --- core/models.py | 4 +++- core/views/qr.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/models.py b/core/models.py index 589c1b3..aae8ec1 100644 --- a/core/models.py +++ b/core/models.py @@ -77,7 +77,9 @@ def __str__(self): return f"{self.id} {self.short or self.location}" @classmethod - def codes(cls, team: "Team") -> List[QrCode]: # todo: have this return an ordered QuerySet instead of a list + def codes( + cls, team: "Team" + ) -> List[QrCode]: # todo: have this return an ordered QuerySet instead of a list pks = QrCode.code_pks(team) return [QrCode.objects.get(id=a) for a in pks] diff --git a/core/views/qr.py b/core/views/qr.py index 177bcac..fa7f8e2 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -158,9 +158,7 @@ def qr_current(request): def qr_catalog(request): i = request.user.team.current_qr_i context = dict(first=i == 0, current=True) - context["qr"] = QrCode.codes(request.user.team)[ - : request.user.team.current_qr_i - ] + context["qr"] = QrCode.codes(request.user.team)[: request.user.team.current_qr_i] return render(request, "core/qr_catalog.html", context=context) From afa8ed8af5fd6492785f88081b847cfb461599f8 Mon Sep 17 00:00:00 2001 From: Jason Cameron Date: Mon, 4 Dec 2023 17:10:35 -0500 Subject: [PATCH 36/36] added checking for skipping --- core/templates/core/qr.html | 6 +++++- core/views/qr.py | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/templates/core/qr.html b/core/templates/core/qr.html index 5e2d5c4..4f7043d 100644 --- a/core/templates/core/qr.html +++ b/core/templates/core/qr.html @@ -69,5 +69,9 @@ {% translate 'Oh ho? What lieth there? Tis the light at the end of the tunnel!
Congratulations valiant scavenger and thank you for playing!' %} {% endif %} - {% endif %} + {% else %} { + +{% translate 'Incorrect QR code, please read your hint again or contact an organizer' %} + +{% endif %} {% endblock %} diff --git a/core/views/qr.py b/core/views/qr.py index 177bcac..ff08d0d 100644 --- a/core/views/qr.py +++ b/core/views/qr.py @@ -104,10 +104,14 @@ def wrapped(*args, **kwargs): @during_hunt def qr(request, key): context = dict(first=False) - context["qr"] = qr = get_object_or_404(QrCode, key=key) context["qr"]: QrCode + context["qr"] = qr = get_object_or_404(QrCode, key=key) codes = QrCode.code_pks(request.user.team) - if qr.id not in codes: + if qr.id != codes[request.user.team.current_qr_i]: + """ + Either the user skipped ahead (is on path) or they found a random qr code (not on path) + Either way... not allowed + """ context["offpath"] = True return render(request, "core/qr.html", context=context) i = codes.index(qr.id) @@ -115,8 +119,7 @@ def qr(request, key): None if len(codes) <= (j := i + 1) else QrCode.objects.get(id=codes[j]) ) context["logic_hint"] = LogicPuzzleHint.get_clue(request.user.team) - # TODO: check if they skipped? - request.user.team.update_current_qr_i(i) + request.user.team.update_current_qr_i(i + 1) return render(request, "core/qr.html", context=context)