Skip to content

Commit

Permalink
Improves the writing of the assignment (#47)
Browse files Browse the repository at this point in the history
* Add test with bugs

* Fix QA bug.

* Adjust test nbs structure.

* Run black and isort.

* Ignore type hinting on an import.
  • Loading branch information
drvinceknight committed Dec 14, 2020
1 parent 9bb723b commit 5166317
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 6 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Unidecode==1.1.1
jupyter
typer
11 changes: 5 additions & 6 deletions src/nbchkr/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
from typing import Tuple, Union

import nbformat # type: ignore
import unidecode # type: ignore
from nbconvert.preprocessors import ExecutePreprocessor # type: ignore

TAGS_REGEX_PATTERNS_TO_IGNORE = ["hide", r"score:\d"]
SOLUTION_REGEX = re.compile(
r"### BEGIN SOLUTION[\s\S](.*?)[\s\S]### END SOLUTION", re.DOTALL
)
SOLUTION_REPL = """### BEGIN SOLUTION
SOLUTION_REPL = r"""### BEGIN SOLUTION
### END SOLUTION"""
Expand Down Expand Up @@ -57,17 +58,15 @@ def remove_cells(
for pattern in tags_regex_patterns_to_ignore
):
try:
source = "".join(cell["source"])
source = unidecode.unidecode("".join(cell["source"]))
new_source = re.sub(
pattern=solution_regex, repl=solution_repl, string=source
)
cell["source"] = new_source

if bool(re.match(pattern=solution_regex, string=source)) is True:
try:
if solution_repl in cell["source"]:
if "outputs" in cell.keys():
cell["outputs"] = []
except KeyError: # pragma: no cover
pass # TODO Add test coverage for this statement
except KeyError: # pragma: no cover
pass # TODO Add test coverage for this statement
cells.append(cell)
Expand Down
188 changes: 188 additions & 0 deletions tests/nbs/specific/main-notebook-with-hidden-problems.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Question 1\n",
"\n",
"(__Hint__: This question is similar to the [first exercise of the Probability chapter of Python for mathematics](https://vknight.org/pfm/tools-for-mathematics/06-probability/exercises/main.html#exercises).)\n",
"\n",
"For each of the following, write a function `sample_experiment`, and repeatedly use it to simulate the probability of an event occurring with the following chances.\n",
"\n",
"For each chance **output** the simulated probability.\n",
"\n",
"a. $0$ \n",
"\n",
"_Available marks: 2_"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": [
"answer:q1-a"
]
},
"outputs": [
{
"data": {
"text/plain": [
"0.0"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import random\n",
"\n",
"def sample_experiment():\n",
" ### BEGIN SOLUTION\n",
" \"\"\"\n",
" Returns true if a random number is less than 0\n",
" \"\"\"\n",
" return random.random() < 0\n",
"\n",
"\n",
"number_of_experiments = 1000\n",
"sum(\n",
" sample_experiment() for repetition in range(number_of_experiments)\n",
") / number_of_experiments\n",
"### END SOLUTION"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"tags": [
"score:1",
"description:correct-answer"
]
},
"outputs": [],
"source": [
"q1_a_answer = _\n",
"q1_a_expected_answer = 0\n",
"feedback_text = \"\"\"Your output is not the expected answer\n",
"\"\"\"\n",
"assert q1_a_answer == q1_a_expected_answer, feedback_text"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"tags": [
"score:1",
"description:use-of-docstrings"
]
},
"outputs": [],
"source": [
"feedback_text = \"\"\"You did not include a docstring. This is important to help document your code.\n",
"\n",
"\n",
"It is done using triple quotation marks. For example:\n",
"\n",
"def get_remainder(m, n):\n",
" \\\"\\\"\\\"\n",
" This function returns the remainder of m when dividing by n\n",
" \\\"\\\"\\\"\n",
"\n",
"\n",
"Using that it's possible to access the docstring,\n",
"one way to do this is to type: `get_remainder?`\n",
"(which only works in Jupyter) or help(get_remainder).\n",
"\n",
"We can also comment code using `#` but this is completely\n",
"ignored by Python so cannot be accessed in the same way.\n",
"\n",
"\"\"\"\n",
"try:\n",
" assert sample_experiment.__doc__ is not None, feedback_text\n",
"except NameError:\n",
" assert False, \"You did not create a function called `sample_experiment`\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Question 2\n",
"\n",
"\n",
"(__Hint__: This question is similar to the [second exercise of the Combinatorics chapter of Python for mathematics](https://vknight.org/pfm/tools-for-mathematics/05-combinations-permutations/exercises/main.html).)\n",
"\n",
"a. Create a variable `number_of_permutations` that gives the number of permutations of `animals = (\"cat\", \"dog\", \"fish\", \"lizard\")` of size 3. Do this by generating and counting them.\n",
"\n",
"_Available marks: 2_"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"tags": [
"answer:q2-a"
]
},
"outputs": [],
"source": [
"import itertools\n",
"\n",
"animals = (\"cat\", \"dog\", \"fish\", \"lizard\")\n",
"### BEGIN SOLUTION\n",
"permutations = tuple(itertools.permutations(animals, 3))\n",
"number_of_permutations = len(permutations)\n",
"### END SOLUTION"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"tags": [
"score:2",
"description:correct-answer"
]
},
"outputs": [],
"source": [
"expected_number_of_permutations = 24\n",
"feedback = \"The expected answer is 24\"\n",
"\n",
"try:\n",
" assert expected_number_of_permutations == number_of_permutations, feedback\n",
"except NameError:\n",
" print(\"You did not create a variable called `number_of_permutations`\")"
]
}
],
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
20 changes: 20 additions & 0 deletions tests/test_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,23 @@ def test_write_nb():
except FileNotFoundError: # TODO Ensure py3.8 is used so that can pass
# `missing_ok=True` to `path.unlink`.
pass


def test_remove_solution_and_output_for_nb_with_hidden_problems():
"""
This checks that solutions text is not included for
`notebook-with-hidden-problems.ipynb` which has two bugs:
- The output is included
- The characters for `### BEGIN SOLUTION` are not recognized.
"""
nb_path = NB_PATH / "specific/main-notebook-with-hidden-problems.ipynb"
nb_node = nbchkr.utils.read(nb_path=nb_path)
assert "permutations = tuple(itertools.permutations(animals, 3))" in str(nb_node)
assert "0.0" in str(nb_node)

student_nb = nbchkr.utils.remove_cells(nb_node=nb_node)
assert "permutations = tuple(itertools.permutations(animals, 3))" not in str(
student_nb
)
assert "0.0" not in str(student_nb)

0 comments on commit 5166317

Please sign in to comment.