|
5 | 5 | from django.conf import settings |
6 | 6 | from django.contrib.auth import get_user_model |
7 | 7 | from django.core.cache import cache |
8 | | -from django.db.models import Count, Q |
| 8 | +from django.db.models import Count |
9 | 9 | from django.utils import timezone |
10 | 10 |
|
11 | 11 | from ...utils.date import get_date_range_this_year, get_school_year_label |
|
16 | 16 | from ..polls.models import Answer, Poll |
17 | 17 |
|
18 | 18 | LAST_MINUTE_WINDOW = datetime.timedelta(minutes=10) |
19 | | -COHORT_CACHE_TIMEOUT = 60 * 60 |
| 19 | +COHORT_CACHE_TIMEOUT = None |
20 | 20 | RANK_BUCKETS = (0.01, 0.1, 1, 5, 20, 50) |
21 | 21 |
|
22 | 22 |
|
@@ -47,15 +47,74 @@ def rank_label(top_percent): |
47 | 47 | return None |
48 | 48 |
|
49 | 49 |
|
50 | | -def cached_cohort_counts(name, start, end, compute_counts): |
51 | | - cache_key = f"wrapped:cohort:v1:{name}:{start.isoformat()}:{end.isoformat()}" |
52 | | - counts = cache.get(cache_key) |
53 | | - if counts is None: |
54 | | - counts = list(compute_counts()) |
55 | | - cache.set(cache_key, counts, COHORT_CACHE_TIMEOUT) |
| 50 | +def cohort_cache_key(name, start, end): |
| 51 | + return f"wrapped:cohort:v1:{name}:{start.isoformat()}:{end.isoformat()}" |
| 52 | + |
| 53 | + |
| 54 | +def get_cached_cohort_counts(name, start, end): |
| 55 | + return cache.get(cohort_cache_key(name, start, end)) |
| 56 | + |
| 57 | + |
| 58 | +def set_cached_cohort_counts(name, start, end, counts): |
| 59 | + counts = list(counts) |
| 60 | + cache.set(cohort_cache_key(name, start, end), counts, COHORT_CACHE_TIMEOUT) |
56 | 61 | return counts |
57 | 62 |
|
58 | 63 |
|
| 64 | +def student_ids_for_cohort(): |
| 65 | + return list(get_user_model().objects.get_students().values_list("id", flat=True)) |
| 66 | + |
| 67 | + |
| 68 | +def compute_signup_counts(start_date, end_date): |
| 69 | + student_ids = student_ids_for_cohort() |
| 70 | + counts_by_user = dict( |
| 71 | + EighthSignup.objects.filter( |
| 72 | + user_id__in=student_ids, |
| 73 | + scheduled_activity__block__date__gte=start_date, |
| 74 | + scheduled_activity__block__date__lte=end_date, |
| 75 | + ) |
| 76 | + .values("user_id") |
| 77 | + .annotate( |
| 78 | + wrapped_count=Count("id"), |
| 79 | + ) |
| 80 | + .values_list("user_id", "wrapped_count") |
| 81 | + ) |
| 82 | + return [counts_by_user.get(student_id, 0) for student_id in student_ids] |
| 83 | + |
| 84 | + |
| 85 | +def compute_unique_activity_counts(start_date, end_date): |
| 86 | + student_ids = student_ids_for_cohort() |
| 87 | + counts_by_user = dict( |
| 88 | + EighthSignup.objects.filter( |
| 89 | + user_id__in=student_ids, |
| 90 | + scheduled_activity__block__date__gte=start_date, |
| 91 | + scheduled_activity__block__date__lte=end_date, |
| 92 | + ) |
| 93 | + .values("user_id") |
| 94 | + .annotate( |
| 95 | + wrapped_count=Count( |
| 96 | + "scheduled_activity__activity", |
| 97 | + distinct=True, |
| 98 | + ) |
| 99 | + ) |
| 100 | + .values_list("user_id", "wrapped_count") |
| 101 | + ) |
| 102 | + return [counts_by_user.get(student_id, 0) for student_id in student_ids] |
| 103 | + |
| 104 | + |
| 105 | +def compute_visit_counts(start, end): |
| 106 | + student_ids = student_ids_for_cohort() |
| 107 | + counts_by_user = dict( |
| 108 | + Request.objects.filter(user_id__in=student_ids, timestamp__gte=start, timestamp__lte=end, method="GET") |
| 109 | + .exclude(path__startswith="/wrapped") |
| 110 | + .exclude(path__startswith="/api") |
| 111 | + .values("user_id") |
| 112 | + .annotate(wrapped_count=Count("id")) |
| 113 | + .values_list("user_id", "wrapped_count") |
| 114 | + ) |
| 115 | + return [counts_by_user.get(student_id, 0) for student_id in student_ids] |
| 116 | + |
| 117 | + |
59 | 118 | def path_area(path): |
60 | 119 | clean_path = urlsplit(path).path |
61 | 120 | if clean_path in ("", "/"): |
@@ -177,30 +236,8 @@ def build_eighth_stats(user, start_date, end_date): |
177 | 236 | for sponsor in sponsors: |
178 | 237 | sponsor_counts[sponsor.name] += 1 |
179 | 238 |
|
180 | | - students = get_user_model().objects.get_students() |
181 | | - signup_counts = cached_cohort_counts( |
182 | | - "signup-counts", |
183 | | - start_date, |
184 | | - end_date, |
185 | | - lambda: students.annotate( |
186 | | - wrapped_count=Count( |
187 | | - "eighthsignup", |
188 | | - filter=Q(eighthsignup__scheduled_activity__block__date__gte=start_date, eighthsignup__scheduled_activity__block__date__lte=end_date), |
189 | | - ) |
190 | | - ).values_list("wrapped_count", flat=True), |
191 | | - ) |
192 | | - unique_counts = cached_cohort_counts( |
193 | | - "unique-activity-counts", |
194 | | - start_date, |
195 | | - end_date, |
196 | | - lambda: students.annotate( |
197 | | - wrapped_count=Count( |
198 | | - "eighthsignup__scheduled_activity__activity", |
199 | | - filter=Q(eighthsignup__scheduled_activity__block__date__gte=start_date, eighthsignup__scheduled_activity__block__date__lte=end_date), |
200 | | - distinct=True, |
201 | | - ) |
202 | | - ).values_list("wrapped_count", flat=True), |
203 | | - ) |
| 239 | + signup_counts = get_cached_cohort_counts("signup-counts", start_date, end_date) |
| 240 | + unique_counts = get_cached_cohort_counts("unique-activity-counts", start_date, end_date) |
204 | 241 |
|
205 | 242 | return { |
206 | 243 | "total": total, |
@@ -243,20 +280,7 @@ def build_usage_stats(user, start, end): |
243 | 280 | month_counts[local_time.strftime("%B")] += 1 |
244 | 281 | hour_counts[local_time.hour] += 1 |
245 | 282 |
|
246 | | - visit_filter = ( |
247 | | - Q(request__timestamp__gte=start, request__timestamp__lte=end, request__method="GET") |
248 | | - & ~Q(request__path__startswith="/wrapped") |
249 | | - & ~Q(request__path__startswith="/api") |
250 | | - ) |
251 | | - visit_counts = cached_cohort_counts( |
252 | | - "visit-counts", |
253 | | - start, |
254 | | - end, |
255 | | - lambda: get_user_model() |
256 | | - .objects.get_students() |
257 | | - .annotate(wrapped_count=Count("request", filter=visit_filter)) |
258 | | - .values_list("wrapped_count", flat=True), |
259 | | - ) |
| 283 | + visit_counts = get_cached_cohort_counts("visit-counts", start, end) |
260 | 284 |
|
261 | 285 | busiest_hour = None |
262 | 286 | if hour_counts: |
|
0 commit comments