mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
core: 整合tag与category
This commit is contained in:
1 parent
454afbcb08
commit
7a41fecb0e
15 files changed
+116
-117
No files matched your search
@@ -35,7 +35,7 @@ class FpsProblemImportHandler extends Handler {
|
|||||||
};
|
};
|
||||||
const title = decodeHTML(p.title.join(' '));
|
const title = decodeHTML(p.title.join(' '));
|
||||||
const tags = filter(p.source, (i: string) => i.trim());
|
const tags = filter(p.source, (i: string) => i.trim());
|
||||||
const pid = await problem.add(domainId, null, title, content, this.user._id, tags, []);
|
const pid = await problem.add(domainId, null, title, content, this.user._id, tags);
|
||||||
await problem.addTestdata(domainId, pid, 'config.yaml', Buffer.from(`time: ${config.time}\nmemory: ${config.memory}`));
|
await problem.addTestdata(domainId, pid, 'config.yaml', Buffer.from(`time: ${config.time}\nmemory: ${config.memory}`));
|
||||||
if (p.test_output) {
|
if (p.test_output) {
|
||||||
for (let i = 0; i < p.test_input.length; i++) {
|
for (let i = 0; i < p.test_input.length; i++) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/fps-importer",
|
"name": "@hydrooj/fps-importer",
|
||||||
"version": "1.1.7",
|
"version": "1.1.8",
|
||||||
"description": "Import FPS problems",
|
"description": "Import FPS problems",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"bson": "^4.2.2"
|
"bson": "^4.2.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"hydrooj": "^2.15.0"
|
"hydrooj": "^2.20.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.19.6",
|
"version": "2.20.0",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "dist/loader.js",
|
"main": "dist/loader.js",
|
||||||
"typings": "dist/loader.d.ts",
|
"typings": "dist/loader.d.ts",
|
||||||
|
|||||||
@@ -96,8 +96,7 @@ class ProblemImportSYZOJHandler extends Handler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const docId = await problem.add(
|
const docId = await problem.add(
|
||||||
domainId, target, p.title, content, this.user._id,
|
domainId, target, p.title, content, this.user._id, p.tags || [], hidden,
|
||||||
p.tags || [], [], hidden,
|
|
||||||
);
|
);
|
||||||
const r = download(`${url}testdata/download`);
|
const r = download(`${url}testdata/download`);
|
||||||
const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${docId}.zip`);
|
const file = path.resolve(os.tmpdir(), 'hydro', `import_${domainId}_${docId}.zip`);
|
||||||
@@ -166,8 +165,7 @@ class ProblemImportSYZOJHandler extends Handler {
|
|||||||
...result.body.localizedContentsOfAllLocales,
|
...result.body.localizedContentsOfAllLocales,
|
||||||
][0].title;
|
][0].title;
|
||||||
const docId = await problem.add(
|
const docId = await problem.add(
|
||||||
domainId, target, title, content, this.user._id,
|
domainId, target, title, content, this.user._id, tags || [], hidden,
|
||||||
tags || [], [], hidden,
|
|
||||||
);
|
);
|
||||||
const syncFiles = async () => {
|
const syncFiles = async () => {
|
||||||
const judge = result.body.judgeInfo;
|
const judge = result.body.judgeInfo;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { isSafeInteger, flatten } from 'lodash';
|
import { isSafeInteger, flatten } from 'lodash';
|
||||||
import yaml from 'js-yaml';
|
|
||||||
import { FilterQuery, ObjectID } from 'mongodb';
|
import { FilterQuery, ObjectID } from 'mongodb';
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import {
|
import {
|
||||||
NoProblemError, PermissionError, ValidationError,
|
NoProblemError, PermissionError, ValidationError,
|
||||||
SolutionNotFoundError, ProblemNotFoundError,
|
SolutionNotFoundError, ProblemNotFoundError, BadRequestError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import {
|
import {
|
||||||
Pdoc, User, Rdoc, PathComponent,
|
Pdoc, User, Rdoc, PathComponent,
|
||||||
@@ -102,14 +101,7 @@ export class ProblemCategoryHandler extends ProblemHandler {
|
|||||||
async get(domainId: string, page = 1, category: string[]) {
|
async get(domainId: string, page = 1, category: string[]) {
|
||||||
this.response.template = 'problem_main.html';
|
this.response.template = 'problem_main.html';
|
||||||
const q: any = { $and: [] };
|
const q: any = { $and: [] };
|
||||||
for (const name of category) {
|
for (const tag of category) q.$and.push({ tag });
|
||||||
q.$and.push({
|
|
||||||
$or: [
|
|
||||||
{ category: { $elemMatch: { $eq: name } } },
|
|
||||||
{ tag: { $elemMatch: { $eq: name } } },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let psdict = {};
|
let psdict = {};
|
||||||
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||||
await bus.serial('problem/list', q, this);
|
await bus.serial('problem/list', q, this);
|
||||||
@@ -138,17 +130,8 @@ export class ProblemCategoryHandler extends ProblemHandler {
|
|||||||
export class ProblemRandomHandler extends ProblemHandler {
|
export class ProblemRandomHandler extends ProblemHandler {
|
||||||
@param('category', Types.String, true, null, parseCategory)
|
@param('category', Types.String, true, null, parseCategory)
|
||||||
async get(domainId: string, category: string[] = []) {
|
async get(domainId: string, category: string[] = []) {
|
||||||
const q: any = category.length ? { $and: [] } : {};
|
const q: FilterQuery<Pdoc> = category.length ? { $and: [] } : {};
|
||||||
for (const name of category) {
|
for (const tag of category) q.$and.push({ tag });
|
||||||
if (name) {
|
|
||||||
q.$and.push({
|
|
||||||
$or: [
|
|
||||||
{ category: { $elemMatch: { $eq: name } } },
|
|
||||||
{ tag: { $elemMatch: { $eq: name } } },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
if (!this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||||
await bus.serial('problem/list', q, this);
|
await bus.serial('problem/list', q, this);
|
||||||
const pid = await problem.random(domainId, q);
|
const pid = await problem.random(domainId, q);
|
||||||
@@ -321,45 +304,6 @@ export class ProblemManageHandler extends ProblemDetailHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProblemSettingsHandler extends ProblemManageHandler {
|
|
||||||
@param('pid', Types.String)
|
|
||||||
async get(domainId: string, pid: string) {
|
|
||||||
this.response.template = 'problem_settings.html';
|
|
||||||
this.response.body.path = [
|
|
||||||
['Hydro', 'homepage'],
|
|
||||||
['problem_main', 'problem_main'],
|
|
||||||
[this.pdoc.title, 'problem_detail', { pid }, true],
|
|
||||||
['problem_settings', null],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('pid', Types.String, null, parsePid)
|
|
||||||
@param('yaml', Types.String)
|
|
||||||
async postConfig(domainId: string, pid: string | number, cfg: string) {
|
|
||||||
const pdoc = await problem.get(domainId, pid);
|
|
||||||
// TODO validate
|
|
||||||
const config = yaml.load(cfg) as any;
|
|
||||||
await problem.edit(domainId, pdoc.docId, { config });
|
|
||||||
this.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('pid', Types.String, null, parsePid)
|
|
||||||
@param('hidden', Types.Boolean)
|
|
||||||
@param('category', Types.String, true, null, parseCategory)
|
|
||||||
@param('tag', Types.String, true, null, parseCategory)
|
|
||||||
async postSetting(
|
|
||||||
domainId: string, pid: string | number, hidden = false,
|
|
||||||
category: string[] = [], tag: string[] = [],
|
|
||||||
) {
|
|
||||||
const pdoc = await problem.get(domainId, pid);
|
|
||||||
const update: Partial<Pdoc> = { hidden, category, tag };
|
|
||||||
await bus.serial('problem/setting', update, this);
|
|
||||||
await problem.edit(domainId, pdoc.docId, update);
|
|
||||||
await global.Hydro.script.difficulty.run({ domainId, pid }, console.log);
|
|
||||||
this.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ProblemEditHandler extends ProblemManageHandler {
|
export class ProblemEditHandler extends ProblemManageHandler {
|
||||||
async get({ pid }) {
|
async get({ pid }) {
|
||||||
this.response.template = 'problem_edit.html';
|
this.response.template = 'problem_edit.html';
|
||||||
@@ -371,17 +315,29 @@ export class ProblemEditHandler extends ProblemManageHandler {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@param('title', Types.String, isTitle)
|
@route('pid', Types.String, null, parsePid)
|
||||||
@param('content', Types.String, isContent)
|
@post('title', Types.String, isTitle)
|
||||||
|
@post('content', Types.String, isContent)
|
||||||
@post('pid', Types.String, isPid, true)
|
@post('pid', Types.String, isPid, true)
|
||||||
async post(domainId: string, title: string, content: string, newPid: string = '') {
|
@post('hidden', Types.Boolean)
|
||||||
|
@post('tag', Types.String, true, null, parseCategory)
|
||||||
|
async post(
|
||||||
|
domainId: string, pid: string | number, title: string, content: string,
|
||||||
|
newPid: string = '', hidden = false, tag: string[],
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
content = JSON.parse(content);
|
content = JSON.parse(content);
|
||||||
} catch { /* Ignore */ }
|
} catch { /* Ignore */ }
|
||||||
const $update: Partial<Pdoc> = { title, content, pid: newPid };
|
if (await problem.get(domainId, newPid)) throw new BadRequestError('new pid exists');
|
||||||
let pdoc = await problem.get(domainId, this.request.params.pid);
|
const $update: Partial<Pdoc> = {
|
||||||
|
title, content, pid: newPid, hidden, tag,
|
||||||
|
};
|
||||||
|
let pdoc = await problem.get(domainId, pid);
|
||||||
|
await bus.serial('problem/before-edit', $update);
|
||||||
pdoc = await problem.edit(domainId, pdoc.docId, $update);
|
pdoc = await problem.edit(domainId, pdoc.docId, $update);
|
||||||
this.response.redirect = this.url('problem_detail', { pid: pdoc.pid || pdoc.docId });
|
await bus.emit('problem/edit', pdoc);
|
||||||
|
await global.Hydro.script.difficulty.run({ domainId, pid }, console.log);
|
||||||
|
this.response.redirect = this.url('problem_detail', { pid: newPid || pdoc.docId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -594,20 +550,19 @@ export class ProblemCreateHandler extends Handler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@param('title', Types.String, isTitle)
|
@post('title', Types.String, isTitle)
|
||||||
@param('pid', Types.String, isPid, true)
|
@post('content', Types.String, isContent)
|
||||||
@param('content', Types.String, isContent)
|
@post('pid', Types.String, isPid, true)
|
||||||
@param('hidden', Types.Boolean)
|
@post('hidden', Types.Boolean)
|
||||||
async post(domainId: string, title: string, pid: string, content: string, hidden = false) {
|
@post('tag', Types.String, true, null, parseCategory)
|
||||||
|
async post(domainId: string, title: string, content: string, pid: string, hidden = false, tag: string[] = []) {
|
||||||
try {
|
try {
|
||||||
content = JSON.parse(content);
|
content = JSON.parse(content);
|
||||||
} catch { /* Ignore */ }
|
} catch { /* Ignore */ }
|
||||||
const docId = await problem.add(
|
if (await problem.get(domainId, pid)) throw new BadRequestError('invalid pid');
|
||||||
domainId, pid, title, content,
|
const docId = await problem.add(domainId, pid, title, content, this.user._id, tag, hidden);
|
||||||
this.user._id, [], [], hidden,
|
this.response.body = { pid: pid || docId };
|
||||||
);
|
this.response.redirect = this.url('problem_settings', { pid: pid || docId });
|
||||||
this.response.body = { pid: docId };
|
|
||||||
this.response.redirect = this.url('problem_settings', { pid: docId });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,7 +573,6 @@ export async function apply() {
|
|||||||
Route('problem_detail', '/p/:pid', ProblemDetailHandler, PERM.PERM_VIEW_PROBLEM);
|
Route('problem_detail', '/p/:pid', ProblemDetailHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
Route('problem_submit', '/p/:pid/submit', ProblemSubmitHandler, PERM.PERM_SUBMIT_PROBLEM);
|
Route('problem_submit', '/p/:pid/submit', ProblemSubmitHandler, PERM.PERM_SUBMIT_PROBLEM);
|
||||||
Route('problem_pretest', '/p/:pid/pretest', ProblemPretestHandler, PERM.PERM_SUBMIT_PROBLEM);
|
Route('problem_pretest', '/p/:pid/pretest', ProblemPretestHandler, PERM.PERM_SUBMIT_PROBLEM);
|
||||||
Route('problem_settings', '/p/:pid/settings', ProblemSettingsHandler);
|
|
||||||
Route('problem_statistics', '/p/:pid/statistics', ProblemStatisticsHandler, PERM.PERM_VIEW_PROBLEM);
|
Route('problem_statistics', '/p/:pid/statistics', ProblemStatisticsHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
Route('problem_edit', '/p/:pid/edit', ProblemEditHandler);
|
Route('problem_edit', '/p/:pid/edit', ProblemEditHandler);
|
||||||
Route('problem_files', '/p/:pid/files', ProblemFilesHandler, PERM.PERM_VIEW_PROBLEM);
|
Route('problem_files', '/p/:pid/files', ProblemFilesHandler, PERM.PERM_VIEW_PROBLEM);
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export class ProblemReceiveHandler extends Handler {
|
|||||||
const exist = await problem.get(domainId, pdoc.pid);
|
const exist = await problem.get(domainId, pdoc.pid);
|
||||||
if (exist) pdoc.pid = undefined;
|
if (exist) pdoc.pid = undefined;
|
||||||
}
|
}
|
||||||
const pid = await problem.add(domainId, pdoc.pid, pdoc.title, pdoc.content, this.user._id, pdoc.tag, pdoc.category);
|
const pid = await problem.add(domainId, pdoc.pid, pdoc.title, pdoc.content, this.user._id, pdoc.tag);
|
||||||
pids.push(pid);
|
pids.push(pid);
|
||||||
this.syncFiles(domainId, baseUrl, pid, files[i]).catch(logAndReturn(logger));
|
this.syncFiles(domainId, baseUrl, pid, files[i]).catch(logAndReturn(logger));
|
||||||
await problem.edit(domainId, pid, { html: pdoc.html });
|
await problem.edit(domainId, pid, { html: pdoc.html });
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ declare module './model/problem' {
|
|||||||
nSubmit: number,
|
nSubmit: number,
|
||||||
nAccept: number,
|
nAccept: number,
|
||||||
tag: string[],
|
tag: string[],
|
||||||
category: string[],
|
/** @deprecated */
|
||||||
|
category?: string[],
|
||||||
data: FileInfo[],
|
data: FileInfo[],
|
||||||
additional_file: FileInfo[],
|
additional_file: FileInfo[],
|
||||||
hidden: boolean,
|
hidden: boolean,
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { inspect, format } from 'util';
|
import { inspect, format, InspectOptions } from 'util';
|
||||||
import { argv } from 'yargs';
|
import { argv } from 'yargs';
|
||||||
import { Time } from './utils';
|
import { Time } from './utils';
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62,
|
||||||
|
63, 68, 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113,
|
||||||
|
129, 134, 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168,
|
||||||
|
169, 170, 171, 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200,
|
||||||
|
201, 202, 203, 204, 205, 206, 207, 208, 209, 214, 215, 220, 221,
|
||||||
|
];
|
||||||
const instances: Record<string, Logger> = {};
|
const instances: Record<string, Logger> = {};
|
||||||
type LogFunction = (format: any, ...param: any[]) => boolean;
|
type LogFunction = (format: any, ...param: any[]) => void;
|
||||||
type LogType = 'success' | 'error' | 'info' | 'warn' | 'debug';
|
type LogType = 'success' | 'error' | 'info' | 'warn' | 'debug';
|
||||||
export interface Logger extends Record<LogType, LogFunction> { }
|
export interface Logger extends Record<LogType, LogFunction> { }
|
||||||
|
|
||||||
export class Logger {
|
export class Logger {
|
||||||
static readonly SUCCESS = 1;
|
static readonly SUCCESS = 1;
|
||||||
static readonly ERROR = 1;
|
static readonly ERROR = 1;
|
||||||
@@ -16,10 +24,24 @@ export class Logger {
|
|||||||
static showDiff = false;
|
static showDiff = false;
|
||||||
static levels: Record<string, number> = {};
|
static levels: Record<string, number> = {};
|
||||||
static lastTime = 0;
|
static lastTime = 0;
|
||||||
static formatters: Record<string, (this: Logger, value: any) => string> = {
|
private code: number;
|
||||||
o: (value) => inspect(value).replace(/\s*\n\s*/g, ' '),
|
private displayName: string;
|
||||||
|
public stream: NodeJS.WritableStream = process.stderr;
|
||||||
|
static options: InspectOptions = {
|
||||||
|
colors: process.stderr.isTTY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static formatters: Record<string, (this: Logger, value: any) => string> = {
|
||||||
|
c: Logger.prototype.color,
|
||||||
|
C: (value) => Logger.color(15, value, ';1'),
|
||||||
|
o: (value) => inspect(value, Logger.options).replace(/\s*\n\s*/g, ' '),
|
||||||
|
};
|
||||||
|
|
||||||
|
static color(code: number, value: any, decoration = '') {
|
||||||
|
if (!Logger.options.colors) return `${value}`;
|
||||||
|
return `\u001B[3${code < 8 ? code : `8;5;${code}`}${decoration}m${value}\u001B[0m`;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(public name: string, private showDiff = false) {
|
constructor(public name: string, private showDiff = false) {
|
||||||
if (name in instances) return instances[name];
|
if (name in instances) return instances[name];
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
@@ -28,6 +50,8 @@ export class Logger {
|
|||||||
hash |= 0;
|
hash |= 0;
|
||||||
}
|
}
|
||||||
instances[name] = this;
|
instances[name] = this;
|
||||||
|
this.code = colors[Math.abs(hash) % colors.length];
|
||||||
|
this.displayName = name ? this.color(`${name} `, ';1') : '';
|
||||||
this.createMethod('success', '[S] ', Logger.SUCCESS);
|
this.createMethod('success', '[S] ', Logger.SUCCESS);
|
||||||
this.createMethod('error', '[E] ', Logger.ERROR);
|
this.createMethod('error', '[E] ', Logger.ERROR);
|
||||||
this.createMethod('info', '[I] ', Logger.INFO);
|
this.createMethod('info', '[I] ', Logger.INFO);
|
||||||
@@ -35,10 +59,14 @@ export class Logger {
|
|||||||
this.createMethod('debug', '[D] ', Logger.DEBUG);
|
this.createMethod('debug', '[D] ', Logger.DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private color(value: any, decoration = '') {
|
||||||
|
return Logger.color(this.code, value, decoration);
|
||||||
|
}
|
||||||
|
|
||||||
private createMethod(name: LogType, prefix: string, minLevel: number) {
|
private createMethod(name: LogType, prefix: string, minLevel: number) {
|
||||||
this[name] = (...args: [any, ...any[]]) => {
|
this[name] = (...args: [any, ...any[]]) => {
|
||||||
if (this.level < minLevel) return false;
|
if (this.level < minLevel) return false;
|
||||||
const msg = `${prefix} ${this.name} ${this.format(...args)}`;
|
const msg = `${prefix} ${this.displayName} ${this.format(...args)}`;
|
||||||
if (process.send) process.send({ event: 'message/log', payload: [msg] });
|
if (process.send) process.send({ event: 'message/log', payload: [msg] });
|
||||||
else global.Hydro.service.bus.parallel('message/log', msg);
|
else global.Hydro.service.bus.parallel('message/log', msg);
|
||||||
return true;
|
return true;
|
||||||
@@ -68,7 +96,9 @@ export class Logger {
|
|||||||
}).split('\n').join('\n ');
|
}).split('\n').join('\n ');
|
||||||
if (Logger.showDiff || this.showDiff) {
|
if (Logger.showDiff || this.showDiff) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (Logger.lastTime) args.push(`+${Time.formatTimeShort(now - Logger.lastTime)}`);
|
if (Logger.lastTime) {
|
||||||
|
args.push(this.color(`+${Time.formatTimeShort(now - Logger.lastTime)}`));
|
||||||
|
}
|
||||||
Logger.lastTime = now;
|
Logger.lastTime = now;
|
||||||
}
|
}
|
||||||
return format(...args);
|
return format(...args);
|
||||||
|
|||||||
@@ -390,8 +390,6 @@ async function ensureIndexes() {
|
|||||||
await ic({ domainId: 1, docType: 1, owner: 1, docId: -1 });
|
await ic({ domainId: 1, docType: 1, owner: 1, docId: -1 });
|
||||||
// For problem
|
// For problem
|
||||||
await ic({ domainId: 1, docType: 1, search: 'text', title: 'text' }, s);
|
await ic({ domainId: 1, docType: 1, search: 'text', title: 'text' }, s);
|
||||||
await ic({ domainId: 1, docType: 1, category: 1, docId: 1 }, s);
|
|
||||||
await ic({ domainId: 1, docType: 1, hidden: 1, category: 1, docId: 1 }, s);
|
|
||||||
await ic({ domainId: 1, docType: 1, tag: 1, docId: 1 }, s);
|
await ic({ domainId: 1, docType: 1, tag: 1, docId: 1 }, s);
|
||||||
await ic({ domainId: 1, docType: 1, hidden: 1, tag: 1, docId: 1 }, s);
|
await ic({ domainId: 1, docType: 1, hidden: 1, tag: 1, docId: 1 }, s);
|
||||||
// For problem solution
|
// For problem solution
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export namespace Pdoc {
|
|||||||
nSubmit: 0,
|
nSubmit: 0,
|
||||||
nAccept: 0,
|
nAccept: 0,
|
||||||
tag: [],
|
tag: [],
|
||||||
category: [],
|
|
||||||
data: [],
|
data: [],
|
||||||
additional_file: [],
|
additional_file: [],
|
||||||
hidden: true,
|
hidden: true,
|
||||||
@@ -72,7 +71,7 @@ export const SETTING_DIFFICULTY_RANGE = [
|
|||||||
export const PROJECTION_LIST: Pdoc.Field[] = [
|
export const PROJECTION_LIST: Pdoc.Field[] = [
|
||||||
'_id', 'domainId', 'docType', 'docId', 'pid',
|
'_id', 'domainId', 'docType', 'docId', 'pid',
|
||||||
'owner', 'title', 'nSubmit', 'nAccept', 'difficulty',
|
'owner', 'title', 'nSubmit', 'nAccept', 'difficulty',
|
||||||
'tag', 'category', 'hidden',
|
'tag', 'hidden',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PROJECTION_PUBLIC: Pdoc.Field[] = [
|
export const PROJECTION_PUBLIC: Pdoc.Field[] = [
|
||||||
@@ -83,11 +82,11 @@ export const PROJECTION_PUBLIC: Pdoc.Field[] = [
|
|||||||
|
|
||||||
export async function add(
|
export async function add(
|
||||||
domainId: string, pid: string = null, title: string, content: Content, owner: number,
|
domainId: string, pid: string = null, title: string, content: Content, owner: number,
|
||||||
tag: string[] = [], category: string[] = [], hidden = false,
|
tag: string[] = [], hidden = false,
|
||||||
) {
|
) {
|
||||||
const pidCounter = await domain.inc(domainId, 'pidCounter', 1);
|
const pidCounter = await domain.inc(domainId, 'pidCounter', 1);
|
||||||
const args: Partial<Pdoc> = {
|
const args: Partial<Pdoc> = {
|
||||||
title, category, tag, hidden, nSubmit: 0, nAccept: 0,
|
title, tag, hidden, nSubmit: 0, nAccept: 0,
|
||||||
};
|
};
|
||||||
if (pid) args.pid = pid;
|
if (pid) args.pid = pid;
|
||||||
return await document.add(domainId, content, owner, document.TYPE_PROBLEM, pidCounter, null, null, args);
|
return await document.add(domainId, content, owner, document.TYPE_PROBLEM, pidCounter, null, null, args);
|
||||||
|
|||||||
@@ -52,9 +52,12 @@ export interface EventMap {
|
|||||||
'domain/update': (domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn
|
'domain/update': (domainId: string, $set: Partial<DomainDoc>, ddoc: DomainDoc) => VoidReturn
|
||||||
|
|
||||||
'document/add': (doc: any) => VoidReturn
|
'document/add': (doc: any) => VoidReturn
|
||||||
'document/set': <T extends keyof DocType>
|
'document/set': <T extends keyof DocType>(
|
||||||
(domainId: string, docType: T, docId: DocType[T], $set: any, $unset: OnlyFieldsOfType<DocType[T], any, true | '' | 1>) => VoidReturn
|
domainId: string, docType: T, docId: DocType[T],
|
||||||
|
$set: any, $unset: OnlyFieldsOfType<DocType[T], any, true | '' | 1>
|
||||||
|
) => VoidReturn
|
||||||
|
|
||||||
|
'problem/before-edit': (doc: Partial<Pdoc>) => VoidReturn
|
||||||
'problem/edit': (doc: Pdoc) => VoidReturn
|
'problem/edit': (doc: Pdoc) => VoidReturn
|
||||||
'problem/list': (query: FilterQuery<Pdoc>, handler: any) => VoidReturn
|
'problem/list': (query: FilterQuery<Pdoc>, handler: any) => VoidReturn
|
||||||
'problem/setting': (update: Partial<Pdoc>, handler: any) => VoidReturn
|
'problem/setting': (update: Partial<Pdoc>, handler: any) => VoidReturn
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ if (useTerminal) {
|
|||||||
process.stdin.on('data', (data) => {
|
process.stdin.on('data', (data) => {
|
||||||
// Control Seq?
|
// Control Seq?
|
||||||
const seq = data.toString('hex');
|
const seq = data.toString('hex');
|
||||||
logger.info(data.toString('hex'));
|
|
||||||
if (seq === '7f') input = input.substr(0, input.length - 1);
|
if (seq === '7f') input = input.substr(0, input.length - 1);
|
||||||
else if (seq === '1b5b41') {
|
else if (seq === '1b5b41') {
|
||||||
// Arrow up
|
// Arrow up
|
||||||
|
|||||||
@@ -25,15 +25,25 @@ async function iterateAllDomain(cb: (ddoc: DomainDoc, current?: number, total?:
|
|||||||
for (const i in ddocs) await cb(ddocs[i], +i, ddocs.length);
|
for (const i in ddocs) await cb(ddocs[i], +i, ddocs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function iterateAllProblem(fields: problem.Pdoc.Field[], cb: (pdoc: problem.Pdoc, current?: number, total?: number) => Promise<any>) {
|
interface PartialPdoc extends problem.Pdoc {
|
||||||
|
[key: string]: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
async function iterateAllProblem(
|
||||||
|
fields: (problem.Pdoc.Field | string)[],
|
||||||
|
cb: (pdoc: PartialPdoc, current?: number, total?: number) => Promise<any>,
|
||||||
|
) {
|
||||||
|
if (!fields.includes('domainId')) fields.push('domainId');
|
||||||
|
if (!fields.includes('docId')) fields.push('docId');
|
||||||
await iterateAllDomain(async (d) => {
|
await iterateAllDomain(async (d) => {
|
||||||
const cursor = problem.getMulti(d._id, {}, fields);
|
const cursor = problem.getMulti(d._id, {}, fields as any);
|
||||||
const total = await problem.getMulti(d._id, {}).count();
|
const total = await problem.getMulti(d._id, {}).count();
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (await cursor.hasNext()) {
|
while (await cursor.hasNext()) {
|
||||||
const doc = await cursor.next();
|
const doc = await cursor.next();
|
||||||
i++;
|
i++;
|
||||||
await cb(doc, i, total);
|
const res = await cb(doc, i, total);
|
||||||
|
if (res) await problem.edit(doc.domainId, doc.docId, res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -162,7 +172,7 @@ const scripts: UpgradeScript[] = [
|
|||||||
},
|
},
|
||||||
async function _8_9() {
|
async function _8_9() {
|
||||||
const _FRESH_INSTALL_IGNORE = 1;
|
const _FRESH_INSTALL_IGNORE = 1;
|
||||||
await iterateAllProblem(['docId', 'domainId'], async (pdoc) => {
|
await iterateAllProblem([], async (pdoc) => {
|
||||||
logger.info('%s/%s', pdoc.domainId, pdoc.docId);
|
logger.info('%s/%s', pdoc.domainId, pdoc.docId);
|
||||||
const [data, additional_file] = await Promise.all([
|
const [data, additional_file] = await Promise.all([
|
||||||
storage.list(`problem/${pdoc.domainId}/${pdoc.docId}/testdata/`),
|
storage.list(`problem/${pdoc.domainId}/${pdoc.docId}/testdata/`),
|
||||||
@@ -174,7 +184,21 @@ const scripts: UpgradeScript[] = [
|
|||||||
for (let i = 0; i < additional_file.length; i++) {
|
for (let i = 0; i < additional_file.length; i++) {
|
||||||
additional_file[i]._id = additional_file[i].name;
|
additional_file[i]._id = additional_file[i].name;
|
||||||
}
|
}
|
||||||
await problem.edit(pdoc.domainId, pdoc.docId, { data, additional_file });
|
return { data, additional_file };
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// Move category to tag
|
||||||
|
async function _9_10() {
|
||||||
|
const _FRESH_INSTALL_IGNORE = 1;
|
||||||
|
await iterateAllProblem(['tag', 'category'], async (pdoc) => {
|
||||||
|
await document.coll.updateOne(
|
||||||
|
{ domainId: pdoc.domainId, docId: pdoc.docId },
|
||||||
|
{
|
||||||
|
$set: { tag: [...pdoc.tag || [], ...pdoc.category || []] },
|
||||||
|
$unset: { category: '' },
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export async function run({
|
|||||||
hint: pdoc.hint,
|
hint: pdoc.hint,
|
||||||
source: pdoc.source,
|
source: pdoc.source,
|
||||||
}, 'html'),
|
}, 'html'),
|
||||||
1, pdoc.source.split(' '), undefined, pdoc.defunct === 'Y',
|
1, pdoc.source.split(' '), pdoc.defunct === 'Y',
|
||||||
);
|
);
|
||||||
const [cdoc] = await query(`SELECT * FROM 'privilege' WHERE rightstr = 'p${pdoc.problem_id}'`);
|
const [cdoc] = await query(`SELECT * FROM 'privilege' WHERE rightstr = 'p${pdoc.problem_id}'`);
|
||||||
await problem.edit('system', pid, {
|
await problem.edit('system', pid, {
|
||||||
|
|||||||
@@ -30,14 +30,7 @@ export class QueryProblemSetAPIHandler extends Handler {
|
|||||||
}
|
}
|
||||||
const query: FilterQuery<Pdoc> = {};
|
const query: FilterQuery<Pdoc> = {};
|
||||||
if ($search) query.$text = { $search };
|
if ($search) query.$text = { $search };
|
||||||
if (tags?.length) {
|
if (tags?.length) query.$and = tags.map((tag) => ({ tag }));
|
||||||
query.$and = tags.map(($eq) => ({
|
|
||||||
$or: [
|
|
||||||
{ category: { $elemMatch: { $eq } } },
|
|
||||||
{ tag: { $elemMatch: { $eq } } },
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (owner) query.owner = owner;
|
if (owner) query.owner = owner;
|
||||||
if (!nonpublic) query.hidden = false;
|
if (!nonpublic) query.hidden = false;
|
||||||
const [raw, count] = await Promise.all([
|
const [raw, count] = await Promise.all([
|
||||||
|
|||||||
Reference in new issue
Block a user