forked from datacamp/pythonwhat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReporter.py
More file actions
153 lines (123 loc) · 4.86 KB
/
Reporter.py
File metadata and controls
153 lines (123 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
from pythonwhat.Feedback import Feedback
import re
import markdown2
from pythonwhat.Test import TestFail, Test
"""
This file holds the reporter class.
"""
class Reporter(object):
"""Do reporting.
This class holds the feedback- or success message and tracks whether there are failed tests
or not. All tests are executed trough do_test() in the Reporter.
"""
active_reporter = None
def __init__(self):
self.failed_test = False
self.feedback = Feedback("Oh no, your solution is incorrect! Please, try again.")
self.success_msg = "Great work!"
self.errors_allowed = False
self.tags = {}
self.failure_msg = ""
self.fallback_ast = None
self.test_stack = []
self.test_mode = None
def set_success_msg(self, success_msg):
self.success_msg = success_msg
def allow_errors(self):
self.errors_allowed = True
def reject_errors(self):
self.errors_allowed = False
def fail(self, failure_msg):
self.failed_test = True
self.feedback = Feedback(failure_msg)
def do_test(self, testobj, prepend_on_fail="", fallback_ast=None):
"""Do test.
Execute a given test, unless some previous test has failed. If the test has failed,
the state of the reporter changes and the feedback is kept.
"""
if self.test_mode is 'or':
return self.test_stack.append([testobj, prepend_on_fail, fallback_ast])
if prepend_on_fail: self.failure_msg = prepend_on_fail
if fallback_ast: self.fallback_ast = fallback_ast
if self.failed_test:
self.feedback.message = self.failure_msg + self.feedback.message
raise TestFail
return
if isinstance(testobj, Test):
testobj.test()
result = testobj.result
if (not result):
self.failed_test = True
self.feedback = testobj.get_feedback()
self.feedback.message = self.failure_msg + self.feedback.message
if not self.feedback.line_info and self.fallback_ast:
self.feedback = Feedback(self.feedback.message, self.fallback_ast)
raise TestFail
else:
result = None
testobj() # run function for side effects
#self.failure_msg_stack.pop()
return result
def do_tests(self, testobjs):
"""Do multiple tests.
Execute an array of tests.
"""
for testobj in testobjs:
if self.failed_test:
break
self.do_test(testobj)
def start_or_test(self):
self.test_mode = 'or'
self.test_stack = []
def end_or_test(self):
self.test_mode = None
first_message = None
success = False
for sct_args in self.test_stack:
try:
self.do_test(*sct_args)
success = True
except TestFail as e:
if not first_message: first_message = self.feedback.message
self.failed_test = False
if success: return
self.failed_test = True
self.feedback.message = first_message
raise TestFail
def set_tag(self, key, value):
self.tags[key] = value
def build_payload(self, error):
if (error and not self.failed_test and not self.errors_allowed):
feedback_msg = "Your code contains an error: `%s`" % str(error[1])
return({
"correct": False,
"message": Reporter.to_html(feedback_msg),
"tags": {"fun": "runtime_error"}})
if self.failed_test:
if not self.feedback.line_info:
return({
"correct": False,
"message": Reporter.to_html(self.feedback.message),
"tags": self.tags})
else:
# Hack to make it work with campus app implementation
if self.feedback.line_info["column_start"] is None:
col_start = None
else:
col_start = self.feedback.line_info["column_start"] + 1
return({
"correct": False,
"message": Reporter.to_html(self.feedback.message),
"line_start": self.feedback.line_info["line_start"],
"column_start": col_start,
"line_end": self.feedback.line_info["line_end"],
"column_end": self.feedback.line_info["column_end"],
"tags": self.tags})
else:
return({
"correct": True,
"message": Reporter.to_html(self.success_msg)
})
@staticmethod
def to_html(msg):
return(re.sub("<p>(.*)</p>", "\\1", markdown2.markdown(msg)).strip())