refactor(web/problem): Codeforces 风格的题目难度评价系统 (#21)
All checks were successful
continuous-integration/drone/push Build is passing

Co-authored-by: Fengyu Xie <63272948+Ignotus0@users.noreply.github.com>
Co-authored-by: Jiaxi Li <91811830+RevolutionBP@users.noreply.github.com>
Co-authored-by: Wenkuo Yu <82705676+youwike@users.noreply.github.com>
Co-authored-by: Haosen Li <91672298+Johnsonloy@users.noreply.github.com>
Co-authored-by: Baoshuo Ren <47095648+renbaoshuo@users.noreply.github.com>
This commit is contained in:
Baoshuo Ren 2022-12-05 17:39:36 +08:00 committed by GitHub
commit 3f2a6feea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 118 additions and 79 deletions

View File

@ -54,7 +54,7 @@ function getProblemTR($info) {
)
);
}
$html .= HTML::tag('td', [], UOJProblem::getDifficultyHTML($problem->info['difficulty']));
$html .= HTML::tag('td', [], $problem->getDifficultyHTML());
$html .= HTML::tag('td', [], ClickZans::getCntBlock($problem->info['zan']));
$html .= HTML::tag_end('tr');
return $html;
@ -66,7 +66,7 @@ $header .= '<th>' . UOJLocale::get('problems::problem') . '</th>';
if (isset($_COOKIE['show_submit_mode'])) {
$header .= '<th class="text-center" style="width:125px;">' . UOJLocale::get('problems::ac ratio') . '</th>';
}
$header .= '<th class="text-center" style="width:8em;">' . UOJLocale::get('problems::difficulty') . '</th>';
$header .= '<th class="text-center" style="width:4em;">' . UOJLocale::get('problems::difficulty') . '</th>';
$header .= '<th class="text-center" style="width:50px;">' . UOJLocale::get('appraisal') . '</th>';
$header .= '</tr>';

View File

@ -390,7 +390,7 @@ if (UOJContest::cur()) {
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>难度</span>
<span><?= UOJProblem::getDifficultyHTML(UOJProblem::info('difficulty')) ?></span>
<span><?= UOJProblem::cur()->getDifficultyHTML() ?></span>
</li>
<?php if (Auth::check()) : ?>
<li class="list-group-item d-flex justify-content-between align-items-center">

View File

@ -553,29 +553,6 @@ $solution_view_type_form->handle = function () {
};
$solution_view_type_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
$difficulty_form = new UOJBs4Form('difficulty');
$difficulty_form->addVInput(
'difficulty',
'text',
'难度系数',
$problem_extra_config['difficulty'],
function ($str) {
if (!is_numeric($str)) {
return '难度系数必须是一个数字';
}
return '';
},
null
);
$difficulty_form->handle = function () {
global $problem, $problem_extra_config;
$config = $problem_extra_config;
$config['difficulty'] = $_POST['difficulty'] + 0;
$esc_config = DB::escape(json_encode($config));
DB::query("update problems set extra_config = '$esc_config' where id = '{$problem['id']}'");
};
$difficulty_form->submit_button_config['class_str'] = 'btn btn-warning d-block w-100 mt-2';
if ($problem['hackable']) {
$test_std_form = new UOJBs4Form('test_std');
$test_std_form->handle = function () use ($problem, $data_disp) {
@ -643,7 +620,6 @@ if ($problem['hackable']) {
$hackable_form->runAtServer();
$view_type_form->runAtServer();
$solution_view_type_form->runAtServer();
$difficulty_form->runAtServer();
$data_form->runAtServer();
$clear_data_form->runAtServer();
$rejudge_form->runAtServer();
@ -803,13 +779,6 @@ $info_form->runAtServer();
<div class="mt-2">
<button type="button" class="btn d-block w-100 btn-primary" data-bs-toggle="modal" data-bs-target="#ProblemSettingsFileModal">试题配置</button>
</div>
<div class="mt-2">
<button id="button-difficulty" type="button" class="btn d-block w-100 btn-primary" onclick="$('#div-difficulty').toggle('fast');">难度系数</button>
<div class="mt-2" id="div-difficulty" style="display:none; padding-left:5px; padding-right:5px;">
<?php $difficulty_form->printHTML(); ?>
</div>
</div>
</div>
</aside>

View File

@ -128,7 +128,7 @@ function getProblemTR($info) {
)
);
}
$html .= HTML::tag('td', [], UOJProblem::getDifficultyHTML($problem->info['difficulty']));
$html .= HTML::tag('td', [], $problem->getDifficultyHTML());
$html .= HTML::tag('td', [], ClickZans::getCntBlock($problem->info['zan']));
$html .= HTML::tag_end('tr');
return $html;
@ -177,8 +177,12 @@ if (Auth::check() && isset($_GET['my'])) {
$cond['problems.uploader'] = Auth::id();
}
if (isset($_GET['difficulty']) && $_GET['difficulty']) {
$cond['problems.difficulty'] = $_GET['difficulty'];
if (isset($_GET['min_difficulty']) && $_GET['min_difficulty']) {
$cond[] = ['problems.difficulty', '>=', $_GET['min_difficulty']];
}
if (isset($_GET['max_difficulty']) && $_GET['max_difficulty']) {
$cond[] = ['problems.difficulty', '<=', $_GET['max_difficulty']];
}
if (empty($cond)) {
@ -191,7 +195,7 @@ $header .= '<th>' . UOJLocale::get('problems::problem') . '</th>';
if (isset($_COOKIE['show_submit_mode'])) {
$header .= '<th class="text-center" style="width:125px;">' . UOJLocale::get('problems::ac ratio') . '</th>';
}
$header .= '<th class="text-center" style="width:8em;">' . UOJLocale::get('problems::difficulty') . '</th>';
$header .= '<th class="text-center" style="width:4em;">' . UOJLocale::get('problems::difficulty') . '</th>';
$header .= '<th class="text-center" style="width:50px;">' . UOJLocale::get('appraisal') . '</th>';
$header .= '</tr>';
@ -247,8 +251,7 @@ $pag = new Paginator([
<div class="row">
<!-- left col -->
<div class="col-lg-9">
<div class="col-md-9">
<!-- title -->
<div class="d-flex justify-content-between">
<h1>
@ -335,8 +338,7 @@ $pag = new Paginator([
<!-- end left col -->
<!-- right col -->
<aside class="col-lg-3 mt-3 mt-lg-0">
<aside class="col-md-3 mt-3 mt-md-0">
<!-- search bar -->
<form method="get" class="mb-3" id="form-problem_search">
<div class="input-group mb-3">
@ -363,15 +365,20 @@ $pag = new Paginator([
</div>
<?php endif ?>
</div>
<div class="row mt-3">
<label class="col-sm-3 col-form-label" for="input-difficulty">难度</label>
<div class="col-sm-9">
<select id="input-difficulty" name="difficulty" class="form-select">
<option value="">全部</option>
<?php foreach (UOJProblem::$difficulty as $opt_name => $opt_value) : ?>
<?= HTML::option($opt_name, $opt_value, $opt_name == $_GET['difficulty']) ?>
<?php endforeach ?>
</select>
<div class="card mt-3">
<div class="card-header fw-bold">
题目难度
</div>
<div class="card-body">
<div class="input-group input-group-sm">
<input type="text" class="form-control" name="min_difficulty" id="input-min_difficulty" maxlength="4" style="width:4em" placeholder="800" value="<?= HTML::escape($_GET['min_difficulty']) ?>" autocomplete="off" />
<span class="input-group-text">~</span>
<input type="text" class="form-control" name="max_difficulty" id="input-max_difficulty" maxlength="4" style="width:4em" placeholder="3500" value="<?= HTML::escape($_GET['max_difficulty']) ?>" autocomplete="off" />
<button type="submit" class="btn btn-outline-secondary">
<i class="bi bi-funnel"></i>
</button>
</div>
</div>
</div>
</form>

View File

@ -253,7 +253,7 @@ $pag = new Paginator($pag_config);
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>难度</span>
<span><?= UOJProblem::getDifficultyHTML(UOJProblem::info('difficulty')) ?></span>
<span><?= UOJProblem::cur()->getDifficultyHTML() ?></span>
</li>
<?php if (Auth::check()) : ?>
<li class="list-group-item d-flex justify-content-between align-items-center">

View File

@ -65,7 +65,7 @@ $problem_editor->runAtServer();
$difficulty_form = new UOJForm('difficulty');
$difficulty_form->addSelect('difficulty', [
'options' => UOJProblem::$difficulty,
'options' => array_combine(UOJProblem::$difficulty, UOJProblem::$difficulty),
'default_value' => UOJProblem::info('difficulty'),
]);
$difficulty_form->handle = function () {
@ -183,4 +183,4 @@ $difficulty_form->runAtServer();
</div>
<?php echoUOJPageFooter() ?>
<?php echoUOJPageFooter() ?>

View File

@ -325,7 +325,7 @@ $submissions_sort_by_choice = !isset($_COOKIE['submissions-sort-by-code-length']
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>难度</span>
<span><?= UOJProblem::getDifficultyHTML(UOJProblem::info('difficulty')) ?></span>
<span><?= UOJProblem::cur()->getDifficultyHTML() ?></span>
</li>
<?php if (Auth::check()) : ?>
<li class="list-group-item d-flex justify-content-between align-items-center">

View File

@ -9,25 +9,45 @@ class UOJProblem {
use UOJArticleTrait;
public static array $difficulty = [
-1 => '暂无评定',
1 => '入门',
2 => '普及-',
3 => '普及/提高-',
4 => '普及+/提高',
6 => '提高+/省选-',
8 => '省选/NOI-',
10 => 'NOI/NOI+/CTSC',
800,
1000,
1200,
1400,
1600,
1800,
1900,
2000,
2100,
2200,
2300,
2400,
2500,
2700,
2900,
3100,
3300,
3500,
];
public static array $difficulty_color = [
-1 => '#bfbfbf',
1 => '#fe4c61',
2 => '#f39c11',
3 => '#ffc116',
4 => '#52c41a',
6 => '#3498db',
8 => '#9d3dcf',
10 => '#0e1d69',
800 => '#008000',
1000 => '#008000',
1200 => '#00c0c0',
1400 => '#00c0c0',
1600 => '#0000ff',
1800 => '#0000ff',
1900 => '#0000ff',
2000 => '#c0c000',
2100 => '#c0c000',
2200 => '#c0c000',
2300 => '#c0c000',
2400 => '#ff8000',
2500 => '#ff8000',
2700 => '#ff8000',
2900 => '#ff0000',
3100 => '#ff0000',
3300 => '#aa0000',
3500 => '#aa0000',
];
public static function query($id) {
@ -78,14 +98,6 @@ class UOJProblem {
return isSuperUser($user) || UOJUser::checkPermission($user, 'problems.create');
}
public static function getDifficultyHTML($difficulty = -1) {
$difficulty = (int)$difficulty;
$difficulty_text = self::$difficulty[$difficulty] ?: self::$difficulty[-1];
$difficulty_color = self::$difficulty_color[$difficulty] ?: self::$difficulty_color[-1];
return HTML::tag('span', ['class' => 'uoj-difficulty', 'style' => "color: $difficulty_color"], $difficulty_text);
}
public function __construct($info) {
$this->info = $info;
}
@ -128,6 +140,14 @@ class UOJProblem {
return UOJUser::getLink($this->info['uploader'] ?: "root");
}
public function getDifficultyHTML() {
$difficulty = (int)$this->info['difficulty'];
$difficulty_text = in_array($difficulty, static::$difficulty) ? $difficulty : '-';
$difficulty_color = in_array($difficulty, static::$difficulty) ? static::$difficulty_color[$difficulty] : '#7e7e7e';
return HTML::tag('span', ['class' => 'uoj-difficulty', 'style' => "color: $difficulty_color"], $difficulty_text);
}
public function findInContests() {
$res = DB::selectAll([
"select contest_id from contests_problems",

View File

@ -0,0 +1,43 @@
<?php
return function ($type) {
if ($type == 'up') {
DB::init();
$problems = DB::selectAll("select * from problems");
foreach ($problems as $info) {
$problem = new UOJProblem($info);
$difficulty = -1;
$extra_config = $problem->getExtraConfig();
if (isset($extra_config['difficulty'])) {
$old_difficulty = (float)$extra_config['difficulty'];
$difficulty = (int)(3.0 * $old_difficulty + 5) * 100;
$difficulty = (function ($search, $arr) {
$closest = null;
foreach ($arr as $item) {
if ($closest === null || abs($search - $closest) > abs($item - $search)) {
$closest = $item;
}
}
return $closest;
})($difficulty, UOJProblem::$difficulty);
}
DB::update([
"update problems",
"set", [
"difficulty" => $difficulty,
],
"where", [
"id" => $problem->info['id'],
]
]);
echo "Problem: {$problem->info['id']} ({$difficulty})\n";
}
}
};