S2OJ/web/app/controllers/judge/submit.php

423 lines
9.6 KiB
PHP

<?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;
}
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 = dataAddHackPoint(UOJHack::cur()->problem->info, $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[] = ["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", [
["judge_type", "!=", "remote"],
],
]));
} else {
$problem_ban_list = array_map(fn ($x) => $x['id'], DB::selectAll([
"select id from problems",
"where", [
["judge_type", "!=", "local"],
],
]));
}
if ($problem_ban_list) {
$assignCond[] = ["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 id from submissions",
"where", array_merge(["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 id, submission_id from submissions_history",
"where", ["status" => $status, "major" => 0], // $assignCond is removed!!! fix this bug in the future!
"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",
"where", array_merge(["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 id, submission_id, input, input_type from hacks",
"where", array_merge(["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);