Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/hydrooj/locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ homework_main: Homework
homework_scoreboard: Scoreboard
judge_playground: Judge Playground
main: Home
manage_rejudge: Rejudge Management
manage_config: System Configurations
manage_dashboard: System
manage_module: Module Management
Expand Down
1 change: 1 addition & 0 deletions packages/hydrooj/locales/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ Logout: 登出
Lost Password: 忘记密码
Lucky: 手气不错
Mail From: 发件人
manage_rejudge: 重测管理
manage_dashboard: 控制面板
manage_join_applications: 加域申请
manage_module: 管理模块
Expand Down
1 change: 1 addition & 0 deletions packages/hydrooj/locales/zh_TW.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ Logout All Sessions: 登出所有會話
Logout This Session: 登出該會話
Logout: 登出
Lost Password: 忘記密碼
manage_rejudge: 重測管理
manage_dashboard: 概況
manage_domain: 管理域
manage_edit: 編輯域資料
Expand Down
133 changes: 130 additions & 3 deletions packages/hydrooj/src/handler/manage.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { exec } from 'child_process';
import { inspect } from 'util';
import * as yaml from 'js-yaml';
import { omit } from 'lodash';
import { omit, pick } from 'lodash';
import moment from 'moment';
import { Filter, ObjectId } from 'mongodb';
import Schema from 'schemastery';
import { Time } from '@hydrooj/utils';
import {
CannotEditSuperAdminError, NotLaunchedByPM2Error, UserNotFoundError, ValidationError,
CannotEditSuperAdminError, ContestNotFoundError, NotLaunchedByPM2Error, ProblemNotFoundError,
RecordNotFoundError, UserNotFoundError, ValidationError,
} from '../error';
import { RecordDoc } from '../interface';
import { Logger } from '../logger';
import { PRIV, STATUS } from '../model/builtin';
import { NORMAL_STATUS, PRIV, STATUS } from '../model/builtin';
import * as contest from '../model/contest';
import domain from '../model/domain';
import problem from '../model/problem';
import record from '../model/record';
import * as setting from '../model/setting';
import * as system from '../model/system';
Expand Down Expand Up @@ -357,6 +364,124 @@ class SystemUserPrivHandler extends SystemHandler {
}
}

class SystemRejudgeHandler extends SystemHandler {
async get() {
this.response.body = {
rrdocs: await record.getMultiRejudgeTask(undefined, {}),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use consistent variable naming for rejudge task documents

The variable name rrdocs suggests "rejudge result documents" but it's actually storing rejudge task documents.

-            rrdocs: await record.getMultiRejudgeTask(undefined, {}),
+            tasks: await record.getMultiRejudgeTask(undefined, {}),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rrdocs: await record.getMultiRejudgeTask(undefined, {}),
tasks: await record.getMultiRejudgeTask(undefined, {}),
🤖 Prompt for AI Agents
In packages/hydrooj/src/handler/manage.ts at line 370, rename the variable
`rrdocs` to a name that clearly reflects it stores rejudge task documents, such
as `rejudgeTaskDocs` or `rejudgeTasks`, to maintain consistent and descriptive
variable naming.

apply: true,
status: NORMAL_STATUS.filter((i: STATUS) => ![STATUS.STATUS_COMPILE_ERROR, STATUS.STATUS_ACCEPTED].includes(i)).join(','),
};
this.response.template = 'manage_rejudge.html';
}

@param('uidOrName', Types.UidOrName, true)
@param('pid', Types.ProblemId, true)
@param('tid', Types.ObjectId, true)
@param('langs', Types.CommaSeperatedArray, true)
@param('beginAtDate', Types.Date, true)
@param('beginAtTime', Types.Time, true)
@param('endAtDate', Types.Date, true)
@param('endAtTime', Types.Time, true)
@param('status', Types.CommaSeperatedArray, true)
@param('type', Types.Range(['preview', 'rejudge']))
@param('high_priority', Types.Boolean)
@param('apply', Types.Boolean)
async post(
domainId: string, uidOrName?: string, pid?: string | number, tid?: ObjectId,
langs: string[] = [], beginAtDate?: string, beginAtTime?: string, endAtDate?: string,
endAtTime?: string, status: string[] = [], _type = 'rejudge', highPriority = false, _apply = false,
) {
const q: Filter<RecordDoc> = {};
if (uidOrName) {
const udoc = await user.getById(domainId, +uidOrName)
|| await user.getByUname(domainId, uidOrName)
|| await user.getByEmail(domainId, uidOrName);
if (udoc) q.uid = udoc._id;
else throw new UserNotFoundError(uidOrName);
}
if (tid) {
const tdoc = await contest.get(domainId, tid);
if (!tdoc) throw new ContestNotFoundError(domainId, tid);
q.contest = tdoc._id;
}
if (pid) {
const pdoc = await problem.get(domainId, pid);
if (pdoc) q.pid = pdoc.docId;
else throw new ProblemNotFoundError(domainId, pid);
}
if (langs.length) q.lang = { $in: langs.filter((i) => setting.langs[i]) };
let beginAt = null;
let endAt = null;
if (beginAtDate) {
beginAt = moment(`${beginAtDate} ${beginAtTime || '00:00'}`);
if (!beginAt.isValid()) throw new ValidationError('beginAtDate', 'beginAtTime');
q._id ||= {};
q._id = { ...q._id, $gte: Time.getObjectID(beginAt) };
}
if (endAtDate) {
endAt = moment(`${endAtDate} ${endAtTime || '23:59'}`);
if (!endAt.isValid()) throw new ValidationError('endAtDate', 'endAtTime');
q._id ||= {};
q._id = { ...q._id, $lte: Time.getObjectID(endAt) };
}
if (beginAt && endAt && beginAt.isSameOrAfter(endAt)) throw new ValidationError('duration');
const rdocs = await record.getMulti(domainId, q).project({ _id: 1, contest: 1 }).toArray();
if (_type === 'preview') {
this.response.body = {
uidOrName,
pid,
tid,
langs: langs.join(','),
beginAtDate,
beginAtTime,
endAtDate,
endAtTime,
status: status.join(','),
highPriority,
apply: _apply,
recordLength: rdocs.length,
rrdocs: await record.getMultiRejudgeTask(undefined, {}),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use consistent variable naming

Same issue as above - maintain consistent naming convention.

-                rrdocs: await record.getMultiRejudgeTask(undefined, {}),
+                tasks: await record.getMultiRejudgeTask(undefined, {}),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rrdocs: await record.getMultiRejudgeTask(undefined, {}),
tasks: await record.getMultiRejudgeTask(undefined, {}),
🤖 Prompt for AI Agents
In packages/hydrooj/src/handler/manage.ts at line 443, the variable name used in
the call to record.getMultiRejudgeTask is inconsistent with the naming
convention used elsewhere. Rename the variable from undefined to a properly
named variable that matches the established naming pattern in the surrounding
code to maintain consistency.

};
this.response.template = 'manage_rejudge.html';
return;
}
const rid = await record.addRejudgeTask(domainId, {
owner: this.user._id,
apply: _apply,
});
Comment on lines +448 to +451
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add count field when creating rejudge task

The rejudge task should include a count of records to be rejudged.

        const rid = await record.addRejudgeTask(domainId, {
            owner: this.user._id,
            apply: _apply,
+           count: rdocs.length,
        });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const rid = await record.addRejudgeTask(domainId, {
owner: this.user._id,
apply: _apply,
});
const rid = await record.addRejudgeTask(domainId, {
owner: this.user._id,
apply: _apply,
count: rdocs.length,
});
🤖 Prompt for AI Agents
In packages/hydrooj/src/handler/manage.ts around lines 448 to 451, the rejudge
task creation is missing a count field that indicates how many records are to be
rejudged. Modify the object passed to addRejudgeTask to include a count property
with the appropriate number of records to be rejudged, ensuring this count
accurately reflects the intended scope of the rejudge task.

const priority = await record.submissionPriority(this.user._id, (highPriority ? 0 : -10000) - rdocs.length * 5 - 50);
if (_apply) await record.reset(domainId, rdocs.map((rdoc) => rdoc._id), true);
else {
await record.collHistory.insertMany(rdocs.map((rdoc) => ({
...pick(rdoc, [
'compilerTexts', 'judgeTexts', 'testCases', 'subtasks',
'score', 'time', 'memory', 'status', 'judgeAt', 'judger',
]),
rid: rdoc._id,
_id: new ObjectId(),
})));
}
Comment on lines +453 to +463
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix conditional logic for history insertion

The current logic creates history records when NOT applying (!_apply), but based on the context, history should be created before the actual rejudge operation regardless of the apply flag.

The history insertion should happen before rejudging to preserve the original state:

-        if (_apply) await record.reset(domainId, rdocs.map((rdoc) => rdoc._id), true);
-        else {
-            await record.collHistory.insertMany(rdocs.map((rdoc) => ({
-                ...pick(rdoc, [
-                    'compilerTexts', 'judgeTexts', 'testCases', 'subtasks',
-                    'score', 'time', 'memory', 'status', 'judgeAt', 'judger',
-                ]),
-                rid: rdoc._id,
-                _id: new ObjectId(),
-            })));
-        }
+        // Always preserve history before rejudging
+        await record.collHistory.insertMany(rdocs.map((rdoc) => ({
+            ...pick(rdoc, [
+                'compilerTexts', 'judgeTexts', 'testCases', 'subtasks',
+                'score', 'time', 'memory', 'status', 'judgeAt', 'judger',
+            ]),
+            rid: rdoc._id,
+            _id: new ObjectId(),
+        })));
+        
+        if (_apply) await record.reset(domainId, rdocs.map((rdoc) => rdoc._id), true);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (_apply) await record.reset(domainId, rdocs.map((rdoc) => rdoc._id), true);
else {
await record.collHistory.insertMany(rdocs.map((rdoc) => ({
...pick(rdoc, [
'compilerTexts', 'judgeTexts', 'testCases', 'subtasks',
'score', 'time', 'memory', 'status', 'judgeAt', 'judger',
]),
rid: rdoc._id,
_id: new ObjectId(),
})));
}
// Always preserve history before rejudging
await record.collHistory.insertMany(rdocs.map((rdoc) => ({
...pick(rdoc, [
'compilerTexts', 'judgeTexts', 'testCases', 'subtasks',
'score', 'time', 'memory', 'status', 'judgeAt', 'judger',
]),
rid: rdoc._id,
_id: new ObjectId(),
})));
if (_apply) await record.reset(domainId, rdocs.map((rdoc) => rdoc._id), true);
🤖 Prompt for AI Agents
In packages/hydrooj/src/handler/manage.ts around lines 453 to 463, the history
records are only inserted when _apply is false, but history should be recorded
before rejudging regardless of the _apply flag. Refactor the code to always
insert history records before the rejudge operation, then conditionally call
record.reset only if _apply is true.

await Promise.all([
record.judge(domainId, rdocs.filter((i) => i.contest).map((i) => i._id), priority, { detail: false },
{ rejudge: _apply ? true : 'controlled' }),
record.judge(domainId, rdocs.filter((i) => !i.contest).map((i) => i._id), priority, {},
{ rejudge: _apply ? true : 'controlled' }),
]);
this.response.redirect = this.url('manage_rejudge_detail', { rid });
}
}

class SystemRejudgeDetailHandler extends SystemHandler {
@param('rid', Types.ObjectId)
async get(domainId: string, rid: ObjectId) {
const rrdoc = await record.getRejudgeTask(domainId, rid);
const rdocs = await record.getMulti(domainId, { _id: { $in: rrdoc.rids } }).toArray();
if (!rrdoc) throw new RecordNotFoundError(domainId, rid);
this.response.body = { rrdoc, rdocs };
this.response.template = 'manage_rejudge_detail.html';
}
}

export const inject = ['config', 'check'];
export async function apply(ctx) {
ctx.Route('manage', '/manage', SystemMainHandler);
Expand All @@ -366,5 +491,7 @@ export async function apply(ctx) {
ctx.Route('manage_config', '/manage/config', SystemConfigHandler);
ctx.Route('manage_user_import', '/manage/userimport', SystemUserImportHandler);
ctx.Route('manage_user_priv', '/manage/userpriv', SystemUserPrivHandler);
ctx.Route('manage_rejudge', '/manage/rejudge', SystemRejudgeHandler);
ctx.Route('manage_rejudge_detail', '/manage/rejudge/:rid', SystemRejudgeDetailHandler);
ctx.Connection('manage_check', '/manage/check-conn', SystemCheckConnHandler);
}
21 changes: 21 additions & 0 deletions packages/hydrooj/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,26 @@ export interface RecordStatDoc {
lang: string;
}

export interface RecordRejudgeDoc extends Document {
content: string;
docId: ObjectId;
docType: document['TYPE_REJUDGE'];
apply: boolean;
finishAt?: Date;
}

export interface RecordRejudgeResultDoc {
_id: ObjectId;
rrid: ObjectId;
rid: ObjectId;
oldRev: ObjectId;
newRev: ObjectId;
oldStatus: number;
newStatus: number;
oldScore: number;
newScore: number;
}

export interface ScoreboardNode {
type: 'string' | 'rank' | 'user' | 'email' | 'record' | 'records' | 'problem' | 'solved' | 'time' | 'total_score';
value: string; // 显示分数
Expand Down Expand Up @@ -534,6 +554,7 @@ declare module './service/db' {
'record': RecordDoc;
'record.stat': RecordStatDoc;
'record.history': RecordHistoryDoc;
'record.rejudge': RecordRejudgeResultDoc;
'document': any;
'document.status': StatusDocBase & {
[K in keyof DocStatusType]: { docType: K } & DocStatusType[K];
Expand Down
1 change: 1 addition & 0 deletions packages/hydrooj/src/lib/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ inject('ControlPanel', 'manage_dashboard');
inject('ControlPanel', 'manage_script');
inject('ControlPanel', 'manage_user_import');
inject('ControlPanel', 'manage_user_priv');
inject('ControlPanel', 'manage_rejudge');
inject('ControlPanel', 'manage_setting');
inject('ControlPanel', 'manage_config');
inject('DomainManage', 'domain_dashboard', { family: 'Properties', icon: 'info' });
Expand Down
5 changes: 4 additions & 1 deletion packages/hydrooj/src/model/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Context } from '../context';
import {
Content, ContestClarificationDoc, DiscussionDoc,
DiscussionReplyDoc, ProblemDoc, ProblemStatusDoc,
Tdoc, TrainingDoc,
RecordRejudgeDoc, Tdoc, TrainingDoc,
} from '../interface';
import bus from '../service/bus';
import db from '../service/db';
Expand All @@ -29,6 +29,7 @@ export const TYPE_DISCUSSION_REPLY = 22 as const;
export const TYPE_CONTEST = 30 as const;
export const TYPE_CONTEST_CLARIFICATION = 31 as const;
export const TYPE_TRAINING = 40 as const;
export const TYPE_REJUDGE = 50 as const;

export interface DocType {
[TYPE_PROBLEM]: ProblemDoc;
Expand All @@ -40,6 +41,7 @@ export interface DocType {
[TYPE_CONTEST]: Tdoc;
[TYPE_CONTEST_CLARIFICATION]: ContestClarificationDoc;
[TYPE_TRAINING]: TrainingDoc;
[TYPE_REJUDGE]: RecordRejudgeDoc;
}

export interface DocStatusType {
Expand Down Expand Up @@ -487,5 +489,6 @@ global.Hydro.model.document = {
TYPE_PROBLEM,
TYPE_PROBLEM_LIST,
TYPE_PROBLEM_SOLUTION,
TYPE_REJUDGE,
TYPE_TRAINING,
};
32 changes: 31 additions & 1 deletion packages/hydrooj/src/model/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
import { ProblemConfigFile } from '@hydrooj/common';
import { Context } from '../context';
import { ProblemNotFoundError } from '../error';
import { JudgeMeta, RecordDoc } from '../interface';
import { JudgeMeta, RecordDoc, RecordRejudgeDoc } from '../interface';
import db from '../service/db';
import { MaybeArray, NumberKeys } from '../typeutils';
import { ArgMethod, buildProjection, Time } from '../utils';
import { STATUS } from './builtin';
import * as document from './document';
import DomainModel from './domain';
import problem from './problem';
import task from './task';
Expand All @@ -21,6 +22,7 @@ export default class RecordModel {
static coll = db.collection('record');
static collStat = db.collection('record.stat');
static collHistory = db.collection('record.history');
static collRejudge = db.collection('record.rejudge');
static PROJECTION_LIST: (keyof RecordDoc)[] = [
'_id', 'score', 'time', 'memory', 'lang',
'uid', 'pid', 'rejudged', 'progress', 'domainId',
Expand Down Expand Up @@ -280,6 +282,30 @@ export default class RecordModel {
for (const rdoc of rdocs) r[rdoc._id.toHexString()] = rdoc;
return r;
}

static async getMultiRejudgeTask(domainId: string | undefined, query: Filter<RecordRejudgeDoc>) {
return document.getMulti(domainId, document.TYPE_REJUDGE, query).toArray();
}

static async getRejudgeTask(domainId: string, _id: ObjectId) {
return document.get(domainId, document.TYPE_REJUDGE, _id);
}

static async addRejudgeTask(domainId: string, doc: Partial<RecordRejudgeDoc>) {
return await document.add(domainId, '', doc.owner, document.TYPE_REJUDGE, null, null, null, doc);
}

static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add type annotation for the newRev parameter

-    static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev) {
+    static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev: ObjectId) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev) {
static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev: ObjectId) {
🤖 Prompt for AI Agents
In packages/hydrooj/src/model/record.ts at line 298, the method
pushRejudgeResult has a parameter newRev without a type annotation. Add an
appropriate type annotation to the newRev parameter to ensure type safety and
clarity in the method signature.

await RecordModel.collRejudge.updateOne({ _id: rrid }, {
$set: {
newStatus,
newScore,
newRev,
},
}, {
upsert: true,
});
}
Comment on lines +298 to +308
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Method name doesn't reflect its actual behavior

The method name pushRejudgeResult suggests appending to an array, but it's actually performing an upsert operation.

Consider renaming the method to better reflect its behavior:

-    static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev: ObjectId) {
+    static async upsertRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev: ObjectId) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static async pushRejudgeResult(rrid: ObjectId, newStatus: number, newScore: number, newRev) {
await RecordModel.collRejudge.updateOne({ _id: rrid }, {
$set: {
newStatus,
newScore,
newRev,
},
}, {
upsert: true,
});
}
static async upsertRejudgeResult(
rrid: ObjectId,
newStatus: number,
newScore: number,
newRev: ObjectId
) {
await RecordModel.collRejudge.updateOne(
{ _id: rrid },
{
$set: {
newStatus,
newScore,
newRev,
},
},
{
upsert: true,
}
);
}
🤖 Prompt for AI Agents
In packages/hydrooj/src/model/record.ts around lines 298 to 308, the method name
pushRejudgeResult is misleading because it suggests appending data, but the
method actually performs an upsert operation on the database. Rename the method
to something more descriptive like upsertRejudgeResult or
updateOrInsertRejudgeResult to accurately reflect its behavior.

}

export async function apply(ctx: Context) {
Expand Down Expand Up @@ -327,6 +353,10 @@ export async function apply(ctx: Context) {
RecordModel.collHistory,
{ key: { rid: 1, _id: -1 }, name: 'basic' },
),
db.ensureIndexes(
RecordModel.collRejudge,
{ key: { domainId: 1, rrid: 1, rid: 1 }, name: 'basic' },
),
]);
}
global.Hydro.model.record = RecordModel;
24 changes: 24 additions & 0 deletions packages/ui-default/pages/manage_rejudge.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { STATUS_TEXTS } from '@hydrooj/common';
import $ from 'jquery';
import CustomSelectAutoComplete from 'vj/components/autocomplete/CustomSelectAutoComplete';
import ProblemSelectAutoComplete from 'vj/components/autocomplete/ProblemSelectAutoComplete';
import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete';
import { NamedPage } from 'vj/misc/Page';

const page = new NamedPage('manage_rejudge', async () => {
UserSelectAutoComplete.getOrConstruct($('[name="uidOrName"]'), {
clearDefaultValue: false,
});
ProblemSelectAutoComplete.getOrConstruct($('[name="pid"]'), {
clearDefaultValue: false,
});
const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0]));
const langs = Object.keys(window.LANGS).filter((i) => !prefixes.has(i)).map((i) => (
{ name: `${i.includes('.') ? `${window.LANGS[i.split('.')[0]].display}/` : ''}${window.LANGS[i].display}`, _id: i }
));
Comment on lines +15 to +18
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Review the language filtering logic

The current logic filters out base languages (like 'cpp') if any variant (like 'cpp.11') exists. This could lead to missing language options in the dropdown.

Consider this alternative approach to include all languages properly:

-  const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0]));
-  const langs = Object.keys(window.LANGS).filter((i) => !prefixes.has(i)).map((i) => (
-    { name: `${i.includes('.') ? `${window.LANGS[i.split('.')[0]].display}/` : ''}${window.LANGS[i].display}`, _id: i }
-  ));
+  const langs = Object.keys(window.LANGS).map((i) => {
+    const parts = i.split('.');
+    const displayName = parts.length > 1 
+      ? `${window.LANGS[parts[0]].display}/${window.LANGS[i].display}`
+      : window.LANGS[i].display;
+    return { name: displayName, _id: i };
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prefixes = new Set(Object.keys(window.LANGS).filter((i) => i.includes('.')).map((i) => i.split('.')[0]));
const langs = Object.keys(window.LANGS).filter((i) => !prefixes.has(i)).map((i) => (
{ name: `${i.includes('.') ? `${window.LANGS[i.split('.')[0]].display}/` : ''}${window.LANGS[i].display}`, _id: i }
));
const langs = Object.keys(window.LANGS).map((i) => {
const parts = i.split('.');
const displayName = parts.length > 1
? `${window.LANGS[parts[0]].display}/${window.LANGS[i].display}`
: window.LANGS[i].display;
return { name: displayName, _id: i };
});
🤖 Prompt for AI Agents
In packages/ui-default/pages/manage_rejudge.page.ts around lines 15 to 18, the
current filtering logic excludes base languages if any variant exists, causing
some languages to be missing from the dropdown. Modify the logic to include all
languages regardless of variants by removing the filtering that excludes base
languages when variants are present. Adjust the mapping to correctly display
language names with their variant prefixes where applicable, ensuring all
languages appear in the dropdown.

CustomSelectAutoComplete.getOrConstruct($('[name=lang]'), { multi: true, data: langs });
const statuses = Object.entries(STATUS_TEXTS).map(([i, j]) => ({ name: j, _id: i }));
CustomSelectAutoComplete.getOrConstruct($('[name=status]'), { multi: true, data: statuses });
});

export default page;
Loading
Loading