题目部分
题目是整个OJ系统的练习基础,无论是平时学生的练习还是比赛时的准备用题,题目系统在OJ中都是至关重要的。在controllers
文件夹下,负责题目部分的代码文件分别为:problem_set.php
,problem.php
,problem_statistics.php
,problem_data_manage.php
,problem_statement_manage.php
,problem_managers_manage.php
。以下是具体的详细内容
题目列表
在problem_set.php
中,文件主要用于管理和展示题目列表。以下是代码的详细解释:
1. 引入库
requirePHPLib('form');
requirePHPLib('judger');
requirePHPLib('data');
这些函数用于引入需要的库文件,分别用于表单处理、评测系统和数据操作。
2. 新增题目按钮
if (isSuperUser($myUser)) {$new_problem_form = new UOJForm('new_problem');$new_problem_form->handle = function() {DB::query("insert into problems (title, is_hidden, submission_requirement) values ('New Problem', 1, '{}')");$id = DB::insert_id();DB::query("insert into problems_contents (id, statement, statement_md) values ($id, '', '')");dataNewProblem($id);};$new_problem_form->submit_button_config['align'] = 'right';$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';$new_problem_form->submit_button_config['text'] = UOJLocale::get('problems::add new');$new_problem_form->submit_button_config['smart_confirm'] = '';$new_problem_form->runAtServer();
}
- 检查当前用户是否是超级用户,如果是,则显示一个用于新增题目的表单。
- 当表单提交时,插入一条新题目记录到
problems
表,并获取新题目的ID。 - 插入一条空的题目内容记录到
problems_contents
表。 - 表单按钮的样式和文本配置。
3. 打印每个题目
function echoProblem($problem) {global $myUser;if (isProblemVisibleToUser($problem, $myUser)) {echo '<tr class="text-center">';if ($problem['submission_id']) {echo '<td class="success">';} else {echo '<td>';}echo '#', $problem['id'], '</td>';echo '<td class="text-left">';if ($problem['is_hidden']) {echo ' <span class="text-danger">[隐藏]</span> ';}echo '<a href="/problem/', $problem['id'], '">', $problem['title'], '</a>';if (isset($_COOKIE['show_tags_mode'])) {foreach (queryProblemTags($problem['id']) as $tag) {echo '<a class="uoj-problem-tag">', '<span class="badge badge-pill badge-secondary">', HTML::escape($tag), '</span>', '</a>';}}echo '</td>';if (isset($_COOKIE['show_submit_mode'])) {$perc = $problem['submit_num'] > 0 ? round(100 * $problem['ac_num'] / $problem['submit_num']) : 0;echo <<<EOD<td><a href="/submissions?problem_id={$problem['id']}&min_score=100&max_score=100">×{$problem['ac_num']}</a></td><td><a href="/submissions?problem_id={$problem['id']}">×{$problem['submit_num']}</a></td><td><div class="progress bot-buffer-no"><div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="$perc" aria-valuemin="0" aria-valuemax="100" style="width: $perc%; min-width: 20px;">{$perc}%</div></div></td>
EOD;}echo '<td class="text-left">', getClickZanBlock('P', $problem['id'], $problem['zan']), '</td>';echo '</tr>';}
}
echoProblem
函数用于输出每个题目的HTML。- 判断题目是否对当前用户可见,如果可见,则输出题目的ID、标题、标签、提交统计和点赞数。
4. 构建查询条件
$cond = array();
$search_tag = null;
$cur_tab = isset($_GET['tab']) ? $_GET['tab'] : 'all';
if ($cur_tab == 'template') {$search_tag = "模板题";
}
if (isset($_GET['tag'])) {$search_tag = $_GET['tag'];
}
if ($search_tag) {$cond[] = "'".DB::escape($search_tag)."' in (select tag from problems_tags where problems_tags.problem_id = problems.id)";
}
if (isset($_GET["search"])) { $cond[]="title like '%".DB::escape($_GET["search"])."%' or id like '%".DB::escape($_GET["search"])."%'";
}
if ($cond) {$cond = join($cond, ' and ');
} else {$cond = '1';
}
- 根据URL参数构建查询条件,用于筛选题目列表。
5. 定义表头
$header = '<tr>';
$header .= '<th class="text-center" style="width:5em;">ID</th>';
$header .= '<th>'.UOJLocale::get('problems::problem').'</th>';
if (isset($_COOKIE['show_submit_mode'])) {$header .= '<th class="text-center" style="width:5em;">'.UOJLocale::get('problems::ac').'</th>';$header .= '<th class="text-center" style="width:5em;">'.UOJLocale::get('problems::submit').'</th>';$header .= '<th class="text-center" style="width:150px;">'.UOJLocale::get('problems::ac ratio').'</th>';
}
$header .= '<th class="text-center" style="width:180px;">'.UOJLocale::get('appraisal').'</th>';
$header .= '</tr>';
- 定义题目列表的表头,根据用户的显示设置调整显示内容。
6. 定义分页器和表格样式
$pag_config = array('page_len' => 100);
$pag_config['col_names'] = array('*');
$pag_config['table_name'] = "problems left join best_ac_submissions on best_ac_submissions.submitter = '{$myUser['username']}' and problems.id = best_ac_submissions.problem_id";
$pag_config['cond'] = $cond;
$pag_config['tail'] = "order by id asc";
$pag = new Paginator($pag_config);$div_classes = array('table-responsive');
$table_classes = array('table', 'table-bordered', 'table-hover', 'table-striped');
- 设置分页器的配置,包括每页显示的题目数量、查询的表名和条件等。
7. 输出HTML内容
<?php echoUOJPageHeader(UOJLocale::get('problems')) ?>
<div class="row"><div class="col-sm-4"><?= HTML::tablist($tabs_info, $cur_tab, 'nav-pills') ?></div><div class="col-sm-4 order-sm-9 checkbox text-right"><label class="checkbox-inline" for="input-show_tags_mode"><input type="checkbox" id="input-show_tags_mode" <?= isset($_COOKIE['show_tags_mode']) ? 'checked="checked" ': ''?>/> <?= UOJLocale::get('problems::show tags') ?></label><label class="checkbox-inline" for="input-show_submit_mode"><input type="checkbox" id="input-show_submit_mode" <?= isset($_COOKIE['show_submit_mode']) ? 'checked="checked" ': ''?>/> <?= UOJLocale::get('problems::show statistics') ?></label></div><div class="col-sm-4 order-sm-5"><?php echo $pag->pagination(); ?></div>
</div>
<div class="top-buffer-sm"></div>
<script type="text/javascript">
$('#input-show_tags_mode').click(function() {if (this.checked) {$.cookie('show_tags_mode', '', {path: '/problems'});} else {$.removeCookie('show_tags_mode', {path: '/problems'});}location.reload();
});
$('#input-show_submit_mode').click(function() {if (this.checked) {$.cookie('show_submit_mode', '', {path: '/problems'});} else {$.removeCookie('show_submit_mode', {path: '/problems'});}location.reload();
});
</script>
<?phpecho '<div class="', join($div_classes, ' '), '">';echo '<table class="', join($table_classes, ' '), '">';echo '<thead>';echo $header;echo '</thead>';echo '<tbody>';foreach ($pag->get() as $idx => $row) {echoProblem($row);echo "\n";}if ($pag->isEmpty()) {echo '<tr><td class="text-center" colspan="233">'.UOJLocale::get('none').'</td></tr>';}echo '</tbody>';echo '</table>';echo '</div>';if (isSuperUser($myUser)) {$new_problem_form->printHTML();}echo $pag->pagination();
?>
<?php echoUOJPageFooter() ?>
- 输出页面头部、标签栏、分页导航、题目列表表格和底部。
- 使用JavaScript控制标签和统计信息的显示。
题目页面与提交
题目页面与提交页面下主要有”描述“、”提交“和”管理“(此功能只有管理员才能看到)功能。
当然,下面是对这段PHP代码的详细解释:
引入必要的PHP库
requirePHPLib('form');
requirePHPLib('judger');
requirePHPLib('form')
:引入处理表单的库。requirePHPLib('judger')
:引入处理评测的库。
验证题目ID并查询题目信息
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {become404Page();
}
$problem_content = queryProblemContent($problem['id']);
- 验证
id
是否为正整数,且查询题目的简要信息。 - 如果题目不存在或ID无效,返回404页面。
- 查询题目详细内容。
处理竞赛相关信息
$contest = validateUInt($_GET['contest_id']) ? queryContest($_GET['contest_id']) : null;
if ($contest != null) {genMoreContestInfo($contest);$problem_rank = queryContestProblemRank($contest, $problem);if ($problem_rank == null) {become404Page();} else {$problem_letter = chr(ord('A') + $problem_rank - 1);}
}
- 验证
contest_id
是否为正整数,且查询竞赛信息。 - 如果竞赛存在,生成更多竞赛信息并查询题目在竞赛中的排名。
- 如果题目不在竞赛中,返回404页面。
- 计算题目在竞赛中的字母标识。
检查用户权限和题目可见性
$is_in_contest = false;
$ban_in_contest = false;
if ($contest != null) {if (!hasContestPermission($myUser, $contest)) {if ($contest['cur_progress'] == CONTEST_NOT_STARTED) {become404Page();} elseif ($contest['cur_progress'] == CONTEST_IN_PROGRESS) {if ($myUser == null || !hasRegistered($myUser, $contest)) {becomeMsgPage("<h1>比赛正在进行中</h1><p>很遗憾,您尚未报名。比赛结束后再来看吧~</p>");} else {$is_in_contest = true;DB::update("update contests_registrants set has_participated = 1 where username = '{$myUser['username']}' and contest_id = {$contest['id']}");}} else {$ban_in_contest = !isProblemVisibleToUser($problem, $myUser);}}
} else {if (!isProblemVisibleToUser($problem, $myUser)) {become404Page();}
}
- 初始化
is_in_contest
和ban_in_contest
为false
。 - 如果竞赛存在且用户无权限:
- 竞赛未开始,返回404页面。
- 竞赛进行中,且用户未登录或未报名,显示比赛进行中的提示信息。
- 如果用户已报名,设置
is_in_contest
为true
并更新数据库。 - 竞赛已结束,检查题目是否对用户可见,设置
ban_in_contest
。
- 如果没有竞赛,检查题目是否对用户可见,不可见则返回404页面。
获取提交需求和额外配置
$submission_requirement = json_decode($problem['submission_requirement'], true);
$problem_extra_config = getProblemExtraConfig($problem);
$custom_test_requirement = getProblemCustomTestRequirement($problem);
- 解析题目的提交需求和自定义测试需求。
- 获取题目的额外配置。
处理自定义测试状态请求
if ($custom_test_requirement && $_GET['get'] == 'custom-test-status-details' && Auth::check()) {if ($custom_test_submission == null) {echo json_encode(null);} else if ($custom_test_submission['status'] != 'Judged') {echo json_encode(array('judged' => false,'html' => getSubmissionStatusDetails($custom_test_submission)));} else {ob_start();$styler = new CustomTestSubmissionDetailsStyler();if (!hasViewPermission($problem_extra_config['view_details_type'], $myUser, $problem, $submission)) {$styler->fade_all_details = true;}echoJudgementDetails($custom_test_submission_result['details'], $styler, 'custom_test_details');$result = ob_get_contents();ob_end_clean();echo json_encode(array('judged' => true,'html' => getSubmissionStatusDetails($custom_test_submission),'result' => $result));}die();
}
- 如果有自定义测试需求,并且GET请求参数
get
为custom-test-status-details
且用户已登录:- 如果没有自定义测试提交记录,返回
null
。 - 如果提交记录的状态不是
Judged
,返回评测状态详情。 - 否则,获取评测详情并返回评测结果。
- 如果没有自定义测试提交记录,返回
检查是否可以使用ZIP上传
$can_use_zip_upload = true;
foreach ($submission_requirement as $req) {if ($req['type'] == 'source code') {$can_use_zip_upload = false;}
}
- 检查提交需求中是否包含源代码,如果包含则不允许使用ZIP上传。
处理ZIP文件上传
function handleUpload($zip_file_name, $content, $tot_size) {global $problem, $contest, $myUser, $is_in_contest;$content['config'][] = array('problem_id', $problem['id']);if ($is_in_contest && $contest['extra_config']["contest_type"] != 'IOI' && !isset($contest['extra_config']["problem_{$problem['id']}"])) {$content['final_test_config'] = $content['config'];$content['config'][] = array('test_sample_only', 'on');}$esc_content = DB::escape(json_encode($content));$language = '/';foreach ($content['config'] as $row) {if (strEndWith($row[0], '_language')) {$language = $row[1];break;}}if ($language != '/') {Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');}$esc_language = DB::escape($language);$result = array();$result['status'] = "Waiting";$result_json = json_encode($result);if ($is_in_contest) {DB::query("insert into submissions (problem_id, contest_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, ${contest['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', 0)");} else {DB::query("insert into submissions (problem_id, submit_time, submitter, content, language, tot_size, status, result, is_hidden) values (${problem['id']}, now(), '${myUser['username']}', '$esc_content', '$esc_language', $tot_size, '${result['status']}', '$result_json', {$problem['is_hidden']})");}
}
- 处理ZIP文件上传:
- 根据上传内容配置更新提交记录。
- 处理语言设置并存储。
- 将提交记录插入数据库。
处理自定义测试上传
function handleCustomTestUpload($zip_file_name, $content, $tot_size) {global $problem, $contest, $myUser;$content['config'][] = array('problem_id', $problem['id']);$content['config'][] = array('custom_test', 'on');$esc_content = DB::escape(json_encode($content));$language = '/';foreach ($content['config'] as $row) {if (strEndWith($row[0], '_language')) {$language = $row[1];break;}}if ($language != '/') {Cookie::set('uoj_preferred_language', $language, time() + 60 * 60 * 24 * 365, '/');}$esc_language = DB::escape($language);$result = array();$result['status'] = "Waiting";$result_json = json_encode($result);DB::insert("insert into custom_test_submissions (problem_id, submit_time, submitter, content, status, result) values ({$problem['id']}, now(), '{$myUser['username']}', '$esc_content', '{$result['status']}', '$result_json')");
}
- 类似ZIP文件上传的处理方式,处理自定义测试上传并插入数据库。
创建并处理表单
if ($can_use_zip_upload) {$zip_answer_form = newZipSubmissionForm('zip_answer',$submission_requirement,'uojRandAvaiableSubmissionFileName','handleUpload');$zip_answer_form->runAtServer();
}$answer_form = newSubmissionForm('answer',$submission_requirement,'handleUpload');
$answer_form->runAtServer();if ($custom_test_requirement) {$custom_test_form = newSubmissionForm('custom_test',$custom_test_requirement,'handleCustomTestUpload');$custom_test_form->runAtServer();
}
- 根据题目的提交需求,创建并处理不同类型的表单:
- 如果允许ZIP上传,创建ZIP提交表单。
- 创建普通提交表单。
- 如果有自定义测试需求,创建自定义测试表单。
管理员题目描述处理
这段代码实现了一个问题管理页面,允许具有相应权限的用户编辑问题的内容、标签和可见性
引入必要的库
requirePHPLib('form');
这行代码引入了form
库,提供表单处理功能。
验证和获取问题信息
if (!validateUInt($_GET['id']) || !($problem = queryProblemBrief($_GET['id']))) {become404Page();
}
validateUInt($_GET['id'])
:验证GET参数id
是否为无符号整数。queryProblemBrief($_GET['id'])
:查询问题简要信息。如果问题不存在或验证失败,调用become404Page()
,返回404页面。
if (!hasProblemPermission($myUser, $problem)) {become403Page();
}
hasProblemPermission($myUser, $problem)
:检查当前用户是否有权限管理这个问题。如果没有权限,调用become403Page()
,返回403页面。
获取问题详细内容和标签
$problem_content = queryProblemContent($problem['id']);
$problem_tags = queryProblemTags($problem['id']);
queryProblemContent($problem['id'])
:获取问题的详细内容。queryProblemTags($problem['id'])
:获取问题的标签。
初始化编辑器
$problem_editor = new UOJBlogEditor();
$problem_editor->name = 'problem';
$problem_editor->blog_url = "/problem/{$problem['id']}";
$problem_editor->cur_data = array('title' => $problem['title'],'content_md' => $problem_content['statement_md'],'content' => $problem_content['statement'],'tags' => $problem_tags,'is_hidden' => $problem['is_hidden']
);
$problem_editor->label_text = array_merge($problem_editor->label_text, array('view blog' => '查看题目','blog visibility' => '题目可见性'
));
- 创建一个新的
UOJBlogEditor
实例用于编辑问题内容。 - 设置编辑器名称、URL和当前数据(标题、内容、标签和可见性)。
- 更新编辑器的标签文本(如“查看题目”和“题目可见性”)。
定义保存操作
$problem_editor->save = function($data) {global $problem, $problem_tags;DB::update("update problems set title = '".DB::escape($data['title'])."' where id = {$problem['id']}");DB::update("update problems_contents set statement = '".DB::escape($data['content'])."', statement_md = '".DB::escape($data['content_md'])."' where id = {$problem['id']}");if ($data['tags'] !== $problem_tags) {DB::delete("delete from problems_tags where problem_id = {$problem['id']}");foreach ($data['tags'] as $tag) {DB::insert("insert into problems_tags (problem_id, tag) values ({$problem['id']}, '".DB::escape($tag)."')");}}if ($data['is_hidden'] != $problem['is_hidden'] ) {DB::update("update problems set is_hidden = {$data['is_hidden']} where id = {$problem['id']}");DB::update("update submissions set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");DB::update("update hacks set is_hidden = {$data['is_hidden']} where problem_id = {$problem['id']}");}
};
- 定义
save
函数以处理编辑器保存操作。 - 更新问题标题和内容(包括Markdown格式和HTML格式)。
- 如果标签发生变化,删除旧标签并插入新标签。
- 如果可见性发生变化,更新问题、提交的可见性。
题目数据管理
在problem_data_manage.php
中,包含着对于一道题目数据上传的逻辑操作。这里截取部分重要代码进行解释
数据上传代码
<div class="modal fade" id="UploadDataModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h4 class="modal-title" id="myModalLabel">上传数据</h4><button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button></div><div class="modal-body"><form action="" method="post" enctype="multipart/form-data" role="form"><div class="form-group"><label for="exampleInputFile">上传zip文件</label><input type="file" name="problem_data_file" id="problem_data_file"><p class="help-block">说明:请将所有数据放置于压缩包根目录内。若压缩包内仅存在文件夹而不存在文件,则会将这些一级子文件夹下的内容移动到根目录下,然后这些一级子文件夹删除;若这些子文件夹内存在同名文件,则会发生随机替换,仅保留一个副本。</p></div><input type="hidden" name="problem_data_file_submit" value="submit"></div><div class="modal-footer"><button type="submit" class="btn btn-success">上传</button></form><button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button></div></div></div></div>
- 用户可以点击上传数据按钮,弹出此模态框。
- 用户可以选择一个ZIP文件,并点击模态框底部的上传按钮来提交选择的文件。
- 提交表单时,数据将被提交到当前页面(
action=""
表示当前页面),并使用POST方法传输数据。 - 上传的ZIP文件将会作为名为
problem_data_file
的文件上传字段提交。 - 提交完成后,可以点击模态框右上角的关闭按钮或底部的关闭按钮来关闭模态框。
在线显示测试数据
这段代码定义了一个名为 getDataDisplayer()
的函数,它根据问题的配置信息动态生成并返回一个 DataDisplayer
对象,用于显示不同类型的数据文件和信息。让我们逐步解释其功能和实现细节:
1. 全局变量声明:
global $data_dir;
global $problem;
- 这些变量包含了数据目录路径
$data_dir
和与问题相关的信息$problem
。
2. 获取允许显示的文件列表:
$allow_files = array_flip(array_filter(scandir($data_dir), function($x){return $x !== '.' && $x !== '..';}));
- 使用
scandir($data_dir)
获取$data_dir
目录下的所有文件和目录。 - 使用
array_filter
过滤掉当前目录 ('.'
) 和上级目录 ('..'
)。 - 使用
array_flip
将文件名作为键,便于后续快速查找文件是否存在。
3. 定义获取文件显示函数的匿名函数:
$getDisplaySrcFunc = function($name) use($allow_files) {return function() use($name, $allow_files) {$src_name = $name . '.cpp';if (isset($allow_files[$src_name])) {echoFilePre($src_name);} else {echoFileNotFound($src_name);}if (isset($allow_files[$name])) {echoFilePre($name);} else {echoFileNotFound($name);}};
};
$getDisplaySrcFunc
是一个匿名函数,接受一个文件名$name
,返回一个函数。- 返回的函数根据
$name
和$name . '.cpp'
在$allow_files
中查找文件是否存在,存在则调用echoFilePre($file_name)
显示文件预览,否则调用echoFileNotFound($file_name)
显示文件未找到的信息。
4. 读取问题的配置文件 problem.conf
:
$problem_conf = getUOJConf("$data_dir/problem.conf");
if ($problem_conf === -1) {// 处理无法读取问题配置文件的情况
}
if ($problem_conf === -2) {// 处理问题配置文件格式错误的情况
}
- 使用
getUOJConf("$data_dir/problem.conf")
函数读取问题的配置文件内容。 - 根据返回值
-1
或-2
处理无法读取或格式错误的情况,分别显示相应的错误信息。
5. 根据配置文件中的信息设置显示器和处理器:
- 根据
use_builtin_judger
的设置和其他配置信息动态生成DataDisplayer
对象,并设置相应的数据显示和处理方法。
// 处理使用内置判题器的情况
if ($problem_conf['use_builtin_judger'] == 'on') {// 设置显示器用于显示测试数据和额外测试数据// 设置显示器用于显示checker等其他类型数据// 根据hackable属性设置显示器用于显示standard和validator// 根据interaction_mode设置显示器用于显示interactor
}
题目数据统计
在单个题目的主页面的右上角中,有一个“统计”按钮。点击按钮即可显示当前题目的相关数据信息。下面列举一些重要逻辑代码:
题目数据相关信息的处理
// 分数分布数据的获取function scoreDistributionData() {$data = array();$result = DB::select("select score, count(*) from submissions where problem_id = {$_GET['id']} and score is not null group by score");$is_res_empty = true;$has_score_0 = false;$has_score_100 = false;while ($row = DB::fetch($result, MYSQLI_NUM)) {if ($row[0] == 0) {$has_score_0 = true;} else if ($row[0] == 100) {$has_score_100 = true;}$score = $row[0] * 100;$data[] = array('score' => $score, 'count' => $row[1]);}if (!$has_score_0) {array_unshift($data, array('score' => 0, 'count' => 0));}if (!$has_score_100) {$data[] = array('score' => 10000, 'count' => 0);}return $data;}// 分数分布数据的预处理$data = scoreDistributionData();$pre_data = $data;$suf_data = $data;for ($i = 0; $i < count($data); $i++) {$data[$i]['score'] /= 100;}for ($i = 1; $i < count($data); $i++) {$pre_data[$i]['count'] += $pre_data[$i - 1]['count'];}for ($i = count($data) - 1; $i > 0; $i--) {$suf_data[$i - 1]['count'] += $suf_data[$i]['count'];}// 提交排序选择的处理$submissions_sort_by_choice = !isset($_COOKIE['submissions-sort-by-code-length']) ? 'time' : 'tot_size';
- 从数据库中查询特定问题 (
problem_id = $_GET['id']
) 的提交数据,并统计每个分数的提交次数。 - 如果没有分数为0或100的记录,手动添加这些分数和对应的次数。
- 返回一个数组
$data
,包含分数和对应的提交次数。 - 使用
scoreDistributionData()
函数获取分数分布数据并存储在$data
数组中。 - 复制
$data
到$pre_data
和$suf_data
,用于预处理。 $data
数组中的每个分数除以100,以得到真实的分数值。$pre_data
数组中每个元素的count
属性表示该分数及之前所有分数的累计提交次数。$suf_data
数组中每个元素的count
属性表示该分数及之后所有分数的累计提交次数。- 检查是否设置了名为
submissions-sort-by-code-length
的 Cookie。 - 如果未设置该 Cookie,将
$submissions_sort_by_choice
设置为'time'
,表示按时间排序。 - 如果设置了该 Cookie,将
$submissions_sort_by_choice
设置为'tot_size'
,表示按代码长度排序。
使用Morris数据可视化
代码使用了 Morris.js 库来生成一个折线图,并对数据进行了一些配置和处理。
new Morris.Line({element: 'score-distribution-chart-suf', // 图表要渲染的元素的 IDdata: <?= json_encode($suf_data) ?>, // 图表使用的数据,使用 PHP 的 json_encode 将 PHP 数组转换为 JavaScript 对象xkey: 'score', // X 轴上的数据字段ykeys: ['count'], // Y 轴上的数据字段labels: ['number'], // 数据字段的标签,用于图例和提示信息lineColors: function(row, sidx, type) { // 定义折线的颜色if (type == 'line') {return '#0b62a4'; // 折线颜色}return getColOfScore(row.src.score / 100); // 如果有其他类型的数据,使用自定义函数获取颜色},xLabelFormat: function(x) { // 定义 X 轴标签的格式化函数return (x.getTime() / 100).toString(); // 将时间转换为指定格式的字符串},hoverCallback: function(index, options, content, row) { // 定义鼠标悬停提示信息的回调函数var scr = row.score / 100; // 获取分数,并转换为实际的分数值return '<div class="morris-hover-row-label">' + 'score: ≥' + scr + '</div>' + // 返回自定义的悬停提示信息的 HTML'<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&min_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';},resize: true // 是否支持自动调整大小
});