Skip to content

Commit 50d0d70

Browse files
committed
New "explanation" field has been added to the problem test-cases settings, the data included in the 'xpl' file will be displayed at the problem statement after its related output.
The test-case information on the problem statement has been arranged by test-cases in order to make it more readable.
1 parent 8fec955 commit 50d0d70

File tree

5 files changed

+70
-18
lines changed

5 files changed

+70
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.2.25 on 2024-08-18 07:16
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('judge', '0147_infer_test_cases_from_zip'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='problemtestcase',
15+
name='explanation_file',
16+
field=models.CharField(blank=True, null=True, default='', max_length=100, verbose_name='explanation file name'),
17+
),
18+
]

judge/models/problem_data.py

+37-16
Original file line numberDiff line numberDiff line change
@@ -104,40 +104,40 @@ def setup_test_cases_content(self):
104104

105105
for i, tc in enumerate([x for x in test_cases if x.is_pretest]):
106106
self.append_tescase_to_statement(zip, content, tc, i)
107-
last = i
108-
109-
if last > 0:
110-
last += 1
107+
last = i + 1
111108

112109
for i, tc in enumerate([x for x in test_cases if not x.is_pretest]):
113110
self.append_tescase_to_statement(zip, content, tc, i + last)
114111

115112
self.test_cases_content = '\n'.join(content)
116113

117114
def append_tescase_to_statement(self, zip, content, tc, i):
118-
content.append(f'## Sample Input {i+1}')
119-
content.append('')
115+
content.append(f'## Test Case {i+1}')
120116

121117
if tc.is_private:
122118
content.append('*Hidden: this is a private test case!* ')
123119

124120
else:
121+
content.append('### Input')
125122
content.append('```')
126-
content.append(zip.read(tc.input_file).decode('utf-8'))
123+
if tc.input_file != '':
124+
content.append(zip.read(tc.input_file).decode('utf-8'))
127125
content.append('```')
128126

129-
content.append('')
130-
content.append(f'## Sample Output {i+1}')
131-
content.append('')
132-
133-
if tc.is_private:
134-
content.append('*Hidden: this is a private test case!* ')
135-
136-
else:
127+
content.append('')
128+
content.append('### Output')
137129
content.append('```')
138-
content.append(zip.read(tc.output_file).decode('utf-8'))
130+
if tc.output_file != '':
131+
content.append(zip.read(tc.output_file).decode('utf-8'))
139132
content.append('```')
140133

134+
if tc.explanation_file != '':
135+
content.append('')
136+
content.append('### Explanation')
137+
138+
if tc.explanation_file != '':
139+
content.append(zip.read(tc.explanation_file).decode('utf-8'))
140+
141141
content.append('')
142142

143143
def infer_test_cases_from_zip(self):
@@ -157,6 +157,22 @@ def infer_test_cases_from_zip(self):
157157
input = [x for x in files if '.in' in x or ('input' in x and '.' in x)]
158158
output = [x for x in files if '.out' in x or ('output' in x and '.' in x)]
159159

160+
# Not all explanations are mandatory, so there can be gaps!
161+
input.sort()
162+
output.sort()
163+
explanation = []
164+
for i in range(len(input)):
165+
in_file = input[i]
166+
167+
xpl_file = in_file.replace('input', 'explanation') if 'input' in in_file else in_file.replace('in', 'xpl')
168+
found = [x for x in files if xpl_file in x]
169+
found.sort()
170+
171+
if len(found) > 0:
172+
explanation.append(found[0])
173+
else:
174+
explanation.append('')
175+
160176
cases = []
161177
for i in range(len(input)):
162178
list = ProblemTestCase.objects.filter(dataset_id=self.problem.pk, input_file=input[i],
@@ -173,6 +189,7 @@ def infer_test_cases_from_zip(self):
173189
ptc.order = i
174190
ptc.input_file = input[i]
175191
ptc.output_file = output[i]
192+
ptc.explanation_file = explanation[i]
176193
ptc.points = 0
177194

178195
cases.append(ptc)
@@ -242,6 +259,9 @@ def _load_test_case_from_doc(self, doc, field, is_pretest):
242259
if test.get('out'):
243260
ptc.output_file = test['out']
244261

262+
if test.get('xpl'):
263+
ptc.explanation_file = test['xpl']
264+
245265
if test.get('points'):
246266
ptc.points = test['points']
247267
else:
@@ -287,6 +307,7 @@ class ProblemTestCase(models.Model):
287307
default='C')
288308
input_file = models.CharField(max_length=100, verbose_name=_('input file name'), blank=True)
289309
output_file = models.CharField(max_length=100, verbose_name=_('output file name'), blank=True)
310+
explanation_file = models.CharField(max_length=100, verbose_name=_('explanation file name'), blank=True)
290311
generator_args = models.TextField(verbose_name=_('generator arguments'), blank=True)
291312
points = models.IntegerField(verbose_name=_('point value'), blank=True, null=True)
292313
is_pretest = models.BooleanField(verbose_name=_('case is pretest?'), default=False)

judge/utils/problem_data.py

+7
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,16 @@ def make_checker(case):
9292
if case.output_file not in self.files:
9393
raise ProblemDataError(_('Output file for case %d does not exist: %s') %
9494
(i, case.output_file))
95+
if case.explanation_file != '' and case.explanation_file not in self.files:
96+
raise ProblemDataError(_('Explanation file for case %d does not exist: %s') %
97+
(i, case.explanation_file))
9598

9699
if case.input_file:
97100
data['in'] = case.input_file
98101
if case.output_file:
99102
data['out'] = case.output_file
103+
if case.explanation_file:
104+
data['xpl'] = case.explanation_file
100105
if case.points is not None:
101106
data['points'] = case.points
102107
if case.generator_args:
@@ -151,6 +156,7 @@ def make_checker(case):
151156
case.checker_args = ''
152157
case.input_file = ''
153158
case.output_file = ''
159+
case.explanation_file = ''
154160
case.save(update_fields=('checker_args', 'input_file', 'output_file'))
155161
elif case.type == 'E':
156162
if not batch:
@@ -159,6 +165,7 @@ def make_checker(case):
159165
case.is_private = batch['is_private']
160166
case.input_file = ''
161167
case.output_file = ''
168+
case.explanation_file = ''
162169
case.generator_args = ''
163170
case.checker = ''
164171
case.checker_args = ''

judge/views/problem_data.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ class ProblemCaseForm(ModelForm):
7575

7676
class Meta:
7777
model = ProblemTestCase
78-
fields = ('order', 'type', 'input_file', 'output_file', 'points', 'is_pretest', 'is_private', 'output_limit',
79-
'output_prefix', 'checker', 'checker_args', 'generator_args', 'batch_dependencies')
78+
fields = ('order', 'type', 'input_file', 'output_file', 'explanation_file', 'points', 'is_pretest',
79+
'is_private', 'output_limit', 'output_prefix', 'checker', 'checker_args', 'generator_args',
80+
'batch_dependencies')
8081
widgets = {
8182
'generator_args': HiddenInput,
8283
'batch_dependencies': HiddenInput,

templates/problem/data.html

+5
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@
404404
<th class="type-column">{{ _('Type') }}</th>
405405
<th>{{ _('Input file') }}</th>
406406
<th>{{ _('Output file') }}</th>
407+
<th>{{ _('Explanation file') }}</th>
407408
<th>{{ _('Points') }}</th>
408409
<th>{{ _('Pretest?') }}</th>
409410
<th>{{ _('Private?') }}</th>
@@ -439,6 +440,10 @@
439440
form['output_file'].value() in valid_files) %} class="bad-file"{% endif %}>
440441
{{ form.output_file.errors }}{{ form.output_file }}
441442
</td>
443+
<td{% if not (form.empty_permitted or form['type'].value() != 'C' or form['explanation_file'].value() == '' or
444+
form['explanation_file'].value() in valid_files) %} class="bad-file"{% endif %}>
445+
{{ form.explanation_file.errors }}{{ form.explanation_file }}
446+
</td>
442447
<td>{{ form.points.errors }}{{ form.points }}</td>
443448
<td>{{ form.is_pretest.errors }}{{ form.is_pretest }}</td>
444449
<td>{{ form.is_private.errors }}{{ form.is_private }}</td>

0 commit comments

Comments
 (0)