core: use problem import options

This commit is contained in:
undefined committed 2024-08-12 03:36:41 +08:00
1 parent 6c19a43bfa
commit d33a3a1fb0
2 files changed
+38 -13

No files matched your search

+2 -2
View File
@@ -18,8 +18,8 @@ class ProblemImportHydroHandler extends Handler {
if (!this.request.files.file) throw new ValidationError('file'); if (!this.request.files.file) throw new ValidationError('file');
if (preferredPrefix && !/^[a-zA-Z]+$/.test(preferredPrefix)) throw new ValidationError('preferredPrefix'); if (preferredPrefix && !/^[a-zA-Z]+$/.test(preferredPrefix)) throw new ValidationError('preferredPrefix');
const promise = problem.import( const promise = problem.import(
domainId, this.request.files.file.filepath, keepUser ? null : this.user._id, domainId, this.request.files.file.filepath,
preferredPrefix, this.progress.bind(this), { preferredPrefix, progress: this.progress.bind(this), operator: keepUser ? null : this.user._id },
).catch((e) => MessageModel.send(1, this.user._id, `Import failed: ${e.message}\n${e.stack}`)); ).catch((e) => MessageModel.send(1, this.user._id, `Import failed: ${e.message}\n${e.stack}`));
let resolved = false; let resolved = false;
await Promise.race([ await Promise.race([
+36 -11
View File
@@ -54,6 +54,13 @@ function findOverrideContent(dir: string, base: string) {
return JSON.stringify(languages); return JSON.stringify(languages);
} }
interface ProblemImportOptions {
preferredPrefix?: string;
progress?: any;
override?: boolean;
operator?: number;
}
export class ProblemModel { export class ProblemModel {
static PROJECTION_CONTEST_LIST: Field[] = [ static PROJECTION_CONTEST_LIST: Field[] = [
'_id', 'domainId', 'docType', 'docId', 'pid', '_id', 'domainId', 'docType', 'docId', 'pid',
@@ -439,9 +446,16 @@ export class ProblemModel {
return true; return true;
} }
static async import(domainId: string, filepath: string, operator = 1, preferredPrefix?: string, progress = null) { static async import(domainId: string, filepath: string, options: ProblemImportOptions = {}) {
let tmpdir = ''; let tmpdir = '';
let del = false; let del = false;
if (typeof options !== 'object') {
logger.warn('ProblemModel.import: options should be an object');
options = {};
}
const {
preferredPrefix, progress, override = false, operator = 1,
} = options;
try { try {
if (filepath.endsWith('.zip')) { if (filepath.endsWith('.zip')) {
tmpdir = path.join(os.tmpdir(), 'hydro', `${Math.random()}.import`); tmpdir = path.join(os.tmpdir(), 'hydro', `${Math.random()}.import`);
@@ -474,10 +488,16 @@ export class ProblemModel {
const pdoc: ProblemDoc = yaml.load(content) as any; const pdoc: ProblemDoc = yaml.load(content) as any;
if (!pdoc) continue; if (!pdoc) continue;
let pid = pdoc.pid; let pid = pdoc.pid;
let overridePid = null;
const isValidPid = async (id: string) => { const isValidPid = async (id: string) => {
if (!(/^[A-Za-z]+[0-9A-Za-z]*$/.test(id))) return false; if (!(/^[A-Za-z]+[0-9A-Za-z]*$/.test(id))) return false;
if (await ProblemModel.get(domainId, id)) return false; const doc = await ProblemModel.get(domainId, id);
if (doc) {
if (!override) return false;
overridePid = doc.docId;
return true;
}
return true; return true;
}; };
const getFiles = async (type: string) => { const getFiles = async (type: string) => {
@@ -496,10 +516,18 @@ export class ProblemModel {
const overrideContent = findOverrideContent(path.join(tmpdir, i), 'problem'); const overrideContent = findOverrideContent(path.join(tmpdir, i), 'problem');
if (pdoc.difficulty && !Number.isSafeInteger(pdoc.difficulty)) delete pdoc.difficulty; if (pdoc.difficulty && !Number.isSafeInteger(pdoc.difficulty)) delete pdoc.difficulty;
if (typeof pdoc.title !== 'string') throw new ValidationError('title', null, 'Invalid title'); if (typeof pdoc.title !== 'string') throw new ValidationError('title', null, 'Invalid title');
const docId = await ProblemModel.add( const docId = overridePid
domainId, pid, pdoc.title.trim(), overrideContent || pdoc.content || 'No content', ? (await ProblemModel.edit(domainId, overridePid, {
operator || pdoc.owner, pdoc.tag || [], { hidden: pdoc.hidden, difficulty: pdoc.difficulty }, title: pdoc.title.trim(),
); content: overrideContent || pdoc.content || 'No content',
tag: pdoc.tag || [],
difficulty: pdoc.difficulty,
})).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 },
);
// TODO delete unused file when updating pdoc
for (const [f, loc] of await getFiles('testdata')) { for (const [f, loc] of await getFiles('testdata')) {
if (f.isDirectory()) { if (f.isDirectory()) {
const sub = await fs.readdir(loc); const sub = await fs.readdir(loc);
@@ -521,11 +549,8 @@ export class ProblemModel {
if (count > 5) continue; if (count > 5) continue;
await RecordModel.add(domainId, docId, operator, f.name.split('.')[1], await fs.readFile(loc, 'utf-8'), true); await RecordModel.add(domainId, docId, operator, f.name.split('.')[1], await fs.readFile(loc, 'utf-8'), true);
} }
if (process.env.HYDRO_CLI) { const message = `${overridePid ? 'Updated' : 'Imported'} problem ${pdoc.pid} (${pdoc.title})`;
logger.info(`Imported problem ${pdoc.pid} (${pdoc.title})`); (process.env.HYDRO_CLI ? logger.info : progress)?.(message);
} else {
progress?.(`Imported problem ${pdoc.pid} (${pdoc.title})`);
}
} }
} finally { } finally {
if (del) await fs.remove(tmpdir); if (del) await fs.remove(tmpdir);