/** * Check in DB for problem added to contest * * @param array $problemData * @param array $contestData * @param Request $r */ public static function assertProblemAddedToContest($problemData, $contestData, $r) { // Get problem and contest from DB $problem = ProblemsDAO::getByAlias($problemData["request"]["alias"]); $contest = ContestsDAO::getByAlias($contestData["request"]["alias"]); // Get problem-contest and verify it $contest_problems = ContestProblemsDAO::getByPK($contest->getContestId(), $problem->getProblemId()); self::assertNotNull($contest_problems); self::assertEquals($r["points"], $contest_problems->getPoints()); self::assertEquals($r["order_in_contest"], $contest_problems->getOrder()); }
/** * Checks the contest details response * * @param type $contestData * @param type $problems * @param type $response */ private function assertContestDetails($contestData, $problems, $response) { // To validate, grab the contest object directly from the DB $contest = ContestsDAO::getByAlias($contestData['request']['alias']); // Assert we are getting correct data $this->assertEquals($contest->getDescription(), $response['description']); $this->assertEquals(Utils::GetPhpUnixTimestamp($contest->getStartTime()), $response['start_time']); $this->assertEquals(Utils::GetPhpUnixTimestamp($contest->getFinishTime()), $response['finish_time']); $this->assertEquals($contest->getWindowLength(), $response['window_length']); $this->assertEquals($contest->getAlias(), $response['alias']); $this->assertEquals($contest->getPointsDecayFactor(), $response['points_decay_factor']); $this->assertEquals($contest->getPartialScore(), $response['partial_score']); $this->assertEquals($contest->getSubmissionsGap(), $response['submissions_gap']); $this->assertEquals($contest->getFeedback(), $response['feedback']); $this->assertEquals($contest->getPenalty(), $response['penalty']); $this->assertEquals($contest->getScoreboard(), $response['scoreboard']); $this->assertEquals($contest->penalty_type, $response['penalty_type']); $this->assertEquals($contest->getPenaltyCalcPolicy(), $response['penalty_calc_policy']); // Assert we have our problems $numOfProblems = count($problems); $this->assertEquals($numOfProblems, count($response['problems'])); // Assert problem data $i = 0; foreach ($response['problems'] as $problem_array) { // Get problem from DB $problem = ProblemsDAO::getByAlias($problems[$i]['request']['alias']); // Assert data in DB $this->assertEquals($problem->getTitle(), $problem_array['title']); $this->assertEquals($problem->getAlias(), $problem_array['alias']); $this->assertEquals($problem->getValidator(), $problem_array['validator']); $this->assertEquals($problem->getTimeLimit(), $problem_array['time_limit']); $this->assertEquals($problem->getMemoryLimit(), $problem_array['memory_limit']); $this->assertEquals($problem->getVisits(), $problem_array['visits']); $this->assertEquals($problem->getSubmissions(), $problem_array['submissions']); $this->assertEquals($problem->getAccepted(), $problem_array['accepted']); $this->assertEquals($problem->getOrder(), $problem_array['order']); // Get points of problem from Contest-Problem relationship $problemInContest = ContestProblemsDAO::getByPK($contest->getContestId(), $problem->getProblemId()); $this->assertEquals($problemInContest->getPoints(), $problem_array['points']); $i++; } }
/** * Validate the request of apiCreate * * @param Request $r * @throws InvalidDatabaseOperationException * @throws NotFoundException */ private static function validateCreate(Request $r) { Validators::isStringNonEmpty($r["contest_alias"], "contest_alias"); Validators::isStringNonEmpty($r["problem_alias"], "problem_alias"); Validators::isStringNonEmpty($r["message"], "message"); try { $r["contest"] = ContestsDAO::getByAlias($r["contest_alias"]); $r["problem"] = ProblemsDAO::getByAlias($r["problem_alias"]); } catch (Exception $e) { throw new InvalidDatabaseOperationException($e); } if (is_null($r["contest"])) { throw new NotFoundException("contestNotFound"); } if (is_null($r["problem"])) { throw new NotFoundException("problemNotFound"); } // Is the combination contest_id and problem_id valid? if (is_null(ContestProblemsDAO::getByPK($r["contest"]->getContestId(), $r["problem"]->getProblemId()))) { throw new NotFoundException("problemNotFoundInContest"); } }
public static function RefreshScoreboardCache($contest_id) { try { $contest = ContestsDAO::getByPK($contest_id); $contest_runs = RunsDAO::GetContestRuns($contest_id); // Get all distinct contestants participating in the contest given contest_id $raw_contest_users = RunsDAO::GetAllRelevantUsers($contest_id, true, null); // Get all problems given contest_id $raw_contest_problems = ContestProblemsDAO::GetRelevantProblems($contest_id); } catch (Exception $e) { throw new InvalidDatabaseOperationException($e); } $problem_mapping = array(); $order = 0; foreach ($raw_contest_problems as $problem) { $problem_mapping[$problem->problem_id] = array('order' => $order++, 'alias' => $problem->alias); } $scoreboardLimit = Scoreboard::getScoreboardTimeLimitUnixTimestamp($contest); // Cache scoreboard until the contest ends (or forever if it has already ended). $timeout = max(0, strtotime($contest->getFinishTime()) - time()); $contestantScoreboardCache = new Cache(Cache::CONTESTANT_SCOREBOARD_PREFIX, $contest_id); $contestantScoreboard = Scoreboard::getScoreboardFromRuns($contest_runs, $raw_contest_users, $problem_mapping, $contest->getPenalty(), $scoreboardLimit, $contest, false, false); $contestantScoreboardCache->set($contestantScoreboard, $timeout); $adminScoreboardCache = new Cache(Cache::ADMIN_SCOREBOARD_PREFIX, $contest_id); $scoreboardLimit = Scoreboard::getScoreboardTimeLimitUnixTimestamp($contest, true); $adminScoreboard = Scoreboard::getScoreboardFromRuns($contest_runs, $raw_contest_users, $problem_mapping, $contest->getPenalty(), $scoreboardLimit, $contest, true, false); $adminScoreboardCache->set($adminScoreboard, $timeout); $contestantEventCache = new Cache(Cache::CONTESTANT_SCOREBOARD_EVENTS_PREFIX, $contest_id); $contestantEventCache->set(Scoreboard::calculateEvents($contest, $contest_runs, $raw_contest_users, $problem_mapping, false), $timeout); $adminEventCache = new Cache(Cache::ADMIN_SCOREBOARD_EVENTS_PREFIX, $contest_id); $adminEventCache->set(Scoreboard::calculateEvents($contest, $contest_runs, $raw_contest_users, $problem_mapping, true), $timeout); // Try to broadcast the updated scoreboards: $log = Logger::getLogger('Scoreboard'); try { $grader = new Grader(); $log->debug('Sending updated scoreboards'); $grader->broadcast($contest->alias, json_encode(array('message' => '/scoreboard/update/', 'scoreboard' => $adminScoreboard)), false, -1, false); $grader->broadcast($contest->alias, json_encode(array('message' => '/scoreboard/update/', 'scoreboard' => $contestantScoreboard)), true, -1, true); } catch (Exception $e) { $log->error('Error broadcasting scoreboard: ' . $e); } }
/** * * Validates Create Run request * * @param Request $r * @throws ApiException * @throws InvalidDatabaseOperationException * @throws NotAllowedToSubmitException * @throws InvalidParameterException * @throws ForbiddenAccessException */ private static function validateCreateRequest(Request $r) { // https://github.com/omegaup/omegaup/issues/739 if ($r['current_user']->username == 'omi') { throw new ForbiddenAccessException(); } $allowedLanguages = array('kp', 'kj', 'c', 'cpp', 'cpp11', 'java', 'py', 'rb', 'pl', 'cs', 'pas', 'cat', 'hs'); try { Validators::isStringNonEmpty($r['problem_alias'], 'problem_alias'); // Check that problem exists $r['problem'] = ProblemsDAO::getByAlias($r['problem_alias']); if ($r['problem']->deprecated) { throw new PreconditionFailedException('problemDeprecated'); } $allowedLanguages = array_intersect($allowedLanguages, explode(',', $r['problem']->languages)); Validators::isInEnum($r['language'], 'language', $allowedLanguages); Validators::isStringNonEmpty($r['source'], 'source'); // Check for practice or public problem, there is no contest info in this scenario if ($r['contest_alias'] == '') { if (Authorization::IsProblemAdmin($r['current_user_id'], $r['problem']) || time() > ProblemsDAO::getPracticeDeadline($r['problem']->getProblemId()) || $r['problem']->getPublic() == true) { if (!RunsDAO::IsRunInsideSubmissionGap(null, $r['problem']->getProblemId(), $r['current_user_id']) && !Authorization::IsSystemAdmin($r['current_user_id'])) { throw new NotAllowedToSubmitException('runWaitGap'); } self::$practice = true; return; } else { throw new NotAllowedToSubmitException('problemIsNotPublic'); } } // Validate contest Validators::isStringNonEmpty($r['contest_alias'], 'contest_alias'); $r['contest'] = ContestsDAO::getByAlias($r['contest_alias']); if ($r['contest'] == null) { throw new InvalidParameterException('parameterNotFound', 'contest_alias'); } if ($r['contest']->languages !== null) { $allowedLanguages = array_intersect($allowedLanguages, explode(',', $r['contest']->languages)); Validators::isInEnum($r['language'], 'language', $allowedLanguages); } // Validate that the combination contest_id problem_id is valid if (!ContestProblemsDAO::getByPK($r['contest']->getContestId(), $r['problem']->getProblemId())) { throw new InvalidParameterException('parameterNotFound', 'problem_alias'); } // Contest admins can skip following checks if (!Authorization::IsContestAdmin($r['current_user_id'], $r['contest'])) { // Before submit something, contestant had to open the problem/contest if (!ContestsUsersDAO::getByPK($r['current_user_id'], $r['contest']->getContestId())) { throw new NotAllowedToSubmitException('runNotEvenOpened'); } // Validate that the run is timely inside contest if (!ContestsDAO::isInsideContest($r['contest'], $r['current_user_id'])) { throw new NotAllowedToSubmitException('runNotInsideContest'); } // Validate if contest is private then the user should be registered if ($r['contest']->getPublic() == 0 && is_null(ContestsUsersDAO::getByPK($r['current_user_id'], $r['contest']->getContestId()))) { throw new NotAllowedToSubmitException('runNotRegistered'); } // Validate if the user is allowed to submit given the submissions_gap if (!RunsDAO::IsRunInsideSubmissionGap($r['contest']->getContestId(), $r['problem']->getProblemId(), $r['current_user_id'])) { throw new NotAllowedToSubmitException('runWaitGap'); } } } catch (ApiException $apiException) { // Propagate ApiException throw $apiException; } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } }
public static function apiDownload(Request $r) { self::authenticateRequest($r); self::validateStats($r); // Get our runs $relevant_columns = array("run_id", "guid", "language", "status", "verdict", "runtime", "penalty", "memory", "score", "contest_score", "time", "submit_delay", "Users.username", "Problems.alias"); try { $runs = RunsDAO::search(new Runs(array("contest_id" => $r["contest"]->getContestId())), "time", "DESC", $relevant_columns); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } $zip = new ZipStream($r["contest_alias"] . '.zip'); // Add runs to zip $table = "guid,user,problem,verdict,points\n"; foreach ($runs as $run) { $zip->add_file_from_path("runs/" . $run->getGuid(), RunController::getSubmissionPath($run)); $columns[0] = 'username'; $columns[1] = 'alias'; $usernameProblemData = $run->asFilteredArray($columns); $table .= $run->getGuid() . "," . $usernameProblemData['username'] . "," . $usernameProblemData['alias'] . "," . $run->getVerdict() . "," . $run->getContestScore(); $table .= "\n"; } $zip->add_file("summary.csv", $table); // Add problem cases to zip $contest_problems = ContestProblemsDAO::GetRelevantProblems($r["contest"]->getContestId()); foreach ($contest_problems as $problem) { $zip->add_file_from_path($problem->getAlias() . "_cases.zip", PROBLEMS_PATH . "/" . $problem->getAlias() . "/cases.zip"); } // Return zip $zip->finish(); die; }
public static function apiDownload(Request $r) { self::authenticateRequest($r); self::validateStats($r); // Get our runs $relevant_columns = array('run_id', 'guid', 'language', 'status', 'verdict', 'runtime', 'penalty', 'memory', 'score', 'contest_score', 'time', 'submit_delay', 'Users.username', 'Problems.alias'); try { $runs = RunsDAO::search(new Runs(array('contest_id' => $r['contest']->getContestId())), 'time', 'DESC', $relevant_columns); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } $zip = new ZipStream($r['contest_alias'] . '.zip'); // Add runs to zip $table = "guid,user,problem,verdict,points\n"; foreach ($runs as $run) { $zip->add_file_from_path('runs/' . $run->getGuid(), RunController::getSubmissionPath($run)); $columns[0] = 'username'; $columns[1] = 'alias'; $usernameProblemData = $run->asFilteredArray($columns); $table .= $run->getGuid() . ',' . $usernameProblemData['username'] . ',' . $usernameProblemData['alias'] . ',' . $run->getVerdict() . ',' . $run->getContestScore(); $table .= "\n"; } $zip->add_file('summary.csv', $table); // Add problem cases to zip $contest_problems = ContestProblemsDAO::GetRelevantProblems($r['contest']->getContestId()); foreach ($contest_problems as $problem) { $zip->add_file_from_path($problem->getAlias() . '_cases.zip', PROBLEMS_PATH . '/' . $problem->getAlias() . '/cases.zip'); } // Return zip $zip->finish(); die; }
/** * Validate problem Details API * * @param Request $r * @throws ApiException * @throws InvalidDatabaseOperationException * @throws NotFoundException * @throws ForbiddenAccessException */ private static function validateDetails(Request $r) { Validators::isStringNonEmpty($r['contest_alias'], 'contest_alias', false); Validators::isStringNonEmpty($r['problem_alias'], 'problem_alias'); // Lang is optional. Default is user's preferred. if (!is_null($r['lang'])) { Validators::isStringOfMaxLength($r['lang'], 'lang', 2); } else { $r['lang'] = UserController::getPreferredLanguage($r); } try { $r['problem'] = ProblemsDAO::getByAlias($r['problem_alias']); } catch (Exception $e) { throw new InvalidDatabaseOperationException($e); } if (is_null($r['problem'])) { throw new NotFoundException('problemNotFound'); } if (isset($r['statement_type']) && !in_array($r['statement_type'], array('html', 'markdown'))) { throw new NotFoundException('invalidStatementType'); } // If we request a problem inside a contest if (!is_null($r['contest_alias'])) { // Is the combination contest_id and problem_id valid? try { $r['contest'] = ContestsDAO::getByAlias($r['contest_alias']); if (is_null($r['contest'])) { throw new NotFoundException('contestNotFound'); } if (is_null(ContestProblemsDAO::getByPK($r['contest']->getContestId(), $r['problem']->getProblemId()))) { throw new NotFoundException('problemNotFoundInContest'); } } catch (ApiException $apiException) { throw $apiException; } catch (Exception $e) { throw new InvalidDatabaseOperationException($e); } // If the contest is private, verify that our user is invited $contest_admin = Authorization::IsContestAdmin($r['current_user_id'], $r['contest']); if ($r['contest']->public != '1') { if (is_null(ContestsUsersDAO::getByPK($r['current_user_id'], $r['contest']->contest_id)) && !$contest_admin) { throw new ForbiddenAccessException(); } } // If the contest has not started, user should not see it, unless // it is admin if (!ContestsDAO::hasStarted($r['contest']) && !$contest_admin) { throw new ForbiddenAccessException('contestNotStarted'); } } else { if (!Authorization::CanEditProblem($r['current_user_id'], $r['problem'])) { // If the problem is requested outside a contest, we need to // check that it is not private if ($r['problem']->public != '1') { throw new ForbiddenAccessException('problemIsPrivate'); } } } }
/** * Validate problem Details API * * @param Request $r * @throws ApiException * @throws InvalidDatabaseOperationException * @throws NotFoundException * @throws ForbiddenAccessException */ private static function validateDetails(Request $r) { Validators::isStringNonEmpty($r["contest_alias"], "contest_alias", false); Validators::isStringNonEmpty($r["problem_alias"], "problem_alias"); // Lang is optional. Default is user's preferred. if (!is_null($r["lang"])) { Validators::isStringOfMaxLength($r["lang"], "lang", 2); } else { $r['lang'] = UserController::getPreferredLanguage($r); } try { $r["problem"] = ProblemsDAO::getByAlias($r["problem_alias"]); } catch (Exception $e) { throw new InvalidDatabaseOperationException($e); } if (is_null($r["problem"])) { throw new NotFoundException("problemNotFound"); } if (isset($r["statement_type"]) && !in_array($r["statement_type"], array("html", "markdown"))) { throw new NotFoundException("invalidStatementType"); } // If we request a problem inside a contest if (!is_null($r["contest_alias"])) { // Is the combination contest_id and problem_id valid? try { $r["contest"] = ContestsDAO::getByAlias($r["contest_alias"]); if (is_null($r["contest"])) { throw new NotFoundException("contestNotFound"); } if (is_null(ContestProblemsDAO::getByPK($r["contest"]->getContestId(), $r["problem"]->getProblemId()))) { throw new NotFoundException("problemNotFoundInContest"); } } catch (ApiException $apiException) { throw $apiException; } catch (Exception $e) { throw new InvalidDatabaseOperationException($e); } // If the contest is private, verify that our user is invited if ($r["contest"]->getPublic() === 0) { if (is_null(ContestsUsersDAO::getByPK($r["current_user_id"], $r["contest"]->getContestId())) && !Authorization::IsContestAdmin($r["current_user_id"], $r["contest"])) { throw new ForbiddenAccessException(); } } // If the contest has not started, user should not see it, unless it is admin if (!ContestsDAO::hasStarted($r["contest"]) && !Authorization::IsContestAdmin($r["current_user_id"], $r["contest"])) { throw new ForbiddenAccessException("contestNotStarted"); } } else { if (!Authorization::CanEditProblem($r["current_user_id"], $r["problem"])) { // If the problem is requested outside a contest, we need to check that it is not private if ($r["problem"]->getPublic() == "0") { throw new ForbiddenAccessException("problemIsPrivate"); } } } }