Skip to content
Snippets Groups Projects
Commit 27e97041 authored by Andreas Klöckner's avatar Andreas Klöckner
Browse files

Add utility functions to code feedback

parent 50a36d76
No related branches found
No related tags found
No related merge requests found
...@@ -13,6 +13,11 @@ ...@@ -13,6 +13,11 @@
Ideas Ideas
===== =====
- Auto-expire
- Do print() and plot() from testing code count?
Make a setting.
- Session reset UI - Session reset UI
- Allow just viewing flow without any possibility for submission - Allow just viewing flow without any possibility for submission
......
...@@ -152,6 +152,55 @@ class Feedback: ...@@ -152,6 +152,55 @@ class Feedback:
self.set_points(points) self.set_points(points)
raise GradingComplete() raise GradingComplete()
def _check_numpy_array_base(self, name, ref, data):
import numpy as np
assert isinstance(ref, np.ndarray)
if not isinstance(data, np.ndarray):
self.finish(0, "'%s' is not a numpy array" % name)
if ref.shape != data.shape:
self.finish(
0, "'%s' does not have correct shape--"
"got: '%s', expected: '%s'" % (
name, data.shape, ref.shape))
if ref.dtype.kind != data.dtype.kind:
self.finish(
0, "'%s' does not have correct data type--"
"got: '%s', expected: '%s'" % (
name, data.dtype.kind, ref.dtype.kind))
def check_numpy_array_allclose(self, name, ref, data, accuracy_critical=True,
rtol=1e-05, atol=1e-08):
import numpy as np
self._check_numpy_array_base(name, ref, data)
good = np.allclose(ref, data, rtol=rtol, atol=atol)
if not good:
self.add_feedback("'%s' is inaccurate" % name)
if accuracy_critical and not good:
self.set_points(0)
raise GradingComplete()
return good
def check_list(self, name, ref, data, entry_type=None):
assert isinstance(ref, list)
if not isinstance(data, list):
self.finish(0, "'%s' is not a list" % name)
if len(ref) != len(data):
self.finish(0, "'%s' has the wrong length--expected %d, got %d"
% (name, len(ref), len(list)))
if entry_type is not None:
for i, entry in enumerate(data):
if not isinstance(entry, entry_type):
self.finish(0, "'%s[i]' has the wrong type" % (name, i))
def run_code(result, run_req): def run_code(result, run_req):
# {{{ compile code # {{{ compile code
......
...@@ -351,7 +351,7 @@ class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue): ...@@ -351,7 +351,7 @@ class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue):
* ``GradingComplete``: An exception class that can be raised to indicated * ``GradingComplete``: An exception class that can be raised to indicated
that the grading code has concluded. that the grading code has concluded.
* ``feedback``: A class instance with three methods:: * ``feedback``: A class instance with the following interface::
feedback.set_points(0.5) # 0<=points<=1 (usually) feedback.set_points(0.5) # 0<=points<=1 (usually)
feedback.add_feedback("This was wrong") feedback.add_feedback("This was wrong")
...@@ -359,6 +359,11 @@ class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue): ...@@ -359,6 +359,11 @@ class PythonCodeQuestion(PageBaseWithTitle, PageBaseWithValue):
# combines the above two and raises GradingComplete # combines the above two and raises GradingComplete
feedback.finish(0, "This was wrong") feedback.finish(0, "This was wrong")
feedback.check_numpy_array_allclose(name, ref, data,
accuracy_critical=True, rtol=1e-5, atol=1e-8)
feedback.check_list(name, ref, data, entry_type=None)
* ``data_files``: A dictionary mapping file names from :attr:`data_files` * ``data_files``: A dictionary mapping file names from :attr:`data_files`
to :class:`bytes` instances with that file's contents. to :class:`bytes` instances with that file's contents.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment