From 6baf5c31494b3bcd92d774f9af7098a181e30157 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 5 Dec 2023 13:02:06 +0000 Subject: [PATCH] Improve APIv2 - Sort all list endpoints by ID, which reduces the likelihood of objects moving between pages during requests. Objects will only shift if an object is deleted, which usually does not happen. - Add a list filter for ID (and other identifier fields), so users can request a list of only the objects they care about (rather than requesting all and then filtering on their side) - Use the rating from Profile.rating instead of re-computing it each time. This attribute should already be updated during each contest rate. --- judge/views/api/api_v2.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/judge/views/api/api_v2.py b/judge/views/api/api_v2.py index 771647f995..dd45c284b9 100644 --- a/judge/views/api/api_v2.py +++ b/judge/views/api/api_v2.py @@ -199,6 +199,7 @@ class APIContestList(APIListView): ('is_rated', 'is_rated'), ) list_filters = ( + ('key', 'key'), ('tag', 'tags__name'), ('organization', 'organizations'), ) @@ -213,7 +214,7 @@ def get_unfiltered_queryset(self): to_attr='tag_list', ), ) - .order_by('end_time') + .order_by('id') ) def get_object_data(self, contest): @@ -393,6 +394,7 @@ class APIProblemList(APIListView): ('partial', 'partial'), ) list_filters = ( + ('code', 'code'), ('group', 'group__full_name'), ('type', 'types__full_name'), ('organization', 'organizations'), @@ -409,7 +411,7 @@ def get_unfiltered_queryset(self): to_attr='type_list', ), ) - .order_by('code') + .order_by('id') .distinct() ) @@ -478,20 +480,18 @@ def get_object_data(self, problem): class APIUserList(APIListView): model = Profile list_filters = ( + ('id', 'id'), + ('username', 'username'), ('organization', 'organizations'), ) def get_unfiltered_queryset(self): - latest_rating_subquery = Rating.objects.filter(user=OuterRef('pk')).order_by('-contest__end_time') return ( Profile.objects .filter(is_unlisted=False, user__is_active=True) - .annotate( - username=F('user__username'), - latest_rating=Subquery(latest_rating_subquery.values('rating')[:1]), - ) + .annotate(username=F('user__username')) .order_by('id') - .only('id', 'points', 'performance_points', 'problem_count', 'display_rank') + .only('id', 'points', 'performance_points', 'problem_count', 'display_rank', 'rating') ) def get_object_data(self, profile): @@ -502,7 +502,7 @@ def get_object_data(self, profile): 'performance_points': profile.performance_points, 'problem_count': profile.problem_count, 'rank': profile.display_rank, - 'rating': profile.latest_rating, + 'rating': profile.rating, } @@ -524,8 +524,6 @@ def get_object_data(self, profile): .values_list('problem__code', flat=True), ) - last_rating = profile.ratings.order_by('-contest__end_time').first() - contest_history = [] participations = ( ContestParticipation.objects @@ -557,7 +555,7 @@ def get_object_data(self, profile): 'problem_count': profile.problem_count, 'solved_problems': solved_problems, 'rank': profile.display_rank, - 'rating': last_rating.rating if last_rating is not None else None, + 'rating': profile.rating, 'organizations': list(profile.organizations.values_list('id', flat=True)), 'contests': contest_history, } @@ -570,6 +568,7 @@ class APISubmissionList(APIListView): ('problem', ProblemSimpleFilter('problem')), ) list_filters = ( + ('id', 'id'), ('language', LanguageListFilter('language')), ('result', 'result'), ) @@ -681,6 +680,9 @@ class APIOrganizationList(APIListView): basic_filters = ( ('is_open', 'is_open'), ) + list_filters = ( + ('id', 'id'), + ) def get_unfiltered_queryset(self): return Organization.objects.annotate(member_count=Count('member')).order_by('id') @@ -700,6 +702,10 @@ class APILanguageList(APIListView): basic_filters = ( ('common_name', 'common_name'), ) + list_filters = ( + ('id', 'id'), + ('key', 'key'), + ) def get_object_data(self, language): return { @@ -717,7 +723,7 @@ class APIJudgeList(APIListView): model = Judge def get_unfiltered_queryset(self): - return Judge.objects.filter(online=True).prefetch_related('runtimes').order_by('name') + return Judge.objects.filter(online=True).prefetch_related('runtimes').order_by('id') def get_object_data(self, judge): return {