core: contestScoreboardRow method

This commit is contained in:
undefined committed 2022-09-14 21:38:55 +08:00
1 parent f5828e5cb3
commit 7b112817c6
2 files changed
+106 -80

No files matched your search

+5
View File
@@ -505,6 +505,11 @@ export interface ContestRule<T = any> {
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
tdoc: Tdoc<30>, pdict: ProblemDict,
) => 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: (
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
tdoc: Tdoc<30>, pdict: ProblemDict, cursor: Cursor<ContestStat & T>, page: number,
+101 -80
View File
@@ -34,6 +34,8 @@ interface AcmDetail extends AcmJournal {
function buildContestRule<T>(def: ContestRule<T>): ContestRule<T> {
const _originalRule = { scoreboard: def.scoreboard, stat: def.stat };
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._originalRule = _originalRule;
return def;
@@ -110,39 +112,14 @@ const acm = buildContestRule({
}
return columns;
},
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];
for (const [rank, tsdoc] of rankedTsdocs) {
async scoreboardRow(isExport, _, tdoc, pdict, udoc, rank, tsdoc, meta) {
const tsddict: Record<number, AcmDetail> = {};
for (const item of tsdoc.detail || []) tsddict[item.pid] = item;
const row: ScoreboardRow = [
{ 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({
type: 'time',
value: `${tsdoc.accept || 0}\n${formatSeconds(tsdoc.time || 0.0, false)}`,
@@ -169,14 +146,46 @@ const acm = buildContestRule({
: '',
hover: accept ? formatSeconds(doc.time) : '',
raw: doc.rid,
style: accept && doc.rid.generationTime === first[pid]
style: accept && doc.rid.generationTime === meta?.first?.[pid]
? 'background-color: rgb(217, 240, 199);'
: 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];
},
async ranked(tdoc, cursor) {
@@ -224,6 +233,45 @@ const oi = buildContestRule({
}
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) {
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
@@ -251,46 +299,14 @@ const oi = buildContestRule({
}
}
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
const rows: ScoreboardNode[][] = [columns];
for (const [rank, tsdoc] of rankedTsdocs) {
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: udict[tsdoc.uid].uname, raw: tsdoc.uid },
const rows: ScoreboardRow[] = [
columns,
...await Promise.all(rankedTsdocs.map(
([rank, tsdoc]) => this.scoreboardRow(
isExport, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc, { psdict, first },
),
)),
];
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];
},
async ranked(tdoc, cursor) {
@@ -391,13 +407,7 @@ const homework = buildContestRule({
}
return columns;
},
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];
for (const [rank, tsdoc] of rankedTsdocs) {
async scoreboardRow(isExport, _, tdoc, pdict, udoc, rank, tsdoc) {
const tsddict = {};
for (const item of tsdoc.detail || []) {
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: 'user',
value: udict[tsdoc.uid].uname,
value: udoc.uname,
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];
},
async ranked(tdoc, cursor) {