feat(src): cache branding lookup and improve leaderboard performance

Prevent DB hits by caching site branding and refactor leaderboard query using annotate
This commit is contained in:
sankalp committed 2026-06-28 16:07:05 +05:45
1 parent ec350b1531
commit df6df1b407
4 files changed
+129 -70

No files matched your search

+24 -9
View File
@@ -1,9 +1,31 @@
from django.contrib.auth import get_user_model
from django.core.cache import cache
from src.accounts.models import Setting, SiteBranding
from src.tickets.models import Ticket
def _get_cached_branding():
branding = cache.get("site_branding")
if branding is not None:
return branding
obj = SiteBranding.objects.filter(is_active=True).first()
if obj:
branding = {
"branding": obj,
"brand_name": obj.brand_name,
"copyright_name": obj.copyright_name,
}
else:
branding = {
"branding": None,
"brand_name": "Project Management",
"copyright_name": "Project Management",
}
cache.set("site_branding", branding, timeout=300)
return branding
def site_stats(request):
User = get_user_model()
@@ -25,13 +47,6 @@ def site_stats(request):
ctx["navigation_style"] = user_ui_settings.get("navigation_style", "sidebar")
ctx["color_theme"] = user_ui_settings.get("color_theme", "blue")
branding = SiteBranding.objects.filter(is_active=True).first()
if branding:
ctx["branding"] = branding
ctx["brand_name"] = branding.brand_name
ctx["copyright_name"] = branding.copyright_name
else:
ctx["branding"] = None
ctx["brand_name"] = "Project Management"
ctx["copyright_name"] = "Project Management"
branding = _get_cached_branding()
ctx.update(branding)
return ctx
+97 -54
View File
@@ -4,6 +4,7 @@ from datetime import date, timedelta
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views.decorators.http import require_http_methods
@@ -48,41 +49,55 @@ def leaderboard(request):
top_n = int(request.GET.get("top", 10))
since = timezone.now() - timedelta(days=days)
users = User.objects.filter(is_active=True)
user_ids = list(User.objects.filter(is_active=True).values_list("id", flat=True))
tickets_created_map = dict(
Ticket.objects.filter(created_by_id__in=user_ids, created_at__gte=since)
.values("created_by_id")
.annotate(count=Count("id"))
.values_list("created_by_id", "count")
)
status_changes_map = dict(
TicketHistory.objects.filter(user_id__in=user_ids, created_at__gte=since)
.values("user_id")
.annotate(count=Count("id"))
.values_list("user_id", "count")
)
comments_map = dict(
TicketComment.objects.filter(user_id__in=user_ids, created_at__gte=since)
.values("user_id")
.annotate(count=Count("id"))
.values_list("user_id", "count")
)
active_days_map = dict(
TicketHistory.objects.filter(user_id__in=user_ids, created_at__gte=since)
.values("user_id")
.annotate(count=Count("created_at", distinct=True))
.values_list("user_id", "count")
)
users = User.objects.filter(id__in=user_ids).only(
"id", "first_name", "last_name", "username"
)
leaderboard_data = []
for user in users:
tickets_created = Ticket.objects.filter(
created_by=user, created_at__gte=since
).count()
status_changes = TicketHistory.objects.filter(
user=user, created_at__gte=since
).count()
comments_made = TicketComment.objects.filter(
user=user, created_at__gte=since
).count()
active_days = (
TicketHistory.objects.filter(user=user, created_at__gte=since)
.dates("created_at", "day")
.count()
)
created = tickets_created_map.get(user.id, 0)
changes = status_changes_map.get(user.id, 0)
comments = comments_map.get(user.id, 0)
days_active = active_days_map.get(user.id, 0)
score = (
(status_changes * 5)
+ (tickets_created * 2)
+ (comments_made * 2)
+ (active_days * 1)
)
score = (changes * 5) + (created * 2) + (comments * 2) + (days_active * 1)
if score > 0 or tickets_created > 0 or comments_made > 0:
if score > 0 or created > 0 or comments > 0:
leaderboard_data.append(
{
"user": user,
"score": score,
"tickets_created": tickets_created,
"status_changes": status_changes,
"comments_made": comments_made,
"active_days": active_days,
"tickets_created": created,
"status_changes": changes,
"comments_made": comments,
"active_days": days_active,
}
)
@@ -139,47 +154,75 @@ def user_contributions(request):
available_years.append(target_year)
available_years.sort(reverse=True)
users = User.objects.filter(is_active=True)[:10]
user_ids = list(
User.objects.filter(is_active=True).values_list("id", flat=True)[:10]
)
tickets_created_map = dict(
Ticket.objects.filter(created_by_id__in=user_ids, created_at__year=target_year)
.values("created_by_id")
.annotate(count=Count("id"))
.values_list("created_by_id", "count")
)
status_changes_map = dict(
TicketHistory.objects.filter(user_id__in=user_ids, created_at__year=target_year)
.values("user_id")
.annotate(count=Count("id"))
.values_list("user_id", "count")
)
comments_map = dict(
TicketComment.objects.filter(user_id__in=user_ids, created_at__year=target_year)
.values("user_id")
.annotate(count=Count("id"))
.values_list("user_id", "count")
)
active_days_map = dict(
TicketHistory.objects.filter(user_id__in=user_ids, created_at__year=target_year)
.values("user_id")
.annotate(count=Count("created_at", distinct=True))
.values_list("user_id", "count")
)
all_history = TicketHistory.objects.filter(
user_id__in=user_ids, created_at__year=target_year
).values("user_id", "created_at")
history_by_user = {}
for h in all_history:
uid = h["user_id"]
if uid not in history_by_user:
history_by_user[uid] = []
history_by_user[uid].append(h["created_at"])
users = User.objects.filter(id__in=user_ids).only(
"id", "first_name", "last_name", "username"
)
contributions = []
for user in users:
tickets_created = Ticket.objects.filter(
created_by=user, created_at__year=target_year
).count()
status_changes = TicketHistory.objects.filter(
user=user, created_at__year=target_year
).count()
comments_made = TicketComment.objects.filter(
user=user, created_at__year=target_year
).count()
active_days = (
TicketHistory.objects.filter(user=user, created_at__year=target_year)
.dates("created_at", "day")
.count()
)
uid = user.id
created = tickets_created_map.get(uid, 0)
changes = status_changes_map.get(uid, 0)
comments = comments_map.get(uid, 0)
days_active = active_days_map.get(uid, 0)
# Build heatmap data
heatmap_values = {}
current = since.date()
# end is already defined above
while current <= end:
heatmap_values[current.isoformat()] = 0
current += timedelta(days=1)
for h in TicketHistory.objects.filter(user=user, created_at__year=target_year):
day = h.created_at.date().isoformat()
for dt in history_by_user.get(uid, []):
day = dt.date().isoformat()
if day in heatmap_values:
heatmap_values[day] += 1
max_val = max(heatmap_values.values()) if heatmap_values else 1
# Group into weeks (list of lists of day objects)
weeks = []
week = []
sorted_days = sorted(heatmap_values.keys())
for i, day_str in enumerate(sorted_days):
if i > 0 and sorted_days[i - 1] < day_str:
pass
for day_str in sorted_days:
day_date = date.fromisoformat(day_str)
count = heatmap_values[day_str]
if count == 0:
@@ -200,7 +243,7 @@ def user_contributions(request):
"day": day_date.weekday(),
}
)
if day_date.weekday() == 6: # Sunday
if day_date.weekday() == 6:
weeks.append(week)
week = []
if week:
@@ -209,10 +252,10 @@ def user_contributions(request):
contributions.append(
{
"user": user,
"tickets_created": tickets_created,
"status_changes": status_changes,
"comments_made": comments_made,
"active_days": active_days,
"tickets_created": created,
"status_changes": changes,
"comments_made": comments,
"active_days": days_active,
"weeks": weeks,
"max_val": max_val,
}
@@ -4,15 +4,16 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('epics', '0001_initial'),
('projects', '0002_projectmember_project_mem_user_id_6b0bb0_idx'),
("epics", "0001_initial"),
("projects", "0002_projectmember_project_mem_user_id_6b0bb0_idx"),
]
operations = [
migrations.AddIndex(
model_name='epic',
index=models.Index(fields=['project', 'sort_order'], name='epics_project_eeb4de_idx'),
model_name="epic",
index=models.Index(
fields=["project", "sort_order"], name="epics_project_eeb4de_idx"
),
),
]
+2 -2
View File
@@ -9,7 +9,7 @@ class EpicListCreateView(generics.ListCreateAPIView):
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
qs = Epic.objects.all()
qs = Epic.objects.all().select_related("project")
project_id = self.request.query_params.get("project")
if project_id:
qs = qs.filter(project_id=project_id)
@@ -17,6 +17,6 @@ class EpicListCreateView(generics.ListCreateAPIView):
class EpicDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Epic.objects.all()
queryset = Epic.objects.all().select_related("project")
serializer_class = EpicSerializer
permission_classes = (permissions.IsAuthenticated,)