Skip to content

Commit 4a0ca4a

Browse files
Explanation for submission details in CWS
1 parent 85df23b commit 4a0ca4a

4 files changed

Lines changed: 174 additions & 19 deletions

File tree

cms/grading/__init__.py

Lines changed: 134 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,125 @@ def N_(message):
5858
return message
5959

6060

61+
class HumanMessage(object):
62+
"""Represent a possible outcome message for a grading, to be presented
63+
to the contestants.
64+
65+
"""
66+
67+
def __init__(self, shorthand, message, help):
68+
"""Initialization.
69+
70+
shorthand (unicode): what to call this message in the code.
71+
message (unicode): the message itself.
72+
help (unicode): a longer explanation for the help page.
73+
74+
"""
75+
self.shorthand = shorthand
76+
self.message = message
77+
self.help = help
78+
79+
80+
class MessageCollection(object):
81+
"""Represent a collection of messages, with error checking."""
82+
83+
def __init__(self, messages=None):
84+
self._messages = {}
85+
self._ordering = []
86+
if messages is not None:
87+
for message in messages:
88+
self.add(message)
89+
90+
def add(self, message):
91+
if message.shorthand in self._messages:
92+
logger.error("Trying to registering duplicate message `%s'.",
93+
message.shorthand)
94+
return
95+
self._messages[message.shorthand] = message
96+
self._ordering.append(message.shorthand)
97+
98+
def get(self, shorthand):
99+
if shorthand not in self._messages:
100+
error = "Trying to get a non-existing message `%s'." % \
101+
shorthand
102+
logger.error(error)
103+
raise KeyError(error)
104+
return self._messages[shorthand]
105+
106+
def all(self):
107+
ret = []
108+
for shorthand in self._ordering:
109+
ret.append(self._messages[shorthand])
110+
return ret
111+
112+
113+
COMPILATION_MESSAGES = MessageCollection([
114+
HumanMessage("success",
115+
N_("Compilation succeeded"),
116+
N_("Your submission successfully compiled to an excutable.")),
117+
HumanMessage("fail",
118+
N_("Compilation failed"),
119+
N_("Your submission did not compile correctly.")),
120+
HumanMessage("timeout",
121+
N_("Compilation timed out"),
122+
N_("Your submission exceeded the time limit while compiling. "
123+
"This might be caused by an excessive use of C++ "
124+
"templates, for example.")),
125+
HumanMessage("signal",
126+
N_("Compilation killed with signal %s (could be triggered "
127+
"by violating memory limits)"),
128+
N_("Your submission was killed with the specified signal. "
129+
"Among other things, this might be caused by exceeding "
130+
"the memory limit for the compilation, and in turn by an "
131+
"excessive use of C++ templates, for example.")),
132+
])
133+
134+
135+
EVALUATION_MESSAGES = MessageCollection([
136+
HumanMessage("success",
137+
N_("Output is correct"),
138+
N_("Your submission ran and gave the correct answer")),
139+
HumanMessage("wrong",
140+
N_("Output isn't correct"),
141+
N_("Your submission ran, but gave the wrong answer")),
142+
HumanMessage("nooutput",
143+
N_("Evaluation didn't produce file %s"),
144+
N_("Your submission ran, but did not write on the "
145+
"correct output file")),
146+
HumanMessage("timeout",
147+
N_("Execution timed out"),
148+
N_("Your submission used too much CPU time.")),
149+
HumanMessage("walltimeout",
150+
N_("Execution timed out (wall clock limit exceeded)"),
151+
N_("Your submission used too much total time. This might "
152+
"be triggered by undefined code, or buffer overflow, "
153+
"for example. Note that in this case the CPU time "
154+
"visible in the submission details might be much smaller "
155+
"than the time limit.")),
156+
HumanMessage("signal",
157+
N_("Execution killed with signal %d (could be triggered by "
158+
"violating memory limits)"),
159+
N_("Your submission was killed with the specified signal. "
160+
"Among other things, this might be caused by exceeding "
161+
"the memory limit. Note that if this is the reason, "
162+
"the memory usage visible in the submission details is "
163+
"the usage before the allocation that caused the "
164+
"signal.")),
165+
HumanMessage("syscall",
166+
N_("Execution killed because of forbidden syscall %s"),
167+
N_("Your submission was killed because it tried to use "
168+
"the forbidden syscall specified in the message.")),
169+
HumanMessage("fileaccess",
170+
N_("Execution killed because of forbidden file access"),
171+
N_("Your submission was killed because it tried to read "
172+
"or write a forbidden file.")),
173+
HumanMessage("returncode",
174+
N_("Execution failed because the return code was nonzero"),
175+
N_("Your submission failed because it exited with a return "
176+
"code different from 0.")),
177+
])
178+
179+
61180
class JobException(Exception):
62181
"""Exception raised by a worker doing a job.
63182
@@ -273,23 +392,23 @@ def compilation_step(sandbox, commands):
273392
logger.debug("Compilation successfully finished.")
274393
success = True
275394
compilation_success = True
276-
text = [N_("Compilation succeeded")]
395+
text = [COMPILATION_MESSAGES.get("success").message]
277396

278397
# Error in compilation: returning the error to the user.
279398
elif (exit_status == Sandbox.EXIT_OK and exit_code != 0) or \
280399
exit_status == Sandbox.EXIT_NONZERO_RETURN:
281400
logger.debug("Compilation failed.")
282401
success = True
283402
compilation_success = False
284-
text = [N_("Compilation failed")]
403+
text = [COMPILATION_MESSAGES.get("fail").message]
285404

286405
# Timeout: returning the error to the user
287406
elif exit_status == Sandbox.EXIT_TIMEOUT or \
288407
exit_status == Sandbox.EXIT_TIMEOUT_WALL:
289408
logger.debug("Compilation timed out.")
290409
success = True
291410
compilation_success = False
292-
text = [N_("Compilation timed out")]
411+
text = [COMPILATION_MESSAGES.get("timeout").message]
293412

294413
# Suicide with signal (probably memory limit): returning the error
295414
# to the user
@@ -299,8 +418,7 @@ def compilation_step(sandbox, commands):
299418
success = True
300419
compilation_success = False
301420
plus["signal"] = signal
302-
text = [N_("Compilation killed with signal %d (could be triggered "
303-
"by violating memory limits)"), signal]
421+
text = [COMPILATION_MESSAGES.get("signal").message, signal]
304422

305423
# Sandbox error: this isn't a user error, the administrator needs
306424
# to check the environment
@@ -497,32 +615,30 @@ def human_evaluation_message(plus):
497615
"""Given the plus object returned by evaluation_step, builds a
498616
human-readable message about what happened.
499617
500-
None is returned in cases when the contestant musn't receive any
618+
None is returned in cases when the contestant mustn't receive any
501619
message (for example, if the execution couldn't be performed) or
502620
when the message will be computed somewhere else (for example, if
503-
the execution was successfull, then the comparator is supposed to
621+
the execution was successful, then the comparator is supposed to
504622
write the message).
505623
506624
"""
507625
exit_status = plus['exit_status']
508626
if exit_status == Sandbox.EXIT_TIMEOUT:
509-
return [N_("Execution timed out")]
627+
return [EVALUATION_MESSAGES.get("timeout").message]
510628
elif exit_status == Sandbox.EXIT_TIMEOUT_WALL:
511-
return [N_("Execution timed out (wall clock limit exceeded)")]
629+
return [EVALUATION_MESSAGES.get("walltimeout").message]
512630
elif exit_status == Sandbox.EXIT_SIGNAL:
513-
return [N_("Execution killed with signal %d (could be triggered by "
514-
"violating memory limits)"), plus['signal']]
631+
return [EVALUATION_MESSAGES.get("signal").message % plus['signal']]
515632
elif exit_status == Sandbox.EXIT_SANDBOX_ERROR:
516633
return None
517634
elif exit_status == Sandbox.EXIT_SYSCALL:
518-
return [N_("Execution killed because of forbidden syscall %s"),
519-
plus['syscall']]
635+
return [EVALUATION_MESSAGES.get("syscall").message % plus['syscall']]
520636
elif exit_status == Sandbox.EXIT_FILE_ACCESS:
521637
# Don't tell which file: would be too much information!
522-
return [N_("Execution killed because of forbidden file access")]
638+
return [EVALUATION_MESSAGES.get("fileaccess").message]
523639
elif exit_status == Sandbox.EXIT_NONZERO_RETURN:
524640
# Don't tell which code: would be too much information!
525-
return [N_("Execution failed because the return code was nonzero")]
641+
return [EVALUATION_MESSAGES.get("returncode").message]
526642
elif exit_status == Sandbox.EXIT_OK:
527643
return None
528644
else:
@@ -692,13 +808,13 @@ def white_diff_step(sandbox, output_filename,
692808
res_file = sandbox.get_file(correct_output_filename)
693809
if white_diff(out_file, res_file):
694810
outcome = 1.0
695-
text = [N_("Output is correct")]
811+
text = [EVALUATION_MESSAGES.get("success").message]
696812
else:
697813
outcome = 0.0
698-
text = [N_("Output isn't correct")]
814+
text = [EVALUATION_MESSAGES.get("wrong").message]
699815
else:
700816
outcome = 0.0
701-
text = [N_("Evaluation didn't produce file %s"), output_filename]
817+
text = [EVALUATION_MESSAGES.get("nooutput").message, output_filename]
702818
return outcome, text
703819

704820

cms/io/rpc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ def process_incoming_request(self, request):
383383
try:
384384
data = json.dumps(response, encoding='utf-8')
385385
except (TypeError, ValueError):
386-
logger.warning("JSON encoding failed.")
386+
logger.warning("JSON encoding failed.", exc_info=True)
387387
return
388388

389389
# Send it.

cms/server/templates/contest/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
{% import json %}
44
{% from cms import LANGUAGE_NAMES, LANGUAGE_TO_SOURCE_EXT_MAP %}
55
{% from cms.server import format_amount_of_time, format_time, format_datetime, format_datetime_smart, get_score_class, encode_for_url %}
6+
{% from cms.grading import COMPILATION_MESSAGES, EVALUATION_MESSAGES %}
67
{% from cmscommon.datetime import make_timestamp, utc %}
78
{% from cmscommon.isocodes import translate_language_country_code %}
89
<!DOCTYPE html>

cms/server/templates/contest/documentation.html

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,47 @@
66
<h1>{{ _("Documentation") }}</h1>
77
</div>
88

9+
<h2>Programming languages and libraries</h2>
910

1011
<a href="{{ url_root }}/stl/index.html">{{ _("Standard Template Library") }}</a>
1112

13+
<h2>Submission details for compilation</h2>
14+
15+
<table class="table table-bordered">
16+
<thead>
17+
<tr>
18+
<th style="width: 30%;">Message</th>
19+
<th>Explanation</th>
20+
</tr>
21+
</thead>
22+
<tbody>
23+
{% for message in COMPILATION_MESSAGES.all() %}
24+
<tr>
25+
<td><em>{{ message.message }}</em></td>
26+
<td>{{ message.help }}</td>
27+
</tr>
28+
{% end %}
29+
</tbody>
30+
</table>
31+
32+
<h2>Submission details for evaluation</h2>
33+
34+
<table class="table table-bordered">
35+
<thead>
36+
<tr>
37+
<th style="width: 30%;">Message</th>
38+
<th>Explanation</th>
39+
</tr>
40+
</thead>
41+
<tbody>
42+
{% for message in EVALUATION_MESSAGES.all() %}
43+
<tr>
44+
<td><em>{{ message.message }}</em></td>
45+
<td>{{ message.help }}</td>
46+
</tr>
47+
{% end %}
48+
</tbody>
49+
</table>
1250

1351
</div>
1452
{% end %}

0 commit comments

Comments
 (0)