From e207cb4194c3569b6d81b24fe63a924fc468f917 Mon Sep 17 00:00:00 2001 From: undefined Date: Tue, 9 Apr 2024 08:12:10 +0800 Subject: [PATCH] core: import: support solution and std --- packages/hydrooj/src/model/problem.ts | 55 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index 730f3e2a7..8cab448c3 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -22,6 +22,8 @@ import { buildProjection } from '../utils'; import { PERM, STATUS } from './builtin'; import * as document from './document'; import DomainModel from './domain'; +import RecordModel from './record'; +import SolutionModel from './solution'; import storage from './storage'; import user from './user'; @@ -33,11 +35,11 @@ function sortable(source: string) { return source.replace(/(\d+)/g, (str) => (str.length >= 6 ? str : ('0'.repeat(6 - str.length) + str))); } -function findOverrideContent(dir: string) { +function findOverrideContent(dir: string, base: string) { let files = fs.readdirSync(dir); - if (files.includes('problem.md')) return fs.readFileSync(path.join(dir, 'problem.md'), 'utf8'); + if (files.includes(`${base}.md`)) return fs.readFileSync(path.join(dir, `${base}.md`), 'utf8'); const languages = {}; - files = files.filter((i) => /^problem_[a-zA-Z_]+\.md$/.test(i)); + files = files.filter((i) => new RegExp(`^${base}_[a-zA-Z_]+\\.md$`).test(i)); if (!files.length) return null; for (const file of files) { const lang = file.slice(8, -3); @@ -470,8 +472,8 @@ export class ProblemModel { if (process.env.HYDRO_CLI) logger.info(`Importing problem ${p.name}`); const i = p.name; if (!p.isDirectory()) continue; - const files = await fs.readdir(path.join(tmpdir, i)); - if (!files.includes('problem.yaml')) continue; + const files = await fs.readdir(path.join(tmpdir, i), { withFileTypes: true }); + if (!files.find((f) => f.name === 'problem.yaml')) continue; const content = fs.readFileSync(path.join(tmpdir, i, 'problem.yaml'), 'utf-8'); const pdoc: ProblemDoc = yaml.load(content) as any; if (!pdoc) continue; @@ -482,6 +484,11 @@ export class ProblemModel { if (await ProblemModel.get(domainId, id)) return false; return true; }; + const getFiles = async (type: string) => { + if (!files.find((f) => f.name === type && f.isDirectory())) return []; + const rs = await fs.readdir(path.join(tmpdir, i, type), { withFileTypes: true }); + return rs.map((r) => [r, path.join(tmpdir, i, type, r.name)] as [fs.Dirent, string]); + }; if (pid) { if (preferredPrefix) { @@ -490,31 +497,33 @@ export class ProblemModel { } if (!await isValidPid(pid)) pid = undefined; } - const overrideContent = findOverrideContent(path.join(tmpdir, i)); + const overrideContent = findOverrideContent(path.join(tmpdir, i), 'problem'); if (pdoc.difficulty && !Number.isSafeInteger(pdoc.difficulty)) delete pdoc.difficulty; if (typeof pdoc.title !== 'string') throw new ValidationError('title', null, 'Invalid title'); const docId = await ProblemModel.add( domainId, pid, pdoc.title.trim(), overrideContent || pdoc.content || 'No content', operator || pdoc.owner, pdoc.tag || [], { hidden: pdoc.hidden, difficulty: pdoc.difficulty }, ); - if (files.includes('testdata')) { - const datas = await fs.readdir(path.join(tmpdir, i, 'testdata'), { withFileTypes: true }); - for (const f of datas) { - if (f.isDirectory()) { - const sub = await fs.readdir(path.join(tmpdir, i, 'testdata', f.name)); - for (const s of sub) await ProblemModel.addTestdata(domainId, docId, s, path.join(tmpdir, i, 'testdata', f.name, s)); - } else if (f.isFile()) { - await ProblemModel.addTestdata(domainId, docId, f.name, path.join(tmpdir, i, 'testdata', f.name)); - } - } + for (const [f, loc] of await getFiles('testdata')) { + if (f.isDirectory()) { + const sub = await fs.readdir(loc); + for (const s of sub) await ProblemModel.addTestdata(domainId, docId, s, path.join(tmpdir, i, 'testdata', f.name, s)); + } else if (f.isFile()) await ProblemModel.addTestdata(domainId, docId, f.name, loc); } - if (files.includes('additional_file')) { - const datas = await fs.readdir(path.join(tmpdir, i, 'additional_file'), { withFileTypes: true }); - for (const f of datas) { - if (f.isFile()) { - await ProblemModel.addAdditionalFile(domainId, docId, f.name, path.join(tmpdir, i, 'additional_file', f.name)); - } - } + for (const [f, loc] of await getFiles('additional_file')) { + if (!f.isFile()) continue; + await ProblemModel.addAdditionalFile(domainId, docId, f.name, loc); + } + for (const [f, loc] of await getFiles('solution')) { + if (!f.isFile()) continue; + await SolutionModel.add(domainId, docId, operator, await fs.readFile(loc, 'utf-8')); + } + let count = 0; + for (const [f, loc] of await getFiles('std')) { + if (!f.isFile()) continue; + count++; + if (count > 5) continue; + await RecordModel.add(domainId, docId, operator, f.name.split('.')[1], await fs.readFile(loc, 'utf-8'), true); } if (process.env.HYDRO_CLI) logger.info(`Imported problem ${pdoc.pid} (${pdoc.title})`); }