{% extends "base.html" %} {% block js_media %} <script type="text/javascript"> window.valid_files = {{valid_files_json}}; window.big_input = (window.valid_files.length > 100); </script> <script type="text/javascript" src="{{ static('jquery-ui.min.js') }}"></script> <script type="text/javascript" src="{{ static('libs/featherlight/featherlight.min.js') }}"></script> <script type="text/javascript" src="{{ static('fine-uploader/jquery.fine-uploader.js') }}"></script> <script type="text/javascript"> $(function () { $("#problem-data-zipfile_fine_uploader").fineUploader({ request: { endpoint: "{{url('problem_zip_upload', problem.code)}}", params: { 'csrfmiddlewaretoken': '{{ csrf_token }}' } }, chunking: { enabled: true, partSize: 40000000, }, resume: { enabled: true }, validation: { allowedExtensions: ['zip'], }, }).on('complete', function (event, id, name, responseJSON) { console.log(responseJSON); if (!responseJSON.success) { alert('Fail to upload: ' + responseJSON.error); } else { $('#submit-button').click(); } });; function update_select2() { $('tbody:not(.extra-row-body) .type-column select').select2({ minimumResultsForSearch: -1 }); } $("#id_problem-data-checker").select2(); update_select2(); function autofill_if_exists($select, file) { if (!$select.val() && ~window.valid_files.indexOf(file)) $select.val(file).trigger('change'); } var $table = $('#case-table'); $table.on('add-row', function (e, $tr) { // update_select2(); $tr.find('input').filter('[id$=file]').each(function () { var $select, val = $(this).replaceWith($select = $('<select>').attr({ id: $(this).attr('id'), name: $(this).attr('name'), style: 'width: 100%' })).val(); $select.select2({ data: window.big_input ? [val] : window.valid_files, allowClear: true, placeholder: '' }).val(val).trigger('change').on('change', function () { var val = $select.val(); if (val) { if ($select.attr('id').endsWith('input_file')) autofill_if_exists($tr.find('select[id$=output_file]'), val.replace(/in(?!.*?in)/, 'out')); else autofill_if_exists($tr.find('select[id$=input_file]'), val.replace(/out(?!.*?out)/, 'in')); } }); }); }); var order = 0; function handle_table_reorder() { var in_batch = false; $table.find('tbody:first tr').each(function () { switch ($(this).attr('data-type')) { case 'C': $(this).find('input[id$=pretest]').toggle(!in_batch); break; case 'S': in_batch = true; break; case 'E': in_batch = false; } }); } function try_parse_json(json) { try { return JSON.parse(json); } catch (e) { return {}; } } function swap_row($a, $b) { var $a_order = $a.find('input[id$=order]'), $b_order = $b.find('input[id$=order]'); var order = $a_order.val(); $a_order.val($b_order.val()); $b_order.val(order); $b.after($a); $a.find('span.order').text($a_order.val()); $b.find('span.order').text($b_order.val()); handle_table_reorder(); } function checker_precision($checker) { var $td = $checker.parent(); var $args = $td.closest('table').find('#id_problem-data-checker_args'); var $precision = $('<input>', { type: 'number', value: try_parse_json($args.val()).precision || 6, title: 'precision (decimal digits)', style: 'width: 4em' }).change(function () { if ($checker.val().startsWith('floats')) $args.val(JSON.stringify({precision: parseInt($(this).val())})); else $args.val(''); }).appendTo($td); $checker.change(function () { $precision.toggle($checker.val().startsWith('floats')).change(); }).change(); } (function toggle_custom() { let $checker = $('#id_problem-data-checker') let $custom_checker = $('#id_problem-data-custom_checker'); let $validator = $('#id_problem-data-custom_validator'); let $interactive = $('#id_problem-data-interactive_judge'); let $sig_handler = $('#id_problem-data-signature_handler'); let $sig_header = $('#id_problem-data-signature_header'); let $ioi_signature = $("#id_problem-data-use_ioi_signature"); $tr_checker = $custom_checker.parent().parent(); $tr_validator = $validator.parent().parent(); $tr_interactive = $interactive.parent().parent(); $tr_sig_handler = $sig_handler.parent().parent(); $tr_sig_header = $sig_header.parent().parent(); $td = $checker.parent(); var $sample = $("<a/>",{ text: "{{_('Instruction')}}", style: "margin-left:3em;", target: "_blank", href: "{{url('custom_checker_sample')}}" }).appendTo($td); $("<a/>",{ text: " ({{_('Instruction')}})", target: "_blank", href: "{{url('custom_checker_sample')}}" }).appendTo($ioi_signature.parent()); $checker.change(function () { $tr_checker.toggle($checker.val() == 'custom').change(); $tr_validator.toggle($checker.val() == 'customval' || $checker.val() == 'testlib').change(); $sample.toggle(['custom', 'customval', 'interact'].includes($checker.val())).change(); }).change(); $ioi_signature.change(function() { $tr_interactive.toggle($ioi_signature.is(':checked')).change(); $tr_sig_header.toggle($ioi_signature.is(':checked')).change(); $tr_sig_handler.toggle($ioi_signature.is(':checked')).change(); }).change(); })(); checker_precision($('#id_problem-data-checker')); $table.on('add-row', function (e, $tr) { var $order = $tr.find('input').filter('[id$=order]').attr('type', 'hidden').val(++order); $order.after($('<span>', {'class': 'order'}).text($order.val())) .after($('<i>', {'class': 'fa fa-fw fa-lg fa-ellipsis-v'})); var $opts = $tr.find('input').slice(2, 6); var $files = $tr.find('select').slice(1, 3); var $delete = $files.end().last(); $tr.find('select[id$=type]').change(function () { var $this = $(this), val = $this.val(), disabled; switch (val) { case 'S': case 'E': disabled = val == 'S'; $opts.toggle(val == 'S'); $files.siblings('.select2').hide(); $delete.toggle(val == 'S'); break; default: $opts.toggle(val == 'C'); $files.siblings('.select2').toggle(val == 'C'); $delete.toggle(val == 'C'); var $prevs = $tr.prevAll('tr[data-type=S], tr[data-type=E]'); disabled = $prevs.length && $prevs.get(0).getAttribute('data-type') == 'S'; $tr.find('input[id$=pretest]').toggle(val == 'C' && !disabled); } $tr.attr('data-type', val).nextUntil('tr[data-type=S], tr[data-type=E], tr[data-type=""]') .find('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 () { switch ($tr.attr('data-type')) { case 'C': case 'S': return true; default: return false; } }, afterOpen: function () { var $input = this.$currentTarget.prev(); this.$instance.find('.generator-args-editor') .find('textarea').val($input.val()).end() .find('.button').click(function () { $input.val($(this).prev().val()); $.featherlight.current().close(); }).end() .show(); } }); checker_precision($tr.find('select[id$=checker]')); }).find('tbody:first').find('tr').each(function () { $table.trigger('add-row', [$(this)]); }); $('form').submit(function () { $table.find('tbody:first').find('tr').each(function () { var filled = false; $(this).find('input, select').each(function () { var $this = $(this); if (!$this.attr('name')) return; if ($this.attr('type') === 'checkbox') filled |= $this.is(':checked'); else if (!$this.attr('name').endsWith('order')) filled |= !!$this.val(); }); if (!filled) $(this).find('input[id$=order]').val(''); }); }); var $total = $('#id_cases-TOTAL_FORMS'); $('a#add-case-row').click(function () { var $tr; $table.find('tbody:first').append($tr = $($table.find('.extra-row-body').html() .replace(/__prefix__/g, $total.val()))); $tr.find('.type-column select option[value="C"]').attr('selected', true); $total.val(parseInt($total.val()) + 1); $table.trigger('add-row', [$tr]); window.scrollBy(0, $tr.height()); return false; }); function isInpFile(x) { let tail = ['.in', '.inp', '.IN', '.INP'] for (let i of tail) { if (x.endsWith(i)) { return true; } } return false; } function isOutFile(x) { let tail = ['.out', '.OUT', '.ans', '.ANS'] for (let i of tail) { if (x.endsWith(i)) { return true; } } return false; } $('a#fill-testcases').click(function (e) { e.preventDefault(); var inFiles = [], outFiles = []; for (let file of window.valid_files) { if (isInpFile(file)) { inFiles.push(file); } if (isOutFile(file)) { outFiles.push(file); } } if (inFiles.length == 0) { alert("No input/output files. Make sure your files' suffices are .in(p)/.out"); return false; } if (inFiles.length != outFiles.length) { alert("The input files do not match the output files!"); return false; } inFiles.sort(); outFiles.sort(); // big number of input if (inFiles.length > 100) { window.big_input = true; } var batch_starts = $('#batch_starts').val(); batch_starts = batch_starts.split(','); batch_starts = batch_starts.filter(e => { return e.length && e == +e; }); batch_starts = new Set(batch_starts.map(x => Math.min(Math.max(1, parseInt(x)), inFiles.length - 1))); batch_starts.add(inFiles.length + 1); var numRows = inFiles.length + 2 * batch_starts.size - 2; while ($total.val() < numRows) { $('a#add-case-row').click(); } // fill cases var row = 0; for (var i = 0; i < inFiles.length; i++) { if (batch_starts.has(i + 1)) { $("#id_cases-"+row+"-type").val('S').change(); row += 1; } var $input = $("#id_cases-"+row+"-input_file"); $input.select2('destroy').empty().select2({ data: [inFiles[i]] }); $input.val(inFiles[i]).change(); var $output = $("#id_cases-"+row+"-output_file"); $output.select2('destroy').empty().select2({ data: [outFiles[i]] }); $output.val(outFiles[i]).change(); if ($("#problem-type").val() == "ICPC") { $("#id_cases-"+row+"-points").val('0').change(); } else { $("#id_cases-"+row+"-points").val('1').change(); } row += 1; if (batch_starts.has(i + 2)) { $("#id_cases-"+(row-1)+"-points").val('1').change(); $("#id_cases-"+row+"-type").val('E').change(); row += 1; } } update_select2(); return false; }); var oldIndex; $('#case-table tbody').sortable({ itemSelector: 'tr', handle: 'i.fa-ellipsis-v', start: function (event, ui) { $item = ui.item; oldIndex = $item.index(); }, stop: function (event, ui) { $item = ui.item; var newIndex = $item.index(); if (newIndex > oldIndex) { var order = parseInt($item.parent().children().slice(oldIndex, newIndex).each(function () { var $order = $(this).find('input[id$=order]'); $order.val(parseInt($order.val()) - 1).siblings('span.order').text($order.val()); }).last().after($item).find('input[id$=order]').val()); $item.find('input[id$=order]').val(order + 1).siblings('span.order').text(order + 1); } else if (newIndex < oldIndex) { var order = parseInt($item.parent().children().slice(newIndex + 1, oldIndex + 1).each(function () { var $order = $(this).find('input[id$=order]'); $order.val(parseInt($order.val()) + 1).siblings('span.order').text($order.val()); }).first().before($item).find('input[id$=order]').val()); $item.find('input[id$=order]').val(order - 1).siblings('span.order').text(order - 1); } if (newIndex != oldIndex) handle_table_reorder(); } }); $('input#delete-all').change(function() { if (this.checked) { $("input[name$='DELETE']").attr('checked', true); } else { $("input[name$='DELETE']").attr('checked', false); } }); // Change to OI if the first row point > 0 if($("#id_cases-0-points").val() != '0') $('#problem-type').val('OI'); $("#problem-type").select2(); // Change batch_starts based on current tests function update_batch_starts() { var numBatches = 0; var batchStarts = []; $("#case-table tbody:first tr").each(function(idx) { $select = $('#id_cases-' + idx + '-type'); if ($select.val() == 'S') { batchStarts.push(idx + 1 - 2 * numBatches); numBatches++; } }); if (batchStarts) { $("#batch_starts").val(batchStarts.join(', ')); } } update_batch_starts(); }).change(); </script> {% include 'fine_uploader/script.html' %} {% endblock %} {% block media %} <link href="{{ static ('fine-uploader/fine-uploader.css') }}" rel="stylesheet"> <style> #case-table .select2 { text-align: initial; } .order-column { width: 1em; } span.order { padding-right: 0.5em; } body.dragging, body.dragging * { cursor: move !important; } .dragged { position: absolute; opacity: 0.5; z-index: 2000; } i.fa-ellipsis-v { cursor: move; } #case-table tbody td { white-space: nowrap; } .type-column { width: 8em; } ul.errorlist { border: 3px red solid; border-radius: 5px; padding-top: 1em; padding-bottom: 1em; background: #e99; } </style> {% endblock %} {% block header %} {% if data_form.instance.has_yml %} <div class="title-line-action"> [<a href="{{ url('problem_data_init', problem.code) }}">{{ _('View YAML') }}</a>] </div> {% endif %} {% endblock %} {% block body %} {% if data_form.instance.feedback %} <ul class="errorlist"> <li>{{ data_form.instance.feedback }}</li> </ul> {% endif %} <form class="problem-data-form" action="" method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ cases_formset.management_form }} <table class="table"> {{ data_form.as_table() }} <th> <label>{{_('Autofill testcases')}}:</label> </th> <td> <div> {{_('Problem type')}}: <select id="problem-type" style="width: 5em"> <option value="ICPC">ICPC</option> <option value="OI">OI</option> </select> <a id="fill-testcases" href="#"> <i class="fa fa-circle"></i> {{_('Fill testcases')}} </a> </div> <div style="margin-top: 1em;"> {{_("Batch start positions")}}: <input id="batch_starts"> </div> <div> {{_("Leave empty if not use batch. If you want to divide to three batches [1, 4], [5, 8], [9, 10], enter: 1, 5, 9")}} </div> </td> </table> <input type="submit" value="{{ _('Apply!') }}" class="button" id="submit-button"> <table id="case-table" class="table"> <thead> <tr> <th class="order-column"></th> <th class="type-column">{{ _('Type') }}</th> <th>{{ _('Input file') }}</th> <th>{{ _('Output file') }}</th> <th>{{ _('Points') }}</th> <th>{{ _('Pretest?') }}</th> {% if cases_formset.can_delete %} <th>{{ _('Delete?') }} <br> <input type="checkbox" name="delete-all" id="delete-all"> </th> {% endif %} </tr> </thead> <tbody> {% for form in all_case_forms %} {% if form.non_field_errors() %} <tr> <td colspan="{{ 9 + cases_formset.can_delete }}">{{ form.non_field_errors() }}</td> </tr> {% endif %} {% if form.prefix and '__prefix__' in form.prefix %} </tbody> <tbody class="extra-row-body" style="display: none"> {% endif %} <tr data-type="{{ form['type'].value() }}"> <td> {{ form.id }}{{ form.order.errors }}{{ form.order }} </td> <td class="type-column">{{ form.type.errors }}{{ form.type }}</td> <td{% if not (form.empty_permitted or form['type'].value() != 'C' or form['input_file'].value() in valid_files) %} class="bad-file"{% endif %}> {{ form.input_file.errors }}{{ form.input_file }} </td> <td{% if not (form.empty_permitted or form['type'].value() != 'C' or form['output_file'].value() in valid_files) %} class="bad-file"{% endif %}> {{ form.output_file.errors }}{{ form.output_file }} </td> <td>{{ form.points.errors }}{{ form.points }}</td> <td>{{ form.is_pretest.errors }}{{ form.is_pretest }}</td> {% if cases_formset.can_delete %} <td>{{ form.DELETE }}</td> {% endif %} </tr> {% endfor %} </tbody> </table> <input type="submit" value="{{ _('Apply!') }}" class="button" id="submit-button"> <a id="add-case-row" href="#"><i class="fa fa-plus"></i> {{ _('Add new case') }}</a> </form> {% endblock %}