vjudge: Provider.Langs (#852)

This commit is contained in:
undefined authored and GitHub committed 2024-08-10 23:14:52 +08:00
1 parent a1d392a409
commit 41e3f59579
8 files changed
+109 -101

No files matched your search

+1
View File
@@ -6,6 +6,7 @@ import { inject } from './lib/ui';
import { Loader } from './loader';
import type { EventMap } from './service/bus';
import type { CheckService } from './service/check';
import type { } from './service/migration';
import type { ConnectionHandler, Handler } from './service/server';
export interface Events<C extends Context = Context> extends cordis.Events<C>, EventMap, ServerEvents<Handler, ConnectionHandler> { }
+15 -2
View File
@@ -25,6 +25,11 @@ export default class MigrationService extends Service {
this.channels[name] = s;
}
dontWait(func: () => Promise<void | boolean>, name: string) {
(func as any).dontWait = name;
return func;
}
async doUpgrade() {
this.called = true;
for (const channel in this.channels) {
@@ -45,8 +50,16 @@ export default class MigrationService extends Service {
continue;
}
logger.info('Upgrading database [%s]: from %d to %d', channel, dbVer, expected);
const result = await func();
if (!result) break;
if ('dontWait' in func) {
logger.info('[Background Task]');
// For those scripts we don't really care if they fail
func().then(() => {
logger.info('Background Task Completed [%s]: from %d to %d', channel, func.dontWait, expected);
}).catch(logger.error);
} else {
const result = await func();
if (!result) break;
}
dbVer++;
await system.set(name, dbVer);
}
+41 -2
View File
@@ -1,8 +1,9 @@
/* eslint-disable no-await-in-loop */
import os from 'os';
import { LangConfig } from '@hydrooj/utils/lib/lang';
import {
Context, db, DomainModel, JudgeHandler, Logger,
ProblemModel, RecordModel, Service, SettingModel, sleep, STATUS, TaskModel, Time,
Context, db, DomainModel, JudgeHandler, Logger, ProblemModel, RecordModel, Service, SettingModel,
sleep, STATUS, SystemModel, TaskModel, Time, yaml,
} from 'hydrooj';
import { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
import providers from './providers/index';
@@ -48,6 +49,10 @@ class AccountService {
await next({ status: STATUS.STATUS_FETCHED });
try {
const langConfig = SettingModel.langs[task.lang];
if (this.Provider.Langs && !langConfig?.validAs?.[this.account.type]) {
end({ status: STATUS.STATUS_COMPILE_ERROR, message: `Language not supported: ${task.lang}` });
return;
}
if (langConfig.validAs?.[this.account.type]) task.lang = langConfig.validAs[this.account.type];
const comment = langConfig.comment;
if (comment) {
@@ -187,11 +192,29 @@ class VJudgeService extends Service {
if (account.enableOn && !account.enableOn.includes(os.hostname())) continue;
this.pool[`${account.type}/${account.handle}`] = new AccountService(provider, account);
}
// FIXME: potential race condition
if (provider.Langs) this.updateLangs(type, provider.Langs);
this[Context.current]?.on('dispose', () => {
// TODO dispose session
});
}
async updateLangs(provider: string, mapping: Record<string, Partial<LangConfig>>) {
const config = yaml.load(SystemModel.get('hydrooj.langs'));
const old = yaml.dump(config);
for (const key in mapping) {
config[key] ||= {
execute: '/bin/echo For remote judge only',
hidden: true,
...mapping[key],
};
config[key].validAs ||= {};
config[key].validAs[provider] = mapping[key].key;
}
const newConfig = yaml.dump(config);
if (old !== newConfig) await SystemModel.set('hydrooj.langs', newConfig);
}
async sync() {
for (const key in this.pool) {
const account = this.pool[key];
@@ -222,6 +245,22 @@ export async function apply(ctx: Context) {
if (process.env.NODE_APP_INSTANCE !== '0') return;
if (process.env.HYDRO_CLI) return;
ctx.plugin(VJudgeService);
ctx.inject(['migration'], async (c) => {
c.migration.registerChannel('vjudge', [
async function init() { }, // eslint-disable-line
c.migration.dontWait(async () => {
const rewrite = (from: string[], to: string) => RecordModel.coll.updateMany({ lang: { $in: from } }, { $set: { lang: to } });
await Promise.all([
rewrite(['csgoj.0', 'poj.1', 'poj.5'], 'c'),
rewrite(['csgoj.1'], 'cc.cc17o2'),
rewrite(['csgoj.3', 'poj.2'], 'java'),
rewrite(['csgoj.6'], 'py.py3'),
rewrite(['csgoj.17'], 'go'),
rewrite(['poj.0', 'poj.4'], 'cc.cc98'),
]);
}, 'update csgoj and poj langs in record collection'),
]);
});
ctx.inject(['vjudge'], async (c) => {
await c.vjudge.start();
for (const [k, v] of Object.entries(providers)) {
+3 -1
View File
@@ -1,3 +1,4 @@
import { LangConfig } from '@hydrooj/utils/lib/lang';
import { JudgeResultBody } from 'hydrooj';
export interface RemoteAccount {
@@ -43,5 +44,6 @@ export interface IBasicProvider {
}
export interface BasicProvider {
new(account: RemoteAccount, save: (data: any) => Promise<void>): IBasicProvider
new(account: RemoteAccount, save: (data: any) => Promise<void>): IBasicProvider;
Langs?: Record<string, Partial<LangConfig>>;
}
+25 -37
View File
@@ -21,40 +21,32 @@ const statusDict = {
11: STATUS.STATUS_COMPILE_ERROR,
};
/* langs
csgoj:
display: CsgOJ
execute: /bin/echo Invalid
hidden: true
remote: csgoj
csgoj.0:
display: C
monaco: c
highlight: c astyle-c
comment: //
csgoj.1:
display: C++
monaco: cpp
highlight: cpp astyle-c
comment: //
csgoj.3:
display: Java
monaco: java
highlight: java astyle-java
comment: //
csgoj.6:
display: Python3
highlight: python
comment: '#'
csgoj.17:
display: Go
highlight: go
comment: //
*/
const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0';
export default class CSGOJProvider extends BasicFetcher implements IBasicProvider {
static Langs = {
c: {
display: 'C',
key: '0',
},
'cc.cc17o2': {
display: 'C++',
key: '1',
},
java: {
display: 'Java',
key: '3',
},
'py.py3': {
display: 'Python3',
key: '6',
},
go: {
display: 'Go',
key: '17',
},
};
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
super(account, 'https://cpc.csgrandeur.cn', 'form', logger, {
headers: { 'User-Agent': userAgent },
@@ -139,17 +131,13 @@ export default class CSGOJProvider extends BasicFetcher implements IBasicProvide
return result.body.rows.map((i) => `P${+i.problem_id}`);
}
async submitProblem(id: string, lang: string, source: string, info, next, end) {
async submitProblem(id: string, lang: string, source: string) {
await this.ensureLogin();
if (!lang.includes('csgoj')) {
end({ status: STATUS.STATUS_COMPILE_ERROR, message: `Language not supported: ${lang}` });
return null;
}
const result = await this.post('/csgoj/Problemset/submit_ajax')
.set('referer', `https://cpc.csgrandeur.cn/csgoj/problemset/submit?pid=${id.split('P')[1]}`)
.send({
pid: id.split('P')[1],
language: lang.split('csgoj.')[1],
language: lang,
source,
});
return result.body.data.solution_id;
+2 -15
View File
@@ -9,6 +9,7 @@ import {
} from 'hydrooj';
import { BasicFetcher } from '../fetch';
import { IBasicProvider, RemoteAccount } from '../interface';
import { VERDICT } from '../verdict';
declare module 'superagent' {
interface Request {
@@ -20,20 +21,6 @@ charset(superagent);
proxy(superagent as any);
const logger = new Logger('remote/hduoj');
const StatusMapping = {
Queuing: STATUS.STATUS_WAITING,
Running: STATUS.STATUS_JUDGING,
Compiling: STATUS.STATUS_COMPILING,
Accepted: STATUS.STATUS_ACCEPTED,
'Presentation Error': STATUS.STATUS_WRONG_ANSWER,
'Runtime Error': STATUS.STATUS_RUNTIME_ERROR,
'Output Limit Exceeded': STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED,
'Wrong Answer': STATUS.STATUS_WRONG_ANSWER,
'Compilation Error': STATUS.STATUS_COMPILE_ERROR,
'Memory Limit Exceeded': STATUS.STATUS_MEMORY_LIMIT_EXCEEDED,
'Time Limit Exceeded': STATUS.STATUS_TIME_LIMIT_EXCEEDED,
};
export default class HDUOJProvider extends BasicFetcher implements IBasicProvider {
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
super(account, 'https://acm.hdu.edu.cn', 'form', logger);
@@ -189,7 +176,7 @@ export default class HDUOJProvider extends BasicFetcher implements IBasicProvide
const { text } = await this.get(`/status.php?first=${id}`);
const { window: { document } } = new JSDOM(text);
const submission = document.querySelector('#fixed_table>table>tbody').children[2];
const status = StatusMapping[submission.children[2].children[0].textContent.trim()]
const status = VERDICT[submission.children[2].children[0].textContent.trim()]
|| STATUS.STATUS_SYSTEM_ERROR;
if (status === STATUS.STATUS_JUDGING) continue;
if (status === STATUS.STATUS_COMPILE_ERROR) {
+20 -44
View File
@@ -10,54 +10,31 @@ import { VERDICT } from '../verdict';
const logger = new Logger('remote/poj');
/* langs
poj:
display: POJ
execute: /bin/echo Invalid
domain:
- poj
poj.0:
display: G++
monaco: cpp
highlight: cpp astyle-c
comment: //
poj.1:
display: GCC
monaco: c
highlight: c astyle-c
comment: //
poj.2:
display: Java
monaco: java
highlight: java astyle-java
comment: //
poj.3:
display: Pascal
monaco: pascal
highlight: pascal
comment: //
poj.4:
display: C++
monaco: cpp
highlight: cpp astyle-c
comment: //
poj.5:
display: C
monaco: c
highlight: c astyle-c
comment: //
poj.6:
display: Fortran
monaco: plain
highlight: plain
*/
const langs = {
default: 'en',
'zh-CN': 'zh',
};
export default class POJProvider extends BasicFetcher implements IBasicProvider {
static Langs = {
'cc.cc98': {
display: 'C++',
key: '0',
},
c: {
display: 'C',
key: '1',
},
java: {
display: 'Java',
key: '2',
},
pas: {
display: 'Pascal',
key: '3',
},
};
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
super(account, 'http://poj.org', 'form', logger);
}
@@ -192,9 +169,8 @@ export default class POJProvider extends BasicFetcher implements IBasicProvider
.map((i) => `P${+i.children[0].innerHTML ? i.children[0].innerHTML : i.children[1].innerHTML}`);
}
async submitProblem(id: string, lang: string, code: string) {
async submitProblem(id: string, language: string, code: string) {
await this.ensureLogin();
const language = lang.includes('poj.') ? lang.split('poj.')[1] : '0';
code = Buffer.from(code).toString('base64');
const { text } = await this.post('/submit').send({
problem_id: id.split('P')[1],
+2
View File
@@ -18,6 +18,8 @@ export const VERDICT = new Proxy<Record<string, STATUS>>({
EXTRA_TEST_PASSED: STATUS.STATUS_ACCEPTED,
COMPILE_ERROR: STATUS.STATUS_COMPILE_ERROR,
'RUNNING_&_JUDGING': STATUS.STATUS_JUDGING,
QUEUING: STATUS.STATUS_WAITING,
RUNNING: STATUS.STATUS_JUDGING,
// Codeforces
'HAPPY_NEW_YEAR!': STATUS.STATUS_ACCEPTED,