diff --git a/judge/contest_format/new_ioi.py b/judge/contest_format/new_ioi.py index 9571c34..3661d7e 100644 --- a/judge/contest_format/new_ioi.py +++ b/judge/contest_format/new_ioi.py @@ -138,7 +138,7 @@ class NewIOIContestFormat(IOIContestFormat): / problem_data["total_points"] * problem_data["problem_points"] ) - if self.config["cumtime"] and points: + if self.config["cumtime"] and problem_data["points"]: cumtime += penalty score += problem_data["points"] diff --git a/judge/views/contests.py b/judge/views/contests.py index 743ea30..8df20b2 100644 --- a/judge/views/contests.py +++ b/judge/views/contests.py @@ -295,6 +295,7 @@ class ContestMixin(object): context["meta_description"] = self.object.summary or metadata[0] context["og_image"] = self.object.og_image or metadata[1] context["has_moss_api_key"] = settings.MOSS_API_KEY is not None + context["can_use_resolver"] = self.object.format_name == "ioi16" context["logo_override_image"] = self.object.logo_override_image if ( not context["logo_override_image"] diff --git a/judge/views/resolver.py b/judge/views/resolver.py index db0c140..f53c16d 100644 --- a/judge/views/resolver.py +++ b/judge/views/resolver.py @@ -1,18 +1,148 @@ from django.views.generic import TemplateView from django.utils.translation import gettext as _ from django.http import HttpResponseForbidden +from judge.models import Contest +from django.utils.safestring import mark_safe + +import json class Resolver(TemplateView): title = _("Resolver") template_name = "resolver/resolver.html" + def get_contest_json(self): + problems = self.contest.contest_problems.values_list("order", "id") + order_to_id = {} + id_to_order = {} + for order, problem_id in problems: + id_to_order[str(problem_id)] = order + + frozen_subtasks = self.contest.format.get_frozen_subtasks() + num_problems = len(problems) + problem_sub = [0] * num_problems + sub_frozen = [0] * num_problems + problems_json = {str(i): {} for i in range(1, num_problems + 1)} + + users = {} + cnt_user = 0 + total_subtask_points_map = {} + + for participation in self.contest.users.filter(virtual=0): + cnt_user += 1 + users[str(cnt_user)] = { + "username": participation.user.user.username, + "name": participation.user.user.first_name + or participation.user.user.username, + "school": participation.user.user.last_name, + "last_submission": participation.cumtime, + "problems": {}, + } + for ( + problem_id, + problem_points, + time, + subtask_points, + total_subtask_points, + subtask, + sub_id, + ) in self.contest.format.get_results_by_subtask(participation, True): + problem_id = str(problem_id) + order = id_to_order[problem_id] + problem_sub[order - 1] = max(problem_sub[order - 1], subtask) + if total_subtask_points: + total_subtask_points_map[(order, subtask)] = total_subtask_points + + cnt_user = 0 + for participation in self.contest.users.filter(virtual=0): + cnt_user += 1 + total_points = {} + points_map = {} + frozen_points_map = {} + problem_points_map = {} + for ( + problem_id, + problem_points, + time, + subtask_points, + total_subtask_points, + subtask, + sub_id, + ) in self.contest.format.get_results_by_subtask(participation, True): + problem_id = str(problem_id) + order = id_to_order[problem_id] + points_map[(order, subtask)] = subtask_points + if order not in total_points: + total_points[order] = 0 + total_points[order] += total_subtask_points + problem_points_map[order] = problem_points + + for ( + problem_id, + problem_points, + time, + subtask_points, + total_subtask_points, + subtask, + sub_id, + ) in self.contest.format.get_results_by_subtask(participation, False): + problem_id = str(problem_id) + order = id_to_order[problem_id] + frozen_points_map[(order, subtask)] = subtask_points + + for order in range(1, num_problems + 1): + for subtask in range(1, problem_sub[order - 1] + 1): + if not total_points.get(order, 0): + continue + if str(order) not in users[str(cnt_user)]["problems"]: + users[str(cnt_user)]["problems"][str(order)] = { + "points": {}, + "frozen_points": {}, + } + problems_json[str(order)][str(subtask)] = round( + total_subtask_points_map[(order, subtask)] + / total_points[order] + * problem_points_map[order], + self.contest.points_precision, + ) + users[str(cnt_user)]["problems"][str(order)]["points"][ + str(subtask) + ] = round( + points_map.get((order, subtask), 0) + / total_points[order] + * problem_points_map[order], + self.contest.points_precision, + ) + users[str(cnt_user)]["problems"][str(order)]["frozen_points"][ + str(subtask) + ] = round( + frozen_points_map.get((order, subtask), 0) + / total_points[order] + * problem_points_map[order], + self.contest.points_precision, + ) + + for i in frozen_subtasks: + order = id_to_order[i] + if frozen_subtasks[i]: + sub_frozen[order - 1] = min(frozen_subtasks[i]) + else: + sub_frozen[order - 1] = problem_sub[order - 1] + 1 + return { + "problem_sub": problem_sub, + "sub_frozen": sub_frozen, + "problems": problems_json, + "users": users, + } + def get_context_data(self, **kwargs): context = super(Resolver, self).get_context_data(**kwargs) - context["contest_json"] = "/static/contest.json" + context["contest_json"] = mark_safe(json.dumps(self.get_contest_json())) return context def get(self, request, *args, **kwargs): if request.user.is_superuser: - return super(Resolver, self).get(request, *args, **kwargs) + self.contest = Contest.objects.get(key=kwargs.get("contest")) + if self.contest.format_name == "ioi16": + return super(Resolver, self).get(request, *args, **kwargs) return HttpResponseForbidden() diff --git a/resources/resolver.css b/resources/resolver.css index 6d7cee4..3b67421 100644 --- a/resources/resolver.css +++ b/resources/resolver.css @@ -252,7 +252,6 @@ .score { font-weight: bold; - width: 70px; color: var(--theme-text-color); } diff --git a/resources/resolver.js b/resources/resolver.js index 6ccbc45..53b7c07 100644 --- a/resources/resolver.js +++ b/resources/resolver.js @@ -10,6 +10,10 @@ function Resolver(problem_sub, sub_frozen, problems, users) { this.delay = false; } +function round2(num) { + return Math.round(num * 100) / 100; +} + Resolver.prototype.status = function (problem) { if (problem.old_verdict == 'NA' && problem.new_verdict == 'NA') { return 'untouched'; @@ -83,15 +87,18 @@ Resolver.prototype.calcOperations = function () { this.rank[id].problem[problemid][j].old_point = this.users[id].problems[problemid].frozen_points[j]; this.rank[id].problem[problemid].old_point += this.rank[id].problem[problemid][j].old_point; this.rank[id].problem[problemid][j].old_verdict = this.pointstatus(this.users[id].problems[problemid].frozen_points[j], problemid, j); + this.rank[id].problem[problemid].old_point = round2(this.rank[id].problem[problemid].old_point) if (this.users[id].problems[problemid].points[j] != -1) { this.rank[id].problem[problemid][j].new_point = this.users[id].problems[problemid].points[j]; this.rank[id].problem[problemid].new_point += this.rank[id].problem[problemid][j].new_point; + this.rank[id].problem[problemid].new_point = round2(this.rank[id].problem[problemid].new_point) this.rank[id].problem[problemid][j].new_verdict = this.pointstatus(this.users[id].problems[problemid].points[j], problemid, j); } } } this.rank[id].problem[problemid].old_verdict = this.pointstatus(this.rank[id].problem[problemid].old_point); this.rank[id].score += this.rank[id].problem[problemid].old_point; + this.rank[id].score = round2(this.rank[id].score) if (this.users[id].problems[problemid].points[1] != -1) { this.rank[id].problem[problemid].new_verdict = this.pointstatus(this.rank[id].problem[problemid].new_point); } @@ -162,6 +169,7 @@ Resolver.prototype.calcOperations = function () { var tmp = this.rankarr[i]; if (tmp.problem[j].new_point > tmp.problem[j].old_point) { tmp.score += tmp.problem[j].new_point - tmp.problem[j].old_point; + tmp.score = round2(tmp.score); } tmp.problem[j].old_point = tmp.problem[j].new_point; tmp.problem[j].new_point = 0; @@ -266,6 +274,7 @@ Resolver.prototype.operation = function (rankid, problemid, sub) { this.rankarr[rankid].problem[problemid].new_point = this.rankarr[rankid].problem[problemid].old_point + this.rankarr[rankid].problem[problemid][sub].new_point - this.rankarr[rankid].problem[problemid][sub].old_point; this.rankarr[rankid].problem[problemid].new_verdict = this.pointstatus(this.rankarr[rankid].problem[problemid].new_point); + this.rankarr[rankid].problem[problemid].new_point = round2(this.rankarr[rankid].problem[problemid].new_point) var op = { id: this.operations.length, type: 'sub', @@ -299,6 +308,7 @@ Resolver.prototype.operation = function (rankid, problemid, sub) { }; if (tmp.problem[problemid].new_point > tmp.problem[problemid].old_point) { tmp.score += tmp.problem[problemid].new_point - tmp.problem[problemid].old_point; + tmp.score = round2(tmp.score) } tmp.problem[problemid].old_point = tmp.problem[problemid].new_point; tmp.problem[problemid].new_point = 0; diff --git a/templates/contest/contest-tabs.html b/templates/contest/contest-tabs.html index 993e0dd..f6094e8 100644 --- a/templates/contest/contest-tabs.html +++ b/templates/contest/contest-tabs.html @@ -22,7 +22,7 @@ {{ make_tab_item('ranking', 'fa fa-bar-chart', None, _('Hidden Rankings')) }} {% endif %} {% endif %} - {% if request.user.is_superuser %} + {% if request.user.is_superuser and can_use_resolver %} {{ make_tab_item('resolver', 'fa fa-check', url('resolver', contest.key), _('Resolver')) }} {% endif %} {% if can_edit %} diff --git a/templates/resolver/media-js.html b/templates/resolver/media-js.html index d6fce6a..972d341 100644 --- a/templates/resolver/media-js.html +++ b/templates/resolver/media-js.html @@ -7,18 +7,15 @@ var speed = 700; var autopress = false; $(document).ready(function() { - $.getJSON('{{ contest_json }}', function (data) { - console.log("OK"); - var resolver = new Resolver(data.problem_sub, data.sub_frozen, data.problems, data.users); - window.resolver = resolver; - resolver.calcOperations(); - resolver.frozen_op = resolver.operations.length - 1; - resetCSS(); - light_theme(); - show_rank(); - }).fail(function(){ - console.log("An error has occurred."); - }); + let data = {{ contest_json }}; + console.log("OK"); + var resolver = new Resolver(data.problem_sub, data.sub_frozen, data.problems, data.users); + window.resolver = resolver; + resolver.calcOperations(); + resolver.frozen_op = resolver.operations.length - 1; + resetCSS(); + light_theme(); + show_rank(); }); function updateSelector() { @@ -74,6 +71,7 @@ tmp.problem[operation.problem_index].old_verdict = operation.new_verdict; tmp.problem[operation.problem_index].new_verdict = 'NA'; tmp.problem[operation.problem_index].old_point = operation.new_point; + tmp.score = round2(tmp.score); $('#rank-' + operation.user_id + ' .p-' + operation.problem_index + ' .pro-score').removeClass('selected'); $('#rank-' + operation.user_id + ' .p-' + operation.problem_index + ' .pro-score').addClass(resolver.status(tmp.problem[operation.problem_index])); $('#rank-' + operation.user_id + ' .score').text(tmp.score); @@ -124,6 +122,7 @@ tmp.problem[operation.problem_index][operation.problem_sub].old_verdict = operation.new_verdict; tmp.problem[operation.problem_index][operation.problem_sub].new_verdict = 'NA'; tmp.problem[operation.problem_index].old_point += operation.new_point - operation.old_point; + tmp.problem[operation.problem_index].old_point = round2(tmp.problem[operation.problem_index].old_point); tmp.problem[operation.problem_index][operation.problem_sub].old_point = operation.new_point; $('#rank-' + operation.user_id + ' .p-' + operation.problem_index + ' .pro-score').text(tmp.problem[operation.problem_index].old_point); $('#rank-' + operation.user_id + ' .p-' + operation.problem_index + '.s-' + operation.problem_sub + ' .p-content').text(operation.new_point); diff --git a/templates/resolver/resolver.html b/templates/resolver/resolver.html index adeb516..3309425 100644 --- a/templates/resolver/resolver.html +++ b/templates/resolver/resolver.html @@ -8,7 +8,6 @@ -