为自己打造一个自动造数据机
stripe_python · · 科技·工程
项目背景
众所周知,现在造数据的常用方法是写一个 C++ 脚本或者使用 cyaron。
既然这个云 Generator 没出来,本蒟蒻只好自己写一个自己用的了。
注意:本项目建议自己使用,项目并没有安全防御功能。
项目结构
本蒟蒻计划使用 python3.8 的 flask 框架搭建。目录如下:
KittenGen
│ LICENSE
│ MANIFEST.in
│ README.md
│ requirements.txt
│ setup.py
│
└─KittenGen
│ app.py
│ lib.py
│ __init__.py
│ __main__.py
│
├─static
│ ├─css
│ │ bootstrap.min.css
│ │ codemirror.min.css
│ │
│ └─js
│ bootstrap.min.js
│ clike.min.js
│ codemirror.min.js
│ jquery.min.js
│ python.min.js
│
└─templates
help.html
index.html
这里 app.py
实现服务器端,templates
中的东西是前端。
代码
前端
这里本蒟蒻使用 Deepseek 帮忙设计了网页。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>KittenGen</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<link href="/static/css/codemirror.min.css" rel="stylesheet" >
<script src="/static/js/codemirror.min.js"></script>
<script src="/static/js/clike.min.js"></script>
<script src="/static/js/python.min.js"></script>
<style type="text/css">
.CodeMirror {
min-height: 150px;
border-radius: 4px;
}
</style>
</head>
<body class="bg-light">
<div class="container py-5">
<form method="post" action="/" class="card shadow" onsubmit="syncCodeEditors()">
<div class="card-body">
{% if error %}
<div class="alert alert-danger" id="errAlert" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<h5 class="alert-heading">错误!</h5>
{{ error }}
</div>
{% endif %}
<!-- Std.cpp 输入区域 -->
<div class="mb-4">
<label class="form-label fw-bold">程序 Std(std.cpp)</label>
<div id="std-editor" class="border rounded overflow-hidden"></div>
<textarea class="d-none" id="std" name="std"></textarea>
</div>
<!-- Maker.py 输入区域 -->
<div class="mb-4">
<label class="form-label fw-bold">
数据生成器(maker.py)
<a href="/help" class="fs-6 text-decoration-none ms-2">查看帮助说明?</a>
</label>
<div id="maker-editor" class="border rounded overflow-hidden"></div>
<textarea class="d-none" id="maker" name="maker"></textarea>
</div>
<!-- 数据组数 -->
<div class="mb-4">
<label for="num" class="form-label fw-bold">数据组数</label>
<input type="number" class="form-control" id="num" name="num"
min="1" value="10" required>
</div>
<!-- 编译选项 -->
<div class="mb-4">
<label for="options" class="form-label fw-bold">C++ 编译选项</label>
<input type="text" class="form-control" id="options" name="options"
value="-O2 -std=c++14 -Wl,--stack=204800000" required>
</div>
<!-- SPJ 开关 -->
<div class="mb-3 form-check form-switch">
<input type="checkbox" class="form-check-input" id="useSpj"
name="useSpj" role="switch">
<label class="form-check-label fw-bold" for="useSpj">是否使用SPJ</label>
</div>
<!-- SPJ 输入区域(默认隐藏) -->
<div id="spjSection" style="display: none;">
<div class="mb-4">
<label class="form-label fw-bold">SPJ(checker.cpp)</label>
<div id="checker-editor" class="border rounded overflow-hidden"></div>
<textarea class="d-none" id="checker" name="checker"></textarea>
</div>
</div>
<!-- Config 开关 -->
<div class="mb-3 form-check form-switch">
<input type="checkbox" class="form-check-input" id="useCfg"
name="useCfg" role="switch">
<label class="form-check-label fw-bold" for="useCfg">是否使用配置文件</label>
</div>
<!-- config.yml 输入区域(默认隐藏) -->
<div id="cfgSection" style="display: none;">
<div class="mb-4">
<label class="form-label fw-bold">配置文件(config.yml)</label>
<div id="config-editor" class="border rounded overflow-hidden"></div>
<textarea class="d-none" id="config" name="config"></textarea>
</div>
</div>
<!-- 提交按钮 -->
<input type="submit" class="btn btn-primary btn-lg w-100 mt-3" id="submit" value="提交">
</div>
</form>
</div>
<script>
// 初始化代码编辑器
const editors = {
std: CodeMirror(document.getElementById('std-editor'), {
mode: 'text/x-c++src',
lineNumbers: true,
theme: 'default',
indentUnit: 4,
extraKeys: {"Ctrl-Space": "autocomplete"},
value: document.getElementById('std').value
}),
maker: CodeMirror(document.getElementById('maker-editor'), {
mode: 'python',
lineNumbers: true,
theme: 'default',
indentUnit: 4,
value: document.getElementById('maker').value
}),
checker: CodeMirror(document.getElementById('checker-editor'), {
mode: 'text/x-c++src',
lineNumbers: true,
theme: 'default',
indentUnit: 4,
value: document.getElementById('checker').value
}),
checker: CodeMirror(document.getElementById('config-editor'), {
lineNumbers: true,
theme: 'default',
indentUnit: 4,
value: document.getElementById('config').value
})
};
// 同步编辑器内容到表单
function syncCodeEditors() {
document.getElementById('std').value = editors.std.getValue();
document.getElementById('maker').value = editors.maker.getValue();
document.getElementById('checker').value = editors.checker.getValue();
}
// SPJ 切换逻辑
document.getElementById('useSpj').addEventListener('change', function() {
const spjSection = document.getElementById('spjSection');
spjSection.style.display = this.checked ? 'block' : 'none';
});
// Config 切换逻辑
document.getElementById('useCfg').addEventListener('change', function() {
const cfgSection = document.getElementById('cfgSection');
cfgSection.style.display = this.checked ? 'block' : 'none';
});
</script>
</body>
</html>
基于 bootstrap
和 codemirror
美化前端,效果如下:
对于 help.html
,使用 <ul>
给出提示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>KittenGen | 帮助</title>
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</head>
<body class="bg-light">
<div class="container py-5">
<div class="card shadow">
<div class="card-body">
<h2 class="mb-4">数据生成器使用说明</h2>
<h4 class="mb-3 text-primary">基本规范</h4>
<ul class="list-group mb-4">
<li class="list-group-item">
<strong>代码中可以使用全局变量 <code>num: int</code></strong> -
表示当前测试点编号(从1开始)
</li>
<li class="list-group-item">
<strong>random 库已自动预导入</strong> -
可直接使用 randint/choice 等函数
</li>
<li class="list-group-item">
<strong>使用 print 函数直接输出</strong> -
内容会自动写入输入文件,无需文件操作
</li>
</ul>
<h4 class="mb-3 text-primary">示例代码</h4>
<strong>一个单点修改,区间查询的数据结构题</strong>
<pre class="bg-dark text-light p-4 rounded mb-4">
<code class="language-python">
N = [0, 100, 2000, 100000, 100000, 200000, 200000, 300000, 400000, 500000, 500000]
Q = [0, 100, 2000, 100000, 100000, 200000, 200000, 300000, 400000, 500000, 500000]
E9 = 10 ** 9
n, q = N[num], Q[num]
print(n, q)
for i in range(n):
print(randint(-E9, E9), end=' ')
print()
for i in range(q):
opt = choice([1, 2])
l = randint(1, n)
if opt == 1:
print(opt, l, randint(-E9, E9))
else:
r = randint(1, n)
if l > r:
l, r = r, l
print(opt, l, r)
</code>
</pre>
<div class="text-center">
<a href="/" class="btn btn-primary btn-lg px-5">返回</a>
</div>
</div>
</div>
</div>
</body>
</html>
效果图:
服务器
先把 std 和 maker 都编译一下。为了方便,本蒟蒻选择直接进入 ./data
路径,用 maker.py
生成数据以后跑 std.exe
。app.py
代码如下:
import os
import zipfile
from flask import Flask, render_template, request, send_file
from .lib import global_vars # 默认变量
app = Flask(__name__)
GCC_PATH = 'g++.exe' # 这里填写你的g++路径
@app.route('/', methods=['GET', 'POST'])
def index(): # 主网页
if request.method == 'GET':
return render_template('index.html', error=None)
cwd = os.getcwd() # 记录当前工作路径
data_path = os.path.join(cwd, 'data')
if not os.path.exists(data_path):
os.mkdir(data_path)
os.chdir(data_path) # 进入数据工作路径
form = request.form
std, maker, num, options = form.get('std'), form.get('maker'), int(form.get('num')), form.get('options')
try:
maker_complied = compile(maker, 'maker.py', 'exec') # 先编译生成器
except (Exception, SystemExit) as err:
os.chdir(cwd) # 切换回原工作路径
return render_template('index.html', error=str(err))
use_spj, use_cfg = form.get('useSpj'), form.get('useCfg')
if use_spj:
checker = form.get('checker')
with open('checker.cpp', 'w', encoding='utf-8') as f:
f.write(checker)
if use_cfg:
config = form.get('config')
with open('config.yml', 'w', encoding='utf-8') as f:
f.write(config)
with open('std.cpp', 'w', encoding='utf-8') as f:
f.write(std) # 写入std.cpp
os.system(f'{GCC_PATH} {options} -o std.exe std.cpp') # 编译 std
for i in range(1, num + 1): # 循环并生成数据
local_vars = global_vars.copy()
in_file = open(f'{i}.in', 'w', encoding='utf-8')
local_vars['num'], local_vars['print'] = i, lambda *args, **kwargs: print(*args, **kwargs, file=in_file) # 设置变量
try:
exec(maker_complied, local_vars)
except (Exception, SystemExit) as err:
os.chdir(cwd) # 切换回原工作路径
return render_template('index.html', error=str(err))
finally:
in_file.close()
os.system(f'std.exe < {i}.in > {i}.out') # 使用 std 生成答案
with zipfile.ZipFile('data.zip', 'w', zipfile.ZIP_DEFLATED) as zf: # 打包 zip 文件
for i in range(1, num + 1):
zf.write(f'{i}.in')
zf.write(f'{i}.out')
if use_spj:
zf.write('checker.cpp')
if use_cfg:
zf.write('config.yml')
os.chdir(cwd) # 切换回原工作路径
return send_file(os.path.join(data_path, 'data.zip'), as_attachment=True) # 返回答案 zip 文件
@app.route('/help/')
def show_help():
return render_template('help.html')
其中,lib.py
代码如下。你可以根据需要自行添加函数或变量。
import random
import math
import string
global_vars = {
'random': random.random,
'randint': random.randint,
'uniform': random.uniform,
'choice': random.choice,
'sample': random.sample,
'seed': random.seed,
'randrange': random.randrange,
'Random': random,
'Math': math,
'PI': math.pi,
'E': math.e,
'ASCII_LOWERCASE': string.ascii_lowercase,
'ASCII_UPPERCASE': string.ascii_uppercase,
'DIGITS': string.digits,
'ASCII_LETTERS': string.ascii_letters,
'SENTENCE_SEPARATORS': ',,,,,,,;;:',
'SENTENCE_TERMINATORS': '....!',
}
上传
写完代码以后,为了让大家用的方便,本蒟蒻将它上传到 pip。
使用 setup.py
打包:
from setuptools import setup, find_packages
from KittenGen import version
with open('README.md', 'r', encoding='utf-8') as f:
long_description = f.read()
setup(
name='KittenGen',
packages=find_packages(exclude=(), include=('*',)),
author='stripe-python',
author_email='[email protected]',
maintainer='stripe-python',
maintainer_email='[email protected]',
license='MIT License',
install_requires=['flask~=3.0.3'],
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
include_package_data=True,
long_description=long_description,
long_description_content_type='text/markdown',
version=version,
)
在 cmd 中运行:
python setup.py sdist bdist_wheel
twine upload dist/*
注:pypi 搞了个恶心的 token,如果你也想上传包,请搜索教程。
即上传至 pip。
便捷使用
你只需要安装好 python,并下载这个包:
pip install KittenGen
然后输入
python -m KittenGen
就可以到 http://127.0.0.1:2025/ 体验。
除此之外,本蒟蒻在 Github 也传了一份,你也可以打开 https://github.com/stripepython/KittenGen 访问。