NDOJ/judge/contest_format/ecoo.py

161 lines
6.5 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
from datetime import timedelta
from django.core.exceptions import ValidationError
from django.db import connection
from django.template.defaultfilters import floatformat
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.utils.timedelta import nice_repr
2022-05-14 17:57:27 +00:00
@register_contest_format("ecoo")
2020-01-21 06:35:58 +00:00
class ECOOContestFormat(DefaultContestFormat):
2022-05-14 17:57:27 +00:00
name = gettext_lazy("ECOO")
config_defaults = {"cumtime": False, "first_ac_bonus": 10, "time_bonus": 5}
config_validators = {
"cumtime": lambda x: True,
"first_ac_bonus": lambda x: x >= 0,
"time_bonus": lambda x: x >= 0,
}
"""
2020-01-21 06:35:58 +00:00
cumtime: Specify True if cumulative time is to be used in breaking ties. Defaults to False.
first_ac_bonus: The number of points to award if a solution gets AC on its first non-IE/CE run. Defaults to 10.
time_bonus: Number of minutes to award an extra point for submitting before the contest end.
Specify 0 to disable. Defaults to 5.
2022-05-14 17:57:27 +00:00
"""
2020-01-21 06:35:58 +00:00
@classmethod
def validate(cls, config):
if config is None:
return
if not isinstance(config, dict):
2022-05-14 17:57:27 +00:00
raise ValidationError(
"ECOO-styled contest expects no config or dict as config"
)
2020-01-21 06:35:58 +00:00
for key, value in config.items():
if key not in cls.config_defaults:
raise ValidationError('unknown config key "%s"' % key)
if not isinstance(value, type(cls.config_defaults[key])):
raise ValidationError('invalid type for config key "%s"' % key)
if not cls.config_validators[key](value):
2022-05-14 17:57:27 +00:00
raise ValidationError(
'invalid value "%s" for config key "%s"' % (value, key)
)
2020-01-21 06:35:58 +00:00
def __init__(self, contest, config):
self.config = self.config_defaults.copy()
self.config.update(config or {})
self.contest = contest
def update_participation(self, participation):
cumtime = 0
points = 0
format_data = {}
with connection.cursor() as cursor:
2022-05-14 17:57:27 +00:00
cursor.execute(
"""
2020-01-21 06:35:58 +00:00
SELECT (
SELECT MAX(ccs.points)
FROM judge_contestsubmission ccs LEFT OUTER JOIN
judge_submission csub ON (csub.id = ccs.submission_id)
WHERE ccs.problem_id = cp.id AND ccs.participation_id = %s AND csub.date = MAX(sub.date)
) AS `score`, MAX(sub.date) AS `time`, cp.id AS `prob`, (
SELECT COUNT(ccs.id)
FROM judge_contestsubmission ccs LEFT OUTER JOIN
judge_submission csub ON (csub.id = ccs.submission_id)
WHERE ccs.problem_id = cp.id AND ccs.participation_id = %s AND csub.result NOT IN ('IE', 'CE')
) AS `subs`, cp.points AS `max_score`
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
GROUP BY cp.id
2022-05-14 17:57:27 +00:00
""",
(participation.id, participation.id, participation.id),
)
2020-01-21 06:35:58 +00:00
for score, time, prob, subs, max_score in cursor.fetchall():
time = from_database_time(time)
dt = (time - participation.start).total_seconds()
2022-05-14 17:57:27 +00:00
if self.config["cumtime"]:
2020-01-21 06:35:58 +00:00
cumtime += dt
bonus = 0
if score > 0:
# First AC bonus
if subs == 1 and score == max_score:
2022-05-14 17:57:27 +00:00
bonus += self.config["first_ac_bonus"]
2020-01-21 06:35:58 +00:00
# Time bonus
2022-05-14 17:57:27 +00:00
if self.config["time_bonus"]:
bonus += (
(participation.end_time - time).total_seconds()
// 60
// self.config["time_bonus"]
)
2020-01-21 06:35:58 +00:00
points += bonus
2022-05-14 17:57:27 +00:00
format_data[str(prob)] = {"time": dt, "points": score, "bonus": bonus}
2020-01-21 06:35:58 +00:00
points += score
participation.cumtime = cumtime
participation.score = points
2021-05-24 20:18:39 +00:00
participation.tiebreaker = 0
2020-01-21 06:35:58 +00:00
participation.format_data = format_data
participation.save()
def display_user_problem(self, participation, contest_problem):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
2022-05-14 17:57:27 +00:00
bonus = (
format_html(
"<small> +{bonus}</small>", bonus=floatformat(format_data["bonus"])
)
if format_data["bonus"]
else ""
)
2020-01-21 06:35:58 +00:00
return format_html(
'<td class="{state}"><a data-featherlight="{url}" href="#">{points}{bonus}<div class="solving-time">{time}</div></a></td>',
2022-05-14 17:57:27 +00:00
state=(
(
"pretest-"
if self.contest.run_pretests_only
and contest_problem.is_pretested
else ""
)
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
),
url=reverse(
"contest_user_submissions_ajax",
2022-05-14 17:57:27 +00:00
args=[
self.contest.key,
participation.user.user.username,
contest_problem.problem.code,
],
),
points=floatformat(format_data["points"]),
2020-01-21 06:35:58 +00:00
bonus=bonus,
2022-05-14 17:57:27 +00:00
time=nice_repr(timedelta(seconds=format_data["time"]), "noday"),
2020-01-21 06:35:58 +00:00
)
else:
2022-05-14 17:57:27 +00:00
return mark_safe("<td></td>")
2020-01-21 06:35:58 +00:00
def display_participation_result(self, participation):
return format_html(
'<td class="user-points">{points}<div class="solving-time">{cumtime}</div></td>',
points=floatformat(participation.score),
2022-05-14 17:57:27 +00:00
cumtime=nice_repr(timedelta(seconds=participation.cumtime), "noday")
if self.config["cumtime"]
else "",
2020-01-21 06:35:58 +00:00
)