mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17: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,
|
||||
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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user