diff --git a/package.json b/package.json index d528be034..485e95a4e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "globby": "^11.0.1", "jest": "^26.2.2", "latest-version": "^5.1.0", - "ora": "^4.0.5", + "ora": "^5.0.0", "semver": "^7.3.2", "ts-jest": "^26.1.4", "typescript": "^3.9.7" diff --git a/packages/hydrooj/TODO b/packages/hydrooj/TODO index a670000cd..c448fc491 100644 --- a/packages/hydrooj/TODO +++ b/packages/hydrooj/TODO @@ -4,6 +4,4 @@ SETTINGS_PRIVACY.allowRegisteredUsers model.setting.ui.name pdoc.secretConfig? message.markAsRead -批量导入用户 -domain_join_application -problem.difficulty_setting \ No newline at end of file +domain_join_application \ No newline at end of file diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index 212f05320..87cd17cac 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "2.12.5", + "version": "2.12.6", "bin": "bin/hydrooj.js", "main": "dist/loader.js", "typings": "dist/loader.d.ts", @@ -35,7 +35,7 @@ "mongodb": "^3.5.9", "nodemailer": "^6.4.10", "nunjucks": "^3.2.2", - "prismjs": "^1.20.0", + "prismjs": "^1.21.0", "proxy-agent": "^3.1.1", "ps-list": "^7.2.0", "serialize-javascript": "^4.0.0", diff --git a/packages/hydrooj/src/entry/common.ts b/packages/hydrooj/src/entry/common.ts index 87867954a..0cb2b4052 100644 --- a/packages/hydrooj/src/entry/common.ts +++ b/packages/hydrooj/src/entry/common.ts @@ -26,7 +26,7 @@ export const builtinHandler = [ ]; export const builtinScript = [ - 'rating', 'register', 'problemStat', 'blacklist', 'deleteUser', + 'rating', 'difficulty', 'problemStat', 'blacklist', 'deleteUser', 'upgrade0_1', ]; diff --git a/packages/hydrooj/src/handler/manage.ts b/packages/hydrooj/src/handler/manage.ts index ac4980278..388a0c7e5 100644 --- a/packages/hydrooj/src/handler/manage.ts +++ b/packages/hydrooj/src/handler/manage.ts @@ -165,6 +165,7 @@ class SystemSettingHandler extends SystemHandler { class SystemUserImportHandler extends SystemHandler { async get() { this.response.body.users = []; + this.response.body.path.push(['manage_user_import']); this.response.template = 'manage_user_import.html'; } @@ -188,7 +189,9 @@ class SystemUserImportHandler extends SystemHandler { } } else messages.push(`Line ${i + 1}: Input invalid.`); } + messages.push(`${udocs.length} users found.`); await Promise.all(tasks); + this.response.body.path.push(['manage_user_import']); this.response.body.users = udocs; this.response.body.messages = messages; } diff --git a/packages/hydrooj/src/handler/problem.ts b/packages/hydrooj/src/handler/problem.ts index 5294b2247..0ae21d130 100644 --- a/packages/hydrooj/src/handler/problem.ts +++ b/packages/hydrooj/src/handler/problem.ts @@ -369,12 +369,23 @@ class ProblemSettingsHandler extends ProblemManageHandler { @param('hidden', Types.Boolean) @param('category', Types.String, true, null, parseCategory) @param('tag', Types.String, true, null, parseCategory) + @param('difficultySetting', Types.UnsignedInt) + @param('difficultyAdmin', Types.UnsignedInt, true) async postSetting( domainId: string, pid: string | number, hidden = false, category: string[] = [], tag: string[] = [], + difficultySetting: string, difficultyAdmin: number, ) { const pdoc = await problem.get(domainId, pid); - await problem.edit(domainId, pdoc.docId, { hidden, category, tag }); + if (!problem.SETTING_DIFFICULTY_RANGE[difficultySetting]) { + throw new ValidationError('difficultySetting'); + } + if (!difficultyAdmin) difficultyAdmin = null; + else if (difficultyAdmin < 1 || difficultyAdmin > 9) throw new ValidationError('difficultyAdmin'); + await problem.edit(domainId, pdoc.docId, { + hidden, category, tag, difficultySetting, difficultyAdmin, + }); + await global.Hydro.script.difficulty.run({ domainId, pid }, console.log); this.back(); } } diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts index ee30309d3..0d0fa8ec9 100644 --- a/packages/hydrooj/src/interface.ts +++ b/packages/hydrooj/src/interface.ts @@ -135,11 +135,15 @@ export interface Pdoc { nAccept: number, tag: string[], category: string[], - difficulty: number, data?: ProblemData, hidden: boolean, config: ProblemConfig, acMsg?: string, + + difficulty?: number, + difficultyAlgo?: number, + difficultyAdmin?: number, + difficultySetting?: number, } export type Pdict = NumericDictionary; diff --git a/packages/hydrooj/src/model/problem.ts b/packages/hydrooj/src/model/problem.ts index bed24de83..77fcbb04a 100644 --- a/packages/hydrooj/src/model/problem.ts +++ b/packages/hydrooj/src/model/problem.ts @@ -10,6 +10,16 @@ import { import { ProblemNotFoundError } from '../error'; import * as testdataConfig from '../lib/testdataConfig'; +export const SETTING_DIFFICULTY_ALGORITHM = 0; +export const SETTING_DIFFICULTY_ADMIN = 1; +export const SETTING_DIFFICULTY_AVERAGE = 2; + +export const SETTING_DIFFICULTY_RANGE = [ + [SETTING_DIFFICULTY_ALGORITHM, 'Use algorithm calculated'], + [SETTING_DIFFICULTY_ADMIN, 'Use admin specificed'], + [SETTING_DIFFICULTY_AVERAGE, 'Use average of above'], +]; + export const pdocHidden: Pdoc = { _id: new ObjectID(), domainId: 'system', @@ -152,6 +162,11 @@ export async function setTestdata(domainId: string, _id: number, filePath: strin } global.Hydro.model.problem = { + SETTING_DIFFICULTY_ADMIN, + SETTING_DIFFICULTY_ALGORITHM, + SETTING_DIFFICULTY_AVERAGE, + SETTING_DIFFICULTY_RANGE, + pdocHidden, add, inc, diff --git a/packages/hydrooj/src/script/difficulty.ts b/packages/hydrooj/src/script/difficulty.ts new file mode 100644 index 000000000..c44d03e69 --- /dev/null +++ b/packages/hydrooj/src/script/difficulty.ts @@ -0,0 +1,73 @@ +import { Pdoc } from '../interface'; +import * as problem from '../model/problem'; + +export const description = 'Create a new user'; + +const _CACHE_INFO = { + last_s: 0.0, + last_y: 0, + values: [0.0], +}; + +function _LOGP(x: number) { + const sqrt_2_pi = 2.506628274631000502415765284811; // Sqrt[Pi] + return (2 * Math.exp(-1.0 * (Math.log(x) ** 2) * 2)) / x / sqrt_2_pi; +} + +function _integrate_ensure_cache(y: number) { + let last_y = _CACHE_INFO.last_y; + if (y <= last_y) return _CACHE_INFO; + let s = _CACHE_INFO.last_s; + const dx = 0.1; + const dT = 2; + let x0 = (last_y / dT) * dx; + while (y > last_y) { + x0 += dx; + s += _LOGP(x0) * dx; + for (let i = 1; i <= dT; i++) _CACHE_INFO.values.push(s); + last_y += dT; + } + _CACHE_INFO.last_y = last_y; + _CACHE_INFO.last_s = s; + return _CACHE_INFO; +} + +_integrate_ensure_cache(1000000); + +function _integrate(y: number) { + _integrate_ensure_cache(y); + return _CACHE_INFO.values[y]; +} + +function difficultyAlgorithm(nSubmit: number, nAccept: number) { + if (!nSubmit) return null; + const s = _integrate(nSubmit); + const ac_rate = nAccept / nSubmit; + const ans = Math.round(10.0 - 1.30 * s * 10.0 * ac_rate); + return Math.max(ans, 1); +} + +function _get_difficulty(pdoc: Pdoc, calculatedDifficulty: number) { + const setting = pdoc.difficultySetting; + if (setting === problem.SETTING_DIFFICULTY_ALGORITHM) return calculatedDifficulty; + if (setting === problem.SETTING_DIFFICULTY_ADMIN) return pdoc.difficultyAdmin; + if (setting === problem.SETTING_DIFFICULTY_AVERAGE && pdoc.difficultyAdmin !== null) { + return Math.round((calculatedDifficulty + pdoc.difficultyAdmin) / 2); + } return calculatedDifficulty; +} + +export async function run({ + domainId, pid, +}) { + const pdoc = await problem.get(domainId, pid); + const difficultyAlgo = difficultyAlgorithm(pdoc.nSubmit, pdoc.nAccept); + const difficulty = _get_difficulty(pdoc, difficultyAlgo); + return await problem.edit(domainId, pdoc.docId, { difficulty, difficultyAlgo }); +} + +export const validate = { + domainId: { $type: 'string' }, + pid: { $type: 'number' }, +}; + +global.Hydro.script.difficulty = { run, description, validate }; diff --git a/packages/hydrooj/src/script/register.ts b/packages/hydrooj/src/script/register.ts deleted file mode 100644 index c9ff877ed..000000000 --- a/packages/hydrooj/src/script/register.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as user from '../model/user'; - -export const description = 'Create a new user'; - -export async function run({ - uname, password, mail, uid, -}) { - if (uid) uid = parseInt(uid, 10); - if (uid && Number.isNaN(uid)) throw new Error('uid'); - uid = await user.create(mail, uname, password, uid); - return uid; -} - -export const validate = { - uname: { $type: 'string' }, - password: { $type: 'string' }, - mail: { $type: 'string' }, -}; - -global.Hydro.script.register = { run, description, validate };