diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-03-10 11:59:40 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-03-10 12:33:12 +0900 |
| commit | 508cfca506d5c4f43d9f9a7377b43d25b2790e6b (patch) | |
| tree | 337d2370810a0427f969a5a50b54c23ecf2eb251 | |
| parent | 1b51b9cf1ea993357af6b76f306e04cdade95bb7 (diff) | |
| download | phperkaigi-2024-albatross-508cfca506d5c4f43d9f9a7377b43d25b2790e6b.tar.gz phperkaigi-2024-albatross-508cfca506d5c4f43d9f9a7377b43d25b2790e6b.tar.zst phperkaigi-2024-albatross-508cfca506d5c4f43d9f9a7377b43d25b2790e6b.zip | |
change score chart to ranking chart
| -rw-r--r-- | services/app/assets/chart.js | 73 | ||||
| -rw-r--r-- | services/app/src/App.php | 7 |
2 files changed, 72 insertions, 8 deletions
diff --git a/services/app/assets/chart.js b/services/app/assets/chart.js index 5873080..9ff08e4 100644 --- a/services/app/assets/chart.js +++ b/services/app/assets/chart.js @@ -42,14 +42,63 @@ document.addEventListener('DOMContentLoaded', async () => { s.scores = bestScores; } + const scoresInChronologicalOrder = stats + .flatMap(s => s.scores.map(score => ({ ...score, user: s.user }))) + .toSorted((a, b) => a.submitted_at - b.submitted_at); + + const scoresAndRanksAtEachTime = (() => { + const result = []; + const currentScoresForUser = new Map(); + for (const { user, submitted_at, code_size } of scoresInChronologicalOrder) { + currentScoresForUser.set(user.name, { user, submitted_at, code_size }); + const ranking = currentScoresForUser + .values() + .toArray() + .toSorted( + (a, b) => a.code_size === b.code_size ? a.submitted_at - b.submitted_at : a.code_size - b.code_size, + ); + const scores = new Map(); + for (const [i, { user, code_size }] of ranking.entries()) { + scores.set(user.name, { + user, + code_size, + rank: i + 1, + }); + } + result.push({ submitted_at: submitted_at, scores }); + } + return result; + })(); + + const rankingHistory = (() => { + const result = new Map(); + for (const { submitted_at, scores } of scoresAndRanksAtEachTime) { + for (const [username, { user, code_size, rank }] of scores.entries()) { + if (!result.has(username)) { + result.set(username, []); + } + const scores = result.get(username); + scores.push({ user, code_size, rank, submitted_at }); + } + } + return result + .values() + .toArray() + .toSorted((a, b) => { + const finalRankA = a[a.length - 1].rank; + const finalRankB = b[b.length - 1].rank; + return finalRankA - finalRankB; + }); + })(); + new Chart( chartCanvas, { type: 'line', data: { - datasets: stats.map(s => ({ - label: `${s.user.name}${s.user.is_admin ? ' (staff)' : ''}`, - data: s.scores.map(row => ({ x: row.submitted_at * 1000, y: row.code_size })), + datasets: rankingHistory.map(s => ({ + label: `${s[0].user.name}${s[0].user.is_admin ? ' (staff)' : ''}`, + data: s.map(row => ({ x: row.submitted_at * 1000, y: row.rank, code_size: row.code_size })), })) }, options: { @@ -67,13 +116,27 @@ document.addEventListener('DOMContentLoaded', async () => { }, title: { display: true, - text: '提出日時', + text: '日時', }, }, y: { title: { display: true, - text: 'コードサイズ (byte)', + text: '順位', + }, + reverse: true, + min: 1, + offset: true, + }, + }, + plugins: { + tooltip: { + callbacks: { + label: (context) => { + const label = context.dataset.label; + const code_size = context.raw.code_size; + return `${label} (${code_size} byte)`; + }, }, }, }, diff --git a/services/app/src/App.php b/services/app/src/App.php index 0c7e3d2..f2f38f4 100644 --- a/services/app/src/App.php +++ b/services/app/src/App.php @@ -256,13 +256,14 @@ final class App if ($quiz === null) { throw new HttpNotFoundException($request); } + $currentUser = $this->getCurrentUser($request); + $isAdmin = $currentUser !== null ? $currentUser->is_admin : false; if ($quiz->isRankingHidden()) { $ranking = null; } else { - $ranking = $answerRepo->getRankingByBestScores($quiz->quiz_id, upto: 20); + $ranking = $answerRepo->getRankingByBestScores($quiz->quiz_id, upto: 20, show_admin: $isAdmin); } - $currentUser = $this->getCurrentUser($request); return $this->render($request, $response, 'quiz_view.html.twig', [ 'page_title' => "問題 #{$quiz->quiz_id}", 'quiz' => $quiz, @@ -964,7 +965,7 @@ final class App ])->withStatus(403); } - $correctAnswers = $answerRepo->listAllCorrectAnswers($quiz->quiz_id); + $correctAnswers = $answerRepo->listAllCorrectAnswers($quiz->quiz_id, show_admin: $isAdmin); $stats = []; foreach ($correctAnswers as $answer) { |
