Skip to content

Commit

Permalink
合并更改
Browse files Browse the repository at this point in the history
  • Loading branch information
hankeke303 committed Nov 15, 2023
2 parents d57b444 + 573796f commit 5ffb07e
Show file tree
Hide file tree
Showing 20 changed files with 479 additions and 28 deletions.
3 changes: 2 additions & 1 deletion config-example.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,6 @@
"renderer_cache_size": "30000",
"divine": true,
"icp_beian_id": "@ICP_BEIAN_ID@",
"icp_beian_url": "@ICP_BEIAN_URL@"
"icp_beian_url": "@ICP_BEIAN_URL@",
"luogu_openapi_token": ""
}
2 changes: 1 addition & 1 deletion libs/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ if (syzoj.config.email.method === "sendmail") {

doSendEmail = async function send_smtp(to, subject, body) {
await transporter.sendMailAsync({
from: `"${syzoj.config.title}" <${syzoj.config.email.options.username}>`,
from: `"${syzoj.config.title}" <${syzoj.config.email.options.address || syzoj.config.email.options.username}>`,
to: to,
subject: subject,
html: body
Expand Down
84 changes: 84 additions & 0 deletions libs/interfaces.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
interface TestcaseDetails {
type: TestcaseResultType;
time: number;
memory: number;
input?: FileContent;
output?: FileContent; // Output in test data
scoringRate: number; // e.g. 0.5
userOutput?: string;
userError?: string;
spjMessage?: string;
systemMessage?: string;
}

interface TestcaseResult {
status: TaskStatus;
result?: TestcaseDetails;
errorMessage?: string;
}

interface SubtaskResult {
score?: number;
cases: TestcaseResult[];
}

declare enum ErrorType {
SystemError,
TestDataError
}

interface CompilationResult {
status: TaskStatus;
message?: string;
}

interface JudgeResult {
subtasks?: SubtaskResult[];
}

interface OverallResult {
error?: ErrorType;
systemMessage?: string;
compile?: CompilationResult;
judge?: JudgeResult;
}

declare enum TaskStatus {
Waiting = 0,
Running = 1,
Done = 2,
Failed = 3,
Skipped = 4
}

declare enum TestcaseResultType {
Accepted = 1,
WrongAnswer = 2,
PartiallyCorrect = 3,
MemoryLimitExceeded = 4,
TimeLimitExceeded = 5,
OutputLimitExceeded = 6,
FileError = 7, // The output file does not exist
RuntimeError = 8,
JudgementFailed = 9, // Special Judge or Interactor fails
InvalidInteraction = 10
}

interface FileContent {
content: string,
name: string
}

declare enum ProgressReportType {
Started = 1,
Compiled = 2,
Progress = 3,
Finished = 4,
Reported = 5,
}

interface ProgressReportData {
taskId: string;
type: ProgressReportType;
progress: OverallResult | CompilationResult;
}
44 changes: 44 additions & 0 deletions libs/judger.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fs = require('fs-extra');
const FastPriorityQueue = require('fastpriorityqueue');
const interface = require('./judger_interfaces');
const judgeResult = require('./judgeResult');
const vjudge = require("./vjudge");

const judgeStateCache = new Map();
const progressPusher = require('../modules/socketio');
Expand Down Expand Up @@ -218,6 +219,49 @@ async function connect() {
module.exports.connect = connect;

module.exports.judge = async function (judge_state, problem, priority) {
if (problem.type.startsWith("vjudge:")) {
progressPusher.createTask(judge_state.task_id);
judgeStateCache.set(judge_state.task_id, {
result: 'Pending',
score: 0,
time: 0,
memory: 0
});

return vjudge(judge_state, problem, async progress => {
console.log(progress);
if (progress.type === interface.ProgressReportType.Compiled) {
progressPusher.updateCompileStatus(progress.taskId, progress.progress);
} else if (progress.type === interface.ProgressReportType.Progress) {
const convertedResult = judgeResult.convertResult(progress.taskId, progress.progress);
judgeStateCache.set(progress.taskId, {
result: getRunningTaskStatusString(progress.progress),
score: convertedResult.score,
time: convertedResult.time,
memory: convertedResult.memory
});
progressPusher.updateProgress(progress.taskId, progress.progress);
} else if (progress.type === interface.ProgressReportType.Finished) {
progressPusher.updateResult(progress.taskId, progress.progress);
setTimeout(async () => {
judgeStateCache.delete(progress.taskId);
progressPusher.cleanupProgress(progress.taskId);

const convertedResult = judgeResult.convertResult(progress.taskId, progress.progress);
judge_state.score = convertedResult.score;
judge_state.pending = false;
judge_state.status = convertedResult.statusString;
judge_state.total_time = convertedResult.time;
judge_state.max_memory = convertedResult.memory;
judge_state.result = convertedResult.result;
judge_state.compilation = progress.progress.compile;
await judge_state.save();
await judge_state.updateRelatedInfo(false);
}, 5000);
}
});
}

let type, param, extraData = null;
switch (problem.type) {
case 'submit-answer':
Expand Down
2 changes: 1 addition & 1 deletion libs/submissions_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const getSubmissionInfo = (s, displayConfig) => ({
userId: s.user_id,
problemName: s.problem.title,
problemId: s.problem_id,
language: displayConfig.showCode ? ((s.language != null && s.language !== '') ? syzoj.languages[s.language].show : null) : null,
language: displayConfig.showCode ? ((s.language != null && s.language !== '') ? (s.problem.getVJudgeLanguages() || syzoj.languages)[s.language].show : null) : null,
codeSize: displayConfig.showCode ? s.code_length : null,
submitTime: syzoj.utils.formatDate(s.submit_time),
});
Expand Down
9 changes: 9 additions & 0 deletions libs/vjudge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const luogu = require("./vjudge/lugou");

module.exports = function vjudge(judge_state, problem, onProgress) {
if (problem.type === "vjudge:luogu") return luogu(judge_state, problem, onProgress);
};

module.exports.languages = {
luogu: luogu.languages
};
166 changes: 166 additions & 0 deletions libs/vjudge/lugou.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/// <reference types="../interfaces" />

const { LuoguOpenApiClient } = require("@menci/luogu-openapi");

/**
* @type {LuoguOpenApiClient}
*/
let client;

const onUpstreamProgressCallbacks = new Map();

/**
* @param {string} trackId
* @param {JudgeRecord} data
*/
function onJudgeProgress(trackId, data) {
const f = onUpstreamProgressCallbacks.get(trackId);
if (!f) return;
f(trackId, data);
}

/**
* @param {(progress: ProgressReportData) => void} onProgress
*/
module.exports = async function vjudge(judge_state, problem, onProgress) {
if (!client) {
client = new LuoguOpenApiClient(syzoj.config.luogu_openapi_token, onJudgeProgress);
}

/**
* @param {JudgeRecord} data
*/
function onUpstreamProgress(trackId, data) {
const compile = data.compile ? {
status: data.compile.success ? /* Done */ 2 : /* Failed */ 3,
message: data.compile.message
} : null;
const judge = data.compile && data.compile.success && data.judge ? {
subtasks: data.judge.subtasks.map(subtask => ({
score: subtask.score,
cases: subtask.cases.map(testcase => {
const status = {
[/* Waiting */ 0]: /* Waiting */ 0,
[/* Judging */ 1]: /* Running */ 1,
[/* CompileError */ 2]: /* Failed */ 3,
[/* OutputLimitExceeded */ 3]: /* Done */ 2,
[/* MemoryLimitExceeded */ 4]: /* Done */ 2,
[/* TimeLimitExceeded */ 5]: /* Done */ 2,
[/* WrongAnswer */ 6]: /* Done */ 2,
[/* RuntimeError */ 7]: /* Done */ 2,
[/* Invalid */ 11]: /* Skipped */ 4,
[/* Accepted */ 12]: /* Done */ 2,
[/* OverallUnaccepted */ 14]: /* Done */ 2
}[testcase.status];
return {
status: status,
result: status === /* Done */ 2 ? {
type: {
[/* Waiting */ 0]: -1,
[/* Judging */ 1]: -1,
[/* CompileError */ 2]: -1,
[/* OutputLimitExceeded */ 3]: /* OutputLimitExceeded */ 6,
[/* MemoryLimitExceeded */ 4]: /* MemoryLimitExceeded */ 4,
[/* TimeLimitExceeded */ 5]: /* TimeLimitExceeded */ 5,
[/* WrongAnswer */ 6]: /* WrongAnswer */ 2,
[/* RuntimeError */ 7]: /* RuntimeError */ 8,
[/* Invalid */ 11]: -1,
[/* Accepted */ 12]: /* Accepted */ 1,
[/* OverallUnaccepted */ 14]: /* PartiallyCorrect */ 3
}[testcase.status],
time: testcase.time,
memory: testcase.memory,
scoringRate: testcase.score / 100,
spjMessage: testcase.description,
systemMessage: JSON.stringify(testcase, null, 2)
} : null
}
})
}))
} : null;
let finished = false;
if (compile && judge) {
finished = ![/* Waiting */ 1, /* Judging */ 2].includes(data.judge.status);
onProgress({
taskId: judge_state.task_id,
type: finished ? /* Finished */ 4 : /* Progress */ 3,
progress: { compile: compile, judge: judge }
});
} else if (compile) {
finished = !data.compile.success;
if (finished) {
onProgress({
taskId: judge_state.task_id,
type: /* Finished */ 4,
progress: { compile: compile }
});
} else {
onProgress({
taskId: judge_state.task_id,
type: /* Compiled */ 2,
progress: compile
});
}
}

if (finished) onUpstreamProgressCallbacks.delete(trackId);
}

try {
const trackId = await client.submit({
pid: problem.vjudge_config,
lang: judge_state.language,
o2: true,
code: judge_state.code
});
onUpstreamProgressCallbacks.set(trackId, onUpstreamProgress);
} catch (e) {
onProgress({
taskId: judge_state.task_id,
type: /* Finished */ 4,
progress: {
error: /* SystemError */ 0,
systemMessage: e.stack
}
});
}
};

const languages = {
"cxx/noi/202107": "ISO C++14 w/ GCC 9.3.0",
"cxx/98/gcc": "ISO C++98 (GCC)",
"cxx/11/gcc": "ISO C++11 (GCC)",
"cxx/14/gcc": "ISO C++14 (GCC)",
"cxx/17/gcc": "ISO C++17 (GCC)",
"cxx/20/gcc ": "ISO C++20 (GCC)",
"c/99/gcc": "ISO C99 (GCC)",
"python3/c": "Python 3 (CPython)",
"python3/py": "Python 3 (PyPy)",
"pascal/fpc": "Pascal",
"rust/rustc": "Rust nightly (rustc)",
"haskell/ghc": "Haskell (GHC)",
"go": "Go",
"php": "PHP",
"ruby": "Ruby",
"js/node/lts": "Node.js LTS",
"perl": "Perl",
"java/8": "Java 8",
"kotlin/jvm": "Kotlin/JVM",
"scala": "Scala",
};

module.exports.languages = {};
let i = 0;
for (const l in languages) {
let base = l.split("/")[0];
if (base === "cxx") base = "cpp";
if (base.startsWith("python")) base = "python";
const lang = {
index: i++,
show: languages[l],
highlight: base,
editor: base
};
if (base === "cpp" || base === "c") lang.format = base;
module.exports.languages[l] = lang;
}
13 changes: 11 additions & 2 deletions models/problem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const problemTagCache = new LRUCache<number, number[]>({
enum ProblemType {
Traditional = "traditional",
SubmitAnswer = "submit-answer",
Interaction = "interaction"
Interaction = "interaction",
VJudge_Luogu = "vjudge:luogu"
}

const statisticsTypes = {
Expand Down Expand Up @@ -116,6 +117,9 @@ export default class Problem extends Model {
})
type: ProblemType;

@TypeORM.Column({ nullable: true, type: "varchar", length: 80 })
vjudge_config: string;

user?: User;
publicizer?: User;
additional_file?: File;
Expand Down Expand Up @@ -307,7 +311,7 @@ export default class Problem extends Model {
if (this.time_limit > syzoj.config.limit.time_limit) return 'Time limit too large';
if (this.memory_limit <= 0) return 'Invalid memory limit';
if (this.memory_limit > syzoj.config.limit.memory_limit) return 'Memory limit too large';
if (!['traditional', 'submit-answer', 'interaction'].includes(this.type)) return 'Invalid problem type';
if (!Object.values(ProblemType).includes(this.type)) return 'Invalid problem type';

if (this.type === 'traditional') {
let filenameRE = /^[\w \-\+\.]*$/;
Expand Down Expand Up @@ -703,4 +707,9 @@ export default class Problem extends Model {

await this.destroy();
}

getVJudgeLanguages() {
let vjudge = this.type.startsWith("vjudge:") ? this.type.split(":")[1] : null;
return vjudge ? require("../libs/vjudge").languages[vjudge] : null;
}
}
Loading

0 comments on commit 5ffb07e

Please sign in to comment.