From d46de63264da5f2be78495656558512b297ca41f Mon Sep 17 00:00:00 2001 From: undefined Date: Sun, 13 Apr 2025 02:10:10 +0800 Subject: [PATCH] core&ui: contest/homework: add search --- framework/eslint-config/package.json | 2 +- packages/hydrooj/src/context.ts | 4 ++-- packages/hydrooj/src/handler/contest.ts | 19 ++++++++++++------- packages/hydrooj/src/handler/homework.ts | 12 ++++++++---- packages/hydrooj/src/model/contest.ts | 6 +++--- packages/scoreboard-xcpcio/package.json | 2 +- packages/ui-default/package.json | 2 +- .../ui-default/templates/contest_main.html | 2 +- .../ui-default/templates/homework_main.html | 2 +- packages/ui-next/package.json | 2 +- 10 files changed, 31 insertions(+), 22 deletions(-) diff --git a/framework/eslint-config/package.json b/framework/eslint-config/package.json index bd90a61a6..9d6785e41 100644 --- a/framework/eslint-config/package.json +++ b/framework/eslint-config/package.json @@ -4,7 +4,7 @@ "license": "MIT", "main": "base.mjs", "dependencies": { - "@eslint-react/eslint-plugin": "^1.45.0", + "@eslint-react/eslint-plugin": "^1.45.3", "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", "@stylistic/eslint-plugin": "^4.2.0", diff --git a/packages/hydrooj/src/context.ts b/packages/hydrooj/src/context.ts index 22068e707..0877f55de 100644 --- a/packages/hydrooj/src/context.ts +++ b/packages/hydrooj/src/context.ts @@ -1,6 +1,6 @@ -import * as cordis from 'cordis'; import LoggerService from '@cordisjs/logger'; import { TimerService } from '@cordisjs/plugin-timer'; +import * as cordis from 'cordis'; import Schema from 'schemastery'; import type { DomainDoc, GeoIP, ModuleInterfaces } from './interface'; import { inject } from './lib/ui'; @@ -50,7 +50,7 @@ const T = any>(origFunc: F, disposeFunc?) => }); }; -export class ApiMixin extends cordis.Service { +export class ApiMixin extends Service { addScript = T(addScript); setImmediate = T(setImmediate, clearImmediate); provideModule = T(provideModule); diff --git a/packages/hydrooj/src/handler/contest.ts b/packages/hydrooj/src/handler/contest.ts index 4e90c5511..52e6edac2 100644 --- a/packages/hydrooj/src/handler/contest.ts +++ b/packages/hydrooj/src/handler/contest.ts @@ -1,6 +1,6 @@ import AdmZip from 'adm-zip'; import { stringify as toCSV } from 'csv-stringify/sync'; -import { pick } from 'lodash'; +import { escapeRegExp, pick } from 'lodash'; import moment from 'moment-timezone'; import { ObjectId } from 'mongodb'; import { @@ -33,14 +33,17 @@ export class ContestListHandler extends Handler { @param('rule', Types.Range(contest.RULES), true) @param('group', Types.Name, true) @param('page', Types.PositiveInt, true) - async get(domainId: string, rule = '', group = '', page = 1) { + @param('q', Types.String, true) + async get(domainId: string, rule = '', group = '', page = 1, q = '') { if (rule && contest.RULES[rule].hidden) throw new BadRequestError(); const groups = (await user.listGroup(domainId, this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) ? undefined : this.user._id)) .map((i) => i.name); if (group && !groups.includes(group)) throw new NotAssignedError(group); const rules = Object.keys(contest.RULES).filter((i) => !contest.RULES[i].hidden); - const q = { - ...this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group + const escaped = escapeRegExp(q.toLowerCase()); + const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi'); + const filter = { + ...(this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group) ? {} : { $or: [ @@ -52,11 +55,13 @@ export class ContestListHandler extends Handler { }, ...rule ? { rule } : { rule: { $in: rules } }, ...group ? { assign: { $in: [group] } } : {}, + ...q ? { title: { $regex } } : {}, }; - await this.ctx.parallel('contest/list', q, this); - const cursor = contest.getMulti(domainId, q).sort({ endAt: -1, beginAt: -1, _id: -1 }); + await this.ctx.parallel('contest/list', filter, this); + const cursor = contest.getMulti(domainId, filter).sort({ endAt: -1, beginAt: -1, _id: -1 }); let qs = rule ? `rule=${rule}` : ''; if (group) qs += qs ? `&group=${group}` : `group=${group}`; + if (q) qs += `${qs ? '&' : ''}q=${encodeURIComponent(q)}`; const [tdocs, tpcount] = await this.paginate(cursor, page, 'contest'); const tids = []; for (const tdoc of tdocs) tids.push(tdoc.docId); @@ -64,7 +69,7 @@ export class ContestListHandler extends Handler { const groupsFilter = groups.filter((i) => !Number.isSafeInteger(+i)); this.response.template = 'contest_main.html'; this.response.body = { - page, tpcount, qs, rule, tdocs, tsdict, groups: groupsFilter, group, + page, tpcount, qs, rule, tdocs, tsdict, groups: groupsFilter, group, q, }; } } diff --git a/packages/hydrooj/src/handler/homework.ts b/packages/hydrooj/src/handler/homework.ts index 3dc5c6b07..95a5c96f7 100644 --- a/packages/hydrooj/src/handler/homework.ts +++ b/packages/hydrooj/src/handler/homework.ts @@ -1,5 +1,5 @@ import yaml from 'js-yaml'; -import { pick } from 'lodash'; +import { escapeRegExp, pick } from 'lodash'; import moment from 'moment-timezone'; import { ObjectId } from 'mongodb'; import { sortFiles, Time } from '@hydrooj/utils/lib/utils'; @@ -26,10 +26,12 @@ const convertPenaltyRules = validatePenaltyRules; class HomeworkMainHandler extends Handler { @param('group', Types.Name, true) @param('page', Types.PositiveInt, true) - async get(domainId: string, group = '', page = 1) { + @param('q', Types.String, true) + async get(domainId: string, group = '', page = 1, q = '') { const groups = (await user.listGroup(domainId, this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_HOMEWORK) ? undefined : this.user._id)) .map((i) => i.name); if (group && !groups.includes(group)) throw new NotAssignedError(group); + const escaped = escapeRegExp(q.toLowerCase()); const cursor = contest.getMulti(domainId, { rule: 'homework', ...this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_HOMEWORK) && !group @@ -43,6 +45,7 @@ class HomeworkMainHandler extends Handler { ], }, ...group ? { assign: { $in: [group] } } : {}, + ...q ? { title: { $regex: new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi') } } : {}, }).sort({ penaltySince: -1, endAt: -1, beginAt: -1, _id: -1, }); @@ -56,10 +59,11 @@ class HomeworkMainHandler extends Handler { } else cal.endAt = tdoc.penaltySince; calendar.push(cal); } - const qs = group ? `group=${group}` : ''; + let qs = group ? `group=${group}` : ''; + if (q) qs += `${qs ? '&' : ''}q=${encodeURIComponent(q)}`; const groupsFilter = groups.filter((i) => !Number.isSafeInteger(+i)); this.response.body = { - tdocs, calendar, tpcount, page, qs, groups: groupsFilter, group, + tdocs, calendar, tpcount, page, qs, groups: groupsFilter, group, q, }; this.response.template = 'homework_main.html'; } diff --git a/packages/hydrooj/src/model/contest.ts b/packages/hydrooj/src/model/contest.ts index ba085cfa7..4f2017d73 100644 --- a/packages/hydrooj/src/model/contest.ts +++ b/packages/hydrooj/src/model/contest.ts @@ -783,11 +783,11 @@ export async function add( }); RULES[rule].check(data); await bus.parallel('contest/before-add', data); - const res = await document.add(domainId, content, owner, document.TYPE_CONTEST, null, null, null, { + const docId = await document.add(domainId, content, owner, document.TYPE_CONTEST, null, null, null, { ...data, title, rule, beginAt, endAt, pids, attend: 0, rated, }); - await bus.parallel('contest/add', data, res); - return res; + await bus.parallel('contest/add', data, docId); + return docId; } export async function edit(domainId: string, tid: ObjectId, $set: Partial) { diff --git a/packages/scoreboard-xcpcio/package.json b/packages/scoreboard-xcpcio/package.json index ba9085576..9524e0cde 100644 --- a/packages/scoreboard-xcpcio/package.json +++ b/packages/scoreboard-xcpcio/package.json @@ -7,6 +7,6 @@ "postinstall": "node -r @hydrooj/register install.ts" }, "dependencies": { - "@xcpcio/board-app": "^0.55.1" + "@xcpcio/board-app": "^0.55.2" } } diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 843ab89a0..ecccc53c4 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -108,7 +108,7 @@ "sticky-kit": "^1.1.3", "style-loader": "^4.0.0", "stylus": "^0.64.0", - "stylus-loader": "7.1.3", + "stylus-loader": "7.1.2", "svgtofont": "^6.3.2", "tether": "1.4.7", "tether-drop": "^1.4.2", diff --git a/packages/ui-default/templates/contest_main.html b/packages/ui-default/templates/contest_main.html index ee73ff80d..a2c572f49 100644 --- a/packages/ui-default/templates/contest_main.html +++ b/packages/ui-default/templates/contest_main.html @@ -59,7 +59,7 @@

{{ _('All Contests') if not rule else _('All {0} Contests').format(_(model.contest.RULES[rule].TEXT)) }}

- {{ _('Show') }}: +