魔改hustoj源码使其支持显示队名和队员及女队标志

news2024/9/27 19:15:06

0. 起因&需求

本文涉及到的开源项目Github地址:https://github.com/zhblue/hustoj

事件的起因是,计算机学院要举办一个院级的ACM比赛,然后捏… 老师给我提了一个需求,就是能不能把比赛排行榜显示的队名下标注对应的队员,并且如果能在女队(指某个队伍的参赛者都为女生)的队伍名伍名后面标注一个“女队”的标志就好了。

当然,这个事情很简单,我们改个昵称就行了。。我相信老师也是这样想的,所以把这个任务交给了我,我也确实这样干了。

然鹅,,,修改完毕后,在测试的时候发现,有的队伍的昵称没有更新,并且他们在提交答案的时候,在“状态”页并没有发现其提交记录,可以说是很多bug了。

去hustoj的F&Q里寻找答案,果然:

在这里插入图片描述
来源: https://github.com/zhblue/hustoj/blob/master/wiki/FAQ.md

也就是说,如果队名足够长,就会严重影响到其他功能的正常使用。而且仅凭更改昵称实现,无法个性化定制每个部分的样式(颜色、字号、换行等),所以,最好的办法就是——修改源码

1. 服务器&数据库

即然要修改源码,我们第一步就是要拿到部署hustoj的那台服务器的ssh权限。(如果是分布式部署,需要拿到部署web服务的那台)。

先在本地终端试一下:

ssh 账户名@主机名或ip

接着输入密码,连接成功:

在这里插入图片描述
直接登录mysql:

mysql -uroot # 这样连接不需要提供密码
use jol; # 默认数据库
select user_id, nick from users where user_id like 'kx%' limit 5;
# 从users表里查找前5个以 kx为前缀的用户(kx是某次竞赛批量生成用户们的前缀)

在这里插入图片描述

2. 比赛数据处理

2.1 生成队伍账号

我们拿到了本次比赛的参赛队伍名单(节选):

账号队名队长队员是否为女队
kx_team01王炸组王晶黄江晴 史旭东
kx_team02做的全队牛颖杰宋佳璘 杨立帆
kx_team03意念交流队吴柯萱冀晓晅 张晗
kx_team04翻斗花园王浩李响 王振铭
kx_team05风雨无组张荣轩许培 李杰

这里我拿到的名单已经包含了账号、队名、密码的信息,所以只需要通过hustoj自带的比赛账号生成器批量生成一下就行。
在这里插入图片描述当然,如果密码尚未指定,还是用简约版的生成器比较好:

在这里插入图片描述

2.2 数据处理

2.1 中的表格被保存为:teamdata.xlsx

我们拿python处理一下表格数据,方便我们之后生成其他命令。

import json
import pandas as pd
import numpy as np

# 读取excel表格
data = pd.read_excel("teamdata.xlsx", sheet_name=0)

def format_team_members(raw_str):
    """
    处理包含多个队员姓名的字符串
    :param raw_str: 直接从表格读出来的字符串
    :return: 处理好的列表,每个列表元素为队员的姓名
    """
    return list(filter(lambda x: x != "" and x != None and x != "nan",
                       raw_str.replace("、", " ").replace("\xa0", " ").split(" ")))
                       
res_dict = dict()

for row in data.itertuples():
    team_name = getattr(row, "队名")
    team_head = getattr(row, "队长")
    team_members = format_team_members(str(getattr(row, "队员")))
    team_id = getattr(row, "账号")
    is_girl = True if getattr(row, "是否为女队") == "是" else False

    res_dict[team_id] = {
        "team_name": team_name, # 队伍名
        "team_head": team_head, # 队长
        "team_members": team_members, # 队员列表
        "team_length": 1 + len(team_members), # 总人数
        "is_girl": is_girl,
    }

	# 此处插旗flag,之后会在此处添加代码

print(json.dumps(res_dict, ensure_ascii=False))

生成的结果如下:

{
    "kx_team01": {
        "team_name": "王炸组",
        "team_head": "王晶",
        "team_members": [
            "黄江晴",
            "史旭东"
        ],
        "team_length": 3,
        "is_girl": false
    },
    "kx_team02": {
        "team_name": "做的全队",
        "team_head": "牛颖杰",
        "team_members": [
            "宋佳璘",
            "杨立帆"
        ],
        "team_length": 3,
        "is_girl": false
    },
    "kx_team03": {
        "team_name": "意念交流队",
        "team_head": "吴柯萱",
        "team_members": [
            "冀晓晅",
            "张晗"
        ],
        "team_length": 3,
        "is_girl": true
    },
    "kx_team04": {
        "team_name": "翻斗花园",
        "team_head": "王浩",
        "team_members": [
            "李响",
            "王振铭"
        ],
        "team_length": 3,
        "is_girl": false
    },
    "kx_team05": {
        "team_name": "风雨无组",
        "team_head": "张荣轩",
        "team_members": [
            "许培",
            "李杰"
        ],
        "team_length": 3,
        "is_girl": true
    }
}

这样,我们就相当于整了一个以user_id为键,以信息为值的哈希表,我们只要以某种方式在竞赛排名页内提供这个json串,用javascript操作dom就可以轻松实现。

2.3 重新编码

如何存储上一个步骤我们得到的信息,并让网页能够读取到呢?

  • 方案一:直接存为 竞赛号(如1009).json文件,然后当作一种静态资源,前端可以通过异步请求拿取。

  • 方案二:用一个.php文件做中转,从$_REQUEST中获取竞赛号,然后返回对应的json字符串。前端通过异步请求拿取。

    • 方案二的存储方案一:存在竞赛号(如1009).json文件里,用file_get_contents()读取
    • 方案二的存储方案二:存在users表的闲置字段里,拿sql获取

此处,我选择 方案二的存储方案二(因为闲置字段确实比较多)。

我们查看一下 users表的模型,可以看到school字段最适合存。

在这里插入图片描述

但是默认长度只有20,我们可以通过修改school字段的大小来解决。或者把json字符串编码一下,缩短一下长度:

(队员们的姓名以空格隔开)[总人数][女]

kx_team01,编码为:(王晶 黄江晴 史旭东)[3]
kx_team02,编码为:(吴柯萱 冀晓晅 张晗)[3][女]

用Python实现:

# 接在上段python代码里的循环体末部,插旗flag的地方

print(f'({" ".join([team_head, *team_members])})'
      f'[{1 + len(team_members)}]'
      f'{"[女]" if is_girl else ""}')

运行结果:

(王晶 黄江晴 史旭东)[3]
(牛颖杰 宋佳璘 杨立帆)[3]
(吴柯萱 冀晓晅 张晗)[3][女]
(王浩 李响 王振铭)[3]
(张荣轩 许培 李杰)[3][女]

编码方式确定了,剩下的就是更新数据库了。

有一种比较偷懒的方式,就是直接生成sql语句,然后复制粘贴到ssh终端里的mysql命令行,挨个执行。

Python编写update sql语句:

# 接在上段python代码里的循环体末部,插旗flag的地方

school =f'({" ".join([team_head, *team_members])})'\
      f'[{1 + len(team_members)}]'\
      f'{"[女]" if is_girl else ""}'
sql = f'update users set nick=\'{team_name}\', school=\'{school}\'' \
      f' where user_id = \'{team_id}\';'
print(sql)

运行结果:

update users set nick='王炸组', school='(王晶 黄江晴 史旭东)[3]' where user_id = 'kx_team01';
update users set nick='做的全队', school='(牛颖杰 宋佳璘 杨立帆)[3]' where user_id = 'kx_team02';
update users set nick='意念交流队', school='(吴柯萱 冀晓晅 张晗)[3][女]' where user_id = 'kx_team03';
update users set nick='翻斗花园', school='(王浩 李响 王振铭)[3]' where user_id = 'kx_team04';
update users set nick='风雨无组', school='(张荣轩 许培 李杰)[3][女]' where user_id = 'kx_team05';

我们直接复制粘贴到mysql客户端里执行即可。

在这里插入图片描述
至此,我们成功将【队长、队员信息、人数、是否为女队】信息存到了数据库里,只剩下中转php文件的编写了。

3 修改源码

PHP是一种在服务器端执行的脚本语言,尤其适用于Web开发并可嵌入HTML中。PHP语法学习了C语言,吸纳Java和Perl多个语言的特色发展出自己的特色语法,并根据它们的长项持续改进提升自己。PHP可以与数据库交互,创建动态内容。
PHP代码不需要编译,而是直接由解释器解释执行。这样就可以在不重启服务器的情况下修改代码,并立即看到效果。这对于快速开发和调试非常方便。

基于这个前提,我们可以直接修改生产环境下的源码并看到效果(不用重启任何程序)。

稍微用点时间,可以找到存放web服务php代码的地方 /home/judge/src/web

在这里插入图片描述

3.1 中转php文件编写(api接口)

我们在 /home/judge/src/web 下新建文件夹 api/team,然后在里面新建一个index.php文件。

cd /home/judge/src/web
mkdir api/team -p
vi api/team/index.php

然后在get.php填写代码(这里我已经研究完了):

<?php
/* /home/judge/src/web/api/team/get.php */
chdir("../..");
/* 参数接收 */
if (isset($_REQUEST['cid']) && is_numeric($_REQUEST['cid'])) {
    $cid = intval($_REQUEST['cid']);
}
/* 返回体设置 */
class ReturnBody
{
    var $code;
    var $msg;
    var $data;

    function success($data)
    {
        $this->code = 200;
        $this->msg = "success";
        $this->data = $data;
        echo json_encode($this, JSON_UNESCAPED_UNICODE);
        exit(0);
    }

    function fail($msg, $data)
    {
        $this->code = 401;
        $this->msg = $msg;
        $this->data = $data;
        echo json_encode($this, JSON_UNESCAPED_UNICODE);
        exit(0);
    }
}
header('Content-Type:Application/json;charset=utf-8');
$ret = new ReturnBody;

/* 必要的信息索引 */
$cache_time = 10;
$OJ_CACHE_SHARE = false;
require_once('./include/cache_start.php');
require_once('./include/db_info.inc.php');
require_once('./include/const.inc.php');
require_once('./include/memcache.php');
require_once('./include/setlang.php');



/* 队伍信息 */
if ($OJ_MEMCACHE) {
    $sql = "select `user_id`, concat('#[', `nick`, ']', `school`) as `name` from `users` where `users`.`user_id` in (select `user_id` from `solution` where `contest_id` = $cid );";
    $fb = mysql_query_cache($sql);
} else {
    $sql = "select `user_id`, concat('#[', `nick`, ']', `school`) as `name`  from `users` where `users`.`user_id` in (select `user_id` from `solution` where `contest_id` = ? );";
    $fb = pdo_query($sql, $cid);
}

$ans = array();
foreach ($fb as $val) {
    $ans[$val["user_id"]] = $val["name"];
}
$ret->success($ans);

if (file_exists('./include/cache_end.php'))
    require_once('./include/cache_end.php');

如此,我们访问http://oj地址/api/team/?cid=竞赛编号,就可以得到json字符串了。

{
    "code": 200,
    "msg": "success",
    "data": {
        "kx_team01": "王炸组(王晶 黄江晴 史旭东)[3]",
        "kx_team02": "做的全队(牛颖杰 宋佳璘 杨立帆)[3]",
        "kx_team03": "意念交流队(吴柯萱 冀晓晅 张晗)[3][女]",
        "kx_team04": "翻斗花园(王浩 李响 王振铭)[3]",
        "kx_team05": "风雨无组(张荣轩 许培 李杰)[3][女]",
    }
}

3.1 找到显示排行榜信息的php文件

很明显,和竞赛排名相关的php文件如下:
在这里插入图片描述
这里之所以有这么多的contestrank开头的文件,是因为排行榜页面不止一个,本文我们以 contestrank.php 为例。

这里有个细节/home/judge/src/web/contestrank.php 确实是排行榜的页面,但是查看具体的代码可以在文件末尾看到这样一行代码:

/Template
require("template/".$OJ_TEMPLATE."/contestrank.php");

也就是说,/home/judge/src/web/contestrank.php 执行完以后,还要再继续执行,主题下的 contestrank.php。(/home/judge/src/web/contestrank.php 只进行了数据的获取,没有编写前端部分)

那么 $OJ_TEMPLATE 是指什么呢?
我们可以通过查看 /home/judge/src/web/include/db_info.inc.php获取。

在该文件,你可以看到这条语句:

static  $OJ_TEMPLATE="syzoj";
//使用的默认模板,template目录下的每个子目录都是一个模板, [bs3 mdui sweet syzoj mario bshark] work with discuss3

修改此处的变量值可以切换主题(如果你不知道,这个是一个很有用的知识),我们也得知$OJ_TEMPLATE其实就是 syzoj

我们最终要修改的文件是:/home/judge/src/web/template/syzoj/contestrank.php

3.2 异步请求

直接编写/home/judge/src/web/template/syzoj/contestrank.php好像有点不太好,最好的方式是把要新增的代码写在另一个文件里,然后在 /home/judge/src/web/template/syzoj/contestrank.php里引用。

# 创建 team-nick.php
touch /home/judge/src/web/include/team-nick.php

然后在 /home/judge/src/web/template/syzoj/contestrank.php 中的加入代码<?php include("include/team-nick.php") ?>

<?php include("include/team-nick.php") ?>
<?php include("template/$OJ_TEMPLATE/footer.php"); ?>

注意:<?php include("template/$OJ_TEMPLATE/footer.php"); ?> 这条是帮助你定位用的,我们实际插入的代码只有第一条。

接下来我们编辑 /home/judge/src/web/include/team-nick.php

<script>
    function getHtmlByTag(team_len) {
        if (team_len == 2) {
            return `
            <span style="background-image: linear-gradient(to right, #74ebd5 0%, #9face6 100%);padding: 3px;font-size: 8px;color:white;border-radius: 0.8em">
            双人成行
            </span>
            `;
        } else if (team_len == 1) {
            return `
        <span style="background-image: linear-gradient(to top, #f43b47 0%, #453a94 100%);padding: 3px;font-size: 8px;color:white;border-radius: 0.5em">
        单枪匹马
        </span>

            `;
        } else {
            return "";
        }
    }

    function getGirlTag(is_girl) {
        if (is_girl) {
            return `
            <span style="background-image: linear-gradient(to right, #fa709a 0%, #fee140 100%);padding: 3px;font-size: 8px;color:white;border-radius: 0.8em">
            女队
            </span>
            `;
        } else {
            return "";
        }
    }


    new Promise((res) => {
        res(document.querySelectorAll('#main > div.padding > table > tbody > tr > td > a[name]') || [])
    }).then((data) => {
        fetch('/api/team/?cid=<?php echo $cid; ?>')
            .then(response => response.json())
            .then(fetch_data => {
                const team_data = fetch_data.data;
                data.forEach((item) => {
                    const raw_text = team_data[item.name] || "";
                    if (raw_text.startsWith("#")) {
                        let team_numbers = raw_text.match(/\([^\)]+\)/g)[0];
                        team_numbers = team_numbers.substring(1, team_numbers.length - 1);
                        // team_numbers = team_numbers.split(" ");
                        let team_name = raw_text.match(/\[[^\)]+\]/g)[0];
                        team_name = team_name.substring(1, team_name.length - 1);

                        let team_len = raw_text.match(/\[[^\)]+\]/g)[1];
                        team_len = team_len.substring(1, team_len.length - 1);


                        item.innerHTML = `
                <div style="display: flex; justify-content: center">
                <div>

          <div style='font-size: 16px;font-weight: bold'>${team_name}${getHtmlByTag(team_len)}${getGirlTag(raw_text.includes("[女]"))}</div>
          <div style='font-size: 12px'>${team_numbers}</div>
          
          
                </div>
                </div>
        `;
                    }
                })

            });

    })
</script>

最后,我们修改一下下 /home/judge/src/web/template/syzoj/contestrank.php 就可以了。(红色圈住的是新增代码)
在这里插入图片描述

4 效果演示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371596.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

光栅化Triangles(笔记)

field of view (可见区域) 该角度越大,需要透视投影的角度越大,成像显示的内容越多 有Y值,则可得出成像范围 屏幕: 典型的光栅处理设备所有像素都被表示为x,y坐标轴形式 3D方块成像步骤: 先将其所在平面化为 与屏幕等长等宽的形式: 如何将一个三角形拆成像素&#xff1f;采样…

C++学习笔记-2

C学习笔记-2输入/输出控制----I/O流命名空间的定义及使用string类型函数改进域解析符::扩大全局变量的作用域形式参数可带有默认值函数重载引用的定义与应用引用的概念及使用引用作为形式参数引用与指针的比较引用作为返回值动态内存空间用new申请动态内存空间用delete释放动态…

unicloud的aggregate聚合查询时间戳转日期

我特么不知道看了这个帖子几百遍才看明白到-----》unicloud数据库中&#xff0c;聚合操作如何操作时间戳&#xff1f; - DCloud问答 自己淋过雨老想着为别人撑伞&#xff0c;可怜我这35岁的老人家&#xff0c;给我去点关注&#xff01;&#xff01;&#xff01;&#xff01;&a…

centos上cliskhouse的安装记录

clickhouse是由俄罗斯Yandex公司开发的列式存储数据库&#xff0c;于2016年开源&#xff0c;clickhouse的定位是快速的数据分析&#xff0c;对于处理海量数据的情况性能非常好&#xff0c;在网上也有很多测试的案例&#xff0c;在大数据的情况下性能远超过其他数据库&#xff0…

ThreadLocal原理、结构、源码解析

文章目录一、Thread简介1.什么是ThreadLocal2.为什么要是用ThreadLocal2.1Synchronized、Lock保证线程安全2.2ThreadLocal保证线程安全3.ThreadLocal和Synchronized的区别二、ThreadLocal原理1.Thread抽象内部结构2.ThreadLocal源码2.1Thread、ThreadLocal、ThreadLocalMap、En…

行业观察 | 内存接口芯片和CXL协议

本文对内存接口芯片的概念和CXL协议进行不完全总结。 更新&#xff1a;2022 / 02 / 25 行业观察 | 内存接口芯片背景内存接口芯片概念趋势CXL 协议背景 DRAM 内存基本被国外巨头 三星、美光 等内存模组制造商垄断&#xff0c;合计市场占有率超过 90%。 内存接口芯片 概念 什…

【Android】Android开发笔记(一)

【Android】Android开发笔记&#xff08;一&#xff09; 在Android Studio中import module和delete moduleimport moduledelete moduleAndroid Studio中App&#xff08;Module&#xff09;无法正常运行在实机上测试App一些基本概念App的工程结构结语在Android Studio中import m…

Leetcode Solutions - Part 2

1. Two Sum 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按…

7-vue-1

谈谈你对MVVM的理解 为什么要有这些模式&#xff0c;目的&#xff1a;职责划分、分层&#xff08;将Model层、View层进行分类&#xff09;借鉴后端思想&#xff0c;对于前端而已&#xff0c;就是如何将数据同步到页面上 MVC模式 代表&#xff1a;Backbone underscore jquer…

【DIY Arduino基于RC气垫船】

在本教程中,我们将学习如何构建基于 Arduino 的 RC 气垫船。我将向您展示构建它的整个过程,从设计和3D打印气垫船部件(包括螺旋桨)开始,到连接电子元件和对Arduino进行编程。 您可以观看以下视频或阅读下面的书面教程。 视频地址:https://www.youtube.com/watch?v=2XIG…

二叉平衡树(C++)

背景&#xff1a; 二叉平衡树&#xff0c;就是根据二叉搜索树进行优化&#xff0c;让其速度更加的快&#xff0c;如果读者没有学过二叉搜索树&#xff0c;可以前往以下链接查看资料&#xff1a;http://t.csdn.cn/cCDQDhttp://t.csdn.cn/cCDQD 二叉搜索树的缺陷&#xff1a; 在…

【Spark分布式内存计算框架——Spark Streaming】1. Streaming 概述(上)Streaming 应用场景、Lambda 架构

前言 在很多实时数据处理的场景中&#xff0c;都需要用到流式处理&#xff08;Stream Process&#xff09;框架&#xff0c;Spark也包含了两个完整的流式处理框架Spark Streaming和Structured Streaming&#xff08;Spark 2.0出现&#xff09;&#xff0c;先阐述流式处理框架&…

Linux--TCP编程--0216 17

观前提示&#xff1a;本篇博文的一些接口需要前几篇博文实现的 线程池的实现Liunx--线程池的实现--0208 09_Gosolo&#xff01;的博客-CSDN博客 线程池的单例模式Linux--线程安全的单例模式--自旋锁--0211_Gosolo&#xff01;的博客-CSDN博客 1.TCP编程需要用的接口 创建 sock…

【数据挖掘】EDA——以2022雪浪算力开发者大赛数据为例

作者简介&#xff1a;重庆大学22级研一&#xff0c;研究方向&#xff1a;时空数据挖掘、图神经网络。目前正在学习大数据、数据挖掘等相关知识&#xff0c;希望毕业后能找到数据相关岗位。 前言 之前写了一个比赛复盘&#xff08;【竞赛复盘】2022雪浪算力开发者大赛——阀体异…

Python脚本之准备测试环境的用户数据

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/127645678 这期是讲述下 我准备测试环境用户数据的经历。 【实际这篇博客推迟发布N个月】 个人博客&#xff1a;https://blog.csdn.…

【读书笔记】《深入浅出数据分析》第三章 寻找最大值

目录 一&#xff0c;Excel却是最基础、最高频、最有机会展示的一款数据分析工具二&#xff0c;作为数据工作者&#xff0c;实际工作中&#xff0c;不管用不用的上&#xff0c;至少到达会的水准1&#xff0c;常用函数2&#xff0c;透视表3&#xff0c;可视化4&#xff0c;数据分…

【RabbitMQ笔记04】消息队列RabbitMQ七种模式之发布订阅模式(Publish/Subscribe)

这篇文章&#xff0c;主要介绍消息队列RabbitMQ七种模式之发布订阅模式&#xff08;Publish/Subscribe&#xff09;。 目录 一、发布订阅模式 1.1、Exchange交换机 &#xff08;1&#xff09;什么是Exchange交换机呢&#xff1f;&#xff1f;&#xff1f; &#xff08;2&am…

数据结构与算法----问答2023

1、什么是哈希表&#xff1f;如何解决碰撞&#xff1f; 哈希表&#xff08;Hash Table&#xff09;&#xff0c;也称为散列表&#xff0c;是一种用于实现字典&#xff08;键值对&#xff09;数据结构的数据结构。它将键映射到哈希表中的一个索引&#xff08;桶&#xff09;来保…

从零开始学习iftop流量监控(找出服务器耗费流量最多的ip和端口)

一、iftop是什么iftop是类似于top的实时流量监控工具。作用&#xff1a;监控网卡的实时流量&#xff08;可以指定网段&#xff09;、反向解析IP、显示端口信息等官网&#xff1a;http://www.ex-parrot.com/~pdw/iftop/二、界面说明>代表发送数据&#xff0c;< 代表接收数…

DHCP服务器的使用以及可能出现的问题(图文详细版)

DHCP服务的使用 开始&#xff0d;管理工具&#xff0d;DHCP,打开DHCP服务器选项窗口 新建作用域 在此处输入名称和描述,单击下一步 随机确定一组IP地址的范围,并指定其子网掩码 , 单击下一步 若想要排除某一个/组特定的IP地址,我们可以在此界面输入该IP地址,若没有,则可…