Skip to content

Commit

Permalink
Merge branch 'master' into truong/ip-based-login
Browse files Browse the repository at this point in the history
  • Loading branch information
hung3a8 authored Jul 8, 2023
2 parents fc04f54 + d43611c commit f3ff24e
Show file tree
Hide file tree
Showing 21 changed files with 255 additions and 135 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ uwsgi.ini
.fuse_hidden*
resources/dark
resources/martor-description.css
resources/select2-dmoj.css
resources/style.css
resources/vars.scss
sass_processed
Expand Down
3 changes: 3 additions & 0 deletions dmoj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,9 @@
'judge.jinja2.DMOJExtension',
'judge.jinja2.spaceless.SpacelessExtension',
],
'bytecode_cache': {
'enabled': True,
},
},
},
{
Expand Down
3 changes: 2 additions & 1 deletion judge/contest_format/atcoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@ def update_participation(self, participation):
participation.format_data = format_data
participation.save()

def display_user_problem(self, participation, contest_problem, frozen=False):
def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
penalty = format_html('<small style="color:red"> ({penalty})</small>',
penalty=floatformat(format_data['penalty'])) if format_data['penalty'] else ''
return format_html(
'<td class="{state}"><a href="{url}">{points}{penalty}<div class="solving-time">{time}</div></a></td>',
state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solves.get(str(contest_problem.id), None) == participation.id else '') +
self.best_solution_state(format_data['points'], contest_problem.points)),
url=reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]),
Expand Down
21 changes: 18 additions & 3 deletions judge/contest_format/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,29 @@ def update_participation(self, participation):
raise NotImplementedError()

@abstractmethod
def display_user_problem(self, participation, contest_problem, frozen=False):
def get_first_solves_and_total_ac(self, problems, participations, frozen=False):
"""
Returns two dictionaries mapping ContestProblem to the first ContestParticipation that solves it
and the total number of accepted submissions.
:param problems: A list of ContestProblem objects.
:param participations: A list of ContestParticipation objects.
:param frozen: Whether the ranking is frozen or not. Only useful for ICPC/VNOJ format.
:return: A tuple of two dictionaries. First one maps ContestProblem's ID to ContestParticipation's ID,
or None if no solves yet. Second one maps ContestProblem's ID to total number of accepted submissions.
"""
raise NotImplementedError()

@abstractmethod
def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
"""
Returns the HTML fragment to show a user's performance on an individual problem. This is expected to use
information from the format_data field instead of computing it from scratch.
:param participation: The ContestParticipation object linking the user to the contest.
:param contest_problem: The ContestProblem object representing the problem in question.
:param frozen: Whether the ranking is frozen or not. Only useful for ICPC format.
:param first_solves: The first dictionary returned by get_first_solves_and_total_ac.
:param frozen: Whether the ranking is frozen or not. Only useful for ICPC/VNOJ format.
:return: An HTML fragment, marked as safe for Jinja2.
"""
raise NotImplementedError()
Expand All @@ -67,7 +82,7 @@ def display_participation_result(self, participation, frozen=False):
information from the format_data field instead of computing it from scratch.
:param participation: The ContestParticipation object.
:param frozen: Whether the ranking is frozen or not. Only useful for ICPC format.
:param frozen: Whether the ranking is frozen or not. Only useful for ICPC/VNOJ format.
:return: An HTML fragment, marked as safe for Jinja2.
"""
raise NotImplementedError()
Expand Down
30 changes: 29 additions & 1 deletion judge/contest_format/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,40 @@ def update_participation(self, participation):
participation.format_data = format_data
participation.save()

def display_user_problem(self, participation, contest_problem, frozen=False):
def get_first_solves_and_total_ac(self, problems, participations, frozen=False):
first_solves = {}
total_ac = {}

for problem in problems:
problem_id = str(problem.id)
min_time = None
first_solves[problem_id] = None
total_ac[problem_id] = 0

for participation in participations:
format_data = (participation.format_data or {}).get(problem_id)
if format_data:
points = format_data['points']
time = format_data['time']

if points == problem.points:
total_ac[problem_id] += 1

# Only acknowledge first solves for live participations
if participation.virtual == 0 and (min_time is None or min_time > time):
min_time = time
first_solves[problem_id] = participation.id

return first_solves, total_ac

def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))

if format_data:
return format_html(
'<td class="{state}"><a href="{url}">{points}<div class="solving-time">{time}</div></a></td>',
state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solves.get(str(contest_problem.id), None) == participation.id else '') +
self.best_solution_state(format_data['points'], contest_problem.points)),
url=reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]),
Expand Down
3 changes: 2 additions & 1 deletion judge/contest_format/ecoo.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def update_participation(self, participation):
participation.format_data = format_data
participation.save()

def display_user_problem(self, participation, contest_problem, frozen=False):
def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
bonus = format_html('<small> +{bonus}</small>',
Expand All @@ -107,6 +107,7 @@ def display_user_problem(self, participation, contest_problem, frozen=False):
return format_html(
'<td class="{state}"><a href="{url}">{points}{bonus}<div class="solving-time">{time}</div></a></td>',
state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solves.get(str(contest_problem.id), None) == participation.id else '') +
self.best_solution_state(format_data['points'], contest_problem.points)),
url=reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]),
Expand Down
30 changes: 29 additions & 1 deletion judge/contest_format/icpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,34 @@ def update_participation(self, participation):
participation.format_data = format_data
participation.save()

def display_user_problem(self, participation, contest_problem, frozen=False):
def get_first_solves_and_total_ac(self, problems, participations, frozen=False):
first_solves = {}
total_ac = {}

prefix = 'frozen_' if frozen else ''
for problem in problems:
problem_id = str(problem.id)
min_time = None
first_solves[problem_id] = None
total_ac[problem_id] = 0

for participation in participations:
format_data = (participation.format_data or {}).get(problem_id)
if format_data:
points = format_data[prefix + 'points']
time = format_data['time']

if points == problem.points:
total_ac[problem_id] += 1

# Only acknowledge first solves for live participations
if participation.virtual == 0 and (min_time is None or min_time > time):
min_time = time
first_solves[problem_id] = participation.id

return first_solves, total_ac

def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
# This prefix is used to help get the correct data from the format_data dictionary
Expand All @@ -163,6 +190,7 @@ def display_user_problem(self, participation, contest_problem, frozen=False):
# The cell will have `pending` css class if there is a new score-changing submission after the frozen time
state = (('pending ' if frozen and format_data['is_frozen'] else '') +
('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solves.get(str(contest_problem.id), None) == participation.id else '') +
self.best_solution_state(format_data[prefix + 'points'], contest_problem.points))
url = reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code])
Expand Down
30 changes: 29 additions & 1 deletion judge/contest_format/legacy_ioi.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,41 @@ def update_participation(self, participation):
participation.format_data = format_data
participation.save()

def display_user_problem(self, participation, contest_problem, frozen=False):
def get_first_solves_and_total_ac(self, problems, participations, frozen=False):
first_solves = {}
total_ac = {}

show_time = self.config['cumtime'] or self.config.get('last_score_altering', False)
for problem in problems:
problem_id = str(problem.id)
min_time = None
first_solves[problem_id] = None
total_ac[problem_id] = 0

for participation in participations:
format_data = (participation.format_data or {}).get(problem_id)
if format_data:
points = format_data['points']
time = format_data['time']

if points == problem.points:
total_ac[problem_id] += 1

# Only acknowledge first solves for live participations
if show_time and participation.virtual == 0 and (min_time is None or min_time > time):
min_time = time
first_solves[problem_id] = participation.id

return first_solves, total_ac

def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
show_time = self.config['cumtime'] or self.config.get('last_score_altering', False)
return format_html(
'<td class="{state}"><a href="{url}">{points}<div class="solving-time">{time}</div></a></td>',
state=(('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solves.get(str(contest_problem.id), None) == participation.id else '') +
self.best_solution_state(format_data['points'], contest_problem.points)),
url=reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code]),
Expand Down
79 changes: 66 additions & 13 deletions judge/contest_format/vnoj.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
SELECT MIN(csub.date)
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 ccs.points = MAX(cs.points)AND csub.date < %s
WHERE ccs.problem_id = cp.id AND ccs.participation_id = %s AND ccs.points = MAX(cs.points) AND csub.date < %s
) AS `time`, cp.id AS `prob`
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
Expand Down Expand Up @@ -163,38 +163,91 @@ def update_participation(self, participation):
participation.format_data = format_data
participation.save()

def display_user_problem(self, participation, contest_problem, frozen=False):
def get_first_solves_and_total_ac(self, problems, participations, frozen=False):
first_solves = {}
total_ac = {}

for problem in problems:
problem_id = str(problem.id)
min_time = None
first_solves[problem_id] = None
total_ac[problem_id] = 0

for participation in participations:
format_data = (participation.format_data or {}).get(problem_id)
if format_data:
has_pending = bool(format_data.get('pending', 0))
prefix = 'frozen_' if frozen and has_pending else ''
points = format_data[prefix + 'points']
time = format_data[prefix + 'time']

if points == problem.points:
total_ac[problem_id] += 1

# Only acknowledge first solves for live participations
if participation.virtual == 0 and (min_time is None or min_time > time):
min_time = time
first_solves[problem_id] = participation.id

return first_solves, total_ac

def display_user_problem(self, participation, contest_problem, first_solves, frozen=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))

if format_data:
first_solved = first_solves.get(str(contest_problem.id), None) == participation.id
url = reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code])

if not frozen:
# Fast path for non-frozen contests
penalty = format_html(
'<small style="color:red"> ({penalty})</small>',
penalty=floatformat(format_data['penalty']),
) if format_data['penalty'] else ''

state = (('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solved else '') +
self.best_solution_state(format_data['points'], contest_problem.points))

points = floatformat(format_data['points'], -self.contest.points_precision)
time = nice_repr(timedelta(seconds=format_data['time']), 'noday')

return format_html(
'<td class="{state}"><a href="{url}"><div>{points}{penalty}</div>'
'<div class="solving-time">{time}</div></a></td>',
state=state,
url=url,
points=points,
penalty=penalty,
time=time,
)

# This prefix is used to help get the correct data from the format_data dictionary
has_pending = bool(format_data.get('pending', 0))

prefix = 'frozen_' if frozen and has_pending else ''
prefix = 'frozen_' if has_pending else ''

# AC before frozen_time
if format_data[prefix + 'points'] == contest_problem.points:
if has_pending and format_data[prefix + 'points'] == contest_problem.points:
has_pending = False
prefix = ''
frozen = False

penalty = format_html(
'<small style="color:red"> ({penalty})</small>',
penalty=floatformat(format_data[prefix + 'penalty']),
) if format_data[prefix + 'penalty'] else ''

state = (('pending ' if frozen and has_pending else '') +
state = (('pending ' if has_pending else '') +
('pretest-' if self.contest.run_pretests_only and contest_problem.is_pretested else '') +
('first-solve ' if first_solved else '') +
self.best_solution_state(format_data[prefix + 'points'], contest_problem.points))

url = reverse('contest_user_submissions',
args=[self.contest.key, participation.user.user.username, contest_problem.problem.code])

points = floatformat(format_data[prefix + 'points'], -self.contest.points_precision)
time = nice_repr(timedelta(seconds=format_data[prefix + 'time']), 'noday')
pending = format_html(' <small style="color:black;" class="ahihi">[{pending}]</small>',
pending=floatformat(format_data['pending'])) if frozen and has_pending else ''
pending = format_html(' <small style="color:black;">[{pending}]</small>',
pending=floatformat(format_data['pending'])) if has_pending else ''

if frozen and has_pending:
if has_pending:
time = '?'
# hide penalty if there are pending submissions
penalty = ''
Expand Down
11 changes: 11 additions & 0 deletions judge/management/commands/import_polygon_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,17 @@ def get_tests_by_batch(name):
for batch in each_test_batches:
del problem_meta['batches'][batch]

# Ignore zero-point batches
zero_point_batches = [name for name, batch in problem_meta['batches'].items() if batch['points'] == 0]
if len(zero_point_batches) > 0:
print('Found zero-point batches:', ', '.join(zero_point_batches))
print('Would you like ignore them (y/n)? ', end='', flush=True)
if input().lower() in ['y', 'yes']:
problem_meta['batches'] = {
name: batch for name, batch in problem_meta['batches'].items() if batch['points'] > 0
}
print(f'Ignored {len(zero_point_batches)} zero-point batches')

# Sort tests by index
problem_meta['normal_cases'].sort()
for batch in problem_meta['batches'].values():
Expand Down
Loading

0 comments on commit f3ff24e

Please sign in to comment.