Skip to content

Commit 2d986fb

Browse files
committed
Improve look of the judging verifier page.
Also fix some display bugs (e.g. displaying scoring only when relevant) while we are at it.
1 parent 0c5494e commit 2d986fb

File tree

3 files changed

+190
-81
lines changed

3 files changed

+190
-81
lines changed

.github/workflows/integration.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,12 @@ jobs:
164164
curl $CURLOPTS -F "sendto=" -F "problem=1-" -F "bodytext=Testing" -F "submit=Send" \
165165
"http://localhost/domjudge/jury/contests/${{ matrix.contest }}/clarifications/send" -o /dev/null
166166
curl $CURLOPTS "http://localhost/domjudge/jury/judging-verifier?verify_multiple=1" -o /dev/null
167-
NUMNOTVERIFIED=$(curl $CURLOPTS "http://localhost/domjudge/jury/judging-verifier" | grep "submissions checked" | sed -r 's/^.* ([0-9]+) submissions checked.*$/\1/')
168-
NUMVERIFIED=$( curl $CURLOPTS "http://localhost/domjudge/jury/judging-verifier" | grep "submissions not checked" | sed -r 's/^.* ([0-9]+) submissions not checked.*$/\1/')
169-
NUMNOMAGIC=$( curl $CURLOPTS "http://localhost/domjudge/jury/judging-verifier" | grep "without magic string" | sed -r 's/^.* ([0-9]+) without magic string.*$/\1/')
167+
VERIFIER_PAGE=$(curl $CURLOPTS "http://localhost/domjudge/jury/judging-verifier")
168+
NUMTOTAL=$( echo "$VERIFIER_PAGE" | grep -oP '[0-9]+ total' | grep -oP '[0-9]+')
169+
NUMNOMAGIC=$( echo "$VERIFIER_PAGE" | grep -oP '[0-9]+ without magic string' | grep -oP '[0-9]+' || echo 0)
170+
NUMEARLIER=$( echo "$VERIFIER_PAGE" | grep -oP '[0-9]+ verified earlier' | grep -oP '[0-9]+' || echo 0)
171+
NUMNOTVERIFIED=$((NUMTOTAL - NUMEARLIER - NUMNOMAGIC))
172+
NUMVERIFIED=$((NUMEARLIER + NUMNOMAGIC))
170173
NUMSUBS=$(curl $CURLOPTS http://localhost/domjudge/api/contests/${{ matrix.contest }}/submissions | python3 -mjson.tool | grep -c '"id":')
171174
# Verify expected results
172175
if [ $NUMNOTVERIFIED -ne ${{ matrix.expected_unverified }} ] || [ $NUMNOMAGIC -ne ${{ matrix.expected_no_magic }} ] || [ $NUMSUBS -gt $((NUMVERIFIED+NUMNOTVERIFIED)) ]; then

webapp/src/Controller/Jury/JuryMiscController.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ public function judgingVerifierAction(Request $request): Response
264264
$verified = [];
265265
$nomatch = [];
266266
$earlier = [];
267+
$hasExpectedScore = false;
267268

268269
$verifier = 'auto-verifier';
269270

@@ -295,6 +296,9 @@ public function judgingVerifierAction(Request $request): Response
295296

296297
$result = mb_strtoupper($judging->getResult());
297298
$actualScore = $judging->getScore();
299+
if ($expectedScore !== null) {
300+
$hasExpectedScore = true;
301+
}
298302
$entry = [
299303
'files' => $submissionFiles,
300304
'actual' => $result,
@@ -370,6 +374,7 @@ public function judgingVerifierAction(Request $request): Response
370374
'problems' => $problems,
371375
'contestId' => $this->dj->getCurrentContest()?->getExternalid(),
372376
'verifyMultiple' => $verifyMultiple,
377+
'hasExpectedScore' => $hasExpectedScore,
373378
]);
374379
}
375380

webapp/templates/jury/check_judgings.html.twig

Lines changed: 179 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,22 @@
1313

1414
<h1>Judging verifier</h1>
1515

16-
<p>
17-
{{ numChecked }} submissions checked:
18-
{{ unexpected | length }} unexpected results,
19-
{% if verifyMultiple %}
20-
{{ multiple | length }} automatically verified (multiple outcomes),
21-
{% else %}
22-
{{ multiple | length }} to check manually,
23-
{% endif %}
24-
{{ verified | length }} automatically verified
25-
<br>
26-
{{ numUnchecked }} submissions not checked:
27-
{{ earlier | length }} verified earlier,
28-
{{ nomatch | length }} without magic string
29-
</p>
30-
31-
{% macro verifyResults(id, header, results, collapse) %}
32-
{% if results is not empty %}
33-
<h2><a class="collapse-link" href="javascript:collapse('#detail{{ id }}')">{{ header }}</a></h2>
34-
<table id="detail{{ id }}" class="{% if collapse | default(false) %}d-none{% endif %} data-table table table-hover table-striped table-sm submissions-table table-full-clickable-cell">
16+
{% macro resultTable(results, hasExpectedScore) %}
17+
<table class="data-table table table-hover table-striped table-sm submissions-table table-full-clickable-cell mb-0">
18+
<thead>
3519
<tr>
36-
<th></th><th>problem</th><th>file(s)</th>
37-
<th>actual</th>
38-
<th>expected (one of)</th>
39-
<th>actual score</th>
40-
<th>expected score</th>
20+
<th scope="col">ID</th>
21+
<th scope="col">problem</th>
22+
<th scope="col">file(s)</th>
23+
<th scope="col">actual</th>
24+
<th scope="col">expected (one of)</th>
25+
{% if hasExpectedScore %}
26+
<th scope="col">actual score</th>
27+
<th scope="col">expected score</th>
28+
{% endif %}
4129
</tr>
30+
</thead>
31+
<tbody>
4232
{% for submitId,result in results %}
4333
{% set link = path('jury_submission', {'contestId': result.contest.externalid, 'submitId': submitId}) %}
4434
<tr>
@@ -61,14 +51,12 @@
6151
</td>
6252
<td>
6353
<a href="{{ link }}">
64-
{% if result is defined and result.actual is defined %}
65-
{{ result.actual | printResult }}
66-
{% endif %}
54+
{{ result.actual | printResult }}
6755
</a>
6856
</td>
6957
<td>
7058
<a href="{{ link }}">
71-
{% if result is defined and result.expected is defined and result.expected is not empty %}
59+
{% if result.expected is defined and result.expected is not empty %}
7260
{% for r in result.expected %}
7361
{{ r | printResult }}
7462
{% if not loop.last %}, {% endif %}
@@ -78,64 +66,177 @@
7866
{% endif %}
7967
</a>
8068
</td>
81-
<td>
82-
<a href="{{ link }}">
83-
{% if result is defined and result.expectedScore is defined and result.expectedScore is not null %}
84-
{{ result.actualScore | number_format(2) }}
85-
{% else %}
86-
-
87-
{% endif %}
88-
</a>
89-
</td>
90-
<td>
91-
<a href="{{ link }}">
92-
{% if result is defined and result.expectedScore is defined and result.expectedScore is not null %}
93-
{{ result.expectedScore | number_format(2) }}
94-
{% else %}
95-
-
96-
{% endif %}
97-
</a>
98-
</td>
69+
{% if hasExpectedScore %}
70+
<td>
71+
<a href="{{ link }}">
72+
{% if result.expectedScore is defined and result.expectedScore is not null %}
73+
{{ result.actualScore | number_format(2) }}
74+
{% else %}
75+
-
76+
{% endif %}
77+
</a>
78+
</td>
79+
<td>
80+
<a href="{{ link }}">
81+
{% if result.expectedScore is defined and result.expectedScore is not null %}
82+
{{ result.expectedScore | number_format(2) }}
83+
{% else %}
84+
-
85+
{% endif %}
86+
</a>
87+
</td>
88+
{% endif %}
9989
</tr>
10090
{% endfor %}
101-
</table>
102-
{% endif %}
91+
</tbody>
92+
</table>
10393
{% endmacro %}
10494

105-
{{ checkJudgings.verifyResults('unexpected', 'Unexpected results', unexpected) }}
106-
{% if verifyMultiple %}
107-
{{ checkJudgings.verifyResults('multiple', 'Automatically verified (multiple outcomes)', multiple, true) }}
95+
{% if contestId is null %}
96+
<div class="alert alert-warning">No active contest selected.</div>
97+
{% elseif numChecked == 0 and numUnchecked == 0 %}
98+
<div class="alert alert-info">No judged submissions found.</div>
10899
{% else %}
109-
{{ checkJudgings.verifyResults('multiple', 'Check manually', multiple) }}
100+
<div class="mb-4 d-flex flex-wrap gap-2 align-items-center">
101+
<span class="badge text-bg-info">{{ numChecked + numUnchecked }} total</span>
102+
{% if unexpected|length > 0 %}
103+
<a href="#detailunexpected"><span class="badge text-bg-danger">{{ unexpected|length }} unexpected</span></a>
104+
{% endif %}
105+
{% if multiple|length > 0 %}
106+
{% if verifyMultiple %}
107+
<a href="#detailmultiple"><span class="badge text-bg-warning">{{ multiple|length }} auto-verified (multiple)</span></a>
108+
{% else %}
109+
<a href="#detailmultiple"><span class="badge text-bg-warning">{{ multiple|length }} check manually</span></a>
110+
{% endif %}
111+
{% endif %}
112+
{% if verified|length > 0 %}
113+
<a href="#detailverified"><span class="badge text-bg-success">{{ verified|length }} verified</span></a>
114+
{% endif %}
115+
{% if earlier|length > 0 %}
116+
<a href="#detailearlier"><span class="badge text-bg-secondary">{{ earlier|length }} verified earlier</span></a>
117+
{% endif %}
118+
{% if nomatch|length > 0 %}
119+
<a href="#detailnomatch"><span class="badge text-bg-secondary">{{ nomatch|length }} without magic string</span></a>
120+
{% endif %}
121+
</div>
122+
123+
{% if unexpected is not empty %}
124+
<div class="card mb-3" id="detailunexpected">
125+
<div class="card-header d-flex justify-content-between align-items-center">
126+
<span class="text-danger fw-bold">
127+
<i class="fas fa-exclamation-triangle me-1"></i>
128+
Unexpected results
129+
</span>
130+
<span class="badge text-bg-danger">{{ unexpected|length }}</span>
131+
</div>
132+
<div>
133+
{{ checkJudgings.resultTable(unexpected, hasExpectedScore) }}
134+
</div>
135+
</div>
136+
{% endif %}
137+
110138
{% if multiple is not empty %}
111-
<form action="{{ path('jury_judging_verifier') }}" method="post">
112-
<input type="hidden" name="verify_multiple" value="1">
113-
<p>
114-
Verify all multiple outcome submissions:
115-
<button type="submit" class="btn btn-primary"><i class="fas fa-check"></i> Verify</button>
116-
</p>
117-
</form>
139+
<div class="card mb-3" id="detailmultiple">
140+
<div class="card-header d-flex justify-content-between align-items-center">
141+
<a class="collapse-link text-decoration-none text-dark fw-bold" href="javascript:collapse('#detailmultiple-body')">
142+
<i class="fas fa-question-circle me-1 text-warning"></i>
143+
{% if verifyMultiple %}
144+
Automatically verified (multiple outcomes)
145+
{% else %}
146+
Check manually
147+
{% endif %}
148+
<i class="fas fa-chevron-down ms-1 small"></i>
149+
</a>
150+
<div class="d-flex align-items-center gap-2">
151+
{% if not verifyMultiple %}
152+
<form action="{{ path('jury_judging_verifier') }}" method="post">
153+
<input type="hidden" name="verify_multiple" value="1">
154+
<button type="submit" class="btn btn-primary btn-sm">
155+
<i class="fas fa-check"></i> Verify all
156+
</button>
157+
</form>
158+
{% endif %}
159+
<span class="badge text-bg-warning">{{ multiple|length }}</span>
160+
</div>
161+
</div>
162+
<div id="detailmultiple-body"{% if verifyMultiple %} class="d-none"{% endif %}>
163+
{{ checkJudgings.resultTable(multiple, hasExpectedScore) }}
164+
</div>
165+
</div>
118166
{% endif %}
119-
{% endif %}
120-
{{ checkJudgings.verifyResults('verified', 'Automatically verified', verified, true) }}
121-
{{ checkJudgings.verifyResults('earlier', 'Verified earlier', earlier, true) }}
122-
{{ checkJudgings.verifyResults('nomatch', 'Without magic string', nomatch, true) }}
123167

124-
{% if problems != [] %}
125-
<h2>Problem runtime analytics</h2>
126-
You probably want to <a href="{{ path('jury_contest', {'contestId': contestId }) }}">rejudge</a>
127-
all submissions with a reasonable overshoot and
128-
{%- if not is_granted('ROLE_ADMIN') %} let an admin {% endif %}
129-
judge the
130-
{%- if is_granted('ROLE_ADMIN') %}<a href="{{ path('jury_contest_request_remaining', {'contestId': contestId}) }}">{% endif %}
131-
remaining testcases
132-
{%- if is_granted('ROLE_ADMIN') %}</a>{% endif %}.
133-
After this compare the maximum runtime for <code>Accepted</code>
134-
solutions and tune those against expected <code>Time Limit Exceeded</code> solutions.<br>
135-
{% for p in problems %}
136-
{% set link = path('analysis_problem', {'probid': p.externalId, 'view': 'hidden'}) %}
137-
<a href="{{ link }}">{{ p | problemBadge }}</a>
138-
{% endfor %}
168+
{% if verified is not empty %}
169+
<div class="card mb-3" id="detailverified">
170+
<div class="card-header d-flex justify-content-between align-items-center">
171+
<a class="collapse-link text-decoration-none text-dark fw-bold" href="javascript:collapse('#detailverified-body')">
172+
<i class="fas fa-check-circle me-1 text-success"></i>
173+
Automatically verified
174+
<i class="fas fa-chevron-down ms-1 small"></i>
175+
</a>
176+
<span class="badge text-bg-success">{{ verified|length }}</span>
177+
</div>
178+
<div id="detailverified-body" class="d-none">
179+
{{ checkJudgings.resultTable(verified, hasExpectedScore) }}
180+
</div>
181+
</div>
182+
{% endif %}
183+
184+
{% if earlier is not empty %}
185+
<div class="card mb-3" id="detailearlier">
186+
<div class="card-header d-flex justify-content-between align-items-center">
187+
<a class="collapse-link text-decoration-none text-dark fw-bold" href="javascript:collapse('#detailearlier-body')">
188+
Verified earlier
189+
<i class="fas fa-chevron-down ms-1 small"></i>
190+
</a>
191+
<span class="badge text-bg-secondary">{{ earlier|length }}</span>
192+
</div>
193+
<div id="detailearlier-body" class="d-none">
194+
{{ checkJudgings.resultTable(earlier, hasExpectedScore) }}
195+
</div>
196+
</div>
197+
{% endif %}
198+
199+
{% if nomatch is not empty %}
200+
<div class="card mb-3" id="detailnomatch">
201+
<div class="card-header d-flex justify-content-between align-items-center">
202+
<a class="collapse-link text-decoration-none text-dark fw-bold" href="javascript:collapse('#detailnomatch-body')">
203+
Without magic string
204+
<i class="fas fa-chevron-down ms-1 small"></i>
205+
</a>
206+
<span class="badge text-bg-secondary">{{ nomatch|length }}</span>
207+
</div>
208+
<div id="detailnomatch-body" class="d-none">
209+
{{ checkJudgings.resultTable(nomatch, hasExpectedScore) }}
210+
</div>
211+
</div>
212+
{% endif %}
213+
214+
{% if problems != [] %}
215+
<div class="card mb-3">
216+
<div class="card-header">
217+
<strong>Problem runtime analytics</strong>
218+
</div>
219+
<div class="card-body">
220+
<p class="mb-2">
221+
You probably want to <a href="{{ path('jury_contest', {'contestId': contestId }) }}">rejudge</a>
222+
all submissions with a reasonable overshoot and
223+
{%- if not is_granted('ROLE_ADMIN') %} let an admin {% endif %}
224+
judge the
225+
{%- if is_granted('ROLE_ADMIN') %}<a href="{{ path('jury_contest_request_remaining', {'contestId': contestId}) }}">{% endif %}
226+
remaining testcases
227+
{%- if is_granted('ROLE_ADMIN') %}</a>{% endif %}.
228+
After this compare the maximum runtime for <code>Accepted</code>
229+
solutions and tune those against expected <code>Time Limit Exceeded</code> solutions.
230+
</p>
231+
<div class="d-flex flex-wrap gap-1">
232+
{% for p in problems %}
233+
{% set link = path('analysis_problem', {'probid': p.externalId, 'view': 'hidden'}) %}
234+
<a href="{{ link }}">{{ p | problemBadge }}</a>
235+
{% endfor %}
236+
</div>
237+
</div>
238+
</div>
239+
{% endif %}
139240
{% endif %}
140241

141242
{% endblock %}

0 commit comments

Comments
 (0)