Skip to content

Commit

Permalink
Merge pull request #143 from UniversalOJ/export-contest-standings
Browse files Browse the repository at this point in the history
feat(contest): add standings export
  • Loading branch information
renbaoshuo authored Aug 9, 2024
2 parents be41da9 + 0eed678 commit d5e6b4b
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 1 deletion.
10 changes: 9 additions & 1 deletion web/app/controllers/contest_inside.php
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,14 @@ function echoStandings() {
$contest_data = queryContestData($contest);
calcStandings($contest, $contest_data, $score, $standings);

if ($contest['cur_progress'] >= CONTEST_FINISHED && hasContestPermission(Auth::user(), $contest)) {
echo <<<EOD
<div>
<a class="btn btn-info" href="/contest/{$contest['id']}/export_standings">下载排名</a>
</div>
EOD;
}

uojIncludeView('contest-standings', [
'contest' => $contest,
'standings' => $standings,
Expand Down Expand Up @@ -550,4 +558,4 @@ function echoContestFinished() {
<?php } ?>
</div>
</div>
<?php echoUOJPageFooter() ?>
<?php echoUOJPageFooter() ?>
70 changes: 70 additions & 0 deletions web/app/controllers/export_contest_standings_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

if (!validateUInt($_GET['id']) || !($contest = queryContest($_GET['id']))) {
become404Page();
}
genMoreContestInfo($contest);

if (!hasContestPermission(Auth::user(), $contest)) {
becomeMsgPage('您没有权限下载此比赛的排名。');
}
if ($contest['cur_progress'] < CONTEST_FINISHED) {
becomeMsgPage('比赛尚未结束,无法下载排名。');
}

$contest_data = queryContestData($contest);
calcStandings($contest, $contest_data, $score, $standings);

function array2csv(array &$array) {
if (count($array) == 0) {
return null;
}

ob_start();

$df = fopen("php://output", 'w');

fputcsv($df, array_keys(reset($array)));

foreach ($array as $row) {
fputcsv($df, $row);
}

fclose($df);

return ob_get_clean();
}

$export_data = [];
$csv_header = ['Rank', 'Username', 'Score', 'Penalty'];
$n_problems = count($contest_data['problems']);

for ($i = 0; $i < $n_problems; $i++) {
$csv_header[] = chr(ord('A') + $i);
$csv_header[] = chr(ord('A') + $i) . '_penalty';
$csv_header[] = chr(ord('A') + $i) . '_submission_id';
}

$export_data[] = $csv_header;

// Convert data in $standings and $score to $export_data
foreach ($standings as $rank => $row) {
// $row: rank, username, score, penalty
$res = [$rank + 1, $row[2][0], $row[0], $row[1]];
for ($i = 0; $i < $n_problems; $i++) {
// $score[$row[2][0]][$i]: score, penalty, submission_id
$res[] = isset($score[$row[2][0]][$i][0]) ? $score[$row[2][0]][$i][0] : "";
$res[] = isset($score[$row[2][0]][$i][1]) ? $score[$row[2][0]][$i][1] : "";
$res[] = isset($score[$row[2][0]][$i][2]) ? $score[$row[2][0]][$i][2] : "";
}
$export_data[] = $res;
}

$csv = array2csv($export_data);

header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: {$now} GMT");
header("Content-Type: text/csv");
header("Content-Disposition: attachment;filename={$contest['id']}_standings.csv");

die($csv);
1 change: 1 addition & 0 deletions web/app/route.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
Route::any('/contest/{id}/submissions', '/contest_inside.php?tab=submissions');
Route::any('/contest/{id}/standings', '/contest_inside.php?tab=standings');
Route::any('/contest/{id}/backstage', '/contest_inside.php?tab=backstage');
Route::any('/contest/{id}/export_standings', '/export_contest_standings_table.php');
Route::any('/contest/{contest_id}/problem/{id}', '/problem.php');
Route::any('/contest/{contest_id}/problem/{id}/statistics', '/problem_statistics.php');

Expand Down

0 comments on commit d5e6b4b

Please sign in to comment.