Files
OnlineJudge/reaction/views/admin.py
yuetsh f485ff6aac fix(reaction): materialize paginated queryset before JSON response
ReactionStatsAPI called the sync paginate_data() without an
object_serializer, leaving results as a lazy QuerySet. json.dumps()
in JSONResponse.response has no custom encoder for QuerySet, so every
request 500'd. list() the results before wrapping in success().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 04:12:44 -06:00

42 lines
1.6 KiB
Python

from django.db.models import Count, F, FloatField, Q
from django.db.models.functions import Cast
from account.decorators import super_admin_required
from reaction.models import Reaction, ReactionType
from utils.api import APIView
MIN_USERS_FOR_RATIO = 3
DEFAULT_ORDERING = "-users"
class ReactionStatsAPI(APIView):
@super_admin_required
def get(self, request):
queryset = (
Reaction.objects.values(pid=F("problem___id"), title=F("problem__title"))
.annotate(users=Count("user", distinct=True))
.annotate(**{t.value: Count("id", filter=Q(type=t.value)) for t in ReactionType})
.annotate(**{f"{t.value}_ratio": Cast(t.value, FloatField()) / Cast("users", FloatField()) for t in ReactionType})
)
problem_id = request.GET.get("problem")
if problem_id:
queryset = queryset.filter(problem___id__iexact=problem_id, problem__contest_id__isnull=True)
ordering = request.GET.get("ordering") or DEFAULT_ORDERING
prefix = "-" if ordering.startswith("-") else ""
field = ordering.lstrip("-")
if field == "users":
order_by = f"{prefix}users"
elif field in ReactionType.values:
# 按占比排序时过滤低样本,避免 1 人点 1 个就冲到 100%
queryset = queryset.filter(users__gte=MIN_USERS_FOR_RATIO)
order_by = f"{prefix}{field}_ratio"
else:
order_by = DEFAULT_ORDERING
queryset = queryset.order_by(order_by)
data = self.paginate_data(request, queryset)
data["results"] = list(data["results"])
return self.success(data)