diff --git a/dmoj/urls.py b/dmoj/urls.py index a791537..f696a1e 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -310,7 +310,6 @@ urlpatterns = [ ticket.NewProblemTicketView.as_view(), name="new_problem_ticket", ), - url(r"^/vote$", problem.Vote.as_view(), name="vote"), url( r"^/manage/submission", include( @@ -529,11 +528,18 @@ urlpatterns = [ ), ), url( - r"^/submissions/(?P\w+)/(?P\w+)/", + r"^/submissions/(?P\w+)/(?P\w+)", paged_list_view( submission.UserContestSubmissions, "contest_user_submissions" ), ), + url( + r"^/submissions/(?P\w+)/(?P\w+)/ajax", + paged_list_view( + submission.UserContestSubmissionsAjax, + "contest_user_submissions_ajax", + ), + ), url( r"^/participations$", contests.ContestParticipationList.as_view(), diff --git a/judge/contest_format/default.py b/judge/contest_format/default.py index 11c8f37..0c053e0 100644 --- a/judge/contest_format/default.py +++ b/judge/contest_format/default.py @@ -55,7 +55,7 @@ class DefaultContestFormat(BaseContestFormat): format_data = (participation.format_data or {}).get(str(contest_problem.id)) if format_data: return format_html( - '{points}
{time}
', + '{points}
{time}
', state=( ( "pretest-" @@ -68,7 +68,7 @@ class DefaultContestFormat(BaseContestFormat): ) ), url=reverse( - "contest_user_submissions", + "contest_user_submissions_ajax", args=[ self.contest.key, participation.user.user.username, diff --git a/judge/management/commands/makedmojmessages.py b/judge/management/commands/makedmojmessages.py index 44324e8..5c2a9ba 100644 --- a/judge/management/commands/makedmojmessages.py +++ b/judge/management/commands/makedmojmessages.py @@ -171,7 +171,8 @@ msgid "" msgstr "" "Content-Type: text/plain; charset=utf-8\\n" - """) + """ + ) if self.verbosity > 1: self.stdout.write("processing navigation bar") for label in NavigationBar.objects.values_list("label", flat=True): diff --git a/judge/models/problem.py b/judge/models/problem.py index bb99ae3..7211a69 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -561,29 +561,6 @@ class Problem(models.Model): save.alters_data = True - def can_vote(self, request): - return False - user = request.user - if not user.is_authenticated: - return False - - # If the user is in contest, nothing should be shown. - if request.in_contest_mode: - return False - - # If the user is not allowed to vote - if user.profile.is_unlisted or user.profile.is_banned_problem_voting: - return False - - # If the user is banned from submitting to the problem. - if self.banned_users.filter(pk=user.pk).exists(): - return False - - # If the user has a full AC submission to the problem (solved the problem). - return self.submission_set.filter( - user=user.profile, result="AC", points=F("problem__points") - ).exists() - class Meta: permissions = ( ("see_private_problem", "See hidden problems"), diff --git a/judge/views/organization.py b/judge/views/organization.py index 248f32c..9a5b52b 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -644,6 +644,7 @@ class KickUserWidgetView( LoginRequiredMixin, AdminOrganizationMixin, SingleObjectMixin, View ): model = Organization + def post(self, request, *args, **kwargs): organization = self.get_object() try: diff --git a/judge/views/problem.py b/judge/views/problem.py index 4c76f92..050f8fa 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -306,78 +306,9 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView): context["meta_description"] = self.object.summary or metadata[0] context["og_image"] = self.object.og_image or metadata[1] - context["can_vote"] = self.object.can_vote(self.request) - if context["can_vote"]: - try: - context["vote"] = ProblemPointsVote.objects.get( - voter=user.profile, problem=self.object - ) - except ObjectDoesNotExist: - context["vote"] = None - else: - context["vote"] = None - - context["has_votes"] = False - if user.is_superuser: - all_votes = list( - self.object.problem_points_votes.order_by("points").values_list( - "points", flat=True - ) - ) - context["all_votes"] = all_votes - context["has_votes"] = len(all_votes) > 0 - context["max_possible_vote"] = 600 - context["min_possible_vote"] = 100 return context -class DeleteVote(ProblemMixin, SingleObjectMixin, View): - def get(self, request, *args, **kwargs): - return HttpResponseForbidden(status=405, content_type="text/plain") - - def post(self, request, *args, **kwargs): - self.object = self.get_object() - if not request.user.is_authenticated: - return HttpResponseForbidden("Not signed in.", content_type="text/plain") - elif self.object.can_vote(request.user): - ProblemPointsVote.objects.filter( - voter=request.profile, problem=self.object - ).delete() - return HttpResponse("success", content_type="text/plain") - else: - return HttpResponseForbidden( - "Not allowed to delete votes on this problem.", - content_type="text/plain", - ) - - -class Vote(ProblemMixin, SingleObjectMixin, View): - def get(self, request, *args, **kwargs): - return HttpResponseForbidden(status=405, content_type="text/plain") - - def post(self, request, *args, **kwargs): - self.object = self.get_object() - if not self.object.can_vote(request): # Not allowed to vote for some reason. - return HttpResponseForbidden( - "Not allowed to vote on this problem.", content_type="text/plain" - ) - - form = ProblemPointsVoteForm(request.POST) - if form.is_valid(): - with transaction.atomic(): - # Delete any pre existing votes. - ProblemPointsVote.objects.filter( - voter=request.profile, problem=self.object - ).delete() - vote = form.save(commit=False) - vote.voter = request.profile - vote.problem = self.object - vote.save() - return JsonResponse({"points": vote.points}) - else: - return JsonResponse(form.errors, status=400) - - class LatexError(Exception): pass diff --git a/judge/views/submission.py b/judge/views/submission.py index d8e1473..3e284b3 100644 --- a/judge/views/submission.py +++ b/judge/views/submission.py @@ -18,6 +18,7 @@ from django.http import HttpResponseRedirect from django.http import JsonResponse from django.shortcuts import get_object_or_404 from django.shortcuts import render +from django.template.defaultfilters import floatformat from django.urls import reverse from django.utils import timezone from django.utils.functional import cached_property @@ -47,6 +48,7 @@ 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 from judge.utils.views import TitleMixin +from judge.utils.timedelta import nice_repr def submission_related(queryset): @@ -358,7 +360,8 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): ) if self.selected_statuses: queryset = queryset.filter( - Q(result__in=self.selected_statuses) | Q(status__in=self.selected_statuses) + Q(result__in=self.selected_statuses) + | Q(status__in=self.selected_statuses) ) return queryset @@ -392,9 +395,7 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): hidden_codes = ["SC", "D", "G"] if not self.request.user.is_superuser and not self.request.user.is_staff: hidden_codes += ["IE"] - return [ - (key, value) for key, value in all_statuses if key not in hidden_codes - ] + return [(key, value) for key, value in all_statuses if key not in hidden_codes] def get_context_data(self, **kwargs): context = super(SubmissionsListBase, self).get_context_data(**kwargs) @@ -782,3 +783,30 @@ class UserContestSubmissions(ForceContestMixin, UserProblemSubmissions): self.contest.name, reverse("contest_view", args=[self.contest.key]), ) + + +class UserContestSubmissionsAjax(UserContestSubmissions): + template_name = "submission/user-ajax.html" + + def contest_time(self, s): + if s.contest.participation.live: + return s.date - s.contest.participation.real_start + return None + + def get_context_data(self, **kwargs): + context = super(UserContestSubmissionsAjax, self).get_context_data(**kwargs) + context["contest"] = self.contest + context["problem"] = self.problem + context["profile"] = self.profile + + contest_problem = self.contest.contest_problems.get(problem=self.problem) + for s in context["submissions"]: + contest_time = self.contest_time(s) + if contest_time: + s.contest_time = nice_repr(contest_time, "noday") + else: + s.contest_time = None + points = floatformat(s.contest.points, -self.contest.points_precision) + total = floatformat(contest_problem.points, -self.contest.points_precision) + s.display_point = f"{points} / {total}" + return context diff --git a/resources/submission.scss b/resources/submission.scss index c66a718..afb1f2e 100644 --- a/resources/submission.scss +++ b/resources/submission.scss @@ -362,3 +362,9 @@ label[for="language"], label[for="status"] { color: gray; } } + +.lightbox-submissions { + td { + padding-right: 0.2em; + } +} \ No newline at end of file diff --git a/templates/contest/media-js.html b/templates/contest/media-js.html index 3e0ac48..108e976 100644 --- a/templates/contest/media-js.html +++ b/templates/contest/media-js.html @@ -1,4 +1,142 @@ + \ No newline at end of file diff --git a/templates/contest/ranking-table.html b/templates/contest/ranking-table.html index d9d674e..8a97818 100644 --- a/templates/contest/ranking-table.html +++ b/templates/contest/ranking-table.html @@ -58,7 +58,7 @@ {% block before_point_head %} {% for problem in problems %} - + {{- contest.get_label_for_problem(loop.index0) }}
{{ problem.points }}
diff --git a/templates/contest/ranking.html b/templates/contest/ranking.html index c0d4e04..b9788fa 100644 --- a/templates/contest/ranking.html +++ b/templates/contest/ranking.html @@ -8,6 +8,8 @@ {% endblock %} {% block users_media %} + + {% if has_rating %} @@ -227,215 +235,6 @@ }); {% endif %} - - {% include "contest/media-js.html" %} {% endblock %} diff --git a/templates/problem/problem.html b/templates/problem/problem.html index 64e4b3d..c4ef5da 100644 --- a/templates/problem/problem.html +++ b/templates/problem/problem.html @@ -346,9 +346,6 @@ {% endblock %} {% block description %} - {% if can_vote and not vote %} - {% include 'problem/voting-form.html' %} - {% endif %} {% if contest_problem and contest_problem.contest.use_clarifications and has_clarifications %}
@@ -374,9 +371,6 @@ {% endblock %} {% block post_description_end %} - {% if can_vote or request.user.is_superuser %} - {% include 'problem/voting-controls.html' %} - {% endif %} {% if request.user.is_authenticated and not request.profile.mute %} diff --git a/templates/problem/voting-controls.html b/templates/problem/voting-controls.html deleted file mode 100644 index 1f93193..0000000 --- a/templates/problem/voting-controls.html +++ /dev/null @@ -1,75 +0,0 @@ - - -{% if can_vote or request.user.is_superuser %} - - - {% if can_vote %} - - {% endif %} - {% if request.user.is_superuser %} - - {% include 'problem/voting-stats.html' %} - {% endif %} - - - - -{% endif %} \ No newline at end of file diff --git a/templates/problem/voting-form.html b/templates/problem/voting-form.html deleted file mode 100644 index 0dc2fd3..0000000 --- a/templates/problem/voting-form.html +++ /dev/null @@ -1,93 +0,0 @@ - -
- {% csrf_token %} - - - - - - - - - - - - -
- {{_('How difficult is this problem?')}} - -
- - {% for i in range(1, (max_possible_vote - min_possible_vote) // 100 + 2) %} - - {% endfor %} - -
-
- {{_('This helps us improve the site')}} - - {{min_possible_vote}} - {{max_possible_vote}} -
-
- -
-
- -
-
- \ No newline at end of file diff --git a/templates/problem/voting-stats.html b/templates/problem/voting-stats.html deleted file mode 100644 index c37896e..0000000 --- a/templates/problem/voting-stats.html +++ /dev/null @@ -1,146 +0,0 @@ - -{{ _('Statistics') }} - - - \ No newline at end of file diff --git a/templates/submission/user-ajax.html b/templates/submission/user-ajax.html new file mode 100644 index 0000000..1b825e0 --- /dev/null +++ b/templates/submission/user-ajax.html @@ -0,0 +1,30 @@ +

+ {{_('Contest submissions of')}} {{link_user(profile)}} # +

+
+ +{% for submission in submissions %} + + {% set can_view = submission_layout(submission, profile_id, request.user, editable_problem_ids, completed_problem_ids) %} + + + + + {% if can_view %} + + {% endif %} + +{% endfor %} + \ No newline at end of file