Skip to content

Commit cb748e2

Browse files
committed
Display score change in submissions admin
1 parent 9d763fb commit cb748e2

File tree

12 files changed

+123
-11
lines changed

12 files changed

+123
-11
lines changed

oioioi/acm/controllers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ def can_see_round(self, request_or_context, round, no_admin=False):
201201
def get_default_safe_exec_mode(self):
202202
return 'cpu'
203203

204+
def calculate_score_diff(self):
205+
return False
206+
204207

205208
class ACMOpenContestController(ACMContestController):
206209
description = _("ACM style contest (open)")

oioioi/contests/admin.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,8 @@ def get_list_display(self, request):
712712
]
713713
if request.contest:
714714
list_display.remove('contest_display')
715+
if request.contest.controller.calculate_score_diff():
716+
list_display.append('score_diff_display')
715717
return list_display
716718

717719
def get_list_display_links(self, request, list_display):
@@ -848,6 +850,14 @@ def score_display(self, instance):
848850
score_display.short_description = _("Score")
849851
score_display.admin_order_field = 'score_with_nulls_smallest'
850852

853+
def score_diff_display(self, instance):
854+
if instance.score_diff is not None:
855+
return format_html(instance.problem_instance.contest.controller.render_score_diff(instance.score_diff))
856+
return format_html('<span class="text-secondary">-</span>')
857+
858+
score_diff_display.short_description = _("Score change")
859+
score_diff_display.admin_order_field = 'score'
860+
851861
def contest_display(self, instance):
852862
return instance.problem_instance.contest
853863

oioioi/contests/controllers.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
UserResultForRound,
3434
submission_kinds,
3535
)
36+
from oioioi.contests.scores import IntegerScore
3637
from oioioi.contests.utils import (
3738
generic_rounds_times,
3839
has_any_active_round,
@@ -756,7 +757,7 @@ def update_user_result_for_contest(self, result):
756757
)
757758
result.score = self._sum_scores(scores)
758759

759-
def update_user_results(self, user, problem_instance):
760+
def update_user_results(self, user, problem_instance, submission):
760761
"""Updates score for problem instance, round and contest.
761762
762763
Usually this method creates instances (if they don't exist) of:
@@ -777,7 +778,7 @@ def update_user_results(self, user, problem_instance):
777778

778779
# First: UserResultForProblem
779780

780-
problem.controller.update_user_results(user, problem_instance)
781+
problem.controller.update_user_results(user, problem_instance, submission)
781782

782783
# Second: UserResultForRound
783784
with transaction.atomic():
@@ -974,6 +975,25 @@ def _is_partial_score(self, test_report):
974975
def show_default_fields(self, problem_instance):
975976
return problem_instance.problem.controller.show_default_fields(problem_instance)
976977

978+
def calculate_score_diff(self):
979+
return True
980+
981+
def score_diff(self, before, after):
982+
print(before, after)
983+
if before is None:
984+
return after
985+
return IntegerScore(after.to_int() - before.to_int())
986+
987+
def render_score_diff(self, diff):
988+
print(diff)
989+
if diff is None:
990+
return '<span class="text-secondary">-</span>'
991+
if diff.to_int() == 0:
992+
return '<span class="text-secondary">0</span>'
993+
if diff.to_int() > 0:
994+
return f'<span class="text-success">+{diff.to_int()}</span>'
995+
return f'<span class="text-danger">{diff.to_int()}</span>'
996+
977997

978998
class PastRoundsHiddenContestControllerMixin(object):
979999
"""ContestController mixin that hides past rounds

oioioi/contests/handlers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def update_user_results(env, **kwargs):
104104
assert 'round_id' not in env
105105
assert 'contest_id' not in env
106106

107-
problem_instance.controller.update_user_results(user, problem_instance)
107+
problem_instance.controller.update_user_results(user, problem_instance, submission)
108108

109109
return env
110110

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 4.2.16 on 2024-09-12 12:25
2+
3+
from django.db import migrations
4+
import oioioi.contests.fields
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('contests', '0018_contest_show_contest_rules'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='submission',
16+
name='score_diff',
17+
field=oioioi.contests.fields.ScoreField(blank=True, max_length=255, null=True),
18+
),
19+
]

oioioi/contests/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ class Submission(models.Model):
516516
score = ScoreField(blank=True, null=True, verbose_name=_("score"))
517517
status = EnumField(submission_statuses, default='?', verbose_name=_("status"))
518518
comment = models.TextField(blank=True, verbose_name=_("comment"))
519+
score_diff = ScoreField(blank=True, null=True)
519520

520521
@property
521522
def problem(self):

oioioi/contests/tests/tests.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4200,3 +4200,39 @@ def test_score_badge(self):
42004200
self.assertIn('badge-warning', self._get_badge_for_problem(response.content, 'zad2'))
42014201
self.assertIn('badge-danger', self._get_badge_for_problem(response.content, 'zad3'))
42024202

4203+
4204+
class TestScoreDiff(TestCase):
4205+
fixtures = [
4206+
'test_users',
4207+
'test_contest',
4208+
'test_full_package',
4209+
'test_problem_instance',
4210+
'test_submission',
4211+
]
4212+
4213+
def _test_diff(self, new_score, expected_diff):
4214+
pi = ProblemInstance.objects.get()
4215+
user = User.objects.get(username='test_user')
4216+
new_submission = Submission.objects.create(
4217+
problem_instance=pi,
4218+
user=user,
4219+
score=IntegerScore(new_score),
4220+
status='OK',
4221+
)
4222+
pi.controller.update_user_results(user, pi, new_submission)
4223+
assert new_submission.score_diff == IntegerScore(expected_diff)
4224+
4225+
def test_simple_programming_contest(self):
4226+
# Simple programming contest takes last score as the final one.
4227+
self._test_diff(50, 50 - 34)
4228+
self._test_diff(100, 100 - 50)
4229+
self._test_diff(20, 20 - 100)
4230+
4231+
def test_oi_finals_contest(self):
4232+
# OI finals contest takes the best score as the final one.
4233+
contest = Contest.objects.get()
4234+
contest.controller_name = 'oioioi.oi.controllers.OIFinalOnsiteContestController'
4235+
contest.save()
4236+
self._test_diff(50, 50 - 34)
4237+
self._test_diff(100, 100 - 50)
4238+
self._test_diff(20, 0)

oioioi/oi/controllers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ def can_see_test_comments(self, request, submissionreport):
279279

280280
def reveal_score(self, request, submission):
281281
super(BOIOnsiteContestController, self).reveal_score(request, submission)
282-
self.update_user_results(submission.user, submission.problem_instance)
282+
self.update_user_results(submission.user, submission.problem_instance, submission)
283283

284284
def update_user_result_for_problem(self, result):
285285
try:

oioioi/pa/tests.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,13 @@ def test_pa_user_results(self):
347347
[result.score for result in UserResultForProblem.objects.filter(user=user)]
348348
)
349349
for pi in ProblemInstance.objects.all():
350-
contest.controller.update_user_results(user, pi)
350+
contest.controller.update_user_results(
351+
user,
352+
pi,
353+
Submission.objects.filter(
354+
problem_instance=pi,
355+
).first(),
356+
)
351357
new_results = sorted(
352358
[result.score for result in UserResultForProblem.objects.filter(user=user)]
353359
)

oioioi/problems/controllers.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ def update_report_statuses(self, submission, queryset):
464464
controller._activate_newest_report(submission, queryset, kind=['INITIAL'])
465465
controller._activate_newest_report(submission, queryset, kind=['USER_OUTS'])
466466

467-
def update_user_results(self, user, problem_instance):
467+
def update_user_results(self, user, problem_instance, submission):
468468
"""Updates score for problem instance.
469469
470470
Usually this method creates instances (if they don't exist) of:
@@ -480,8 +480,19 @@ def update_user_results(self, user, problem_instance):
480480
) = UserResultForProblem.objects.select_for_update().get_or_create(
481481
user=user, problem_instance=problem_instance
482482
)
483+
if problem_instance.controller.calculate_score_diff():
484+
if created:
485+
score_before = None
486+
else:
487+
score_before = result.score
483488
problem_instance.controller.update_user_result_for_problem(result)
484489
result.save()
490+
if problem_instance.controller.calculate_score_diff():
491+
score_after = result.score
492+
submission.score_diff = problem_instance.controller.score_diff(
493+
score_before, score_after
494+
)
495+
submission.save()
485496

486497
def validate_submission_form(self, request, problem_instance, form, cleaned_data):
487498
return cleaned_data
@@ -660,7 +671,7 @@ def change_submission_kind(self, submission, kind):
660671
submission.problem_instance.controller.judge(submission, is_rejudge=True)
661672
if submission.user:
662673
submission.problem_instance.controller.update_user_results(
663-
submission.user, submission.problem_instance
674+
submission.user, submission.problem_instance, submission
664675
)
665676
if old_kind == 'NORMAL' or kind == 'NORMAL':
666677
submission.problem_instance.problem.controller.recalculate_statistics_for_user(

0 commit comments

Comments
 (0)