From d7cc716216ec0261864e02d844cfd0207264616f Mon Sep 17 00:00:00 2001 From: Maarten Sijm <9739541+mpsijm@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:59:19 +0100 Subject: [PATCH 01/23] #2307 Multi-pass: copy feedback dir between passes (but remove nextpass.in) (cherry picked from commit e2de765d200435c90cba78e0e0c166487839266b) --- judge/judgedaemon.main.php | 9 +++++++++ judge/testcase_run.sh | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/judge/judgedaemon.main.php b/judge/judgedaemon.main.php index 262d3a1004..83178fcc66 100644 --- a/judge/judgedaemon.main.php +++ b/judge/judgedaemon.main.php @@ -1410,6 +1410,15 @@ function judge(array $judgeTask): bool $passdir = $testcasedir . '/' . $passCnt; mkdir($passdir, 0755, true); + if ($passCnt > 1) { + $cpcmd = 'cp -R ' . $passdir . '/../' . ($passCnt - 1) . '/feedback ' . $passdir . '/'; + system($cpcmd); + logmsg(LOG_INFO, " Copying feedback dir from pass " . ($passCnt - 1) . ': ' . $cpcmd); + $rmcmd = 'rm ' . $passdir . '/feedback/nextpass.in'; + system($rmcmd); + logmsg(LOG_INFO, " Executing " . $rmcmd); + } + // Copy program with all possible additional files to testcase // dir. Use hardlinks to preserve space with big executables. $programdir = $passdir . '/execdir'; diff --git a/judge/testcase_run.sh b/judge/testcase_run.sh index 7cffaa7ec1..fa316ee76b 100755 --- a/judge/testcase_run.sh +++ b/judge/testcase_run.sh @@ -195,7 +195,7 @@ if [ $COMBINED_RUN_COMPARE -eq 1 ]; then # A combined run and compare script may now already need the # feedback directory, and perhaps access to the test answers (but # only the original that lives outside the chroot). - mkdir feedback + mkdir -p feedback RUNARGS="$RUNARGS $TESTOUT compare.meta feedback" fi @@ -232,8 +232,8 @@ if [ $COMBINED_RUN_COMPARE -eq 0 ]; then exitcode=0 # Create dir for feedback files and make it writable for $RUNUSER - mkdir feedback - chmod a+w feedback + mkdir -p feedback + chmod -R a+w feedback runcheck $GAINROOT "$RUNGUARD" ${DEBUG:+-v} $CPUSET_OPT -u "$RUNUSER" -g "$RUNGROUP" \ -m $SCRIPTMEMLIMIT -t $SCRIPTTIMELIMIT --no-core \ From 4885ad3f9ab8c5fcb4b402c2c175148c36cdadbf Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Sat, 23 Mar 2024 10:56:24 +0100 Subject: [PATCH 02/23] Ugly code for a mobile-friendly view, only for demonstration purposes. (cherry picked from commit b93d0ad90deb8947ca5ee6d0f1f5d6017bd3f79d) --- webapp/public/style_domjudge.css | 21 +++ webapp/src/Twig/TwigExtension.php | 37 ++++ .../partials/scoreboard_table.html.twig | 173 +++++++++++++++++- 3 files changed, 228 insertions(+), 3 deletions(-) diff --git a/webapp/public/style_domjudge.css b/webapp/public/style_domjudge.css index e443f7271c..6962ee6662 100644 --- a/webapp/public/style_domjudge.css +++ b/webapp/public/style_domjudge.css @@ -699,3 +699,24 @@ blockquote { padding: 3px; border-radius: 5px; } + +.strike-diagonal { + position: relative; + text-align: center; +} + +.strike-diagonal:before { + position: absolute; + content: ""; + left: 0; + top: 50%; + right: 0; + border-top: 2px solid; + border-color: firebrick; + + -webkit-transform:rotate(-35deg); + -moz-transform:rotate(-35deg); + -ms-transform:rotate(-35deg); + -o-transform:rotate(-35deg); + transform:rotate(-35deg); +} \ No newline at end of file diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index 2a73e324e4..93f7ebdc38 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -23,6 +23,7 @@ use App\Service\DOMJudgeService; use App\Service\EventLogService; use App\Service\SubmissionService; +use App\Utils\Scoreboard\ScoreboardMatrixItem; use App\Utils\Utils; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; @@ -110,6 +111,7 @@ public function getFilters(): array new TwigFilter('fileTypeIcon', $this->fileTypeIcon(...)), new TwigFilter('problemBadge', $this->problemBadge(...), ['is_safe' => ['html']]), new TwigFilter('problemBadgeForContest', $this->problemBadgeForContest(...), ['is_safe' => ['html']]), + new TwigFilter('problemBadgeMaybe', $this->problemBadgeMaybe(...), ['is_safe' => ['html']]), new TwigFilter('printMetadata', $this->printMetadata(...), ['is_safe' => ['html']]), new TwigFilter('printWarningContent', $this->printWarningContent(...), ['is_safe' => ['html']]), new TwigFilter('entityIdBadge', $this->entityIdBadge(...), ['is_safe' => ['html']]), @@ -1091,6 +1093,41 @@ public function problemBadge(ContestProblem $problem, bool $grayedOut = false): ); } + public function problemBadgeMaybe(ContestProblem $problem, ScoreboardMatrixItem $matrixItem): string + { + $rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff'); + if (!$matrixItem->isCorrect) { + $rgb = 'whitesmoke'; + } + $background = Utils::parseHexColor($rgb); + + // Pick a border that's a bit darker. + $darker = $background; + $darker[0] = max($darker[0] - 64, 0); + $darker[1] = max($darker[1] - 64, 0); + $darker[2] = max($darker[2] - 64, 0); + $border = Utils::rgbToHex($darker); + + // Pick the foreground text color based on the background color. + $foreground = ($background[0] + $background[1] + $background[2] > 450) ? '#000000' : '#ffffff'; + if (!$matrixItem->isCorrect) { + $foreground = 'silver'; + $border = 'linen'; + } + + $ret = sprintf( + '%s', + $rgb, + $border, + $foreground, + $problem->getShortname() + ); + if (!$matrixItem->isCorrect && $matrixItem->numSubmissions > 0) { + $ret = '' . $ret . ''; + } + return $ret; + } + public function problemBadgeForContest(Problem $problem, ?Contest $contest = null): string { $contest ??= $this->dj->getCurrentContest(); diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index b0f64f2eab..f4bad952ed 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -28,7 +28,7 @@ {% endif %} - +
{# output table column groups (for the styles) #} @@ -316,6 +316,173 @@
+ + + {# output table column groups (for the styles) #} + + + {% if showFlags %} + + {% else %} + + {% endif %} + {% if showAffiliationLogos %} + + {% endif %} + + + + + + + {% set teamColspan = 2 %} + {% if showAffiliationLogos %} + {% set teamColspan = teamColspan + 1 %} + {% endif %} + + + + + + + + + {% set previousSortOrder = -1 %} + {% set previousTeam = null %} + {% set backgroundColors = {"#FFFFFF": 1} %} + {% set medalCount = 0 %} + {% for score in scores %} + {% set classes = [] %} + {% if score.team.category.sortorder != previousSortOrder %} + {% if previousSortOrder != -1 %} + {# Output summary of previous sort order #} + {% include 'partials/scoreboard_summary.html.twig' with {sortOrder: previousSortOrder} %} + {% endif %} + {% set classes = classes | merge(['sortorderswitch']) %} + {% set previousSortOrder = score.team.category.sortorder %} + {% set previousTeam = null %} + {% endif %} + + {# process medal color #} + {% set medalColor = '' %} + {% if showLegends %} + {% set medalColor = score.team | medalType(contest, scoreboard) %} + {% endif %} + + {# check whether this is us, otherwise use category colour #} + {% if myTeamId is defined and myTeamId == score.team.teamid %} + {% set classes = classes | merge(['scorethisisme']) %} + {% set color = '#FFFF99' %} + {% else %} + {% set color = score.team.category.color %} + {% endif %} + + + + {% if showAffiliationLogos %} + + {% endif %} + {% if color is null %} + {% set color = "#FFFFFF" %} + {% set colorClass = "_FFFFFF" %} + {% else %} + {% set colorClass = color | replace({"#": "_"}) %} + {% set backgroundColors = backgroundColors | merge({(color): 1}) %} + {% endif %} + + {% set totalTime = score.totalTime %} + {% if scoreInSeconds %} + {% set totalTime = totalTime | printTimeRelative %} + {% endif %} + {% set totalPoints = score.numPoints %} + + + + + + {% endfor %} + +
rankteamscore
+ {# Only print rank when score is different from the previous team #} + {% if not displayRank %} + ? + {% elseif previousTeam is null or scoreboard.scores[previousTeam.teamid].rank != score.rank %} + {{ score.rank }} + {% else %} + {% endif %} + {% set previousTeam = score.team %} + + {% if showFlags %} + {% if score.team.affiliation %} + {% set link = null %} + {% if jury %} + {% set link = path('jury_team_affiliation', {'affilId': score.team.affiliation.affilid}) %} + {% endif %} + + {{ score.team.affiliation.country|countryFlag }} + + {% endif %} + {% endif %} + + {% if score.team.affiliation %} + {% set link = null %} + {% if jury %} + {% set link = path('jury_team_affiliation', {'affilId': score.team.affiliation.affilid}) %} + {% endif %} + + {% set affiliationId = score.team.affiliation.externalid %} + {% set affiliationImage = affiliationId | assetPath('affiliation') %} + {% if affiliationImage %} + {{ score.team.affiliation.name }} + {% else %} + {{ affiliationId }} + {% endif %} + + {% endif %} + + {% set link = null %} + {% set extra = null %} + {% if static %} + {% set link = '#' %} + {% set extra = 'data-bs-toggle="modal" data-bs-target="#team-modal-' ~ score.team.teamid ~ '"' %} + {% else %} + {% if jury %} + {% set link = path('jury_team', {teamId: score.team.teamid}) %} + {% elseif public %} + {% set link = path('public_team', {teamId: score.team.teamid}) %} + {% set extra = 'data-ajax-modal' %} + {% else %} + {% set link = path('team_team', {teamId: score.team.teamid}) %} + {% set extra = 'data-ajax-modal' %} + {% endif %} + {% endif %} + + + {% if false and usedCategories | length > 1 and scoreboard.bestInCategory(score.team, limitToTeamIds) %} + + {{ score.team.category.name }} + + {% endif %} + {{ score.team.effectiveName }} + + {% if showAffiliations %} + + {% if score.team.affiliation %} + {{ score.team.affiliation.name }} + {% endif %} + + {% endif %} + + {{ totalPoints }}
{{ totalTime }}
+ + {% for problem in problems %} + {% set matrixItem = scoreboard.matrix[score.team.teamid][problem.probid] %} + {{ problem | problemBadgeMaybe(matrixItem) }} + {% endfor %} +
+ {% if static %} {% for score in scores %} {% embed 'partials/modal.html.twig' with {'modalId': 'team-modal-' ~ score.team.teamid} %} @@ -366,7 +533,7 @@ {% else %} {% set cellColors = {first: 'Solved first', correct: 'Solved', incorrect: 'Tried, incorrect', pending: 'Tried, pending', neutral: 'Untried'} %} {% endif %} - +
@@ -385,7 +552,7 @@ {% endif %} {% if medalsEnabled %} -
Cell colours
+
From 591737de54eef50c20aa21d1c623d104425cd9a8 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 5 Apr 2024 15:19:35 +0200 Subject: [PATCH 03/23] Update mobile scoreboard. - Make team names not fall of the screen by calculating their max width. - Make non mobile scoreboard not left-aligned. - Add a bit of margin to the header. - Make the problem boxes right aligned. - Change the card at the top to show 2 lines (name + contestt time) on mobile. (cherry picked from commit 4f6b3ecf7711e43f489f6827e38ecfac5cf6ac59) --- webapp/public/style_domjudge.css | 3 +- .../templates/partials/scoreboard.html.twig | 52 +++++++++++-------- webapp/templates/public/scoreboard.html.twig | 3 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/webapp/public/style_domjudge.css b/webapp/public/style_domjudge.css index 6962ee6662..7ca835411f 100644 --- a/webapp/public/style_domjudge.css +++ b/webapp/public/style_domjudge.css @@ -219,6 +219,7 @@ del { display: block; overflow: hidden; } + .toolong:after { content: ""; width: 30%; @@ -719,4 +720,4 @@ blockquote { -ms-transform:rotate(-35deg); -o-transform:rotate(-35deg); transform:rotate(-35deg); -} \ No newline at end of file +} diff --git a/webapp/templates/partials/scoreboard.html.twig b/webapp/templates/partials/scoreboard.html.twig index 3c4f58e4e0..4fb8a18bfb 100644 --- a/webapp/templates/partials/scoreboard.html.twig +++ b/webapp/templates/partials/scoreboard.html.twig @@ -18,31 +18,37 @@ {% endif %}
-
- {{ current_contest.name }} - - {% if scoreboard is null %} - {{ current_contest | printContestStart }} - {% elseif scoreboard.freezeData.showFinal(jury) %} - {% if current_contest.finalizetime is empty %} - preliminary results - not final - {% else %} - final standings - {% endif %} - {% elseif scoreboard.freezeData.stopped %} - contest over, waiting for results - {% elseif static %} - {% set now = 'now'|date('U') %} - {{ current_contest.starttime | printelapsedminutes(now) }} - {% else %} - {% if current_contest.freezeData.started %} - started: +
+
+
+ {{ current_contest.name }} +
+
+ + {% if scoreboard is null %} + {{ current_contest | printContestStart }} + {% elseif scoreboard.freezeData.showFinal(jury) %} + {% if current_contest.finalizetime is empty %} + preliminary results - not final + {% else %} + final standings + {% endif %} + {% elseif scoreboard.freezeData.stopped %} + contest over, waiting for results + {% elseif static %} + {% set now = 'now'|date('U') %} + {{ current_contest.starttime | printelapsedminutes(now) }} {% else %} - starts: + {% if current_contest.freezeData.started %} + started: + {% else %} + starts: + {% endif %} + {{ current_contest.starttime | printtime }} - ends: {{ current_contest.endtime | printtime }} {% endif %} - {{ current_contest.starttime | printtime }} - ends: {{ current_contest.endtime | printtime }} - {% endif %} - + +
+
{% if static %} diff --git a/webapp/templates/public/scoreboard.html.twig b/webapp/templates/public/scoreboard.html.twig index 6ab323c152..90b7cddd7a 100644 --- a/webapp/templates/public/scoreboard.html.twig +++ b/webapp/templates/public/scoreboard.html.twig @@ -12,7 +12,7 @@ {% set bannerImage = globalBannerAssetPath() %} {% endif %} {% if bannerImage %} - + {% endif %}
@@ -53,6 +53,7 @@ {% if static and refresh is defined %} disableRefreshOnModal(); {% endif %} + resizeMobileTeamNames(); }; {% if static and refresh is defined %} From 276698f2c3af0bde2fd12a8591d0bc11c795b843 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 5 Apr 2024 16:11:43 +0200 Subject: [PATCH 04/23] Scale problem badges for very small screens. (cherry picked from commit 3cd918fbdbf34c0b7f538c6b86f0b0a1b0dea882) --- webapp/public/style_domjudge.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webapp/public/style_domjudge.css b/webapp/public/style_domjudge.css index 7ca835411f..df57f21594 100644 --- a/webapp/public/style_domjudge.css +++ b/webapp/public/style_domjudge.css @@ -220,6 +220,12 @@ del { overflow: hidden; } +.mobile-problem-badges { + position: relative; + display: block; + white-space: nowrap; +} + .toolong:after { content: ""; width: 30%; From f2d4a3a90c1cebf4db7be5b2000330e268b0413a Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Sat, 6 Apr 2024 13:28:22 +0200 Subject: [PATCH 05/23] Move scoreboard javascript to JS file so browser can cache it. (cherry picked from commit 0a820070d7d6c188e06180487661555cd1c99fb8) --- webapp/public/js/domjudge.js | 49 ++++++++++++++++++++ webapp/templates/public/scoreboard.html.twig | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index 97dd01eb73..4a2ef98e2d 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -944,3 +944,52 @@ function initializeKeyboardShortcuts() { } }); } + +// Make sure the items in the desktop scoreboard fit +document.querySelectorAll(".desktop-scoreboard .forceWidth:not(.toolong)").forEach(el => { + if (el instanceof Element && el.scrollWidth > el.offsetWidth) { + el.classList.add("toolong"); + } +}); + +/** + * Helper method to resize mobile team names and problem badges + */ +function resizeMobileTeamNamesAndProblemBadges() { + // Make team names fit on the screen, but only when the mobile + // scoreboard is visible + const mobileScoreboard = document.querySelector('.mobile-scoreboard'); + if (mobileScoreboard.offsetWidth === 0) { + return; + } + const windowWidth = document.body.offsetWidth; + const teamNameMaxWidth = Math.max(10, windowWidth - 150); + const problemBadgesMaxWidth = Math.max(10, windowWidth - 78); + document.querySelectorAll(".mobile-scoreboard .forceWidth:not(.toolong)").forEach(el => { + el.classList.remove("toolong"); + el.style.maxWidth = teamNameMaxWidth + 'px'; + if (el instanceof Element && el.scrollWidth > el.offsetWidth) { + el.classList.add("toolong"); + } else { + el.classList.remove("toolong"); + } + }); + document.querySelectorAll(".mobile-scoreboard .mobile-problem-badges:not(.toolong)").forEach(el => { + el.classList.remove("toolong"); + el.style.maxWidth = problemBadgesMaxWidth + 'px'; + if (el instanceof Element && el.scrollWidth > el.offsetWidth) { + el.classList.add("toolong"); + const scale = el.offsetWidth / el.scrollWidth; + const offset = -1 * (el.scrollWidth - el.offsetWidth) / 2; + el.style.transform = `scale(${scale}) translateX(${offset}px)`; + } else { + el.classList.remove("toolong"); + el.style.transform = null; + } + }); +} + +if (document.querySelector('.mobile-scoreboard')) { + window.addEventListener('resize', resizeMobileTeamNamesAndProblemBadges); + resizeMobileTeamNamesAndProblemBadges(); +} diff --git a/webapp/templates/public/scoreboard.html.twig b/webapp/templates/public/scoreboard.html.twig index 90b7cddd7a..e1f0dbb559 100644 --- a/webapp/templates/public/scoreboard.html.twig +++ b/webapp/templates/public/scoreboard.html.twig @@ -53,7 +53,7 @@ {% if static and refresh is defined %} disableRefreshOnModal(); {% endif %} - resizeMobileTeamNames(); + resizeMobileTeamNamesAndProblemBadges(); }; {% if static and refresh is defined %} From 3d94e05c6bc309dba9ca4c22e50e5b97bd320e7e Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Mon, 8 Apr 2024 20:47:36 +0200 Subject: [PATCH 06/23] Run mobile scoreboard logic only after the page has loaded. (cherry picked from commit cbef321ad6ff7bd2d3ad258a914277b1c06be53e) --- webapp/public/js/domjudge.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index 4a2ef98e2d..b41497676b 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -989,7 +989,9 @@ function resizeMobileTeamNamesAndProblemBadges() { }); } -if (document.querySelector('.mobile-scoreboard')) { - window.addEventListener('resize', resizeMobileTeamNamesAndProblemBadges); - resizeMobileTeamNamesAndProblemBadges(); -} +$(function() { + if (document.querySelector('.mobile-scoreboard')) { + window.addEventListener('resize', resizeMobileTeamNamesAndProblemBadges); + resizeMobileTeamNamesAndProblemBadges(); + } +}); From fcbf151d6eaecb389f09a11497617bc3445a639f Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Sat, 25 May 2024 13:24:01 +0200 Subject: [PATCH 07/23] Fix pending subs on mobile scoreboard. (cherry picked from commit 6f0d7dfa9c693e38f3eada51dbe47d6e6bfff8e6) --- webapp/src/Twig/TwigExtension.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index 93f7ebdc38..ae06221a6c 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -1122,8 +1122,12 @@ public function problemBadgeMaybe(ContestProblem $problem, ScoreboardMatrixItem $foreground, $problem->getShortname() ); - if (!$matrixItem->isCorrect && $matrixItem->numSubmissions > 0) { - $ret = '' . $ret . ''; + if (!$matrixItem->isCorrect) { + if ($matrixItem->numSubmissionsPending > 0) { + $ret = '' . $ret . ''; + } else if ($matrixItem->numSubmissions > 0) { + $ret = '' . $ret . ''; + } } return $ret; } From eae5f41d77897bd5ffe8c03ab52e288063ea9bee Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 6 Sep 2024 11:49:30 +0200 Subject: [PATCH 08/23] Display as table to center align properly (cherry picked from commit ef836fe60e35579c36249ae6ff9cc46e0b8fda82) --- webapp/templates/partials/scoreboard_table.html.twig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index f4bad952ed..761fd2c6ca 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -28,7 +28,7 @@ {% endif %} -
Medals {% if not scoreboard.freezeData.showFinal %}(tentative){% endif %}
+
{# output table column groups (for the styles) #} @@ -533,7 +533,7 @@ {% else %} {% set cellColors = {first: 'Solved first', correct: 'Solved', incorrect: 'Tried, incorrect', pending: 'Tried, pending', neutral: 'Untried'} %} {% endif %} -
+
@@ -552,7 +552,7 @@ {% endif %} {% if medalsEnabled %} -
Cell colours
+
From 33edd074295a42b8f7d864e9be78e4bcf0896053 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Wed, 23 Oct 2024 20:03:57 +0200 Subject: [PATCH 09/23] Hide scoreboard summary between sortorders for mobile scoreboard (cherry picked from commit c8a5ef7f0ac716f3278205852afbcd00288c5ca6) --- webapp/templates/partials/scoreboard_table.html.twig | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index 761fd2c6ca..9fbba7c52b 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -354,10 +354,6 @@ {% for score in scores %} {% set classes = [] %} {% if score.team.category.sortorder != previousSortOrder %} - {% if previousSortOrder != -1 %} - {# Output summary of previous sort order #} - {% include 'partials/scoreboard_summary.html.twig' with {sortOrder: previousSortOrder} %} - {% endif %} {% set classes = classes | merge(['sortorderswitch']) %} {% set previousSortOrder = score.team.category.sortorder %} {% set previousTeam = null %} @@ -471,7 +467,7 @@ - {# output table column groups (for the styles) #} - + {% if enable_ranking %} + + {% endif %} {% if showFlags %} {% else %} @@ -331,9 +333,11 @@ {% endif %} - - - + {% if enable_ranking %} + + + + {% endif %} {% set teamColspan = 2 %} {% if showAffiliationLogos %} @@ -341,9 +345,13 @@ {% endif %} - + {% if enable_ranking %} + + {% endif %} - + {% if enable_ranking %} + + {% endif %} @@ -373,16 +381,18 @@ {% set color = score.team.category.color %} {% endif %} - + {% if enable_ranking %} + + {% endif %} - {% set totalTime = score.totalTime %} - {% if scoreInSeconds %} - {% set totalTime = totalTime | printTimeRelative %} + {% if enable_ranking %} + {% set totalTime = score.totalTime %} + {% if scoreInSeconds %} + {% set totalTime = totalTime | printTimeRelative %} + {% endif %} + {% set totalPoints = score.numPoints %} + {% endif %} - {% set totalPoints = score.numPoints %} - -
Medals {% if not scoreboard.freezeData.showFinal %}(tentative){% endif %}{{ totalPoints }}
{{ totalTime }}
+ {% for problem in problems %} {% set matrixItem = scoreboard.matrix[score.team.teamid][problem.probid] %} From 40c91dd026ccc4c8f4924a60032765e2069d36bf Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Wed, 23 Oct 2024 20:04:08 +0200 Subject: [PATCH 10/23] Hide ugly bar before problem badges on mobile scoreboard (cherry picked from commit 5e51d8111040c65951d9f460568dd5936d6e3480) --- webapp/public/style_domjudge.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webapp/public/style_domjudge.css b/webapp/public/style_domjudge.css index df57f21594..cccfe2d517 100644 --- a/webapp/public/style_domjudge.css +++ b/webapp/public/style_domjudge.css @@ -195,6 +195,9 @@ del { border-right: 1px solid silver; padding: 0; } +.scoreboard td.no-border, .scoreboard th.no-border { + border: none; +} .scoreboard td.score_cell { min-width: 4.2em; border-right: none; From e3a730ef10a826458c22e4c3c9b814aba622aa6a Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 12:13:51 +0100 Subject: [PATCH 11/23] Disable ranking on mobile scoreboard when requested (cherry picked from commit 38447d5222ac43e7dbffc9e222ad4beaac8a51c2) --- .../partials/scoreboard_table.html.twig | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index 9fbba7c52b..ca6203fb82 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -320,7 +320,9 @@
rankrankteamscorescore
- {# Only print rank when score is different from the previous team #} - {% if not displayRank %} - ? - {% elseif previousTeam is null or scoreboard.scores[previousTeam.teamid].rank != score.rank %} - {{ score.rank }} - {% else %} - {% endif %} - {% set previousTeam = score.team %} - + {# Only print rank when score is different from the previous team #} + {% if not displayRank %} + ? + {% elseif previousTeam is null or scoreboard.scores[previousTeam.teamid].rank != score.rank %} + {{ score.rank }} + {% else %} + {% endif %} + {% set previousTeam = score.team %} + {% if showFlags %} {% if score.team.affiliation %} @@ -459,12 +469,14 @@ {% endif %} {{ totalPoints }}
{{ totalTime }}
{{ totalPoints }}
{{ totalTime }}
From e6ba5063ddec6a19f0a744d03dd91bf9ba6e781f Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 12:26:23 +0100 Subject: [PATCH 12/23] Allow problem badges to go below country flag and affiliation logo (cherry picked from commit 95deac3cbb1b0cb6941ffce15edc6b6c55e895d6) --- webapp/templates/partials/scoreboard_table.html.twig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index ca6203fb82..c7292c1dc0 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -479,8 +479,12 @@ {% endif %}
- + {% if showAffiliationLogos %} + {% set problemSpan = 3 %} + {% else %} + {% set problemSpan = 2 %} + {% endif %} + {% for problem in problems %} {% set matrixItem = scoreboard.matrix[score.team.teamid][problem.probid] %} {{ problem | problemBadgeMaybe(matrixItem) }} From 7acfad4f8df4c924bc20dbfaaebce5443b8cd125 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 12:36:20 +0100 Subject: [PATCH 13/23] Move colgroups to correct position (cherry picked from commit 3adc23824c55633b6fa3acb173b543121ddf0844) --- .../templates/partials/scoreboard_table.html.twig | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index c7292c1dc0..b186cde787 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -30,6 +30,11 @@ + {% set teamColspan = 2 %} + {% if showAffiliationLogos %} + {% set teamColspan = teamColspan + 1 %} + {% endif %} + {# output table column groups (for the styles) #} {% if enable_ranking %} @@ -58,12 +63,6 @@ {% endfor %} {% endif %} - - {% set teamColspan = 2 %} - {% if showAffiliationLogos %} - {% set teamColspan = teamColspan + 1 %} - {% endif %} - {% if enable_ranking %} @@ -317,7 +316,6 @@
- {# output table column groups (for the styles) #} {% if enable_ranking %} @@ -338,6 +336,7 @@ {% endif %} + {% set teamColspan = 2 %} {% if showAffiliationLogos %} From 611ff9cd0fb8a728e3d819c60cb51bb0ace8fa9a Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 13:37:33 +0100 Subject: [PATCH 14/23] Fix HTML W3C stuff (cherry picked from commit 108970b57ff528f806cd4330d2e311dee9bc04eb) --- webapp/public/style_domjudge.css | 2 +- webapp/templates/partials/scoreboard_table.html.twig | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/webapp/public/style_domjudge.css b/webapp/public/style_domjudge.css index cccfe2d517..7b8db354a2 100644 --- a/webapp/public/style_domjudge.css +++ b/webapp/public/style_domjudge.css @@ -284,7 +284,7 @@ img.affiliation-logo { .silver-medal { background-color: #aaa } .bronze-medal { background-color: #c08e55 } -#scoresolv,#scoretotal { width: 2.5em; } +#scoresolv,#scoretotal,#scoresolvmobile,#scoretotalmobile { width: 2.5em; } .scorenc,.scorett,.scorepl { text-align: center; width: 2ex; } .scorenc { font-weight: bold; } td.scorenc { border-color: silver; border-right: 0; } diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index b186cde787..5077ca53f3 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -38,22 +38,22 @@ {# output table column groups (for the styles) #} {% if enable_ranking %} - + {% endif %} {% if showFlags %} - + {% else %} {% endif %} {% if showAffiliationLogos %} - + {% endif %} - + {% if enable_ranking %} - - + + {% endif %} From 6d5635d441148da9b1d0aa882874d79ac447de05 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 13:45:38 +0100 Subject: [PATCH 15/23] Fix double team:xxx ID's (cherry picked from commit 706fc05ff61fd8c778d6a7ba31d4738c0f538636) --- webapp/public/js/domjudge.js | 4 +--- webapp/templates/partials/scoreboard_table.html.twig | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index b41497676b..c0fe0496aa 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -246,9 +246,7 @@ function getHeartCol(row) { function getTeamname(row) { - var res = row.getAttribute("id"); - if ( res === null ) return res; - return res.replace(/^team:/, ''); + return row.getAttribute("data-team-id"); } function toggle(id, show) diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index 5077ca53f3..3d0d9f9607 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -133,7 +133,7 @@ {% else %} {% set color = score.team.category.color %} {% endif %} - + {% if enable_ranking %} + {% if enable_ranking %}
{# Only print rank when score is different from the previous team #} @@ -379,7 +379,7 @@ {% else %} {% set color = score.team.category.color %} {% endif %} -
{# Only print rank when score is different from the previous team #} From d2144c281520167b3b77e8535eeb4cc6477c8f65 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 14:14:34 +0100 Subject: [PATCH 16/23] Fix elseif (cherry picked from commit 6a6ae1067f6880388bf88e926c53a9d167c25a68) --- webapp/src/Twig/TwigExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index ae06221a6c..a496f540be 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -1125,7 +1125,7 @@ public function problemBadgeMaybe(ContestProblem $problem, ScoreboardMatrixItem if (!$matrixItem->isCorrect) { if ($matrixItem->numSubmissionsPending > 0) { $ret = '' . $ret . ''; - } else if ($matrixItem->numSubmissions > 0) { + } elseif ($matrixItem->numSubmissions > 0) { $ret = '' . $ret . ''; } } From 7ee7fa0a6074f5f8eba82c548977bc5f99bae935 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 15:31:39 +0100 Subject: [PATCH 17/23] Fix hearts for scoreboard on local filesystem Fixes #2724 (cherry picked from commit 42bb954aa4be6b13b3c337b82a99e8eeeaf00986) --- webapp/public/js/domjudge.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index c0fe0496aa..2135c38bb8 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -200,7 +200,7 @@ function getCookie(name) function getSelectedTeams() { - var cookieVal = getCookie("domjudge_teamselection"); + var cookieVal = localStorage.getItem("domjudge_teamselection"); if (cookieVal === null || cookieVal === "") { return new Array(); } @@ -284,10 +284,15 @@ function toggle(id, show) } var cookieVal = JSON.stringify(favTeams); - setCookie("domjudge_teamselection", cookieVal); + localStorage.setItem("domjudge_teamselection", cookieVal); $('.loading-indicator').addClass('ajax-loader'); + // If we are on a local file system, reload the window + if (window.location.protocol === 'file:') { + window.location.reload(); + return; + } $.ajax({ url: scoreboardUrl, cache: false From 121276b9bb23f5e75aa662362b98a6cdadd5b4ee Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Fri, 22 Nov 2024 15:18:27 +0100 Subject: [PATCH 18/23] =?UTF-8?q?Allow=20to=20=E2=9D=A4=EF=B8=8F=20on=20mo?= =?UTF-8?q?bile=20scoreboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2762. (cherry picked from commit a53c4816cce4fcab4bcca4075557de4de797ae77) --- webapp/public/js/domjudge.js | 164 ++++++++++++------ .../partials/scoreboard_table.html.twig | 30 ++-- 2 files changed, 125 insertions(+), 69 deletions(-) diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index 2135c38bb8..2e7e2097de 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -207,13 +207,29 @@ function getSelectedTeams() return JSON.parse(cookieVal); } -function getScoreboard() +function getScoreboards(mobile) { - var scoreboard = document.getElementsByClassName("scoreboard"); - if (scoreboard === null || scoreboard[0] === null || scoreboard[0] === undefined) { + const scoreboards = document.getElementsByClassName("scoreboard"); + if (scoreboards === null || scoreboards[0] === null || scoreboards[0] === undefined) { return null; } - return scoreboard[0].rows; + let scoreboardRows = {}; + const mobileScoreboardClass = 'mobile-scoreboard'; + const desktopScoreboardClass = 'desktop-scoreboard'; + for (let i = 0; i < scoreboards.length; i++) { + if (scoreboards[i].classList.contains(mobileScoreboardClass)) { + scoreboardRows.mobile = scoreboards[i].rows; + } else if (scoreboards[i].classList.contains(desktopScoreboardClass)) { + scoreboardRows.desktop = scoreboards[i].rows; + } + } + if (mobile === undefined) { + return scoreboardRows; + } else if (mobile) { + return scoreboardRows.mobile; + } else { + return scoreboardRows.desktop; + } } function getRank(row) @@ -226,7 +242,7 @@ function getHeartCol(row) { var td = null; // search for td before the team name for (var i = 1; i < 4; i++) { - if (tds[i].className == "scoretn") { + if (tds[i].classList.contains("scoretn")) { td = tds[i - 1]; break; } @@ -249,16 +265,28 @@ function getTeamname(row) return row.getAttribute("data-team-id"); } -function toggle(id, show) +function toggle(id, show, mobile) { - var scoreboard = getScoreboard(); + var scoreboard = getScoreboards(mobile); if (scoreboard === null) return; + // Filter out all rows that do not have a data-team-id attribute or have + // the class `scoreheader`. + // The mobile scoreboard has them, and we need to ignore them. + scoreboard = Array.from(scoreboard) + .filter( + row => row.getAttribute("data-team-id") + || row.classList.contains("scoreheader") + ); + var favTeams = getSelectedTeams(); // count visible favourite teams (if filtered) var visCnt = 0; for (var i = 0; i < favTeams.length; i++) { for (var j = 0; j < scoreboard.length; j++) { + if (!scoreboard[j].getAttribute("data-team-id")) { + continue; + } var scoreTeamname = getTeamname(scoreboard[j]); if (scoreTeamname === null) { continue; @@ -302,69 +330,99 @@ function toggle(id, show) }); } -function addHeart(rank, row, id, isFav) +function addHeart(rank, row, id, isFav, mobile) { var heartCol = getHeartCol(row); var iconClass = isFav ? "fas fa-heart" : "far fa-heart"; - return heartCol.innerHTML + ""; + return heartCol.innerHTML + ""; } function initFavouriteTeams() { - var scoreboard = getScoreboard(); - if (scoreboard === null) { + const scoreboards = getScoreboards(); + if (scoreboards === null) { return; } var favTeams = getSelectedTeams(); - var toAdd = new Array(); - var cntFound = 0; - var lastRank = 0; - for (var j = 0; j < scoreboard.length; j++) { - var found = false; - var teamname = getTeamname(scoreboard[j]); - if (teamname === null) { - continue; - } - var firstCol = getRank(scoreboard[j]); - var heartCol = getHeartCol(scoreboard[j]); - var rank = firstCol.innerHTML; - for (var i = 0; i < favTeams.length; i++) { - if (teamname === favTeams[i]) { - found = true; - heartCol.innerHTML = addHeart(rank, scoreboard[j], j, found); - toAdd[cntFound] = scoreboard[j].cloneNode(true); - if (rank.trim().length === 0) { - // make rank explicit in case of tie - getRank(toAdd[cntFound]).innerHTML += lastRank; + Object.keys(scoreboards).forEach(function(key) { + var toAdd = new Array(); + var toAddMobile = new Array(); + var cntFound = 0; + var lastRank = 0; + const scoreboard = scoreboards[key]; + const mobile = key === 'mobile'; + let teamIndex = 1; + for (var j = 0; j < scoreboard.length; j++) { + var found = false; + var teamname = getTeamname(scoreboard[j]); + if (teamname === null) { + continue; + } + var firstCol = getRank(scoreboard[j]); + var heartCol = getHeartCol(scoreboard[j]); + var rank = firstCol.innerHTML; + for (var i = 0; i < favTeams.length; i++) { + if (teamname === favTeams[i]) { + found = true; + heartCol.innerHTML = addHeart(rank, scoreboard[j], teamIndex, found, mobile); + toAdd[cntFound] = scoreboard[j].cloneNode(true); + if (mobile) { + toAddMobile[cntFound] = scoreboard[j + 1].cloneNode(true); + } + if (rank.trim().length === 0) { + // make rank explicit in case of tie + getRank(toAdd[cntFound]).innerHTML += lastRank; + } + scoreboard[j].style.background = "lightyellow"; + const scoretn = scoreboard[j].querySelector('.scoretn'); + if (scoretn && scoretn.classList.contains('cl_FFFFFF')) { + scoretn.classList.remove('cl_FFFFFF'); + scoretn.classList.add('cl_FFFFE0'); + } + if (mobile) { + scoreboard[j + 1].style.background = "lightyellow"; + } + cntFound++; + break; } - scoreboard[j].style.background = "lightyellow"; - cntFound++; - break; } + if (!found) { + heartCol.innerHTML = addHeart(rank, scoreboard[j], teamIndex, found, mobile); + } + if (rank !== "") { + lastRank = rank; + } + + teamIndex++; } - if (!found) { - heartCol.innerHTML = addHeart(rank, scoreboard[j], j, found); - } - if (rank !== "") { - lastRank = rank; - } - } - // copy favourite teams to the top of the scoreboard - for (var i = 0; i < cntFound; i++) { - var copy = toAdd[i]; - var style = ""; - if (i === 0) { - style += "border-top: 2px solid black;"; + let addCounter = 1; + const copyRow = function (i, copy, addTopBorder, addBottomBorder, noMiddleBorder) { + let style = ""; + if (noMiddleBorder) { + style += "border-bottom-width: 0;"; + } + if (addTopBorder && i === 0) { + style += "border-top: 2px solid black;"; + } + if (addBottomBorder && i === cntFound - 1) { + style += "border-bottom: thick solid black;"; + } + copy.setAttribute("style", style); + const tbody = scoreboard[1].parentNode; + tbody.insertBefore(copy, scoreboard[addCounter]); + addCounter++; } - if (i === cntFound - 1) { - style += "border-bottom: thick solid black;"; + + // copy favourite teams to the top of the scoreboard + for (let i = 0; i < cntFound; i++) { + copyRow(i, toAdd[i], true, !mobile, mobile); + if (mobile) { + copyRow(i, toAddMobile[i], false, true, false); + } } - copy.setAttribute("style", style); - var tbody = scoreboard[1].parentNode; - tbody.insertBefore(copy, scoreboard[i + 1]); - } + }); } // This function is a specific addition for using DOMjudge within a diff --git a/webapp/templates/partials/scoreboard_table.html.twig b/webapp/templates/partials/scoreboard_table.html.twig index 3d0d9f9607..1d2591fc82 100644 --- a/webapp/templates/partials/scoreboard_table.html.twig +++ b/webapp/templates/partials/scoreboard_table.html.twig @@ -28,7 +28,7 @@ {% endif %} - +
{% set teamColspan = 2 %} {% if showAffiliationLogos %} @@ -38,22 +38,22 @@ {# output table column groups (for the styles) #} {% if enable_ranking %} - + {% endif %} {% if showFlags %} - + {% else %} {% endif %} {% if showAffiliationLogos %} - + {% endif %} - + {% if enable_ranking %} - - + + {% endif %} @@ -106,7 +106,7 @@ {% set previousSortOrder = -1 %} {% set previousTeam = null %} - {% set backgroundColors = {"#FFFFFF": 1} %} + {% set backgroundColors = {"#FFFFFF": 1, '#FFFFE0': 1} %} {% set medalCount = 0 %} {% for score in scores %} {% set classes = [] %} @@ -315,25 +315,25 @@
- +
{# output table column groups (for the styles) #} {% if enable_ranking %} - + {% endif %} {% if showFlags %} - + {% else %} {% endif %} {% if showAffiliationLogos %} - + {% endif %} - + {% if enable_ranking %} - + {% endif %} @@ -356,7 +356,6 @@ {% set previousSortOrder = -1 %} {% set previousTeam = null %} - {% set backgroundColors = {"#FFFFFF": 1} %} {% set medalCount = 0 %} {% for score in scores %} {% set classes = [] %} @@ -431,7 +430,6 @@ {% set colorClass = "_FFFFFF" %} {% else %} {% set colorClass = color | replace({"#": "_"}) %} - {% set backgroundColors = backgroundColors | merge({(color): 1}) %} {% endif %}
{% set link = null %} From 9b03ea119b9cf2317ec223c5e24ed9c171387c1d Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Sat, 23 Nov 2024 11:45:00 +0100 Subject: [PATCH 19/23] Fix handling of domserver errors in judgedaemon. - Treat 500 / internal server error as retry-able - If we still fail after all configured retries, we do mark the endpoint as errored and sleep for a while longer. Afterwards, we are registering ourselves again and if that succeeds will try to fetch work again. (cherry picked from commit c850eb36272e90fcea7727014dd8c1a83be71ce0) --- judge/judgedaemon.main.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/judge/judgedaemon.main.php b/judge/judgedaemon.main.php index 83178fcc66..3355db24ac 100644 --- a/judge/judgedaemon.main.php +++ b/judge/judgedaemon.main.php @@ -164,9 +164,6 @@ function request(string $url, string $verb = 'GET', $data = '', bool $failonerro ": http status code: " . $status . ", request size = " . strlen(print_r($data, true)) . ", response: " . $response; - if ($status == 500) { - break; - } } else { $succeeded = true; break; @@ -751,9 +748,12 @@ function fetch_executable_internal( // Request open submissions to judge. Any errors will be treated as // non-fatal: we will just keep on retrying in this loop. + $row = []; $judging = request('judgehosts/fetch-work', 'POST', ['hostname' => $myhost], false); - // If $judging is null, an error occurred; don't try to decode. - if (!is_null($judging)) { + // If $judging is null, an error occurred; we marked the endpoint already as errorred above. + if (is_null($judging)) { + continue; + } else { $row = dj_json_decode($judging); } From 116e9cfa1799b763dff5f3f7c9e19dffa933f6a0 Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Fri, 22 Nov 2024 17:30:30 +0100 Subject: [PATCH 20/23] Indicate to the judgehost to retry if we are cleaning up old queue tasks. Related: https://github.com/DOMjudge/domjudge/commit/80c1a43bfb85013ae5fa02b4bf60c4b3527d08f0 (cherry picked from commit 780c7c91cba50005f72ba74efe8d9cec0357322a) --- judge/judgedaemon.main.php | 12 ++++++++++++ webapp/src/Controller/API/JudgehostController.php | 10 +++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/judge/judgedaemon.main.php b/judge/judgedaemon.main.php index 3355db24ac..5d58d45dfe 100644 --- a/judge/judgedaemon.main.php +++ b/judge/judgedaemon.main.php @@ -60,6 +60,7 @@ function read_credentials(): void "waiting" => false, "errorred" => false, "last_attempt" => -1, + "retrying" => false, ]; } if (count($endpoints) <= 0) { @@ -780,8 +781,19 @@ function fetch_executable_internal( // We have gotten a work packet. $endpoints[$endpointID]["waiting"] = false; + // All tasks are guaranteed to be of the same type. $type = $row[0]['type']; + + if ($type == 'try_again') { + if (!$endpoints[$endpointID]['retrying']) { + logmsg(LOG_INFO, "API indicated to retry fetching work (this might take a while to clean up)."); + } + $endpoints[$endpointID]['retrying'] = true; + continue; + } + $endpoints[$endpointID]['retrying'] = false; + logmsg(LOG_INFO, "⇝ Received " . sizeof($row) . " '" . $type . "' judge tasks (endpoint $endpointID)"); diff --git a/webapp/src/Controller/API/JudgehostController.php b/webapp/src/Controller/API/JudgehostController.php index 8eb6239515..a644a944c9 100644 --- a/webapp/src/Controller/API/JudgehostController.php +++ b/webapp/src/Controller/API/JudgehostController.php @@ -1594,8 +1594,16 @@ public function getJudgeTasksAction(Request $request): array ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR); + if ($jobid === null) { + return; + } $judgetasks = $this->getJudgetasks($jobid, $max_batchsize, $judgehost); - if ($judgetasks !== null) { + if (empty($judgetasks)) { + // Somehow we got ourselves in a situation that there was a queue task without remaining judge tasks. + // This should not happen, but if it does, we need to clean up. Each of the fetch-work calls will clean + // up one queue task. We need to signal to the judgehost that there might be more work to do. + $judgetasks = [['type' => 'try_again']]; + } else { // Mark it as being worked on. $this->em->createQueryBuilder() ->update(QueueTask::class, 'qt') From 22764f3c597ac9ba3627303c7fc4b14cb887fadb Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Sat, 23 Nov 2024 14:11:03 +0100 Subject: [PATCH 21/23] Remove transaction from fetch work API. We have seen the transaction to fail, resulting in exceptions/500s. There is also no need to have a transaction at all. We now do check after the update whether we won instead and if not, tell the judgehost to try again. (cherry picked from commit c06ee8acae7a27a898a5b018cc52ca7e98c44f60) --- .../Controller/API/JudgehostController.php | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/webapp/src/Controller/API/JudgehostController.php b/webapp/src/Controller/API/JudgehostController.php index a644a944c9..9d2e21ee20 100644 --- a/webapp/src/Controller/API/JudgehostController.php +++ b/webapp/src/Controller/API/JudgehostController.php @@ -1583,40 +1583,42 @@ public function getJudgeTasksAction(Request $request): array // This is case 2.a) from above: start something new. // This runs transactional to prevent a queue task being picked up twice. $judgetasks = null; - $this->em->wrapInTransaction(function () use ($judgehost, $max_batchsize, &$judgetasks) { - $jobid = $this->em->createQueryBuilder() - ->from(QueueTask::class, 'qt') - ->innerJoin('qt.judging', 'j') - ->select('j.judgingid') + $jobid = $this->em->createQueryBuilder() + ->from(QueueTask::class, 'qt') + ->innerJoin('qt.judging', 'j') + ->select('j.judgingid') + ->andWhere('qt.startTime IS NULL') + ->addOrderBy('qt.priority') + ->addOrderBy('qt.teamPriority') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR); + if ($jobid !== null) { + // Mark it as being worked on. + $result = $this->em->createQueryBuilder() + ->update(QueueTask::class, 'qt') + ->set('qt.startTime', Utils::now()) + ->andWhere('qt.judging = :jobid') ->andWhere('qt.startTime IS NULL') - ->addOrderBy('qt.priority') - ->addOrderBy('qt.teamPriority') - ->setMaxResults(1) + ->setParameter('jobid', $jobid) ->getQuery() - ->getOneOrNullResult(AbstractQuery::HYDRATE_SINGLE_SCALAR); - if ($jobid === null) { - return; - } - $judgetasks = $this->getJudgetasks($jobid, $max_batchsize, $judgehost); - if (empty($judgetasks)) { - // Somehow we got ourselves in a situation that there was a queue task without remaining judge tasks. - // This should not happen, but if it does, we need to clean up. Each of the fetch-work calls will clean - // up one queue task. We need to signal to the judgehost that there might be more work to do. + ->execute(); + + if ($result == 0) { + // Another judgehost beat us to it. $judgetasks = [['type' => 'try_again']]; } else { - // Mark it as being worked on. - $this->em->createQueryBuilder() - ->update(QueueTask::class, 'qt') - ->set('qt.startTime', Utils::now()) - ->andWhere('qt.judging = :jobid') - ->andWhere('qt.startTime IS NULL') - ->setParameter('jobid', $jobid) - ->getQuery() - ->execute(); + $judgetasks = $this->getJudgetasks($jobid, $max_batchsize, $judgehost); + if (empty($judgetasks)) { + // Somehow we got ourselves in a situation that there was a queue task without remaining judge tasks. + // This should not happen, but if it does, we need to clean up. Each of the fetch-work calls will clean + // up one queue task. We need to signal to the judgehost that there might be more work to do. + $judgetasks = [['type' => 'try_again']]; + } + } + if (!empty($judgetasks)) { + return $judgetasks; } - }); - if (!empty($judgetasks)) { - return $judgetasks; } if ($this->config->get('enable_parallel_judging')) { From edab328e209e2bc364e6d82ed40e4e85b6b644ce Mon Sep 17 00:00:00 2001 From: Jaap Eldering Date: Sat, 23 Nov 2024 15:13:01 +0100 Subject: [PATCH 22/23] Don't try to update aborted judgings This (typically?) happens while cancelling a rejudging while there are judgedaemons actively judging. Fixes errors like ``` [Nov 23 13:34:53.075] judgedaemon[526392]: warning: Error while executing curl POST to url http://localhost/domjudge/api/judgehosts/add-judging-run/tiger-1/1555747: http status code: 500, request size = 67745, response: array ( 'code' => 500, 'message' => 'internal bug: the evaluated result changed during judging', 'class' => 'BadMethodCallException', 'trace' => array ( 0 => array ( 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => '/home/jaap/domjudge/git/domjudge/webapp/src/Controller/API/JudgehostController.php', 'line' => 1053, 'args' => array ( ), ), ... ``` (cherry picked from commit cb37af5abd111b48780b4875866fb93f521ecb27) --- webapp/src/Controller/API/JudgehostController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webapp/src/Controller/API/JudgehostController.php b/webapp/src/Controller/API/JudgehostController.php index 9d2e21ee20..08bc208329 100644 --- a/webapp/src/Controller/API/JudgehostController.php +++ b/webapp/src/Controller/API/JudgehostController.php @@ -1032,6 +1032,12 @@ private function addSingleJudgingRun( // Only update if the current result is different from what we had before. // This should only happen when the old result was NULL. if ($oldResult !== $result) { + if ($oldResult === 'aborted') { + // This judging was cancelled while we worked on it, + // probably as part of a cancelled rejudging. + // Throw away our work, and return that we're done. + return false; + } if ($oldResult !== null) { throw new BadMethodCallException('internal bug: the evaluated result changed during judging'); } From 296242d12e93a1a9029e7870de9586098616a556 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Sat, 23 Nov 2024 15:39:08 +0100 Subject: [PATCH 23/23] Convert whitesmoke to hex before parsing it This fixes many deprecation errors on the mobile scoreboard. (cherry picked from commit bb76cedc05049535e206750036994172d37516a5) --- webapp/src/Twig/TwigExtension.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index a496f540be..3a44fccb52 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -1065,9 +1065,9 @@ public function fileTypeIcon(string $type): string public function problemBadge(ContestProblem $problem, bool $grayedOut = false): string { - $rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff'); + $rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff'); if ($grayedOut) { - $rgb = 'whitesmoke'; + $rgb = Utils::convertToHex('whitesmoke'); } $background = Utils::parseHexColor($rgb); @@ -1095,9 +1095,9 @@ public function problemBadge(ContestProblem $problem, bool $grayedOut = false): public function problemBadgeMaybe(ContestProblem $problem, ScoreboardMatrixItem $matrixItem): string { - $rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff'); + $rgb = Utils::convertToHex($problem->getColor() ?? '#ffffff'); if (!$matrixItem->isCorrect) { - $rgb = 'whitesmoke'; + $rgb = Utils::convertToHex('whitesmoke'); } $background = Utils::parseHexColor($rgb); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy