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 Falseres_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 效果演示


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

  1. 修改sqlarchemy源码使其支持jdbc连接mysql

    注意:本文不会将所有完整源码贴出,只是将具体的思路以及部分源码贴出,需要感兴趣的读者自己实验然后实现吆. 缘起 公司最近的项目需要将之前的部分业务的数据库连接方式改为jdbc,但由于之前的项目都使用s ...

  2. SF14 | Supertrend“超级趋势线”指标魔改升级(源码)

    量化策略开发,高质量社群,交易思路分享等相关内容 简文: 什么是超级趋势指标SuperTrend Indicator? 超级趋势指标SuperTrend Indicator是一个在外汇交易中常用指标, ...

  3. 利用CMake编译OpenCV-4.1.2源码,使其可以在VS2012下进行图像处理开发的记录(因缺少OpenBLAS未成功)

    目前,OpenCV已经发展到OpenCV4.X了. OpenCV是以CMake 作为项目架构系统的开源项目. 在OpenCV4中,只为我们CMake编译好了64位的适用于vc14(Visual Stu ...

  4. 百万军中取上将首级如探囊取物, 千万行里改关键源码在弹指瞬间。 功能超强的程序编辑器!

    TSEPro11_Setup.exe 百万军中取上将首级如探囊取物, 千万行里改关键源码在弹指瞬间. 功能超强的程序编辑器! 为防内容被恶意篡改,参考 MD5 (TSEPro11_Setup.exe) ...

  5. 新版代挂网站PHP源码+去除授权/支持燃鹅代抽

    正文: 最新PHP代挂网站源码+无需授权/支持燃鹅代抽,有兴趣自行去体验吧. 更新内容: 1.上线新功能[心悦礼包] 可完成G分签到,热门手游礼包领取等. 2.上线新功能[动漫任务] 可完成手Q动漫每 ...

  6. 精仿交易猫手游1:1源码可运营 支持二维码收款

    介绍: 精仿交易猫手游1:1源码可运营 支持二维码收款 源码安装需要asp主机 后台地址/admin 帐号admin密码admin 网盘下载地址: https://zijiewangpan.com/w ...

  7. 2023 首发 最新聚支付系统源码 无后门 支持易支付和码支付

    聚支付是什么水分我就不多说了 支持易支付和码支付等功能 能正常运营,已屏蔽官方的hm 完美运营.. 2023 首发 最新聚支付系统源码 无后门 支持易支付和码支付

  8. 变现宝(知识付费)1.024源码独立版支持更新

    变现宝(知识付费)1.024源码独立版支持更新 新增 详情页banner广告 修复 会员折扣为空或为0时专题支付价格都是0.01得bug 修复 不支持会员额度免费解锁的素材会员无法通过支付解锁得bug ...

  9. 魔趣-nexus源码编译

    魔趣-nexus源码编译 目标手机是Motorola nexus,开发代号为 shamu 1.先下载魔趣android分支kmp源码(mkp分支是android 9.0版) 2.下载shamu硬件适配 ...

最新文章

  1. mysql 优化器算法_SQL 查询优化器底层原理解析【MySQL 篇】
  2. 冠军奖30万!刘强东搞了个“猪脸识别”比赛,中美两地同时启动(附比赛详细日程及赛题说明)
  3. JavaSE(十)之反射
  4. 第十三期:你不想错过的那些JSON工具
  5. layui复选框组件:如何操控隐藏域实现checked状态切换(含代码、案例、截图)
  6. 使用MyEclipse修改web项目名称
  7. linux 内存坏了,Linux的缓存内存 Cache Memory详解
  8. 5个界面效果很炫的JavaScript UI框架
  9. sqlserver卸载不完全导致安装失败
  10. 网络调试助手(模拟下位机收发数据)快速指南
  11. mac视频播放器哪个最好用?不妨试试OmniPlayer Pro mac中文版吧
  12. 学计算机ppt感想60字,ppt制作的体会和感受
  13. 无缘无故,谷歌浏览器主页被篡改为360导航,如何解决?
  14. 使用防火墙禁止软件联网
  15. Python基础入门篇【26】--python基础入门练习卷B
  16. 在C ++中加载TORCHSCRIPT模型
  17. 树莓派笔记12:通过SPI操作OLED显示屏
  18. 财务自由到底是啥感觉啊...
  19. 天创速盈带您速读:拼多多有必要开直通车吗?有哪些禁忌?
  20. Ubuntu16.04安装VCS和Verdi遇到的两个问题

热门文章

  1. javaweb实习实训管理系统mysql
  2. 2017暑假集训总结
  3. 课时 26:理解 CNI 和 CNI 插件(溪恒)
  4. 网络流初步——最大流费用流
  5. 西电操作系统上机实验6
  6. 汇编语言实验12完整代码及详细解析
  7. 真无线蓝牙耳机排行榜:2020年蓝牙耳机十大名牌排行
  8. 2022数学建模国赛备赛阶段性记录(1-1)
  9. 获取每天最高分数且最新的数据(java实现)
  10. Java中this关键字及this()方法的使用