diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json
index 130cbc9a8..de4462891 100644
--- a/packages/hydrooj/package.json
+++ b/packages/hydrooj/package.json
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
- "version": "2.26.14",
+ "version": "2.26.15",
"bin": "bin/hydrooj.js",
"main": "dist/loader.js",
"typings": "dist/loader.d.ts",
diff --git a/packages/hydrooj/src/interface.ts b/packages/hydrooj/src/interface.ts
index fc963df5f..8a07cfc88 100644
--- a/packages/hydrooj/src/interface.ts
+++ b/packages/hydrooj/src/interface.ts
@@ -269,9 +269,10 @@ export interface RecordDoc {
}
export interface ScoreboardNode {
- type: string,
- value: string,
- raw?: any,
+ type: string;
+ value: string;
+ raw?: any;
+ score?: number;
}
export type ScoreboardRow = ScoreboardNode[] & { raw?: any };
diff --git a/packages/hydrooj/src/model/contest.ts b/packages/hydrooj/src/model/contest.ts
index aab0c2c49..7ddf045da 100644
--- a/packages/hydrooj/src/model/contest.ts
+++ b/packages/hydrooj/src/model/contest.ts
@@ -110,21 +110,10 @@ const acm: ContestRule = {
}
for (const pid of tdoc.pids) {
const doc = tsddict[pid] || {};
- let rid;
- let colAccepted;
- let colTime;
- let colTimeStr;
- if (doc.accept) {
- rid = doc.rid;
- colAccepted = `${_('Accepted')} ${doc.naccept ? ` (-${doc.naccept})` : ''}`;
- colTime = doc.time;
- colTimeStr = misc.formatSeconds(colTime);
- } else {
- rid = null;
- colAccepted = doc.naccept ? `(-${doc.naccept})` : '-';
- colTime = '-';
- colTimeStr = '';
- }
+ const rid = doc.accept ? doc.rid : null;
+ const colAccepted = `${doc.accept ? `${_('Accepted')} ` : ''}${doc.naccept ? ` (-${doc.naccept})` : ''}`;
+ const colTime = doc.accept ? doc.time : '-';
+ const colTimeStr = doc.accept ? misc.formatSeconds(colTime) : '-';
if (isExport) {
row.push({ type: 'string', value: colAccepted });
row.push({ type: 'string', value: colTime });
@@ -132,7 +121,8 @@ const acm: ContestRule = {
} else {
row.push({
type: 'record',
- value: '{0}
{1}'.format(colAccepted, colTimeStr),
+ score: doc.accept ? 100 : 0,
+ value: '{0}\n{1}'.format(colAccepted, colTimeStr),
raw: rid,
});
}
@@ -185,6 +175,19 @@ const oi: ContestRule = {
});
}
}
+ const psdict = {};
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ if (isDone(tdoc)) {
+ const psdocs = await problem.getMultiStatus(tdoc.domainId, {
+ $or: rankedTsdocs.map(([, i]) => ({
+ uid: i.uid,
+ docId: { $in: tdoc.pids },
+ })),
+ }).toArray();
+ for (const psdoc of psdocs) {
+ psdict[`${psdoc.uid}/${psdoc.docId}`] = psdoc;
+ }
+ }
const rows = [columns];
for (const [rank, tsdoc] of rankedTsdocs) {
const tsddict = {};
@@ -198,11 +201,29 @@ const oi: ContestRule = {
{ type: 'string', value: tsdoc.score || 0 },
);
for (const pid of tdoc.pids) {
- row.push({
- type: 'record',
- value: tsddict[pid]?.score ?? '-',
- raw: tsddict[pid]?.rid || null,
- });
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ if (isDone(tdoc)) {
+ row.push({
+ type: 'records',
+ value: '',
+ raw: [
+ {
+ value: tsddict[pid]?.score ?? '-',
+ raw: tsddict[pid]?.rid || null,
+ },
+ {
+ value: psdict[`${tsdoc.uid}/${pid}`]?.score ?? '-',
+ raw: psdict[`${tsdoc.uid}/${pid}`]?.rid ?? null,
+ },
+ ],
+ });
+ } else {
+ row.push({
+ type: 'record',
+ value: tsddict[pid]?.score ?? '-',
+ raw: tsddict[pid]?.rid || null,
+ });
+ }
}
rows.push(row);
}
@@ -313,13 +334,13 @@ const homework: ContestRule = {
});
}
}
- const rows = [columns];
+ const rows: ScoreboardRow[] = [columns];
for (const [rank, tsdoc] of rankedTsdocs) {
const tsddict = {};
for (const item of tsdoc.detail || []) {
tsddict[item.pid] = item;
}
- const row = [
+ const row: ScoreboardRow = [
{ type: 'string', value: rank },
{
type: 'user',
diff --git a/packages/ui-default/backendlib/template.js b/packages/ui-default/backendlib/template.js
index 3ebb103f5..59fc9e057 100644
--- a/packages/ui-default/backendlib/template.js
+++ b/packages/ui-default/backendlib/template.js
@@ -66,6 +66,7 @@ class Nunjucks extends nunjucks.Environment {
this.addFilter('base64_encode', (s) => Buffer.from(s).toString('base64'));
this.addFilter('base64_decode', (s) => Buffer.from(s, 'base64').toString());
this.addFilter('bitand', (self, val) => self & val);
+ this.addFilter('replaceBr', (self) => self.toString().replace(/\n/g, '
'));
this.addFilter('toString', (self) => (typeof self === 'string' ? self : JSON.stringify(self, replacer)));
this.addFilter('content', (content, language, html) => {
let s = '';
diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json
index 961f6cf1a..beb056d8c 100644
--- a/packages/ui-default/package.json
+++ b/packages/ui-default/package.json
@@ -1,6 +1,6 @@
{
"name": "@hydrooj/ui-default",
- "version": "4.8.29",
+ "version": "4.8.30",
"author": "undefined ",
"license": "AGPL-3.0",
"main": "hydro.js",
diff --git a/packages/ui-default/templates/contest_scoreboard.html b/packages/ui-default/templates/contest_scoreboard.html
index df1415808..97ffab617 100644
--- a/packages/ui-default/templates/contest_scoreboard.html
+++ b/packages/ui-default/templates/contest_scoreboard.html
@@ -41,10 +41,21 @@
{{ user.render_inline(udict[column.raw], badge=false) }}
{% elif column.type == 'record' and column.raw %}
- {{ column.value|safe }}
+ {{ column.value|replaceBr|safe }}
+ {% elif column.type == 'records' %}
+ {%- for record in column.raw -%}
+ {% if loop.index0 %}/{% endif %}
+ {% if record.raw %}
+
+ {{ record.value|replaceBr|safe }}
+
+ {% else %}
+ {{ record.value|replaceBr|safe }}
+ {% endif %}
+ {%- endfor -%}
{% else %}
- {{ column.value|safe }}
+ {{ column.value|replaceBr|safe }}
{% endif %}
{%- endfor -%}
diff --git a/packages/utils/lib/utils.ts b/packages/utils/lib/utils.ts
index 5bf1cf821..4d4b1be75 100644
--- a/packages/utils/lib/utils.ts
+++ b/packages/utils/lib/utils.ts
@@ -292,7 +292,7 @@ function _digit2(number: number) {
}
export function formatSeconds(_seconds = '0') {
- const seconds = parseInt(_seconds, 10);
+ const seconds = +_seconds;
return '{0}:{1}:{2}'.format(
_digit2(Math.floor(seconds / 3600)),
_digit2(Math.floor((seconds % 3600) / 60)),