S2OJ/web/app/controllers/judge/submit.php
Baoshuo e0cc425031
All checks were successful
continuous-integration/drone/push Build is passing
fix: assignCond in queryMinorSubmissionToJudge
2023-02-17 13:57:26 +08:00

434 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
requirePHPLib('judger');
requirePHPLib('data');
if (!authenticateJudger()) {
UOJResponse::page404();
}
function submissionJudged() {
UOJSubmission::onJudged(UOJRequest::post('id'), UOJRequest::post('result'), UOJRequest::post('judge_time'));
}
function customTestSubmissionJudged() {
$submission = DB::selectFirst([
"select submitter, status, content, result, problem_id from custom_test_submissions",
"where", ['id' => $_POST['id']]
]);
if ($submission == null) {
return;
}
if ($submission['status'] != 'Judging') {
return;
}
$result = json_decode($_POST['result'], true);
$result['details'] = uojTextEncode($result['details']);
DB::update([
"update custom_test_submissions",
"set", [
'status' => $result['status'],
'status_details' => '',
'result' => json_encode($result, JSON_UNESCAPED_UNICODE)
], "where", ['id' => $_POST['id']]
]);
}
function hackJudged() {
$result = json_decode($_POST['result'], true);
UOJHack::init($_POST['id']);
UOJHack::cur()->setProblem();
UOJHack::cur()->setSubmission();
if ($result['score']) {
$status = 'Judged, Waiting';
} else {
$status = 'Judged';
}
$ok = DB::update([
"update hacks",
"set", [
'success' => $result['score'],
'status' => $status,
'details' => uojTextEncode($result['details'])
], "where", ['id' => $_POST['id']]
]);
if (!$result['score']) {
return;
} else {
$problem_link = UOJHack::cur()->problem->getLink();
$submission_link = UOJHack::cur()->submission->getLink();
$hacker_link = UOJUser::getLink(UOJHack::info('hacker'), ['color' => false]);
$hack_link = UOJHack::cur()->getLink();
sendSystemMsg(UOJHack::info('owner'), '提交被 Hack 通知', <<<EOD
您对题目 {$problem_link} 的提交 {$submission_link} {$hacker_link} 成功 Hack请查看 Hack {$hack_link} 获取详情。
EOD);
}
if (!$ok) {
return;
}
if (!(validateUploadedFile('hack_input') && validateUploadedFile('std_output'))) {
UOJLog::error("hack successfully but received no data. id: {$_POST['id']}");
return;
}
$input = UOJContext::storagePath() . UOJHack::info('input');
$up_in = $_FILES["hack_input"]['tmp_name'];
$up_out = $_FILES["std_output"]['tmp_name'];
if (!UOJHack::cur()->problem->needToReviewHack()) {
$err = UOJHack::cur()->problem->addHackPoint($up_in, $up_out);
if ($err === '') {
unlink($input);
DB::update([
"update hacks",
"set", [
'status' => 'Judged'
], "where", ['id' => $_POST['id']]
]);
return;
} else {
UOJLog::error("hack successfully but failed to add an extra test: {$err}");
}
}
move_uploaded_file($up_in, "{$input}_in");
move_uploaded_file($up_out, "{$input}_out");
DB::update([
"update hacks",
"set", [
'status' => 'Judged, WaitingM'
], "where", ['id' => $_POST['id']]
]);
}
if (isset($_POST['submit'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
if (isset($_POST['is_hack'])) {
hackJudged();
} elseif (isset($_POST['is_custom_test'])) {
customTestSubmissionJudged();
} else {
submissionJudged();
}
}
if (isset($_POST['update-status'])) {
if (!validateUInt($_POST['id'])) {
die("Wow! hacker! T_T....");
}
$status_details = $_POST['status'];
if (isset($_POST['is_custom_test'])) {
DB::update([
"update custom_test_submissions",
"set", ["status_details" => $status_details],
"where", ["id" => $_POST['id']]
]);
} else {
DB::update([
"update submissions",
"set", ["status_details" => $status_details],
"where", ["id" => $_POST['id']]
]);
}
die();
}
$assignCond = [];
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["assigned_to_judger", "!=", "any"],
["assigned_to_judger", "!=", $_POST['judger_name']]
]
]));
if ($problem_ban_list) {
$assignCond[] = ["submissions.problem_id", "not in", DB::rawtuple($problem_ban_list)];
}
if ($_POST['judger_name'] == "remote_judger") {
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["type", "!=", "remote"],
],
]));
} else {
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["type", "!=", "local"],
],
]));
}
if ($problem_ban_list) {
$assignCond[] = ["submissions.problem_id", "not in", DB::rawtuple($problem_ban_list)];
}
$submission = null;
$hack = null;
function querySubmissionToJudge($status, $set_q) {
global $assignCond;
for ($times = 0; $times < 10; $times++) {
$submission = DB::selectFirst([
"select submissions.id from submissions",
"where", array_merge(["submissions.status" => $status], $assignCond),
"order by id limit 1"
]);
if (!$submission) {
return null;
}
$ok = DB::transaction(function () use (&$submission, $set_q, $status) {
DB::update([
"update submissions",
"set", $set_q,
"where", [
"id" => $submission['id'],
"status" => $status
]
]);
if (DB::affected_rows() == 1) {
$submission = DB::selectFirst([
"select id, problem_id, content, status, judge_time from submissions",
"where", ["id" => $submission['id']]
]);
return true;
} else {
return false;
}
});
if ($ok) {
return $submission;
}
}
}
function queryMinorSubmissionToJudge($status, $set_q) {
global $assignCond;
for ($times = 0; $times < 10; $times++) {
$submission = null;
$his = DB::selectFirst([
"select submissions_history.id, submissions_history.submission_id from submissions_history",
"inner join submissions on submissions.id = submissions_history.submission_id",
"where", array_merge(["submissions_history.status" => $status, "submissions_history.major" => 0], $assignCond),
"order by id limit 1"
]);
if (!$his) {
return null;
}
$ok = DB::transaction(function () use (&$submission, &$his, $set_q, $status) {
$submission = DB::selectFirst([
"select id, problem_id, content from submissions",
"where", ["id" => $his['submission_id']], DB::for_share()
]);
if (!$submission) {
return false;
}
DB::update([
"update submissions_history",
"set", $set_q,
"where", [
"id" => $his['id'],
"status" => $status
]
]);
if (DB::affected_rows() == 1) {
$ret = DB::selectFirst([
"select status, judge_time from submissions_history",
"where", ["id" => $his['id']]
]);
if ($ret === false) {
return false;
}
$submission += $ret;
return true;
} else {
return false;
}
});
if ($ok) {
return $submission;
}
}
}
function queryCustomTestSubmissionToJudge() {
global $assignCond;
while (true) {
$submission = DB::selectFirst([
"select id, problem_id, content from custom_test_submissions as submissions",
"where", array_merge(["submissions.judge_time" => null], $assignCond),
"order by id limit 1"
]);
if (!$submission) {
return null;
}
$submission['is_custom_test'] = '';
DB::update([
"update custom_test_submissions",
"set", [
"judge_time" => DB::now(),
"status" => 'Judging',
], "where", [
"id" => $submission['id'],
"judge_time" => null,
]
]);
if (DB::affected_rows() == 1) {
$submission['status'] = 'Judging';
return $submission;
}
}
}
function queryHackToJudge() {
global $assignCond;
while (true) {
if (DB::selectFirst([
"select 1 from hacks",
"where", [
["status", "!=", "Waiting"],
["status", "!=", "Judged"],
], "order by id limit 1"
])) {
return null;
}
$hack = DB::selectFirst([
"select hacks.id, hacks.submission_id, hacks.input, hacks.input_type from hacks",
"inner join submissions on submissions.id = hacks.submission_id",
"where", array_merge(["hacks.judge_time" => null], $assignCond),
"order by id limit 1"
]);
if (!$hack) {
return null;
}
DB::update([
"update hacks",
"set", [
"judge_time" => DB::now(),
"status" => 'Judging'
],
"where", [
"id" => $hack['id'],
"judge_time" => null
]
]);
if (DB::affected_rows() == 1) {
$hack['status'] = 'Judging';
return $hack;
}
}
}
function findSubmissionToJudge() {
global $submission, $hack;
$submission = querySubmissionToJudge('Waiting', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = queryCustomTestSubmissionToJudge();
if ($submission) {
return true;
}
$submission = querySubmissionToJudge('Waiting Rejudge', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = querySubmissionToJudge('Judged, Waiting', [
"status" => 'Judged, Judging'
]);
if ($submission) {
return true;
}
$submission = queryMinorSubmissionToJudge('Waiting Rejudge', [
"judge_time" => DB::now(),
"judger" => $_POST['judger_name'],
"status" => 'Judging'
]);
if ($submission) {
return true;
}
$submission = queryMinorSubmissionToJudge('Judged, Waiting', [
"status" => 'Judged, Judging'
]);
if ($submission) {
return true;
}
$hack = queryHackToJudge();
if ($hack) {
$submission = DB::selectFirst([
"select id, problem_id, content from submissions",
"where", [
"id" => $hack['submission_id'],
"score" => 100
]
]);
if (!$submission) {
$details = "<error>the score gained by the hacked submission is not 100.</error>";
DB::update([
"update hacks",
"set", [
'success' => 0,
'status' => 'Judged',
'details' => uojTextEncode($details)
], "where", ["id" => $hack['id']]
]);
return false;
}
return true;
}
return false;
}
if (isset($_POST['fetch_new']) && !$_POST['fetch_new']) {
die("Nothing to judge");
}
if (!findSubmissionToJudge()) {
die("Nothing to judge");
}
$submission['id'] = (int)$submission['id'];
$submission['problem_id'] = (int)$submission['problem_id'];
$submission['problem_mtime'] = filemtime("/var/uoj_data/{$submission['problem_id']}");
$submission['content'] = json_decode($submission['content'], true);
if (isset($submission['status']) && $submission['status'] == 'Judged, Judging' && isset($submission['content']['final_test_config'])) {
$submission['content']['config'] = $submission['content']['final_test_config'];
unset($submission['content']['final_test_config']);
}
if ($hack) {
$submission['is_hack'] = "";
$submission['hack']['id'] = (int)$hack['id'];
$submission['hack']['input'] = $hack['input'];
$submission['hack']['input_type'] = $hack['input_type'];
}
echo json_encode($submission);