首页 > 其他分享 >创新实训(八)——题目相关的逻辑处理解释

创新实训(八)——题目相关的逻辑处理解释

时间:2024-06-23 17:21:17浏览次数:21  
标签:逻辑 题目 contest content 实训 problem data id

题目部分

题目是整个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">&times;{$problem['ac_num']}</a></td>
            <td><a href="/submissions?problem_id={$problem['id']}">&times;{$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>
<?php
    echo '<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_contestban_in_contestfalse
  • 如果竞赛存在且用户无权限:
    • 竞赛未开始,返回404页面。
    • 竞赛进行中,且用户未登录或未报名,显示比赛进行中的提示信息。
    • 如果用户已报名,设置 is_in_contesttrue 并更新数据库。
    • 竞赛已结束,检查题目是否对用户可见,设置 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请求参数 getcustom-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">&times;</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', // 图表要渲染的元素的 ID
    data: <?= 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: &ge;' + scr + '</div>' + // 返回自定义的悬停提示信息的 HTML
            '<div class="morris-hover-point">' + '<a href="/submissions?problem_id=' + <?= $problem['id'] ?> + '&amp;min_score=' + scr + '">' + 'number: ' + row.count + '</a>' + '</div>';
    },
    resize: true // 是否支持自动调整大小
});

标签:逻辑,题目,contest,content,实训,problem,data,id
From: https://www.cnblogs.com/hakurokawashiro/p/18263667

相关文章

  • 创新实训(10)- 大模型服务进一步完善&邮件服务
    之前为应付中期检查简单接入了一个基础服务,并未对prompt词等做太深入的细分,为了实现更人性化的效果,我对大模型的服务进行了更进一步的完善首先是前端的效果:超级用户端:一般用户端:三个按钮分别对应AI整理格式,AI基础纠错,AI结合题目和代码详细检查错误。前两者的返回值是......
  • 创新实训(8)- 大模型服务进一步完善&邮件服务
    之前为应付中期检查简单接入了一个基础服务,并未对prompt词等做太深入的细分,为了实现更人性化的效果,我对大模型的服务进行了更进一步的完善首先是前端的效果:超级用户端:一般用户端:三个按钮分别对应AI整理格式,AI基础纠错,AI结合题目和代码详细检查错误。前两者的返回值是......
  • 【计算机组成原理】部分题目汇总
    计算机组成原理部分题目汇总一.简答题RISC和CICS简要说明,比较异同RISC(精简指令集)注重简单快速的指令执行,使用少量通用寄存器,固定长度指令,优化硬件性能,依赖软件(如编译器)来提升效率。CISC(复杂指令集)包含多样复杂的指令,能一条指令完成多步操作,采用变长指令,减少指令数但......
  • [本科项目实训] Anaconda 环境迁移
    问题描述由于本地显卡GeForceRTX4070Ti推理速度较慢,我们拟采用服务器GeForceRTX3090进行最后的项目展示,因而需要重新配置环境。由于Cuda版本和Conda版本基本一致,拟采用condapack指令进行快速迁移。解决方案第一步,在base环境下载condapack:pipinstallconda-pack第......
  • [本科项目实训] NVIDIA-GPU 显存异常与处理
    现象描述在运行程序时,发现torch.cuda.OutOfMemoryError:CUDAoutofmemory.错误,考虑模型大小远小于所用显卡显存,使用:$nvidia-smi#或每隔两秒自动刷新$watch-n2-dnvidia-smi进行查看,发现显存占用高且GPU利用低,结果如下:+------------------------------------------......
  • [本科项目实训] HuggingFace简介与Git lfs模型下载
    HuggingFace[1]HuggingFace是一个人工智能领域尤其是自然语言处理领域的开源平台,包含数据集、模型、文档、解决方案等内容的分享。由于LLM的参数量较大,往往将参数文件托管到该平台并使用transformers[3]库进行模型调用。模型下载由于项目要求模型本地运行,因而需要下载模......
  • [本科项目实训] Node.js、npm、pnpm配置及GLIBC_2.28缺失解决方案
    问题描述Node.js是一个基于Google的V8引擎的事件驱动I/O服务端JavaScript环境。以下主要解决在Ubuntu环境中的Node配置问题。解决方案使用node-v可以查看当前的node版本,如果未安装node和npm可以尝试以下指令:aptupdateaptupgradeapt-getinstallnodejsapt-get......
  • 创新实训(七)——比赛(Contest)内部逻辑处理
    比赛部分比赛部分包含比赛列表显示、单个比赛内部信息显示、比赛管理、比赛人员以及比赛报名页面这五个代码主要代码文件。此外在内部逻辑处理时还使用了model和lib下的部分配置代码比赛列表比赛列表包含“正在进行或即将到来的比赛”和“已结束的比赛”这两部分具体代码实现......
  • 数据宽度,逻辑运算
    一、数据宽度数据宽度指的是在计算机中表示数据时所占用的内存空间大小,它决定了数据在内存中的表示范围和精度。数据宽度通常以位(bit)或字节(Byte)为单位来衡量,不同的数据类型有不同的宽度要求。在计算机科学中,数据宽度是一个基本概念,它涉及到如何有效地在内存中存储和处理数据。位......
  • 计算机系统基础实训五—CacheLab实验
    实验目的与要求1、让学生更好地应用程序性能的优化方法;2、让学生更好地理解存储器层次结构在程序运行过程中所起的重要作用;3、让学生更好地理解高速缓存对程序性能的影响;实验原理与内容本实验将帮助您了解缓存对C程序性能的影响。实验由两部分组成。在第一部分中,您将编写......