Skip to content
Snippets Groups Projects
Commit ed356494 authored by Philipp Risius's avatar Philipp Risius
Browse files

split showcase into three parts

parent 5d5bdcc7
No related branches found
No related tags found
No related merge requests found
%% Cell type:code id:da8df839 tags:
``` python
import pathlib
import yaml
import numpy as np
from pytest_nbgrader.test_assertions import almost_equal, equal_scope, equal_types, equal_value
from pytest_nbgrader.test_cases import TestCase, TestSubtask
from pytest_nbgrader import dumper
```
%% Cell type:code id:7ceaa747 tags:
``` python
# Task 1: Exchange two variables
left = {
'float': [1.0, 2.0, 3.0],
'int': [0, 1, 2],
'bool': [True, True, False, False],
'mixed': [1, 2.0, 3 + 4j, True],
}
right = {
'float': [4.0, 5.0, 6.0],
'int': [30, 121, 4452],
'bool': [True, False, True, False],
'mixed': [1 + 3j, 2, False, 5.0],
}
type_mixtures = (
('float', 'float'),
('int', 'int'),
('bool', 'bool'),
('int', 'float'),
('int', 'bool'),
('float', 'bool'),
('mixed', 'mixed'),
)
test_cases = {
f'{left_types}_{right_types}': [
TestCase(
inputs=(tuple(), {'a': x, 'b': y}), expected=(tuple(), {'a': y, 'b': x})
)
for x, y in zip(left[left_types], right[right_types])
]
for left_types, right_types in type_mixtures
}
assertions = {
equal_value: (('a', 'b'), {}),
equal_types: (('a', 'b'), {}),
equal_scope: (tuple(), {}),
}
task_exchange = {
types: TestSubtask(cases=test_cases[types], assertions=assertions)
for types in [f'{left}_{right}' for left, right in type_mixtures]
}
```
%% Cell type:code id:be6cbc07 tags:
``` python
# TODO: Task2: Euclidean distance
epsilon = 1e-6
cases = [
(
tuple(),
{
f'{vec}{component}': hash(f'{vec}{component}+{salt}') % 7
- 3
+ 0.01 * np.random.rand() * epsilon
for vec in 'ab'
for component in '123'
}
| {'ganz_kleiner_wert': epsilon},
)
for salt in 'abcdefghijklmnopqrstuvwxyz'
]
def euclidean_norm(a1, a2, a3, **args):
return {'abs_a': (a1 ** 2 + a2 ** 2 + a3 ** 2) ** (1 / 2)}
def difference_vector(a1, a2, a3, b1, b2, b3, **args):
return {'ab1': b1 - a1, 'ab2': b2 - a2, 'ab3': b3 - a3}
def euclidean_distance(a1, a2, a3, b1, b2, b3, **args):
return {
'abs_ab': euclidean_norm(*difference_vector(a1, a2, a3, b1, b2, b3).values())[
'abs_a'
]
}
def intersection_yz_plane(a1, a2, a3, b1, b2, b3, ganz_kleiner_wert, **args):
if abs(b1 - a1) > ganz_kleiner_wert:
s = a1 / (a1 - b1)
return {
'schnittpunkt1': 0,
'schnittpunkt2': a2 + s * (b2 - a2),
'schnittpunkt3': a3 + s * (b3 - a3),
}
else:
return {f'schnittpunkt{n}': None for n in range(1, 4)}
def cross_product(a1, a2, a3, b1, b2, b3, **args):
return {
'kreuz_produkt1': a2 * b3 - a3 * b2,
'kreuz_produkt2': a3 * b1 - a1 * b3,
'kreuz_produkt3': a1 * b2 - a2 * b1,
}
generators = {
'norm': euclidean_norm,
'difference': difference_vector,
'distance': euclidean_distance,
'schnittpunkt': intersection_yz_plane,
'kreuzprodukt': cross_product,
}
task_euclidean_geometry = {
key: TestSubtask(
cases=[
TestCase(
inputs=(args, kwargs), expected=(tuple(), function(*args, **kwargs))
)
for args, kwargs in cases
],
assertions={
almost_equal: (
tuple(function(**cases[0][1]).keys()),
{'atol': epsilon, 'rtol': epsilon},
),
},
)
for key, function in generators.items()
}
```
%% Cell type:code id:524e04dd tags:
``` python
# Task 3: Divisibility
## Subtask 1: order inputs a, b. Divisor shall be 1.
np.random.seed(1)
def order(a, b, **args):
return (a, b) if b > a else (b, a)
cases_order = [
(tuple(), dict(zip('ab', np.random.randint(-1e2, 1e2, 2))) | {'divisor': 1})
for _ in range(30)
]
results_order = [
(
tuple(),
dict(zip('ab', order(*args, **kwargs))),
)
for args, kwargs in cases_order
]
test_cases_order = [TestCase(*s) for s in zip(cases_order, results_order)]
task_divisibility = {
'order': TestSubtask(
cases=test_cases_order, assertions={equal_value: (('a', 'b'), {})}
)
}
## Subtask 2-5 assume ordered intervals (a, b). Divisor shall be 1.
def anzahl(a, b, **args):
return b - a + 1
cases_anzahl = [(args, kwargs | {'divisor': 1}) for args, kwargs in results_order]
test_cases_anzahl = [
TestCase(
inputs=(args, kwargs),
expected=(args, kwargs | {'anzahl': anzahl(*args, **kwargs)}),
)
for args, kwargs in cases_anzahl
]
task_divisibility['anzahl'] = TestSubtask(
cases=test_cases_anzahl, assertions={equal_value: (('anzahl',), {})}
)
## Subtasks 3-5 assume various divisors
def teilbar_klein(a, b, divisor, **args):
try:
return (i for i in range(a, b + 1) if i % divisor == 0).__next__()
except StopIteration:
return None
def anzahl_teilbar(a, b, divisor, **args):
return sum([i % divisor == 0 for i in range(a, b + 1)])
def produkt_teilbar(a, b, divisor, **args):
return np.prod(
[i for i in range(a, b + 1) if i % divisor == 0 and i != 0], dtype=object
)
generators = {
'teilbar_klein': teilbar_klein,
'anzahl_teilbar': anzahl_teilbar,
'produkt_teilbar': produkt_teilbar,
}
cases = [
(args, kwargs | {'divisor': np.random.randint(1, 20) * np.random.choice((-1, 1))})
for args, kwargs in results_order
]
task_divisibility |= {
key: TestSubtask(
[
TestCase(
inputs=(args, kwargs),
expected=(tuple(), {key: function(*args, **kwargs)}),
)
for args, kwargs in cases
],
assertions={equal_value: ((key,), {})},
)
for key, function in generators.items()
}
```
%% Cell type:code id:4e2ddbba tags:
``` python
dumper.dump_exercise(
{
'ExchangeVariables': task_exchange,
'EuclideanGeometry': task_euclidean_geometry,
'Divisibility': task_divisibility,
},
)
```
%% Cell type:markdown id:9774c4db-446a-4736-9b81-3147f7f2bb86 tags: %% Cell type:markdown id:9774c4db-446a-4736-9b81-3147f7f2bb86 tags:
# Showcase of nbgrader Features # Basic Notebook Features
%% Cell type:markdown id:fd8395f8-7c55-4ff3-9f65-305fb14c6c87 tags: %% Cell type:markdown id:fd8395f8-7c55-4ff3-9f65-305fb14c6c87 tags:
## Read-Only Cells ## Read-Only Cells
%% Cell type:code id:317bf900-a253-427c-b462-f46906223f16 tags: %% Cell type:code id:317bf900-a253-427c-b462-f46906223f16 tags:
``` python ``` python
import jupyterquiz import jupyterquiz
``` ```
%% Cell type:markdown id:2c76fb6c-c223-4378-8bb8-e93dc7598942 tags: %% Cell type:markdown id:2c76fb6c-c223-4378-8bb8-e93dc7598942 tags:
## Manually graded cells ## Manually graded cells
%% Cell type:markdown id:9bb543ea-7672-48f5-b61c-131c9360a93d tags: %% Cell type:markdown id:9bb543ea-7672-48f5-b61c-131c9360a93d tags:
Two options for manually graded cells: Two options for manually graded cells:
* set a read-only task to be done elsewhere * set a read-only task to be done elsewhere
* provide space for an answer * provide space for an answer
* both options can be markdown or code cells * both options can be markdown or code cells
%% Cell type:markdown id:1011509a-9018-4bcb-9bd6-ab28746312c0 tags: %% Cell type:markdown id:1011509a-9018-4bcb-9bd6-ab28746312c0 tags:
## Automatically graded cells ## Automatically graded cells
%% Cell type:code id:859cec0c-0ed2-48e0-8e3b-a5354b517652 tags: %% Cell type:markdown id:c1040a9f-845f-4f49-8396-3d8d8566593e tags:
``` python Write a function `fib(n)` to calculate the `n`-th Fibonacci number!
# This is an autograded cell
### BEGIN SOLUTION %% Cell type:code id:859cec0c-0ed2-48e0-8e3b-a5354b517652 tags:
def fib(n): ``` python
def fib(n: int) -> int:
"""calculate the n-th fibonacci number.""" """calculate the n-th fibonacci number."""
### BEGIN SOLUTION
# Students won't see the section inside the delimiters
return 1 if 0 <= n <= 1 else fib(n-1) + fib(n-2) return 1 if 0 <= n <= 1 else fib(n-1) + fib(n-2)
### END SOLUTION
### END SOLUTION
``` ```
%% Cell type:code id:0e9bbfd5-94f1-4b85-94d5-29151a89963c tags: %% Cell type:markdown id:e1a4330d-9ae7-40d2-be4b-f7f94564d35b tags:
### Manually written tests
%% Cell type:code id:9619ee19-e3f4-419c-802a-6d4c7de51791 tags:
``` python ``` python
# This test is visible # This test is visible
assert fib(0) == 1 assert fib(0) == 1
```
# This is an autotest. It will evaluate using the solution %% Cell type:code id:65e0b2ef-5b7b-4362-a40c-ace635562ca1 tags:
### AUTOTEST fib(1)
``` python
# Hidden tests won't be shown to students
### BEGIN HIDDEN TESTS ### BEGIN HIDDEN TESTS
# These tests are hidden. Students won't see them.
assert fib(4) == 5
### END HIDDEN TESTS ### END HIDDEN TESTS
``` ```
%% Cell type:markdown id:977d17b7-9643-42d1-9eb8-5c657a76fa9e tags: %% Cell type:markdown id:59c96c59-c563-4d21-bc34-bf58e1f4f4fb tags:
## Quizzes (thanks to jupyterquiz)
%% Cell type:markdown id:3a8fa6dd-8e96-4e34-9e6a-d44d56bf0af1 tags:
* Quizzes are converted to json and embedded as spans
* the converted version is "invisible"
#### Quiz ### Automatically generated tests
* (SC) What are ungraded quizzes good for?
+ This
- That
#### End Quiz %% Cell type:code id:2c8836da-d468-49ed-aab4-24a340750f22 tags:
%% Cell type:code id:b7e97aee-911e-448a-be5a-0ab4a7f71ab2 tags:
``` python ``` python
jupyterquiz.display_quiz('#quiz0') # This is an autotest. It will evaluate using the solution
### AUTOTEST fib(1); fib(2)
``` ```
%% Output %% Cell type:code id:fd405a9e-c8bc-4546-8bff-41f73c7b4c06 tags:
--------------------------------------------------------------------------- ``` python
NameError Traceback (most recent call last) # Hashed autotests hide what is being tested
Cell In[4], line 1 ### HASHED AUTOTEST fib(3)
----> 1 jupyterquiz.display_quiz('#quiz0') ```
NameError: name 'jupyterquiz' is not defined
......
%% Cell type:markdown id:84bbb171-a0e2-4506-a89e-e8f4899f537d tags:
## Quizzes (thanks to jupyterquiz)
%% Cell type:markdown id:d60f1183-279d-445c-9113-a5011e2c1f73 tags:
* Quizzes are converted to json and embedded as spans
* the converted version is "invisible"
#### Quiz
* (SC) What are ungraded quizzes good for?
+ This
- That
#### End Quiz
%% Cell type:code id:8d5f80cf-0a4f-40f7-b7a5-d84bd6f3d851 tags:
``` python
jupyterquiz.display_quiz('#quiz0')
```
%% Cell type:markdown id:ed5cb36f-5137-49b4-9b3f-e46ed0214d8a tags:
# Pytest Integration
%% Cell type:markdown id:1f18a76f-5acb-42fd-9062-2c86dd3f2e03 tags:
## Example: Divisibility
%% Cell type:markdown id:1098fa0f-433d-440d-b3bd-8c56c9d75836 tags:
You get a `lower_bound` and an `upper_bound` of an interval, as well as a `divisor`.
How many numbers in the interval are divisible by `divisor`? Write your solution to `number_of_divisibles`!
%% Cell type:markdown id:3947c769-e153-48e5-8f6d-d55ec8ab5098 tags:
Here is an example. Note that your code needs to work with different numbers as well!
%% Cell type:code id:e5474f20-00d2-4ed0-b550-c8688d4f820b tags:
``` python
lower_bound, upper_bound, divisor = 4, 19, 7
number_of_divisibles = 2
```
%% Cell type:code id:eadc399b-0fbc-42df-820e-a3adb557ca9c tags:
``` python
### BEGIN SOLUTION
number_of_divisibles = 0
for value in range(lower_bound, upper_bound):
if value % divisor == 0:
number_of_divisibles += 1
### END SOLUTION
```
%% Cell type:markdown id:98d0695a-033d-4a81-bb3e-6933b283d345 tags:
### Submit to Pytest
%% Cell type:code id:c8f51814-d7d3-488e-a54c-e920c9c2231f tags:
``` python
import pytest
from pytest_nbgrader import loader
import pathlib
loader.Submission.submit(_i)
```
%% Cell type:markdown id:4c1ec8d5-b1ac-446d-aad0-add9e12f8096 tags:
### Run test cases
%% Cell type:code id:49fe1fa4-c73d-4d05-a208-21164756d32f tags:
``` python
test_cases = pathlib.Path('tests') / 'number_divisible.yml'
```
%% Output
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[2], line 1
----> 1 test_cases = pathlib.Path('tests')
NameError: name 'pathlib' is not defined
%% Cell type:code id:1080730e-c653-4f11-84fd-3625685f333c tags:
``` python
assert pytest.main(['-x', '--cases', test_cases] is pytest.ExitCode.OK
```
%% Cell type:markdown id:93200728-9e8e-462f-98fb-a8fdcf134465 tags:
### Run Pytest File
%% Cell type:code id:641296d4-86fb-481e-91c9-ec1e62a41209 tags:
``` python
```
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