Add more caching
This commit is contained in:
parent
130c96a2fe
commit
c3cecb3f58
10 changed files with 121 additions and 126 deletions
|
@ -1,27 +1,32 @@
|
|||
from inspect import signature
|
||||
from django.core.cache import cache
|
||||
from django.core.cache import cache, caches
|
||||
from django.db.models.query import QuerySet
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
|
||||
import hashlib
|
||||
|
||||
MAX_NUM_CHAR = 15
|
||||
MAX_NUM_CHAR = 50
|
||||
NONE_RESULT = "__None__"
|
||||
|
||||
|
||||
def arg_to_str(arg):
|
||||
if hasattr(arg, "id"):
|
||||
return str(arg.id)
|
||||
if isinstance(arg, list) or isinstance(arg, QuerySet):
|
||||
return hashlib.sha1(str(list(arg)).encode()).hexdigest()[:MAX_NUM_CHAR]
|
||||
if len(str(arg)) > MAX_NUM_CHAR:
|
||||
return str(arg)[:MAX_NUM_CHAR]
|
||||
return str(arg)
|
||||
|
||||
|
||||
def filter_args(args_list):
|
||||
return [x for x in args_list if not isinstance(x, WSGIRequest)]
|
||||
|
||||
|
||||
l0_cache = caches["l0"] if "l0" in caches else None
|
||||
|
||||
|
||||
def cache_wrapper(prefix, timeout=None):
|
||||
def arg_to_str(arg):
|
||||
if hasattr(arg, "id"):
|
||||
return str(arg.id)
|
||||
if isinstance(arg, list) or isinstance(arg, QuerySet):
|
||||
return hashlib.sha1(str(list(arg)).encode()).hexdigest()[:MAX_NUM_CHAR]
|
||||
if len(str(arg)) > MAX_NUM_CHAR:
|
||||
return str(arg)[:MAX_NUM_CHAR]
|
||||
return str(arg)
|
||||
|
||||
def filter_args(args_list):
|
||||
return [x for x in args_list if not isinstance(x, WSGIRequest)]
|
||||
|
||||
def get_key(func, *args, **kwargs):
|
||||
args_list = list(args)
|
||||
signature_args = list(signature(func).parameters.keys())
|
||||
|
@ -32,23 +37,41 @@ def cache_wrapper(prefix, timeout=None):
|
|||
key = key.replace(" ", "_")
|
||||
return key
|
||||
|
||||
def _get(key):
|
||||
if not l0_cache:
|
||||
return cache.get(key)
|
||||
print("GET", key, l0_cache.get(key))
|
||||
return l0_cache.get(key) or cache.get(key)
|
||||
|
||||
def _set_l0(key, value):
|
||||
if l0_cache:
|
||||
print("SET", key, value)
|
||||
l0_cache.set(key, value, 30)
|
||||
|
||||
def _set(key, value, timeout):
|
||||
_set_l0(key, value)
|
||||
cache.set(key, value, timeout)
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
cache_key = get_key(func, *args, **kwargs)
|
||||
result = cache.get(cache_key)
|
||||
result = _get(cache_key)
|
||||
if result is not None:
|
||||
_set_l0(cache_key, result)
|
||||
if result == NONE_RESULT:
|
||||
result = None
|
||||
return result
|
||||
result = func(*args, **kwargs)
|
||||
if result is None:
|
||||
result = NONE_RESULT
|
||||
cache.set(cache_key, result, timeout)
|
||||
_set(cache_key, result, timeout)
|
||||
return result
|
||||
|
||||
def dirty(*args, **kwargs):
|
||||
cache_key = get_key(func, *args, **kwargs)
|
||||
cache.delete(cache_key)
|
||||
if l0_cache:
|
||||
l0_cache.delete(cache_key)
|
||||
|
||||
wrapper.dirty = dirty
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ from . import (
|
|||
render,
|
||||
social,
|
||||
spaceless,
|
||||
submission,
|
||||
timedelta,
|
||||
)
|
||||
from . import registry
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
from . import registry
|
||||
|
||||
|
||||
@registry.function
|
||||
def submission_layout(
|
||||
submission,
|
||||
profile_id,
|
||||
user,
|
||||
editable_problem_ids,
|
||||
completed_problem_ids,
|
||||
tester_problem_ids,
|
||||
):
|
||||
problem_id = submission.problem_id
|
||||
|
||||
if problem_id in editable_problem_ids:
|
||||
return True
|
||||
|
||||
if problem_id in tester_problem_ids:
|
||||
return True
|
||||
|
||||
if profile_id == submission.user_id:
|
||||
return True
|
||||
|
||||
if user.has_perm("judge.change_submission"):
|
||||
return True
|
||||
|
||||
if user.has_perm("judge.view_all_submission"):
|
||||
return True
|
||||
|
||||
if submission.problem.is_public and user.has_perm("judge.view_public_submission"):
|
||||
return True
|
||||
|
||||
if hasattr(submission, "contest"):
|
||||
contest = submission.contest.participation.contest
|
||||
if contest.is_editable_by(user):
|
||||
return True
|
||||
|
||||
if submission.problem_id in completed_problem_ids and submission.problem.is_public:
|
||||
return True
|
||||
|
||||
return False
|
|
@ -220,6 +220,47 @@ class Submission(models.Model):
|
|||
def id_secret(self):
|
||||
return self.get_id_secret(self.id)
|
||||
|
||||
def is_accessible_by(self, profile):
|
||||
from judge.utils.problems import (
|
||||
user_completed_ids,
|
||||
user_tester_ids,
|
||||
user_editable_ids,
|
||||
)
|
||||
|
||||
if not profile:
|
||||
return False
|
||||
|
||||
problem_id = self.problem_id
|
||||
user = profile.user
|
||||
|
||||
if profile.id == self.user_id:
|
||||
return True
|
||||
|
||||
if problem_id in user_editable_ids(profile):
|
||||
return True
|
||||
|
||||
if self.problem_id in user_completed_ids(profile):
|
||||
if self.problem.is_public:
|
||||
return True
|
||||
if problem_id in user_tester_ids(profile):
|
||||
return True
|
||||
|
||||
if user.has_perm("judge.change_submission"):
|
||||
return True
|
||||
|
||||
if user.has_perm("judge.view_all_submission"):
|
||||
return True
|
||||
|
||||
if self.problem.is_public and user.has_perm("judge.view_public_submission"):
|
||||
return True
|
||||
|
||||
if hasattr(
|
||||
self, "contest"
|
||||
) and self.contest.participation.contest.is_editable_by(user):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
class Meta:
|
||||
permissions = (
|
||||
("abort_any_submission", "Abort any submission"),
|
||||
|
|
|
@ -10,6 +10,8 @@ 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 django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from judge.models import Problem, Submission
|
||||
from judge.ml.collab_filter import CollabFilter
|
||||
|
@ -24,6 +26,7 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
@cache_wrapper(prefix="user_tester")
|
||||
def user_tester_ids(profile):
|
||||
return set(
|
||||
Problem.testers.through.objects.filter(profile=profile).values_list(
|
||||
|
@ -32,6 +35,7 @@ def user_tester_ids(profile):
|
|||
)
|
||||
|
||||
|
||||
@cache_wrapper(prefix="user_editable")
|
||||
def user_editable_ids(profile):
|
||||
result = set(
|
||||
(
|
||||
|
@ -42,22 +46,19 @@ def user_editable_ids(profile):
|
|||
return result
|
||||
|
||||
|
||||
@cache_wrapper(prefix="contest_complete")
|
||||
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()
|
||||
result = set(
|
||||
participation.submissions.filter(
|
||||
submission__result="AC", points=F("problem__points")
|
||||
)
|
||||
cache.set(key, result, 86400)
|
||||
.values_list("problem__problem__id", flat=True)
|
||||
.distinct()
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@cache_wrapper(prefix="user_complete", timeout=86400)
|
||||
@cache_wrapper(prefix="user_complete")
|
||||
def user_completed_ids(profile):
|
||||
result = set(
|
||||
Submission.objects.filter(
|
||||
|
@ -69,7 +70,7 @@ def user_completed_ids(profile):
|
|||
return result
|
||||
|
||||
|
||||
@cache_wrapper(prefix="contest_attempted", timeout=86400)
|
||||
@cache_wrapper(prefix="contest_attempted")
|
||||
def contest_attempted_ids(participation):
|
||||
result = {
|
||||
id: {"achieved_points": points, "max_points": max_points}
|
||||
|
@ -84,7 +85,7 @@ def contest_attempted_ids(participation):
|
|||
return result
|
||||
|
||||
|
||||
@cache_wrapper(prefix="user_attempted", timeout=86400)
|
||||
@cache_wrapper(prefix="user_attempted")
|
||||
def user_attempted_ids(profile):
|
||||
result = {
|
||||
id: {
|
||||
|
@ -248,3 +249,22 @@ def finished_submission(sub):
|
|||
keys += ["contest_complete:%d" % participation.id]
|
||||
keys += ["contest_attempted:%d" % participation.id]
|
||||
cache.delete_many(keys)
|
||||
|
||||
|
||||
@receiver([pre_save], sender=Problem)
|
||||
def on_problem_save(sender, instance, **kwargs):
|
||||
if instance.id is None:
|
||||
return
|
||||
prev_editors = list(sender.objects.get(id=instance.id).editor_ids)
|
||||
new_editors = list(instance.editor_ids)
|
||||
if prev_editors != new_editors:
|
||||
all_editors = set(prev_editors + new_editors)
|
||||
for profile_id in all_editors:
|
||||
user_editable_ids.dirty(profile_id)
|
||||
|
||||
prev_testers = list(sender.objects.get(id=instance.id).tester_ids)
|
||||
new_testers = list(instance.tester_ids)
|
||||
if prev_testers != new_testers:
|
||||
all_testers = set(prev_testers + new_testers)
|
||||
for profile_id in all_testers:
|
||||
user_tester_ids.dirty(profile_id)
|
||||
|
|
|
@ -84,31 +84,7 @@ class SubmissionMixin(object):
|
|||
class SubmissionDetailBase(LoginRequiredMixin, TitleMixin, SubmissionMixin, DetailView):
|
||||
def get_object(self, queryset=None):
|
||||
submission = super(SubmissionDetailBase, self).get_object(queryset)
|
||||
profile = self.request.profile
|
||||
problem = submission.problem
|
||||
if self.request.user.has_perm("judge.view_all_submission"):
|
||||
return submission
|
||||
if problem.is_public and self.request.user.has_perm(
|
||||
"judge.view_public_submission"
|
||||
):
|
||||
return submission
|
||||
if submission.user_id == profile.id:
|
||||
return submission
|
||||
if problem.is_editor(profile):
|
||||
return submission
|
||||
if problem.is_public or problem.testers.filter(id=profile.id).exists():
|
||||
if Submission.objects.filter(
|
||||
user_id=profile.id,
|
||||
result="AC",
|
||||
problem_id=problem.id,
|
||||
points=problem.points,
|
||||
).exists():
|
||||
return submission
|
||||
if hasattr(
|
||||
submission, "contest"
|
||||
) and submission.contest.participation.contest.is_editable_by(
|
||||
self.request.user
|
||||
):
|
||||
if submission.is_accessible_by(self.request.profile):
|
||||
return submission
|
||||
|
||||
raise PermissionDenied()
|
||||
|
@ -483,19 +459,9 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
|
|||
authenticated = self.request.user.is_authenticated
|
||||
context["dynamic_update"] = False
|
||||
context["show_problem"] = self.show_problem
|
||||
context["completed_problem_ids"] = (
|
||||
user_completed_ids(self.request.profile) if authenticated else []
|
||||
)
|
||||
context["editable_problem_ids"] = (
|
||||
user_editable_ids(self.request.profile) if authenticated else []
|
||||
)
|
||||
context["tester_problem_ids"] = (
|
||||
user_tester_ids(self.request.profile) if authenticated else []
|
||||
)
|
||||
|
||||
context["profile"] = self.request.profile
|
||||
context["all_languages"] = Language.objects.all().values_list("key", "name")
|
||||
context["selected_languages"] = self.selected_languages
|
||||
|
||||
context["all_statuses"] = self.get_searchable_status_codes()
|
||||
context["selected_statuses"] = self.selected_statuses
|
||||
|
||||
|
@ -779,19 +745,10 @@ def single_submission(request, submission_id, show_problem=True):
|
|||
"submission/row.html",
|
||||
{
|
||||
"submission": submission,
|
||||
"completed_problem_ids": user_completed_ids(request.profile)
|
||||
if authenticated
|
||||
else [],
|
||||
"editable_problem_ids": user_editable_ids(request.profile)
|
||||
if authenticated
|
||||
else [],
|
||||
"tester_problem_ids": user_tester_ids(request.profile)
|
||||
if authenticated
|
||||
else [],
|
||||
"show_problem": show_problem,
|
||||
"problem_name": show_problem
|
||||
and submission.problem.translated_name(request.LANGUAGE_CODE),
|
||||
"profile_id": request.profile.id if authenticated else 0,
|
||||
"profile": request.profile if authenticated else None,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -1010,9 +967,6 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
|
|||
context["contest"] = self.contest
|
||||
context["problem"] = self.problem
|
||||
context["profile"] = self.profile
|
||||
context["profile_id"] = (
|
||||
self.request.profile.id if self.request.profile else None
|
||||
)
|
||||
|
||||
contest_problem = self.contest.contest_problems.get(problem=self.problem)
|
||||
filtered_submissions = []
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue