mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core&ui: add keep scoreboard hidden option
This commit is contained in:
8 files changed
+30
-26
No files matched your search
+3
-3
@@ -47,16 +47,16 @@
|
|||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-import-resolver-webpack": "^0.13.11",
|
"eslint-import-resolver-webpack": "^0.13.11",
|
||||||
"fs-extra": "^11.3.4",
|
"fs-extra": "^11.3.4",
|
||||||
"globals": "^17.4.0",
|
"globals": "^17.5.0",
|
||||||
"globby": "~14.0.2",
|
"globby": "~14.0.2",
|
||||||
"inspectpack": "^4.7.1",
|
"inspectpack": "^4.7.1",
|
||||||
"mongodb-memory-server": "11.0.1",
|
"mongodb-memory-server": "11.0.1",
|
||||||
"nyc": "^18.0.0",
|
"nyc": "^18.0.0",
|
||||||
"ora": "^9.3.0",
|
"ora": "^9.3.0",
|
||||||
"oxlint": "^1.58.0",
|
"oxlint": "^1.60.0",
|
||||||
"package-json": "^10.0.1",
|
"package-json": "^10.0.1",
|
||||||
"semver": "^7.7.4",
|
"semver": "^7.7.4",
|
||||||
"simple-git": "^3.33.0",
|
"simple-git": "^3.36.0",
|
||||||
"supertest": "^7.2.2",
|
"supertest": "^7.2.2",
|
||||||
"supports-hyperlinks": "^4.4.0",
|
"supports-hyperlinks": "^4.4.0",
|
||||||
"typescript": "5.8.3",
|
"typescript": "5.8.3",
|
||||||
|
|||||||
@@ -403,12 +403,14 @@ export class ContestEditHandler extends Handler {
|
|||||||
@param('maintainer', Types.NumericArray, true)
|
@param('maintainer', Types.NumericArray, true)
|
||||||
@param('allowViewCode', Types.Boolean)
|
@param('allowViewCode', Types.Boolean)
|
||||||
@param('allowPrint', Types.Boolean)
|
@param('allowPrint', Types.Boolean)
|
||||||
|
@param('keepScoreboardHidden', Types.Boolean)
|
||||||
@param('langs', Types.CommaSeperatedArray, true)
|
@param('langs', Types.CommaSeperatedArray, true)
|
||||||
async postUpdate(
|
async postUpdate(
|
||||||
domainId: string, tid: ObjectId, beginAtDate: string, beginAtTime: string, duration: number,
|
domainId: string, tid: ObjectId, beginAtDate: string, beginAtTime: string, duration: number,
|
||||||
title: string, content: string, rule: string, _pids: string, rated = false,
|
title: string, content: string, rule: string, _pids: string, rated = false,
|
||||||
_code = '', autoHide = false, assign: string[] = [], lock: number = null,
|
_code = '', autoHide = false, assign: string[] = [], lock: number = null,
|
||||||
contestDuration: number = null, maintainer: number[] = [], allowViewCode = false, allowPrint = false, langs: string[] = [],
|
contestDuration: number = null, maintainer: number[] = [], allowViewCode = false, allowPrint = false,
|
||||||
|
keepScoreboardHidden = false, langs: string[] = [],
|
||||||
) {
|
) {
|
||||||
if (!Object.keys(contest.RULES).includes(rule) || contest.RULES[rule].hidden) throw new ValidationError('rule');
|
if (!Object.keys(contest.RULES).includes(rule) || contest.RULES[rule].hidden) throw new ValidationError('rule');
|
||||||
if (autoHide) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
if (autoHide) this.checkPerm(PERM.PERM_EDIT_PROBLEM);
|
||||||
@@ -450,7 +452,7 @@ export class ContestEditHandler extends Handler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
await contest.edit(domainId, tid, {
|
await contest.edit(domainId, tid, {
|
||||||
assign, _code, autoHide, lockAt, maintainer, allowViewCode, allowPrint, langs,
|
assign, _code, autoHide, lockAt, maintainer, allowViewCode, allowPrint, keepScoreboardHidden, langs,
|
||||||
});
|
});
|
||||||
this.response.body = { tid };
|
this.response.body = { tid };
|
||||||
this.response.redirect = this.url('contest_detail', { tid });
|
this.response.redirect = this.url('contest_detail', { tid });
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ export interface Tdoc extends Document {
|
|||||||
privateFiles?: FileInfo[];
|
privateFiles?: FileInfo[];
|
||||||
allowViewCode?: boolean;
|
allowViewCode?: boolean;
|
||||||
allowPrint?: boolean;
|
allowPrint?: boolean;
|
||||||
|
keepScoreboardHidden?: boolean;
|
||||||
|
|
||||||
// For contest
|
// For contest
|
||||||
lockAt?: Date;
|
lockAt?: Date;
|
||||||
|
|||||||
@@ -312,9 +312,9 @@ const oi = buildContestRule({
|
|||||||
}
|
}
|
||||||
return { score, detail, display };
|
return { score, detail, display };
|
||||||
},
|
},
|
||||||
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
showScoreboard: (tdoc, now) => now > tdoc.endAt && !tdoc.keepScoreboardHidden,
|
||||||
showSelfRecord: (tdoc, now) => now > tdoc.endAt,
|
showSelfRecord: (tdoc, now) => now > tdoc.endAt && !tdoc.keepScoreboardHidden,
|
||||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
showRecord: (tdoc, now) => now > tdoc.endAt && !tdoc.keepScoreboardHidden,
|
||||||
async scoreboardHeader(config, _, tdoc, pdict) {
|
async scoreboardHeader(config, _, tdoc, pdict) {
|
||||||
const columns: ScoreboardNode[] = [
|
const columns: ScoreboardNode[] = [
|
||||||
{ type: 'rank', value: '#' },
|
{ type: 'rank', value: '#' },
|
||||||
@@ -477,9 +477,9 @@ const ioi = buildContestRule({
|
|||||||
const strictioi = buildContestRule({
|
const strictioi = buildContestRule({
|
||||||
TEXT: 'IOI(Strict)',
|
TEXT: 'IOI(Strict)',
|
||||||
submitAfterAccept: false,
|
submitAfterAccept: false,
|
||||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
showRecord: (tdoc, now) => now > tdoc.endAt && !tdoc.keepScoreboardHidden,
|
||||||
showSelfRecord: () => true,
|
showSelfRecord: (tdoc) => !tdoc.keepScoreboardHidden || !isDone(tdoc),
|
||||||
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
showScoreboard: (tdoc, now) => now > tdoc.endAt && !tdoc.keepScoreboardHidden,
|
||||||
stat(tdoc, journal) {
|
stat(tdoc, journal) {
|
||||||
const detail = {};
|
const detail = {};
|
||||||
let score = 0;
|
let score = 0;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
_, avatar, BadRequestError, ContestModel, ContestNotEndedError, Context, db, findFileSync,
|
_, avatar, ContestModel, ContestNotEndedError, Context, db, findFileSync,
|
||||||
ForbiddenError, fs, Handler, InvalidTokenError, ObjectId, param, parseTimeMS, PERM, ProblemConfig, ProblemModel,
|
ForbiddenError, fs, Handler, InvalidTokenError, ObjectId, param, parseTimeMS, PERM, ProblemConfig, ProblemModel,
|
||||||
randomstring, Schema, SettingModel, STATUS, STATUS_SHORT_TEXTS, STATUS_TEXTS,
|
randomstring, Schema, SettingModel, STATUS, STATUS_SHORT_TEXTS, STATUS_TEXTS,
|
||||||
SystemModel, TokenModel, Types, UserModel, Zip,
|
SystemModel, TokenModel, Types, UserModel, Zip,
|
||||||
@@ -89,12 +89,6 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function generateCdpZip(tdoc) {
|
async function generateCdpZip(tdoc) {
|
||||||
const serverUrl = SystemModel.get('server.url');
|
|
||||||
try {
|
|
||||||
new URL(serverUrl);
|
|
||||||
} catch (e) {
|
|
||||||
throw new BadRequestError('Server URL not set');
|
|
||||||
}
|
|
||||||
let token = 0;
|
let token = 0;
|
||||||
const getFeed = (type: string, data: any) => ({
|
const getFeed = (type: string, data: any) => ({
|
||||||
type, id: data.id, data, token: `t${token++}`,
|
type, id: data.id, data, token: `t${token++}`,
|
||||||
@@ -168,7 +162,7 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
group_ids: i.group.map((j) => groupId[j]),
|
group_ids: i.group.map((j) => groupId[j]),
|
||||||
organization_id: orgId[i.organization],
|
organization_id: orgId[i.organization],
|
||||||
photo: [{ href: `contest/${tdoc._id}/teams/${i.team_id}/photo`, filename: 'photo.png', mime: 'image/png', width: 1920, height: 1080 }],
|
photo: [{ href: `contest/${tdoc._id}/teams/${i.team_id}/photo`, filename: 'photo.png', mime: 'image/png', width: 1920, height: 1080 }],
|
||||||
logo: [{ href: new URL(i.avatar, serverUrl).toString(), filename: 'logo.webp', mime: 'image/webp', width: 128, height: 128 }],
|
logo: [{ href: new URL(i.avatar, SystemModel.get('server.url')).toString(), filename: 'logo.webp', mime: 'image/webp', width: 128, height: 128 }],
|
||||||
})),
|
})),
|
||||||
...tdoc.pids.map((i, idx) => getFeed('problems', {
|
...tdoc.pids.map((i, idx) => getFeed('problems', {
|
||||||
id: `${i}`, label: String.fromCharCode(65 + idx), name: pdict[i].title, ordinal: idx,
|
id: `${i}`, label: String.fromCharCode(65 + idx), name: pdict[i].title, ordinal: idx,
|
||||||
@@ -268,17 +262,11 @@ export function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
|||||||
async display({ tdoc }) {
|
async display({ tdoc }) {
|
||||||
if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST);
|
if (!this.user.own(tdoc)) this.checkPerm(PERM.PERM_EDIT_CONTEST);
|
||||||
if (!ContestModel.isDone(tdoc)) throw new ContestNotEndedError();
|
if (!ContestModel.isDone(tdoc)) throw new ContestNotEndedError();
|
||||||
const serverUrl = SystemModel.get('server.url');
|
|
||||||
try {
|
|
||||||
new URL(serverUrl);
|
|
||||||
} catch (e) {
|
|
||||||
throw new BadRequestError('Server URL not set');
|
|
||||||
}
|
|
||||||
const [tokenId] = await TokenModel.add(
|
const [tokenId] = await TokenModel.add(
|
||||||
TokenModel.TYPE_EXPORT, 600,
|
TokenModel.TYPE_EXPORT, 600,
|
||||||
{ domainId: tdoc.domainId, contestId: tdoc._id },
|
{ domainId: tdoc.domainId, contestId: tdoc._id },
|
||||||
);
|
);
|
||||||
const source = new URL(`/d/${tdoc.domainId}/contest/${tdoc._id}/resolver-cdp/${tokenId}`, serverUrl).toString();
|
const source = new URL(`/d/${tdoc.domainId}/contest/${tdoc._id}/resolver-cdp/${tokenId}`, SystemModel.get('server.url')).toString();
|
||||||
const target = new URL('https://resolver.hydrooj.com');
|
const target = new URL('https://resolver.hydrooj.com');
|
||||||
target.searchParams.set('source', source);
|
target.searchParams.set('source', source);
|
||||||
target.searchParams.set('mode', tdoc.rule === 'oi' ? 'oi' : 'acm');
|
target.searchParams.set('mode', tdoc.rule === 'oi' ? 'oi' : 'acm');
|
||||||
|
|||||||
@@ -419,6 +419,7 @@ Forgot Password and/or Username: 忘记密码和/或用户名
|
|||||||
Forgot password or username?: 忘记密码或者用户名?
|
Forgot password or username?: 忘记密码或者用户名?
|
||||||
Formula blocks: 公式区块
|
Formula blocks: 公式区块
|
||||||
Freeze scoreboard with N minutes remaining: 在赛事剩余N分钟时封榜
|
Freeze scoreboard with N minutes remaining: 在赛事剩余N分钟时封榜
|
||||||
|
Keep scoreboard hidden even after contest ends: 即使比赛结束后也保持成绩表隐藏
|
||||||
fs_upload: 上传文件
|
fs_upload: 上传文件
|
||||||
Gender Visibility: 性别可见性
|
Gender Visibility: 性别可见性
|
||||||
Gender: 性别
|
Gender: 性别
|
||||||
|
|||||||
@@ -418,6 +418,7 @@ Forgot Password and/or Username: 忘記密碼和/或使用者名稱
|
|||||||
Forgot password or username?: 忘記密碼或者使用者名?
|
Forgot password or username?: 忘記密碼或者使用者名?
|
||||||
Formula blocks: 公式區塊
|
Formula blocks: 公式區塊
|
||||||
Freeze scoreboard with N minutes remaining: 在賽事剩餘N分鐘時封榜
|
Freeze scoreboard with N minutes remaining: 在賽事剩餘N分鐘時封榜
|
||||||
|
Keep scoreboard hidden even after contest ends: 即使比賽結束後也保持成績表隱藏
|
||||||
fs_upload: 上傳檔案
|
fs_upload: 上傳檔案
|
||||||
Gender Visibility: 性別可見性
|
Gender Visibility: 性別可見性
|
||||||
Gender: 性別
|
Gender: 性別
|
||||||
|
|||||||
@@ -174,6 +174,17 @@
|
|||||||
value:tdoc.allowPrint|default(false),
|
value:tdoc.allowPrint|default(false),
|
||||||
row:false
|
row:false
|
||||||
}) }}
|
}) }}
|
||||||
|
<div style="display: none" class="contest-rule-settings contest-rule--oi contest-rule--strictioi">
|
||||||
|
{{ form.form_checkbox({
|
||||||
|
columns:4,
|
||||||
|
label:'Keep Scoreboard Hidden',
|
||||||
|
name:'keepScoreboardHidden',
|
||||||
|
placeholder:_('Keep scoreboard hidden even after contest ends'),
|
||||||
|
value:tdoc.keepScoreboardHidden|default(false),
|
||||||
|
disabled:true,
|
||||||
|
row:false
|
||||||
|
}) }}
|
||||||
|
</div>
|
||||||
<div style="display: none" class="contest-rule-settings contest-rule--acm contest-rule--ioi">
|
<div style="display: none" class="contest-rule-settings contest-rule--acm contest-rule--ioi">
|
||||||
{{ form.form_text({
|
{{ form.form_text({
|
||||||
columns:4,
|
columns:4,
|
||||||
|
|||||||
Reference in new issue
Block a user