From 4350f484da9132cb8f7f6b4e256f8678c0959688 Mon Sep 17 00:00:00 2001 From: Abubakar Meigag <113248042+Abubakar-Meigag@users.noreply.github.com> Date: Wed, 11 Mar 2026 17:31:33 +0000 Subject: [PATCH 01/15] Fix expected output in sed exercise instructions (#353) Fix expected output: change 'with' to 'wIth' in sed exercise instructions --- individual-shell-tools/sed/script-01.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/individual-shell-tools/sed/script-01.sh b/individual-shell-tools/sed/script-01.sh index 3eba6fa..d592970 100755 --- a/individual-shell-tools/sed/script-01.sh +++ b/individual-shell-tools/sed/script-01.sh @@ -4,4 +4,4 @@ set -euo pipefail # TODO: Write a command to output input.txt with all occurrences of the letter `i` replaced with `I`. # The output should contain 11 lines. -# The first line of the output should be: "ThIs Is a sample fIle for experImentIng with sed.". +# The first line of the output should be: "ThIs Is a sample fIle for experImentIng wIth sed.". From 743e4911fb9de80d1c5934bdc857738d9f60ed7e Mon Sep 17 00:00:00 2001 From: LonMcGregor <3817332+LonMcGregor@users.noreply.github.com> Date: Mon, 11 May 2026 10:28:21 +0100 Subject: [PATCH 02/15] Automate testing of initial sprints in tools module (#472) * Expected output now in expected folder * Github action which runs after validation, when Needs Review * Script that can be run to test locally before push * Split number sys qs into automatable/manualy checkable --------- Co-authored-by: l --- .github/workflows/validate-pr-metadata.yml | 57 +++++++- .gitignore | 1 + .../individual-shell-tools/awk/script-01.sh | 6 + .../individual-shell-tools/awk/script-02.sh | 6 + .../individual-shell-tools/awk/script-03.sh | 6 + .../individual-shell-tools/awk/script-04.sh | 3 + .../individual-shell-tools/awk/script-05.sh | 6 + .../awk/script-06-stretch.sh | 1 + .../awk/script-07-stretch.sh | 6 + .../individual-shell-tools/cat/script-01.sh | 1 + .../individual-shell-tools/cat/script-02.sh | 5 + .../individual-shell-tools/cat/script-03.sh | 3 + .../cat/script-04-stretch.sh | 5 + .../individual-shell-tools/grep/script-01.sh | 6 + .../individual-shell-tools/grep/script-02.sh | 9 ++ .../individual-shell-tools/grep/script-03.sh | 1 + .../individual-shell-tools/grep/script-04.sh | 10 ++ .../individual-shell-tools/grep/script-05.sh | 5 + .../individual-shell-tools/grep/script-06.sh | 2 + .../individual-shell-tools/grep/script-07.sh | 3 + expect/individual-shell-tools/ls/script-01.sh | 5 + expect/individual-shell-tools/ls/script-02.sh | 3 + expect/individual-shell-tools/ls/script-03.sh | 11 ++ expect/individual-shell-tools/ls/script-04.sh | 8 ++ .../individual-shell-tools/sed/script-01.sh | 11 ++ .../individual-shell-tools/sed/script-02.sh | 11 ++ .../individual-shell-tools/sed/script-03.sh | 6 + .../individual-shell-tools/sed/script-04.sh | 11 ++ .../individual-shell-tools/sed/script-05.sh | 11 ++ .../individual-shell-tools/sed/script-06.sh | 11 ++ expect/individual-shell-tools/wc/script-01.sh | 1 + expect/individual-shell-tools/wc/script-02.sh | 1 + expect/individual-shell-tools/wc/script-03.sh | 4 + expect/jq/script-01.sh | 1 + expect/jq/script-02.sh | 1 + expect/jq/script-03.sh | 2 + expect/jq/script-04.sh | 6 + expect/jq/script-05.sh | 6 + expect/jq/script-06.sh | 6 + expect/jq/script-07.sh | 6 + expect/jq/script-08.sh | 6 + expect/jq/script-09.sh | 6 + expect/jq/script-10.sh | 1 + expect/jq/script-11.sh | 1 + expect/number-systems/Part-1.json | 1 + expect/shell-pipelines/ls-grep/script-01.sh | 11 ++ expect/shell-pipelines/ls-grep/script-02.sh | 10 ++ expect/shell-pipelines/ls-grep/script-03.sh | 7 + expect/shell-pipelines/ls-grep/script-04.sh | 1 + .../sort-uniq-head-tail/script-01.sh | 6 + .../sort-uniq-head-tail/script-02.sh | 6 + .../sort-uniq-head-tail/script-03.sh | 3 + .../sort-uniq-head-tail/script-04.sh | 1 + .../sort-uniq-head-tail/script-05.sh | 6 + .../sort-uniq-head-tail/script-06.sh | 2 + .../sort-uniq-head-tail/script-07.sh | 2 + expect/shell-pipelines/tr/script-01.sh | 11 ++ expect/shell-pipelines/tr/script-02.sh | 11 ++ number-systems/Part-1.md | 54 +++++++ number-systems/Part-2.md | 22 +++ number-systems/README.md | 65 --------- test-sdc.sh | 132 ++++++++++++++++++ 62 files changed, 561 insertions(+), 68 deletions(-) create mode 100755 expect/individual-shell-tools/awk/script-01.sh create mode 100755 expect/individual-shell-tools/awk/script-02.sh create mode 100755 expect/individual-shell-tools/awk/script-03.sh create mode 100755 expect/individual-shell-tools/awk/script-04.sh create mode 100755 expect/individual-shell-tools/awk/script-05.sh create mode 100755 expect/individual-shell-tools/awk/script-06-stretch.sh create mode 100755 expect/individual-shell-tools/awk/script-07-stretch.sh create mode 100755 expect/individual-shell-tools/cat/script-01.sh create mode 100755 expect/individual-shell-tools/cat/script-02.sh create mode 100755 expect/individual-shell-tools/cat/script-03.sh create mode 100755 expect/individual-shell-tools/cat/script-04-stretch.sh create mode 100755 expect/individual-shell-tools/grep/script-01.sh create mode 100755 expect/individual-shell-tools/grep/script-02.sh create mode 100755 expect/individual-shell-tools/grep/script-03.sh create mode 100755 expect/individual-shell-tools/grep/script-04.sh create mode 100755 expect/individual-shell-tools/grep/script-05.sh create mode 100755 expect/individual-shell-tools/grep/script-06.sh create mode 100755 expect/individual-shell-tools/grep/script-07.sh create mode 100755 expect/individual-shell-tools/ls/script-01.sh create mode 100755 expect/individual-shell-tools/ls/script-02.sh create mode 100755 expect/individual-shell-tools/ls/script-03.sh create mode 100755 expect/individual-shell-tools/ls/script-04.sh create mode 100755 expect/individual-shell-tools/sed/script-01.sh create mode 100755 expect/individual-shell-tools/sed/script-02.sh create mode 100755 expect/individual-shell-tools/sed/script-03.sh create mode 100755 expect/individual-shell-tools/sed/script-04.sh create mode 100755 expect/individual-shell-tools/sed/script-05.sh create mode 100755 expect/individual-shell-tools/sed/script-06.sh create mode 100755 expect/individual-shell-tools/wc/script-01.sh create mode 100755 expect/individual-shell-tools/wc/script-02.sh create mode 100755 expect/individual-shell-tools/wc/script-03.sh create mode 100755 expect/jq/script-01.sh create mode 100755 expect/jq/script-02.sh create mode 100755 expect/jq/script-03.sh create mode 100755 expect/jq/script-04.sh create mode 100755 expect/jq/script-05.sh create mode 100755 expect/jq/script-06.sh create mode 100755 expect/jq/script-07.sh create mode 100755 expect/jq/script-08.sh create mode 100755 expect/jq/script-09.sh create mode 100755 expect/jq/script-10.sh create mode 100755 expect/jq/script-11.sh create mode 100644 expect/number-systems/Part-1.json create mode 100755 expect/shell-pipelines/ls-grep/script-01.sh create mode 100755 expect/shell-pipelines/ls-grep/script-02.sh create mode 100755 expect/shell-pipelines/ls-grep/script-03.sh create mode 100755 expect/shell-pipelines/ls-grep/script-04.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-01.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-02.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-03.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-04.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-05.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-06.sh create mode 100755 expect/shell-pipelines/sort-uniq-head-tail/script-07.sh create mode 100755 expect/shell-pipelines/tr/script-01.sh create mode 100755 expect/shell-pipelines/tr/script-02.sh create mode 100644 number-systems/Part-1.md create mode 100644 number-systems/Part-2.md delete mode 100644 number-systems/README.md create mode 100755 test-sdc.sh diff --git a/.github/workflows/validate-pr-metadata.yml b/.github/workflows/validate-pr-metadata.yml index 10ef3c7..8ff276f 100644 --- a/.github/workflows/validate-pr-metadata.yml +++ b/.github/workflows/validate-pr-metadata.yml @@ -1,9 +1,8 @@ -name: Validate PR Metadata +name: Validate PR Metadata and Check SDC-Tools Tasks on: - pull_request_target: + pull_request: types: - labeled - - unlabeled - opened - edited - reopened @@ -16,3 +15,55 @@ jobs: - uses: CodeYourFuture/actions/validate-pr-metadata@main with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test_sdc_tasks: + name: Test SDC Tasks + runs-on: ubuntu-slim + permissions: + pull-requests: write + if: ${{ github.event.label.name == 'Needs Review' }} + steps: + - name: checkout base branch + uses: actions/checkout@v5 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v47.0.5 + - name: test individual shell tools + id: test-individual-shell-tools + if: contains(steps.changed-files.outputs.modified_files, 'individual-shell-tools/') + run: ./test-sdc.sh individual-shell-tools + shell: bash + - name: test jq + id: test-jq + if: contains(steps.changed-files.outputs.modified_files, 'jq/') + run: ./test-sdc.sh jq + shell: bash + - name: test shell-pipelines + id: test-shell-pipelines + if: contains(steps.changed-files.outputs.modified_files, 'shell-pipelines/') + run: ./test-sdc.sh shell-pipelines + shell: bash + - name: test number-systems + id: test-number-systems + if: contains(steps.changed-files.outputs.modified_files, 'number-systems/') + run: ./test-sdc.sh number-systems + shell: bash + - name: make output comment + if: steps.test-individual-shell-tools.outputs.attempted == 'y' || steps.test-jq.outputs.attempted == 'y' || steps.test-shell-pipelines.outputs.attempted == 'y' || steps.test-number-systems.outputs.attempted == 'y' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_URL: ${{ github.event.pull_request.html_url }} + run: | + gh pr comment $ISSUE_URL --body-file testoutput.txt + - name: add appropriate labels + if: steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE_URL: ${{ github.event.pull_request.html_url }} + run: | + gh pr edit $ISSUE_URL --add-label "Complete" + gh pr edit $ISSUE_URL --remove-label "Needs Review" + - name: fail if not complete + if: (steps.test-individual-shell-tools.outputs.attempted == 'y' || steps.test-jq.outputs.attempted == 'y' || steps.test-shell-pipelines.outputs.attempted == 'y') && !(steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y') + run: | + exit -1 diff --git a/.gitignore b/.gitignore index 3c3629e..c4234ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +testoutput.txt diff --git a/expect/individual-shell-tools/awk/script-01.sh b/expect/individual-shell-tools/awk/script-01.sh new file mode 100755 index 0000000..050f043 --- /dev/null +++ b/expect/individual-shell-tools/awk/script-01.sh @@ -0,0 +1,6 @@ +Ahmed +Basia +Mehmet +Leila +Piotr +Chandra diff --git a/expect/individual-shell-tools/awk/script-02.sh b/expect/individual-shell-tools/awk/script-02.sh new file mode 100755 index 0000000..ee7e0fe --- /dev/null +++ b/expect/individual-shell-tools/awk/script-02.sh @@ -0,0 +1,6 @@ +Ahmed London +Basia London +Mehmet Birmingham +Leila London +Piotr Glasgow +Chandra Birmingham diff --git a/expect/individual-shell-tools/awk/script-03.sh b/expect/individual-shell-tools/awk/script-03.sh new file mode 100755 index 0000000..f5d0b1f --- /dev/null +++ b/expect/individual-shell-tools/awk/script-03.sh @@ -0,0 +1,6 @@ +Ahmed 1 +Basia 22 +Mehmet 3 +Leila 1 +Piotr 15 +Chandra 12 diff --git a/expect/individual-shell-tools/awk/script-04.sh b/expect/individual-shell-tools/awk/script-04.sh new file mode 100755 index 0000000..6e95a2f --- /dev/null +++ b/expect/individual-shell-tools/awk/script-04.sh @@ -0,0 +1,3 @@ +Ahmed 4 +Basia 6 +Leila 1 diff --git a/expect/individual-shell-tools/awk/script-05.sh b/expect/individual-shell-tools/awk/script-05.sh new file mode 100755 index 0000000..dd7e1d3 --- /dev/null +++ b/expect/individual-shell-tools/awk/script-05.sh @@ -0,0 +1,6 @@ +Ahmed 3 +Basia 3 +Mehmet 3 +Leila 1 +Piotr 5 +Chandra 2 diff --git a/expect/individual-shell-tools/awk/script-06-stretch.sh b/expect/individual-shell-tools/awk/script-06-stretch.sh new file mode 100755 index 0000000..fb1e7bc --- /dev/null +++ b/expect/individual-shell-tools/awk/script-06-stretch.sh @@ -0,0 +1 @@ +54 diff --git a/expect/individual-shell-tools/awk/script-07-stretch.sh b/expect/individual-shell-tools/awk/script-07-stretch.sh new file mode 100755 index 0000000..33ff777 --- /dev/null +++ b/expect/individual-shell-tools/awk/script-07-stretch.sh @@ -0,0 +1,6 @@ +Ahmed 15 +Basia 37 +Mehmet 32 +Leila 1 +Piotr 61 +Chandra 18 diff --git a/expect/individual-shell-tools/cat/script-01.sh b/expect/individual-shell-tools/cat/script-01.sh new file mode 100755 index 0000000..ed7bf7d --- /dev/null +++ b/expect/individual-shell-tools/cat/script-01.sh @@ -0,0 +1 @@ +Once upon a time... diff --git a/expect/individual-shell-tools/cat/script-02.sh b/expect/individual-shell-tools/cat/script-02.sh new file mode 100755 index 0000000..769b7e0 --- /dev/null +++ b/expect/individual-shell-tools/cat/script-02.sh @@ -0,0 +1,5 @@ +Once upon a time... +There was a house made of gingerbread. +It looked delicious. +I was tempted to take a bite of it. +But this seemed like a bad idea... diff --git a/expect/individual-shell-tools/cat/script-03.sh b/expect/individual-shell-tools/cat/script-03.sh new file mode 100755 index 0000000..f97bd50 --- /dev/null +++ b/expect/individual-shell-tools/cat/script-03.sh @@ -0,0 +1,3 @@ + 1 It looked delicious. + 2 I was tempted to take a bite of it. + 3 But this seemed like a bad idea... diff --git a/expect/individual-shell-tools/cat/script-04-stretch.sh b/expect/individual-shell-tools/cat/script-04-stretch.sh new file mode 100755 index 0000000..6f35e89 --- /dev/null +++ b/expect/individual-shell-tools/cat/script-04-stretch.sh @@ -0,0 +1,5 @@ + 1 Once upon a time... + 2 There was a house made of gingerbread. + 3 It looked delicious. + 4 I was tempted to take a bite of it. + 5 But this seemed like a bad idea... diff --git a/expect/individual-shell-tools/grep/script-01.sh b/expect/individual-shell-tools/grep/script-01.sh new file mode 100755 index 0000000..bc96ffe --- /dev/null +++ b/expect/individual-shell-tools/grep/script-01.sh @@ -0,0 +1,6 @@ +Doctor: Hello +Doctor: What's wrong today? +Doctor: That sounds frustrating. When did this start? +Doctor: Say "Hi". +Doctor: You didn't say hello +Doctor: You're welcome, goodbye diff --git a/expect/individual-shell-tools/grep/script-02.sh b/expect/individual-shell-tools/grep/script-02.sh new file mode 100755 index 0000000..bad606e --- /dev/null +++ b/expect/individual-shell-tools/grep/script-02.sh @@ -0,0 +1,9 @@ +Doctor: Hello +Patient: Hello Doctor +Doctor: What's wrong today? +Doctor: That sounds frustrating. When did this start? +Doctor: Say "Hi". +Doctor: You didn't say hello +Doctor: You're welcome, goodbye +Patient: I went to the doctor! +Spouse: I'm glad you saw the Doctor: did they cure you? diff --git a/expect/individual-shell-tools/grep/script-03.sh b/expect/individual-shell-tools/grep/script-03.sh new file mode 100755 index 0000000..ec63514 --- /dev/null +++ b/expect/individual-shell-tools/grep/script-03.sh @@ -0,0 +1 @@ +9 diff --git a/expect/individual-shell-tools/grep/script-04.sh b/expect/individual-shell-tools/grep/script-04.sh new file mode 100755 index 0000000..5814afa --- /dev/null +++ b/expect/individual-shell-tools/grep/script-04.sh @@ -0,0 +1,10 @@ +Doctor: What's wrong today? +Doctor: That sounds frustrating. When did this start? +Doctor: Say "Hi". +Patient: Hi +Patient: I seem to be cured! +Doctor: You're welcome, goodbye + +Patient: I went to the doctor! +Spouse: I'm glad you saw the Doctor: did they cure you? +Patient: Yes! diff --git a/expect/individual-shell-tools/grep/script-05.sh b/expect/individual-shell-tools/grep/script-05.sh new file mode 100755 index 0000000..2ed07ed --- /dev/null +++ b/expect/individual-shell-tools/grep/script-05.sh @@ -0,0 +1,5 @@ +Doctor: You didn't say hello +Patient: I seem to be cured! +-- +Patient: I went to the doctor! +Spouse: I'm glad you saw the Doctor: did they cure you? diff --git a/expect/individual-shell-tools/grep/script-06.sh b/expect/individual-shell-tools/grep/script-06.sh new file mode 100755 index 0000000..87b3a4f --- /dev/null +++ b/expect/individual-shell-tools/grep/script-06.sh @@ -0,0 +1,2 @@ +dialogue-2.txt +dialogue.txt diff --git a/expect/individual-shell-tools/grep/script-07.sh b/expect/individual-shell-tools/grep/script-07.sh new file mode 100755 index 0000000..aed78c6 --- /dev/null +++ b/expect/individual-shell-tools/grep/script-07.sh @@ -0,0 +1,3 @@ +dialogue-2.txt:2 +dialogue-3.txt:0 +dialogue.txt:6 diff --git a/expect/individual-shell-tools/ls/script-01.sh b/expect/individual-shell-tools/ls/script-01.sh new file mode 100755 index 0000000..dd590d6 --- /dev/null +++ b/expect/individual-shell-tools/ls/script-01.sh @@ -0,0 +1,5 @@ +child-directory +script-01.sh +script-02.sh +script-03.sh +script-04.sh diff --git a/expect/individual-shell-tools/ls/script-02.sh b/expect/individual-shell-tools/ls/script-02.sh new file mode 100755 index 0000000..bfd087e --- /dev/null +++ b/expect/individual-shell-tools/ls/script-02.sh @@ -0,0 +1,3 @@ +helper-1.txt +helper-2.txt +helper-3.txt diff --git a/expect/individual-shell-tools/ls/script-03.sh b/expect/individual-shell-tools/ls/script-03.sh new file mode 100755 index 0000000..36acd71 --- /dev/null +++ b/expect/individual-shell-tools/ls/script-03.sh @@ -0,0 +1,11 @@ +.: +child-directory +script-01.sh +script-02.sh +script-03.sh +script-04.sh + +./child-directory: +helper-1.txt +helper-2.txt +helper-3.txt diff --git a/expect/individual-shell-tools/ls/script-04.sh b/expect/individual-shell-tools/ls/script-04.sh new file mode 100755 index 0000000..86e7570 --- /dev/null +++ b/expect/individual-shell-tools/ls/script-04.sh @@ -0,0 +1,8 @@ +First exercise (sorted newest to oldest): +helper-3.txt +helper-1.txt +helper-2.txt +Second exercise (sorted oldest to newest): +helper-2.txt +helper-1.txt +helper-3.txt diff --git a/expect/individual-shell-tools/sed/script-01.sh b/expect/individual-shell-tools/sed/script-01.sh new file mode 100755 index 0000000..d92b03a --- /dev/null +++ b/expect/individual-shell-tools/sed/script-01.sh @@ -0,0 +1,11 @@ +ThIs Is a sample fIle for experImentIng wIth sed. + +It contaIns many lInes, and there are some thIngs you may want to do wIth each of them. + +We'll Include some score InformatIon: +37 AlIsha +15 Jacob +7 PIetro +3 Katya + +We also should remember, when we go shoppIng, to get 4 Items: oranges,cheese,bread,olIves. diff --git a/expect/individual-shell-tools/sed/script-02.sh b/expect/individual-shell-tools/sed/script-02.sh new file mode 100755 index 0000000..e326bd4 --- /dev/null +++ b/expect/individual-shell-tools/sed/script-02.sh @@ -0,0 +1,11 @@ +This is a sample file for experimenting with sed. + +It contains many lines, and there are some things you may want to do with each of them. + +We'll include some score information: + Alisha + Jacob + Pietro + Katya + +We also should remember, when we go shopping, to get items: oranges,cheese,bread,olives. diff --git a/expect/individual-shell-tools/sed/script-03.sh b/expect/individual-shell-tools/sed/script-03.sh new file mode 100755 index 0000000..088b17d --- /dev/null +++ b/expect/individual-shell-tools/sed/script-03.sh @@ -0,0 +1,6 @@ +This is a sample file for experimenting with sed. + +It contains many lines, and there are some things you may want to do with each of them. + +We'll include some score information: + diff --git a/expect/individual-shell-tools/sed/script-04.sh b/expect/individual-shell-tools/sed/script-04.sh new file mode 100755 index 0000000..852fa15 --- /dev/null +++ b/expect/individual-shell-tools/sed/script-04.sh @@ -0,0 +1,11 @@ +This is a sample file for experimenting with sed. + +It contains many lines, and there are some things you may want to do with each of them. + +We'll include some score information: +37 Alisha +15 Jacob +7 Pietro +3 Katya + +We also should remember, when we go shopping, to get 4 items: oranges,cheese,bread,olives. diff --git a/expect/individual-shell-tools/sed/script-05.sh b/expect/individual-shell-tools/sed/script-05.sh new file mode 100755 index 0000000..fe4d9d1 --- /dev/null +++ b/expect/individual-shell-tools/sed/script-05.sh @@ -0,0 +1,11 @@ +This is a sample file for experimenting with sed. + +It contains many lines, and there are some things you may want to do with each of them. + +We'll include some score information: +Alisha 37 +Jacob 15 +Pietro 7 +Katya 3 + +We also should remember, when we go shopping, to get 4 items: oranges,cheese,bread,olives. diff --git a/expect/individual-shell-tools/sed/script-06.sh b/expect/individual-shell-tools/sed/script-06.sh new file mode 100755 index 0000000..d9f950f --- /dev/null +++ b/expect/individual-shell-tools/sed/script-06.sh @@ -0,0 +1,11 @@ +This is a sample file for experimenting with sed. + +It contains many lines, and there are some things you may want to do with each of them. + +We'll include some score information: +37 Alisha +15 Jacob +7 Pietro +3 Katya + +We also should remember, when we go shopping, to get 4 items: oranges, cheese, bread, olives. diff --git a/expect/individual-shell-tools/wc/script-01.sh b/expect/individual-shell-tools/wc/script-01.sh new file mode 100755 index 0000000..d1ad53e --- /dev/null +++ b/expect/individual-shell-tools/wc/script-01.sh @@ -0,0 +1 @@ +19 ../helper-files/helper-3.txt diff --git a/expect/individual-shell-tools/wc/script-02.sh b/expect/individual-shell-tools/wc/script-02.sh new file mode 100755 index 0000000..6bd060b --- /dev/null +++ b/expect/individual-shell-tools/wc/script-02.sh @@ -0,0 +1 @@ +3 ../helper-files/helper-3.txt diff --git a/expect/individual-shell-tools/wc/script-03.sh b/expect/individual-shell-tools/wc/script-03.sh new file mode 100755 index 0000000..572716a --- /dev/null +++ b/expect/individual-shell-tools/wc/script-03.sh @@ -0,0 +1,4 @@ + 1 4 20 ../helper-files/helper-1.txt + 1 7 39 ../helper-files/helper-2.txt + 3 19 92 ../helper-files/helper-3.txt + 5 30 151 total diff --git a/expect/jq/script-01.sh b/expect/jq/script-01.sh new file mode 100755 index 0000000..e16f4ed --- /dev/null +++ b/expect/jq/script-01.sh @@ -0,0 +1 @@ +Selma diff --git a/expect/jq/script-02.sh b/expect/jq/script-02.sh new file mode 100755 index 0000000..18711af --- /dev/null +++ b/expect/jq/script-02.sh @@ -0,0 +1 @@ +35 Fashion Street, London, E1 6PX diff --git a/expect/jq/script-03.sh b/expect/jq/script-03.sh new file mode 100755 index 0000000..d63f6ec --- /dev/null +++ b/expect/jq/script-03.sh @@ -0,0 +1,2 @@ +Selma, Software Engineer +Selma, Software Engineer diff --git a/expect/jq/script-04.sh b/expect/jq/script-04.sh new file mode 100755 index 0000000..050f043 --- /dev/null +++ b/expect/jq/script-04.sh @@ -0,0 +1,6 @@ +Ahmed +Basia +Mehmet +Leila +Piotr +Chandra diff --git a/expect/jq/script-05.sh b/expect/jq/script-05.sh new file mode 100755 index 0000000..ee7e0fe --- /dev/null +++ b/expect/jq/script-05.sh @@ -0,0 +1,6 @@ +Ahmed London +Basia London +Mehmet Birmingham +Leila London +Piotr Glasgow +Chandra Birmingham diff --git a/expect/jq/script-06.sh b/expect/jq/script-06.sh new file mode 100755 index 0000000..f5d0b1f --- /dev/null +++ b/expect/jq/script-06.sh @@ -0,0 +1,6 @@ +Ahmed 1 +Basia 22 +Mehmet 3 +Leila 1 +Piotr 15 +Chandra 12 diff --git a/expect/jq/script-07.sh b/expect/jq/script-07.sh new file mode 100755 index 0000000..59c783f --- /dev/null +++ b/expect/jq/script-07.sh @@ -0,0 +1,6 @@ +Ahmed 4 +Basia 6 +Mehmet 17 +Leila 1 +Piotr 8 +Chandra 6 diff --git a/expect/jq/script-08.sh b/expect/jq/script-08.sh new file mode 100755 index 0000000..dd7e1d3 --- /dev/null +++ b/expect/jq/script-08.sh @@ -0,0 +1,6 @@ +Ahmed 3 +Basia 3 +Mehmet 3 +Leila 1 +Piotr 5 +Chandra 2 diff --git a/expect/jq/script-09.sh b/expect/jq/script-09.sh new file mode 100755 index 0000000..33ff777 --- /dev/null +++ b/expect/jq/script-09.sh @@ -0,0 +1,6 @@ +Ahmed 15 +Basia 37 +Mehmet 32 +Leila 1 +Piotr 61 +Chandra 18 diff --git a/expect/jq/script-10.sh b/expect/jq/script-10.sh new file mode 100755 index 0000000..fb1e7bc --- /dev/null +++ b/expect/jq/script-10.sh @@ -0,0 +1 @@ +54 diff --git a/expect/jq/script-11.sh b/expect/jq/script-11.sh new file mode 100755 index 0000000..4e9bdff --- /dev/null +++ b/expect/jq/script-11.sh @@ -0,0 +1 @@ +164 diff --git a/expect/number-systems/Part-1.json b/expect/number-systems/Part-1.json new file mode 100644 index 0000000..d96c538 --- /dev/null +++ b/expect/number-systems/Part-1.json @@ -0,0 +1 @@ +{"answers": [0, 1110, 45, 1000, "01011", 11111, 100010, 15, 8, 2, "(10$|10[^0-9])", "E", 182, 902, 11, 33]} diff --git a/expect/shell-pipelines/ls-grep/script-01.sh b/expect/shell-pipelines/ls-grep/script-01.sh new file mode 100755 index 0000000..5481774 --- /dev/null +++ b/expect/shell-pipelines/ls-grep/script-01.sh @@ -0,0 +1,11 @@ +Ariane +Daniel +HoChiMinh +KualaLumpur +Levi +London +NewYork +Niamh +Olga +York +dHondt diff --git a/expect/shell-pipelines/ls-grep/script-02.sh b/expect/shell-pipelines/ls-grep/script-02.sh new file mode 100755 index 0000000..35f2e44 --- /dev/null +++ b/expect/shell-pipelines/ls-grep/script-02.sh @@ -0,0 +1,10 @@ +Ariane +Daniel +HoChiMinh +KualaLumpur +Levi +London +NewYork +Niamh +Olga +York diff --git a/expect/shell-pipelines/ls-grep/script-03.sh b/expect/shell-pipelines/ls-grep/script-03.sh new file mode 100755 index 0000000..37ec450 --- /dev/null +++ b/expect/shell-pipelines/ls-grep/script-03.sh @@ -0,0 +1,7 @@ +Ariane +Daniel +Levi +London +Niamh +Olga +York diff --git a/expect/shell-pipelines/ls-grep/script-04.sh b/expect/shell-pipelines/ls-grep/script-04.sh new file mode 100755 index 0000000..7f8f011 --- /dev/null +++ b/expect/shell-pipelines/ls-grep/script-04.sh @@ -0,0 +1 @@ +7 diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-01.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-01.sh new file mode 100755 index 0000000..a622bc9 --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-01.sh @@ -0,0 +1,6 @@ +Ahmed London 1 10 4 +Basia London 22 9 6 +Chandra Birmingham 12 6 +Leila London 1 +Mehmet Birmingham 3 12 17 +Piotr Glasgow 15 2 25 11 8 diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-02.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-02.sh new file mode 100755 index 0000000..c560f60 --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-02.sh @@ -0,0 +1,6 @@ +Basia London 22 9 6 +Piotr Glasgow 15 2 25 11 8 +Chandra Birmingham 12 6 +Mehmet Birmingham 3 12 17 +Leila London 1 +Ahmed London 1 10 4 diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-03.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-03.sh new file mode 100755 index 0000000..52d05bc --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-03.sh @@ -0,0 +1,3 @@ +Basia London 22 9 6 +Piotr Glasgow 15 2 25 11 8 +Chandra Birmingham 12 6 diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-04.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-04.sh new file mode 100755 index 0000000..9c18f93 --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-04.sh @@ -0,0 +1 @@ +Piotr Glasgow 15 2 25 11 8 diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-05.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-05.sh new file mode 100755 index 0000000..5b6440e --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-05.sh @@ -0,0 +1,6 @@ +Entry German +Entry Mariana +Entry Sally +Exit German +Exit Mariana +Exit Sally diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-06.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-06.sh new file mode 100755 index 0000000..93bd06b --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-06.sh @@ -0,0 +1,2 @@ + 5 Entry + 4 Exit diff --git a/expect/shell-pipelines/sort-uniq-head-tail/script-07.sh b/expect/shell-pipelines/sort-uniq-head-tail/script-07.sh new file mode 100755 index 0000000..93bd06b --- /dev/null +++ b/expect/shell-pipelines/sort-uniq-head-tail/script-07.sh @@ -0,0 +1,2 @@ + 5 Entry + 4 Exit diff --git a/expect/shell-pipelines/tr/script-01.sh b/expect/shell-pipelines/tr/script-01.sh new file mode 100755 index 0000000..8dea4eb --- /dev/null +++ b/expect/shell-pipelines/tr/script-01.sh @@ -0,0 +1,11 @@ +Dear Yara, + +Mz apologies for sending this response so late. As zou know, there's been a lot going on. + +Unfortunatelz I don't think I'll be able to make it to Yimbabwe, but but sounds amaying. + +Hope zou're doing well, and enjoz the trip. + +Thanks, + +Karolina diff --git a/expect/shell-pipelines/tr/script-02.sh b/expect/shell-pipelines/tr/script-02.sh new file mode 100755 index 0000000..e00561a --- /dev/null +++ b/expect/shell-pipelines/tr/script-02.sh @@ -0,0 +1,11 @@ +Dear Zara, + +My apologies for sending this response so late. As you know, there's been a lot going on! + +Unfortunately I don't think I'll be able to make it to Zimbabwe, but but sounds amazing! + +Hope you're doing well, and enjoy the trip! + +Thanks, + +Karolina diff --git a/number-systems/Part-1.md b/number-systems/Part-1.md new file mode 100644 index 0000000..d8f9c29 --- /dev/null +++ b/number-systems/Part-1.md @@ -0,0 +1,54 @@ +Do not use any tools or programming to solve these problems. Work it out yourself by hand, and fill in the answers. + +Do not convert any binary numbers to decimal when solving a question unless the question explicitly tells you to. + +The goal of these exercises is for you to gain an intuition for binary numbers. Using tools to solve the problems defeats the point. + +The answers to these questions should be a number, either in binary, hex, or decimal. + +Q1: Convert the decimal number 14 to binary. +Answer: + +Q2: Convert the binary number 101101 to decimal: +Answer: + +Q3: Which is larger: 1000 or 0111? +Answer: + +Q4: Which is larger: 00100 or 01011? +Answer: + +Q5: What is 10101 + 01010? +Answer: + +Q6: What is 10001 + 10001? +Answer: + +Q7: What's the largest number you can store with 4 bits, if you want to be able to represent the number 0? +Answer: + +Q8: How many bits would you need in order to store the numbers between 0 and 255 inclusive? +Answer: + +Q9: How many bits would you need in order to store the numbers between 0 and 3 inclusive? +Answer: + +Q10: How many bits would you need in order to store the numbers between 0 and 1000 inclusive? +Answer: + +Q11: Convert the decimal number 14 to hex. +Answer: + +Q12: Convert the decimal number 386 to hex. +Answer: + +Q13: Convert the hex number 386 to decimal. +Answer: + +Q14: Convert the hex number B to decimal. +Answer: + +Q15: If reading the byte 0x21 as a number, what decimal number would it mean? +Answer: + +Q16: Continues in Part-2 diff --git a/number-systems/Part-2.md b/number-systems/Part-2.md new file mode 100644 index 0000000..92b7005 --- /dev/null +++ b/number-systems/Part-2.md @@ -0,0 +1,22 @@ +Do not use any tools or programming to solve these problems. Work it out yourself by hand, and fill in the answers. + +Do not convert any binary numbers to decimal when solving a question unless the question explicitly tells you to. + +The goal of these exercises is for you to gain an intuition for binary numbers. Using tools to solve the problems defeats the point. + +The answers to these questions will require a bit of explanation, not just a simple answer. + +Q16: How can you test if a binary number is a power of two (e.g. 1, 2, 4, 8, 16, ...)? +Answer: + +Q17: If reading the byte 0x21 as an ASCII character, what character would it mean? +Answer: + +Q18: If reading the byte 0x21 as a greyscale colour, as described in "Approaches for Representing Colors and Images", what colour would it mean? +Answer: + +Q19: If reading the bytes 0xAA00FF as an RGB colour, as described in "Approaches for Representing Colors and Images", what colour would it mean? +Answer: + +Q20: If reading the bytes 0xAA00FF as a sequence of three one-byte decimal numbers, what decimal numbers would they be? +Answer: diff --git a/number-systems/README.md b/number-systems/README.md deleted file mode 100644 index 77a3bde..0000000 --- a/number-systems/README.md +++ /dev/null @@ -1,65 +0,0 @@ -Do not use any tools or programming to solve these problems. Work it out yourself by hand, and fill in the answers. - -Do not convert any binary numbers to decimal when solving a question unless the question explicitly tells you to. - -The goal of these exercises is for you to gain an intuition for binary numbers. Using tools to solve the problems defeats the point. - -Convert the decimal number 14 to binary. -Answer: - -Convert the binary number 101101 to decimal: -Answer: - -Which is larger: 1000 or 0111? -Answer: - -Which is larger: 00100 or 01011? -Answer: - -What is 10101 + 01010? -Answer: - -What is 10001 + 10001? -Answer: - -What's the largest number you can store with 4 bits, if you want to be able to represent the number 0? -Answer: - -How many bits would you need in order to store the numbers between 0 and 255 inclusive? -Answer: - -How many bits would you need in order to store the numbers between 0 and 3 inclusive? -Answer: - -How many bits would you need in order to store the numbers between 0 and 1000 inclusive? -Answer: - -How can you test if a binary number is a power of two (e.g. 1, 2, 4, 8, 16, ...)? -Answer: - -Convert the decimal number 14 to hex. -Answer: - -Convert the decimal number 386 to hex. -Answer: - -Convert the hex number 386 to decimal. -Answer: - -Convert the hex number B to decimal. -Answer: - -If reading the byte 0x21 as a number, what decimal number would it mean? -Answer: - -If reading the byte 0x21 as an ASCII character, what character would it mean? -Answer: - -If reading the byte 0x21 as a greyscale colour, as described in "Approaches for Representing Colors and Images", what colour would it mean? -Answer: - -If reading the bytes 0xAA00FF as an RGB colour, as described in "Approaches for Representing Colors and Images", what colour would it mean? -Answer: - -If reading the bytes 0xAA00FF as a sequence of three one-byte decimal numbers, what decimal numbers would they be? -Answer: diff --git a/test-sdc.sh b/test-sdc.sh new file mode 100755 index 0000000..b8f32cf --- /dev/null +++ b/test-sdc.sh @@ -0,0 +1,132 @@ +# This test file will be automatically run when you submit a pull request +# You should not modify this file +# You can run this file using ./test-sdc.sh task-directory-name to check your output + +echo "" > testoutput.txt +echo "Results of test" >> testoutput.txt + +if [[ "$1" == "individual-shell-tools" ]]; then + cd individual-shell-tools + pass=0 + total=0 + for directory in */; do + cd $directory + if [ "$directory" == "helper-files/" ]; then + cd .. + continue + fi + for exercise in *.sh; do + total=$(($total+1)) + ./$exercise > ../../test.tmp + cmp ../../test.tmp ../../expect/individual-shell-tools/$directory$exercise --quiet + if [ $? -eq 0 ]; then + pass=$(($pass+1)) + else + if [[ "$exercise" == *"stretch"* && $(stat -c%s ../../test.tmp) == "0" ]]; then + # Stretch task not attempted, ignoring" + total=$(($total-1)) + else + echo "Failed $directory$exercise, please check" >> ../../testoutput.txt + fi + fi + done + cd .. + done + cd .. + rm test.tmp + echo "You passed $pass/$total tasks." >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "attempted=y" >> "$GITHUB_OUTPUT" + fi + if [ $pass -ge $total ]; then + echo "This task is complete!" >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "complete=y" >> "$GITHUB_OUTPUT" + fi + fi + cat testoutput.txt +elif [[ "$1" == "shell-pipelines" ]]; then + cd shell-pipelines + pass=0 + total=0 + for directory in */; do + cd $directory + for exercise in *.sh; do + total=$(($total+1)) + ./$exercise > ../../test.tmp + cmp ../../test.tmp ../../expect/shell-pipelines/$directory$exercise --quiet + if [ $? -eq 0 ]; then + pass=$(($pass+1)) + else + echo "Failed $directory$exercise, please check" >> ../../testoutput.txt + fi + done + cd .. + done + cd .. + rm test.tmp + echo "You passed $pass/$total tasks." >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "attempted=y" >> "$GITHUB_OUTPUT" + fi + if [ $pass -eq $total ]; then + echo "This task is complete!" >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "complete=y" >> "$GITHUB_OUTPUT" + fi + fi + cat testoutput.txt +elif [[ "$1" == "jq" ]]; then + cd jq + pass=0 + total=0 + for exercise in *.sh; do + total=$(($total+1)) + ./$exercise > ../test.tmp + cmp ../test.tmp ../expect/jq/$exercise --quiet + if [ $? -eq 0 ]; then + pass=$(($pass+1)) + else + echo "Failed $exercise, please check" >> ../testoutput.txt + fi + done + cd .. + rm test.tmp + echo "You passed $pass/$total tasks." >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "attempted=y" >> "$GITHUB_OUTPUT" + fi + if [ $pass -eq $total ]; then + echo "This task is complete!" >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "complete=y" >> "$GITHUB_OUTPUT" + fi + fi + cat testoutput.txt +elif [[ "$1" == "number-systems" ]]; then + pass=0 + for question in $(seq 1 15); do + answer=$(jq -r ".answers[$question]" expect/number-systems/Part-1.json) + nextq=$(($question+1)) + Q_START=$(grep -n "Q$question:" number-systems/Part-1.md | cut -d: -f1) + ANS_START=$(($Q_START + 1)) + NEXT_Q_START=$(grep -n "Q$nextq:" number-systems/Part-1.md | cut -d: -f1) + ANS_END=$(($NEXT_Q_START - 1)) + sed -n "$ANS_START,${ANS_END}p;${NEXT_Q_START}q" number-systems/Part-1.md | cut -d: -f2- > answerfile + grep --quiet -iE $answer answerfile + if [ $? -eq 0 ]; then + pass=$(($pass+1)) + else + echo "Q$question incorrect, please check" >> testoutput.txt + fi + rm answerfile + done + echo "You passed $pass/15 tasks in part 1." >> testoutput.txt + if [ -v GITHUB_OUTPUT ]; then + echo "attempted=y" >> "$GITHUB_OUTPUT" + fi + echo "Please let a volunteer check the answers for part 2." >> testoutput.txt + cat testoutput.txt +else + echo "Please run this with a valid test directory name as argument" +fi From f38fee54def1961f51947ca50ef9dde930a00c39 Mon Sep 17 00:00:00 2001 From: Daniel Wagner-Hall Date: Tue, 12 May 2026 14:26:50 +0100 Subject: [PATCH 03/15] Make test-sdc script more portable (#520) * Add a shebang because `-v` is a bash-ism not present in whatever my default shell is on macOS. * Remove `--quiet` from `cmp` - macOS's `cmp` doesn't support it. --- test-sdc.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test-sdc.sh b/test-sdc.sh index b8f32cf..acf9897 100755 --- a/test-sdc.sh +++ b/test-sdc.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # This test file will be automatically run when you submit a pull request # You should not modify this file # You can run this file using ./test-sdc.sh task-directory-name to check your output @@ -18,7 +20,7 @@ if [[ "$1" == "individual-shell-tools" ]]; then for exercise in *.sh; do total=$(($total+1)) ./$exercise > ../../test.tmp - cmp ../../test.tmp ../../expect/individual-shell-tools/$directory$exercise --quiet + cmp ../../test.tmp ../../expect/individual-shell-tools/$directory$exercise if [ $? -eq 0 ]; then pass=$(($pass+1)) else @@ -54,7 +56,7 @@ elif [[ "$1" == "shell-pipelines" ]]; then for exercise in *.sh; do total=$(($total+1)) ./$exercise > ../../test.tmp - cmp ../../test.tmp ../../expect/shell-pipelines/$directory$exercise --quiet + cmp ../../test.tmp ../../expect/shell-pipelines/$directory$exercise if [ $? -eq 0 ]; then pass=$(($pass+1)) else @@ -83,7 +85,7 @@ elif [[ "$1" == "jq" ]]; then for exercise in *.sh; do total=$(($total+1)) ./$exercise > ../test.tmp - cmp ../test.tmp ../expect/jq/$exercise --quiet + cmp ../test.tmp ../expect/jq/$exercise if [ $? -eq 0 ]; then pass=$(($pass+1)) else From ab33fa5254177acf1df97d1557f5f87ef9a5eeeb Mon Sep 17 00:00:00 2001 From: Daniel Wagner-Hall Date: Tue, 12 May 2026 15:26:14 +0100 Subject: [PATCH 04/15] Add automated tests for cowsay implementation (#521) --- .github/workflows/validate-pr-metadata.yml | 11 +++-- expect/implement-cowsay/cow-grass.txt | 10 +++++ expect/implement-cowsay/turtle-fish.txt | 22 ++++++++++ test-sdc.sh | 47 ++++++++++++++++++++++ 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 expect/implement-cowsay/cow-grass.txt create mode 100644 expect/implement-cowsay/turtle-fish.txt diff --git a/.github/workflows/validate-pr-metadata.yml b/.github/workflows/validate-pr-metadata.yml index 8ff276f..b4ff602 100644 --- a/.github/workflows/validate-pr-metadata.yml +++ b/.github/workflows/validate-pr-metadata.yml @@ -48,15 +48,20 @@ jobs: if: contains(steps.changed-files.outputs.modified_files, 'number-systems/') run: ./test-sdc.sh number-systems shell: bash + - name: test implement-cowsay + id: test-implement-cowsay + if: contains(steps.changed-files.outputs.modified_files, 'implement-cowsay/') + run: ./test-sdc.sh implement-cowsay + shell: bash - name: make output comment - if: steps.test-individual-shell-tools.outputs.attempted == 'y' || steps.test-jq.outputs.attempted == 'y' || steps.test-shell-pipelines.outputs.attempted == 'y' || steps.test-number-systems.outputs.attempted == 'y' + if: steps.test-individual-shell-tools.outputs.attempted == 'y' || steps.test-jq.outputs.attempted == 'y' || steps.test-shell-pipelines.outputs.attempted == 'y' || steps.test-number-systems.outputs.attempted == 'y' || steps.test-implement-cowsay.outputs.attempted == 'y' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_URL: ${{ github.event.pull_request.html_url }} run: | gh pr comment $ISSUE_URL --body-file testoutput.txt - name: add appropriate labels - if: steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y' + if: steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y' || steps.test-implement-cowsay.outputs.complete == 'y' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_URL: ${{ github.event.pull_request.html_url }} @@ -64,6 +69,6 @@ jobs: gh pr edit $ISSUE_URL --add-label "Complete" gh pr edit $ISSUE_URL --remove-label "Needs Review" - name: fail if not complete - if: (steps.test-individual-shell-tools.outputs.attempted == 'y' || steps.test-jq.outputs.attempted == 'y' || steps.test-shell-pipelines.outputs.attempted == 'y') && !(steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y') + if: (steps.test-individual-shell-tools.outputs.attempted == 'y' || steps.test-jq.outputs.attempted == 'y' || steps.test-shell-pipelines.outputs.attempted == 'y' || steps.test-implement-cowsay.outputs.attempted == 'y') && !(steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y' || steps.test-implement-cowsay.outputs.complete == 'y') run: | exit -1 diff --git a/expect/implement-cowsay/cow-grass.txt b/expect/implement-cowsay/cow-grass.txt new file mode 100644 index 0000000..d9c665a --- /dev/null +++ b/expect/implement-cowsay/cow-grass.txt @@ -0,0 +1,10 @@ + ________________ +| Grass, delicious | + ================ + \ + \ + ^__^ + (oo)\_______ + (__)\ )\/\ + ||----w | + || || diff --git a/expect/implement-cowsay/turtle-fish.txt b/expect/implement-cowsay/turtle-fish.txt new file mode 100644 index 0000000..5dadfa0 --- /dev/null +++ b/expect/implement-cowsay/turtle-fish.txt @@ -0,0 +1,22 @@ + ______________ +| Fish are cool! | + ============== + \ + \ + \ + \ + ___-------___ + _-~~ ~~-_ + _-~ /~-_ + /^\__/^\ /~ \ / \ + /| O|| O| / \_______________/ \ + | |___||__| / / \ \ + | \ / / \ \ + | (_______) /______/ \_________ \ + | / / \ / \ + \ \^\\ \ / \ / + \ || \______________/ _-_ //\__// + \ ||------_-~~-_ ------------- \ --/~ ~\ || __/ + ~-----||====/~ |==================| |/~~~~~ + (_(__/ ./ / \_\ \. + (_(___/ \_____)_) diff --git a/test-sdc.sh b/test-sdc.sh index acf9897..adc8dc4 100755 --- a/test-sdc.sh +++ b/test-sdc.sh @@ -129,6 +129,53 @@ elif [[ "$1" == "number-systems" ]]; then fi echo "Please let a volunteer check the answers for part 2." >> testoutput.txt cat testoutput.txt +elif [[ "$1" == "implement-cowsay" ]]; then + if [ -v GITHUB_OUTPUT ]; then + echo "attempted=y" >> "$GITHUB_OUTPUT" + fi + if [[ -e .cowsay-venv ]]; then + echo ".cowsay-venv already exists - couldn't test cowsay" >> testoutput.txt + else + if [ ! -e implement-cowsay/requirements.txt ]; then + echo "Expected implement-cowsay/requirements.txt to exist but it didn't" >> testoutput.txt + else + python3 -m venv .cowsay-venv + . .cowsay-venv/bin/activate + pip3 install -r implement-cowsay/requirements.txt + + all_ok=true + + python3 implement-cowsay/cow.py Grass, delicious > test.tmp + cmp test.tmp expect/implement-cowsay/cow-grass.txt + if [ $? -ne 0 ]; then + echo "Unexpected cowsay output for Grass, delicious" >> testoutput.txt + all_ok=false + fi + + python3 implement-cowsay/cow.py --animal turtle "Fish are cool!" > test.tmp + cmp test.tmp expect/implement-cowsay/turtle-fish.txt + if [ $? -ne 0 ]; then + echo "Unexpected cowsay output for Fish are cool!" >> testoutput.txt + all_ok=false + fi + + python3 implement-cowsay/cow.py --help | grep dragon + if [ $? -ne 0 ]; then + echo "Expected python3 cow.py --help to include dragon as it's one of the animal options" >> testoutput.txt + all_ok=false + fi + + grep dragon implement-cowsay/cow.py > /dev/null + if [ $? -eq 0 ]; then + echo "Didn't expect cow.py to include the word dragon - you should pull in the animal options from the library not write them yourself" >> testoutput.txt + all_ok=false + fi + + if [[ "${all_ok}" == "true" && -v GITHUB_OUTPUT ]]; then + echo "completed=y" >> "$GITHUB_OUTPUT" + fi + fi + fi else echo "Please run this with a valid test directory name as argument" fi From d3857c25cc7a1a94111183aa2901ef73c45c4865 Mon Sep 17 00:00:00 2001 From: Daniel Wagner-Hall Date: Wed, 13 May 2026 10:05:37 +0100 Subject: [PATCH 05/15] Improve cowsay comments (#522) * Trigger on added files - Cowsay involves writing brand new files * Hide previous comments * Fix completed -> complete * Clean up output a little --- .github/workflows/validate-pr-metadata.yml | 4 +++- test-sdc.sh | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/validate-pr-metadata.yml b/.github/workflows/validate-pr-metadata.yml index b4ff602..6fa2e1d 100644 --- a/.github/workflows/validate-pr-metadata.yml +++ b/.github/workflows/validate-pr-metadata.yml @@ -50,7 +50,7 @@ jobs: shell: bash - name: test implement-cowsay id: test-implement-cowsay - if: contains(steps.changed-files.outputs.modified_files, 'implement-cowsay/') + if: contains(steps.changed-files.outputs.all_modified_files, 'implement-cowsay/') run: ./test-sdc.sh implement-cowsay shell: bash - name: make output comment @@ -59,6 +59,8 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE_URL: ${{ github.event.pull_request.html_url }} run: | + gh release -R CodeYourFuture/trainee-tracker view --json assets -q ".assets[] | select(.name | startswith(\"pr-metadata-validator-musl-\")).url" | xargs curl --fail -L -o /tmp/pr-metadata-validator && chmod 0755 /tmp/pr-metadata-validator + /tmp/pr-metadata-validator --only-close-existing-comments-with-tag="sdc-test-results" "${ISSUE_URL}" gh pr comment $ISSUE_URL --body-file testoutput.txt - name: add appropriate labels if: steps.test-individual-shell-tools.outputs.complete == 'y' || steps.test-jq.outputs.complete == 'y' || steps.test-shell-pipelines.outputs.complete == 'y' || steps.test-implement-cowsay.outputs.complete == 'y' diff --git a/test-sdc.sh b/test-sdc.sh index adc8dc4..62d1c7e 100755 --- a/test-sdc.sh +++ b/test-sdc.sh @@ -5,7 +5,7 @@ # You can run this file using ./test-sdc.sh task-directory-name to check your output echo "" > testoutput.txt -echo "Results of test" >> testoutput.txt +echo "Results of test:" >> testoutput.txt if [[ "$1" == "individual-shell-tools" ]]; then cd individual-shell-tools @@ -148,14 +148,14 @@ elif [[ "$1" == "implement-cowsay" ]]; then python3 implement-cowsay/cow.py Grass, delicious > test.tmp cmp test.tmp expect/implement-cowsay/cow-grass.txt if [ $? -ne 0 ]; then - echo "Unexpected cowsay output for Grass, delicious" >> testoutput.txt + echo "Unexpected cowsay output for 'Grass, delicious'." >> testoutput.txt all_ok=false fi python3 implement-cowsay/cow.py --animal turtle "Fish are cool!" > test.tmp cmp test.tmp expect/implement-cowsay/turtle-fish.txt if [ $? -ne 0 ]; then - echo "Unexpected cowsay output for Fish are cool!" >> testoutput.txt + echo "Unexpected cowsay output for --animal turtle Fish are cool!" >> testoutput.txt all_ok=false fi @@ -172,7 +172,8 @@ elif [[ "$1" == "implement-cowsay" ]]; then fi if [[ "${all_ok}" == "true" && -v GITHUB_OUTPUT ]]; then - echo "completed=y" >> "$GITHUB_OUTPUT" + echo "complete=y" >> "$GITHUB_OUTPUT" + echo "Your implementation passed all checks." >> testoutput.txt fi fi fi From 6c16e453f63e49b558e61af6b8cc3753b4157bb4 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Fri, 29 May 2026 21:15:27 +0100 Subject: [PATCH 06/15] Step 1: Why we use types --- sprint-5-prep/1-use-types.py | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 sprint-5-prep/1-use-types.py diff --git a/sprint-5-prep/1-use-types.py b/sprint-5-prep/1-use-types.py new file mode 100644 index 0000000..28d3014 --- /dev/null +++ b/sprint-5-prep/1-use-types.py @@ -0,0 +1,50 @@ +# Exercise-1 + +# Predict what double("22") will do. +# Then run the code and check. Did it do what you expected? +# Why did it return the value it did? + +def double(value): + return value * 2 + +#Answer +#I guess it'll return 2222. That's because any value in quotes is a string, +# and when strings are multiplied, they repeat the characters. + +def double(value): + return value * 2 + +print("double is:", double("22")) #double is: 2222 + +# Yes, It returned 2222 as I predicted. +# Python's operator "*" when used with a string and a number, repeats this string as "number" times, +# and it didn't convert the string "22" into number 22. + + +#Exercise-2 + +def double(number): + return number * 3 + +print(double(10)) + + +#Read the above code and write down what the bug is. How would you fix it? + +# Answer +# There are two way to fix it: +# 1. Change *3 to *2. + +def double(number): + return number * 2 + +print(double(10)) + +# or + +# 2. Rename the function to "triple" if we want multiply by 3 - it keeps the logic correct. + +def triple(number): + return number * 3 + +print(triple(10)) From b5c1ee63d002324b394d4b18470448a62aeb67ea Mon Sep 17 00:00:00 2001 From: Konvaly Date: Fri, 29 May 2026 21:16:03 +0100 Subject: [PATCH 07/15] Completed task 2-type checking with mypy --- sprint-5-prep/2-type-checking-mypy.py | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 sprint-5-prep/2-type-checking-mypy.py diff --git a/sprint-5-prep/2-type-checking-mypy.py b/sprint-5-prep/2-type-checking-mypy.py new file mode 100644 index 0000000..4d4cc1b --- /dev/null +++ b/sprint-5-prep/2-type-checking-mypy.py @@ -0,0 +1,31 @@ +from typing import Dict + +def open_account(balances: Dict[str, int], name: str, amount: int)-> None: + balances[name] = amount + +def sum_balances(accounts: Dict[str, int])-> int: + total = 0 + for name, pence in accounts.items(): + print(f"{name} had balance {pence}") + total += pence + return total + +def format_pence_as_string(total_pence: int)-> str: + if total_pence < 100: + return f"{total_pence}p" + pounds = int(total_pence / 100) + pence = total_pence % 100 + return f"£{pounds}.{pence:02d}" + +balances = { + "Sima": 700, + "Linn": 545, + "Georg": 831, +} + +open_account(balances, "Tobi", 913) +open_account(balances, "Olya", 713) + +total_pence = sum_balances(balances) + +print(f"The bank accounts total {total_string}") \ No newline at end of file From a5f9233b34067c52e082ed9836c5b93c474a22a1 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Fri, 29 May 2026 21:25:02 +0100 Subject: [PATCH 08/15] Add .venv, __pycache__, .mypy_cache to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c4234ab..63db17d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules testoutput.txt +.venv +__pycache__ +.mypy_cache \ No newline at end of file From be0ac7609236a8f563c47a134a094969eb2ed3e7 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Fri, 29 May 2026 23:12:52 +0100 Subject: [PATCH 09/15] Complete task-3 Classes and objects --- sprint-5-prep/3-classes-and-objects.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 sprint-5-prep/3-classes-and-objects.py diff --git a/sprint-5-prep/3-classes-and-objects.py b/sprint-5-prep/3-classes-and-objects.py new file mode 100644 index 0000000..2613ea1 --- /dev/null +++ b/sprint-5-prep/3-classes-and-objects.py @@ -0,0 +1,24 @@ +class Person: + def __init__(self, name: str, age: int, preferred_operating_system: str): + self.name = name + self.age = age + self.preferred_operating_system = preferred_operating_system + +imran = Person("Imran", 22, "Ubuntu") +print(imran.name) +#print(imran.address) +#error: Person class has no attribute "address" + +eliza = Person("Eliza", 34, "Arch Linux") +print(eliza.name) +#print(eliza.address) +#the same error: Person class has no attribute "address" + +def is_adult(person: Person) -> bool: + return person.age >= 18 + +print(is_adult(imran)) + +def get_phone(person: Person) -> str: + return person.phone_number +# After running mypy this method also causes an error, because Person class has no attribute "phone_number" \ No newline at end of file From bf737b7188327ce3328c6c56746cd2bd044ed1f3 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Sat, 30 May 2026 17:19:39 +0100 Subject: [PATCH 10/15] Complete task-4 Methods --- sprint-5-prep/4-methods.py | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 sprint-5-prep/4-methods.py diff --git a/sprint-5-prep/4-methods.py b/sprint-5-prep/4-methods.py new file mode 100644 index 0000000..b442f30 --- /dev/null +++ b/sprint-5-prep/4-methods.py @@ -0,0 +1,44 @@ +# Exercises-1 +# Think of the advantages of using methods instead of free functions. +# Write them down in your notebook. + +# 1. Organisation: all Person-related logic lives inside the Person class. + +# 2. Encapsulation: we only have to update the method once, +# for example if we change "age" to "date_of_birth" inside the class. +# With free functions, we'd have to update every function that touches person.age across the whole codebase. + +# 3. Simple search: if we type "imran." our editor shows everything Person can do. +# With free functions, we have to remember they exist. + +# 4. Clear ownership: "person.is_adult()" is definitely about a person, +# but with "is_adult(x)" we don't know what it's about. + +# 5. Easier to catch errors with mypy, because all methods related to the class are in one place. + + +# Exercises-2 +# Change the Person class to take a date of birth (using the standard library’s datetime.date class) +# and store it in a field instead of age. +# +# Update the is_adult method to act the same as before. + +from datetime import date + +class Person: + def __init__(self, name: str, date_of_birth: date, preferred_operating_system: str): + self.name = name + self.date_of_birth = date_of_birth + self.preferred_operating_system = preferred_operating_system + + def is_adult(self) -> bool: + today = date.today() + age = today.year - self.date_of_birth.year + + if(today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day): + age -= 1 + + return age >= 18 + +imran = Person("Imran", date(2003, 6, 20), "Ubuntu") +print(imran.is_adult()) From 27eb06f7eab355085b7f0876a6be395e502d698e Mon Sep 17 00:00:00 2001 From: Konvaly Date: Sun, 31 May 2026 14:26:02 +0100 Subject: [PATCH 11/15] Complete task-5 Dataclasses --- sprint-5-prep/5-dataclasses.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 sprint-5-prep/5-dataclasses.py diff --git a/sprint-5-prep/5-dataclasses.py b/sprint-5-prep/5-dataclasses.py new file mode 100644 index 0000000..9ebbb12 --- /dev/null +++ b/sprint-5-prep/5-dataclasses.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass +from datetime import date + +@dataclass(frozen=True) +class Person: + name: str + date_of_birth: date + preferred_operating_system: str + + def is_adult(self) -> bool: + today = date.today() + + age = today.year - self.date_of_birth.year + if (today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day): + age -= 1 + return age >= 18 + + +zoi = Person("Zoi", date(2003, 3, 23), "Ubuntu") + +print("zoi:", zoi) +print("zoi.is_adult():", zoi.is_adult()) \ No newline at end of file From 8654138627c6c44734b83c0ef59b68c7336cdffe Mon Sep 17 00:00:00 2001 From: Konvaly Date: Sun, 31 May 2026 14:45:51 +0100 Subject: [PATCH 12/15] Complete task-6 Generics --- sprint-5-prep/6-generics.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 sprint-5-prep/6-generics.py diff --git a/sprint-5-prep/6-generics.py b/sprint-5-prep/6-generics.py new file mode 100644 index 0000000..8bbece5 --- /dev/null +++ b/sprint-5-prep/6-generics.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass +from typing import List + +@dataclass(frozen=True) +class Person: + name: str + children: list["Person"] + age: int + +fatma = Person(name="Fatma", age=9, children=[]) +aisha = Person(name="Aisha", age=5, children=[]) + +imran = Person(name="Imran", age=35, children=[fatma, aisha]) + +def print_family_tree(person: Person) -> None: + print(person.name) + for child in person.children: + print(f"- {child.name} ({child.age})") + +print_family_tree(imran) \ No newline at end of file From a4c9cf309b9eddb3b2fe377d70a3c5a64955bea9 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Sun, 31 May 2026 15:05:43 +0100 Subject: [PATCH 13/15] Complete task-7 Type-guided refactorings --- sprint-5-prep/7-type-guided-refactorings.py | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 sprint-5-prep/7-type-guided-refactorings.py diff --git a/sprint-5-prep/7-type-guided-refactorings.py b/sprint-5-prep/7-type-guided-refactorings.py new file mode 100644 index 0000000..1d098e4 --- /dev/null +++ b/sprint-5-prep/7-type-guided-refactorings.py @@ -0,0 +1,42 @@ +from dataclasses import dataclass +from typing import List + +@dataclass(frozen=True) +class Person: + name: str + age: int + preferred_operating_systems: List[str] + + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: str + + +def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]: + possible_laptops = [] + for laptop in laptops: + if laptop.operating_system in person.preferred_operating_systems: + possible_laptops.append(laptop) + return possible_laptops + + +people = [ + Person(name="Imran", age=22, preferred_operating_systems=["Ubuntu"]), + Person(name="Eliza", age=34, preferred_operating_systems=["Arch Linux"]), +] + +laptops = [ + Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system="Arch Linux"), + Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="Ubuntu"), + Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="ubuntu"), + Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system="macOS"), +] + +for person in people: + possible_laptops = find_possible_laptops(laptops, person) + print(f"Possible laptops for {person.name}: {possible_laptops}") \ No newline at end of file From 49db0115937a308a43d70614202c4f165d02e887 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Sun, 31 May 2026 22:37:09 +0100 Subject: [PATCH 14/15] Complete task-8 Enums --- sprint-5-prep/8-enums.py | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 sprint-5-prep/8-enums.py diff --git a/sprint-5-prep/8-enums.py b/sprint-5-prep/8-enums.py new file mode 100644 index 0000000..7b666ef --- /dev/null +++ b/sprint-5-prep/8-enums.py @@ -0,0 +1,79 @@ +from dataclasses import dataclass +from enum import Enum +from typing import List +import sys + +class OperatingSystem(Enum): + MACOS = "macOS" + ARCH = "Arch Linux" + UBUNTU = "Ubuntu" + +@dataclass(frozen=True) +class Person: + name: str + age: int + preferred_operating_system: OperatingSystem + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: OperatingSystem + +os_input_name = input("Your name is: ") + +os_input_age = input("Your age is: ") +try: + age = int(os_input_age) +except ValueError: + print(f"Error: '{os_input_age}' is not a valid age", file=sys.stderr) + sys.exit(1) + +os_input_preferred_os = input("Preferred operating system is: ") +try: + preferred_operating_system = OperatingSystem(os_input_preferred_os) +except ValueError: + print(f"Error: '{os_input_preferred_os}' is not a valid operating system", file=sys.stderr) + sys.exit(1) + +person = Person(name = os_input_name, age=age, preferred_operating_system=preferred_operating_system) + +laptops = [ + Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system=OperatingSystem.ARCH), + Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU), + Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU), + Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS), + Laptop(id=5, manufacturer="Apple", model="macBook", screen_size_in_inches=14, operating_system=OperatingSystem.MACOS), + Laptop(id=6, manufacturer="Apple", model="macBook", screen_size_in_inches=15, operating_system=OperatingSystem.MACOS), +] + +def find_specific_os(laptops: List[Laptop], preferred_os: OperatingSystem) -> List[Laptop]: + possible_laptops = [] + + for laptop in laptops: + if (laptop.operating_system == preferred_os): + possible_laptops.append(laptop) + + possible_laptops_amount = len(possible_laptops) + + print(f"The amount of possible laptops with preferred operating system is", possible_laptops_amount) + + return possible_laptops + +result = find_specific_os(laptops, preferred_operating_system) + +counts_laptop_per_os: dict[OperatingSystem, int] = {} + +for laptop in laptops: + os = laptop.operating_system + if os in counts_laptop_per_os: + counts_laptop_per_os[os] += 1 + else: + counts_laptop_per_os[os] = 1 + +top_os = max(counts_laptop_per_os, key=lambda os: counts_laptop_per_os[os]) + +if top_os != preferred_operating_system: + print(f"If you're willing to use {top_os.value}, you're more likely to get a laptop ({counts_laptop_per_os[top_os]} available)") \ No newline at end of file From 7343b299ba19f3291fdcdc0db418ec5f09f37755 Mon Sep 17 00:00:00 2001 From: Konvaly Date: Sun, 31 May 2026 22:59:05 +0100 Subject: [PATCH 15/15] Complete task-9 Inheritance --- sprint-5-prep/9-inheritance.py | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 sprint-5-prep/9-inheritance.py diff --git a/sprint-5-prep/9-inheritance.py b/sprint-5-prep/9-inheritance.py new file mode 100644 index 0000000..af08fd1 --- /dev/null +++ b/sprint-5-prep/9-inheritance.py @@ -0,0 +1,67 @@ +class Parent: + def __init__(self, first_name: str, last_name: str): + self.first_name = first_name + self.last_name = last_name + + def get_name(self) -> str: + return f"{self.first_name} {self.last_name}" + + +class Child(Parent): + def __init__(self, first_name: str, last_name: str): + super().__init__(first_name, last_name) + self.previous_last_names = [] + + def change_last_name(self, last_name) -> None: + self.previous_last_names.append(self.last_name) + self.last_name = last_name + + def get_full_name(self) -> str: + suffix = "" + if len(self.previous_last_names) > 0: + suffix = f" (née {self.previous_last_names[0]})" + return f"{self.first_name} {self.last_name}{suffix}" + + +# --- Predictions --- + +person1 = Child("Elizaveta", "Alekseeva") + +print(person1.get_name()) +# Prediction: "Elizaveta Alekseeva" +# Reason: Child inherits get_name() from Parent, no name change yet + +print(person1.get_full_name()) +# Prediction: "Elizaveta Alekseeva" +# Reason: no previous last names, so suffix is empty string + +person1.change_last_name("Tyurina") +# Saves "Alekseeva" into previous_last_names list, changes last_name to "Tyurina" + +print(person1.get_name()) +# Prediction: "Elizaveta Tyurina" +# Reason: get_name() uses self.last_name which is now "Tyurina" + +print(person1.get_full_name()) +# Prediction: "Elizaveta Tyurina (nee Alekseeva)" +# Reason: previous_last_names is not empty, so suffix shows the original last name + +person2 = Parent("Elizaveta", "Alekseeva") + +print(person2.get_name()) +# Prediction: "Elizaveta Alekseeva" +# Reason: Parent has get_name(), works normally + +# print(person2.get_full_name()) +# Prediction: ERROR - AttributeError +# Reason: Parent class does not have get_full_name() method, only Child does + +# person2.change_last_name("Tyurina") +# Prediction: ERROR - AttributeError +# Reason: Parent class does not have change_last_name() method, only Child does + +# print(person2.get_name()) +# Would print "Elizaveta Alekseeva" but we can't reach this line because of the error above + +# print(person2.get_full_name()) +# Would also error - Parent still doesn't have get_full_name() \ No newline at end of file