Getting started

scicode-widgets can be installed with:

pip install scwidgets

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%2Fgetting_started.ipynb. Note that also the LaTeX rendering is resolved when running the notebook.

Creating a code exercise

This is how a simple coding exercise can look like.

[1]:
from scwidgets import CodeExercise

import matplotlib.pyplot as plt
import numpy as np

# This is what the students sees and can adapt
def sin(x, omega):
    import numpy as np
    # We already provide the solution for the demo
    return np.sin(x*omega)


x = np.linspace(-2*np.pi, 2*np.pi, 100)
def update_func(code_ex):
    y = code_ex.code(x, code_ex.parameters["omega"])
    ax = code_ex.figure.gca()
    ax.plot(x, y)

code_ex = CodeExercise(
    code=sin,
    outputs=plt.figure(),
    parameters={"omega": (0.5, 3.14, 0.1)},
    update=update_func,
    update_mode="continuous",
    title="Sine function",
    description="Implements $\\sin(x\\omega)$",
)

code_ex.run_update() # For the demonstration we run the widget one time
display(code_ex)

Please look at exercises section for more information about the exercises that can be created and their customization options.

Check student’s solution

You can create checks (like unit tests) to help students verify their solutions:

[2]:
from scwidgets import (
    CheckRegistry,
    assert_numpy_allclose,
    assert_shape,
    assert_type,
)


check_registry = CheckRegistry()

def sine(arr):
    import numpy as np
    return np.cos(arr) # oops! wrong solution

check_code_ex = CodeExercise(
    code=sine,
    update=lambda code_ex: print(code_ex.code(np.pi)),
    check_registry=check_registry,
)

def assert_2pi_periodic() -> str:
    out = check_code_ex.code([0, 2*np.pi])
    if not np.allclose(out[0], out[1]):
        return "Function is not periodic."
    return "" # empty strings means it passes

check_registry.add_check(
    check_code_ex,
    asserts=[
        assert_2pi_periodic,
    ]
)

check_registry.add_check(
    check_code_ex,
    asserts=[
        assert_type, # checks if same type as reference values
        assert_shape, # checks if same shape as reference values
        assert_numpy_allclose, # checks if allclose to reference values
    ],
    inputs_parameters=[{"arr": np.asarray([0., 0.78539816, 1.57079633, 2.35619449, 3.14159265])}],
    outputs_references=[(np.asarray([0., 7.07106781e-01, 1.00000000e+00, 7.07106781e-01, 0.]),)]
)

check_code_ex.run_check()
check_code_ex
[2]:

Please look at the section on how to add checks for all options to create them.

nbgrader integration

One can use nbgrader by using their macros.

def sin(arr: np.ndarray):
    """
    :param arr: array of arbitrary shape
    :return: returns the sine
    """
    import numpy as np
    ### BEGIN SOLUTION
    sin_arr = np.sin(arr)
    ### END SOLUTION
    return sin_arr

Then, nbgrader will convert this to

[3]:
def sin(arr: np.ndarray):
    """
    :param arr: array of arbitrary shape
    :return: returns the sine
    """
    import numpy as np
    # YOUR CODE HERE
    raise NotImplementedError()
    return sin_arr

code_ex = CodeExercise(
    code=sin
)

display(code_ex)

It requires to add a hook in the config to copy over the student’s answers to the grading subfolder. A step-by-step tutorial how to make an nbgrader project compatible with scwidget can be seen in the repo.