public static function isInsideContest(Contests $contest, $user_id) { if (time() > strtotime($contest->finish_time) || time() < strtotime($contest->start_time)) { return false; } if (is_null($contest->window_length)) { return true; } $contest_user = ContestsUsersDAO::getByPK($user_id, $contest->contest_id); $first_access_time = $contest_user->access_time; return time() <= strtotime($first_access_time) + $contest->window_length * 60; }
public static function CheckAndSaveFirstTimeAccess($user_id, $contest_id) { $contest_user = self::getByPK($user_id, $contest_id); // If is null, add our contest_user relationship if (is_null($contest_user)) { $contest_user = new ContestsUsers(); $contest_user->setUserId($user_id); $contest_user->setContestId($contest_id); $contest_user->setAccessTime(date("Y-m-d H:i:s")); $contest_user->setScore(0); $contest_user->setTime(0); ContestsUsersDAO::save($contest_user); } else { if ($contest_user->getAccessTime() === "0000-00-00 00:00:00") { // If its set to default time, update it $contest_user->setAccessTime(date("Y-m-d H:i:s")); ContestsUsersDAO::save($contest_user); } } return $contest_user; }
public static function CheckAndSaveFirstTimeAccess($user_id, $contest_id, $grant_access = false) { $contest_user = self::getByPK($user_id, $contest_id); if (is_null($contest_user)) { if (!$grant_access) { // User was not authorized to do this. throw new ForbiddenAccessException(); } $contest_user = new ContestsUsers(); $contest_user->setUserId($user_id); $contest_user->setContestId($contest_id); $contest_user->setAccessTime(date('Y-m-d H:i:s')); $contest_user->setScore(0); $contest_user->setTime(0); ContestsUsersDAO::save($contest_user); } elseif ($contest_user->getAccessTime() === '0000-00-00 00:00:00') { // If its set to default time, update it $contest_user->setAccessTime(date('Y-m-d H:i:s')); ContestsUsersDAO::save($contest_user); } return $contest_user; }
/** * Create a new run * * @param Request $r * @return array * @throws Exception * @throws InvalidDatabaseOperationException * @throws InvalidFilesystemOperationException */ public static function apiCreate(Request $r) { // Init self::initializeGrader(); // Authenticate user self::authenticateRequest($r); // Validate request self::validateCreateRequest($r); self::$log->info('New run being submitted!!'); $response = array(); if (self::$practice) { if (OMEGAUP_LOCKDOWN) { throw new ForbiddenAccessException('lockdown'); } $submit_delay = 0; $contest_id = null; $test = 0; } else { //check the kind of penalty_type for this contest $penalty_type = $r['contest']->penalty_type; switch ($penalty_type) { case 'contest_start': // submit_delay is calculated from the start // of the contest $start = $r['contest']->getStartTime(); break; case 'problem_open': // submit delay is calculated from the // time the user opened the problem $opened = ContestProblemOpenedDAO::getByPK($r['contest']->getContestId(), $r['problem']->getProblemId(), $r['current_user_id']); if (is_null($opened)) { //holy moly, he is submitting a run //and he hasnt even opened the problem //what should be done here? throw new NotAllowedToSubmitException('runEvenOpened'); } $start = $opened->getOpenTime(); break; case 'none': case 'runtime': //we dont care $start = null; break; default: self::$log->error('penalty_type for this contests is not a valid option, asuming `none`.'); $start = null; } if (!is_null($start)) { //ok, what time is it now? $c_time = time(); $start = strtotime($start); //asuming submit_delay is in minutes $submit_delay = (int) (($c_time - $start) / 60); } else { $submit_delay = 0; } $contest_id = $r['contest']->getContestId(); $test = Authorization::IsContestAdmin($r['current_user_id'], $r['contest']) ? 1 : 0; } // Populate new run object $run = new Runs(array('user_id' => $r['current_user_id'], 'problem_id' => $r['problem']->getProblemId(), 'contest_id' => $contest_id, 'language' => $r['language'], 'source' => $r['source'], 'status' => 'new', 'runtime' => 0, 'penalty' => $submit_delay, 'memory' => 0, 'score' => 0, 'contest_score' => $contest_id != null ? 0 : null, 'submit_delay' => $submit_delay, 'guid' => md5(uniqid(rand(), true)), 'verdict' => 'JE', 'test' => $test)); try { // Push run into DB RunsDAO::save($run); SubmissionLogDAO::save(new SubmissionLog(array('user_id' => $run->user_id, 'run_id' => $run->run_id, 'contest_id' => $run->contest_id, 'ip' => ip2long($_SERVER['REMOTE_ADDR'])))); // Update submissions counter++ $r['problem']->setSubmissions($r['problem']->getSubmissions() + 1); ProblemsDAO::save($r['problem']); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } try { // Create file for the run $filepath = RunController::getSubmissionPath($run); FileHandler::CreateFile($filepath, $r['source']); } catch (Exception $e) { throw new InvalidFilesystemOperationException($e); } // Call Grader try { self::$grader->Grade([$run->guid], false, false); } catch (Exception $e) { self::$log->error('Call to Grader::grade() failed:'); self::$log->error($e); } if (self::$practice) { $response['submission_deadline'] = 0; } else { // Add remaining time to the response try { $contest_user = ContestsUsersDAO::getByPK($r['current_user_id'], $r['contest']->getContestId()); if ($r['contest']->getWindowLength() === null) { $response['submission_deadline'] = strtotime($r['contest']->getFinishTime()); } else { $response['submission_deadline'] = min(strtotime($r['contest']->getFinishTime()), strtotime($contest_user->getAccessTime()) + $r['contest']->getWindowLength() * 60); } } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } } // Happy ending $response['guid'] = $run->getGuid(); $response['status'] = 'ok'; // Expire rank cache UserController::deleteProblemsSolvedRankCacheList(); return $response; }
/** * Update a Contest * * @param Request $r * @return array * @throws InvalidDatabaseOperationException */ public static function apiUpdate(Request $r) { if (OMEGAUP_LOCKDOWN) { throw new ForbiddenAccessException("lockdown"); } // Authenticate request self::authenticateRequest($r); // Validate request self::validateCreateOrUpdate($r, true); // Update contest DAO if (!is_null($r["public"])) { // If going public if ($r["public"] == 1) { self::validateContestCanBePublic($r["contest"]); } $r["contest"]->setPublic($r["public"]); } $valueProperties = array("title", "description", "start_time" => array("transform" => function ($value) { return gmdate('Y-m-d H:i:s', $value); }), "finish_time" => array("transform" => function ($value) { return gmdate('Y-m-d H:i:s', $value); }), "window_length" => array("transform" => function ($value) { return $value == "NULL" ? NULL : $value; }), "scoreboard", "points_decay_factor", "partial_score", "submissions_gap", "feedback", "penalty" => array("transform" => function ($value) { return max(0, intval($value)); }), "penalty_type", "penalty_calc_policy", "show_scoreboard_after", "contestant_must_register"); self::updateValueProperties($r, $r["contest"], $valueProperties); // Push changes try { // Begin a new transaction ContestsDAO::transBegin(); // Save the contest object with data sent by user to the database ContestsDAO::save($r["contest"]); // If the contest is private, add the list of allowed users if (!is_null($r["public"]) && $r["public"] != 1 && $r["hasPrivateUsers"]) { // Get current users $cu_key = new ContestsUsers(array("contest_id" => $r["contest"]->getContestId())); $current_users = ContestsUsersDAO::search($cu_key); $current_users_id = array(); foreach ($current_users as $cu) { array_push($current_users_id, $current_users->getUserId()); } // Check who needs to be deleted and who needs to be added $to_delete = array_diff($current_users_id, $r["private_users_list"]); $to_add = array_diff($r["private_users_list"], $current_users_id); // Add users in the request foreach ($to_add as $userkey) { // Create a temp DAO for the relationship $temp_user_contest = new ContestsUsers(array("contest_id" => $r["contest"]->getContestId(), "user_id" => $userkey, "access_time" => "0000-00-00 00:00:00", "score" => 0, "time" => 0)); // Save the relationship in the DB ContestsUsersDAO::save($temp_user_contest); } // Delete users foreach ($to_delete as $userkey) { // Create a temp DAO for the relationship $temp_user_contest = new ContestsUsers(array("contest_id" => $r["contest"]->getContestId(), "user_id" => $userkey)); // Delete the relationship in the DB ContestsUsersDAO::delete(ContestProblemsDAO::search($temp_user_contest)); } } if (!is_null($r['problems'])) { // Get current problems $p_key = new Problems(array("contest_id" => $r["contest"]->getContestId())); $current_problems = ProblemsDAO::search($p_key); $current_problems_id = array(); foreach ($current_problems as $p) { array_push($current_problems_id, $p->getProblemId()); } // Check who needs to be deleted and who needs to be added $to_delete = array_diff($current_problems_id, self::$problems_id); $to_add = array_diff(self::$problems_id, $current_problems_id); foreach ($to_add as $problem) { $contest_problem = new ContestProblems(array('contest_id' => $r["contest"]->getContestId(), 'problem_id' => $problem, 'points' => $r["problems"][$problem]['points'])); ContestProblemsDAO::save($contest_problem); } foreach ($to_delete as $problem) { $contest_problem = new ContestProblems(array('contest_id' => $r["contest"]->getContestId(), 'problem_id' => $problem)); ContestProblemsDAO::delete(ContestProblemsDAO::search($contest_problem)); } } // End transaction ContestsDAO::transEnd(); } catch (Exception $e) { // Operation failed in the data layer, rollback transaction ContestsDAO::transRollback(); throw new InvalidDatabaseOperationException($e); } // Expire contest-info cache Cache::deleteFromCache(Cache::CONTEST_INFO, $r["contest_alias"]); // Expire contest scoreboard cache Scoreboard::InvalidateScoreboardCache($r["contest"]->getContestId()); // Happy ending $response = array(); $response["status"] = 'ok'; self::$log->info("Contest updated (alias): " . $r['contest_alias']); return $response; }
/** * Entry point for Problem Details API * * @param Request $r * @throws InvalidFilesystemOperationException * @throws InvalidDatabaseOperationException */ public static function apiDetails(Request $r) { // Get user. // Allow unauthenticated requests if we are not openning a problem // inside a contest. try { self::authenticateRequest($r); } catch (UnauthorizedException $e) { if (!is_null($r['contest_alias'])) { throw $e; } } // Validate request self::validateDetails($r); $response = array(); // Create array of relevant columns $relevant_columns = array('title', 'alias', 'validator', 'time_limit', 'validator_time_limit', 'overall_wall_time_limit', 'extra_wall_time', 'memory_limit', 'output_limit', 'visits', 'submissions', 'accepted', 'difficulty', 'creation_date', 'source', 'order', 'points', 'public', 'languages', 'slow', 'stack_limit', 'email_clarifications'); // Read the file that contains the source if (!ProblemController::isLanguageSupportedForProblem($r)) { // If there is no language file for the problem, return the spanish version. $r['lang'] = 'es'; } $statement_type = ProblemController::getStatementType($r); Cache::getFromCacheOrSet(Cache::PROBLEM_STATEMENT, $r['problem']->getAlias() . '-' . $r['lang'] . '-' . $statement_type, $r, 'ProblemController::getProblemStatement', $file_content, APC_USER_CACHE_PROBLEM_STATEMENT_TIMEOUT); // Add problem statement to source $response['problem_statement'] = $file_content; $response['problem_statement_language'] = $r['lang']; // Add the example input. $sample_input = null; Cache::getFromCacheOrSet(Cache::PROBLEM_SAMPLE, $r['problem']->getAlias() . '-sample.in', $r, 'ProblemController::getSampleInput', $sample_input, APC_USER_CACHE_PROBLEM_STATEMENT_TIMEOUT); if (!empty($sample_input)) { $response['sample_input'] = $sample_input; } // Add the problem the response $response = array_merge($response, $r['problem']->asFilteredArray($relevant_columns)); // If the problem is public or if the user has admin privileges, show the // problem source and alias of owner. if ($r['problem']->public || Authorization::IsProblemAdmin($r['current_user_id'], $r['problem'])) { $problemsetter = UsersDAO::getByPK($r['problem']->author_id); if (!is_null($problemsetter)) { $response['problemsetter'] = array('username' => $problemsetter->username, 'name' => is_null($problemsetter->name) ? $problemsetter->username : $problemsetter->name); } } else { unset($response['source']); } if (!is_null($r['current_user_id'])) { // Create array of relevant columns for list of runs $relevant_columns = array('guid', 'language', 'status', 'verdict', 'runtime', 'penalty', 'memory', 'score', 'contest_score', 'time', 'submit_delay'); // Search the relevant runs from the DB $contest = ContestsDAO::getByAlias($r['contest_alias']); $keyrun = new Runs(array('user_id' => $r['current_user_id'], 'problem_id' => $r['problem']->getProblemId(), 'contest_id' => is_null($r['contest']) ? null : $r['contest']->getContestId())); // Get all the available runs done by the current_user try { $runs_array = RunsDAO::search($keyrun); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } // Add each filtered run to an array if (count($runs_array) >= 0) { $runs_filtered_array = array(); foreach ($runs_array as $run) { $filtered = $run->asFilteredArray($relevant_columns); $filtered['alias'] = $r['problem']->alias; $filtered['username'] = $r['current_user']->username; $filtered['time'] = strtotime($filtered['time']); array_push($runs_filtered_array, $filtered); } } $response['runs'] = $runs_filtered_array; } if (!is_null($r['contest'])) { // At this point, contestant_user relationship should be established. try { ContestsUsersDAO::CheckAndSaveFirstTimeAccess($r['current_user_id'], $r['contest']->contest_id); } catch (ApiException $e) { throw $e; } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } // As last step, register the problem as opened if (!ContestProblemOpenedDAO::getByPK($r['contest']->getContestId(), $r['problem']->getProblemId(), $r['current_user_id'])) { //Create temp object $keyContestProblemOpened = new ContestProblemOpened(array('contest_id' => $r['contest']->getContestId(), 'problem_id' => $r['problem']->getProblemId(), 'user_id' => $r['current_user_id'])); try { // Save object in the DB ContestProblemOpenedDAO::save($keyContestProblemOpened); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } } } elseif (isset($r['show_solvers']) && $r['show_solvers']) { $response['solvers'] = RunsDAO::GetBestSolvingRunsForProblem($r['problem']->problem_id); } if (!is_null($r['current_user_id'])) { ProblemViewedDAO::MarkProblemViewed($r['current_user_id'], $r['problem']->problem_id); } $response['score'] = self::bestScore($r); $response['status'] = 'ok'; return $response; }
/** * First access time should not change */ public function testAccessTimeIsAlwaysFirstAccessForPrivate() { // Get a contest $contestData = ContestsFactory::createContest(null, 0); // Get a user for our scenario $contestant = UserFactory::createUser(); // Add user to our private contest ContestsFactory::addUser($contestData, $contestant); // Prepare our request $r = new Request(); $r['contest_alias'] = $contestData['request']['alias']; // Log in the user $r['auth_token'] = $this->login($contestant); // Call api $response = ContestController::apiDetails($r); // We need to grab the access time from the ContestUsers table $contest = ContestsDAO::getByAlias($contestData['request']['alias']); $contest_user = ContestsUsersDAO::getByPK($contestant->getUserId(), $contest->getContestId()); $firstAccessTime = $contest_user->getAccessTime(); // Call API again, access time should not change $response = ContestController::apiDetails($r); $contest_user = ContestsUsersDAO::getByPK($contestant->getUserId(), $contest->getContestId()); $this->assertEquals($firstAccessTime, $contest_user->getAccessTime()); }
/** * Test sending runs after the window length expired * * @expectedException NotAllowedToSubmitException */ public function testNewRunOutWindowLengthPublicContest() { // Set the context for the first contest $r = $this->setValidRequest(); // Alter Contest window length to 20 // This means: once I started the contest, I have 20 more mins // to finish it. $contest = ContestsDAO::getByAlias($r["contest_alias"]); $contest->setWindowLength(20); ContestsDAO::save($contest); // Alter first access time of our contestant such that he started // 21 minutes ago, this is, window length has expired by 1 minute $contest_user = ContestsUsersDAO::getByPK($this->contestant->getUserId(), $contest->getContestId()); $contest_user->setAccessTime(date("Y-m-d H:i:s", time() - 21 * 60)); //Window length is in minutes ContestsUsersDAO::save($contest_user); // Call API RunController::apiCreate($r); }
/** * Entry point for Problem Details API * * @param Request $r * @throws InvalidFilesystemOperationException * @throws InvalidDatabaseOperationException */ public static function apiDetails(Request $r) { // Get user. // Allow unauthenticated requests if we are not openning a problem // inside a contest. try { self::authenticateRequest($r); } catch (ForbiddenAccessException $e) { if (!is_null($r["contest_alias"])) { throw $e; } } // Validate request self::validateDetails($r); $response = array(); // Create array of relevant columns $relevant_columns = array("title", "author_id", "alias", "validator", "time_limit", "validator_time_limit", "overall_wall_time_limit", "extra_wall_time", "memory_limit", "output_limit", "visits", "submissions", "accepted", "difficulty", "creation_date", "source", "order", "points", "public", "languages", "slow", "stack_limit", "email_clarifications"); // Read the file that contains the source if (!ProblemController::isLanguageSupportedForProblem($r)) { // If there is no language file for the problem, return the spanish version. $r['lang'] = 'es'; } $statement_type = ProblemController::getStatementType($r); Cache::getFromCacheOrSet(Cache::PROBLEM_STATEMENT, $r["problem"]->getAlias() . "-" . $r["lang"] . "-" . $statement_type, $r, 'ProblemController::getProblemStatement', $file_content, APC_USER_CACHE_PROBLEM_STATEMENT_TIMEOUT); // Add problem statement to source $response["problem_statement"] = $file_content; $response["problem_statement_language"] = $r['lang']; // Add the example input. $sample_input = null; Cache::getFromCacheOrSet(Cache::PROBLEM_SAMPLE, $r["problem"]->getAlias() . "-sample.in", $r, 'ProblemController::getSampleInput', $sample_input, APC_USER_CACHE_PROBLEM_STATEMENT_TIMEOUT); if (!empty($sample_input)) { $response['sample_input'] = $sample_input; } // Add the problem the response $response = array_merge($response, $r["problem"]->asFilteredArray($relevant_columns)); if (!is_null($r['current_user_id'])) { // Create array of relevant columns for list of runs $relevant_columns = array("guid", "language", "status", "verdict", "runtime", "penalty", "memory", "score", "contest_score", "time", "submit_delay"); // Search the relevant runs from the DB $contest = ContestsDAO::getByAlias($r["contest_alias"]); $keyrun = new Runs(array("user_id" => $r["current_user_id"], "problem_id" => $r["problem"]->getProblemId(), "contest_id" => is_null($r["contest"]) ? null : $r["contest"]->getContestId())); // Get all the available runs done by the current_user try { $runs_array = RunsDAO::search($keyrun); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } // Add each filtered run to an array if (count($runs_array) >= 0) { $runs_filtered_array = array(); foreach ($runs_array as $run) { $filtered = $run->asFilteredArray($relevant_columns); $filtered['time'] = strtotime($filtered['time']); array_push($runs_filtered_array, $filtered); } } $response["runs"] = $runs_filtered_array; } if (!is_null($r["contest"])) { // At this point, contestant_user relationship should be established. try { $contest_user = ContestsUsersDAO::CheckAndSaveFirstTimeAccess($r["current_user_id"], $r["contest"]->getContestId()); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } // As last step, register the problem as opened if (!ContestProblemOpenedDAO::getByPK($r["contest"]->getContestId(), $r["problem"]->getProblemId(), $r["current_user_id"])) { //Create temp object $keyContestProblemOpened = new ContestProblemOpened(array("contest_id" => $r["contest"]->getContestId(), "problem_id" => $r["problem"]->getProblemId(), "user_id" => $r["current_user_id"])); try { // Save object in the DB ContestProblemOpenedDAO::save($keyContestProblemOpened); } catch (Exception $e) { // Operation failed in the data layer throw new InvalidDatabaseOperationException($e); } } } else { if (isset($r['show_solvers']) && $r['show_solvers']) { $response['solvers'] = RunsDAO::GetBestSolvingRunsForProblem($r['problem']->problem_id); } } if (!is_null($r['current_user_id'])) { ProblemViewedDAO::MarkProblemViewed($r['current_user_id'], $r['problem']->problem_id); } $response["score"] = self::bestScore($r); $response["status"] = "ok"; return $response; }