|
1 | 1 | import os |
2 | 2 | import uuid |
| 3 | +from datetime import timedelta |
3 | 4 |
|
4 | 5 | from django.conf import settings |
5 | 6 | from django.contrib import messages |
|
9 | 10 | from django.core.files.base import ContentFile |
10 | 11 | from django.core.files.storage import default_storage |
11 | 12 | from django.core.paginator import Paginator |
12 | | -from django.db.models import Case, F, Value, When |
| 13 | +from django.db.models import Case, F, Value, When, Count, Q |
13 | 14 | from django.http import Http404, HttpResponse, JsonResponse |
14 | 15 | from django.shortcuts import get_object_or_404, redirect, render |
| 16 | +from django.utils import timezone |
15 | 17 | from django.utils.translation import gettext_lazy as _ |
16 | 18 | from django.views.decorators.http import require_http_methods |
17 | 19 | from martor.utils import LazyEncoder |
18 | 20 |
|
19 | 21 | from project.newsletter import operations |
20 | 22 | from project.newsletter.forms import PostForm, SubscriptionForm |
21 | | -from project.newsletter.models import Post, Subscription |
| 23 | +from project.newsletter.models import Category, Post, Subscription |
22 | 24 |
|
23 | 25 | LIST_POSTS_PAGE_SIZE = 100 |
24 | 26 |
|
@@ -162,6 +164,80 @@ def update_post(request, slug): |
162 | 164 | return render(request, "staff/post_form.html", {"form": form, "post": post}) |
163 | 165 |
|
164 | 166 |
|
| 167 | +@staff_member_required(login_url=settings.LOGIN_URL) |
| 168 | +@require_http_methods(["GET"]) |
| 169 | +def analytics(request): |
| 170 | + """ |
| 171 | + The post detail view. |
| 172 | + """ |
| 173 | + now = timezone.now() |
| 174 | + subscription_aggregates = Subscription.objects.all().aggregate( |
| 175 | + subscriptions=Count("user", filter=Q(categories__isnull=False)), |
| 176 | + subscriptions_30_days=Count( |
| 177 | + "id", |
| 178 | + filter=Q(categories__isnull=False, created__gte=now - timedelta(days=30)), |
| 179 | + ), |
| 180 | + subscriptions_90_days=Count( |
| 181 | + "id", |
| 182 | + filter=Q(categories__isnull=False, created__gte=now - timedelta(days=90)), |
| 183 | + ), |
| 184 | + subscriptions_180_days=Count( |
| 185 | + "id", |
| 186 | + filter=Q(categories__isnull=False, created__gte=now - timedelta(days=180)), |
| 187 | + ), |
| 188 | + ) |
| 189 | + subscription_category_aggregates = dict( |
| 190 | + Category.objects.annotate(count=Count("subscriptions")) |
| 191 | + .order_by("title") |
| 192 | + .values_list("title", "count") |
| 193 | + ) |
| 194 | + post_aggregates = Post.objects.all().aggregate( |
| 195 | + posts=Count("id"), |
| 196 | + posts_30_days=Count( |
| 197 | + "id", |
| 198 | + filter=Q(created__gte=now - timedelta(days=30)), |
| 199 | + ), |
| 200 | + posts_90_days=Count( |
| 201 | + "id", |
| 202 | + filter=Q(created__gte=now - timedelta(days=90)), |
| 203 | + ), |
| 204 | + posts_180_days=Count( |
| 205 | + "id", |
| 206 | + filter=Q(created__gte=now - timedelta(days=180)), |
| 207 | + ), |
| 208 | + ) |
| 209 | + post_category_aggregates = dict( |
| 210 | + Category.objects.annotate(count=Count("posts")) |
| 211 | + .order_by("title") |
| 212 | + .values_list("title", "count") |
| 213 | + ) |
| 214 | + |
| 215 | + return render( |
| 216 | + request, |
| 217 | + "staff/analytics.html", |
| 218 | + { |
| 219 | + "aggregates": { |
| 220 | + "Subscriptions": subscription_aggregates["subscriptions"], |
| 221 | + "Subscriptions (30 days)": subscription_aggregates[ |
| 222 | + "subscriptions_30_days" |
| 223 | + ], |
| 224 | + "Subscriptions (90 days)": subscription_aggregates[ |
| 225 | + "subscriptions_90_days" |
| 226 | + ], |
| 227 | + "Subscriptions (180 days)": subscription_aggregates[ |
| 228 | + "subscriptions_180_days" |
| 229 | + ], |
| 230 | + "Posts": post_aggregates["posts"], |
| 231 | + "Posts (30 days)": post_aggregates["posts_30_days"], |
| 232 | + "Posts (90 days)": post_aggregates["posts_90_days"], |
| 233 | + "Posts (180 days)": post_aggregates["posts_180_days"], |
| 234 | + }, |
| 235 | + "subscription_category_aggregates": subscription_category_aggregates, |
| 236 | + "post_category_aggregates": post_category_aggregates, |
| 237 | + }, |
| 238 | + ) |
| 239 | + |
| 240 | + |
165 | 241 | @staff_member_required(login_url=settings.LOGIN_URL) |
166 | 242 | @require_http_methods(["POST"]) |
167 | 243 | def toggle_post_privacy(request, slug): |
|
0 commit comments