feat: async refresh of agenda data (#8146)

This commit is contained in:
Jennifer Richards 2024-11-04 15:24:35 +00:00 committed by GitHub
parent 19d80ffc83
commit 7a5b152c5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 18 deletions

12
ietf/meeting/tasks.py Normal file
View file

@ -0,0 +1,12 @@
# Copyright The IETF Trust 2024, All Rights Reserved
#
# Celery task definitions
#
from celery import shared_task
from .views import generate_agenda_data
@shared_task
def agenda_data_refresh():
generate_agenda_data(force_refresh=True)

View file

@ -26,6 +26,7 @@ from zoneinfo import ZoneInfo
from django.urls import reverse as urlreverse from django.urls import reverse as urlreverse
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.serializers.json import DjangoJSONEncoder
from django.test import Client, override_settings from django.test import Client, override_settings
from django.db.models import F, Max from django.db.models import F, Max
from django.http import QueryDict, FileResponse from django.http import QueryDict, FileResponse
@ -49,7 +50,7 @@ from ietf.meeting.utils import condition_slide_order
from ietf.meeting.utils import add_event_info_to_session_qs, participants_for_meeting from ietf.meeting.utils import add_event_info_to_session_qs, participants_for_meeting
from ietf.meeting.utils import create_recording, get_next_sequence, bluesheet_data from ietf.meeting.utils import create_recording, get_next_sequence, bluesheet_data
from ietf.meeting.views import session_draft_list, parse_agenda_filter_params, sessions_post_save, agenda_extract_schedule from ietf.meeting.views import session_draft_list, parse_agenda_filter_params, sessions_post_save, agenda_extract_schedule
from ietf.meeting.views import get_summary_by_area, get_summary_by_type, get_summary_by_purpose from ietf.meeting.views import get_summary_by_area, get_summary_by_type, get_summary_by_purpose, generate_agenda_data
from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, ProceedingsMaterialTypeName from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, ProceedingsMaterialTypeName
from ietf.utils.decorators import skip_coverage from ietf.utils.decorators import skip_coverage
from ietf.utils.mail import outbox, empty_outbox, get_payload_text from ietf.utils.mail import outbox, empty_outbox, get_payload_text
@ -245,30 +246,34 @@ class MeetingTests(BaseMeetingTestCase):
# Agenda API tests # Agenda API tests
# -> Meeting data # -> Meeting data
r = self.client.get(urlreverse("ietf.meeting.views.api_get_agenda_data", kwargs=dict(num=meeting.number))) # First, check that the generation function does the right thing
self.assertEqual(r.status_code, 200) generated_data = generate_agenda_data(meeting.number)
rjson = json.loads(r.content.decode("utf8")) self.assertEqual(
self.assertJSONEqual( generated_data,
r.content.decode("utf8"),
{ {
"meeting": { "meeting": {
"number": meeting.number, "number": meeting.number,
"city": meeting.city, "city": meeting.city,
"startDate": meeting.date.isoformat(), "startDate": meeting.date.isoformat(),
"endDate": meeting.end_date().isoformat(), "endDate": meeting.end_date().isoformat(),
"updated": rjson.get("meeting").get("updated"), # Just expect the value to exist "updated": generated_data.get("meeting").get("updated"), # Just expect the value to exist
"timezone": meeting.time_zone, "timezone": meeting.time_zone,
"infoNote": meeting.agenda_info_note, "infoNote": meeting.agenda_info_note,
"warningNote": meeting.agenda_warning_note "warningNote": meeting.agenda_warning_note
}, },
"categories": rjson.get("categories"), # Just expect the value to exist "categories": generated_data.get("categories"), # Just expect the value to exist
"isCurrentMeeting": True, "isCurrentMeeting": True,
"usesNotes": False, # make_meeting_test_data sets number=72 "usesNotes": False, # make_meeting_test_data sets number=72
"schedule": rjson.get("schedule"), # Just expect the value to exist "schedule": generated_data.get("schedule"), # Just expect the value to exist
"floors": [] "floors": []
} }
) )
# -> Session Materials with patch("ietf.meeting.views.generate_agenda_data", return_value=generated_data):
r = self.client.get(urlreverse("ietf.meeting.views.api_get_agenda_data", kwargs=dict(num=meeting.number)))
self.assertEqual(r.status_code, 200)
# json.dumps using the DjangoJSONEncoder to handle timestamps consistently
self.assertJSONEqual(r.content.decode("utf8"), json.dumps(generated_data, cls=DjangoJSONEncoder))
# -> Session MaterialM
r = self.client.get(urlreverse("ietf.meeting.views.api_get_session_materials", kwargs=dict(session_id=session.id))) r = self.client.get(urlreverse("ietf.meeting.views.api_get_session_materials", kwargs=dict(session_id=session.id)))
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
rjson = json.loads(r.content.decode("utf8")) rjson = json.loads(r.content.decode("utf8"))

View file

@ -25,6 +25,7 @@ from tempfile import mkstemp
from wsgiref.handlers import format_date_time from wsgiref.handlers import format_date_time
from django import forms from django import forms
from django.core.cache import caches
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.http import (HttpResponse, HttpResponseRedirect, HttpResponseForbidden, from django.http import (HttpResponse, HttpResponseRedirect, HttpResponseForbidden,
HttpResponseNotFound, Http404, HttpResponseBadRequest, HttpResponseNotFound, Http404, HttpResponseBadRequest,
@ -1657,8 +1658,16 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
} }
}) })
@cache_page(5 * 60)
def api_get_agenda_data (request, num=None): def generate_agenda_data(num=None, force_refresh=False):
"""Generate data for the api_get_agenda_data endpoint
:num: meeting number
:force_refresh: True to force a refresh of the cache
"""
cache = caches["default"]
cache_timeout = 6 * 60
meeting = get_ietf_meeting(num) meeting = get_ietf_meeting(num)
if meeting is None: if meeting is None:
raise Http404("No such full IETF meeting") raise Http404("No such full IETF meeting")
@ -1667,6 +1676,12 @@ def api_get_agenda_data (request, num=None):
else: else:
pass pass
cache_key = f"generate_agenda_data_{meeting.number}"
if not force_refresh:
cached_value = cache.get(cache_key)
if cached_value is not None:
return cached_value
# Select the schedule to show # Select the schedule to show
schedule = get_schedule(meeting, None) schedule = get_schedule(meeting, None)
@ -1685,10 +1700,8 @@ def api_get_agenda_data (request, num=None):
# Get Floor Plans # Get Floor Plans
floors = FloorPlan.objects.filter(meeting=meeting).order_by('order') floors = FloorPlan.objects.filter(meeting=meeting).order_by('order')
#debug.show('all([(item.acronym,item.session.order_number,item.session.order_in_meeting()) for item in filtered_assignments])') result = {
return JsonResponse({
"meeting": { "meeting": {
"number": schedule.meeting.number, "number": schedule.meeting.number,
"city": schedule.meeting.city, "city": schedule.meeting.city,
@ -1704,7 +1717,13 @@ def api_get_agenda_data (request, num=None):
"usesNotes": meeting.uses_notes(), "usesNotes": meeting.uses_notes(),
"schedule": list(map(agenda_extract_schedule, filtered_assignments)), "schedule": list(map(agenda_extract_schedule, filtered_assignments)),
"floors": list(map(agenda_extract_floorplan, floors)) "floors": list(map(agenda_extract_floorplan, floors))
}) }
cache.set(cache_key, result, timeout=cache_timeout)
return result
def api_get_agenda_data(request, num=None):
return JsonResponse(generate_agenda_data(num, force_refresh=False))
def api_get_session_materials(request, session_id=None): def api_get_session_materials(request, session_id=None):