Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New "explanation" field has been added to the problem test-cases sett…
Browse files Browse the repository at this point in the history
…ings, 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.
FherStk committed Aug 18, 2024
1 parent 8fec955 commit a3a4763
Showing 5 changed files with 72 additions and 19 deletions.
18 changes: 18 additions & 0 deletions judge/migrations/0148_test_case_explanation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.25 on 2024-08-18 07:16

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('judge', '0147_infer_test_cases_from_zip'),
]

operations = [
migrations.AddField(
model_name='problemtestcase',
name='explanation_file',
field=models.CharField(blank=True, null=True, default='', max_length=100, verbose_name='explanation file name'),
),
]
56 changes: 39 additions & 17 deletions judge/models/problem_data.py
Original file line number Diff line number Diff line change
@@ -104,40 +104,41 @@ def setup_test_cases_content(self):

for i, tc in enumerate([x for x in test_cases if x.is_pretest]):
self.append_tescase_to_statement(zip, content, tc, i)
last = i

if last > 0:
last += 1
last = i + 1

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

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

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

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

else:
content.append('### Input')
content.append('```')
content.append(zip.read(tc.input_file).decode('utf-8'))
if tc.input_file != '':
content.append(zip.read(tc.input_file).decode('utf-8'))
content.append('```')

content.append('')
content.append(f'## Sample Output {i+1}')
content.append('')

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

else:

content.append('')
content.append('### Output')
content.append('```')
content.append(zip.read(tc.output_file).decode('utf-8'))
if tc.output_file != '':
content.append(zip.read(tc.output_file).decode('utf-8'))
content.append('```')

if tc.explanation_file != '':
content.append('')
content.append('### Explanation')
content.append('```')
if tc.explanation_file != '':
content.append(zip.read(tc.explanation_file).decode('utf-8'))
content.append('```')

content.append('')

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

# Not all explanations are mandatory, so there can be gaps!
input.sort()
output.sort()
explanation = []
for i in range(len(input)):
in_file = input[i]

xpl_file = in_file.replace('input', 'explanation') if 'input' in in_file else in_file.replace('in', 'xpl')
found = [x for x in files if xpl_file in x]
found.sort()

if len(found) > 0:
explanation.append(found[0])
else:
explanation.append('')

cases = []
for i in range(len(input)):
list = ProblemTestCase.objects.filter(dataset_id=self.problem.pk, input_file=input[i],
@@ -173,6 +190,7 @@ def infer_test_cases_from_zip(self):
ptc.order = i
ptc.input_file = input[i]
ptc.output_file = output[i]
ptc.explanation_file = explanation[i]
ptc.points = 0

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

if test.get('xpl'):
ptc.explanation_file = test['xpl']

if test.get('points'):
ptc.points = test['points']
else:
@@ -287,6 +308,7 @@ class ProblemTestCase(models.Model):
default='C')
input_file = models.CharField(max_length=100, verbose_name=_('input file name'), blank=True)
output_file = models.CharField(max_length=100, verbose_name=_('output file name'), blank=True)
explanation_file = models.CharField(max_length=100, verbose_name=_('explanation file name'), blank=True)
generator_args = models.TextField(verbose_name=_('generator arguments'), blank=True)
points = models.IntegerField(verbose_name=_('point value'), blank=True, null=True)
is_pretest = models.BooleanField(verbose_name=_('case is pretest?'), default=False)
7 changes: 7 additions & 0 deletions judge/utils/problem_data.py
Original file line number Diff line number Diff line change
@@ -92,11 +92,16 @@ def make_checker(case):
if case.output_file not in self.files:
raise ProblemDataError(_('Output file for case %d does not exist: %s') %
(i, case.output_file))
if case.explanation_file != '' and case.explanation_file not in self.files:
raise ProblemDataError(_('Explanation file for case %d does not exist: %s') %
(i, case.explanation_file))

if case.input_file:
data['in'] = case.input_file
if case.output_file:
data['out'] = case.output_file
if case.explanation_file:
data['xpl'] = case.explanation_file
if case.points is not None:
data['points'] = case.points
if case.generator_args:
@@ -151,6 +156,7 @@ def make_checker(case):
case.checker_args = ''
case.input_file = ''
case.output_file = ''
case.explanation_file = ''
case.save(update_fields=('checker_args', 'input_file', 'output_file'))
elif case.type == 'E':
if not batch:
@@ -159,6 +165,7 @@ def make_checker(case):
case.is_private = batch['is_private']
case.input_file = ''
case.output_file = ''
case.explanation_file = ''
case.generator_args = ''
case.checker = ''
case.checker_args = ''
5 changes: 3 additions & 2 deletions judge/views/problem_data.py
Original file line number Diff line number Diff line change
@@ -75,8 +75,9 @@ class ProblemCaseForm(ModelForm):

class Meta:
model = ProblemTestCase
fields = ('order', 'type', 'input_file', 'output_file', 'points', 'is_pretest', 'is_private', 'output_limit',
'output_prefix', 'checker', 'checker_args', 'generator_args', 'batch_dependencies')
fields = ('order', 'type', 'input_file', 'output_file', 'explanation_file', 'points', 'is_pretest',
'is_private', 'output_limit', 'output_prefix', 'checker', 'checker_args', 'generator_args',
'batch_dependencies')
widgets = {
'generator_args': HiddenInput,
'batch_dependencies': HiddenInput,
5 changes: 5 additions & 0 deletions templates/problem/data.html
Original file line number Diff line number Diff line change
@@ -404,6 +404,7 @@
<th class="type-column">{{ _('Type') }}</th>
<th>{{ _('Input file') }}</th>
<th>{{ _('Output file') }}</th>
<th>{{ _('Explanation file') }}</th>
<th>{{ _('Points') }}</th>
<th>{{ _('Pretest?') }}</th>
<th>{{ _('Private?') }}</th>
@@ -439,6 +440,10 @@
form['output_file'].value() in valid_files) %} class="bad-file"{% endif %}>
{{ form.output_file.errors }}{{ form.output_file }}
</td>
<td{% if not (form.empty_permitted or form['type'].value() != 'C' or form['explanation_file'].value() == '' or
form['explanation_file'].value() in valid_files) %} class="bad-file"{% endif %}>
{{ form.explanation_file.errors }}{{ form.explanation_file }}
</td>
<td>{{ form.points.errors }}{{ form.points }}</td>
<td>{{ form.is_pretest.errors }}{{ form.is_pretest }}</td>
<td>{{ form.is_private.errors }}{{ form.is_private }}</td>

0 comments on commit a3a4763

Please sign in to comment.