Important
Jupyter widgets cannot be run within in documentation. To be able 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.
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 usecase for your `sum_of_squares` function and implement that usecase 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)