147 lines
6.1 KiB
Python
147 lines
6.1 KiB
Python
from collections import defaultdict
|
|
from math import e
|
|
|
|
from django.core.cache import cache
|
|
from django.db.models import Case, Count, ExpressionWrapper, F, Max, Q, When
|
|
from django.db.models.fields import FloatField
|
|
from django.utils import timezone
|
|
from django.utils.translation import gettext as _, gettext_noop
|
|
|
|
from judge.models import Problem, Submission
|
|
|
|
__all__ = ['contest_completed_ids', 'get_result_data', 'user_completed_ids', 'user_authored_ids', 'user_editable_ids']
|
|
|
|
|
|
def user_authored_ids(profile):
|
|
result = set(Problem.objects.filter(authors=profile).values_list('id', flat=True))
|
|
return result
|
|
|
|
|
|
def user_editable_ids(profile):
|
|
result = set((Problem.objects.filter(authors=profile) | Problem.objects.filter(curators=profile))
|
|
.values_list('id', flat=True))
|
|
return result
|
|
|
|
|
|
def contest_completed_ids(participation):
|
|
key = 'contest_complete:%d' % participation.id
|
|
result = cache.get(key)
|
|
if result is None:
|
|
result = set(participation.submissions.filter(submission__result='AC', points=F('problem__points'))
|
|
.values_list('problem__problem__id', flat=True).distinct())
|
|
cache.set(key, result, 86400)
|
|
return result
|
|
|
|
|
|
def user_completed_ids(profile):
|
|
key = 'user_complete:%d' % profile.id
|
|
result = cache.get(key)
|
|
if result is None:
|
|
result = set(Submission.objects.filter(user=profile, result='AC', points=F('problem__points'))
|
|
.values_list('problem_id', flat=True).distinct())
|
|
cache.set(key, result, 86400)
|
|
return result
|
|
|
|
|
|
def contest_attempted_ids(participation):
|
|
key = 'contest_attempted:%s' % participation.id
|
|
result = cache.get(key)
|
|
if result is None:
|
|
result = {id: {'achieved_points': points, 'max_points': max_points}
|
|
for id, max_points, points in (participation.submissions
|
|
.values_list('problem__problem__id', 'problem__points')
|
|
.annotate(points=Max('points'))
|
|
.filter(points__lt=F('problem__points')))}
|
|
cache.set(key, result, 86400)
|
|
return result
|
|
|
|
|
|
def user_attempted_ids(profile):
|
|
key = 'user_attempted:%s' % profile.id
|
|
result = cache.get(key)
|
|
if result is None:
|
|
result = {id: {'achieved_points': points, 'max_points': max_points}
|
|
for id, max_points, points in (Submission.objects.filter(user=profile)
|
|
.values_list('problem__id', 'problem__points')
|
|
.annotate(points=Max('points'))
|
|
.filter(points__lt=F('problem__points')))}
|
|
cache.set(key, result, 86400)
|
|
return result
|
|
|
|
|
|
def _get_result_data(results):
|
|
return {
|
|
'categories': [
|
|
# Using gettext_noop here since this will be tacked into the cache, so it must be language neutral.
|
|
# The caller, SubmissionList.get_result_data will run ugettext on the name.
|
|
{'code': 'AC', 'name': gettext_noop('Accepted'), 'count': results['AC']},
|
|
{'code': 'WA', 'name': gettext_noop('Wrong'), 'count': results['WA']},
|
|
{'code': 'CE', 'name': gettext_noop('Compile Error'), 'count': results['CE']},
|
|
{'code': 'TLE', 'name': gettext_noop('Timeout'), 'count': results['TLE']},
|
|
{'code': 'ERR', 'name': gettext_noop('Error'),
|
|
'count': results['MLE'] + results['OLE'] + results['IR'] + results['RTE'] + results['AB'] + results['IE']},
|
|
],
|
|
'total': sum(results.values()),
|
|
}
|
|
|
|
|
|
def get_result_data(*args, **kwargs):
|
|
if args:
|
|
submissions = args[0]
|
|
if kwargs:
|
|
raise ValueError(_("Can't pass both queryset and keyword filters"))
|
|
else:
|
|
submissions = Submission.objects.filter(**kwargs) if kwargs is not None else Submission.objects
|
|
raw = submissions.values('result').annotate(count=Count('result')).values_list('result', 'count')
|
|
return _get_result_data(defaultdict(int, raw))
|
|
|
|
|
|
def editable_problems(user, profile=None):
|
|
subquery = Problem.objects.all()
|
|
if profile is None:
|
|
profile = user.profile
|
|
if not user.has_perm('judge.edit_all_problem'):
|
|
subfilter = Q(authors__id=profile.id) | Q(curators__id=profile.id)
|
|
if user.has_perm('judge.edit_public_problem'):
|
|
subfilter |= Q(is_public=True)
|
|
subquery = subquery.filter(subfilter)
|
|
return subquery
|
|
|
|
|
|
def hot_problems(duration, limit):
|
|
cache_key = 'hot_problems:%d:%d' % (duration.total_seconds(), limit)
|
|
qs = cache.get(cache_key)
|
|
if qs is None:
|
|
qs = Problem.objects.filter(is_public=True, is_organization_private=False,
|
|
submission__date__gt=timezone.now() - duration, points__gt=3, points__lt=25)
|
|
qs0 = qs.annotate(k=Count('submission__user', distinct=True)).order_by('-k').values_list('k', flat=True)
|
|
|
|
if not qs0:
|
|
return []
|
|
# make this an aggregate
|
|
mx = float(qs0[0])
|
|
|
|
qs = qs.annotate(unique_user_count=Count('submission__user', distinct=True))
|
|
# fix braindamage in excluding CE
|
|
qs = qs.annotate(submission_volume=Count(Case(
|
|
When(submission__result='AC', then=1),
|
|
When(submission__result='WA', then=1),
|
|
When(submission__result='IR', then=1),
|
|
When(submission__result='RTE', then=1),
|
|
When(submission__result='TLE', then=1),
|
|
When(submission__result='OLE', then=1),
|
|
output_field=FloatField(),
|
|
)))
|
|
qs = qs.annotate(ac_volume=Count(Case(
|
|
When(submission__result='AC', then=1),
|
|
output_field=FloatField(),
|
|
)))
|
|
qs = qs.filter(unique_user_count__gt=max(mx / 3.0, 1))
|
|
|
|
qs = qs.annotate(ordering=ExpressionWrapper(
|
|
0.5 * F('points') * (0.4 * F('ac_volume') / F('submission_volume') + 0.6 * F('ac_rate')) +
|
|
100 * e ** (F('unique_user_count') / mx), output_field=FloatField(),
|
|
)).order_by('-ordering').defer('description')[:limit]
|
|
|
|
cache.set(cache_key, qs, 900)
|
|
return qs
|