Skip to content

Commit a06d407

Browse files
authored
Merge pull request #362 from carpentries-incubator/develop
Develop
2 parents b84333e + 40b2dc1 commit a06d407

14 files changed

Lines changed: 146 additions & 72 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ This indicates that the maintainers will welcome pull requests fixing such issue
6969

7070
Current maintainers of this lesson (in alphabetical order) are:
7171

72-
* [Matthew Bluteau][matthew-bluteau] - Lead Maintainer for the period 1 May 2024 - 30 April 2024
72+
* [Matthew Bluteau][matthew-bluteau] - Lead Maintainer for the period 1 May 2024 - 31 October 2024
7373
* [Steve Crouch][steve-crouch]
7474
* [Kamilla Kopec-Harding][kamilla-kopec-harding]
7575
* [Doug Lowe][doug-lowe]

_episodes/23-continuous-integration-automated-testing.md

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -360,29 +360,40 @@ to refer to the values from the matrix.
360360
So, our `.github/workflows/main.yml` should look like the following:
361361

362362
~~~
363-
...
363+
# Same key-value pairs as in "Defining Our Workflow" section
364+
name: CI
365+
on: push
366+
jobs:
367+
build:
368+
369+
# Here we add the matrices definition:
364370
strategy:
365371
matrix:
366-
os: [ubuntu-latest, macos-latest, windows-latest]
372+
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
367373
python-version: ["3.10", "3.11"]
368374
375+
# Here we add the reference to the os matrix values
369376
runs-on: {% raw %}${{ matrix.os }}{% endraw %}
370377
371-
...
372-
373-
# a job is a seq of steps
378+
# Same key-value pairs as in "Defining Our Workflow" section
374379
steps:
375380
376-
# Next we need to checkout out repository, and set up Python
377-
# A 'name' is just an optional label shown in the log - helpful to clarify progress - and can be anything
378381
- name: Checkout repository
379382
uses: actions/checkout@v4
380383
381384
- name: Set up Python
382385
uses: actions/setup-python@v4
383386
with:
387+
# Here we add the reference to the python-version matrix values
384388
python-version: {% raw %}${{ matrix.python-version }}{% endraw %}
385-
...
389+
# Same steps as in "Defining Our Workflow" section
390+
- name: Install Python dependencies
391+
run: |
392+
python3 -m pip install --upgrade pip
393+
python3 -m pip install -r requirements.txt
394+
- name: Test with PyTest
395+
run: |
396+
python3 -m pytest --cov=catchment.models tests/test_models.py
386397
~~~
387398
{: .language-yaml}
388399

@@ -410,4 +421,25 @@ which potentially saves us a lot of time waiting for testing results.
410421
Overall, this approach allows us to massively scale our automated testing
411422
across platforms we wish to test.
412423

424+
> ## Failed CI Builds
425+
> A CI build can fail when, e.g. a used Python package no longer supports a particular version of
426+
> Python indicated in a GitHub Actions CI build matrix. In this case, the solution is either to
427+
> upgrade the Python version in the build matrix (when possible) or downgrade the package version (and not use the latest one like we have been doing in this course).
428+
>
429+
> Also note that, if one job fails in the build for any reason - all subsequent jobs will get cancelled because of the default behavior of
430+
> GitHub Actions. From [GitHub's documentation](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#handling-failures):
431+
>
432+
> >*"GitHub will cancel all in-progress and queued jobs in the matrix if any job in the matrix fails."*
433+
>
434+
> This behaviour can be controlled by changing the value of the `fail-fast` property:
435+
> ~~~
436+
> ...
437+
> strategy:
438+
> fail-fast: false
439+
> matrix:
440+
> ...
441+
> ~~~
442+
{: .language-yaml}
443+
{: .callout}
444+
413445
{% include links.md %}

_episodes/24-diagnosing-issues-improving-robustness.md

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -120,37 +120,49 @@ to check that the normalisation function is correct for some test data.
120120
])
121121
def test_patient_normalise(test, expected):
122122
"""Test normalisation works for arrays of one and positive integers.
123-
Assumption that test accuracy of two decimal places is sufficient."""
123+
Test with a relative and absolute tolerance of 0.01."""
124124
from inflammation.models import patient_normalise
125-
npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
125+
result = patient_normalise(np.array(test))
126+
npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
126127
~~~
127128
{: .language-python}
128129

129-
Note that we are using the `assert_almost_equal()` Numpy testing function
130+
Note that we are using the `assert_allclose()` Numpy testing function
130131
instead of `assert_array_equal()`,
131-
since it allows us to test against values that are *almost* equal.
132+
since it allows us to test against values that are **close** to each other.
132133
This is very useful when we have numbers with arbitrary decimal places
133134
and are only concerned with a certain degree of precision,
134-
like the test case above,
135-
where we make the assumption that a test accuracy of two decimal places is sufficient.
135+
like the test case above.
136+
137+
> ## Relative and absolute tolerance
138+
> **Relative tolerance** in unit testing means that the acceptable difference between the expected and actual results
139+
> depends on the size of the expected result itself. So, if your expected result is 100,
140+
> a relative tolerance of 0.1 (or 10%) means the actual result can be anywhere from 90 to 110 and still be considered correct.
141+
>
142+
> **Absolute tolerance**, on the other hand,
143+
> sets a fixed allowable difference regardless of the magnitude of the expected result.
144+
> For example, if you set an absolute tolerance of 5,
145+
> it means the actual result can be within 5 units of the expected result,
146+
> regardless of whether the expected result is 10 or 1000.
147+
{: .callout}
136148

137149
Run the tests again using `python -m pytest tests/test_models.py`
138150
and you will note that the new test is failing,
139151
with an error message that does not give many clues as to what went wrong.
140152

141153
~~~
142-
E AssertionError:
143-
E Arrays are not almost equal to 2 decimals
154+
E AssertionError:
155+
E Not equal to tolerance rtol=0.01, atol=0.01
144156
E
145-
E Mismatched elements: 6 / 9 (66.7%)
146-
E Max absolute difference: 0.57142857
147-
E Max relative difference: 1.345
148-
E x: array([[0.14, 0.29, 0.43],
149-
E [0.5 , 0.62, 0.75],
150-
E [0.78, 0.89, 1. ]])
151-
E y: array([[0.33, 0.67, 1. ],
152-
E [0.67, 0.83, 1. ],
153-
E [0.78, 0.89, 1. ]])
157+
E Mismatched elements: 6 / 9 (66.7%)
158+
E Max absolute difference: 0.57142857
159+
E Max relative difference: 0.57356077
160+
E x: array([[0.142857, 0.285714, 0.428571],
161+
E [0.5 , 0.625 , 0.75 ],
162+
E [0.777778, 0.888889, 1. ]])
163+
E y: array([[0.33, 0.67, 1. ],
164+
E [0.67, 0.83, 1. ],
165+
E [0.78, 0.89, 1. ]])
154166
155167
tests/test_models.py:53: AssertionError
156168
~~~
@@ -407,7 +419,7 @@ due to the division by zero as we predicted.
407419

408420
~~~
409421
E AssertionError:
410-
E Arrays are not almost equal to 2 decimals
422+
E Not equal to tolerance rtol=0.01, atol=0.01
411423
E
412424
E x and y nan location mismatch:
413425
E x: array([[nan, nan, nan],
@@ -489,7 +501,8 @@ def patient_normalise(data):
489501
> > def test_patient_normalise(test, expected):
490502
> > """Test normalisation works for arrays of one and positive integers."""
491503
> > from inflammation.models import patient_normalise
492-
> > npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
504+
> > result = patient_normalise(np.array(test))
505+
> > npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
493506
> > ...
494507
> > ~~~
495508
> > {: .language-python}
@@ -569,11 +582,14 @@ but of an inappropriate value.
569582
def test_patient_normalise(test, expected, expect_raises):
570583
"""Test normalisation works for arrays of one and positive integers."""
571584
from inflammation.models import patient_normalise
585+
572586
if expect_raises is not None:
573587
with pytest.raises(expect_raises):
574-
npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
588+
result = patient_normalise(np.array(test))
589+
npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
575590
else:
576-
npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
591+
result = patient_normalise(np.array(test))
592+
npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
577593
~~~
578594
{: .language-python}
579595
@@ -651,14 +667,17 @@ Be sure to commit your changes so far and push them to GitHub.
651667
> > test = np.array(test)
652668
> > if expect_raises is not None:
653669
> > with pytest.raises(expect_raises):
654-
> > npt.assert_almost_equal(patient_normalise(test), np.array(expected), decimal=2)
670+
> > result = patient_normalise(test)
671+
> > npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
672+
> >
655673
> > else:
656-
> > npt.assert_almost_equal(patient_normalise(test), np.array(expected), decimal=2)
674+
> > result = patient_normalise(test)
675+
> > npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
657676
> > ...
658677
> > ~~~
659678
> >
660679
> > Note the conversion from `list` to `np.array` has been moved
661-
> > out of the call to `npt.assert_almost_equal()` within the test function,
680+
> > out of the call to `npt.assert_allclose()` within the test function,
662681
> > and is now only applied to list items (rather than all items).
663682
> > This allows for greater flexibility with our test inputs,
664683
> > since this wouldn't work in the test case that uses a string.

_episodes/43-software-release.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ allowing us to distinguish between runtime and development dependencies.
190190

191191
~~~
192192
$ poetry add matplotlib numpy
193-
$ poetry add --dev pylint
193+
$ poetry add --group dev pylint
194194
$ poetry install
195195
~~~
196196
{: .language-bash}

_episodes/51-managing-software.md

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ GitHub provides **Issues** -
4747
a framework for managing bug reports, feature requests, and lists of future work.
4848

4949
Go back to the home page for your `python-intermediate-inflammation` repository in GitHub,
50-
and click on the **Issue** tab.
50+
and click on the `Issues` tab.
5151
You should see a page listing the open issues on your repository -
52-
currently there should be none.
52+
currently there should be none.
53+
If you do not see the `Issues` tab, you must first enable it in the settings of your repository:
54+
go to the `Settings` tab, scroll down to the `Features` section and activate the checkmark on `Issues`.
5355

5456
![List of project issues in GitHub](../fig/github-issue-list.png){: .image-with-shadow width="1000px"}
5557

@@ -269,7 +271,7 @@ representation of tasks and may not be as suitable for higher-level project mana
269271
prioritising tasks for future development, planning sprints and releases. Luckily,
270272
GitHub provides two project management tools for this purpose - **Projects** and **Milestones**.
271273

272-
Both Projects and Milestones provide [agile development and project management systems](https://www.atlassian.com/agile)
274+
Both GitHub Projects and Milestones provide [agile development and project management systems](https://www.atlassian.com/agile)
273275
and ways of organising issues into smaller "sub-projects" (i.e.
274276
smaller than the "project" represented by the whole repository).
275277
Projects provide a way of visualising and organising work which is not time-bound and is on a higher level (e.g. more suitable for
@@ -287,7 +289,7 @@ for now, we will have a brief look at Projects.
287289

288290
### Projects
289291

290-
A Project uses a "project board" consisting of columns and cards to keep track of tasks
292+
A GitHub Project uses a "project board" consisting of columns and cards to keep track of tasks
291293
(although GitHub now also provides a table view over a project's tasks).
292294
You break down your project into smaller sub-projects,
293295
which in turn are split into tasks which you write on cards,
@@ -299,7 +301,14 @@ such as issues and pull requests -
299301
cards can be added to track the progress of such tasks
300302
and automatically moved between columns based on their progress or status.
301303

302-
> ## Project are a Cross-Repository Management Tool
304+
GitHub projects are "an adaptable, flexible tool for planning and tracking work on GitHub" -
305+
they now provide interchangeable spreadsheet, task-board, or roadmap views of your project
306+
that integrates with your issues and pull requests on GitHub to help you
307+
plan and track your work effectively.
308+
We recommend you to have a look at [GitHub’s documentation on Projects](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects)
309+
and see if they are suitable for your software development workflow.
310+
311+
> ## GitHub Projects are a Cross-Repository Management Tool
303312
> [Project in GitHub](https://docs.github.com/en/issues/planning-and-tracking-with-projects/learning-about-projects/about-projects)
304313
> are created on a user or organisation level,
305314
> i.e. they can span all repositories owned by a user or organisation in GitHub
@@ -308,32 +317,32 @@ and automatically moved between columns based on their progress or status.
308317
> to help you plan and track your team's work effectively.
309318
{: .callout}
310319

311-
Let's create a Project in GitHub to plan the first release of our code.
320+
Let's have a quick look at how Projects are created in GitHub - we will not use them much in
321+
this course but it is good to be aware of how to make use of them when suitable.
312322

313323
1. From your GitHub account's home page (not your repository's home page!),
314324
select the "Projects" tab, then click the `New project` button on the right.
315325

316-
![Adding a new project board in GitHub](../fig/github-new-project.png){: .image-with-shadow width="1000px"}
317-
318-
2. In the "Select a template" pop-up window, select "Board" -
319-
this will give you a classic "cards on a board" view of the project.
320-
An alternative is the "Table" view,
321-
which presents a spreadsheet-like and slightly more condensed view of a project.
322-
323-
![Selecting a project board template in GitHub](../fig/github-board-template.png){: .image-with-shadow width="600px"}
324-
325-
3. GitHub will create an unnamed project board for you.
326-
You should populate the name and the description of the project from the project's Settings,
327-
which can be found by clicking the `...` button in the top right corner of the board.
328-
329-
![Project board setting in GitHub](../fig/github-project-settings.png){: .image-with-shadow width="800px"}
330-
331-
4. We can, for example, use "Inflammation project - release v0.1"
332-
and "Tasks for the v0.1 release of the inflammation project"
333-
for the name and description of our project, respectively.
334-
Or you can use anything that suits your project.
335-
336-
![Naming a project in GitHub](../fig/github-name-project.png){: .image-with-shadow width="800px"}
326+
![Adding a new project board in GitHub](../fig/github-new-project.png){: .image-with-shadow width="800px"}
327+
328+
2. In the "Create project" pop-up window, you can either start from one of the featured existing
329+
project templates or create your project from scratch using one of the three standard project
330+
types/views that you customise yourself:
331+
- Table - a spreadsheet-style table to filter, sort and group your issues and pull requests.
332+
- Board - a "cards on a board" view of the project, with issues and pull requests being
333+
spread across customizable columns as cards on kanban board
334+
- Roadmap - suitable for a high-level visualisation of your project over time.
335+
![Selecting a project board template in GitHub](../fig/github-project-template.png){: .image-with-shadow width="800px"}
336+
Regardless of which project type/view you select, you can easily switch to a different
337+
project layout later on.
338+
339+
3. For example, select the "Board" type for the project, fill in the name of your project
340+
(e.g. "Inflammation project - release v0.1"), and select `Create project`.
341+
4. After it is created, you should also populate the description of the project from the project's Settings,
342+
which can be found by clicking the `...` button in the top right corner of the project.
343+
![Project board setting in GitHub](../fig/github-project-settings.png){: .image-with-shadow width="800px"}
344+
![Adding project description and metadata in GitHub](../fig/github-project-description.png){: .image-with-shadow width="800px"}
345+
After adding a description, select `Save`.
337346

338347
5. GitHub's default card board template contains
339348
the following three columns with pretty self-explanatory names:
@@ -349,6 +358,8 @@ Let's create a Project in GitHub to plan the first release of our code.
349358
if you have tasks that get held up by waiting on other people
350359
(e.g. to respond to your questions)
351360
then moving them to a separate column makes their current state clearer.
361+
Another way to organise your table is to have a column for each quarter of the year -
362+
it is up to you to decide how you want to view your project's activities.
352363

353364
To add a new column,
354365
press the `+` button on the right;
@@ -375,7 +386,7 @@ Let's create a Project in GitHub to plan the first release of our code.
375386
or write more detailed comments
376387
(for that, use the `Convert to issue` option from the `...` menu on the card itself).
377388

378-
![Coverting a task to issue](../fig/github-convert-task-to-issue.png){: .image-with-shadow width="800px"}
389+
![Converting a task to issue](../fig/github-convert-task-to-issue.png){: .image-with-shadow width="800px"}
379390

380391
7. In addition to creating new tasks as notes and converting them to issues -
381392
you can add an existing issue or pull request (from any repository visible to you)
@@ -386,6 +397,10 @@ and pressing the `Enter` key.
386397
Issues and pull requests on cards will automatically be moved to the `Done` column for you
387398
when you close the issue or merge the pull request -
388399
which is very convenient and can save you some project management time.
400+
9. Finally, you can change the way you view your project by adding another view.
401+
For example, we can add a Table view to our Board view by clicking the `New button`
402+
and selecting it from the drop down menu.
403+
![Add another project view](../fig/github-project-add-view.png){: .image-with-shadow width="800px"}
389404

390405
> ## Exercise: Working With Projects
391406
> Spend a few minutes planning what you want to do with your project as a bigger chunk of work

0 commit comments

Comments
 (0)