Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDC Tools sprints 3 and 4 #1276

Merged
merged 33 commits into from
Feb 3, 2025
Merged
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9751f76
SDC Tools sprints 3 and 4
illicitonion Dec 24, 2024
e16d252
Add module success criteria
illicitonion Dec 30, 2024
a5c60c6
Simplify themes
illicitonion Dec 30, 2024
3a37c85
Update common-content/en/module/tools/comparing-javascript-and-python…
illicitonion Jan 22, 2025
706d7a2
Update common-content/en/module/tools/comparing-javascript-and-python…
illicitonion Jan 22, 2025
beffbc7
Update common-content/en/module/tools/comparing-javascript-and-python…
illicitonion Jan 22, 2025
9f63e52
Update common-content/en/module/tools/comparing-javascript-and-python…
illicitonion Jan 22, 2025
964da99
Update common-content/en/module/tools/comparing-javascript-and-python…
illicitonion Jan 22, 2025
6f09666
Update common-content/en/module/tools/comparing-javascript-and-python…
illicitonion Jan 22, 2025
165b2d6
Update common-content/en/module/tools/converted-program/index.md
illicitonion Jan 22, 2025
bd744cb
Update common-content/en/module/tools/converted-program/index.md
illicitonion Jan 22, 2025
d59e0ac
Update common-content/en/module/tools/converting-javascript-to-python…
illicitonion Jan 22, 2025
8670f27
Update common-content/en/module/tools/counting-words/index.md
illicitonion Jan 22, 2025
83b06ba
Update common-content/en/module/tools/first-nodejs-program/index.md
illicitonion Jan 22, 2025
ffbf68c
Update common-content/en/module/tools/first-nodejs-program/index.md
illicitonion Jan 22, 2025
246882f
Update common-content/en/module/tools/single-use-data-analysis/index.md
illicitonion Jan 22, 2025
8de321f
Update common-content/en/module/tools/using-python-dependencies/index.md
illicitonion Jan 22, 2025
5e78448
Update common-content/en/module/tools/using-python-dependencies/index.md
illicitonion Jan 22, 2025
9c2e465
Update common-content/en/module/tools/using-python-dependencies/index.md
illicitonion Jan 22, 2025
b3dca09
Tooltip convention
illicitonion Jan 23, 2025
1891137
Review feedback
illicitonion Jan 24, 2025
72b11df
Review feedback
illicitonion Jan 24, 2025
d6b8570
Fix typo
illicitonion Jan 24, 2025
c771e22
Feedback on converting exercise
illicitonion Jan 24, 2025
ee3ede4
Much of Sally's first round of feedback
illicitonion Jan 27, 2025
febb904
Most of Ali's review comments
illicitonion Jan 27, 2025
0baae4b
Clarify the fs.promises import
illicitonion Jan 30, 2025
3b3d403
Add prompt to think about why fs is async
illicitonion Jan 30, 2025
bc3246f
Add link to docs
illicitonion Jan 30, 2025
3dfc99e
Compare virtual environments to node_modules
illicitonion Jan 30, 2025
70abc83
Convert filter+lambda to an exploration exercise
illicitonion Jan 30, 2025
ab96743
Merge branch 'main' into sdc-tools-sprint3and4
SallyMcGrath Feb 2, 2025
4b8ddf7
Process tooltip
illicitonion Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Much of Sally's first round of feedback
  • Loading branch information
illicitonion committed Jan 27, 2025
commit ee3ede48e956395b4865992dab58ccd0fad583a7
43 changes: 35 additions & 8 deletions common-content/en/module/tools/first-nodejs-program/index.md
Original file line number Diff line number Diff line change
@@ -10,13 +10,13 @@ objectives = [
]
+++

Below we have a small NodeJS program. It is a bit like `wc`. It counts words in a file. Specifically, it counts words which contain a hyphen (`-`) character.
Below we have a small NodeJS program. It is a bit like `wc`. It counts words in a file which contain a hyphen (`-`) character.

It accepts one command line argument - the path of the file to read and count.
Our program accepts one command line argument - the path of the file to read and count.

Its output to stdout is just the number of words which contain a hyphen.
Our program's output to stdout is just the number of words which contain a hyphen.

It uses the same language (JavaScript) as we've written before, but uses some different APIs.
Our program uses the same language (JavaScript) as we've written before, but uses some different APIs.

```js
import process from "node:process";
@@ -56,9 +56,9 @@ We're importing another module.

The `fs` module is built into NodeJS for interacting with the filesystem.

This time, we're not importing the whole module. We are destructuring. The `node:fs` module exposes an object, and we are saying "import the `promises` property from the `fs` module, and bind it to the name `fs`".
This time, we're not importing the whole module. We are {{<tooltip text="destructuring" title="Destructuring">}}Destructuring is a form of variable assignment where we give variables values based on where we can find them structurally in another value. Examples:<br /><br />We can write `const [first, second] = [3, 1];` to assign `first = 3` and `second = 1`.<br /><br />We can write `const {name, age} = {name: "Amir", age: 34};` to assign `name = "Amir"` and `age = 34`.{{</tooltip>}}. The `node:fs` module exposes an object, and we are saying "import the `promises` property from the `fs` module, and bind it to the name `fs`".

It's the equivalent to us writing `import { promises } from "node:fs"; const fs = promises;`.
It is like writing `import { promises } from "node:fs"; const fs = promises;`.

We are doing this because many of the things in the `fs` module don't support `async`/`await`, but `fs` has a sub-module called `promises` where everything supports `async`/`await`. Because we want to use `async`/`await`, we will use that. But having to write `fs.promises.readFile` is a bit annoying, so instead we import `fs.promises` as if it was just named `fs`.

@@ -70,42 +70,69 @@ We're getting the `argv` array from the `process` module, and slicing it. We can

Again, `Array.slice` is exactly the same as we know from JavaScript, but `process.argv` is a new API we can use to get the array we need.

Play computer with the rest of the program - read each line, and explain what you think that line does. After you make your predictions, expand the explanations below and compare them to your predictions.

<details>
<summary>

```js
if (argv.length != 1) {
console.error(`Expected exactly 1 argument (a path) to be passed but got ${argv.length}.`);
process.exit(1);
}
```

</summary>
We always expect our program to be given exactly one argument. Here we check this using an `if` statement, just like we've seen before.

`console.error` writes a message to stderr (which is where error messages should go).

`process.exit` is a function which, when called, will stop our program running. Passing a non-zero number to it indicates that our program did not succeed.
`process.exit` is a function which, when called, will stop our program running. Passing a non-zero number to it indicates that our program did not succeed. We can read more about it in the official NodeJS documentation for the `process` module.

</details>

<details>
<summary>

```js
const path = argv[0];
```
</summary>

Giving a useful name to our argument.
</details>

<details>
<summary>

```js
const content = await fs.readFile(path, "utf-8");
```
</summary>

Reading the file at the path passed as an argument. We're using the `fs` module here from `node`, but everything else is just JavaScript - declaring a variable, using `await` because `fs.promises.readFile` is an `async` function, calling a function.
</details>

<details>
<summary>

```js
const wordsContainingHyphens = content.split(" ").filter((word) => word.indexOf("-") > -1).length;
```
</summary>

Just some regular JavaScript. Taking a string, splitting it into an array, filtering the array, searching strings to see if they contain characters, and getting the length of an array.
</details>

<details>
<summary>

```js
console.log(wordsContainingHyphens);
```
</summary>

`console.log` in a NodeJS environment logs to stdout, so this outputs our result to stdout.
</details>

{{<note type="Exercise">}}
Save the above program into a file. Run the file with `node`, and count how many words contain hyphens in a few different files.
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ objectives = [

To use a library, we need to fetch the code we're going to use. When using NodeJS, we use a tool called `npm` for this.

First we need a `package.json` file - this a file that `npm` will read to understand your project.
First we need a `package.json` file - this a file that `npm` will read to understand your project. This is the same as the `package.json` file you've seen when using `npm` in the past.

Make this `package.json` file in the same directory as your hyphen-counting program:

2 changes: 1 addition & 1 deletion common-content/en/module/tools/reading-a-file/index.md
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ with open(args.path, "r") as f:
content = f.read()
```

Comparing these shows some interesting differences, particularly around scope.
Comparing these shows some interesting differences, particularly around {{<tooltip text="scope" title="Scope">}}Scope is where a variable can be accessed from.{{</tooltip>}}.

### Scope

10 changes: 10 additions & 0 deletions common-content/en/module/tools/single-use-data-analysis/index.md
Original file line number Diff line number Diff line change
@@ -20,6 +20,16 @@ When we want to answer some question, sometimes it's useful to write a program w

It's not always obvious whether it's easier to try to use tools that already exist, or to write our own.

> [!TIP]
>
> This is like in real life! Imagine if you had two differently sized bottles, and wanted to pour all of the liquid from one to the other without spilling any.
>
> You can imagine making the perfect tube that has exactly the right size connector at each end to connect to the bottles.
>
> Or maybe you already have a funnel that's about the right size - not perfect, but close enough, and you can probably use.
>
> But if you got a really wide or really narrow bottle, maybe that funnel wouldn't be good enough and you would need to make a custom solution.

Sometimes the format of our data makes it easier or harder to use existing tools.

Let's look at some sample data:
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ In this example, we'll call it `cow.py`.
import cowsay
```

If we try to run `python3 cow.py`, we'll get an error:
Run `python3 cow.py`. It will trigger this error:

```
ModuleNotFoundError: No module named 'cowsay'
@@ -54,7 +54,7 @@ When we activate a virtual environment, its name gets shown before our terminal

Run `python3 cow.py`. We don't get an error because we installed `cowsay` into our active virtual environment.

Open a new terminal and run `python3 cow.py`. An error will occur because we haven't activated a virtual environment.
Open a new terminal and run `python3 cow.py`. You will get an error again! This is because we haven't activated a virtual environment.

Run `. .venv/bin/activate` and then `python3 cow.py`. It will start working again.

8 changes: 7 additions & 1 deletion common-content/en/module/tools/virtual-environments/index.md
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ objectives=[
]
+++

We often need to use libraries in Python.
We often use libraries in Python.

Python handles dependencies differently from JavaScript, but it has similarities.

@@ -23,6 +23,12 @@ To install the dependencies, we need to make something called a virtual environm

First we need to _create_ the virtual environment. We do this by running `python3 -m venv .venv`. This will create a virtual environment in a directory named `.venv`. We could actually create it anywhere, e.g. we could run `python3 -m venv /tmp/python_modules` to create it in a directory named `/tmp/python_modules`. We tend to just use a directory called `.venv` at the root of our project.

> [!NOTE]
>
> This is another example of a convention - you could name your virtual environment anything, but if we all agree to call it `.venv` then we all know what this directory is when we see it.
>
> It also means we can write scripts, or `.gitignore` file entries assuming that's where the virtual environment will be.

Next we need to _activate_ the virtual environment. We do this by running `. .venv/bin/activate` (yes, the command we're running is `.` with a path as an argument - the `.` is important). This will only activate the virtual environment for the terminal window we're in - if you're using more than one terminal window, you'll need to activate it in each of them.

Finally we need to install our dependencies into the virtual environment. We do this by running `pip install -r requirements.txt`. This is saying "Please install all of the dependencies listed in `requirements.txt` into the currently active virtual environment".
Loading