11import json
22import logging
33
4+ from django .core .cache import cache
45from django_scopes import scope , scopes_disabled
56from i18nfield .utils import I18nJSONEncoder
67
910
1011LOGGER = logging .getLogger (__name__ )
1112
13+ _CACHE_TTL = 600
14+
1215
1316@app .task (name = 'pretalx.agenda.export_schedule_html' )
1417def export_schedule_html (* , event_id : int , make_zip = True ):
@@ -33,15 +36,20 @@ def export_schedule_html(*, event_id: int, make_zip=True):
3336
3437@app .task (name = 'eventyay.agenda.warm_schedule_caches' )
3538def warm_schedule_caches (* , schedule_pk : int ):
36- """Pre-build and cache non-enriched schedule JSON for the newly released schedule.
39+ """Pre-build and cache schedule JSON payloads for the newly released schedule.
3740
38- Called shortly after schedule_release so the first real user request hits
39- a warm cache instead of paying the full build_data cost.
40- """
41- from django .core .cache import cache
41+ Warms non-enriched, enriched, and exporters caches so the first real user
42+ request after a schedule publish hits a warm cache instead of paying the
43+ full build_data cost.
4244
45+ Note: Schedule and agenda.views.utils are imported locally to break the
46+ circular import chain: base.models.schedule → agenda.tasks → base.models.
47+ """
48+ # Local imports required to break circular dependency:
49+ # base.models.schedule imports export_schedule_html from this module,
50+ # so module-level imports of Schedule or agenda.views.utils create a cycle.
51+ from eventyay .agenda .views .utils import build_public_schedule_exporters , escape_json_for_script
4352 from eventyay .base .models import Schedule
44- from eventyay .agenda .views .utils import escape_json_for_script
4553
4654 with scopes_disabled ():
4755 schedule = (
@@ -55,14 +63,26 @@ def warm_schedule_caches(*, schedule_pk: int):
5563 with scope (event = schedule .event ):
5664 for featured in (True , False ):
5765 try :
58- data = schedule .build_data (
59- all_talks = False ,
60- enrich = False ,
61- include_featured_speaker_metadata = featured ,
62- )
63- result = escape_json_for_script (json .dumps (data , cls = I18nJSONEncoder ))
64- cache .set (f'eagenda:schedule:{ schedule .pk } :{ int (featured )} ' , result , 300 )
66+ non_enriched = escape_json_for_script (json .dumps (
67+ schedule .build_data (all_talks = False , enrich = False , include_featured_speaker_metadata = featured ),
68+ cls = I18nJSONEncoder ,
69+ ))
70+ cache .set (f'eagenda:schedule:{ schedule .pk } :{ int (featured )} ' , non_enriched , _CACHE_TTL )
6571 except Exception :
66- LOGGER .exception ('Failed to warm non-enriched cache for schedule %s' , schedule .pk )
72+ LOGGER .exception ('Failed to warm non-enriched cache for schedule %s (featured=%s)' , schedule .pk , featured )
73+
74+ try :
75+ enriched = escape_json_for_script (json .dumps (
76+ schedule .build_data (all_talks = False , enrich = True , include_featured_speaker_metadata = featured ),
77+ cls = I18nJSONEncoder ,
78+ ))
79+ cache .set (f'eagenda:enriched:{ schedule .pk } :{ int (featured )} ' , enriched , _CACHE_TTL )
80+ except Exception :
81+ LOGGER .exception ('Failed to warm enriched cache for schedule %s (featured=%s)' , schedule .pk , featured )
82+
83+ try :
84+ build_public_schedule_exporters (schedule .event , version = schedule .version )
85+ except Exception :
86+ LOGGER .exception ('Failed to warm exporters cache for schedule %s' , schedule .pk )
6787
6888 LOGGER .info ('Pre-warmed schedule caches for schedule pk=%s version=%s' , schedule .pk , schedule .version )
0 commit comments