From 44cbe572eadf79aa3e055ec7e7f5ea3681dddb37 Mon Sep 17 00:00:00 2001 From: Kushagra Tiwari Date: Thu, 12 Feb 2026 13:02:50 +0530 Subject: [PATCH 1/2] Refactor question data handling in templates and views - Updated template to use cached user and last post user fields for improved performance. - Changed answer count retrieval to use a new annotation in the view. - Enhanced the last_post_user method to handle cases where the user may not exist. - Optimized user fetching to reduce database queries by bulk fetching user data for displayed questions. --- static/website/templates/index.html | 26 ++++++++--------- website/models.py | 4 +-- website/views.py | 44 +++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/static/website/templates/index.html b/static/website/templates/index.html index 66db570..64b11b5 100755 --- a/static/website/templates/index.html +++ b/static/website/templates/index.html @@ -169,12 +169,12 @@

Answers

- {{ question.answer_set.count }} + {{ question.answer_count }} - - {{ question.user|truncatechars:10 }} + + {{ question.cached_user|truncatechars:10 }} @@ -250,8 +250,8 @@

Answers

- - {{ question.user|truncatechars:10 }} + + {{ question.cached_user|truncatechars:10 }} @@ -263,8 +263,8 @@

Answers

- - {{ question.last_post_user|truncatechars:10 }} + + {{ question.cached_last_post_user|truncatechars:10 }} @@ -273,7 +273,7 @@

Answers

- {{ question.answer_set.count }} + {{ question.answer_count }} {% endfor %} @@ -350,8 +350,8 @@

Answers

- - {{ question.user|truncatechars:10 }} + + {{ question.cached_user|truncatechars:10 }} @@ -363,8 +363,8 @@

Answers

- - {{ question.last_post_user|truncatechars:10 }} + + {{ question.cached_last_post_user|truncatechars:10 }} @@ -373,7 +373,7 @@

Answers

- {{ question.answer_set.count }} + {{ question.answer_count }} {% endfor %} diff --git a/website/models.py b/website/models.py index 5623a90..cd9040e 100755 --- a/website/models.py +++ b/website/models.py @@ -25,8 +25,8 @@ def user(self): return user.username def last_post_user(self): - user = User.objects.get(id=self.last_post_by) - return user.username + user = User.objects.filter(id=self.last_post_by).first() + return user.username if user else "Unknown User" class Meta: get_latest_by = "date_created" diff --git a/website/views.py b/website/views.py index 85772d7..f5cbf9e 100755 --- a/website/views.py +++ b/website/views.py @@ -32,14 +32,35 @@ def home(request): - questions = Question.objects.filter(status=1).order_by('date_created').reverse()[:100] - active_questions = Question.objects.filter(status=1, last_active__isnull=False).order_by('last_active').reverse()[:100] - + # Base query with answer count annotation + base_queryset = Question.objects.annotate(answer_count=Count('answer')) + + questions = base_queryset.filter(status=1).order_by('-date_created')[:100] + active_questions = base_queryset.filter(status=1, last_active__isnull=False).order_by('-last_active')[:100] + # Retrieve latest questions per category for the slider subquery = Question.objects.filter(category=OuterRef('category'), status=1).values('category').annotate(max_date=Max('date_created')).values('max_date') - slider_questions = Question.objects.filter( + slider_questions = base_queryset.filter( date_created=Subquery(subquery), status=1 ).order_by('category') + + # spam questions + spam_questions = base_queryset.filter(status=2).order_by('-last_active')[:100] + + # Bulk fetch users for all displayed questions to avoid N+1 + all_questions = list(questions) + list(active_questions) + list(slider_questions) + list(spam_questions) + uids = set() + for q in all_questions: + uids.add(q.uid) + if q.last_post_by: + uids.add(q.last_post_by) + + users = {u.id: u.username for u in User.objects.filter(id__in=uids)} + + # Attach usernames to question objects so templates don't trigger queries + for q in all_questions: + q.cached_user = users.get(q.uid, "Unknown User") + q.cached_last_post_user = users.get(q.last_post_by, "Unknown User") if q.last_post_by else "Unknown User" # Mapping of foss name as in spk db & its corresponding category name in forums db category_fosses = {val.replace(" ", "-") : val for val in categories} @@ -60,18 +81,17 @@ def home(request): if foss not in category_question_map: category_question_map[foss] = None - # spam questions - spam_questions = Question.objects.filter(status=2).order_by('last_active').reverse()[:100] # Sort category_question_map by category name category_question_map = dict(sorted(category_question_map.items(), key= lambda item: item[0].lower())) show_spam_list = is_administrator(request.user) or is_forumsadmin(request.user) + context = { - 'questions': questions, - 'active_questions':active_questions, - 'spam_questions': spam_questions, - 'category_question_map': category_question_map, - 'show_spam_list': show_spam_list -} + 'questions': questions, + 'active_questions': active_questions, + 'spam_questions': spam_questions, + 'category_question_map': category_question_map, + 'show_spam_list': show_spam_list + } return render(request, "website/templates/index.html", context) From 653be99507a2fcf5750f15a6fac03fb3d576908c Mon Sep 17 00:00:00 2001 From: Kushagra Tiwari Date: Tue, 17 Feb 2026 16:32:40 +0530 Subject: [PATCH 2/2] Add log analysis script for homepage response time percentiles - Introduced a new script to analyze log files and compute response time percentiles for homepage requests. - Implemented functionality to handle log file detection, argument parsing, and data sampling. - Added plotting capabilities to visualize response time distributions and comparisons across specified days. - Included error handling for missing log files and provided usage instructions. --- scripts/log.py | 208 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 scripts/log.py diff --git a/scripts/log.py b/scripts/log.py new file mode 100644 index 0000000..aa36ed0 --- /dev/null +++ b/scripts/log.py @@ -0,0 +1,208 @@ +import re +import random +import argparse +from pathlib import Path +from datetime import datetime +from collections import defaultdict +import numpy as np +import matplotlib.pyplot as plt + +# keep max samples per day (adjust if needed) +MAX_SAMPLES = 200000 + +# Log format looks like: +# [pid: ...] ... [Tue Feb 10 14:47:06 2026] GET /... => generated ... in 2740 msecs +# Use a greedy prefix so we capture the *last* [...] date block before GET. +pattern = re.compile( + r".*\[(?P(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+[A-Za-z]{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})\]\s+GET\s+(?P/[^\s]*)\s+=> generated .* in (?P