Skip to content

Commit 6b43dee

Browse files
authored
Recap screen fetch cached data on load (#4576)
1 parent 6e0957b commit 6b43dee

3 files changed

Lines changed: 74 additions & 14 deletions

File tree

backend/reviews/admin.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ def with_pct(counts_dict):
6262
.order_by("speaker__gender")
6363
)
6464
gender_counts = with_pct(
65-
{
66-
item["speaker__gender"] or "unknown": item["count"]
67-
for item in gender_stats
68-
}
65+
{item["speaker__gender"] or "unknown": item["count"] for item in gender_stats}
6966
)
7067

7168
level_stats = (
@@ -87,9 +84,7 @@ def with_pct(counts_dict):
8784
)
8885

8986
speaker_level_stats = (
90-
qs.values("speaker_level")
91-
.annotate(count=Count("id"))
92-
.order_by("speaker_level")
87+
qs.values("speaker_level").annotate(count=Count("id")).order_by("speaker_level")
9388
)
9489
speaker_level_counts = with_pct(
9590
{item["speaker_level"]: item["count"] for item in speaker_level_stats}
@@ -455,6 +450,7 @@ def review_recap_compute_analysis_view(self, request, review_session_id):
455450
conference = review_session.conference
456451
accepted_submissions = list(self._get_accepted_submissions(conference))
457452
force_recompute = request.GET.get("recompute") == "1"
453+
check_only = request.GET.get("check") == "1"
458454

459455
from django.core.cache import cache
460456

@@ -471,6 +467,9 @@ def review_recap_compute_analysis_view(self, request, review_session_id):
471467
if cached_result is not None:
472468
return JsonResponse(cached_result)
473469

470+
if check_only:
471+
return JsonResponse({"status": "empty"})
472+
474473
# Use cache.add as a lock to prevent duplicate task dispatch.
475474
# Short TTL so lock auto-expires if the worker is killed before cleanup.
476475
computing_key = f"{combined_cache_key}:computing"

backend/reviews/templates/reviews-recap.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,22 @@ <h2 class="recap-section-title">🔗 Similar Talks</h2>
682682

683683
btn.addEventListener('click', function() { fetchAnalysis(false); });
684684
recomputeBtn.addEventListener('click', function() { fetchAnalysis(true); });
685+
686+
// Auto-load cached results on page load (check only, don't trigger computation)
687+
fetch(computeUrl + '?check=1', {
688+
headers: { 'X-Requested-With': 'XMLHttpRequest' }
689+
})
690+
.then(function(response) {
691+
if (!response.ok) return;
692+
return response.json();
693+
})
694+
.then(function(data) {
695+
if (!data || data.status === 'processing' || data.status === 'error' || data.status === 'empty') return;
696+
handleResult(data);
697+
})
698+
.catch(function() {
699+
// Silently ignore errors on auto-load
700+
});
685701
})();
686702
</script>
687703
{% endblock %}

backend/reviews/tests/test_recap.py

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,7 @@ def test_recap_view_redirects_when_shortlist_not_visible(rf, mocker):
174174
response = admin.review_recap_view(request, review_session.id)
175175

176176
assert response.status_code == 302
177-
assert (
178-
response.url
179-
== f"/admin/reviews/reviewsession/{review_session.id}/change/"
180-
)
177+
assert response.url == f"/admin/reviews/reviewsession/{review_session.id}/change/"
181178

182179

183180
# --- review_recap_compute_analysis_view tests ---
@@ -194,9 +191,7 @@ def cache_get_side_effect(key):
194191
mock_cache_get = mocker.patch(
195192
"django.core.cache.cache.get", side_effect=cache_get_side_effect
196193
)
197-
mock_cache_add = mocker.patch(
198-
"django.core.cache.cache.add", return_value=True
199-
)
194+
mock_cache_add = mocker.patch("django.core.cache.cache.add", return_value=True)
200195
mocker.patch("django.core.cache.cache.set")
201196
mocker.patch("django.core.cache.cache.delete")
202197
mock_task = mocker.patch("reviews.tasks.compute_recap_analysis.apply_async")
@@ -573,6 +568,56 @@ def cache_get_side_effect(key):
573568
mock_check.assert_not_called()
574569

575570

571+
def test_compute_analysis_view_check_only_returns_empty_on_cache_miss(rf, mocker):
572+
user, conference, review_session, submissions = _create_recap_setup()
573+
574+
_, _, mock_task, mock_check = _mock_analysis_deps(mocker, cache_return=None)
575+
576+
request = rf.get("/?check=1")
577+
request.user = user
578+
579+
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
580+
response = admin.review_recap_compute_analysis_view(request, review_session.id)
581+
582+
data = json.loads(response.content)
583+
assert data == {"status": "empty"}
584+
585+
# Task should NOT be dispatched in check-only mode
586+
mock_task.assert_not_called()
587+
mock_check.assert_not_called()
588+
589+
590+
def test_compute_analysis_view_check_only_returns_cached_result(rf, mocker):
591+
user, conference, review_session, submissions = _create_recap_setup()
592+
sub1, sub2 = submissions
593+
594+
cached_data = {
595+
"submissions_list": [
596+
{
597+
"id": sub1.id,
598+
"title": str(sub1.title),
599+
"type": sub1.type.name,
600+
"speaker": sub1.speaker.display_name,
601+
"similar": [],
602+
},
603+
],
604+
"topic_clusters": {"topics": [], "outliers": [], "submission_topics": {}},
605+
}
606+
607+
_, _, mock_task, _ = _mock_analysis_deps(mocker, cache_return=cached_data)
608+
609+
request = rf.get("/?check=1")
610+
request.user = user
611+
612+
admin = ReviewSessionAdmin(ReviewSession, AdminSite())
613+
response = admin.review_recap_compute_analysis_view(request, review_session.id)
614+
615+
data = json.loads(response.content)
616+
assert data["submissions_list"][0]["id"] == sub1.id
617+
618+
mock_task.assert_not_called()
619+
620+
576621
def test_error_cache_ttl_is_shorter_than_result_ttl():
577622
from reviews.tasks import ERROR_CACHE_TTL, RESULT_CACHE_TTL
578623

0 commit comments

Comments
 (0)