使用Python导出hustoj题目提交代码结果(用于收集留言的题目)

1. 前情提要

2022年9月10日,为IMAU七十周年校庆,我计算机院了举办“校庆杯”大学生程序设计大赛。其中有一道题J题,使用了SPJ,用来收集所有参赛者的祝福。

1576: 祈愿imau

时间限制:1s 内存限制:128MB Special Judge

题目描述

嗨 亲爱的农大IT人,相信点开这道题的时候,比赛已经接近尾声。
我想告诉你的是,这道题是一道特殊判断题(Special Judge),也就是说,这道题的答案不唯一,是否判题通过取决于一个智能程序,而不是单纯的文本比对。
用任意编程语言的输出方法,输出你们队伍对农大的祝福,并留下你的队名,提交即可AC。并且,我们会在比赛结束后将你们的祝福收集,作为本次比赛的额外收获。
同时也祝你们中秋快乐!——出题组

输入格式

输出格式

你对农大的祝福

由于队伍比较多,从管理后台一条一条复制粘贴显然是不合适的,所以接下来记录一下我导出的过程。

2. SSH 登录判题服务器

学校OJ采用的是 开源项目 hustoj(https://github.com/zhblue/hustoj),并在内网部署,其提交的代码以文本形式存在数据库中。

2.1 登录ssh

打开cmd,输入ssh远程连接指令。
(这里的user为判题用户,如果有root账户的密码也可以用root)

ssh user@172.xx.xx.xx

如果是第一次登录,会给出提示:

The authenticity of host ‘172.20.25.11 (172.20.25.11)’ can’t be established.
ECDSA key fingerprint is SHA256:xNk0TUMV41iUcslAClIk1AqddxIQHFfWOrjiakPbIWw.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

这里我们输入 yes 即可。

再次连接,提示输入密码,验证后成功进入服务器。

2.2 查看判题数据库配置

hustoj有一键安装脚本,所有的流程都是全自动执行的,所以我们事先不知道数据库相关配置,这时候就需要查看配置文件。根据hustoj官方文档,我们得知,其配置信息都存放在 /home/judge/etc/judge.conf 中。

但是直接提取会显示权限不足,遂换用root用户

我们使用 su 提权

su

输入root密码后,当前账户变成了root,这时我们再访问

vi /home/judge/etc/judge.conf

然后就会看到配置,其中含mysql的账户和密码。

OJ_HOST_NAME=127.0.0.1
OJ_USER_NAME=debian-sys
OJ_PASSWORD=**********
OJ_DB_NAME=jol
OJ_PORT_NUMBER=3306

我们再来查看一下该账户是否支持远程访问。

在ssh上:

mysql -uroot

然后输入SQL查询语句

select user, host from mysql.user;

查询结果:

+------------------+-----------+
| user             | host      |
+------------------+-----------+
| debian-sys       | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+
5 rows in set (0.00 sec)

可以看到,debian-sys账户的host为 %,说明支持远程访问,我们接下来就可以脱离SSH进行操作了。如果这里不是 %,可以去百度一下mysql账户如何开启远程访问。

3. 写SQL

3.1 使用 Navicat 连接数据库

我们使用上述配置,用 Navicat连接,查看表结构。
在这里插入图片描述

3.2 查看模型

可知,我们需要的数据集中在 solutionsource_code 表中。

逆向模型后:
在这里插入图片描述
已知题目IDproblem_id = 1576,通过题的 result = 4

3.3 写出SQL

select user_id, nick, source, result, `language`
from solution
left join source_code
on solution.solution_id = source_code.solution_id
where problem_id = 1576 and result = 4;

这样,我们就拿到了所有源代码及提交记录信息。

3.4 用Python实现

import os
import time
import pymysql
from tqdm import tqdm
import sys

database = {
    "host": "172.xx.xx.xxx",
    "username": "debian-sys",
    "password": "******",
    "database": "jol"
}

conn = pymysql.connect(
    host=database["host"],
    port=3306,
    user=database["username"],
    password=database["password"],
    database=database["database"],
    charset='utf8'
)

# 获取游标对象
cursor = conn.cursor()

# 查询 SQL 语句
# sql = "select solution_id,source from source_code where solution_id in (select solution_id from solution where problem_id = 1576) limit 10"


sql = "select user_id, nick, source, result, `language` from solution left join source_code on solution.solution_id = source_code.solution_id where problem_id = 1576 and result = 4";

cursor.execute(sql)
data = cursor.fetchall()

4. 源码处理

我们拿到了源码,但只是源码,我们还需要在本地编译运行后拿取stdout输出的内容。

我们可以针对不同语言,编写对应的编译运行代码,然后通过 重定向 >xxx.txt的方式,将结果保存在文本文件中。

# 6 = python
# 0 = c
# 1 = c++
# 3 = java
lang_dict = {
    "0": ".c",
    "1": ".cpp",
    "3": ".java",
    "6": ".py"
}
complier_dict = {
    "0": [
        "g++ -o Main {filename}",
        "Main <nullptr.txt > {filename}.txt"
    ],
    "1": [
        "g++ -o Main {filename}",
        "Main <nullptr.txt > {filename}.txt"
    ],
    "3": [
        "copy {filename} Main.java",
        "javac Main.java",
        "java Main <nullptr.txt > {filename}.txt"
    ],
    "6": [
        "python {filename} <nullptr.txt > {filename}.txt"
    ]
}

你可能会问为什么有一个 <nullptr.txt ,是因为有的队伍写了input.hasNext()等类似语句,使得初次导出时卡在那里了,所以索性直接把输入流也重定向了。
运行时需要在脚本同目录下建一个 nullptr.txt,内容为空。

循环执行指令,生成.txt文件

for item in tqdm(data):
    file_path = f"{item[0]}{lang_dict[str(item[4])]}"
    
    with open(file_path,"w") as f:
        f.write(item[2])

    for i in complier_dict[str(item[4])]:
        cmd= str(i).replace("{filename}", file_path)
        os.system(cmd)
        time.sleep(1)

完整代码:


import os
import subprocess
import time

import pymysql
from tqdm import tqdm
import sys

database = {
    "host": "172.xx.xx.xx",
    "username": "debian-sys",
    "password": "*******",
    "database": "jol"
}

conn = pymysql.connect(
    host=database["host"],
    port=3306,
    user=database["username"],
    password=database["password"],
    database=database["database"],
    charset='utf8'
)
# 获取游标对象
cursor = conn.cursor()

# 查询 SQL 语句
# sql = "select solution_id,source from source_code where solution_id in (select solution_id from solution where problem_id = 1576) limit 10"


sql = "select user_id, nick, source, result, `language` from solution left join source_code on solution.solution_id = source_code.solution_id where problem_id = 1576 and result = 4";

cursor.execute(sql)
data = cursor.fetchall()
# 6 = python
# 0 = c
# 1 = c++
# 3 = java
lang_dict = {
    "0": ".c",
    "1": ".cpp",
    "3": ".java",
    "6": ".py"
}
complier_dict = {
    "0": [
        "g++ -o Main {filename}",
        "Main <nullptr.txt > {filename}.txt"
    ],
    "1": [
        "g++ -o Main {filename}",
        "Main <nullptr.txt > {filename}.txt"
    ],
    "3": [
        "copy {filename} Main.java",
        "javac Main.java",
        "java Main <nullptr.txt > {filename}.txt"
    ],
    "6": [
        "python {filename} <nullptr.txt > {filename}.txt"
    ]
}

res_list = []
for item in tqdm(data):
    file_path = f"{item[0]}{lang_dict[str(item[4])]}"
    # print(file_path)
    # print(item)
    with open(file_path,"w") as f:
        f.write(item[2])

    for i in complier_dict[str(item[4])]:
        cmd= str(i).replace("{filename}", file_path)
        print(cmd)
        os.system(cmd)
        time.sleep(1)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

okfang616

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值