diff --git a/judge/jinja2/submission.py b/judge/jinja2/submission.py index 7c39569..cdb3634 100644 --- a/judge/jinja2/submission.py +++ b/judge/jinja2/submission.py @@ -3,34 +3,39 @@ from . import registry @registry.function def submission_layout( - submission, profile_id, user, editable_problem_ids, completed_problem_ids + submission, + profile_id, + user, + editable_problem_ids, + completed_problem_ids, + tester_problem_ids, ): problem_id = submission.problem_id - can_view = False if problem_id in editable_problem_ids: - can_view = True + return True + + if problem_id in tester_problem_ids: + return True if profile_id == submission.user_id: - can_view = True + return True if user.has_perm("judge.change_submission"): - can_view = True + return True if user.has_perm("judge.view_all_submission"): - can_view = True + return True if submission.problem.is_public and user.has_perm("judge.view_public_submission"): - can_view = True + return True - if submission.problem_id in completed_problem_ids: - can_view |= ( - submission.problem.is_public or profile_id in submission.problem.tester_ids - ) - - if not can_view and hasattr(submission, "contest"): + if hasattr(submission, "contest"): contest = submission.contest.participation.contest if contest.is_editable_by(user): - can_view = True + return True - return can_view + if submission.problem_id in completed_problem_ids and submission.problem.is_public: + return True + + return False diff --git a/judge/models/problem.py b/judge/models/problem.py index 70ed115..2b92832 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -6,7 +6,7 @@ from django.contrib.contenttypes.fields import GenericRelation from django.core.cache import cache from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator from django.db import models -from django.db.models import CASCADE, F, FilteredRelation, Q, SET_NULL +from django.db.models import CASCADE, F, FilteredRelation, Q, SET_NULL, Exists, OuterRef from django.db.models.functions import Coalesce from django.urls import reverse from django.utils.functional import cached_property @@ -369,7 +369,7 @@ class Problem(models.Model): ) @classmethod - def get_visible_problems(cls, user): + def get_visible_problems(cls, user, profile=None): # Do unauthenticated check here so we can skip authentication checks later on. if not user.is_authenticated or not user: return cls.get_public_problems() @@ -383,7 +383,7 @@ class Problem(models.Model): # - not is_organization_private or in organization or `judge.see_organization_problem` # - author or curator or tester queryset = cls.objects.defer("description") - + profile = profile or user.profile if not ( user.has_perm("judge.see_private_problem") or user.has_perm("judge.edit_all_problem") @@ -393,13 +393,25 @@ class Problem(models.Model): # Either not organization private or in the organization. q &= Q(is_organization_private=False) | Q( is_organization_private=True, - organizations__in=user.profile.organizations.all(), + organizations__in=profile.organizations.all(), ) # Authors, curators, and testers should always have access, so OR at the very end. - q |= Q(authors=user.profile) - q |= Q(curators=user.profile) - q |= Q(testers=user.profile) + filter = Exists( + Problem.authors.through.objects.filter( + problem=OuterRef("pk"), profile=profile + ) + ) + filter |= Exists( + Problem.curators.through.objects.filter( + problem=OuterRef("pk"), profile=profile + ) + ) + filter |= Exists( + Problem.testers.through.objects.filter( + problem=OuterRef("pk"), profile=profile + ) + ) queryset = queryset.filter(q) return queryset diff --git a/judge/utils/diggpaginator.py b/judge/utils/diggpaginator.py index de9ee7d..2ba8581 100644 --- a/judge/utils/diggpaginator.py +++ b/judge/utils/diggpaginator.py @@ -191,6 +191,9 @@ class DiggPaginator(ExPaginator): # validate padding value max_padding = int(math.ceil(self.body / 2.0) - 1) self.padding = kwargs.pop("padding", min(4, max_padding)) + count_override = kwargs.pop("count", None) + if count_override is not None: + self.__dict__["count"] = count_override if self.padding > max_padding: raise ValueError("padding too large for body (max %d)" % max_padding) super(DiggPaginator, self).__init__(*args, **kwargs) diff --git a/judge/utils/problems.py b/judge/utils/problems.py index 6deb750..057edf0 100644 --- a/judge/utils/problems.py +++ b/judge/utils/problems.py @@ -19,14 +19,17 @@ __all__ = [ "contest_completed_ids", "get_result_data", "user_completed_ids", - "user_authored_ids", "user_editable_ids", + "user_tester_ids", ] -def user_authored_ids(profile): - result = set(Problem.objects.filter(authors=profile).values_list("id", flat=True)) - return result +def user_tester_ids(profile): + return set( + Problem.testers.through.objects.filter(profile=profile).values_list( + "problem_id", flat=True + ) + ) def user_editable_ids(profile): diff --git a/judge/views/problem.py b/judge/views/problem.py index c54b7dd..4ba7ef9 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -482,13 +482,10 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView padding=2, orphans=orphans, allow_empty_first_page=allow_empty_first_page, + count=queryset.values("pk").count() if not self.in_contest else None, **kwargs ) if not self.in_contest: - # Get the number of pages and then add in this magic. - # noinspection PyStatementEffect - paginator.num_pages - queryset = queryset.add_i18n_name(self.request.LANGUAGE_CODE) sort_key = self.order.lstrip("-") if sort_key in self.sql_sort: @@ -670,8 +667,12 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView if self.request.profile: context["organizations"] = self.request.profile.organizations.all() - all_authors_ids = set(Problem.objects.values_list("authors", flat=True)) - context["all_authors"] = Profile.objects.filter(id__in=all_authors_ids) + all_authors_ids = Problem.objects.values_list("authors", flat=True) + context["all_authors"] = ( + Profile.objects.filter(id__in=all_authors_ids) + .select_related("user") + .values("id", "user__username") + ) context["category"] = self.category context["categories"] = ProblemGroup.objects.all() if self.show_types: diff --git a/judge/views/submission.py b/judge/views/submission.py index 5594c32..9bc6eea 100644 --- a/judge/views/submission.py +++ b/judge/views/submission.py @@ -41,9 +41,7 @@ from judge.models import ProblemTranslation from judge.models import Profile from judge.models import Submission from judge.utils.problems import get_result_data -from judge.utils.problems import user_authored_ids -from judge.utils.problems import user_completed_ids -from judge.utils.problems import user_editable_ids +from judge.utils.problems import user_completed_ids, user_editable_ids, user_tester_ids from judge.utils.problem_data import get_problem_case from judge.utils.raw_sql import join_sql_subquery, use_straight_join from judge.utils.views import DiggPaginatorMixin @@ -476,12 +474,12 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): context["completed_problem_ids"] = ( user_completed_ids(self.request.profile) if authenticated else [] ) - context["authored_problem_ids"] = ( - user_authored_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["all_languages"] = Language.objects.all().values_list("key", "name") context["selected_languages"] = self.selected_languages @@ -769,15 +767,15 @@ def single_submission(request, submission_id, show_problem=True): "submission/row.html", { "submission": submission, - "authored_problem_ids": user_authored_ids(request.profile) - if authenticated - else [], "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), diff --git a/resources/problem.scss b/resources/problem.scss index 8528761..6122e8c 100644 --- a/resources/problem.scss +++ b/resources/problem.scss @@ -369,7 +369,6 @@ ul.problem-list { margin-left: 2.5%; padding-bottom: 1em; border-radius: 5px; - margin-bottom: 1em; display: flex; justify-content: space-between; } diff --git a/templates/problem/search-form.html b/templates/problem/search-form.html index 073d3c7..c1dfc3c 100644 --- a/templates/problem/search-form.html +++ b/templates/problem/search-form.html @@ -59,7 +59,7 @@ diff --git a/templates/submission/row.html b/templates/submission/row.html index e25933f..1c4d8d7 100644 --- a/templates/submission/row.html +++ b/templates/submission/row.html @@ -1,4 +1,4 @@ -{% set can_view = submission_layout(submission, profile_id, request.user, editable_problem_ids, completed_problem_ids) %} +{% set can_view = submission_layout(submission, profile_id, request.user, editable_problem_ids, completed_problem_ids, tester_problem_ids) %}
{%- if submission.is_graded -%}