Important
Jupyter widgets cannot be run within the documentation. To interact with the widget, you must run a mybinder instance. To run a mybinder instance of this notebook, please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fnbgrader.ipynb. Note that also the LaTeX rendering is resolved when running the notebook.
Integrating with nbgrader¶
Problem1 from the quickstart of nbgrader using scwidgets¶
When you initialize the quickstart project from nbgrader, a problem1.ipynb file is generated. In this notebook, we show how scwidgets can be used with nbgrader. We transformed the problem1.ipynb to a version that uses scwidgets.
[1]:
from scwidgets import CodeExercise, TextExercise, ExerciseRegistry, CheckRegistry, CodeInput, assert_type, assert_equal
[2]:
### BEGIN HIDDEN TESTS
if 'NAME' not in globals():
NAME = "reference solution"
### END HIDDEN TESTS
exercise_registry = ExerciseRegistry(filename_prefix="problem1")
exercise_registry
[2]:
[3]:
check_registry = CheckRegistry()
check_registry
[3]:
[4]:
def squares(n):
"""Compute the squares of numbers from 1 to n, such that the
ith element of the returned list equals i^2.
"""
### BEGIN SOLUTION
if n < 1:
raise ValueError("n must be greater than or equal to 1")
return [i ** 2 for i in range(1, n + 1)]
### END SOLUTION
description = """
Write a function that returns a list of numbers,
such that $x_i=i^2$, for $1\leq i \leq n$.
Make sure it handles the case where $n<1$ by raising a `ValueError`.
"""
code_ex_squares = CodeExercise(
code=squares,
parameters={"n": (1, 10, 1)},
update=lambda code_ex: print(code_ex.code(code_ex.parameters['n'])),
check_registry=check_registry,
exercise_registry=exercise_registry,
key="Part A (2 points)",
description=description
)
# Check that squares returns the correct output for several inputs
check_registry.add_check(
code_ex_squares,
asserts=[assert_type, assert_equal],
inputs_parameters=[{"n": i} for i in [1, 2, 10, 11]],
outputs_references=[([1],), ([1, 4],), ([1, 4, 9, 16, 25, 36, 49, 64, 81, 100],), ([1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121],)],
)
# Check that squares raises an error for invalid inputs
def assert_raise_error() -> str:
try:
code_ex_squares.code.unwrapped_function(0)
except ValueError:
return ""
else:
return "Did not raise error for zero"
try:
code_ex_squares.code.unwrapped_function(-4)
except ValueError:
return ""
else:
return "Did not error for negative number"
check_registry.add_check(
code_ex_squares,
asserts=[
assert_raise_error,
]
)
code_ex_squares
[4]:
[5]:
### BEGIN HIDDEN TESTS
exercise_registry.load_answer_from_student_name(NAME, code_ex_squares)
checks = check_registry.check_widget(code_ex_squares)
assert checks[0].successful, checks[0].message()
### END HIDDEN TESTS
[6]:
### BEGIN HIDDEN TESTS
assert checks[1].successful, checks[1].message()
### END HIDDEN TESTS
[7]:
from scwidgets import assert_type, assert_equal
def sum_of_squares(n):
"""Compute the sum of the squares of numbers from 1 to n."""
### BEGIN SOLUTION
return sum(squares(n))
### END SOLUTION
description = """
Using your `squares` function, write a function
that computes the sum of the squares of the numbers
from 1 to $n$. Your function should call the `squares`
function -- it should NOT reimplement its functionality.
"""
code_ex_sum_of_squares = CodeExercise(
code=CodeInput(sum_of_squares, builtins={"squares": code_ex_squares.code.function}),
parameters={"n": (1, 10, 1)},
update=lambda code_ex: print(code_ex.code(code_ex.parameters['n'])),
check_registry=check_registry,
exercise_registry=exercise_registry,
key="Part B (1 point)",
description=description
)
# Check that sum_of_squares returns the correct answer for various inputs
check_registry.add_check(
code_ex_sum_of_squares,
asserts=[assert_type, assert_equal],
inputs_parameters=[{"n": i} for i in [1, 2, 10, 11]],
outputs_references=[(1,), (5,), (385,), (506,)],
)
# Check that sum_of_squares relies on squares
def assert_uses_squares() -> str:
"""Check that sum_of_squares relies on squares."""
code_ex_sum_of_squares.code.builtins = {}
try:
code_ex_sum_of_squares.code.unwrapped_function(1) # not using builtins
except NameError:
result = ""
else:
result = "sum_of_squares does not use squares"
code_ex_sum_of_squares.code.builtins = {"squares": code_ex_squares.code.function}
return result
check_registry.add_check(
code_ex_sum_of_squares,
asserts=[
assert_uses_squares,
]
)
code_ex_sum_of_squares
[7]:
[8]:
### BEGIN HIDDEN TESTS
exercise_registry.load_answer_from_student_name(NAME, code_ex_sum_of_squares)
checks = check_registry.check_widget(code_ex_sum_of_squares)
assert checks[0].successful, checks[0].message()
### END HIDDEN TESTS
[9]:
### BEGIN HIDDEN TESTS
assert checks[1].successful, checks[1].message()
### END HIDDEN TESTS
[10]:
value = """
### BEGIN SOLUTION
$\sum_{i=1}^n i^2$
### END SOLUTION
"""
description = """
Using LaTeX math notation, write out the equation
that is implemented by your `sum_of_squares` function."""
text_ex = TextExercise(
value=value,
exercise_registry=exercise_registry,
key="Part C (1 point)",
description=description
)
text_ex
[10]:
[11]:
### BEGIN HIDDEN TESTS
exercise_registry.load_answer_from_student_name(NAME, text_ex)
### END HIDDEN TESTS
[12]:
def pyramidal_number(n):
"""Returns the n^th pyramidal number"""
summation = sum_of_squares(n)
### BEGIN SOLUTION
### END SOLUTION
description = """
Find a use case for your `sum_of_squares` function and implement that use case in the cell below.
"""
code_ex_pyramidal_number = CodeExercise(
code=CodeInput(pyramidal_number, builtins={"sum_of_squares": code_ex_sum_of_squares.code.function}),
parameters={"n": (1, 10, 1)},
update=lambda code_ex: print(code_ex.code(code_ex.parameters['n'])),
exercise_registry=exercise_registry,
key="Part D (2 points)",
description=description
)
code_ex_pyramidal_number
[12]:
[13]:
### BEGIN HIDDEN TESTS
exercise_registry.load_answer_from_student_name(NAME, code_ex_pyramidal_number)
### END HIDDEN TESTS
[14]:
description = """
State the formulae for an arithmetic and geometric
sum and verify them numerically for an example of
your choice."""
text_ex = TextExercise(
value="""
### BEGIN SOLUTION
### END SOLUTION
""",
exercise_registry=exercise_registry,
key="Part E (4 points)",
description=description
)
text_ex
[14]:
[15]:
### BEGIN HIDDEN TESTS
exercise_registry.load_answer_from_student_name(NAME, text_ex)
### END HIDDEN TESTS
[16]:
try:
exercise_registry.load_file_from_student_name(NAME)
except FileNotFoundError:
exercise_registry.create_new_file_from_student_name(NAME)