mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: contestScoreboardRow method
This commit is contained in:
1 parent
f5828e5cb3
commit
7b112817c6
2 files changed
+106
-80
No files matched your search
@@ -505,6 +505,11 @@ export interface ContestRule<T = any> {
|
|||||||
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||||
tdoc: Tdoc<30>, pdict: ProblemDict,
|
tdoc: Tdoc<30>, pdict: ProblemDict,
|
||||||
) => Promise<ScoreboardRow>;
|
) => Promise<ScoreboardRow>;
|
||||||
|
scoreboardRow: (
|
||||||
|
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||||
|
tdoc: Tdoc<30>, pdict: ProblemDict, udoc: Udoc | User, rank: number, tsdoc: ContestStat & T,
|
||||||
|
meta?: any,
|
||||||
|
) => Promise<ScoreboardRow>;
|
||||||
scoreboard: (
|
scoreboard: (
|
||||||
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||||
tdoc: Tdoc<30>, pdict: ProblemDict, cursor: Cursor<ContestStat & T>, page: number,
|
tdoc: Tdoc<30>, pdict: ProblemDict, cursor: Cursor<ContestStat & T>, page: number,
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ interface AcmDetail extends AcmJournal {
|
|||||||
function buildContestRule<T>(def: ContestRule<T>): ContestRule<T> {
|
function buildContestRule<T>(def: ContestRule<T>): ContestRule<T> {
|
||||||
const _originalRule = { scoreboard: def.scoreboard, stat: def.stat };
|
const _originalRule = { scoreboard: def.scoreboard, stat: def.stat };
|
||||||
def.scoreboard = (def._originalRule?.scoreboard || def.scoreboard).bind(def);
|
def.scoreboard = (def._originalRule?.scoreboard || def.scoreboard).bind(def);
|
||||||
|
def.scoreboardHeader = (def._originalRule?.scoreboardHeader || def.scoreboardHeader).bind(def);
|
||||||
|
def.scoreboardRow = (def._originalRule?.scoreboardRow || def.scoreboardRow).bind(def);
|
||||||
def.stat = (def._originalRule?.stat || def.stat).bind(def);
|
def.stat = (def._originalRule?.stat || def.stat).bind(def);
|
||||||
def._originalRule = _originalRule;
|
def._originalRule = _originalRule;
|
||||||
return def;
|
return def;
|
||||||
@@ -110,39 +112,14 @@ const acm = buildContestRule({
|
|||||||
}
|
}
|
||||||
return columns;
|
return columns;
|
||||||
},
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboardRow(isExport, _, tdoc, pdict, udoc, rank, tsdoc, meta) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score && a.time === b.time, page);
|
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
|
||||||
// Find first accept
|
|
||||||
const first = {};
|
|
||||||
const data = await document.collStatus.aggregate([
|
|
||||||
{
|
|
||||||
$match: {
|
|
||||||
domainId: tdoc.domainId,
|
|
||||||
docType: document.TYPE_CONTEST,
|
|
||||||
docId: tdoc.docId,
|
|
||||||
accept: { $gte: 1 },
|
|
||||||
detail: { $elemMatch: { status: STATUS.STATUS_ACCEPTED } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ $unwind: '$detail' },
|
|
||||||
{ $match: { 'detail.status': STATUS.STATUS_ACCEPTED } },
|
|
||||||
{ $sort: { 'detail.rid': 1 } },
|
|
||||||
{ $group: { _id: '$detail.pid', first: { $first: '$detail.rid' } } },
|
|
||||||
]).toArray();
|
|
||||||
for (const t of data) first[t._id] = t.first.generationTime;
|
|
||||||
|
|
||||||
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
|
||||||
const rows: ScoreboardRow[] = [columns];
|
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
|
||||||
const tsddict: Record<number, AcmDetail> = {};
|
const tsddict: Record<number, AcmDetail> = {};
|
||||||
for (const item of tsdoc.detail || []) tsddict[item.pid] = item;
|
for (const item of tsdoc.detail || []) tsddict[item.pid] = item;
|
||||||
const row: ScoreboardRow = [
|
const row: ScoreboardRow = [
|
||||||
{ type: 'rank', value: rank.toString() },
|
{ type: 'rank', value: rank.toString() },
|
||||||
{ type: 'user', value: udict[tsdoc.uid].uname, raw: tsdoc.uid },
|
{ type: 'user', value: udoc.uname, raw: tsdoc.uid },
|
||||||
];
|
];
|
||||||
if (isExport) row.push({ type: 'email', value: udict[tsdoc.uid].mail });
|
if (isExport) row.push({ type: 'email', value: udoc.mail });
|
||||||
row.push({
|
row.push({
|
||||||
type: 'time',
|
type: 'time',
|
||||||
value: `${tsdoc.accept || 0}\n${formatSeconds(tsdoc.time || 0.0, false)}`,
|
value: `${tsdoc.accept || 0}\n${formatSeconds(tsdoc.time || 0.0, false)}`,
|
||||||
@@ -169,14 +146,46 @@ const acm = buildContestRule({
|
|||||||
: '',
|
: '',
|
||||||
hover: accept ? formatSeconds(doc.time) : '',
|
hover: accept ? formatSeconds(doc.time) : '',
|
||||||
raw: doc.rid,
|
raw: doc.rid,
|
||||||
style: accept && doc.rid.generationTime === first[pid]
|
style: accept && doc.rid.generationTime === meta?.first?.[pid]
|
||||||
? 'background-color: rgb(217, 240, 199);'
|
? 'background-color: rgb(217, 240, 199);'
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows.push(row);
|
return row;
|
||||||
}
|
},
|
||||||
|
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
||||||
|
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score && a.time === b.time, page);
|
||||||
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
|
// Find first accept
|
||||||
|
const first = {};
|
||||||
|
const data = await document.collStatus.aggregate([
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
domainId: tdoc.domainId,
|
||||||
|
docType: document.TYPE_CONTEST,
|
||||||
|
docId: tdoc.docId,
|
||||||
|
accept: { $gte: 1 },
|
||||||
|
detail: { $elemMatch: { status: STATUS.STATUS_ACCEPTED } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ $unwind: '$detail' },
|
||||||
|
{ $match: { 'detail.status': STATUS.STATUS_ACCEPTED } },
|
||||||
|
{ $sort: { 'detail.rid': 1 } },
|
||||||
|
{ $group: { _id: '$detail.pid', first: { $first: '$detail.rid' } } },
|
||||||
|
]).toArray();
|
||||||
|
for (const t of data) first[t._id] = t.first.generationTime;
|
||||||
|
|
||||||
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
|
const rows: ScoreboardRow[] = [
|
||||||
|
columns,
|
||||||
|
...await Promise.all(rankedTsdocs.map(
|
||||||
|
([rank, tsdoc]) => this.scoreboardRow(
|
||||||
|
isExport, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc, { first },
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
];
|
||||||
return [rows, udict, nPages];
|
return [rows, udict, nPages];
|
||||||
},
|
},
|
||||||
async ranked(tdoc, cursor) {
|
async ranked(tdoc, cursor) {
|
||||||
@@ -224,6 +233,45 @@ const oi = buildContestRule({
|
|||||||
}
|
}
|
||||||
return columns;
|
return columns;
|
||||||
},
|
},
|
||||||
|
async scoreboardRow(isExport, _, tdoc, pdict, udoc, rank, tsdoc, meta) {
|
||||||
|
const tsddict = {};
|
||||||
|
for (const item of tsdoc.journal || []) {
|
||||||
|
if (!tsddict[item.pid] || tsddict[item.pid].score < item.score || this.submitAfterAccept) tsddict[item.pid] = item;
|
||||||
|
}
|
||||||
|
const row: ScoreboardNode[] = [
|
||||||
|
{ type: 'rank', value: rank.toString() },
|
||||||
|
{ type: 'user', value: udoc.uname, raw: tsdoc.uid },
|
||||||
|
];
|
||||||
|
if (isExport) row.push({ type: 'email', value: udoc.mail });
|
||||||
|
row.push({ type: 'total_score', value: tsdoc.score || 0 });
|
||||||
|
for (const pid of tdoc.pids) {
|
||||||
|
const index = `${tsdoc.uid}/${tdoc.domainId}/${pid}`;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||||
|
const node: ScoreboardNode = (!isExport && isDone(tdoc)
|
||||||
|
&& meta?.psdict?.[index]?.rid
|
||||||
|
&& tsddict[pid]?.rid?.toHexString() !== meta?.psdict?.[index]?.rid?.toHexString())
|
||||||
|
? {
|
||||||
|
type: 'records',
|
||||||
|
value: '',
|
||||||
|
raw: [{
|
||||||
|
value: tsddict[pid]?.score ?? '-',
|
||||||
|
raw: tsddict[pid]?.rid || null,
|
||||||
|
}, {
|
||||||
|
value: meta?.psdict?.[index]?.score ?? '-',
|
||||||
|
raw: meta?.psdict?.[index]?.rid ?? null,
|
||||||
|
}],
|
||||||
|
} : {
|
||||||
|
type: 'record',
|
||||||
|
value: tsddict[pid]?.score ?? '-',
|
||||||
|
raw: tsddict[pid]?.rid || null,
|
||||||
|
};
|
||||||
|
if (tsddict[pid]?.status === STATUS.STATUS_ACCEPTED && tsddict[pid]?.rid.generationTime === meta?.first?.[pid]) {
|
||||||
|
node.style = 'background-color: rgb(217, 240, 199);';
|
||||||
|
}
|
||||||
|
row.push(node);
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
@@ -251,46 +299,14 @@ const oi = buildContestRule({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
const rows: ScoreboardNode[][] = [columns];
|
const rows: ScoreboardRow[] = [
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
columns,
|
||||||
const tsddict = {};
|
...await Promise.all(rankedTsdocs.map(
|
||||||
for (const item of tsdoc.journal || []) {
|
([rank, tsdoc]) => this.scoreboardRow(
|
||||||
if (!tsddict[item.pid] || tsddict[item.pid].score < item.score || this.submitAfterAccept) tsddict[item.pid] = item;
|
isExport, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc, { psdict, first },
|
||||||
}
|
),
|
||||||
const row: ScoreboardNode[] = [
|
)),
|
||||||
{ type: 'rank', value: rank.toString() },
|
|
||||||
{ type: 'user', value: udict[tsdoc.uid].uname, raw: tsdoc.uid },
|
|
||||||
];
|
];
|
||||||
if (isExport) row.push({ type: 'email', value: udict[tsdoc.uid].mail });
|
|
||||||
row.push({ type: 'total_score', value: tsdoc.score || 0 });
|
|
||||||
for (const pid of tdoc.pids) {
|
|
||||||
const index = `${tsdoc.uid}/${tdoc.domainId}/${pid}`;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
||||||
const node: ScoreboardNode = (!isExport && isDone(tdoc)
|
|
||||||
&& psdict[index]?.rid
|
|
||||||
&& tsddict[pid]?.rid?.toHexString() !== psdict[index]?.rid?.toHexString())
|
|
||||||
? {
|
|
||||||
type: 'records',
|
|
||||||
value: '',
|
|
||||||
raw: [{
|
|
||||||
value: tsddict[pid]?.score ?? '-',
|
|
||||||
raw: tsddict[pid]?.rid || null,
|
|
||||||
}, {
|
|
||||||
value: psdict[index]?.score ?? '-',
|
|
||||||
raw: psdict[index]?.rid ?? null,
|
|
||||||
}],
|
|
||||||
} : {
|
|
||||||
type: 'record',
|
|
||||||
value: tsddict[pid]?.score ?? '-',
|
|
||||||
raw: tsddict[pid]?.rid || null,
|
|
||||||
};
|
|
||||||
if (tsddict[pid]?.status === STATUS.STATUS_ACCEPTED && tsddict[pid]?.rid.generationTime === first[pid]) {
|
|
||||||
node.style = 'background-color: rgb(217, 240, 199);';
|
|
||||||
}
|
|
||||||
row.push(node);
|
|
||||||
}
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
return [rows, udict, nPages];
|
return [rows, udict, nPages];
|
||||||
},
|
},
|
||||||
async ranked(tdoc, cursor) {
|
async ranked(tdoc, cursor) {
|
||||||
@@ -391,13 +407,7 @@ const homework = buildContestRule({
|
|||||||
}
|
}
|
||||||
return columns;
|
return columns;
|
||||||
},
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboardRow(isExport, _, tdoc, pdict, udoc, rank, tsdoc) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
|
||||||
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
|
||||||
const rows: ScoreboardRow[] = [columns];
|
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
|
||||||
const tsddict = {};
|
const tsddict = {};
|
||||||
for (const item of tsdoc.detail || []) {
|
for (const item of tsdoc.detail || []) {
|
||||||
if (!tsddict[item.pid] || tsddict[item.pid].score <= item.score) tsddict[item.pid] = item;
|
if (!tsddict[item.pid] || tsddict[item.pid].score <= item.score) tsddict[item.pid] = item;
|
||||||
@@ -406,7 +416,7 @@ const homework = buildContestRule({
|
|||||||
{ type: 'rank', value: rank.toString() },
|
{ type: 'rank', value: rank.toString() },
|
||||||
{
|
{
|
||||||
type: 'user',
|
type: 'user',
|
||||||
value: udict[tsdoc.uid].uname,
|
value: udoc.uname,
|
||||||
raw: tsdoc.uid,
|
raw: tsdoc.uid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -441,8 +451,19 @@ const homework = buildContestRule({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows.push(row);
|
return row;
|
||||||
}
|
},
|
||||||
|
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
||||||
|
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
||||||
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
|
const rows: ScoreboardRow[] = [
|
||||||
|
columns,
|
||||||
|
...await Promise.all(rankedTsdocs.map(
|
||||||
|
([rank, tsdoc]) => this.scoreboardRow(isExport, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc),
|
||||||
|
)),
|
||||||
|
];
|
||||||
return [rows, udict, nPages];
|
return [rows, udict, nPages];
|
||||||
},
|
},
|
||||||
async ranked(tdoc, cursor) {
|
async ranked(tdoc, cursor) {
|
||||||
|
|||||||
Reference in new issue
Block a user