diff --git a/webapp/src/Twig/TwigExtension.php b/webapp/src/Twig/TwigExtension.php index 9ddbfaddb4..6a5e823afc 100644 --- a/webapp/src/Twig/TwigExtension.php +++ b/webapp/src/Twig/TwigExtension.php @@ -215,26 +215,31 @@ public function printtime(string|float|null $datetime, ?string $format = null, ? } } - public function printHumanTimeDiff(float|null $datetime): string + public function printHumanTimeDiff(float|null $startTime, float|null $endTime): string { - if ($datetime === null) { + if ($startTime === null) { return ''; } - $diff = Utils::now() - $datetime; + $suffix = ''; + if ($endTime === null) { + $suffix = ' ago'; + $endTime = Utils::now(); + } + $diff = $endTime - $startTime; if ($diff < 120) { - return (int)($diff) . ' seconds ago'; + return (int)($diff) . ' seconds' . $suffix; } $diff /= 60; if ($diff < 120) { - return (int)($diff) . ' minutes ago'; + return (int)($diff) . ' minutes' . $suffix; } $diff /= 60; if ($diff < 48) { - return (int)($diff) . ' hours ago'; + return (int)($diff) . ' hours' . $suffix; } $diff /= 24; - return (int)($diff) . ' days ago'; + return (int)($diff) . ' days' . $suffix; } /** @@ -438,7 +443,7 @@ public function displayTestcaseResults(array $testcases, bool $submissionDone, b $lastTypeSample = true; foreach ($testcases as $testcase) { if ($testcase->getSample() != $lastTypeSample) { - $results .= ' | '; + $results .= ' | '; $lastTypeSample = $testcase->getSample(); } @@ -662,6 +667,7 @@ private function getCommonPrefix(array $strings): string */ public function printHosts(array $hostnames): string { + $hostnames = array_values($hostnames); if (empty($hostnames)) { return ""; } diff --git a/webapp/templates/jury/analysis/download_graphs.html.twig b/webapp/templates/jury/analysis/download_graphs.html.twig index 3ba2ef2ac5..9978d672e2 100644 --- a/webapp/templates/jury/analysis/download_graphs.html.twig +++ b/webapp/templates/jury/analysis/download_graphs.html.twig @@ -48,7 +48,7 @@ $(function () { $('.container-fluid svg').each(function () { - var $button = $(''); + var $button = $(''); var $svg = $(this); // Pick a good place for the button, on the submission page next to diff --git a/webapp/templates/jury/partials/submission_graph.html.twig b/webapp/templates/jury/partials/submission_graph.html.twig index 6a74534f57..c8cb166fbe 100644 --- a/webapp/templates/jury/partials/submission_graph.html.twig +++ b/webapp/templates/jury/partials/submission_graph.html.twig @@ -1,21 +1,43 @@ {% set timelimit = submission.problem.timelimit * submission.language.timeFactor %}
{% if selectedJudging is not null %} -
-

Testcase Runtimes

+
+
+ testcase CPU times + + {%- if selectedJudging is not null and selectedJudging.result != 'compiler-error' -%} + | max: + {{ selectedJudging.maxRuntime | number_format(3, '.', '') }}s + | sum: {{ selectedJudging.sumRuntime | number_format(3, '.', '') }}s + {% endif %} + + +
+
+
{% endif %} {% if externalJudgement is not null and externalJudgement.result is not null %} -
-

External Testcase Runtimes

- +
+
+ External Testcase CPU times +
+
+ +
{% endif %} {% if judgings|length > 1 %} -
-

Max Runtimes

- +
+
+
+ Max. CPU times +
+
+ +
+
{% endif %}
@@ -59,7 +81,7 @@ } chart.yAxis .tickValues(tickValues) - .axisLabel('Runtime'); + .axisLabel('CPU time'); if (tickStep >= 1) { chart.yAxis.tickFormat(function(d) { return d3.format(',f')(d) + 's' }); } else { @@ -145,7 +167,7 @@ var format = d3.format(".3f"); return "Testcase " + obj.data.description + "
Runtime: " + format(obj.data.value) + "s"; }); - chart.xAxis.axisLabel("Testcase Rank"); + chart.xAxis.axisLabel("testcase rank"); d3.select('#testcaseruntime svg') .datum(testcase_times) .call(chart); diff --git a/webapp/templates/jury/partials/verify_form.html.twig b/webapp/templates/jury/partials/verify_form.html.twig index a11c2f1ae3..3d90e65fdb 100644 --- a/webapp/templates/jury/partials/verify_form.html.twig +++ b/webapp/templates/jury/partials/verify_form.html.twig @@ -3,7 +3,9 @@ {# Display verification data: verified, by whom, and comment #}

- {{ label }}: {% if judging.verified %}yes{% else %}no{% endif %} + {% if judging.verified %} + {{ label }}: yes + {% endif %} {%- if judging.verified and judging.juryMember is not empty -%} , by {{ judging.juryMember }} {%- if judging.verifyComment is not empty %} diff --git a/webapp/templates/jury/submission.html.twig b/webapp/templates/jury/submission.html.twig index c7390f383e..22146b75a0 100644 --- a/webapp/templates/jury/submission.html.twig +++ b/webapp/templates/jury/submission.html.twig @@ -83,9 +83,10 @@ {% endfor %} {% endif %} -

+
+

- Submission {{ submission.submitid }} + Submission s{{ submission.submitid }} {% if submission.originalSubmission %} {% set origSubmissionUrl = path('jury_submission', {submitId: submission.originalSubmission.submitid}) %} (resubmit of s{{ submission.originalSubmission.submitid }}) @@ -103,45 +104,30 @@ (ignored) {% endif %}

+ {% if not submission.importError %} - {% if is_granted('ROLE_ADMIN') %} - {% if submission.valid %} - {% set action = 'ignore' %} - {% else %} - {% set action = 'unignore' %} + + {% if is_granted('ROLE_ADMIN') %} + {% if submission.valid %} + {% set action = 'ignore' %} + {% else %} + {% set action = 'unignore' %} + {% endif %} +
+ + +
{% endif %} -
- - -
- {% endif %} - {% include 'jury/partials/rejudge_form.html.twig' with {table: 'submission', id: submission.submitid} %} - {% endif %} -
- - {% if not submission.valid %} -
This submission is not used during scoreboard calculations.
- {% endif %} + {% include 'jury/partials/rejudge_form.html.twig' with {table: 'submission', id: submission.submitid} %} + - {% if submission.importError %} -
-
This submission could not be imported into DOMjudge and thus will only show external data.
-
The error during import was: {{ submission.importError }}
-
- {% endif %} +
- {% if not submission.contestProblem %} -
- This submission is for a problem that is not part (anymore) of the contest of the submission. -
- {% endif %} - - {# Condensed submission info on a single line with icons #} -
+ {# Condensed submission info on a single line with icons #} @@ -149,30 +135,33 @@ - {% if submission.user %} - + {% if false %} + {# TODO: add the info to a hover #} + {% if submission.user %} + {{ submission.user.username }} {{ submission.user | entityIdBadge('u') }} - {% endif %} + {% endif %} + {% endif %} - {% if current_contest.cid != submission.contest.cid %} - + {% if current_contest.cid != submission.contest.cid %} + {{ submission.contest.shortname }} {{ submission.contest | entityIdBadge('c') }} - {% endif %} + {% endif %} - + {% if submission.contestProblem %} - {{ submission.contestProblem | problemBadge }}: {{ submission.problem.name }} + {{ submission.contestProblem | problemBadge }} {{ submission.problem.name }} {% else %} {{ submission.problem.name }} {% endif %} @@ -180,7 +169,7 @@ - + {{ submission.language.name }} @@ -188,38 +177,58 @@ - + {{ submission.submittime | printtime(null, submission.contest) }} - + {{ submission.problem.timelimit * submission.language.timeFactor }}s - + View {{ submission.files | printFiles }} - {% if external_ccs_submission_url is not empty %} - {% set externalSubmissionUrl = submission | externalCcsUrl %} - {% if externalSubmissionUrl is not empty %} - + {% if external_ccs_submission_url is not empty %} + {% set externalSubmissionUrl = submission | externalCcsUrl %} + {% if externalSubmissionUrl is not empty %} + View in external CCS - {% endif %} + {% endif %} + {% endif %} {% endif %} +
+ {% if not submission.valid %} +
This submission is not used during scoreboard calculations.
+ {% endif %} + + {% if submission.importError %} +
+
This submission could not be imported into DOMjudge and thus will only show external data.
+
The error during import was: {{ submission.importError }}
+
+ {% endif %} + + {% if not submission.contestProblem %} +
+ This submission is for a problem that is not part (anymore) of the contest of the submission. +
+ {% endif %} + + {% if submission.externalid %}
External ID: @@ -335,6 +344,8 @@ {% endif %} +
+ {% if unjudgableReasons is not empty %} {% for reason in unjudgableReasons %}
{{ reason }}
@@ -343,13 +354,11 @@ {% if selectedJudging is not null or externalJudgement is not null %} - {% include 'jury/partials/submission_graph.html.twig' %} - {% if selectedJudging is not null %} {# Show judging information #} -
-

+
+

Judging j{{ selectedJudging.judgingid }} {% if selectedJudging.rejudging %} (rejudging @@ -358,8 +367,75 @@ {% elseif not selectedJudging.valid %} (Invalid) {% endif %} + + + {% if not submission.importError %} + {% if selectedJudging is null or selectedJudging.result is empty %} + {%- if selectedJudging and selectedJudging.started %} + {{- '' | printValidJuryResult -}} + {%- else %} + {{- 'queued' | printValidJuryResult -}} + {%- endif %} + {%- else %} + {{- selectedJudging.result | printValidJuryResult -}} + {%- endif %} + {%- if submission.stillBusy -%} + (…) + {%- endif -%} + {% endif %} + {%- if lastJudging is not null -%} + {% set lastSubmissionLink = path('jury_submission', {submitId: lastSubmission.submitid}) %}{#- + -#} + (s{{ lastSubmission.submitid }}: {{ lastJudging.result }}){#- + -#} + {%- endif -%} + {%- if externalJudgement is not null %} + {% if submission.importError %} + External result: {{ externalJudgement.result | printValidJuryResult }} + {% else %} + (external: {{ externalJudgement.result | printValidJuryResult }}) + {% endif %} + {%- endif %} + {%- if selectedJudging is not null and judgehosts is not empty -%} + , on {{ judgehosts | printHosts }} + {%- if selectedJudging.starttime and selectedJudging.endtime -%} + , took {{ selectedJudging.starttime | printHumanTimeDiff(selectedJudging.endtime) }} + {% endif %} + + {# TODO: Only display if relevant, say 60s+ after submission. #} + {%- if selectedJudging.starttime %} + {%- if selectedJudging.starttime - submission.submittime > 60 -%} + (started {{ selectedJudging.starttime | printtime('H:i:s') }}) + {%- endif -%} + {%- if not selectedJudging.endtime -%} + {%- if selectedJudging.valid or selectedJudging.rejudging -%} +  [still judging - busy {{ selectedJudging.starttime | printtimediff }}] + {%- else -%} +  [aborted] + {%- endif -%} + {%- endif -%} + + {% else %} + Judging not started yet + {%- endif -%} + {% endif -%} + {%- if externalJudgement is not null %} + (external judging started: {{ externalJudgement.starttime | printtime('H:i:s') }} + {%- if externalJudgement.endtime -%} + , finished in {{ externalJudgement.starttime | printtimediff(externalJudgement.endtime) }}s + {%- else -%} +  [still judging - busy {{ externalJudgement.starttime | printtimediff }}] + {%- endif -%} + ) + {%- endif -%} + {%- if externalJudgement is not null and externalJudgement.result != 'compiler-error' and externalJudgement.result != null -%} + , external max/sum runtime: + {{ externalJudgement.maxRuntime | number_format(2, '.', '') }}/{{ externalJudgement.sumRuntime | number_format(2, '.', '') }}s + {% endif %} +

-   + + {% if selectedJudging.debugPackages | length > 0 %} {% for debug_package in selectedJudging.debugPackages %} @@ -405,91 +481,63 @@ {%- endif %} {%- endif %} +
{% endif %} - -
-
- {% if not submission.importError %} - Result: - {% if selectedJudging is null or selectedJudging.result is empty %} - {%- if selectedJudging and selectedJudging.started %} - {{- '' | printValidJuryResult -}} - {%- else %} - {{- 'queued' | printValidJuryResult -}} - {%- endif %} - {%- else %} - {{- selectedJudging.result | printValidJuryResult -}} - {%- endif %} - {%- if submission.stillBusy -%} - (…) - {%- endif -%} - {% endif %} - {%- if lastJudging is not null -%} - {% set lastSubmissionLink = path('jury_submission', {submitId: lastSubmission.submitid}) %}{#- - -#} - (s{{ lastSubmission.submitid }}: {{ lastJudging.result }}){#- - -#} - {%- endif -%} - {%- if externalJudgement is not null %} - {% if submission.importError %} - External result: {{ externalJudgement.result | printValidJuryResult }} + {# Display compile output #} + {% set color = '#6666FF' %} + {% set message = 'not finished yet' %} + {% set output = null %} + {% if selectedJudging is not null %} + {% set output = selectedJudging.outputCompile(true) %} + {% endif %} + {% if output is not null %} + {% if selectedJudging.result == 'compiler-error' %} + {% set message = 'unsuccessful' %} + {% else %} + {% set message = 'successful' %} + {% if output is not empty %} + {% set outputLines = output | lineCount %} + {% if outputLines == 1 %} + {% set message = message ~ ' (with 1 line of output)' %} {% else %} - (external: {{ externalJudgement.result | printValidJuryResult }}) + {% set message = message ~ ' (with ' ~ outputLines ~ ' lines of output)' %} {% endif %} - {%- endif %} - {%- if selectedJudging is not null and judgehosts is not empty -%} - , Judgehost(s): - {% for judgehostid, hostname in judgehosts %} - {% set judgehostLink = path('jury_judgehost', {judgehostid: judgehostid}) %} - {{ hostname | printHost }} - {% endfor %} - - {%- if selectedJudging.starttime -%} - Judging started: {{ selectedJudging.starttime | printtime('H:i:s') }} - {%- if selectedJudging.endtime -%} - , finished in {{ selectedJudging.starttime | printtimediff(selectedJudging.endtime) }}s - {%- elseif selectedJudging.valid or selectedJudging.rejudging -%} -  [still judging - busy {{ selectedJudging.starttime | printtimediff }}] - {%- else -%} -  [aborted] - {%- endif -%} - - {% else %} - Judging not started yet - {%- endif -%} - {% endif -%} - {%- if externalJudgement is not null %} - (external judging started: {{ externalJudgement.starttime | printtime('H:i:s') }} - {%- if externalJudgement.endtime -%} - , finished in {{ externalJudgement.starttime | printtimediff(externalJudgement.endtime) }}s - {%- else -%} -  [still judging - busy {{ externalJudgement.starttime | printtimediff }}] - {%- endif -%} - ) - {%- endif -%} - {%- if selectedJudging is not null and selectedJudging.result != 'compiler-error' -%} - , max/sum runtime: - {{ selectedJudging.maxRuntime | number_format(2, '.', '') }}/{{ selectedJudging.sumRuntime | number_format(2, '.', '') }}s - {%- if lastJudging is not null -%} - - (s{{ lastSubmission.submitid }}: - {{ lastJudging.maxRuntime | number_format(2, '.', '') }}{#- - -#}/{{ lastJudging.sumRuntime | number_format(2, '.', '') }}s) - - {%- endif -%} - {% endif -%} - {%- if externalJudgement is not null and externalJudgement.result != 'compiler-error' and externalJudgement.result != null -%} - , external max/sum runtime: - {{ externalJudgement.maxRuntime | number_format(2, '.', '') }}/{{ externalJudgement.sumRuntime | number_format(2, '.', '') }}s {% endif %} -
+ {% endif %} + {% endif %} + + Compilation {{ message }} + +{% if selectedJudging is not null and selectedJudging.compileMetadata is not null %} + {{ selectedJudging.compileMetadata | printMetadata }} + +
+
{{ selectedJudging.compileMetadata }}
+
+ +
+{% endif %} +
+
Compilation output
+ {% if output is empty %} +
There were no compiler errors or warnings.
+ {% else %} +
{{ output }}
+ {% endif %} +
{# Display testcase results #} {% if externalJudgement is not null or (selectedJudging is not null and selectedJudging.result != 'compiler-error') %} {% if not submission.importError %} - + + {% endif %} {% if lastJudging is not null %} @@ -529,27 +589,18 @@ {% endif %}
testcase runs: {% if selectedJudging is null %} {% set judgingDone = false %} @@ -508,6 +556,18 @@ {% endif %} {% endif %} + {# Show JS toggle of previous submission results #} + {% if lastJudging is not null %} + + show/hide + results of previous submission s{{ lastSubmission.submitid }} + {% if lastJudging.verifyComment %} + (verify comment: '{{ lastJudging.verifyComment }}') + {% endif %} + + {% endif %} +
- - {# Show JS toggle of previous submission results #} - {% if lastJudging is not null %} - - show/hide - results of previous submission s{{ lastSubmission.submitid }} - {% if lastJudging.verifyComment %} - (verify comment: '{{ lastJudging.verifyComment }}') - {% endif %} - - {% endif %} {% endif %} -
- +
+ + - {# Show verify info, but only when a result is known #} +{# Show verify info, but only when a result is known #} + {# TODO: This should probably also move... #} {% if selectedJudging is not null and selectedJudging.result is not empty %} {% include 'jury/partials/verify_form.html.twig' with { label: 'Verified', @@ -578,55 +629,15 @@
Judging is not ready yet!
{% endif %} - {# Display compile output #} - {% set color = '#6666FF' %} - {% set message = 'not finished yet' %} - {% set output = null %} - {% if selectedJudging is not null %} - {% set output = selectedJudging.outputCompile(true) %} - {% endif %} - {% if output is not null %} - {% if selectedJudging.result == 'compiler-error' %} - {% set message = 'unsuccessful' %} - {% else %} - {% set message = 'successful' %} - {% if output is not empty %} - {% set outputLines = output | lineCount %} - {% if outputLines == 1 %} - {% set message = message ~ ' (with 1 line of output)' %} - {% else %} - {% set message = message ~ ' (with ' ~ outputLines ~ ' lines of output)' %} - {% endif %} - {% endif %} - {% endif %} - {% endif %} -

- - Compilation {{ message }} - -

- {% if selectedJudging is not null and selectedJudging.compileMetadata is not null %} - {{ selectedJudging.compileMetadata | printMetadata }} - -
-
{{ selectedJudging.compileMetadata }}
-
-
-
- {% endif %} -
-
Compilation output
- {% if output is empty %} -
There were no compiler errors or warnings.
- {% else %} -
{{ output }}
- {% endif %} -
+ + + + +{% include 'jury/partials/submission_graph.html.twig' %} + +
+ +
{% if externalJudgement is not null or (selectedJudging is not null and selectedJudging.result != 'compiler-error') %} {# Show run info. Only when compilation was successful or we have an external judgement #}