• 使用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
    
    • 1

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

    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
    
    • 1

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

    vi /home/judge/etc/judge.conf
    
    • 1

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

    OJ_HOST_NAME=127.0.0.1
    OJ_USER_NAME=debian-sys
    OJ_PASSWORD=**********
    OJ_DB_NAME=jol
    OJ_PORT_NUMBER=3306
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    在ssh上:

    mysql -uroot
    
    • 1

    然后输入SQL查询语句

    select user, host from mysql.user;
    
    • 1

    查询结果:

    +------------------+-----------+
    | user             | host      |
    +------------------+-----------+
    | debian-sys       | %         |
    | mysql.infoschema | localhost |
    | mysql.session    | localhost |
    | mysql.sys        | localhost |
    | root             | localhost |
    +------------------+-----------+
    5 rows in set (0.00 sec)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到,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;
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    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  {filename}.txt"
        ],
        "1": [
            "g++ -o Main {filename}",
            "Main  {filename}.txt"
        ],
        "3": [
            "copy {filename} Main.java",
            "javac Main.java",
            "java Main  {filename}.txt"
        ],
        "6": [
            "python {filename}  {filename}.txt"
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    你可能会问为什么有一个 ,是因为有的队伍写了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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    完整代码:

    
    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  {filename}.txt"
        ],
        "1": [
            "g++ -o Main {filename}",
            "Main  {filename}.txt"
        ],
        "3": [
            "copy {filename} Main.java",
            "javac Main.java",
            "java Main  {filename}.txt"
        ],
        "6": [
            "python {filename}  {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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
  • 相关阅读:
    阿里云基础知识小结
    成集云 | 成集云-经销商返利系统集成金蝶云星辰 | 解决方案
    (三)CSS前端开发面试会问到的问题有哪些?
    性能测试 —— Jmeter 命令行详细
    第三章 内存管理 六、基本地址变换结构
    修timing中的SI-noise问题
    leetcode困难262.行程和用户
    Java中的异常体系
    SpringBoot Mybatis 多数据源 MySQL+Oracle+Redis
    第11章 使用类
  • 原文地址:https://blog.csdn.net/okfang616/article/details/126802827