diff --git a/.gitignore b/.gitignore index 3d63e323d..77b7db019 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ venv/ .venv/ .*history .tool-versions +.vscode/ diff --git a/_episodes/10-section1-intro.md b/_episodes/10-section1-intro.md index 9bd313ed9..e3efdddee 100644 --- a/_episodes/10-section1-intro.md +++ b/_episodes/10-section1-intro.md @@ -15,7 +15,8 @@ you need to use a number of different tools." to decide which tool is right for you, which may be a matter of personal preference or what the team or community you belong to is using." --- -The first section of the course is dedicated to setting up your environment for collaborative software development. +The first section of the course is dedicated to setting up your environment for collaborative software development +and introducing the project that we will be working on throughout the course. In order to build working (research) software efficiently and to do it in collaboration with others rather than in isolation, you will have to get comfortable with using a number of different tools interchangeably @@ -86,6 +87,17 @@ issue management, code review, code testing/Continuous Integration, and collaborative development. +An important concept in collaborative development is version control workflows +(i.e. how to effectively use version control on a project with others). + +### Python Coding Style +Most programming languages will have associated standards and conventions for how the source code +should be formatted and styled. +Although this sounds pedantic, +it is important for maintaining the consistency and readability of code across a project. +Therefore, one should be aware of these guidelines +and adhere to whatever the project you are working on has specified. +In Python, we will be looking at a convention called PEP8. Let's get started with setting up our software development environment! diff --git a/_episodes/13-ides.md b/_episodes/13-ides.md index 83a0537b8..d9c6b0c26 100644 --- a/_episodes/13-ides.md +++ b/_episodes/13-ides.md @@ -492,7 +492,8 @@ including: inline documentation ([*docstrings*](../15-coding-conventions/index.html#documentation-strings-aka-docstrings) for any symbol created in accordance with [PEP-257](https://peps.python.org/pep-0257/) - Parameter Info - - the names of parameters in method and function calls + the names and expected types of parameters in method and function calls. + Use this when cursor is on the argument of a function call. - Type Info - type of an expression diff --git a/_episodes/14-collaboration-using-git.md b/_episodes/14-collaboration-using-git.md index a7d354704..642ed2cba 100644 --- a/_episodes/14-collaboration-using-git.md +++ b/_episodes/14-collaboration-using-git.md @@ -105,9 +105,9 @@ sequenceDiagram Staging Area->>+Local Repository: git commit Local Repository->>+Remote Repository: git push Remote Repository->>+Local Repository: git fetch - Local Repository->>+Staging Area:git checkout + Local Repository->>+Staging Area:git checkout / git switch Local Repository->>+Staging Area:git merge - Remote Repository->>+Working Directory: git pull (shortcut for git fetch followed by git checkout/merge) + Remote Repository->>+Working Directory: git pull (shortcut for git fetch followed by git merge/rebase) --> \n", + "- Post-exercise comment: there will probably be the temptation to inherit from the builtin `list` class when implementing `Library`. \n", + " - Subclassing from builtin types is generally a bad idea for a host of complex reasons. \n", + " - The most easily comprehensible is that by inheriting a builtin type, you inherit a lot of behaviour that you might not intend to have for your new class. \n", + " - On the flip side, it is also quite restrictive. Is a Library really just a list? We might decide to add other functionality, like opening hours, later on. How does that data fit into the definition of a list? Well, it doesn't. \n", + " - If you really want to inherit from something here, then the better approach is to use the `collections.abc` (abstract base class) module in Python and pick something from there that gets you closer to your desired functionality.\n", + "- A note about inheritance vs composition is probably worthwhile\n", + " - Inheritance tends to be quite abused and IMO should not be used as often as it is; it leads to very complex hierarchies of classes that make it difficult to determine where something is actually defined\n", + " - Composition tends to lead to better outcomes in my experience\n", + "- If anyone asks about Doctor OO implementation, some additional feature ideas\n", + " - using the `dataclass` decorator offers a lot of advantages for these classes\n", + " - adding an age to patients could be helpful (fairly simple)\n", + " - a date range (or start date) for a patient's observations\n", + " - a Study class that is a list of Doctors (perhaps too much)\n", + " - a CSV reader of patient data (and preferably groups of patients data)" + ] + }, + { + "cell_type": "markdown", + "id": "9830a50e", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Class vs Static Methods\n", + "\n", + "One common use case for class methods is to create alternate _constructors_ for a class:\n", + "\n", + "```python\n", + "class Circle:\n", + " def __init__(self, radius):\n", + " self.radius = radius\n", + "\n", + " @classmethod\n", + " def from_diameter(cls, diameter):\n", + " return cls(radius=diameter / 2)\n", + "```\n", + "\n", + "Then, we can create `Circle` objects using either the radius or diameter:\n", + "\n", + "```python\n", + "circle_1 = Circle(radius=1)\n", + "circle_2 = Circle.from_diameter(radius=2)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "bcff090f", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## β 5 Minute Break β" + ] + }, + { + "cell_type": "markdown", + "id": "4f5c1134", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Functional Programming" + ] + }, + { + "cell_type": "markdown", + "id": "15ea1a76", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- We have undoubtedly all written functions during our time programming, but this doesn't mean we have done \"functional\" style programming, so let's look at what that involves\n", + "- The functional style will probably seem quite foreign, and it might not be obvious why it is useful initially\n", + " - This is just a taster, but I encourage you to stick with it and read further into the functional style if you are interested\n" + ] + }, + { + "cell_type": "markdown", + "id": "54d010c3", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Breakout: Start from the Top \"What is a Function?\"\n", + "\n", + "Start from the top of this page and read through, completing any exercises along the way. Like with the Book/Library exercise previously, it is recommended to put your new code into separate files/modules." + ] + }, + { + "cell_type": "markdown", + "id": "19fab4ad", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- Send learners into breakout rooms for about 40 minutes\n", + "- Post-exercise comments\n", + "- Invariably, the question will come up of when to use functional vs OO programming, and are the two mutually exclusive (i.e. do you have to use one or the other)\n", + " - as mentioned previously, it is unlikely you will do purely OO or functional programming, especially if you are using Python\n", + " - the two are certainly not mutually exclusive\n", + "- Testability is a huge point that should be emphasised. Even if you aren't following a strict functional style, it is likely that incorporating parts of this style into your code will make it more testable.\n", + "- Ask if anyone was able to get the `Pool.map` implementation to actually be quicker\n", + " - this could be a nice opportunity to talk about profiling, but likely not enough time\n", + "- Next slide for comment about the `reduce` function" + ] + }, + { + "cell_type": "markdown", + "id": "132d07f6", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "#### The `reduce` function\n", + "\n", + "A `for` loop under the hood: https://github.com/python/cpython/blob/3.10/Lib/functools.py#L237\n", + "\n", + "```python\n", + "def reduce(function, sequence):\n", + " # some argument checking\n", + " # ...\n", + " it = iterator(sequence)\n", + " for element in it:\n", + " value = function(value, element)\n", + " return value\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "id": "a2ce5283", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- In Python's case, there is very literally a procedural underpinning to some of its functional attributes\n", + " - but this is not the case for all languages" + ] + }, + { + "cell_type": "markdown", + "id": "6ebea73f", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Alternate Sum of Squares\n", + "\n", + "```python\n", + "from functools import reduce\n", + "\n", + "\n", + "def sum_of_squares(sequence):\n", + " squares = [x * x for x in sequence]\n", + " return reduce(lambda a, b: a + b, squares)\n", + "\n", + "\n", + "def to_integer(sequence):\n", + " return [int(x) for x in sequence]\n", + "\n", + "\n", + "def remove_comments(sequence):\n", + " return [x for x in sequence if x[0] != \"#\"]\n", + "\n", + "sequence = ['#100', '1', '2', '4']\n", + "sum_of_squares(to_integer(remove_comments(sequence)))\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "0aa217d2", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- This is closer to what actual purely function languages do: they chain together a sequence of function calls\n", + "- And example of what this looks like in another language can be seen here: https://bielsnohr.github.io/2023/02/18/elm-advent-of-code-2022.html#array-handling" + ] + }, + { + "cell_type": "markdown", + "id": "9cc57670", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## β 10 Minute Break β" + ] + }, + { + "cell_type": "markdown", + "id": "9876d27e", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Software Design\n", + "\n", + "> **Software design** is the process by which an agent creates a specification of a software artifact intended to accomplish goals, using a set of primitive components and subject to constraints. Software design may refer to either \"all the activity involved in conceptualizing, framing, implementing, commissioning, and ultimately modifying complex systems\" or \"the activity following requirements specification and before programming, as ... [in] a stylized software engineering process.\" " + ] + }, + { + "cell_type": "markdown", + "id": "0b284f23", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "- Part of the software development process\n", + "- Be careful: the _process_ of designing software **VERSUS** _a design_ of some software (sometimes called a _design model_)" + ] + }, + { + "cell_type": "markdown", + "id": "57442f6f", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- We don't want to get bogged down in definitions, but it is good to know the difference between software design and architecture\n", + "- Read definition of software design and subsequent points" + ] + }, + { + "cell_type": "markdown", + "id": "2bff7e4f", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "> **Software architecture** refers to the fundamental structures of a software system and the discipline of creating such structures and systems. Each structure comprises software elements, relations among them, and properties of both elements and relations. The architecture of a software system is a metaphor, analogous to the architecture of a building. It functions as a blueprint for the system and the developing project, laying out the tasks necessary to be executed by the design teams.\n", + "\n", + "- This can be thought of as a particular sub-category of the design model that becomes more important the larger your software project gets" + ] + }, + { + "cell_type": "markdown", + "id": "c73ae51a", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- Software architecture tends to be a much higher level view with little detail about specific implementation, focussing rather on abstrated components and how they will interact\n", + " - a software degin model will be much lower level and refer to particular modules and classes" + ] + }, + { + "cell_type": "markdown", + "id": "350f7cb4", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Breakout: Start from the Top\n", + "\n", + "Start reading from the top of this page all the way to the end. Complete exercises as you go.\n", + "\n", + "For the **π Types of Software** exercise, please take 5 minutes at the beginning of the session to write down answers to the questions in the shared document, and share your answers with your group." + ] + }, + { + "cell_type": "markdown", + "id": "522a8f37", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- Send learners into breakout rooms for about 35 minutes" + ] + }, + { + "cell_type": "markdown", + "id": "29903c07", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Some More Thoughts about Software Design\n", + "\n", + "- Requirements collections is the important prerequisite since they determine the design constraints; the questions you ask to collect requirements are similar to the exercise **π Types of Software**, but there can be many more\n", + "- Draw it out!\n", + " - Use diagrams and schematics to try and visualise what a software programme/system will look like\n", + " - It doesn't have to be fancy; just having something is far better than nothing\n", + " - Seeing the connections between software components can usually help you further refine the design\n", + "- Design it twice\n", + " - The more designs you consider, the higher the chance you will move towards a better one" + ] + }, + { + "cell_type": "markdown", + "id": "869f0e23", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- Also, emphasise the point from the course content: it is good to not get too caught up in the details of whether you are implementing an MVC architecture\n", + " - this is probably not an architecture that most would see or implement in research software\n", + " - but the point of separating the \"model\" from the \"view\" (i.e. plotting) is definitely something worthwhile" + ] + }, + { + "cell_type": "markdown", + "id": "4191fbd3", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Persistence" + ] + }, + { + "cell_type": "markdown", + "id": "f849ba0c", + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "source": [ + "- not much intro needed, other than the fact that reading and writing data is an important step for your code (even if not the most exciting)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "da9f8129", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "outputs": [], + "source": [ + "import json\n", + "\n", + "patient = {\"name\": \"Alice\", \"observations\": [1, 6, 8, 0, 5]}\n", + "with open(\"patient_serialized.json\", \"w\") as fp:\n", + " json.dump(patient, fp)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3375de80", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\"name\": \"Alice\", \"observations\": [1, 6, 8, 0, 5]}" + ] + } + ], + "source": [ + "! cat patient_serialized.json " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "035dda90", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'name': 'Alice', 'observations': [1, 6, 8, 0, 5]}\n" + ] + } + ], + "source": [ + "with open(\"patient_serialized.json\", \"r\") as fp:\n", + " patient_deserialized = json.load(fp)\n", + "print(patient_deserialized)" + ] + }, + { + "cell_type": "markdown", + "id": "9df64860", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "### Breakout: Start from the Top\n", + "\n", + "Start reading from the top of this page all the way to the end. Complete exercises as you go." + ] + }, + { + "cell_type": "markdown", + "id": "91d1b9a1", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## π End of Section 3 π\n", + "\n", + "Please fill out the end-of-section survey!" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.6" + }, + "rise": { + "theme": "solarized" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/slides/section_4_managing_software.ipynb b/slides/section_4_managing_software.ipynb new file mode 100644 index 000000000..a947f5fc1 --- /dev/null +++ b/slides/section_4_managing_software.ipynb @@ -0,0 +1,201 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8f4f88a1", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Section 4: Improving and Managing Software Over its Lifetime\n", + "\n", + "\n", + "\n", + "