mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core&ui: contest/homework: add search
This commit is contained in:
10 files changed
+31
-22
No files matched your search
@@ -4,7 +4,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "base.mjs",
|
"main": "base.mjs",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-react/eslint-plugin": "^1.45.0",
|
"@eslint-react/eslint-plugin": "^1.45.3",
|
||||||
"@eslint/compat": "^1.2.8",
|
"@eslint/compat": "^1.2.8",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@stylistic/eslint-plugin": "^4.2.0",
|
"@stylistic/eslint-plugin": "^4.2.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as cordis from 'cordis';
|
|
||||||
import LoggerService from '@cordisjs/logger';
|
import LoggerService from '@cordisjs/logger';
|
||||||
import { TimerService } from '@cordisjs/plugin-timer';
|
import { TimerService } from '@cordisjs/plugin-timer';
|
||||||
|
import * as cordis from 'cordis';
|
||||||
import Schema from 'schemastery';
|
import Schema from 'schemastery';
|
||||||
import type { DomainDoc, GeoIP, ModuleInterfaces } from './interface';
|
import type { DomainDoc, GeoIP, ModuleInterfaces } from './interface';
|
||||||
import { inject } from './lib/ui';
|
import { inject } from './lib/ui';
|
||||||
@@ -50,7 +50,7 @@ const T = <F extends (...args: any[]) => any>(origFunc: F, disposeFunc?) =>
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ApiMixin extends cordis.Service<never, Context> {
|
export class ApiMixin extends Service {
|
||||||
addScript = T(addScript);
|
addScript = T(addScript);
|
||||||
setImmediate = T(setImmediate, clearImmediate);
|
setImmediate = T(setImmediate, clearImmediate);
|
||||||
provideModule = T(provideModule);
|
provideModule = T(provideModule);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import { stringify as toCSV } from 'csv-stringify/sync';
|
import { stringify as toCSV } from 'csv-stringify/sync';
|
||||||
import { pick } from 'lodash';
|
import { escapeRegExp, pick } from 'lodash';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import {
|
import {
|
||||||
@@ -33,14 +33,17 @@ export class ContestListHandler extends Handler {
|
|||||||
@param('rule', Types.Range(contest.RULES), true)
|
@param('rule', Types.Range(contest.RULES), true)
|
||||||
@param('group', Types.Name, true)
|
@param('group', Types.Name, true)
|
||||||
@param('page', Types.PositiveInt, 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();
|
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))
|
const groups = (await user.listGroup(domainId, this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) ? undefined : this.user._id))
|
||||||
.map((i) => i.name);
|
.map((i) => i.name);
|
||||||
if (group && !groups.includes(group)) throw new NotAssignedError(group);
|
if (group && !groups.includes(group)) throw new NotAssignedError(group);
|
||||||
const rules = Object.keys(contest.RULES).filter((i) => !contest.RULES[i].hidden);
|
const rules = Object.keys(contest.RULES).filter((i) => !contest.RULES[i].hidden);
|
||||||
const q = {
|
const escaped = escapeRegExp(q.toLowerCase());
|
||||||
...this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group
|
const $regex = new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi');
|
||||||
|
const filter = {
|
||||||
|
...(this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_CONTEST) && !group)
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
$or: [
|
$or: [
|
||||||
@@ -52,11 +55,13 @@ export class ContestListHandler extends Handler {
|
|||||||
},
|
},
|
||||||
...rule ? { rule } : { rule: { $in: rules } },
|
...rule ? { rule } : { rule: { $in: rules } },
|
||||||
...group ? { assign: { $in: [group] } } : {},
|
...group ? { assign: { $in: [group] } } : {},
|
||||||
|
...q ? { title: { $regex } } : {},
|
||||||
};
|
};
|
||||||
await this.ctx.parallel('contest/list', q, this);
|
await this.ctx.parallel('contest/list', filter, this);
|
||||||
const cursor = contest.getMulti(domainId, q).sort({ endAt: -1, beginAt: -1, _id: -1 });
|
const cursor = contest.getMulti(domainId, filter).sort({ endAt: -1, beginAt: -1, _id: -1 });
|
||||||
let qs = rule ? `rule=${rule}` : '';
|
let qs = rule ? `rule=${rule}` : '';
|
||||||
if (group) qs += qs ? `&group=${group}` : `group=${group}`;
|
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 [tdocs, tpcount] = await this.paginate(cursor, page, 'contest');
|
||||||
const tids = [];
|
const tids = [];
|
||||||
for (const tdoc of tdocs) tids.push(tdoc.docId);
|
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));
|
const groupsFilter = groups.filter((i) => !Number.isSafeInteger(+i));
|
||||||
this.response.template = 'contest_main.html';
|
this.response.template = 'contest_main.html';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
page, tpcount, qs, rule, tdocs, tsdict, groups: groupsFilter, group,
|
page, tpcount, qs, rule, tdocs, tsdict, groups: groupsFilter, group, q,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import { pick } from 'lodash';
|
import { escapeRegExp, pick } from 'lodash';
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
import { ObjectId } from 'mongodb';
|
import { ObjectId } from 'mongodb';
|
||||||
import { sortFiles, Time } from '@hydrooj/utils/lib/utils';
|
import { sortFiles, Time } from '@hydrooj/utils/lib/utils';
|
||||||
@@ -26,10 +26,12 @@ const convertPenaltyRules = validatePenaltyRules;
|
|||||||
class HomeworkMainHandler extends Handler {
|
class HomeworkMainHandler extends Handler {
|
||||||
@param('group', Types.Name, true)
|
@param('group', Types.Name, true)
|
||||||
@param('page', Types.PositiveInt, 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))
|
const groups = (await user.listGroup(domainId, this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_HOMEWORK) ? undefined : this.user._id))
|
||||||
.map((i) => i.name);
|
.map((i) => i.name);
|
||||||
if (group && !groups.includes(group)) throw new NotAssignedError(group);
|
if (group && !groups.includes(group)) throw new NotAssignedError(group);
|
||||||
|
const escaped = escapeRegExp(q.toLowerCase());
|
||||||
const cursor = contest.getMulti(domainId, {
|
const cursor = contest.getMulti(domainId, {
|
||||||
rule: 'homework',
|
rule: 'homework',
|
||||||
...this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_HOMEWORK) && !group
|
...this.user.hasPerm(PERM.PERM_VIEW_HIDDEN_HOMEWORK) && !group
|
||||||
@@ -43,6 +45,7 @@ class HomeworkMainHandler extends Handler {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
...group ? { assign: { $in: [group] } } : {},
|
...group ? { assign: { $in: [group] } } : {},
|
||||||
|
...q ? { title: { $regex: new RegExp(q.length >= 2 ? escaped : `\\A${escaped}`, 'gmi') } } : {},
|
||||||
}).sort({
|
}).sort({
|
||||||
penaltySince: -1, endAt: -1, beginAt: -1, _id: -1,
|
penaltySince: -1, endAt: -1, beginAt: -1, _id: -1,
|
||||||
});
|
});
|
||||||
@@ -56,10 +59,11 @@ class HomeworkMainHandler extends Handler {
|
|||||||
} else cal.endAt = tdoc.penaltySince;
|
} else cal.endAt = tdoc.penaltySince;
|
||||||
calendar.push(cal);
|
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));
|
const groupsFilter = groups.filter((i) => !Number.isSafeInteger(+i));
|
||||||
this.response.body = {
|
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';
|
this.response.template = 'homework_main.html';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -783,11 +783,11 @@ export async function add(
|
|||||||
});
|
});
|
||||||
RULES[rule].check(data);
|
RULES[rule].check(data);
|
||||||
await bus.parallel('contest/before-add', 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,
|
...data, title, rule, beginAt, endAt, pids, attend: 0, rated,
|
||||||
});
|
});
|
||||||
await bus.parallel('contest/add', data, res);
|
await bus.parallel('contest/add', data, docId);
|
||||||
return res;
|
return docId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function edit(domainId: string, tid: ObjectId, $set: Partial<Tdoc>) {
|
export async function edit(domainId: string, tid: ObjectId, $set: Partial<Tdoc>) {
|
||||||
|
|||||||
@@ -7,6 +7,6 @@
|
|||||||
"postinstall": "node -r @hydrooj/register install.ts"
|
"postinstall": "node -r @hydrooj/register install.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xcpcio/board-app": "^0.55.1"
|
"@xcpcio/board-app": "^0.55.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
"sticky-kit": "^1.1.3",
|
"sticky-kit": "^1.1.3",
|
||||||
"style-loader": "^4.0.0",
|
"style-loader": "^4.0.0",
|
||||||
"stylus": "^0.64.0",
|
"stylus": "^0.64.0",
|
||||||
"stylus-loader": "7.1.3",
|
"stylus-loader": "7.1.2",
|
||||||
"svgtofont": "^6.3.2",
|
"svgtofont": "^6.3.2",
|
||||||
"tether": "1.4.7",
|
"tether": "1.4.7",
|
||||||
"tether-drop": "^1.4.2",
|
"tether-drop": "^1.4.2",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
<h1 class="section__title">{{ _('All Contests') if not rule else _('All {0} Contests').format(_(model.contest.RULES[rule].TEXT)) }}</h1>
|
<h1 class="section__title">{{ _('All Contests') if not rule else _('All {0} Contests').format(_(model.contest.RULES[rule].TEXT)) }}</h1>
|
||||||
<div class="section__tools">
|
<div class="section__tools">
|
||||||
<form method="get" name="filter-form">
|
<form method="get" name="filter-form">
|
||||||
{{ _('Show') }}:
|
<input type="text" class="inline compact textbox" name="q" value="{{ q }}" placeholder="{{ _('Search contests...') }}">
|
||||||
<select class="inline compact select" name="group">
|
<select class="inline compact select" name="group">
|
||||||
<option value=""{% if not group %} selected{% endif %}>{{ _('All') }}</option>
|
<option value=""{% if not group %} selected{% endif %}>{{ _('All') }}</option>
|
||||||
{%- for g in groups -%}
|
{%- for g in groups -%}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</span>
|
</span>
|
||||||
{% if groups.length %}
|
{% if groups.length %}
|
||||||
<form method="get" class="filter-form" name="filter-form">
|
<form method="get" class="filter-form" name="filter-form">
|
||||||
{{ _('Show') }}:
|
<input type="text" class="inline compact textbox" name="q" value="{{ q }}" placeholder="{{ _('Search homeworks...') }}">
|
||||||
<select class="inline compact select" name="group">
|
<select class="inline compact select" name="group">
|
||||||
<option value=""{% if not group %} selected{% endif %}>{{ _('All') }}</option>
|
<option value=""{% if not group %} selected{% endif %}>{{ _('All') }}</option>
|
||||||
{%- for g in groups -%}
|
{%- for g in groups -%}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^19.1.0",
|
"@types/react": "^19.1.1",
|
||||||
"@types/react-dom": "^19.1.2",
|
"@types/react-dom": "^19.1.2",
|
||||||
"@vitejs/plugin-react-swc": "^3.8.1",
|
"@vitejs/plugin-react-swc": "^3.8.1",
|
||||||
"vite": "^6.2.6"
|
"vite": "^6.2.6"
|
||||||
|
|||||||
Reference in new issue
Block a user