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.
This commit is contained in:
Evan 2023-12-05 13:02:06 +00:00 committed by Tudor Brindus
parent 2035e606f3
commit 6baf5c3149

View File

@ -199,6 +199,7 @@ class APIContestList(APIListView):
('is_rated', 'is_rated'),
)
list_filters = (
('key', 'key'),
('tag', 'tags__name'),
('organization', 'organizations'),
)
@ -213,7 +214,7 @@ class APIContestList(APIListView):
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 @@ class APIProblemList(APIListView):
to_attr='type_list',
),
)
.order_by('code')
.order_by('id')
.distinct()
)
@ -478,20 +480,18 @@ class APIProblemDetail(APIDetailView):
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 @@ class APIUserList(APIListView):
'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 @@ class APIUserDetail(APIDetailView):
.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 @@ class APIUserDetail(APIDetailView):
'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 {