WHERE externalid IS NOT NULL AND enabled=1'); while ($row = $teams->next()) { $totals = $DB->q('MAYBETUPLE SELECT points_restricted AS points, totaltime_restricted AS totaltime FROM rankcache WHERE cid = %i AND teamid = %i', $cid, $row['teamid']); if ($totals === null) { $totals['points'] = $totals['totaltime'] = 0; } $rank = calcTeamRank($cdata, $row['teamid'], $totals, TRUE); $lastProblem = $DB->q('MAYBEVALUE SELECT MAX(solvetime_restricted) FROM scorecache WHERE teamid=%i AND cid=%i', $row['teamid'], $cid); if ($lastProblem === NULL) { $lastProblem = 0; } else { $lastProblem = scoretime($lastProblem); } $data .= '<Standing LastProblemTime="' . $lastProblem . '" ProblemsSolved="' . $totals['points'] . '" Rank="' . $rank . '" TeamID="' . $row['externalid'] . '" TotalTime="' . $totals['totaltime'] . '"/>'; } $data .= '</icpc>'; curl_setopt($ch, CURLOPT_POSTFIELDS, $data); } $response = curl_exec($ch); if ($response === FALSE) { error("Error while retrieving data from icpc.baylor.edu: " . curl_error($ch)); } $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($status == 401) { error("Access forbidden, is your token valid?"); } if ($status < 200 || $status >= 300) {
/** * Scoreboard */ function scoreboard($args) { global $DB, $api, $cdatas, $cids; if (isset($args['cid'])) { $cid = safe_int($args['cid']); } else { if (count($cids) == 1) { $cid = reset($cids); } else { $api->createError("No contest ID specified but active contest is ambiguous."); } } $filter = array(); if (array_key_exists('category', $args)) { $filter['categoryid'] = array($args['category']); } if (array_key_exists('country', $args)) { $filter['country'] = array($args['country']); } if (array_key_exists('affiliation', $args)) { $filter['affilid'] = array($args['affiliation']); } $scoreboard = genScoreBoard($cdatas[$cid], !$args['public'], $filter); $prob2label = $DB->q('KEYVALUETABLE SELECT probid, shortname FROM contestproblem WHERE cid = %i', $cid); $res = array(); foreach ($scoreboard['scores'] as $teamid => $data) { $row = array('rank' => $data['rank'], 'team' => $teamid); $row['score'] = array('num_solved' => safe_int($data['num_points']), 'total_time' => safe_int($data['total_time'])); $row['problems'] = array(); foreach ($scoreboard['matrix'][$teamid] as $probid => $pdata) { $prob = array('label' => $prob2label[$probid], 'num_judged' => safe_int($pdata['num_submissions']), 'num_pending' => safe_int($pdata['num_pending']), 'solved' => safe_bool($pdata['is_correct'])); if ($prob['solved']) { $prob['time'] = scoretime($pdata['time']); $first = first_solved($pdata['time'], $scoreboard['summary']['problems'][$probid]['best_time_sort'][$data['sortorder']]); $prob['first_to_solve'] = safe_bool($first); } $row['problems'][] = $prob; } usort($row['problems'], 'cmp_prob_label'); $res[] = $row; } return $res; }
/** * Update tables used for efficiently computing team ranks * * Given a contestid and teamid (re)calculate the time * and solved problems for a team. * * Due to current transactions usage, this function MUST NOT contain * any START TRANSACTION or COMMIT statements. */ function updateRankCache($cid, $team) { global $DB; logmsg(LOG_DEBUG, "updateRankCache '{$cid}' '{$team}'"); $team_penalty = $DB->q("VALUE SELECT penalty FROM team WHERE teamid = %i", $team); // First acquire an advisory lock to prevent other calls to // calcScoreRow() from interfering with our update. $lockstr = "domjudge.{$cid}.{$team}"; if ($DB->q("VALUE SELECT GET_LOCK('{$lockstr}',3)") != 1) { error("updateRankCache failed to obtain lock '{$lockstr}'"); } // Fetch values from scoreboard cache per problem $scoredata = $DB->q("SELECT *, cp.points\n\t FROM scorecache\n\t LEFT JOIN contestproblem cp USING(probid,cid)\n\t WHERE cid = %i and teamid = %i", $cid, $team); $num_points = array('public' => 0, 'restricted' => 0); $total_time = array('public' => $team_penalty, 'restricted' => $team_penalty); while ($srow = $scoredata->next()) { // Only count solved problems foreach (array('public', 'restricted') as $variant) { if ($srow['is_correct_' . $variant]) { $penalty = calcPenaltyTime($srow['is_correct_' . $variant], $srow['submissions_' . $variant]); $num_points[$variant] += $srow['points']; $total_time[$variant] += scoretime($srow['solvetime_' . $variant]) + $penalty; } } } // Update the rank cache table $DB->q("REPLACE INTO rankcache (cid, teamid,\n\t points_restricted, totaltime_restricted,\n\t points_public, totaltime_public)\n\t VALUES (%i,%i,%i,%i,%i,%i)", $cid, $team, $num_points['restricted'], $total_time['restricted'], $num_points['public'], $total_time['public']); // Release the lock if ($DB->q("VALUE SELECT RELEASE_LOCK('{$lockstr}')") != 1) { error("updateRankCache failed to release lock '{$lockstr}'"); } }
function tsv_results_get() { // we'll here assume that the requested file will be of the current contest, // as all our scoreboard interfaces do // 1 External ID 24314 integer // 2 Rank in contest 1 integer // 3 Award Gold Medal string // 4 Number of problems the team has solved 4 integer // 5 Total Time 534 integer // 6 Time of the last submission 233 integer // 7 Group Winner North American string global $cdata, $DB, $extid_to_name; $categs = $DB->q('COLUMN SELECT categoryid FROM team_category WHERE visible = 1'); $sb = genScoreBoard($cdata, true, array('categoryid' => $categs)); $extid_to_name = $DB->q('KEYVALUETABLE SELECT externalid, name FROM team ORDER BY externalid'); $numteams = sizeof($sb['scores']); // determine number of problems solved by median team $cnt = 0; foreach ($sb['scores'] as $teamid => $srow) { $cnt++; $median = $srow['num_correct']; if ($cnt > $numteams / 2) { // XXX: lower or upper median? break; } } $ranks = array(); $group_winners = array(); $data = array(); foreach ($sb['scores'] as $teamid => $srow) { $maxtime = -1; foreach ($sb['matrix'][$teamid] as $prob) { $maxtime = max($maxtime, scoretime($prob['time'])); } $rank = $srow['rank']; $num_correct = $srow['num_correct']; if ($rank <= 4) { $awardstring = "Gold Medal"; } else { if ($rank <= 8) { $awardstring = "Silver Medal"; } else { if ($rank <= 12) { $awardstring = "Bronze Medal"; } else { if ($num_correct >= $median) { // teams with equally solved number of problems get the same rank if (!isset($ranks[$num_correct])) { $ranks[$num_correct] = $rank; } $rank = $ranks[$num_correct]; $awardstring = "Ranked"; } else { $awardstring = "Honorable"; $rank = ""; } } } } $groupwinner = ""; if (!isset($group_winners[$srow['categoryid']])) { $group_winners[$srow['categoryid']] = true; $groupwinner = $DB->q('VALUE SELECT name FROM team_category WHERE categoryid = %i', $srow['categoryid']); } $data[] = array(@$sb['teams'][$teamid]['externalid'], $rank, $awardstring, $srow['num_correct'], $srow['total_time'], $maxtime, $groupwinner); } // sort by rank/name uasort($data, 'cmp_extid_name'); return $data; }
/** * Calculate the rank for a single team based on the cache tables */ function calcTeamRank($cdata, $teamid, $teamtotals, $jury = FALSE) { global $DB; if (empty($cdata)) { return; } $fdata = calcFreezeData($cdata); $cid = $cdata['cid']; // Use jury scoreboard when jury or final scoreboard should be displayed $variant = $jury || $fdata['showfinal'] ? 'restricted' : 'public'; $points = isset($teamtotals['points']) ? $teamtotals['points'] : 0; $totaltime = isset($teamtotals['totaltime']) ? $teamtotals['totaltime'] : 0; $sortorder = $DB->q('VALUE SELECT sortorder FROM team_category LEFT JOIN team USING (categoryid) WHERE teamid = %i', $teamid); // Number of teams that definitely ranked higher $better = $DB->q("VALUE SELECT COUNT(team.teamid)\n\t FROM rankcache AS rc\n\t LEFT JOIN team USING (teamid)\n\t LEFT JOIN team_category USING (categoryid)\n\t WHERE cid = %i AND sortorder = %i AND enabled = 1\n\t AND (points_{$variant} > %i OR\n\t (points_{$variant} = %i AND totaltime_{$variant} < %i))", $cid, $sortorder, $points, $points, $totaltime); $rank = $better + 1; // Resolve ties based on latest correctness points, only necessary when we actually // solved at least one problem, so this list should usually be short if ($points > 0) { $tied = $DB->q("COLUMN SELECT team.teamid\n\t\t FROM rankcache AS rc\n\t\t LEFT JOIN team USING (teamid)\n\t\t LEFT JOIN team_category USING (categoryid)\n\t\t WHERE cid = %i AND sortorder = %i AND enabled = 1\n\t\t AND points_{$variant} = %i AND totaltime_{$variant} = %i", $cid, $sortorder, $points, $totaltime); // All teams that are tied for this position, in most cases this will // only be the team we are finding the rank for, only retrieve rest of // the data when there are actual ties if (count($tied) > 1) { // initialize teamdata for each team $teamdata = array(); foreach ($tied as $tiedid) { $teamdata[$tiedid]['solve_times'] = array(); } // Get submission times for each of the teams $scoredata = $DB->q("SELECT teamid, solvetime_{$variant} AS solvetime\n\t\t\t FROM scorecache AS sc\n\t\t\t LEFT JOIN problem p USING (probid)\n\t\t\t LEFT JOIN contestproblem cp USING (probid, cid)\n\t\t\t WHERE sc.cid = %i AND is_correct_{$variant} = 1\n\t\t\t AND allow_submit = 1 AND teamid IN (%Ai)", $cid, $tied); while ($srow = $scoredata->next()) { $teamdata[$srow['teamid']]['solve_times'][] = scoretime($srow['solvetime']); } // Now check for each team if it is ranked higher than $teamid foreach ($tied as $tiedid) { if ($tiedid == $teamid) { continue; } if (tiebreaker($teamdata[$tiedid], $teamdata[$teamid]) < 0) { $rank++; } } } } return $rank; }