From f4d6d6d2e0fde2c48c74e2eeb4a5a16ddaadaa2f Mon Sep 17 00:00:00 2001 From: SAN-MUYUN Date: Sat, 1 Nov 2025 11:08:41 +0800 Subject: [PATCH] Implement exercise view-commits --- view_commits/.gitmastery-exercise.json | 19 ++ view_commits/README.md | 7 + view_commits/__init__.py | 0 view_commits/download.py | 8 + view_commits/res/answers.txt | 16 ++ view_commits/tests/__init__.py | 0 view_commits/tests/specs/base.yml | 6 + .../tests/specs/extra_question_three.yml | 27 +++ .../tests/specs/incomplete_answers.yml | 22 +++ .../tests/specs/incomplete_question_three.yml | 22 +++ view_commits/tests/specs/no_answers.yml | 22 +++ view_commits/tests/specs/valid_answers.yml | 26 +++ .../tests/specs/wrong_question_one.yml | 25 +++ .../tests/specs/wrong_question_three.yml | 26 +++ .../tests/specs/wrong_question_two.yml | 26 +++ view_commits/tests/test_verify.py | 173 ++++++++++++++++++ view_commits/verify.py | 31 ++++ 17 files changed, 456 insertions(+) create mode 100644 view_commits/.gitmastery-exercise.json create mode 100644 view_commits/README.md create mode 100644 view_commits/__init__.py create mode 100644 view_commits/download.py create mode 100644 view_commits/res/answers.txt create mode 100644 view_commits/tests/__init__.py create mode 100644 view_commits/tests/specs/base.yml create mode 100644 view_commits/tests/specs/extra_question_three.yml create mode 100644 view_commits/tests/specs/incomplete_answers.yml create mode 100644 view_commits/tests/specs/incomplete_question_three.yml create mode 100644 view_commits/tests/specs/no_answers.yml create mode 100644 view_commits/tests/specs/valid_answers.yml create mode 100644 view_commits/tests/specs/wrong_question_one.yml create mode 100644 view_commits/tests/specs/wrong_question_three.yml create mode 100644 view_commits/tests/specs/wrong_question_two.yml create mode 100644 view_commits/tests/test_verify.py create mode 100644 view_commits/verify.py diff --git a/view_commits/.gitmastery-exercise.json b/view_commits/.gitmastery-exercise.json new file mode 100644 index 0000000..28fcfdd --- /dev/null +++ b/view_commits/.gitmastery-exercise.json @@ -0,0 +1,19 @@ +{ + "exercise_name": "view-commits", + "tags": [ + "git-diff", + "git-show" + ], + "requires_git": true, + "requires_github": true, + "base_files": { + "answers.txt": "answers.txt" + }, + "exercise_repo": { + "repo_type": "remote", + "repo_name": "duty-roster", + "repo_title": "gm-duty-roster", + "create_fork": false, + "init": true + } +} \ No newline at end of file diff --git a/view_commits/README.md b/view_commits/README.md new file mode 100644 index 0000000..91dffaf --- /dev/null +++ b/view_commits/README.md @@ -0,0 +1,7 @@ +# view-commits + +The duty-roster repo contains five files, each one containing names of people assigned to a duty roster for a specific day of the week. For example, Monday.txt contains names of people assigned to duties on Mondays. These files are updated monthly, to record any changes to duty assignments. + +## Task + +Answer the questions in the answers.txt, by examining the relevant commits in the duty-roster repo. diff --git a/view_commits/__init__.py b/view_commits/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/view_commits/download.py b/view_commits/download.py new file mode 100644 index 0000000..7fa35d0 --- /dev/null +++ b/view_commits/download.py @@ -0,0 +1,8 @@ +from exercise_utils.cli import run_command +from exercise_utils.file import append_to_file, create_or_update_file +from exercise_utils.gitmastery import create_start_tag + +def setup(verbose: bool = False): + create_start_tag(verbose) + + diff --git a/view_commits/res/answers.txt b/view_commits/res/answers.txt new file mode 100644 index 0000000..66ad1e1 --- /dev/null +++ b/view_commits/res/answers.txt @@ -0,0 +1,16 @@ +Q: In February, who was replaced in the Wednesday duty roster? +A: + +Q: In February, who joined the Tuesday duty roster? +A: + +Q: In April, what were the new names added to the duty rosters? Remove/add extra rows where appropriate. +A: +- name1 +- name2 + +Q: In January, who were in the Tuesday duty roster? Remove/add extra rows where appropriate. +A: +- name1 +- name2 + diff --git a/view_commits/tests/__init__.py b/view_commits/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/view_commits/tests/specs/base.yml b/view_commits/tests/specs/base.yml new file mode 100644 index 0000000..00c3a53 --- /dev/null +++ b/view_commits/tests/specs/base.yml @@ -0,0 +1,6 @@ +initialization: + steps: + - type: commit + empty: true + message: Empty commit + id: start diff --git a/view_commits/tests/specs/extra_question_three.yml b/view_commits/tests/specs/extra_question_three.yml new file mode 100644 index 0000000..fdfbadc --- /dev/null +++ b/view_commits/tests/specs/extra_question_three.yml @@ -0,0 +1,27 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Eric + + Q: In February, who joined the Tuesday duty roster? + A: Bruce + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + - Betsy + - Beth + - Daisy + - Eric + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: + - Charlie + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/incomplete_answers.yml b/view_commits/tests/specs/incomplete_answers.yml new file mode 100644 index 0000000..07cb5c0 --- /dev/null +++ b/view_commits/tests/specs/incomplete_answers.yml @@ -0,0 +1,22 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Eric + + Q: In February, who joined the Tuesday duty roster? + A: Bruce + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/incomplete_question_three.yml b/view_commits/tests/specs/incomplete_question_three.yml new file mode 100644 index 0000000..41fdfbb --- /dev/null +++ b/view_commits/tests/specs/incomplete_question_three.yml @@ -0,0 +1,22 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Eric + + Q: In February, who joined the Tuesday duty roster? + A: Bruce + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: Betsy Daisy + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: Charlie + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/no_answers.yml b/view_commits/tests/specs/no_answers.yml new file mode 100644 index 0000000..fa04a68 --- /dev/null +++ b/view_commits/tests/specs/no_answers.yml @@ -0,0 +1,22 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: + + Q: In February, who joined the Tuesday duty roster? + A: + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/valid_answers.yml b/view_commits/tests/specs/valid_answers.yml new file mode 100644 index 0000000..9e3c8cf --- /dev/null +++ b/view_commits/tests/specs/valid_answers.yml @@ -0,0 +1,26 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Eric + + Q: In February, who joined the Tuesday duty roster? + A: Bruce + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + - Betsy + - Beth + - Daisy + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: + - Charlie + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/wrong_question_one.yml b/view_commits/tests/specs/wrong_question_one.yml new file mode 100644 index 0000000..649e0a0 --- /dev/null +++ b/view_commits/tests/specs/wrong_question_one.yml @@ -0,0 +1,25 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Ergodic + + Q: In February, who joined the Tuesday duty roster? + A: Bruce + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + - Betsy + - Daisy + - Beth + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: Charlie + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/wrong_question_three.yml b/view_commits/tests/specs/wrong_question_three.yml new file mode 100644 index 0000000..5f68734 --- /dev/null +++ b/view_commits/tests/specs/wrong_question_three.yml @@ -0,0 +1,26 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Eric + + Q: In February, who joined the Tuesday duty roster? + A: Bruce + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + - Betsy + - Bruce + - Daisy + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: + - Charlie + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/specs/wrong_question_two.yml b/view_commits/tests/specs/wrong_question_two.yml new file mode 100644 index 0000000..e2c6cda --- /dev/null +++ b/view_commits/tests/specs/wrong_question_two.yml @@ -0,0 +1,26 @@ +initialization: + steps: + - type: new-file + filename: answers.txt + contents: | + Q: In February, who was replaced in the Wednesday duty roster? + A: Eric + + Q: In February, who joined the Tuesday duty roster? + A: Bru + + Q: In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces. + A: + - Betsy + - Beth + - Daisy + + Q: In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces. + A: + - Charlie + - type: add + files: + - answers.txt + - type: commit + message: Initial commit + id: start \ No newline at end of file diff --git a/view_commits/tests/test_verify.py b/view_commits/tests/test_verify.py new file mode 100644 index 0000000..dd8130b --- /dev/null +++ b/view_commits/tests/test_verify.py @@ -0,0 +1,173 @@ +from git_autograder import GitAutograderStatus, GitAutograderTestLoader, assert_output +from git_autograder.answers.rules import HasExactValueRule, HasExactListRule, NotEmptyRule, ContainsListRule +from ..verify import QUESTION_ONE, QUESTION_TWO, QUESTION_THREE, QUESTION_FOUR, verify + +REPOSITORY_NAME = "view-commits" + +loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify) + +CORRECT_QUESTION_THREE = """ +- Betsy +- Beth +- Daisy +""" + +INCOMPLETE_QUESTION_THREE = """ +- Betsy +- Daisy +""" + +WRONG_QUESTION_THREE = """ +- Betsy +- Bruce +- Daisy +""" + +EXTRA_QUESTION_THREE = """ +- Betsy +- Beth +- Eric +- Daisy +""" + +def test_no_answers(): + with loader.load( + "specs/no_answers.yml", + mock_answers={ + QUESTION_ONE: "", + QUESTION_TWO: "", + QUESTION_THREE: "", + QUESTION_FOUR: "" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + NotEmptyRule.EMPTY.format(question=QUESTION_ONE), + NotEmptyRule.EMPTY.format(question=QUESTION_TWO), + NotEmptyRule.EMPTY.format(question=QUESTION_THREE), + NotEmptyRule.EMPTY.format(question=QUESTION_FOUR) + ], + ) + +def test_incomplete_answer(): + with loader.load( + "specs/incomplete_answers.yml", + mock_answers={ + QUESTION_ONE: "Eric", + QUESTION_TWO: "Bruce", + QUESTION_THREE: "", + QUESTION_FOUR: "" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + NotEmptyRule.EMPTY.format(question=QUESTION_THREE), + NotEmptyRule.EMPTY.format(question=QUESTION_FOUR) + ], + ) + +def test_wrong_question_one(): + with loader.load( + "specs/wrong_question_one.yml", + mock_answers={ + QUESTION_ONE: "Ergodic", + QUESTION_TWO: "Bruce", + QUESTION_THREE: CORRECT_QUESTION_THREE, + QUESTION_FOUR: "- Charlie" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + HasExactValueRule.NOT_EXACT.format(question=QUESTION_ONE) + ], + ) + +def test_wrong_question_two(): + with loader.load( + "specs/wrong_question_two.yml", + mock_answers={ + QUESTION_ONE: "Eric", + QUESTION_TWO: "Bru", + QUESTION_THREE: CORRECT_QUESTION_THREE, + QUESTION_FOUR: "- Charlie" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + HasExactValueRule.NOT_EXACT.format(question=QUESTION_TWO) + ], + ) + +def test_incomplete_question_three(): + with loader.load( + "specs/incomplete_question_three.yml", + mock_answers={ + QUESTION_ONE: "Eric", + QUESTION_TWO: "Bruce", + QUESTION_THREE: INCOMPLETE_QUESTION_THREE, + QUESTION_FOUR: "- Charlie" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + HasExactListRule.INCORRECT_UNORDERED.format(question=QUESTION_THREE) + ], + ) + +def test_wrong_question_three(): + with loader.load( + "specs/wrong_question_three.yml", + mock_answers={ + QUESTION_ONE: "Eric", + QUESTION_TWO: "Bruce", + QUESTION_THREE: WRONG_QUESTION_THREE, + QUESTION_FOUR: "- Charlie" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + HasExactListRule.INCORRECT_UNORDERED.format(question=QUESTION_THREE), + ], + ) + +def test_wrong_question_three_extra_answer(): + with loader.load( + "specs/extra_question_three.yml", + mock_answers={ + QUESTION_ONE: "Eric", + QUESTION_TWO: "Bruce", + QUESTION_THREE: EXTRA_QUESTION_THREE, + QUESTION_FOUR: "- Charlie" + }, + ) as output: + assert_output( + output, + GitAutograderStatus.UNSUCCESSFUL, + [ + ContainsListRule.INVALID_ITEM.format(question=QUESTION_THREE) + ], + ) + +def test_valid_answers(): + with loader.load( + "specs/valid_answers.yml", + mock_answers={ + QUESTION_ONE: "Eric", + QUESTION_TWO: "Bruce", + QUESTION_THREE: CORRECT_QUESTION_THREE, + QUESTION_FOUR:"- Charlie" + }, + ) as output: + assert_output(output, GitAutograderStatus.SUCCESSFUL) \ No newline at end of file diff --git a/view_commits/verify.py b/view_commits/verify.py new file mode 100644 index 0000000..ce5a8b4 --- /dev/null +++ b/view_commits/verify.py @@ -0,0 +1,31 @@ +from git_autograder import ( + GitAutograderOutput, + GitAutograderExercise, + GitAutograderStatus, +) +from git_autograder.answers.rules import HasExactValueRule, NotEmptyRule, HasExactListRule, ContainsListRule + +QUESTION_ONE = "In February, who was replaced in the Wednesday duty roster?" +QUESTION_TWO = "In February, who joined the Tuesday duty roster?" +QUESTION_THREE = "In April, what were the new names added to the duty rosters? Give the list of names as one line, separated by spaces." +QUESTION_FOUR = "In January, who were in the Tuesday duty roster? Give the list of names as one line, separated by spaces." + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + ( + exercise.answers.add_validation(QUESTION_ONE, NotEmptyRule()) + .add_validation(QUESTION_ONE, HasExactValueRule("Eric", is_case_sensitive=True)) + .add_validation(QUESTION_TWO, NotEmptyRule()) + .add_validation(QUESTION_TWO, HasExactValueRule("Bruce", is_case_sensitive=True)) + .add_validation(QUESTION_THREE, NotEmptyRule()) + .add_validation(QUESTION_THREE, HasExactListRule(["Betsy", "Beth", "Daisy"], is_case_sensitive=True)) + .add_validation(QUESTION_THREE, ContainsListRule(["Betsy", "Beth", "Daisy"], subset=True, is_case_sensitive=True)) + .add_validation(QUESTION_FOUR, NotEmptyRule()) + .add_validation(QUESTION_FOUR, HasExactListRule(["Charlie"], is_case_sensitive=True)) + .add_validation(QUESTION_FOUR, ContainsListRule(["Charlie"], subset=True, is_case_sensitive=True)) + .validate() + ) + + return exercise.to_output( + ["Great work in viewing and understanding the diff of a specific commit!"], + GitAutograderStatus.SUCCESSFUL, + )