mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
vjudge: Provider.Langs (#852)
This commit is contained in:
1 parent
a1d392a409
commit
41e3f59579
8 files changed
+107
-99
No files matched your search
@@ -6,6 +6,7 @@ import { inject } from './lib/ui';
|
|||||||
import { Loader } from './loader';
|
import { Loader } from './loader';
|
||||||
import type { EventMap } from './service/bus';
|
import type { EventMap } from './service/bus';
|
||||||
import type { CheckService } from './service/check';
|
import type { CheckService } from './service/check';
|
||||||
|
import type { } from './service/migration';
|
||||||
import type { ConnectionHandler, Handler } from './service/server';
|
import type { ConnectionHandler, Handler } from './service/server';
|
||||||
|
|
||||||
export interface Events<C extends Context = Context> extends cordis.Events<C>, EventMap, ServerEvents<Handler, ConnectionHandler> { }
|
export interface Events<C extends Context = Context> extends cordis.Events<C>, EventMap, ServerEvents<Handler, ConnectionHandler> { }
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ export default class MigrationService extends Service {
|
|||||||
this.channels[name] = s;
|
this.channels[name] = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dontWait(func: () => Promise<void | boolean>, name: string) {
|
||||||
|
(func as any).dontWait = name;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
async doUpgrade() {
|
async doUpgrade() {
|
||||||
this.called = true;
|
this.called = true;
|
||||||
for (const channel in this.channels) {
|
for (const channel in this.channels) {
|
||||||
@@ -45,8 +50,16 @@ export default class MigrationService extends Service {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
logger.info('Upgrading database [%s]: from %d to %d', channel, dbVer, expected);
|
logger.info('Upgrading database [%s]: from %d to %d', channel, dbVer, expected);
|
||||||
|
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();
|
const result = await func();
|
||||||
if (!result) break;
|
if (!result) break;
|
||||||
|
}
|
||||||
dbVer++;
|
dbVer++;
|
||||||
await system.set(name, dbVer);
|
await system.set(name, dbVer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
import {
|
import {
|
||||||
Context, db, DomainModel, JudgeHandler, Logger,
|
Context, db, DomainModel, JudgeHandler, Logger, ProblemModel, RecordModel, Service, SettingModel,
|
||||||
ProblemModel, RecordModel, Service, SettingModel, sleep, STATUS, TaskModel, Time,
|
sleep, STATUS, SystemModel, TaskModel, Time, yaml,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
import { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
|
import { BasicProvider, IBasicProvider, RemoteAccount } from './interface';
|
||||||
import providers from './providers/index';
|
import providers from './providers/index';
|
||||||
@@ -48,6 +49,10 @@ class AccountService {
|
|||||||
await next({ status: STATUS.STATUS_FETCHED });
|
await next({ status: STATUS.STATUS_FETCHED });
|
||||||
try {
|
try {
|
||||||
const langConfig = SettingModel.langs[task.lang];
|
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];
|
if (langConfig.validAs?.[this.account.type]) task.lang = langConfig.validAs[this.account.type];
|
||||||
const comment = langConfig.comment;
|
const comment = langConfig.comment;
|
||||||
if (comment) {
|
if (comment) {
|
||||||
@@ -187,11 +192,29 @@ class VJudgeService extends Service {
|
|||||||
if (account.enableOn && !account.enableOn.includes(os.hostname())) continue;
|
if (account.enableOn && !account.enableOn.includes(os.hostname())) continue;
|
||||||
this.pool[`${account.type}/${account.handle}`] = new AccountService(provider, account);
|
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', () => {
|
this[Context.current]?.on('dispose', () => {
|
||||||
// TODO dispose session
|
// 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() {
|
async sync() {
|
||||||
for (const key in this.pool) {
|
for (const key in this.pool) {
|
||||||
const account = this.pool[key];
|
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.NODE_APP_INSTANCE !== '0') return;
|
||||||
if (process.env.HYDRO_CLI) return;
|
if (process.env.HYDRO_CLI) return;
|
||||||
ctx.plugin(VJudgeService);
|
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) => {
|
ctx.inject(['vjudge'], async (c) => {
|
||||||
await c.vjudge.start();
|
await c.vjudge.start();
|
||||||
for (const [k, v] of Object.entries(providers)) {
|
for (const [k, v] of Object.entries(providers)) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { LangConfig } from '@hydrooj/utils/lib/lang';
|
||||||
import { JudgeResultBody } from 'hydrooj';
|
import { JudgeResultBody } from 'hydrooj';
|
||||||
|
|
||||||
export interface RemoteAccount {
|
export interface RemoteAccount {
|
||||||
@@ -43,5 +44,6 @@ export interface IBasicProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface BasicProvider {
|
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>>;
|
||||||
}
|
}
|
||||||
@@ -21,40 +21,32 @@ const statusDict = {
|
|||||||
11: STATUS.STATUS_COMPILE_ERROR,
|
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';
|
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 {
|
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>) {
|
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
||||||
super(account, 'https://cpc.csgrandeur.cn', 'form', logger, {
|
super(account, 'https://cpc.csgrandeur.cn', 'form', logger, {
|
||||||
headers: { 'User-Agent': userAgent },
|
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}`);
|
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();
|
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')
|
const result = await this.post('/csgoj/Problemset/submit_ajax')
|
||||||
.set('referer', `https://cpc.csgrandeur.cn/csgoj/problemset/submit?pid=${id.split('P')[1]}`)
|
.set('referer', `https://cpc.csgrandeur.cn/csgoj/problemset/submit?pid=${id.split('P')[1]}`)
|
||||||
.send({
|
.send({
|
||||||
pid: id.split('P')[1],
|
pid: id.split('P')[1],
|
||||||
language: lang.split('csgoj.')[1],
|
language: lang,
|
||||||
source,
|
source,
|
||||||
});
|
});
|
||||||
return result.body.data.solution_id;
|
return result.body.data.solution_id;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
import { BasicFetcher } from '../fetch';
|
import { BasicFetcher } from '../fetch';
|
||||||
import { IBasicProvider, RemoteAccount } from '../interface';
|
import { IBasicProvider, RemoteAccount } from '../interface';
|
||||||
|
import { VERDICT } from '../verdict';
|
||||||
|
|
||||||
declare module 'superagent' {
|
declare module 'superagent' {
|
||||||
interface Request {
|
interface Request {
|
||||||
@@ -20,20 +21,6 @@ charset(superagent);
|
|||||||
proxy(superagent as any);
|
proxy(superagent as any);
|
||||||
const logger = new Logger('remote/hduoj');
|
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 {
|
export default class HDUOJProvider extends BasicFetcher implements IBasicProvider {
|
||||||
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
||||||
super(account, 'https://acm.hdu.edu.cn', 'form', logger);
|
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 { text } = await this.get(`/status.php?first=${id}`);
|
||||||
const { window: { document } } = new JSDOM(text);
|
const { window: { document } } = new JSDOM(text);
|
||||||
const submission = document.querySelector('#fixed_table>table>tbody').children[2];
|
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;
|
|| STATUS.STATUS_SYSTEM_ERROR;
|
||||||
if (status === STATUS.STATUS_JUDGING) continue;
|
if (status === STATUS.STATUS_JUDGING) continue;
|
||||||
if (status === STATUS.STATUS_COMPILE_ERROR) {
|
if (status === STATUS.STATUS_COMPILE_ERROR) {
|
||||||
|
|||||||
@@ -10,54 +10,31 @@ import { VERDICT } from '../verdict';
|
|||||||
|
|
||||||
const logger = new Logger('remote/poj');
|
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 = {
|
const langs = {
|
||||||
default: 'en',
|
default: 'en',
|
||||||
'zh-CN': 'zh',
|
'zh-CN': 'zh',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class POJProvider extends BasicFetcher implements IBasicProvider {
|
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>) {
|
constructor(public account: RemoteAccount, private save: (data: any) => Promise<void>) {
|
||||||
super(account, 'http://poj.org', 'form', logger);
|
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}`);
|
.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();
|
await this.ensureLogin();
|
||||||
const language = lang.includes('poj.') ? lang.split('poj.')[1] : '0';
|
|
||||||
code = Buffer.from(code).toString('base64');
|
code = Buffer.from(code).toString('base64');
|
||||||
const { text } = await this.post('/submit').send({
|
const { text } = await this.post('/submit').send({
|
||||||
problem_id: id.split('P')[1],
|
problem_id: id.split('P')[1],
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const VERDICT = new Proxy<Record<string, STATUS>>({
|
|||||||
EXTRA_TEST_PASSED: STATUS.STATUS_ACCEPTED,
|
EXTRA_TEST_PASSED: STATUS.STATUS_ACCEPTED,
|
||||||
COMPILE_ERROR: STATUS.STATUS_COMPILE_ERROR,
|
COMPILE_ERROR: STATUS.STATUS_COMPILE_ERROR,
|
||||||
'RUNNING_&_JUDGING': STATUS.STATUS_JUDGING,
|
'RUNNING_&_JUDGING': STATUS.STATUS_JUDGING,
|
||||||
|
QUEUING: STATUS.STATUS_WAITING,
|
||||||
|
RUNNING: STATUS.STATUS_JUDGING,
|
||||||
|
|
||||||
// Codeforces
|
// Codeforces
|
||||||
'HAPPY_NEW_YEAR!': STATUS.STATUS_ACCEPTED,
|
'HAPPY_NEW_YEAR!': STATUS.STATUS_ACCEPTED,
|
||||||
|
|||||||
Reference in new issue
Block a user