Getting started

scicode-widgets can be installed with:

pip install scicode-widgets

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%2Fgetting_started.ipynb.

Creating a code execrcise

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 provide already 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="Sinus 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 section exercises for more information about the execrises that can be created and their customization options.

Check students solution

You can create checks for student that work like unit tests helping the student to verify the students solution

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


check_registry = CheckRegistry()

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

check_code_ex = CodeExercise(
    code=sinus,
    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 how to add checks for all options to create checks.

nbgrader integration

One can use nbgrader by using their macros.

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

Then nbgrader will convert this to

[3]:
def sin(arr: np.ndarray):
    """
    :param arr: array of arbitrary shape
    :return: returns the sinus
    """
    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 students answers to the grading subfolder. A step-by-step tutorial how to make an nbgrader project compatible with scicode-widget can be seen in the repo TODO.