mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core: syzoj-ng批量导入
This commit is contained in:
1 parent
1bf6fcd423
commit
b7330b05d1
2 files changed
+210
-174
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.15.4",
|
||||
"version": "2.15.5",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "dist/loader.js",
|
||||
"typings": "dist/loader.d.ts",
|
||||
|
||||
@@ -33,184 +33,220 @@ class ProblemImportSYZOJHandler extends Handler {
|
||||
};
|
||||
}
|
||||
|
||||
async v2(domainId: string, target: string, hidden = false, url: string) {
|
||||
const res = await superagent.get(`${url}export`);
|
||||
assert(res.status === 200, new RemoteOnlineJudgeError('Cannot connect to target server'));
|
||||
assert(res.body.success, new RemoteOnlineJudgeError((res.body.error || {}).message));
|
||||
const p = res.body.obj;
|
||||
const content: ContentNode[] = [];
|
||||
if (p.description) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Description'),
|
||||
text: p.description,
|
||||
});
|
||||
}
|
||||
if (p.input_format) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Input Format'),
|
||||
text: p.input_format,
|
||||
});
|
||||
}
|
||||
if (p.output_format) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Output Format'),
|
||||
text: p.output_format,
|
||||
});
|
||||
}
|
||||
if (p.example) {
|
||||
content.push({
|
||||
type: 'Plain',
|
||||
subType: 'markdown',
|
||||
text: p.example,
|
||||
});
|
||||
}
|
||||
if (p.hint) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Hint'),
|
||||
text: p.hint,
|
||||
});
|
||||
}
|
||||
if (p.limit_and_hint) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Limit And Hint'),
|
||||
text: p.output_format,
|
||||
});
|
||||
}
|
||||
if (p.have_additional_file) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Additional File'),
|
||||
text: `${url}download/additional_file`,
|
||||
});
|
||||
}
|
||||
const docId = await problem.add(
|
||||
domainId, target, p.title, content, this.user._id,
|
||||
p.tags || [], [], hidden,
|
||||
);
|
||||
const r = download(`${url}testdata/download`);
|
||||
const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${docId}.zip`);
|
||||
const w = fs.createWriteStream(file);
|
||||
await new Promise((resolve, reject) => {
|
||||
w.on('finish', resolve);
|
||||
w.on('error', reject);
|
||||
r.pipe(w);
|
||||
});
|
||||
const zip = new AdmZip(file);
|
||||
const entries = zip.getEntries();
|
||||
for (const entry of entries) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await problem.addTestdata(domainId, docId, entry.entryName, entry.getData());
|
||||
}
|
||||
await problem.edit(domainId, docId, {
|
||||
config: {
|
||||
time: `${p.time_limit}ms`,
|
||||
memory: `${p.memory_limit}m`,
|
||||
filename: p.file_io_input_name.split('.')[0],
|
||||
type: p.type === 'traditional' ? 'default' : p.type,
|
||||
},
|
||||
});
|
||||
return docId;
|
||||
}
|
||||
|
||||
async v3(
|
||||
domainId: string, target: string, hidden: boolean,
|
||||
protocol: string, host: string, pid: string | number,
|
||||
wait = false,
|
||||
) {
|
||||
const result = await superagent.post(`${protocol}://${host === 'loj.ac' ? 'api.loj.ac.cn' : host}/api/problem/getProblem`)
|
||||
.send({
|
||||
displayId: +pid,
|
||||
localizedContentsOfAllLocales: true,
|
||||
tagsOfLocale: this.user.viewLang || this.session.viewLang,
|
||||
samples: true,
|
||||
testData: true,
|
||||
additionalFiles: true,
|
||||
});
|
||||
const content = {};
|
||||
for (const c of result.body.localizedContentsOfAllLocales) {
|
||||
const sections = c.contentSections;
|
||||
for (const section of sections) {
|
||||
section.subType = 'markdown';
|
||||
if (section.type === 'Sample') {
|
||||
section.payload = [
|
||||
result.body.samples[section.sampleId].inputData,
|
||||
result.body.samples[section.sampleId].outputData,
|
||||
];
|
||||
delete section.sampleId;
|
||||
}
|
||||
}
|
||||
content[c.locale] = sections;
|
||||
}
|
||||
const tags = result.body.tagsOfLocale.map((node) => node.name);
|
||||
const title = [
|
||||
...filter(
|
||||
result.body.localizedContentsOfAllLocales,
|
||||
(node) => node.locale === (this.user.viewLang || this.session.viewLang),
|
||||
),
|
||||
...result.body.localizedContentsOfAllLocales,
|
||||
][0].title;
|
||||
const docId = await problem.add(
|
||||
domainId, target, title, content, this.user._id,
|
||||
tags || [], [], hidden,
|
||||
);
|
||||
const syncFiles = async () => {
|
||||
const judge = result.body.judgeInfo;
|
||||
const r = await superagent.post(`${protocol}://${host === 'loj.ac' ? 'api.loj.ac.cn' : host}/api/problem/downloadProblemFiles`)
|
||||
.send({
|
||||
problemId: +pid,
|
||||
type: 'TestData',
|
||||
filenameList: result.body.testData.map((node) => node.filename),
|
||||
});
|
||||
const urls = {};
|
||||
if (r.body.error) return;
|
||||
for (const t of r.body.downloadInfo) urls[t.filename] = t.downloadUrl;
|
||||
for (const f of result.body.testData) {
|
||||
const p = new PassThrough();
|
||||
superagent.get(urls[f.filename]).pipe(p);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await problem.addTestdata(domainId, docId, f.filename, p);
|
||||
}
|
||||
if (judge) {
|
||||
await problem.edit(domainId, docId, {
|
||||
config: {
|
||||
time: `${judge.timeLimit}ms`,
|
||||
memory: `${judge.memoryLimit}m`,
|
||||
// TODO other config
|
||||
},
|
||||
});
|
||||
}
|
||||
const a = await superagent.post(`${protocol}://${host === 'loj.ac' ? 'api.loj.ac.cn' : host}/api/problem/downloadProblemFiles`)
|
||||
.send({
|
||||
problemId: +pid,
|
||||
type: 'AdditionalFile',
|
||||
filenameList: result.body.additionalFiles.map((node) => node.filename),
|
||||
});
|
||||
const aurls = {};
|
||||
if (a.body.error) return;
|
||||
for (const t of a.body.downloadInfo) aurls[t.filename] = t.downloadUrl;
|
||||
for (const f of result.body.additionalFiles) {
|
||||
const p = new PassThrough();
|
||||
superagent.get(aurls[f.filename]).pipe(p);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await storage.put(`problem/${domainId}/${docId}/additional_file/${f.filename}`, p);
|
||||
}
|
||||
};
|
||||
if (wait) await syncFiles();
|
||||
else syncFiles().catch(logger.error);
|
||||
return docId;
|
||||
}
|
||||
|
||||
@post('url', Types.String, true)
|
||||
@param('pid', Types.String, true, isPid)
|
||||
@param('hidden', Types.Boolean)
|
||||
async post(domainId: string, url: string, targetPid: string, hidden = false) {
|
||||
assert(url.match(RE_SYZOJ), new ValidationError('url'));
|
||||
if (!url.endsWith('/')) url += '/';
|
||||
const [, protocol, host, n, pid] = RE_SYZOJ.exec(url);
|
||||
if (n === 'p') {
|
||||
// SYZOJ-NG
|
||||
const result = await superagent.post(`${protocol}://${host === 'loj.ac' ? 'api.loj.ac.cn' : host}/api/problem/getProblem`)
|
||||
.send({
|
||||
displayId: +pid,
|
||||
localizedContentsOfAllLocales: true,
|
||||
tagsOfLocale: this.user.viewLang || this.session.viewLang,
|
||||
samples: true,
|
||||
testData: true,
|
||||
additionalFiles: true,
|
||||
});
|
||||
const content = {};
|
||||
for (const c of result.body.localizedContentsOfAllLocales) {
|
||||
const sections = c.contentSections;
|
||||
for (const section of sections) {
|
||||
section.subType = 'markdown';
|
||||
if (section.type === 'Sample') {
|
||||
section.payload = [
|
||||
result.body.samples[section.sampleId].inputData,
|
||||
result.body.samples[section.sampleId].outputData,
|
||||
];
|
||||
delete section.sampleId;
|
||||
}
|
||||
}
|
||||
content[c.locale] = sections;
|
||||
}
|
||||
const tags = result.body.tagsOfLocale.map((node) => node.name);
|
||||
const title = [
|
||||
...filter(
|
||||
result.body.localizedContentsOfAllLocales,
|
||||
(node) => node.locale === (this.user.viewLang || this.session.viewLang),
|
||||
),
|
||||
...result.body.localizedContentsOfAllLocales,
|
||||
][0].title;
|
||||
const docId = await problem.add(
|
||||
domainId, targetPid, title, content, this.user._id,
|
||||
tags || [], [], hidden,
|
||||
);
|
||||
(async () => {
|
||||
const judge = result.body.judgeInfo;
|
||||
const r = await superagent.post(`${protocol}://${host === 'loj.ac' ? 'api.loj.ac.cn' : host}/api/problem/downloadProblemFiles`)
|
||||
.send({
|
||||
problemId: +pid,
|
||||
type: 'TestData',
|
||||
filenameList: result.body.testData.map((node) => node.filename),
|
||||
});
|
||||
const urls = {};
|
||||
if (r.body.error) return;
|
||||
for (const t of r.body.downloadInfo) urls[t.filename] = t.downloadUrl;
|
||||
for (const f of result.body.testData) {
|
||||
const p = new PassThrough();
|
||||
superagent.get(urls[f.filename]).pipe(p);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await problem.addTestdata(domainId, docId, f.filename, p);
|
||||
}
|
||||
if (judge) {
|
||||
await problem.edit(domainId, docId, {
|
||||
config: {
|
||||
time: `${judge.timeLimit}ms`,
|
||||
memory: `${judge.memoryLimit}m`,
|
||||
// TODO other config
|
||||
},
|
||||
});
|
||||
}
|
||||
const a = await superagent.post(`${protocol}://${host === 'loj.ac' ? 'api.loj.ac.cn' : host}/api/problem/downloadProblemFiles`)
|
||||
.send({
|
||||
problemId: +pid,
|
||||
type: 'AdditionalFile',
|
||||
filenameList: result.body.additionalFiles.map((node) => node.filename),
|
||||
});
|
||||
const aurls = {};
|
||||
if (a.body.error) return;
|
||||
for (const t of a.body.downloadInfo) aurls[t.filename] = t.downloadUrl;
|
||||
for (const f of result.body.additionalFiles) {
|
||||
const p = new PassThrough();
|
||||
superagent.get(aurls[f.filename]).pipe(p);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await storage.put(`problem/${domainId}/${docId}/additional_file/${f.filename}`, p);
|
||||
}
|
||||
})().catch(logger.error);
|
||||
this.response.body = { pid: docId };
|
||||
this.response.redirect = this.url('problem_settings', { pid: docId });
|
||||
} else {
|
||||
const res = await superagent.get(`${url}export`);
|
||||
assert(res.status === 200, new RemoteOnlineJudgeError('Cannot connect to target server'));
|
||||
assert(res.body.success, new RemoteOnlineJudgeError((res.body.error || {}).message));
|
||||
const p = res.body.obj;
|
||||
const content: ContentNode[] = [];
|
||||
if (p.description) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Description'),
|
||||
text: p.description,
|
||||
});
|
||||
}
|
||||
if (p.input_format) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Input Format'),
|
||||
text: p.input_format,
|
||||
});
|
||||
}
|
||||
if (p.output_format) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Output Format'),
|
||||
text: p.output_format,
|
||||
});
|
||||
}
|
||||
if (p.example) {
|
||||
content.push({
|
||||
type: 'Plain',
|
||||
subType: 'markdown',
|
||||
text: p.example,
|
||||
});
|
||||
}
|
||||
if (p.hint) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Hint'),
|
||||
text: p.hint,
|
||||
});
|
||||
}
|
||||
if (p.limit_and_hint) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Limit And Hint'),
|
||||
text: p.output_format,
|
||||
});
|
||||
}
|
||||
if (p.have_additional_file) {
|
||||
content.push({
|
||||
type: 'Text',
|
||||
subType: 'markdown',
|
||||
sectionTitle: this.translate('Additional File'),
|
||||
text: `${url}download/additional_file`,
|
||||
});
|
||||
}
|
||||
const docId = await problem.add(
|
||||
domainId, targetPid, p.title, content, this.user._id,
|
||||
p.tags || [], [], hidden,
|
||||
);
|
||||
const r = download(`${url}testdata/download`);
|
||||
const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${docId}.zip`);
|
||||
const w = fs.createWriteStream(file);
|
||||
await new Promise((resolve, reject) => {
|
||||
w.on('finish', resolve);
|
||||
w.on('error', reject);
|
||||
r.pipe(w);
|
||||
});
|
||||
const zip = new AdmZip(file);
|
||||
const entries = zip.getEntries();
|
||||
for (const entry of entries) {
|
||||
@post('prefix', Types.String, true)
|
||||
@post('start', Types.UnsignedInt, true)
|
||||
@post('end', Types.UnsignedInt, true)
|
||||
async post(
|
||||
domainId: string, url: string, targetPid: string, hidden = false,
|
||||
prefix: string, start: number, end: number,
|
||||
) {
|
||||
if (prefix) {
|
||||
if (!prefix.endsWith('/')) prefix += '/';
|
||||
const base = `${prefix}${start}/`;
|
||||
assert(base.match(RE_SYZOJ), new ValidationError('prefix'));
|
||||
const [, protocol, host] = RE_SYZOJ.exec(base);
|
||||
for (let i = start; i <= end; i++) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await problem.addTestdata(domainId, docId, entry.entryName, entry.getData());
|
||||
await this.v3(domainId, undefined, hidden, protocol, host, i, true);
|
||||
}
|
||||
} else {
|
||||
assert(url.match(RE_SYZOJ), new ValidationError('url'));
|
||||
if (!url.endsWith('/')) url += '/';
|
||||
const [, protocol, host, n, pid] = RE_SYZOJ.exec(url);
|
||||
if (n === 'p') {
|
||||
// SYZOJ-NG
|
||||
const docId = await this.v3(
|
||||
domainId, targetPid, hidden,
|
||||
protocol, host, pid, false,
|
||||
);
|
||||
this.response.body = { pid: pid || docId };
|
||||
this.response.redirect = this.url('problem_settings', { pid: pid || docId });
|
||||
} else {
|
||||
const docId = await this.v2(domainId, targetPid, hidden, url);
|
||||
this.response.body = { pid: pid || docId };
|
||||
this.response.redirect = this.url('problem_settings', { pid: pid || docId });
|
||||
}
|
||||
await problem.edit(domainId, docId, {
|
||||
config: {
|
||||
time: `${p.time_limit}ms`,
|
||||
memory: `${p.memory_limit}m`,
|
||||
filename: p.file_io_input_name.split('.')[0],
|
||||
type: p.type === 'traditional' ? 'default' : p.type,
|
||||
},
|
||||
});
|
||||
this.response.body = { pid: pid || docId };
|
||||
this.response.redirect = this.url('problem_settings', { pid: pid || docId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user