Skip to content

Commit 023f2b6

Browse files
int-y1kiritofeng
authored andcommitted
Add support for archiving a submission
1 parent f34b90c commit 023f2b6

File tree

18 files changed

+116
-28
lines changed

18 files changed

+116
-28
lines changed

judge/admin/submission.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ def get_formset(self, request, obj=None, **kwargs):
120120

121121
class SubmissionAdmin(VersionAdmin):
122122
readonly_fields = ('user', 'problem', 'date', 'judged_date')
123-
fields = ('user', 'problem', 'date', 'judged_date', 'locked_after', 'time', 'memory', 'points', 'language',
124-
'status', 'result', 'case_points', 'case_total', 'judged_on', 'error')
123+
fields = ('user', 'problem', 'date', 'judged_date', 'locked_after', 'is_archived', 'time', 'memory', 'points',
124+
'language', 'status', 'result', 'case_points', 'case_total', 'judged_on', 'error')
125125
actions = ('judge', 'recalculate_score')
126126
list_display = ('id', 'problem_code', 'problem_name', 'user_column', 'execution_time', 'pretty_memory',
127127
'points', 'language_column', 'status', 'result', 'judge_column')
@@ -135,6 +135,8 @@ def get_readonly_fields(self, request, obj=None):
135135
fields = self.readonly_fields
136136
if not request.user.has_perm('judge.lock_submission'):
137137
fields += ('locked_after',)
138+
if not request.user.has_perm('judge.archive_submission'):
139+
fields += ('is_archived',)
138140
return fields
139141

140142
def get_queryset(self, request):
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Generated by Django 4.2.17 on 2025-03-02 10:16
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('judge', '0150_add_performance_ceiling'),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name='submission',
15+
options={
16+
'permissions': (
17+
('abort_any_submission', 'Abort any submission'),
18+
('rejudge_submission', 'Rejudge the submission'),
19+
('rejudge_submission_lot', 'Rejudge a lot of submissions'),
20+
('spam_submission', 'Submit without limit'),
21+
('view_all_submission', 'View all submission'),
22+
('resubmit_other', "Resubmit others' submission"),
23+
('lock_submission', 'Change lock status of submission'),
24+
('archive_submission', 'Archive any submission'),
25+
),
26+
'verbose_name': 'submission',
27+
'verbose_name_plural': 'submissions',
28+
},
29+
),
30+
migrations.AddField(
31+
model_name='submission',
32+
name='is_archived',
33+
field=models.BooleanField(default=False, verbose_name='is archived'),
34+
),
35+
]

judge/models/problem.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,8 @@ def submission_source_visibility(self):
394394
return self.submission_source_visibility_mode
395395

396396
def update_stats(self):
397-
all_queryset = self.submission_set.filter(user__is_unlisted=False)
398-
ac_queryset = all_queryset.filter(points__gte=self.points, result='AC')
397+
all_queryset = self.submission_set.filter(user__is_unlisted=False, is_archived=False)
398+
ac_queryset = all_queryset.filter(result='AC', case_points__gte=F('case_total'))
399399
self.user_count = ac_queryset.values('user').distinct().count()
400400
submissions = all_queryset.count()
401401
if submissions:
@@ -466,7 +466,8 @@ def save(self, *args, **kwargs):
466466

467467
def is_solved_by(self, user):
468468
# Return true if a full AC submission to the problem from the user exists.
469-
return self.submission_set.filter(user=user.profile, result='AC', points__gte=F('problem__points')).exists()
469+
return self.submission_set.filter(
470+
user=user.profile, is_archived=False, result='AC', case_points__gte=F('case_total')).exists()
470471

471472
def vote_permission_for_user(self, user):
472473
if not user.is_authenticated:

judge/models/profile.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def display_name(self):
224224

225225
@cached_property
226226
def has_any_solves(self):
227-
return self.submission_set.filter(result='AC', case_points__gte=F('case_total')).exists()
227+
return self.submission_set.filter(is_archived=False, result='AC', case_points__gte=F('case_total')).exists()
228228

229229
@cached_property
230230
def resolved_ace_theme(self):
@@ -243,15 +243,16 @@ def calculate_points(self, table=_pp_table):
243243
from judge.models import Problem
244244
public_problems = Problem.get_public_problems()
245245
data = (
246-
public_problems.filter(submission__user=self, submission__points__isnull=False)
246+
public_problems.filter(submission__user=self, submission__is_archived=False,
247+
submission__points__isnull=False)
247248
.annotate(max_points=Max('submission__points')).order_by('-max_points')
248249
.values_list('max_points', flat=True).filter(max_points__gt=0)
249250
)
250251
bonus_function = settings.DMOJ_PP_BONUS_FUNCTION
251252
points = sum(data)
252253
entries = min(len(data), len(table))
253254
problems = (
254-
public_problems.filter(submission__user=self, submission__result='AC',
255+
public_problems.filter(submission__user=self, submission__is_archived=False, submission__result='AC',
255256
submission__case_points__gte=F('submission__case_total'))
256257
.values('id').distinct().count()
257258
)

judge/models/submission.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class Submission(models.Model):
8888
contest_object = models.ForeignKey('Contest', verbose_name=_('contest'), null=True, blank=True,
8989
on_delete=models.SET_NULL, related_name='+', db_index=False)
9090
locked_after = models.DateTimeField(verbose_name=_('submission lock'), null=True, blank=True)
91+
is_archived = models.BooleanField(verbose_name=_('is archived'), default=False)
9192

9293
@classmethod
9394
def result_class_from_code(cls, result, case_points, case_total):
@@ -132,6 +133,12 @@ def judge(self, *args, rejudge=False, force_judge=False, rejudge_user=None, **kw
132133

133134
judge.alters_data = True
134135

136+
def archive(self):
137+
self.is_archived = True
138+
self.save(update_fields=['is_archived'])
139+
140+
archive.alters_data = True
141+
135142
def abort(self):
136143
abort_submission(self)
137144

@@ -227,6 +234,7 @@ class Meta:
227234
('view_all_submission', _('View all submission')),
228235
('resubmit_other', _("Resubmit others' submission")),
229236
('lock_submission', _('Change lock status of submission')),
237+
('archive_submission', _('Archive any submission')),
230238
)
231239
verbose_name = _('submission')
232240
verbose_name_plural = _('submissions')

judge/models/tests/test_problem.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ def give_basic_problem_ac(self, user, points=None):
251251
problem=self.basic_problem,
252252
result='AC',
253253
points=self.basic_problem.points if points is None else points,
254+
case_points=(self.basic_problem.points if points is None else points) * 100,
255+
case_total=self.basic_problem.points * 100,
254256
language=Language.get_python3(),
255257
)
256258

@@ -294,8 +296,8 @@ def test_problem_voting_permissions(self):
294296
self.assertEqual(self.basic_problem.vote_permission_for_user(self.users['normal']), VotePermission.VOTE)
295297

296298
partial_ac = create_user(username='partial_ac')
297-
self.give_basic_problem_ac(partial_ac, 0.5) # ensure this value is not equal to its point value
298-
self.assertNotEqual(self.basic_problem.points, 0.5)
299+
self.give_basic_problem_ac(partial_ac, 0.5) # ensure this value is less than its point value
300+
self.assertLess(0.5, self.basic_problem.points)
299301
self.assertEqual(self.basic_problem.vote_permission_for_user(partial_ac), VotePermission.VIEW)
300302

301303
def test_problems_list(self):

judge/performance_points.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@ def get_pp_breakdown(user, start=0, end=settings.DMOJ_PP_ENTRIES):
3737
INNER JOIN judge_submission ON (judge_problem.id = judge_submission.problem_id)
3838
WHERE (judge_problem.is_public AND
3939
NOT judge_problem.is_organization_private AND
40+
NOT judge_submission.is_archived AND
4041
judge_submission.points IS NOT NULL AND
4142
judge_submission.user_id = %s)
4243
GROUP BY judge_problem.id
4344
HAVING MAX(judge_submission.points) > 0.0
4445
) AS max_points_table
4546
{join_type} judge_submission ON (
4647
judge_submission.problem_id = max_points_table.problem_id AND
48+
NOT judge_submission.is_archived AND
4749
judge_submission.points = max_points_table.max_points AND
4850
judge_submission.user_id = %s
4951
)

judge/signals.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ def submission_delete(sender, instance, **kwargs):
127127
instance.problem.update_stats()
128128

129129

130+
@receiver(post_save, sender=Submission)
131+
def submission_update(sender, instance, update_fields, **kwargs):
132+
if update_fields and 'is_archived' in update_fields:
133+
finished_submission(instance)
134+
instance.user._updating_stats_only = True
135+
instance.user.calculate_points()
136+
instance.problem._updating_stats_only = True
137+
instance.problem.update_stats()
138+
139+
130140
@receiver(post_delete, sender=ContestSubmission)
131141
def contest_submission_delete(sender, instance, **kwargs):
132142
participation = instance.participation

judge/tasks/submission.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,34 @@
1010
__all__ = ('apply_submission_filter', 'rejudge_problem_filter', 'rescore_problem')
1111

1212

13-
def apply_submission_filter(queryset, id_range, languages, results):
13+
def apply_submission_filter(queryset, id_range, languages, results, archive_locked):
1414
if id_range:
1515
start, end = id_range
1616
queryset = queryset.filter(id__gte=start, id__lte=end)
1717
if languages:
1818
queryset = queryset.filter(language_id__in=languages)
1919
if results:
2020
queryset = queryset.filter(result__in=results)
21-
queryset = queryset.exclude(locked_after__lt=timezone.now()) \
22-
.exclude(status__in=Submission.IN_PROGRESS_GRADING_STATUS)
21+
if not archive_locked:
22+
queryset = queryset.exclude(locked_after__lt=timezone.now())
23+
queryset = queryset.exclude(status__in=Submission.IN_PROGRESS_GRADING_STATUS)
2324
return queryset
2425

2526

2627
@shared_task(bind=True)
27-
def rejudge_problem_filter(self, problem_id, id_range=None, languages=None, results=None, user_id=None):
28+
def rejudge_problem_filter(self, problem_id, id_range=None, languages=None, results=None, archive_locked=None,
29+
user_id=None):
2830
queryset = Submission.objects.filter(problem_id=problem_id)
29-
queryset = apply_submission_filter(queryset, id_range, languages, results)
31+
queryset = apply_submission_filter(queryset, id_range, languages, results, archive_locked)
3032
user = User.objects.get(id=user_id)
3133

3234
rejudged = 0
3335
with Progress(self, queryset.count()) as p:
3436
for submission in queryset.iterator():
35-
submission.judge(rejudge=True, batch_rejudge=True, rejudge_user=user)
37+
if not submission.is_locked:
38+
submission.judge(rejudge=True, batch_rejudge=True, rejudge_user=user)
39+
else:
40+
submission.archive()
3641
rejudged += 1
3742
if rejudged % 10 == 0:
3843
p.done = rejudged

judge/utils/problems.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ def user_completed_ids(profile):
3434
key = 'user_complete:%d' % profile.id
3535
result = cache.get(key)
3636
if result is None:
37-
result = set(Submission.objects.filter(user=profile, result='AC', case_points__gte=F('case_total'))
37+
result = set(Submission.objects
38+
.filter(user=profile, is_archived=False, result='AC', case_points__gte=F('case_total'))
3839
.values_list('problem_id', flat=True).distinct())
3940
cache.set(key, result, 86400)
4041
return result

0 commit comments

Comments
 (0)