mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-22 12:18:42 +00:00
feat: Problem Data Configure Page (#37)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
commit
5c3c654281
62
web/app/controllers/problem_data_configure.php
Normal file
62
web/app/controllers/problem_data_configure.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
requireLib('bootstrap5');
|
||||||
|
requirePHPLib('form');
|
||||||
|
requirePHPLib('judger');
|
||||||
|
requirePHPLib('data');
|
||||||
|
|
||||||
|
UOJProblem::init(UOJRequest::get('id')) || UOJResponse::page404();
|
||||||
|
UOJProblem::cur()->userCanManage(Auth::user()) || UOJResponse::page403();
|
||||||
|
|
||||||
|
$problem_configure = new UOJProblemConfigure(UOJProblem::cur());
|
||||||
|
$problem_configure->runAtServer();
|
||||||
|
$problem_conf_str = '';
|
||||||
|
|
||||||
|
foreach ($problem_configure->problem_conf->conf as $key => $val) {
|
||||||
|
if ($key == 'use_builtin_judger' && $val == 'off') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key == 'use_builtin_checker' && $val == 'ownchk') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$problem_conf_str .= "{$key} {$val}\n";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php echoUOJPageHeader('数据配置 - ' . HTML::stripTags(UOJProblem::cur()->getTitle(['with' => 'id']))) ?>
|
||||||
|
|
||||||
|
<h1>
|
||||||
|
<?= UOJProblem::cur()->getTitle(['with' => 'id']) ?> 数据配置
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header fw-bold">problem.conf 预览</div>
|
||||||
|
|
||||||
|
<div class="card-body p-0" id="problem-conf-preview">
|
||||||
|
<pre class="bg-light mb-0 p-3"><code><?= $problem_conf_str ?></code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-footer bg-transparent small text-muted">
|
||||||
|
此处显示的 <code>problem.conf</code> 为根据右侧填写的配置信息生成的内容预览,并非题目当前实际配置文件。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card mt-3">
|
||||||
|
<div class="card-header fw-bold">功能说明</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<p>此处可以对 <b>传统题</b> 进行快速配置,在填写完成之后点击页面底部的「提交」按钮即可替换现有配置文件并进行数据同步。</p>
|
||||||
|
<p>目前暂不支持对交互题、提交答案题、通信题进行配置,对于此类题目请手动编写配置文件。</p>
|
||||||
|
<p>如需要配置子任务依赖,也可以基于此处生成的配置文件进行修改后再手动上传。</p>
|
||||||
|
<p>关于数据配置的更多帮助,请查阅 <a href="https://s2oj.github.io/#/manage/tutorial/problem_data" target="_blank">帮助文档</a>。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-8 mt-3 mt-md-0">
|
||||||
|
<?php $problem_configure->printHTML() ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php echoUOJPageFooter() ?>
|
@ -90,58 +90,6 @@ if ($_POST['problem_data_file_submit'] == 'submit') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加配置文件
|
|
||||||
if ($_POST['problem_settings_file_submit'] == 'submit') {
|
|
||||||
if ($_POST['use_builtin_checker'] and $_POST['n_tests']) {
|
|
||||||
$set_filename = "/var/uoj_data/upload/{$problem['id']}/problem.conf";
|
|
||||||
$has_legacy = false;
|
|
||||||
if (file_exists($set_filename)) {
|
|
||||||
$has_legacy = true;
|
|
||||||
unlink($set_filename);
|
|
||||||
}
|
|
||||||
$setfile = fopen($set_filename, "w");
|
|
||||||
fwrite($setfile, "use_builtin_judger on\n");
|
|
||||||
if ($_POST['use_builtin_checker'] != 'ownchk') {
|
|
||||||
fwrite($setfile, "use_builtin_checker " . $_POST['use_builtin_checker'] . "\n");
|
|
||||||
}
|
|
||||||
fwrite($setfile, "n_tests " . $_POST['n_tests'] . "\n");
|
|
||||||
if ($_POST['n_ex_tests']) {
|
|
||||||
fwrite($setfile, "n_ex_tests " . $_POST['n_ex_tests'] . "\n");
|
|
||||||
} else {
|
|
||||||
fwrite($setfile, "n_ex_tests 0\n");
|
|
||||||
}
|
|
||||||
if ($_POST['n_sample_tests']) {
|
|
||||||
fwrite($setfile, "n_sample_tests " . $_POST['n_sample_tests'] . "\n");
|
|
||||||
} else {
|
|
||||||
fwrite($setfile, "n_sample_tests 0\n");
|
|
||||||
}
|
|
||||||
if (isset($_POST['input_pre'])) {
|
|
||||||
fwrite($setfile, "input_pre " . $_POST['input_pre'] . "\n");
|
|
||||||
}
|
|
||||||
if (isset($_POST['input_suf'])) {
|
|
||||||
fwrite($setfile, "input_suf " . $_POST['input_suf'] . "\n");
|
|
||||||
}
|
|
||||||
if (isset($_POST['output_pre'])) {
|
|
||||||
fwrite($setfile, "output_pre " . $_POST['output_pre'] . "\n");
|
|
||||||
}
|
|
||||||
if (isset($_POST['output_suf'])) {
|
|
||||||
fwrite($setfile, "output_suf " . $_POST['output_suf'] . "\n");
|
|
||||||
}
|
|
||||||
fwrite($setfile, "time_limit " . ($_POST['time_limit'] ?: 1) . "\n");
|
|
||||||
fwrite($setfile, "memory_limit " . ($_POST['memory_limit'] ?: 256) . "\n");
|
|
||||||
fclose($setfile);
|
|
||||||
if (!$has_legacy) {
|
|
||||||
echo "<script>alert('添加成功!请点击「检验配置并同步数据」按钮以应用新配置文件。')</script>";
|
|
||||||
} else {
|
|
||||||
echo "<script>alert('替换成功!请点击「检验配置并同步数据」按钮以应用新配置文件。')</script>";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$errmsg = "添加配置文件失败,请检查是否所有必填输入框都已填写!";
|
|
||||||
becomeMsgPage('<div>' . $errmsg . '</div><a href="/problem/' . $problem['id'] . '/manage/data">返回</a>');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$info_form = new UOJForm('info');
|
$info_form = new UOJForm('info');
|
||||||
$attachment_url = UOJProblem::cur()->getAttachmentUri();
|
$attachment_url = UOJProblem::cur()->getAttachmentUri();
|
||||||
$info_form->appendHTML(<<<EOD
|
$info_form->appendHTML(<<<EOD
|
||||||
@ -670,7 +618,7 @@ if ($problem['hackable']) {
|
|||||||
<button type="button" class="btn d-block w-100 btn-primary" data-bs-toggle="modal" data-bs-target="#UploadDataModal">上传数据</button>
|
<button type="button" class="btn d-block w-100 btn-primary" data-bs-toggle="modal" data-bs-target="#UploadDataModal">上传数据</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<button type="button" class="btn d-block w-100 btn-primary" data-bs-toggle="modal" data-bs-target="#ProblemSettingsFileModal">试题配置</button>
|
<a role="button" class="btn d-block w-100 btn-primary" href="<?= UOJProblem::cur()->getUri('/manage/data/configure') ?>">数据配置</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
@ -705,108 +653,4 @@ if ($problem['hackable']) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php $problem_conf = UOJProblem::cur()->getProblemConf() ?>
|
|
||||||
|
|
||||||
<div class="modal fade" id="ProblemSettingsFileModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-lg">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h4 class="modal-title" id="myModalLabel">试题配置</h4>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<form class="form-horizontal" action="" method="post" role="form">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="use_builtin_checker" class="col-sm-5 control-label">比对函数</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $checker_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('use_builtin_checker', 'ownchk') : ""; ?>
|
|
||||||
<select class="form-select" id="use_builtin_checker" name="use_builtin_checker">
|
|
||||||
<option value="ncmp" <?= $checker_value == "ncmp" ? 'selected' : '' ?>>ncmp: 整数序列</option>
|
|
||||||
<option value="wcmp" <?= $checker_value == "wcmp" ? 'selected' : '' ?>>wcmp: 字符串序列</option>
|
|
||||||
<option value="lcmp" <?= $checker_value == "lcmp" ? 'selected' : '' ?>>lcmp: 多行数据(忽略行内与行末的多余空格,同时忽略文末回车)</option>
|
|
||||||
<option value="fcmp" <?= $checker_value == "fcmp" ? 'selected' : '' ?>>fcmp: 多行数据(不忽略行末空格,但忽略文末回车)</option>
|
|
||||||
<option value="rcmp4" <?= $checker_value == "rcmp4" ? 'selected' : '' ?>>rcmp4: 浮点数序列(误差不超过 1e-4)</option>
|
|
||||||
<option value="rcmp6" <?= $checker_value == "rcmp6" ? 'selected' : '' ?>>rcmp6: 浮点数序列(误差不超过 1e-6)</option>
|
|
||||||
<option value="rcmp9" <?= $checker_value == "rcmp9" ? 'selected' : '' ?>>rcmp9: 浮点数序列(误差不超过 1e-9)</option>
|
|
||||||
<option value="yesno" <?= $checker_value == "yesno" ? 'selected' : '' ?>>yesno: Yes、No(不区分大小写)</option>
|
|
||||||
<option value="uncmp" <?= $checker_value == "uncmp" ? 'selected' : '' ?>>uncmp: 整数集合</option>
|
|
||||||
<option value="bcmp" <?= $checker_value == "bcmp" ? 'selected' : '' ?>>bcmp: 二进制文件</option>
|
|
||||||
<option value="ownchk" <?= $checker_value == "ownchk" ? 'selected' : '' ?>>自定义校验器(需上传 chk.cpp)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="n_tests" class="col-sm-5 control-label">n_tests</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $n_tests_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('n_tests', '') : ""; ?>
|
|
||||||
<input type="number" class="form-control" id="n_tests" name="n_tests" placeholder="数据点个数(必填)" value="<?= $n_tests_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="n_ex_tests" class="col-sm-5 control-label">n_ex_tests</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $n_ex_tests_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('n_ex_tests', 0) : ""; ?>
|
|
||||||
<input type="number" class="form-control" id="n_ex_tests" name="n_ex_tests" placeholder="额外数据点个数(默认为 0)" value="<?= $n_ex_tests_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="n_sample_tests" class="col-sm-5 control-label">n_sample_tests</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $n_sample_tests_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('n_sample_tests', 0) : ""; ?>
|
|
||||||
<input type="number" class="form-control" id="n_sample_tests" name="n_sample_tests" placeholder="样例测试点个数(默认为 0)" value="<?= $n_sample_tests_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="input_pre" class="col-sm-5 control-label">input_pre</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $input_pre_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('input_pre', 'input') : ""; ?>
|
|
||||||
<input type="text" class="form-control" id="input_pre" name="input_pre" placeholder="输入文件名称(默认为 input)" value="<?= $input_pre_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="input_suf" class="col-sm-5 control-label">input_suf</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $input_suf_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('input_suf', 'txt') : ""; ?>
|
|
||||||
<input type="text" class="form-control" id="input_suf" name="input_suf" placeholder="输入文件后缀(默认为 txt)" value="<?= $input_suf_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="output_pre" class="col-sm-5 control-label">output_pre</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $output_pre_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('output_pre', 'output') : ''; ?>
|
|
||||||
<input type="text" class="form-control" id="output_pre" name="output_pre" placeholder="输出文件名称(默认为 output)" value="<?= $output_pre_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="output_suf" class="col-sm-5 control-label">output_suf</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $output_suf_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('output_suf', 'txt') : ""; ?>
|
|
||||||
<input type="text" class="form-control" id="output_suf" name="output_suf" placeholder="输出文件后缀(默认为 txt)" value="<?= $output_suf_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="time_limit" class="col-sm-5 control-label">time_limit</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $time_limit_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('time_limit', 1) : ""; ?>
|
|
||||||
<input type="text" class="form-control" id="time_limit" name="time_limit" placeholder="时间限制(默认为 1s)" value="<?= $time_limit_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<label for="memory_limit" class="col-sm-5 control-label">memory_limit</label>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<?php $memory_limit_value = $problem_conf instanceof UOJProblemConf ? $problem_conf->getVal('memory_limit', 256) : ""; ?>
|
|
||||||
<input type="number" class="form-control" id="memory_limit" name="memory_limit" placeholder="内存限制(默认为 256 MB)" value="<?= $memory_limit_value ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="problem_settings_file_submit" value="submit">
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="submit" class="btn btn-success">确定</button>
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -201,7 +201,9 @@ class SyncProblemDataHandler {
|
|||||||
|
|
||||||
public function _updateProblemConf($new_problem_conf) {
|
public function _updateProblemConf($new_problem_conf) {
|
||||||
try {
|
try {
|
||||||
putUOJConf("{$this->data_dir}/problem.conf", $new_problem_conf);
|
putUOJConf("{$this->upload_dir}/problem.conf", $new_problem_conf);
|
||||||
|
|
||||||
|
$this->_sync();
|
||||||
return '';
|
return '';
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
@ -506,3 +508,7 @@ function dataAddHackPoint($problem, $uploaded_input_file, $uploaded_output_file,
|
|||||||
|
|
||||||
return (new SyncProblemDataHandler($problem, $user))->addHackPoint($uploaded_input_file, $uploaded_output_file, $reason);
|
return (new SyncProblemDataHandler($problem, $user))->addHackPoint($uploaded_input_file, $uploaded_output_file, $reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dataUpdateProblemConf($problem, $new_problem_conf) {
|
||||||
|
return (new SyncProblemDataHandler($problem))->updateProblemConf($new_problem_conf);
|
||||||
|
}
|
||||||
|
@ -125,6 +125,8 @@ class UOJForm {
|
|||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'div_class' => '',
|
'div_class' => '',
|
||||||
'input_class' => 'form-control',
|
'input_class' => 'form-control',
|
||||||
|
'input_attrs' => [],
|
||||||
|
'input_div_class' => null,
|
||||||
'default_value' => '',
|
'default_value' => '',
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'label_class' => 'form-label',
|
'label_class' => 'form-label',
|
||||||
@ -148,6 +150,10 @@ class UOJForm {
|
|||||||
], $config['label']);
|
], $config['label']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($config['input_div_class'] !== null) {
|
||||||
|
$html .= HTML::tag_begin('div', ['class' => $config['input_div_class']]);
|
||||||
|
}
|
||||||
|
|
||||||
$html .= HTML::empty_tag('input', [
|
$html .= HTML::empty_tag('input', [
|
||||||
'class' => $config['input_class'],
|
'class' => $config['input_class'],
|
||||||
'type' => $config['type'],
|
'type' => $config['type'],
|
||||||
@ -155,13 +161,17 @@ class UOJForm {
|
|||||||
'id' => "input-$name",
|
'id' => "input-$name",
|
||||||
'value' => $config['default_value'],
|
'value' => $config['default_value'],
|
||||||
'placeholder' => $config['placeholder'],
|
'placeholder' => $config['placeholder'],
|
||||||
]);
|
] + $config['input_attrs']);
|
||||||
$html .= HTML::tag('div', ['class' => 'invalid-feedback', 'id' => "help-$name"], '');
|
$html .= HTML::tag('div', ['class' => 'invalid-feedback', 'id' => "help-$name"], '');
|
||||||
|
|
||||||
if ($config['help']) {
|
if ($config['help']) {
|
||||||
$html .= HTML::tag('div', ['class' => $config['help_class']], $config['help']);
|
$html .= HTML::tag('div', ['class' => $config['help_class']], $config['help']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($config['input_div_class'] !== null) {
|
||||||
|
$html .= HTML::tag_end('div');
|
||||||
|
}
|
||||||
|
|
||||||
$html .= HTML::tag_end('div');
|
$html .= HTML::tag_end('div');
|
||||||
|
|
||||||
$this->add($name, $html, $config['validator_php'], $config['validator_js']);
|
$this->add($name, $html, $config['validator_php'], $config['validator_js']);
|
||||||
@ -255,6 +265,7 @@ class UOJForm {
|
|||||||
$config += [
|
$config += [
|
||||||
'div_class' => '',
|
'div_class' => '',
|
||||||
'select_class' => 'form-select',
|
'select_class' => 'form-select',
|
||||||
|
'select_div_class' => null,
|
||||||
'options' => [],
|
'options' => [],
|
||||||
'default_value' => '',
|
'default_value' => '',
|
||||||
'label' => '',
|
'label' => '',
|
||||||
@ -275,6 +286,10 @@ class UOJForm {
|
|||||||
], $config['label']);
|
], $config['label']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($config['select_div_class'] !== null) {
|
||||||
|
$html .= HTML::tag_begin('div', ['class' => $config['select_div_class']]);
|
||||||
|
}
|
||||||
|
|
||||||
// Select
|
// Select
|
||||||
$html .= HTML::tag_begin('select', ['id' => "input-$name", 'name' => $name, 'class' => $config['select_class']]);
|
$html .= HTML::tag_begin('select', ['id' => "input-$name", 'name' => $name, 'class' => $config['select_class']]);
|
||||||
|
|
||||||
@ -293,6 +308,10 @@ class UOJForm {
|
|||||||
$html .= HTML::tag('div', ['class' => $config['help_class']], $config['help']);
|
$html .= HTML::tag('div', ['class' => $config['help_class']], $config['help']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($config['select_div_class'] !== null) {
|
||||||
|
$html .= HTML::tag_end('div');
|
||||||
|
}
|
||||||
|
|
||||||
$html .= HTML::tag_end('div');
|
$html .= HTML::tag_end('div');
|
||||||
|
|
||||||
$this->add(
|
$this->add(
|
||||||
@ -503,6 +522,13 @@ class UOJForm {
|
|||||||
if (!$this->config['no_submit']) {
|
if (!$this->config['no_submit']) {
|
||||||
echo HTML::tag_begin('div', ['class' => $this->config['submit_container']['class']]);
|
echo HTML::tag_begin('div', ['class' => $this->config['submit_container']['class']]);
|
||||||
|
|
||||||
|
if ($this->config['back_button']['href'] !== null) {
|
||||||
|
echo HTML::tag('a', [
|
||||||
|
'class' => $this->config['back_button']['class'],
|
||||||
|
'href' => $this->config['back_button']['href']
|
||||||
|
], '返回');
|
||||||
|
}
|
||||||
|
|
||||||
echo HTML::tag('button', [
|
echo HTML::tag('button', [
|
||||||
'type' => 'submit',
|
'type' => 'submit',
|
||||||
'id' => "button-submit-{$this->form_name}",
|
'id' => "button-submit-{$this->form_name}",
|
||||||
@ -511,13 +537,6 @@ class UOJForm {
|
|||||||
'class' => $this->config['submit_button']['class']
|
'class' => $this->config['submit_button']['class']
|
||||||
], $this->config['submit_button']['text']);
|
], $this->config['submit_button']['text']);
|
||||||
|
|
||||||
if ($this->config['back_button']['href'] !== null) {
|
|
||||||
echo HTML::tag('a', [
|
|
||||||
'class' => $this->config['back_button']['class'],
|
|
||||||
'href' => $this->config['back_button']['href']
|
|
||||||
], '返回');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo HTML::tag_end('div');
|
echo HTML::tag_end('div');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,6 +663,10 @@ class UOJProblem {
|
|||||||
return "/var/uoj_data/{$this->info['id']}";
|
return "/var/uoj_data/{$this->info['id']}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUploadFolderPath() {
|
||||||
|
return "/var/uoj_data/upload/{$this->info['id']}";
|
||||||
|
}
|
||||||
|
|
||||||
public function getDataZipPath() {
|
public function getDataZipPath() {
|
||||||
return "/var/uoj_data/{$this->info['id']}.zip";
|
return "/var/uoj_data/{$this->info['id']}.zip";
|
||||||
}
|
}
|
||||||
@ -672,6 +676,10 @@ class UOJProblem {
|
|||||||
return "{$this->getDataFolderPath()}/$name";
|
return "{$this->getDataFolderPath()}/$name";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUploadFilePath($name = '') {
|
||||||
|
return "{$this->getUploadFolderPath()}/$name";
|
||||||
|
}
|
||||||
|
|
||||||
public function getResourcesFolderPath() {
|
public function getResourcesFolderPath() {
|
||||||
return UOJContext::storagePath() . "/problem_resources/" . $this->info['id'];
|
return UOJContext::storagePath() . "/problem_resources/" . $this->info['id'];
|
||||||
}
|
}
|
||||||
|
369
web/app/models/UOJProblemConfigure.php
Normal file
369
web/app/models/UOJProblemConfigure.php
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class UOJProblemConfigure {
|
||||||
|
public UOJProblem $problem;
|
||||||
|
public UOJProblemConf $problem_conf;
|
||||||
|
public string $href;
|
||||||
|
|
||||||
|
public array $conf_keys;
|
||||||
|
public UOJForm $simple_form;
|
||||||
|
|
||||||
|
public static $supported_checkers = [
|
||||||
|
'ownchk' => '自定义校验器',
|
||||||
|
'ncmp' => 'ncmp: 单行或多行整数序列',
|
||||||
|
'wcmp' => 'wcmp: 单行或多行字符串序列',
|
||||||
|
'fcmp' => 'fcmp: 单行或多行数据(不忽略行末空格,但忽略文末回车)',
|
||||||
|
'bcmp' => 'bcmp: 逐字节比较',
|
||||||
|
'uncmp' => 'uncmp: 单行或多行整数集合',
|
||||||
|
'yesno' => 'yesno: YES、NO 序列(不区分大小写)',
|
||||||
|
'rcmp4' => 'rcmp4: 浮点数序列,绝对或相对误差在 1e-4 以内则视为答案正确',
|
||||||
|
'rcmp6' => 'rcmp6: 浮点数序列,绝对或相对误差在 1e-6 以内则视为答案正确',
|
||||||
|
'rcmp9' => 'rcmp9: 浮点数序列,绝对或相对误差在 1e-9 以内则视为答案正确',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static $supported_score_types = [
|
||||||
|
'int' => '整数,每个测试点的部分分向下取整',
|
||||||
|
'real-0' => '整数,每个测试点的部分分四舍五入到整数',
|
||||||
|
'real-1' => '实数,四舍五入到小数点后 1 位',
|
||||||
|
'real-2' => '实数,四舍五入到小数点后 2 位',
|
||||||
|
'real-3' => '实数,四舍五入到小数点后 3 位',
|
||||||
|
'real-4' => '实数,四舍五入到小数点后 4 位',
|
||||||
|
'real-5' => '实数,四舍五入到小数点后 5 位',
|
||||||
|
'real-6' => '实数,四舍五入到小数点后 6 位',
|
||||||
|
'real-7' => '实数,四舍五入到小数点后 7 位',
|
||||||
|
'real-8' => '实数,四舍五入到小数点后 8 位',
|
||||||
|
];
|
||||||
|
|
||||||
|
private static function getCardHeader($title, $body_class = 'vstack gap-3') {
|
||||||
|
return <<<EOD
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div class="card h-100 overflow-hidden">
|
||||||
|
<div class="card-header fw-bold">
|
||||||
|
{$title}
|
||||||
|
</div>
|
||||||
|
<div class="card-body {$body_class}">
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getCardFooter() {
|
||||||
|
return <<<EOD
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(UOJProblem $problem) {
|
||||||
|
$this->problem = $problem;
|
||||||
|
$problem_conf = $this->problem->getProblemConf('data');
|
||||||
|
if (!($problem_conf instanceof UOJProblemConf)) {
|
||||||
|
$problem_conf = new UOJProblemConf([]);
|
||||||
|
}
|
||||||
|
$this->problem_conf = $problem_conf;
|
||||||
|
|
||||||
|
$this->href = "/problem/{$this->problem->info['id']}/manage/data";
|
||||||
|
|
||||||
|
$this->simple_form = new UOJForm('simple');
|
||||||
|
|
||||||
|
$encoded_problem_conf = json_encode($problem_conf->conf, JSON_FORCE_OBJECT);
|
||||||
|
$this->simple_form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
var problem_conf = {$encoded_problem_conf};
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(static::getCardHeader('基本信息'));
|
||||||
|
$this->addSelect($this->simple_form, 'use_builtin_judger', ['on' => '默认', 'off' => '自定义 Judger'], '测评逻辑', 'on');
|
||||||
|
$this->addSelect($this->simple_form, 'use_builtin_checker', self::$supported_checkers, '比对函数', 'ncmp');
|
||||||
|
$this->addSelect($this->simple_form, 'score_type', self::$supported_score_types, '测试点分数数值类型', 'int');
|
||||||
|
$this->simple_form->appendHTML(static::getCardFooter());
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(static::getCardHeader('数据配置'));
|
||||||
|
$this->addNumberInput($this->simple_form, 'n_tests', '数据点个数', 10);
|
||||||
|
$this->addNumberInput($this->simple_form, 'n_ex_tests', '额外数据点个数', 0);
|
||||||
|
$this->addNumberInput($this->simple_form, 'n_sample_tests', '样例数据点个数', 0, ['help' => '样例数据点为额外数据点中的前 x 个数据点。']);
|
||||||
|
$this->simple_form->appendHTML(static::getCardFooter());
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(static::getCardHeader('文件配置'));
|
||||||
|
$this->addTextInput($this->simple_form, 'input_pre', '输入文件名称', '');
|
||||||
|
$this->addTextInput($this->simple_form, 'input_suf', '输入文件后缀', '');
|
||||||
|
$this->addTextInput($this->simple_form, 'output_pre', '输出文件名称', '');
|
||||||
|
$this->addTextInput($this->simple_form, 'output_suf', '输出文件后缀', '');
|
||||||
|
$this->simple_form->appendHTML(static::getCardFooter());
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(static::getCardHeader('运行时限制'));
|
||||||
|
$this->addTimeLimitInput($this->simple_form, 'time_limit', '时间限制', 1, ['help' => '单位为秒,至多三位小数。']);
|
||||||
|
$this->addNumberInput($this->simple_form, 'memory_limit', '内存限制', 256, ['help' => '单位为 MiB。']);
|
||||||
|
$this->addNumberInput($this->simple_form, 'output_limit', '输出长度限制', 64, ['help' => '单位为 MiB。']);
|
||||||
|
$this->simple_form->appendHTML(static::getCardFooter());
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(static::getCardHeader('测试点分值', ''));
|
||||||
|
$this->simple_form->appendHTML(<<<EOD
|
||||||
|
<details id="div-point-score-container-outer">
|
||||||
|
<summary>展开/收起全部</summary>
|
||||||
|
<div id="div-point-score-container" class="row gx-3 gy-2 mt-0"></div>
|
||||||
|
</details>
|
||||||
|
<div id="div-point-score-unavailable" style="display: none;">在启用 Subtask 时「测试点分值」不可用。</div>
|
||||||
|
EOD);
|
||||||
|
$this->simple_form->appendHTML(static::getCardFooter());
|
||||||
|
$this->simple_form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#input-n_tests').change(function() {
|
||||||
|
problem_conf['n_tests'] = $(this).val();
|
||||||
|
$('#div-point-score-container').problem_configure_point_scores(problem_conf);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#input-score_type').change(function() {
|
||||||
|
var score_type = $(this).val();
|
||||||
|
var step = '1';
|
||||||
|
problem_conf['score_type'] = score_type;
|
||||||
|
|
||||||
|
if (score_type == 'int') {
|
||||||
|
step = '1';
|
||||||
|
} else {
|
||||||
|
var decimal_places = parseInt(score_type.substring(5));
|
||||||
|
|
||||||
|
if (decimal_places == 0) {
|
||||||
|
step = '1';
|
||||||
|
} else {
|
||||||
|
step = (0).toFixed(decimal_places - 1) + '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-point-score-input', $('#div-point-score-container')).attr('step', step);
|
||||||
|
$('.uoj-problem-configure-point-score-input', $('#div-point-score-container')).first().trigger('change');
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-subtask-score-input', $('#div-point-score-container')).attr('step', step);
|
||||||
|
$('.uoj-problem-configure-subtask-score-input', $('#div-point-score-container')).first().trigger('change');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#div-point-score-container').problem_configure_point_scores(problem_conf);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(static::getCardHeader('Subtask 配置', 'p-0'));
|
||||||
|
$this->simple_form->appendHTML(<<<EOD
|
||||||
|
<div class="form-check form-switch m-3">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="input-enable_subtasks">
|
||||||
|
<label class="form-check-label" for="input-enable_subtasks">启用 Subtask</label>
|
||||||
|
</div>
|
||||||
|
<div id="div-subtasks-container"></div>
|
||||||
|
EOD);
|
||||||
|
$this->simple_form->appendHTML(static::getCardFooter());
|
||||||
|
$this->simple_form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#input-enable_subtasks').change(function() {
|
||||||
|
if (this.checked) {
|
||||||
|
$('#div-point-score-container-outer').hide();
|
||||||
|
$('#div-point-score-unavailable').show();
|
||||||
|
$('#div-subtasks-container').problem_configure_subtasks(problem_conf);
|
||||||
|
} else {
|
||||||
|
$('#div-point-score-container-outer').show();
|
||||||
|
$('#div-point-score-unavailable').hide();
|
||||||
|
$('#div-subtasks-container').empty();
|
||||||
|
$('.uoj-problem-configure-point-score-input').val('');
|
||||||
|
|
||||||
|
var subtask_keys = Object.keys(problem_conf).filter(function(key) {
|
||||||
|
return /^subtask_/.test(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i = 0; i < subtask_keys.length; ++i) {
|
||||||
|
problem_conf[subtask_keys[i]] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
problem_conf['n_subtasks'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (problem_conf['n_subtasks']) {
|
||||||
|
$('#input-enable_subtasks').prop('checked', true).trigger('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
|
||||||
|
$this->simple_form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$(document).on("keydown", "form", function(event) {
|
||||||
|
return event.key != "Enter";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
|
||||||
|
$this->simple_form->succ_href = $this->href;
|
||||||
|
$this->simple_form->config['form']['class'] = 'row gy-3';
|
||||||
|
$this->simple_form->config['submit_container']['class'] = 'col-12 text-center mt-3';
|
||||||
|
$this->simple_form->config['back_button']['href'] = $this->href;
|
||||||
|
$this->simple_form->config['back_button']['class'] = 'btn btn-secondary me-2';
|
||||||
|
|
||||||
|
$this->simple_form->handle = fn (&$vdata) => $this->onUpload($vdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSelect(UOJForm $form, $key, $options, $label, $default_val = '', $cfg = []) {
|
||||||
|
$this->conf_keys[$key] = true;
|
||||||
|
$form->addSelect($key, [
|
||||||
|
'options' => $options,
|
||||||
|
'label' => $label,
|
||||||
|
'div_class' => 'row gx-2',
|
||||||
|
'label_class' => 'col-form-label col-4',
|
||||||
|
'select_div_class' => 'col-8',
|
||||||
|
'default_value' => $this->problem_conf->getVal($key, $default_val),
|
||||||
|
] + $cfg);
|
||||||
|
$form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$('#input-{$key}').change(function() {
|
||||||
|
problem_conf['{$key}'] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addNumberInput(UOJForm $form, $key, $label, $default_val = '', $cfg = []) {
|
||||||
|
$this->conf_keys[$key] = true;
|
||||||
|
$form->addInput($key, [
|
||||||
|
'type' => 'number',
|
||||||
|
'label' => $label,
|
||||||
|
'div_class' => 'row gx-2',
|
||||||
|
'label_class' => 'col-form-label col-4',
|
||||||
|
'input_div_class' => 'col-8',
|
||||||
|
'default_value' => $this->problem_conf->getVal($key, $default_val),
|
||||||
|
'validator_php' => function ($x) {
|
||||||
|
return validateInt($x) ? '' : '必须为一个整数';
|
||||||
|
},
|
||||||
|
] + $cfg);
|
||||||
|
$form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$('#input-{$key}').change(function() {
|
||||||
|
problem_conf['{$key}'] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addTimeLimitInput(UOJForm $form, $key, $label, $default_val = '', $cfg = []) {
|
||||||
|
$this->conf_keys[$key] = true;
|
||||||
|
$form->addInput($key, [
|
||||||
|
'type' => 'number',
|
||||||
|
'label' => $label,
|
||||||
|
'input_attrs' => ['step' => 0.001],
|
||||||
|
'div_class' => 'row gx-2',
|
||||||
|
'label_class' => 'col-form-label col-4',
|
||||||
|
'input_div_class' => 'col-8',
|
||||||
|
'default_value' => $this->problem_conf->getVal($key, $default_val),
|
||||||
|
'validator_php' => function ($x) {
|
||||||
|
if (!validateUFloat($x)) {
|
||||||
|
return '必须为整数或小数,且值大于等于零';
|
||||||
|
} elseif (round($x * 1000) != $x * 1000) {
|
||||||
|
return '至多包含三位小数';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
] + $cfg);
|
||||||
|
$form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$('#input-{$key}').change(function() {
|
||||||
|
problem_conf['{$key}'] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addTextInput(UOJForm $form, $key, $label, $default_val = '', $cfg = []) {
|
||||||
|
$this->conf_keys[$key] = true;
|
||||||
|
$form->addInput($key, [
|
||||||
|
'label' => $label,
|
||||||
|
'div_class' => 'row gx-2',
|
||||||
|
'label_class' => 'col-form-label col-4',
|
||||||
|
'input_div_class' => 'col-8',
|
||||||
|
'default_value' => $this->problem_conf->getVal($key, $default_val),
|
||||||
|
'validator_php' => function ($x) {
|
||||||
|
return ctype_graph($x) ? '' : '必须仅包含除空格以外的可见字符';
|
||||||
|
},
|
||||||
|
] + $cfg);
|
||||||
|
$form->appendHTML(<<<EOD
|
||||||
|
<script>
|
||||||
|
$('#input-{$key}').change(function() {
|
||||||
|
problem_conf['{$key}'] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runAtServer() {
|
||||||
|
$this->simple_form->runAtServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUpload(array &$vdata) {
|
||||||
|
$conf = $this->problem_conf->conf;
|
||||||
|
$conf_keys = $this->conf_keys;
|
||||||
|
$n_tests = intval(UOJRequest::post('n_tests', 'validateUInt', $this->problem_conf->getVal('n_tests', 10)));
|
||||||
|
$n_subtasks = intval(UOJRequest::post('n_subtasks', 'validateUInt', $this->problem_conf->getVal('n_subtasks', 0)));
|
||||||
|
|
||||||
|
for ($i = 1; $i <= $n_tests; $i++) {
|
||||||
|
$conf_keys["point_score_$i"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$conf_keys['n_subtasks'] = true;
|
||||||
|
for ($i = 1; $i <= $n_subtasks; $i++) {
|
||||||
|
$conf_keys["subtask_type_$i"] = true;
|
||||||
|
$conf_keys["subtask_score_$i"] = true;
|
||||||
|
$conf_keys["subtask_end_$i"] = true;
|
||||||
|
$conf_keys["subtask_used_time_type_$i"] = true;
|
||||||
|
|
||||||
|
// $conf_keys["subtask_dependence_$i"] = true;
|
||||||
|
|
||||||
|
// $subtask_dependence_str = UOJRequest::post("subtask_dependence_$i", 'is_string', '');
|
||||||
|
|
||||||
|
// if ($subtask_dependence_str == 'many') {
|
||||||
|
// $subtask_dependence_cnt = 0;
|
||||||
|
|
||||||
|
// while (UOJRequest::post("subtask_dependence_{$i}_{$subtask_dependence_cnt}", 'is_string', '') != '') {
|
||||||
|
// $subtask_dependence_cnt++;
|
||||||
|
// $conf_keys["subtask_dependence_{$i}_{$subtask_dependence_cnt}"] = true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_keys($conf_keys) as $key) {
|
||||||
|
$val = UOJRequest::post($key, 'is_string', '');
|
||||||
|
if ($key === 'use_builtin_judger') {
|
||||||
|
if ($val === 'off') {
|
||||||
|
unset($conf[$key]);
|
||||||
|
} else {
|
||||||
|
$conf[$key] = $val;
|
||||||
|
}
|
||||||
|
} elseif ($key === 'use_builtin_checker') {
|
||||||
|
if ($val === 'ownchk') {
|
||||||
|
unset($conf[$key]);
|
||||||
|
} else {
|
||||||
|
$conf[$key] = $val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($val !== '') {
|
||||||
|
$conf[$key] = $val;
|
||||||
|
} else if (isset($conf[$key])) {
|
||||||
|
unset($conf[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$err = dataUpdateProblemConf($this->problem->info, $conf);
|
||||||
|
if ($err) {
|
||||||
|
UOJResponse::message('<div>' . $err . '</div><a href="' . $this->href . '">返回</a>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function printHTML() {
|
||||||
|
$this->simple_form->printHTML();
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ Route::group(
|
|||||||
Route::any('/problem/{id}/manage/statement', '/problem_statement_manage.php');
|
Route::any('/problem/{id}/manage/statement', '/problem_statement_manage.php');
|
||||||
Route::any('/problem/{id}/manage/permissions', '/problem_permissions_manage.php');
|
Route::any('/problem/{id}/manage/permissions', '/problem_permissions_manage.php');
|
||||||
Route::any('/problem/{id}/manage/data', '/problem_data_manage.php');
|
Route::any('/problem/{id}/manage/data', '/problem_data_manage.php');
|
||||||
|
Route::any('/problem/{id}/manage/data/configure', '/problem_data_configure.php');
|
||||||
Route::any('/download/testlib.h', '/download.php?type=testlib.h');
|
Route::any('/download/testlib.h', '/download.php?type=testlib.h');
|
||||||
Route::any('/download/problem/{id}/data.zip', '/download.php?type=problem');
|
Route::any('/download/problem/{id}/data.zip', '/download.php?type=problem');
|
||||||
Route::any('/download/problem/{id}/attachment.zip', '/download.php?type=attachment');
|
Route::any('/download/problem/{id}/attachment.zip', '/download.php?type=attachment');
|
||||||
|
261
web/js/uoj.js
261
web/js/uoj.js
@ -1177,6 +1177,267 @@ $.fn.remote_submit_type_group = function(oj, pid, url, submit_type) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// problem_configure: print problem.conf
|
||||||
|
$.fn.problem_conf_preview = function(problem_conf) {
|
||||||
|
return $(this).each(function() {
|
||||||
|
var keys = Object.keys(problem_conf);
|
||||||
|
var res = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var key = keys[i];
|
||||||
|
var value = problem_conf[key];
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'use_builtin_judger' && value == 'off') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == 'use_builtin_checker' && value == 'ownchk') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res += key + ' ' + value + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).html('<pre class="bg-light mb-0 p-3"><code>' + res + '</code></pre>');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// problem_configure: point scores
|
||||||
|
$.fn.problem_configure_point_scores = function(problem_conf) {
|
||||||
|
return $(this).each(function() {
|
||||||
|
var _this = this;
|
||||||
|
var n_tests = parseInt(problem_conf['n_tests']);
|
||||||
|
|
||||||
|
$(this).empty();
|
||||||
|
|
||||||
|
if (isNaN(n_tests) || n_tests <= 0) {
|
||||||
|
$(this).html('不可用。');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= n_tests; i++) {
|
||||||
|
var input_point_score = $('<input class="form-control form-control-sm uoj-problem-configure-point-score-input" type="number" name="point_score_' + i + '" id="input-point_score_' + i + '" min="0" max="100" />');
|
||||||
|
|
||||||
|
if (problem_conf['point_score_' + i]) {
|
||||||
|
input_point_score.val(problem_conf['point_score_' + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function(i){
|
||||||
|
input_point_score.change(function() {
|
||||||
|
problem_conf['point_score_' + i] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
})(i);
|
||||||
|
|
||||||
|
$(this).append(
|
||||||
|
$('<div class="col-sm-6" />').append(
|
||||||
|
$('<div class="" />')
|
||||||
|
.append($('<div class="" />').append('<label for="input-point_score_' + i + '" class="col-form-label col-form-label-sm">测试点 #' + i + '</label>'))
|
||||||
|
.append($('<div class="" />').append(input_point_score))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-point-score-input', this).change(function() {
|
||||||
|
var full_score = 100;
|
||||||
|
var rest_tests = parseInt(problem_conf['n_tests'] || '10');
|
||||||
|
var score_type = problem_conf['score_type'] || 'int';
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-point-score-input', _this).each(function() {
|
||||||
|
var point_score = parseInt($(this).val());
|
||||||
|
if (!isNaN(point_score)) {
|
||||||
|
full_score -= point_score;
|
||||||
|
rest_tests--;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-point-score-input', _this).each(function() {
|
||||||
|
if ($(this).val() == '') {
|
||||||
|
var val = full_score / rest_tests;
|
||||||
|
|
||||||
|
if (score_type == 'int') {
|
||||||
|
val = Math.floor(val);
|
||||||
|
} else {
|
||||||
|
var decimal_places = parseInt(score_type.substring(5));
|
||||||
|
|
||||||
|
val = val.toFixed(decimal_places);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).attr('placeholder', val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-point-score-input', this).first().trigger('change');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// problem_configure: subtasks
|
||||||
|
$.fn.problem_configure_subtasks = function(problem_conf) {
|
||||||
|
return $(this).each(function() {
|
||||||
|
var _this = this;
|
||||||
|
var n_subtasks = parseInt(problem_conf['n_subtasks'] || '0');
|
||||||
|
|
||||||
|
$(this).empty();
|
||||||
|
|
||||||
|
if (isNaN(n_subtasks)) {
|
||||||
|
$(this).html('不可用。');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var input_n_subtasks = $('<input class="form-control" type="number" name="n_subtasks" id="input-n_subtasks" />');
|
||||||
|
var div_subtasks = $('<div class="list-group list-group-flush border-top" />');
|
||||||
|
|
||||||
|
if (n_subtasks) {
|
||||||
|
input_n_subtasks.val(n_subtasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).append(
|
||||||
|
$('<div class="m-3" />').append(
|
||||||
|
$('<div class="row" />').append(
|
||||||
|
$('<div class="col-4" />').append('<label for="input-n_subtasks" class="col-form-label">子任务数</label>')
|
||||||
|
).append(
|
||||||
|
$('<div class="col-8" />').append(input_n_subtasks)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).append(div_subtasks);
|
||||||
|
|
||||||
|
input_n_subtasks.change(function() {
|
||||||
|
div_subtasks.empty();
|
||||||
|
|
||||||
|
var n_subtasks = parseInt(input_n_subtasks.val() || '0');
|
||||||
|
var n_tests = parseInt(problem_conf['n_tests'] || '10');
|
||||||
|
problem_conf['n_subtasks'] = input_n_subtasks.val();
|
||||||
|
|
||||||
|
for (var i = 1; i <= n_subtasks; i++) {
|
||||||
|
var input_subtask_type = $('<select class="form-select form-select-sm" name="subtask_type_' + i + '" id="input-subtask_type_' + i + '" />');
|
||||||
|
var input_subtask_end = $('<input class="form-control form-control-sm uoj-problem-configure-subtask-end-input" type="number" name="subtask_end_' + i + '" id="input-subtask_end_' + i + '" min="0" max="' + n_tests + '" />');
|
||||||
|
var input_subtask_score = $('<input class="form-control form-control-sm uoj-problem-configure-subtask-score-input" type="number" name="subtask_score_' + i + '" id="input-subtask_score_' + i + '" min="0" max="100" />');
|
||||||
|
var input_subtask_used_time_type = $('<select class="form-select form-select-sm" name="subtask_used_time_type_' + i + '" id="input-subtask_used_time_type_' + i + '" />');
|
||||||
|
|
||||||
|
input_subtask_type
|
||||||
|
.append($('<option value="packed" />').text('错一个就零分'))
|
||||||
|
.append($('<option value="min" />').text('取所有测试点中的最小值'));
|
||||||
|
input_subtask_used_time_type
|
||||||
|
.append($('<option value="sum" />').text('全部相加'))
|
||||||
|
.append($('<option value="max" />').text('取所有测试点中的最大值'));
|
||||||
|
|
||||||
|
(function(i) {
|
||||||
|
input_subtask_type.change(function() {
|
||||||
|
problem_conf['subtask_type_' + i] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
|
||||||
|
input_subtask_end.change(function() {
|
||||||
|
problem_conf['subtask_end_' + i] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
})
|
||||||
|
|
||||||
|
input_subtask_score.change(function() {
|
||||||
|
problem_conf['subtask_score_' + i] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
|
||||||
|
input_subtask_used_time_type.change(function() {
|
||||||
|
problem_conf['subtask_used_time_type_' + i] = $(this).val();
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
})(i);
|
||||||
|
|
||||||
|
if (problem_conf['subtask_type_' + i]) {
|
||||||
|
input_subtask_type.val(problem_conf['subtask_type_' + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problem_conf['subtask_end_' + i]) {
|
||||||
|
input_subtask_end.val(problem_conf['subtask_end_' + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problem_conf['subtask_score_' + i]) {
|
||||||
|
input_subtask_score.val(problem_conf['subtask_score_' + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (problem_conf['subtask_used_time_type_' + i]) {
|
||||||
|
input_subtask_used_time_type.val(problem_conf['subtask_used_time_type_' + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
div_subtasks.append(
|
||||||
|
$('<div class="list-group-item" />').append(
|
||||||
|
$('<div class="fw-bold" />').text('Subtask #' + i)
|
||||||
|
).append(
|
||||||
|
$('<div />').append(
|
||||||
|
$('<div class="row mt-2" />').append(
|
||||||
|
$('<div class="col-sm-6" />').append('<label for="input-subtask_type_' + i + '" class="col-form-label col-form-label-sm">评分类型</label>')
|
||||||
|
).append(
|
||||||
|
$('<div class="col-sm-6" />').append(input_subtask_type)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<div class="row mt-2" />').append(
|
||||||
|
$('<div class="col-sm-6" />').append('<label for="input-subtask_end_' + i + '" class="col-form-label col-form-label-sm">最后一个测试点的编号</label>')
|
||||||
|
).append(
|
||||||
|
$('<div class="col-sm-6" />').append(input_subtask_end)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<div class="row mt-2" />').append(
|
||||||
|
$('<div class="col-sm-6" />').append('<label for="input-subtask_score_' + i + '" class="col-form-label col-form-label-sm">分数</label>')
|
||||||
|
).append(
|
||||||
|
$('<div class="col-sm-6" />').append(input_subtask_score)
|
||||||
|
)
|
||||||
|
).append(
|
||||||
|
$('<div class="row mt-2" />').append(
|
||||||
|
$('<div class="col-sm-6" />').append('<label for="input-subtask_used_time_type_' + i + '" class="col-form-label col-form-label-sm">程序用时统计方式</label>')
|
||||||
|
).append(
|
||||||
|
$('<div class="col-sm-6" />').append(input_subtask_used_time_type)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-subtask-score-input', _this).change(function() {
|
||||||
|
var full_score = 100;
|
||||||
|
var rest_subtasks = parseInt(problem_conf['n_subtasks'] || '10');
|
||||||
|
var score_type = problem_conf['score_type'] || 'int';
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-subtask-score-input', _this).each(function() {
|
||||||
|
var subtask_score = parseInt($(this).val());
|
||||||
|
|
||||||
|
if (!isNaN(subtask_score)) {
|
||||||
|
full_score -= subtask_score;
|
||||||
|
rest_subtasks--;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-subtask-score-input', _this).each(function() {
|
||||||
|
if ($(this).val() == '') {
|
||||||
|
var val = full_score / rest_subtasks;
|
||||||
|
|
||||||
|
if (score_type == 'int') {
|
||||||
|
val = Math.floor(val);
|
||||||
|
} else {
|
||||||
|
var decimal_places = parseInt(score_type.substring(5));
|
||||||
|
|
||||||
|
val = val.toFixed(decimal_places);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).attr('placeholder', val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.uoj-problem-configure-subtask-score-input', _this).first().trigger('change');
|
||||||
|
$('#problem-conf-preview').problem_conf_preview(problem_conf);
|
||||||
|
});
|
||||||
|
|
||||||
|
input_n_subtasks.trigger('change');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// custom test
|
// custom test
|
||||||
function custom_test_onsubmit(response_text, div_result, url) {
|
function custom_test_onsubmit(response_text, div_result, url) {
|
||||||
if (response_text != '') {
|
if (response_text != '') {
|
||||||
|
Loading…
Reference in New Issue
Block a user