Skip to content

Commit

Permalink
Add batch dependencies to site data interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Riolku authored and kiritofeng committed Jan 21, 2024
1 parent 61318c7 commit 435839e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 29 deletions.
18 changes: 18 additions & 0 deletions judge/migrations/0145_site_data_batch_prerequisites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.21 on 2024-01-06 04:33

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('judge', '0144_submission_index_cleanup'),
]

operations = [
migrations.AddField(
model_name='problemtestcase',
name='batch_dependencies',
field=models.TextField(blank=True, help_text='batch dependencies as a comma-separated list of integers', verbose_name='batch dependencies'),
),
]
4 changes: 3 additions & 1 deletion judge/models/problem_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,6 @@ class ProblemTestCase(models.Model):
output_limit = models.IntegerField(verbose_name=_('output limit length'), blank=True, null=True)
checker = models.CharField(max_length=10, verbose_name=_('checker'), choices=CHECKERS, blank=True)
checker_args = models.TextField(verbose_name=_('checker arguments'), blank=True,
help_text=_('Checker arguments as a JSON object.'))
help_text=_('checker arguments as a JSON object'))
batch_dependencies = models.TextField(verbose_name=_('batch dependencies'), blank=True,
help_text=_('batch dependencies as a comma-separated list of integers'))
18 changes: 18 additions & 0 deletions judge/utils/problem_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def __init__(self, problem, data, cases, files):
def make_init(self):
cases = []
batch = None
batch_count = 0

def end_batch():
if not batch['batched']:
Expand Down Expand Up @@ -109,14 +110,31 @@ def make_checker(case):
case.save(update_fields=('checker_args', 'is_pretest'))
(batch['batched'] if batch else cases).append(data)
elif case.type == 'S':
batch_count += 1
if batch:
end_batch()
if case.points is None:
raise ProblemDataError(_('Batch start case #%d requires points.') % i)
dependencies = []
if case.batch_dependencies.strip():
try:
dependencies = list(map(int, case.batch_dependencies.split(',')))
except ValueError:
raise ProblemDataError(
_('Dependencies must be a comma-separated list of integers for batch start case #%d.') % i,
)
for batch_number in dependencies:
if batch_number >= batch_count:
raise ProblemDataError(
_('Dependencies must depend on previous batches for batch start case #%d.') % i,
)
elif batch_number < 1:
raise ProblemDataError(_('Dependencies must be positive for batch start case #%d.') % i)
batch = {
'points': case.points,
'batched': [],
'is_pretest': case.is_pretest,
'dependencies': dependencies,
}
if case.generator_args:
batch['generator_args'] = case.generator_args.splitlines()
Expand Down
5 changes: 3 additions & 2 deletions judge/views/problem_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ class ProblemCaseForm(ModelForm):

class Meta:
model = ProblemTestCase
fields = ('order', 'type', 'input_file', 'output_file', 'points',
'is_pretest', 'output_limit', 'output_prefix', 'checker', 'checker_args', 'generator_args')
fields = ('order', 'type', 'input_file', 'output_file', 'points', 'is_pretest', 'output_limit',
'output_prefix', 'checker', 'checker_args', 'generator_args', 'batch_dependencies')
widgets = {
'generator_args': HiddenInput,
'batch_dependencies': HiddenInput,
'type': Select(attrs={'style': 'width: 100%'}),
'points': NumberInput(attrs={'style': 'width: 4em'}),
'output_prefix': NumberInput(attrs={'style': 'width: 4.5em'}),
Expand Down
69 changes: 43 additions & 26 deletions templates/problem/data.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,37 +125,42 @@
.find('input[id$=points], input[id$=pretest]').toggle(!disabled);
}).change();

var tooltip_classes = 'tooltipped tooltipped-s';
$tr.find('a.edit-generator-args').mouseover(function () {
switch ($tr.attr('data-type')) {
case 'C':
case 'S':
var $this = $(this).addClass(tooltip_classes);
$this.attr('aria-label', $this.prev().val() || '(none)');
}
}).mouseout(function () {
$(this).removeClass(tooltip_classes).removeAttr('aria-label');
}).featherlight($('.generator-args-editor'), {
beforeOpen: function () {
function addToolTip(linkClass, editorClass) {
var tooltip_classes = 'tooltipped tooltipped-s';
$tr.find(linkClass).mouseover(function () {
switch ($tr.attr('data-type')) {
case 'C':
case 'S':
var $this = $(this).addClass(tooltip_classes);
$this.attr('aria-label', $this.prev().val() || '(none)');
}
}).mouseout(function () {
$(this).removeClass(tooltip_classes).removeAttr('aria-label');
}).featherlight($(editorClass), {
beforeOpen: function () {
switch ($tr.attr('data-type')) {
case 'C':
case 'S':
return true;
default:
default:
return false;
}
},
afterOpen: function () {
var $input = this.$currentTarget.prev();
this.$instance.find('.generator-args-editor')
}
},
afterOpen: function () {
var $input = this.$currentTarget.prev();
this.$instance.find(editorClass)
.find('textarea').val($input.val()).end()
.find('.button').click(function () {
$input.val($(this).prev().val());
$.featherlight.current().close();
}).end()
$input.val($(this).prev().val());
$.featherlight.current().close();
}).end()
.show();
}
});
}
});
}

addToolTip('a.edit-generator-args', '.generator-args-editor');
addToolTip('a.edit-batch-dependencies', '.batch-dependencies-editor');

checker_precision($tr.find('select[id$=checker]'));
}).find('tbody:first').find('tr').each(function () {
Expand Down Expand Up @@ -300,18 +305,18 @@
cursor: move;
}

.edit-generator-args {
.edit-generator-args, .edit-batch-dependencies {
position: relative;
}

.generator-args-editor textarea {
.generator-args-editor textarea, .batch-dependencies-editor textarea {
display: block;
width: 100%;
margin-bottom: 0.5em;
height: 8em;
}

.generator-args-editor .button {
.generator-args-editor .button, .batch-dependencies-editor .button {
display: block;
float: right;
}
Expand Down Expand Up @@ -370,6 +375,10 @@
<input type="checkbox" data-suffix="generator_args">
{{ _('Generator args') }}
</label>
<label>
<input type="checkbox" data-suffix="batch_dependencies">
{{ _('Batch dependencies') }}
</label>
</div>
<table id="case-table" class="table">
<thead>
Expand All @@ -384,6 +393,7 @@
<th class="output-limit">{{ _('Output limit') }}</th>
<th class="checker">{{ _('Checker') }}</th>
<th class="generator-args">{{ _('Generator args') }}</th>
<th class="batch-dependencies">{{ _('Batch Dependencies') }}</th>
{% if cases_formset.can_delete %}
<th>{{ _('Delete?') }}</th>
{% endif %}
Expand Down Expand Up @@ -424,6 +434,12 @@
{{ _('Edit') }}
</a>
</td>
<td class="batch-dependencies">{{ form.batch_dependencies.errors }}{{ form.batch_dependencies }}
<a href="javascript:void(0)" class="edit-batch-dependencies">
<i class="fa fa-pencil"></i>
{{ _('Edit') }}
</a>
</td>
{% if cases_formset.can_delete %}
<td>{{ form.DELETE }}</td>
{% endif %}
Expand All @@ -435,4 +451,5 @@
<a id="add-case-row" href="#"><i class="fa fa-plus"></i> {{ _('Add new case') }}</a>
</form>
<div style="display: none" class="generator-args-editor"><textarea></textarea><a class="button">{{ _('Save') }}</a></div>
<div style="display: none" class="batch-dependencies-editor"><textarea></textarea><a class="button">{{ _('Save') }}</a></div>
{% endblock %}

0 comments on commit 435839e

Please sign in to comment.