diff --git a/tabbycat/draw/views.py b/tabbycat/draw/views.py index 9c4e1e6e3ca..441e6eb4a0b 100644 --- a/tabbycat/draw/views.py +++ b/tabbycat/draw/views.py @@ -1,3 +1,4 @@ +import copy import datetime import json import logging @@ -5,12 +6,14 @@ from itertools import product from zoneinfo import ZoneInfo -from django.conf import settings from django.contrib import messages +from django.core.cache import cache from django.db import DatabaseError, transaction from django.db.models import OuterRef, Subquery from django.http import HttpResponseRedirect +from django.urls import reverse from django.utils import timezone +from django.utils.cache import get_cache_key from django.utils.functional import cached_property from django.utils.html import escape, format_html from django.utils.safestring import mark_safe @@ -64,6 +67,16 @@ logger = logging.getLogger(__name__) +def invalidate_draw_release_caches(tournament, request): + """Invalidate page cache for PublicDrawForCurrentRoundsView and TournamentPublicHomeView + when the draw is released or unreleased, so the public sees updated content.""" + fake_request = copy.deepcopy(request) + fake_request.method = 'GET' + for view_name in ('tournament-public-index', 'draw-public-current-rounds'): + fake_request.path = reverse(view_name, kwargs={'tournament_slug': tournament.slug}) + cache.delete(get_cache_key(fake_request)) + + class BaseDisplayDrawTableView(TournamentMixin, VueTableTemplateView): """Base class for views showing a draw table to the public in some way. Subclasses are *not* necessarily public views; they may be admin/assistant @@ -110,7 +123,6 @@ def get_debates_for_round(cls, round): return round.debate_set_with_prefetches() def get_tables(self): - # If the view has debates specified specifically, use those in a single table if hasattr(self, 'get_debates'): table = PublicDrawTableBuilder(view=self, sort_key=self.sort_key, @@ -229,6 +241,17 @@ class PublicDrawForCurrentRoundsView(PublicDrawMixin, BaseDisplayDrawForCurrentR def is_page_enabled(self, tournament): return tournament.pref('public_draw') == 'current' + def get_tables(self): + cached_tables = [] + for round in self.rounds: # Only return cached tables if all rounds are cached + if cached_table := cache.get('public_draw_table.%s.%s' % (round.id, self.request.LANGUAGE_CODE)): + cached_tables.append(cached_table) + else: + break + else: + return json.dumps(cached_tables) + return super().get_tables() + # ============================================================================== # Viewing Draw (Briefing Room) @@ -793,10 +816,12 @@ def post(self, request, *args, **kwargs): self.round.draw_status = Round.Status.RELEASED self.round.save() self.log_action() - - if settings.ENABLE_PUSH_NOTIFICATIONS: - self.send_push_notifications() - + cache.set( + 'public_draw_table.%s.%s' % (self.round.id, request.LANGUAGE_CODE), + json.dumps([t.jsondict() for t in PublicDrawForCurrentRoundsView(tournament=self.tournament, request=request).get_tables()]), + 30, # 30 seconds + ) + invalidate_draw_release_caches(self.tournament, request) messages.success(request, _("Released the draw.")) return super().post(request, *args, **kwargs) @@ -860,6 +885,12 @@ def post(self, request, *args, **kwargs): self.round.draw_status = Round.Status.TEAMS_RELEASED self.round.save() self.log_action() + cache.set( + 'public_draw_table.%s.%s' % (self.round.id, request.LANGUAGE_CODE), + json.dumps([t.jsondict() for t in PublicDrawForCurrentRoundsView(tournament=self.tournament, request=request).get_tables()]), + 30, # 30 seconds + ) + invalidate_draw_release_caches(self.tournament, request) messages.success(request, _("Released the draw (teams only).")) return super().post(request, *args, **kwargs) @@ -878,6 +909,7 @@ def post(self, request, *args, **kwargs): self.round.draw_status = Round.Status.CONFIRMED self.round.save() self.log_action() + invalidate_draw_release_caches(self.tournament, request) messages.success(request, _("Unreleased the draw.")) return super().post(request, *args, **kwargs) diff --git a/tabbycat/utils/views.py b/tabbycat/utils/views.py index 84cdc524371..e914fff7e5a 100644 --- a/tabbycat/utils/views.py +++ b/tabbycat/utils/views.py @@ -11,6 +11,8 @@ from django.views.generic import TemplateView, View from django.views.generic.base import ContextMixin, TemplateResponseMixin +from .tables import BaseTableBuilder + logger = logging.getLogger(__name__) @@ -62,10 +64,9 @@ class VueTableTemplateView(TemplateView): def get_context_data(self, **kwargs): tables = self.get_tables() - tables_dicts = [tb.jsondict() for tb in tables if tb is not None] + tables_dicts = [tb.jsondict() if isinstance(tb, BaseTableBuilder) else tb for tb in tables if tb is not None] kwargs["tables_data"] = json.dumps(tables_dicts) - kwargs["tables_count"] = list(range(len(tables))) kwargs["tables_orientation"] = self.tables_orientation return super().get_context_data(**kwargs)