feat(problem/remote): add uoj

This commit is contained in:
Baoshuo Ren 2023-01-22 17:00:12 +08:00
parent 9611ea35da
commit 6e945ef711
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
3 changed files with 138 additions and 4 deletions

View File

@ -35,6 +35,14 @@ $new_remote_problem_form->addInput('remote_problem_id', [
$vdata['remote_problem_id'] = $id; $vdata['remote_problem_id'] = $id;
return '';
} elseif ($remote_oj === 'uoj') {
if (!validateUInt($id)) {
return '不合法的题目 ID';
}
$vdata['remote_problem_id'] = $id;
return ''; return '';
} }

View File

@ -218,3 +218,66 @@ function retry_loop(callable $f, $retry = 5, $ms = 10) {
} }
return $ret; return $ret;
} }
function getAbsoluteUrl($relativeUrl, $baseUrl) {
// if already absolute URL
if (parse_url($relativeUrl, PHP_URL_SCHEME) !== null) {
return $relativeUrl;
}
// queries and anchors
if ($relativeUrl[0] === '#' || $relativeUrl[0] === '?') {
return $baseUrl . $relativeUrl;
}
// parse base URL and convert to: $scheme, $host, $path, $query, $port, $user, $pass
extract(parse_url($baseUrl));
// if base URL contains a path remove non-directory elements from $path
if (isset($path) === true) {
$path = preg_replace('#/[^/]*$#', '', $path);
} else {
$path = '';
}
// if realtive URL starts with //
if (substr($relativeUrl, 0, 2) === '//') {
return $scheme . ':' . $relativeUrl;
}
// if realtive URL starts with /
if ($relativeUrl[0] === '/') {
$path = null;
}
$abs = null;
// if realtive URL contains a user
if (isset($user) === true) {
$abs .= $user;
// if realtive URL contains a password
if (isset($pass) === true) {
$abs .= ':' . $pass;
}
$abs .= '@';
}
$abs .= $host;
// if realtive URL contains a port
if (isset($port) === true) {
$abs .= ':' . $port;
}
$abs .= $path . '/' . $relativeUrl . (isset($query) === true ? '?' . $query : null);
// replace // or /./ or /foo/../ with /
$re = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
for ($n = 1; $n > 0; $abs = preg_replace($re, '/', $abs, -1, $n)) {
}
// return absolute URL
return $scheme . '://' . $abs;
}

View File

@ -23,6 +23,15 @@ class UOJRemoteProblem {
], ],
'languages' => ['C', 'C++', 'Java11', 'Python3', 'Pascal'], 'languages' => ['C', 'C++', 'Java11', 'Python3', 'Pascal'],
], ],
'uoj' => [
'name' => 'UniversalOJ',
'short_name' => 'UOJ',
'url' => 'https://uoj.ac',
'not_exist_texts' => [
'未找到该页面',
],
'languages' => ['C', 'C++03', 'C++11', 'C++14', 'C++17', 'C++20', 'Python3', 'Python2.7', 'Java8', 'Java11', 'Java17', 'Pascal'],
],
]; ];
static function getCodeforcesProblemUrl($id) { static function getCodeforcesProblemUrl($id) {
@ -45,6 +54,10 @@ class UOJRemoteProblem {
}, $id); }, $id);
} }
static function getUojProblemUrl($id) {
return static::$providers['uoj']['url'] . '/problem/' . $id;
}
static function getCodeforcesProblemBasicInfoFromHtml($id, $html) { static function getCodeforcesProblemBasicInfoFromHtml($id, $html) {
$remote_provider = static::$providers['codeforces']; $remote_provider = static::$providers['codeforces'];
@ -273,11 +286,59 @@ class UOJRemoteProblem {
]; ];
} }
static function getUojProblemBasicInfo($id) {
$remote_provider = static::$providers['uoj'];
$curl = new Curl();
$curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36 S2OJ/3.1.0');
$res = retry_loop(function () use (&$curl, $id) {
$curl->get(static::getUojProblemUrl($id));
if ($curl->error) {
return false;
}
return $curl->response;
});
if (!$res) return null;
$dom = new \IvoPetkov\HTML5DOMDocument();
$dom->loadHTML($res);
$title_dom = $dom->querySelector('.page-header');
$title_matches = [];
preg_match('/^#[1-9][0-9]*\. (.*)$/', trim($title_dom->textContent), $title_matches);
$title = "{$remote_provider['short_name']}{$id}{$title_matches[1]}";
$statement_dom = $dom->querySelector('.uoj-article');
$statement = HTML::tag('h3', [], '题目描述');
foreach ($statement_dom->querySelectorAll('a') as &$elem) {
$href = $elem->getAttribute('href');
$href = getAbsoluteUrl($href, $remote_provider['url']);
$elem->setAttribute('href', $href);
}
$statement .= $statement_dom->innerHTML;
return [
'type' => 'html',
'title' => $title,
'time_limit' => null,
'memory_limit' => null,
'difficulty' => -1,
'statement' => $statement,
];
}
public static function getProblemRemoteUrl($oj, $id) { public static function getProblemRemoteUrl($oj, $id) {
if ($oj === 'codeforces') { if ($oj === 'codeforces') {
return static::getCodeforcesProblemUrl($id); return static::getCodeforcesProblemUrl($id);
} else if ($oj === 'atcoder') { } else if ($oj === 'atcoder') {
return static::getAtcoderProblemUrl($id); return static::getAtcoderProblemUrl($id);
} else if ($oj === 'uoj') {
return static::getUojProblemUrl($id);
} }
return null; return null;
@ -289,6 +350,8 @@ class UOJRemoteProblem {
return static::getCodeforcesProblemBasicInfo($id); return static::getCodeforcesProblemBasicInfo($id);
} else if ($oj === 'atcoder') { } else if ($oj === 'atcoder') {
return static::getAtcoderProblemBasicInfo($id); return static::getAtcoderProblemBasicInfo($id);
} else if ($oj === 'uoj') {
return static::getUojProblemBasicInfo($id);
} }
return null; return null;