mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
vjudge: Provider.Langs (#852)
This commit is contained in:
1 parent
a1d392a409
commit
41e3f59579
8 files changed
+109
-101
No files matched your search
@@ -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> { }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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>>;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user