Add backend for resolver
This commit is contained in:
parent
793ccb52fd
commit
36007e86ed
8 changed files with 156 additions and 18 deletions
|
@ -138,7 +138,7 @@ class NewIOIContestFormat(IOIContestFormat):
|
||||||
/ problem_data["total_points"]
|
/ problem_data["total_points"]
|
||||||
* problem_data["problem_points"]
|
* problem_data["problem_points"]
|
||||||
)
|
)
|
||||||
if self.config["cumtime"] and points:
|
if self.config["cumtime"] and problem_data["points"]:
|
||||||
cumtime += penalty
|
cumtime += penalty
|
||||||
score += problem_data["points"]
|
score += problem_data["points"]
|
||||||
|
|
||||||
|
|
|
@ -295,6 +295,7 @@ class ContestMixin(object):
|
||||||
context["meta_description"] = self.object.summary or metadata[0]
|
context["meta_description"] = self.object.summary or metadata[0]
|
||||||
context["og_image"] = self.object.og_image or metadata[1]
|
context["og_image"] = self.object.og_image or metadata[1]
|
||||||
context["has_moss_api_key"] = settings.MOSS_API_KEY is not None
|
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
|
context["logo_override_image"] = self.object.logo_override_image
|
||||||
if (
|
if (
|
||||||
not context["logo_override_image"]
|
not context["logo_override_image"]
|
||||||
|
|
|
@ -1,18 +1,148 @@
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
|
from judge.models import Contest
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
class Resolver(TemplateView):
|
class Resolver(TemplateView):
|
||||||
title = _("Resolver")
|
title = _("Resolver")
|
||||||
template_name = "resolver/resolver.html"
|
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):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(Resolver, self).get_context_data(**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
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if request.user.is_superuser:
|
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()
|
return HttpResponseForbidden()
|
||||||
|
|
|
@ -252,7 +252,6 @@
|
||||||
|
|
||||||
.score {
|
.score {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
width: 70px;
|
|
||||||
color: var(--theme-text-color);
|
color: var(--theme-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,10 @@ function Resolver(problem_sub, sub_frozen, problems, users) {
|
||||||
this.delay = false;
|
this.delay = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function round2(num) {
|
||||||
|
return Math.round(num * 100) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
Resolver.prototype.status = function (problem) {
|
Resolver.prototype.status = function (problem) {
|
||||||
if (problem.old_verdict == 'NA' && problem.new_verdict == 'NA') {
|
if (problem.old_verdict == 'NA' && problem.new_verdict == 'NA') {
|
||||||
return 'untouched';
|
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][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].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][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) {
|
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][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 += 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][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].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 += 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) {
|
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);
|
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];
|
var tmp = this.rankarr[i];
|
||||||
if (tmp.problem[j].new_point > tmp.problem[j].old_point) {
|
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 += 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].old_point = tmp.problem[j].new_point;
|
||||||
tmp.problem[j].new_point = 0;
|
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].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].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_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 = {
|
var op = {
|
||||||
id: this.operations.length,
|
id: this.operations.length,
|
||||||
type: 'sub',
|
type: 'sub',
|
||||||
|
@ -299,6 +308,7 @@ Resolver.prototype.operation = function (rankid, problemid, sub) {
|
||||||
};
|
};
|
||||||
if (tmp.problem[problemid].new_point > tmp.problem[problemid].old_point) {
|
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 += 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].old_point = tmp.problem[problemid].new_point;
|
||||||
tmp.problem[problemid].new_point = 0;
|
tmp.problem[problemid].new_point = 0;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
{{ make_tab_item('ranking', 'fa fa-bar-chart', None, _('Hidden Rankings')) }}
|
{{ make_tab_item('ranking', 'fa fa-bar-chart', None, _('Hidden Rankings')) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% 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')) }}
|
{{ make_tab_item('resolver', 'fa fa-check', url('resolver', contest.key), _('Resolver')) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_edit %}
|
{% if can_edit %}
|
||||||
|
|
|
@ -7,18 +7,15 @@
|
||||||
var speed = 700;
|
var speed = 700;
|
||||||
var autopress = false;
|
var autopress = false;
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$.getJSON('{{ contest_json }}', function (data) {
|
let data = {{ contest_json }};
|
||||||
console.log("OK");
|
console.log("OK");
|
||||||
var resolver = new Resolver(data.problem_sub, data.sub_frozen, data.problems, data.users);
|
var resolver = new Resolver(data.problem_sub, data.sub_frozen, data.problems, data.users);
|
||||||
window.resolver = resolver;
|
window.resolver = resolver;
|
||||||
resolver.calcOperations();
|
resolver.calcOperations();
|
||||||
resolver.frozen_op = resolver.operations.length - 1;
|
resolver.frozen_op = resolver.operations.length - 1;
|
||||||
resetCSS();
|
resetCSS();
|
||||||
light_theme();
|
light_theme();
|
||||||
show_rank();
|
show_rank();
|
||||||
}).fail(function(){
|
|
||||||
console.log("An error has occurred.");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateSelector() {
|
function updateSelector() {
|
||||||
|
@ -74,6 +71,7 @@
|
||||||
tmp.problem[operation.problem_index].old_verdict = operation.new_verdict;
|
tmp.problem[operation.problem_index].old_verdict = operation.new_verdict;
|
||||||
tmp.problem[operation.problem_index].new_verdict = 'NA';
|
tmp.problem[operation.problem_index].new_verdict = 'NA';
|
||||||
tmp.problem[operation.problem_index].old_point = operation.new_point;
|
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').removeClass('selected');
|
||||||
$('#rank-' + operation.user_id + ' .p-' + operation.problem_index + ' .pro-score').addClass(resolver.status(tmp.problem[operation.problem_index]));
|
$('#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);
|
$('#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].old_verdict = operation.new_verdict;
|
||||||
tmp.problem[operation.problem_index][operation.problem_sub].new_verdict = 'NA';
|
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 += 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;
|
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 + ' .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);
|
$('#rank-' + operation.user_id + ' .p-' + operation.problem_index + '.s-' + operation.problem_sub + ' .p-content').text(operation.new_point);
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="{{ static('bootstrap/bootstrap.min.css') }}">
|
<link rel="stylesheet" href="{{ static('bootstrap/bootstrap.min.css') }}">
|
||||||
<link rel="stylesheet" href="{{ static('resolver.css') }}">
|
<link rel="stylesheet" href="{{ static('resolver.css') }}">
|
||||||
<link rel="shortcut icon" href="#">
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
|
||||||
|
|
Loading…
Reference in a new issue