1202 views
owned this note
<style>h3{border-bottom: 3px solid black}</style>
# **Homework 4: Python Practice**
**Due**: Friday, November 10, 2023 at 11:59 p.m.
# Overview
One way to get started learning a new language is to read code in that language. In this assignment, we have you do this in a controlled way, by giving you a buggy starter file that you write tests for based on specification and debug using the Python documentation as a resource. Once you’ve gotten a bit of comfort modifying others’ code, you’re asked to write some code of your own.
:::warning
### Important Python Resources
The majority of the work for this assignment will come from reading, testing, and debugging a file we give you.
We are intentionally giving you less guidance on Python for this reason--we expect you to use the resources you have available, including:
- **[Our Python Stencil setup guide](https://docs.cs200.io/s/python-stencil-setup-guide)** (for how to load the stencil)
- **[Our guide for going to Python from Java](https://docs.google.com/document/d/1wwOL1PsgdfeAzv77m6iBNzwX0m9HV5i_nJoKKZad_tY/edit?usp=sharing)** which includes links to the Python documentation that you should refer to
- **[Our Python Testing Guide](https://docs.cs200.io/s/python-testing-guide)**: how to run and debug tests, as well as this [annotated VSCode debugging guide](https://brown-csci0200.github.io/assets/lectures/23python1/Lec25_whiteboard_f22.pdf) from lecture
- **[Our Python Style Guide](https://docs.google.com/document/d/1yadShv9w5oe_NWIt4fDk-DG_neBIy0AdX4mer2S0O3A/edit?usp=sharing)**: how to format your Python code
<br />
**Why is the assignment structured this way?** At this point in the course, you know most of the programming concepts (what a loop is, what a class is, what a list is, etc) to pick up Python, and you mostly need to learn to translate the syntax differences between Java and Python.
**Some common challenges that you might encounter as you start using CS in the “real world” are being able to read language documentation, and being able to read other people’s code.** We have you practice these simultaneously as a way of starting to pick up Python syntax!
:::
This [HW4 FAQ](https://edstem.org/us/courses/32789/discussion/2879186) post will be maintained on Ed.
# Learning objectives
- Write comprehensive test cases based on function specification in order to debug existing code
- Learn list comprehension in order to reduce total lines of code
- Understand python sets, lists, and dictionaries and learn how to manipulate them
- Be able to understand already written Python code and what it does
- Use Python libraries to read command-line arguments and process files
:::warning
<b>Different Ed and Hours Policy: </b>In Ed and hours, the TAs will not tell you what test cases to write or how to fix the bugs in the <code>hw4.py</code> file we give you. You <b>can</b> use Ed and hours for:
</p>
<ul><li>Asking for clarification on <i>specific</i> Python documentation</li>
<li>Asking for guidance in interpreting the debugger (but you should not ask the TA to run through the debugger for you)</li>
<li>Asking how to translate a specific Java/programming concept to Python</li>
<li>Asking for help debugging the additional file you write (<code>hw4_repl.py</code>)</li>
</ul>
:::
# Stencil and Setup Logistics
You can download the stencil code using this **[Github Classroom](https://classroom.github.com/a/d1f6m-y0)** link.
:::warning
:warning: **Warning**: Since this is our first Python assignment, **be sure to read the [**Python Stencil Setup Guide**](https://docs.cs200.io/s/python-stencil-setup-guide)** for important instructions on how to set up VSCode to use the stencil. In particular, **make sure you [set up your environment](https://docs.cs200.io/#Setting-up-your-environment)**--if you don't this, you won't be able to run any tests!
:::
# Writing tests from specification
### Task 1-A
Open up the starter code file called `hw4.py`. **This file contains many bugs, and the code behavior does not match the documentation!**
Similar to javadoc, python classes and methods have a *docstring* that describes what each class/method should do, what arguments it takes, and what it returns. **Read every docstring and familiarize yourself with what the code is meant to do.**
### Task 1-B
Based on the _specification_ (docstrings), write tests for the code. See instructions below for guidance on how to think about writing tests.
Overall, your tests should:
- Pass only *well-formed* input into the `Group` constructor, for now (see note below for guidance).
- Test every method (except for `populate_skills`, which is a helper method to the constructor)
- Test what you perceive might be common interactions between methods
- Test edge cases based on the docstrings (for example, cases where a given input does not exist in the data)
<details><summary><b><i>Note:</i> What is well-formed input?</b></summary>
Well-formed input is a list of dictionaries, where each dictionary representing a person has the keys “name” (mapping to a string), “time in group” (mapping to a float), and “skills” (mapping to a list of strings). See <code>test_hw4.py</code> for an example of such an input.
</details>
<details><summary><b><i>Note:</i> What does <code>import hw4 as h4</code> mean?</b></summary>
To use definitions (classes, methods/functions) from another file, you need to <i>import</i> that file. We will see multiple ways of importing files as we finish up the semester, but this particular line lets you refer to the definitions in hw4.py by prepending <code>h4</code>. to them (for example, to create a new Group, use <code>h4.Group(...)</code>). In the next section, you can see how this comes in handy.
</details>
<br />
:::info
<b><i>Important</i>: Instructions for Writing Tests</b>
We’ve given you a testing file to start with that shows common ways to set up tests and use assertions, and you should refer to our <a href="https://docs.google.com/document/d/1MQOK-lUaqbdOGZGp5X5uN92qM7VnnHLNd4RRrnHBulE/edit?usp=sharing">Python testing guide</a> for more guidance.
<b>For readability, please remove the <code>test_example</code> function from this file after you’re done reading it</b>, and make your own test functions. Our testing guide contains the contents of this example test if you need to refer to it after you’ve removed it from the stencil code.
<p style="height: 10px">
</p>
If there are stated assumptions in the documentation, you do not have to test for violations of those assumptions. For example, for simplicity, we assume that all names are unique, so you do not have to write test cases where there are two instances of the same name in a group. You can also assume that the constructor (and helper <code>populate_skills</code> method) are correct (free of bugs) for well-formed inputs.
<p style="height: 17px">
</p>
<b>You shouldn’t be changing <code>hw4.py</code> at this point (e.g. to add getters or setters), nor should you be testing field values directly (e.g., writing something like <code>student.numcourses == 3</code>)</b> – just like we did for Java, if you want to check that the side effects of a method are correct, try creating assertions for other methods. For example, after you call <code>add_skill</code>, how can you use <code>measure_progress</code> to make sure that a duplicate skill was not added?
<p style="height: 17px">
</p>
When you run these tests locally on the code we have given you, <b>they will not all pass</b>. That’s the point! The code we have given you is buggy, meaning that the observed behavior doesn’t match the specification. Your job in the next few tasks will be to fix the code using your tests and the debugger.
:::
### Task 1-C
Submit your testing file to the Gradescope assignment called **Homework 4: Testing**, which uses wheat/chaff grading to tell you if you’ve caught all the bugs in the file. This ensures that your set of tests is complete enough to move on to the next step. To save yourself time, verify that you have all of the points for this task before moving on to the next one.
:::warning
<b><i>Important</i>: Ed Policy Reminder</b>
As stated at the start of this document, the Ed policy for <i>the first two</i> sections of the assignment is a bit different than for previous assignments. The TAs cannot tell you which exact tests you missed (but you can ask for some hints), and they cannot solve the bugs for you. Since we are giving you a buggy but mostly syntactic code, your TAs cannot give much more than hints in the right direction. Part of the goal here is to get you more comfortable using tests and the debugger to solve bugs on your own, and more comfortable with reading the documentation for a new language. Remember that you can (and should!) refer to the resources linked at the top of this page.
<b>We are not trying to trick you here – all of the chaffs should be catch-able if you think through the expected behaviors and the edge cases of the code.</b>
:::
# Testing and debugging others' code
### Task 2-A
Use your testing file and the VScode debugger to debug and fix the bugs in <code>hw4.py</code>, so that all of your tests pass. **Read task 2-B before starting, because you are asked to document your debugging process.**
:::info
<details><summary><b>Debugging Tip</b></summary>
You might find yourself editing your testing file as you find and fix bugs. If you want to run tests locally on the buggy file, we suggest making a copy of <code>hw4.py</code> <i>before</i> changing it (for example, copying the file to <code>hw4_buggy.py</code> in the same folder, and then making changes to <code>hw4.py</code>). Then, you can switch between which file you are testing by changing just the import line
<br>
<br>
<code>import hw4 as h4</code> (for the file with your fixes) vs. <code>import hw4_buggy as h4</code> (for the file with bugs)
<br>
<br>
Nothing else in the testing file needs to change, because all of your tests just reference the name <code>h4</code>!
<br>
<br>
<b>When you submit your work, make sure that your final test submission to Gradescope has</b> <code>import hw4 as h4</code> <b>at the top to ensure we use your fixed version when grading!</b>
:::
</details>
:::info
<details><summary><b>Seeing new Python syntax for the first time?</b></summary>
While doing this, you may encounter Python syntax that you are not familiar with – that’s the point! Read through the resources we gave you to try to interpret the syntax, and if you’re really stuck, ask for some help on Ed. While long, the Python built-in types documentation is your friend here.
For example, if you encounter syntax like <code>my_list[1:3]</code>, which is very different from what we’ve seen in Java, try scrolling through the built-in types documentation until you encounter something that looks like this syntax. Hint: it appears in a helpful table in a section for list operations, and it has something to do with slicing.
Once you find the documentation and read what it does, we encourage you to play around with it – make a new file and try making your own data structures that call on the new syntax in multiple ways. Just like we did in class, you can also put a `pass` statement at the end of any file, debug the file, and then use the debug console to try out the new syntax:

:::
</details>
### Task 2-B
Create a document with a series of screenshots/images that shows how you debugged `add_skill`.
You should have screenshots/images for:
1. The failing test function that catches the bug (with a caption that gives the line number of the assertion that failed in pytest, if there is more than one assertion in the test function)
2. A drawing of what you expect the field(s) of `Group` to look like after you run `add_skill` with the inputs you used in your test function. This doesn’t have to follow a specific format, but make sure we can understand what is a list, what is a dictionary, etc.
3. What the debugging window looked like when you found the bug. Make sure this includes:
- The code (so that we see which line the debugger is on)
- Any relevant variables expanded in the variables pane to the left (“relevant variables” are those that do not match the drawing in part 2)
Write a short (3-5 sentence) caption that explains how the screenshot from (3) demonstrates the issue with the code and how it contributes to the failing assertion from (1).
If there were multiple issues with `add_skill`, include a debug screenshot and explanation for each one.
Save the screenshots and captions in a file called `hw4_debug.pdf`.
# Writing your own code
:::info
**Note**: The following tasks ask you to modify the code we have given you. After you make your modifications, you should make sure the tests you wrote in the previous part still pass. The point is for you to use your tests to help you fix your implementation!
:::
</details>
### Task 3-A
Change the `average_length_of_membership` method to use a list comprehension instead of a loop. All of your tests should still pass after you complete this task, but there should be no for-loops in your code.
:::success
<details><summary><b><i>Hint:</i> Task 3-A</b></summary>
There is a built-in function to add up all of the elements of a list. Read the documentation to find it.
:::
</details>
### Task 3-B
Add a method called `people_with_skill` to the `Group` class that takes in a skill (as a string) and returns a list of all of the names of people with that skill. If the skill is not found, this method should return an empty list. The order of the names does not have to be the same as they appear in `self.person_list`. Add test(s) for this method in your test file.
### Task 3-C
Add a method called `validate_data` to the Group class that takes in no inputs (except for self, which is an input to all methods in a class).
:::info
<details><summary><b><i>Details</i></b></summary>
It should go through each `person` in `self.person_list` and:
- Verify that the `person` is a dictionary
- Verify that the `person` has the keys `"name"`, `"time in group"`, and `"skills"`
- Cast `person["name"]` to a string
- Cast `person["time in group"]` to a float
- Verify that `person["skills"]` is a **list** of **strings**
If any of these attributes are violated, the method should `raise ValueError("Data unable to validate")`
:::
</details>
:::info
<details><summary><b><i>Note:</i> Type-Checking and Casting</b></summary>
To check if something is of a certain type in Python, you can use <code>type()</code>:
<br>
<code>my_list = ["a", "b", "c"]</code>
<br>
<code>assert type(my_list) is list # will pass</code>
<br>
<code>assert type(my_list) is dict # will fail</code>
<br>
<br>
To cast something to another type, you can use <code>float()</code>, <code>int()</code>, <code>str()</code>, etc:
<br>
<code>val1 = "0.1"</code>
<br><code>val2 = 0.1</code>
<br><code>val3 = "oh point one"</code>
<br><br><code>res1 = float(val1) # will be a float with value 0.1</code>
<br><code>res2 = float(val2) # will be a float with value 0.1</code>
<br><code>res3 = float(val3) # will raise a ValueError</code>
:::
</details>
Call this method from the constructor, and write test(s) for the constructor to make sure the validation works.
# Working with Python libraries and I/O
In the next tasks, you'll gain some practice working with some fundamental python libraries to read files. You'll also practice reading a JSON file, which is a common way to specify data in a text format, like a CSV file. You can read more about JSON files [here](https://www.w3schools.com/whatis/whatis_json.asp).
### Task 4-A
Create a new file called `hw4_repl.py`. At the top of the file, add these import statements:
```python
import json
import sys
from hw4 import Group
```
This file will not have any class definitions. Unlike in Java, in Python, you can create a file without any class definitions, or even function/method definitions! You can write code in this file similar to how you would write code for a main method in Java, and it will run.
### Task 4-B
Create a loop that goes through all of the command-line arguments and prints them out, one-by-one. The list of command-line arguments are in `sys.argv`, which is a list that you can use similarly to how you used args (as in `public static void main(String[] args)`) in Java.
:::info
**Example**: The intended behavior when you run this file from the terminal is:
```
hw04-stencil-yourname> python hw4_repl.py
hw04-stencil-yourname> python hw4_repl.py arg1 arg2 arg3
arg1
arg2
arg3
```
:::
</details>
:::success
<details><summary><b><i>Hint: </i>Task 4-B</b></summary>
The first element of sys.argv will be the name of the Python file. You should not print this out. How can you use something you learned in the previous sections about getting a subset of a list to skip the first element of sys.argv when printing?
:::
</details>
For instructions on how to run Python from a terminal, see [here](https://docs.cs200.io/s/python-stencil-setup-guide#When-you-need-it-Running-Python-from-a-terminal).
### Task 4-C
Now, modify your loop from Task 4-B so that it reads in each command-line argument as a JSON file.
:::info
**What is JSON?** JSON stands for [JavaScript Object Notation](https://json.org/example.html) and is a standardized format for representing data as lists, dictionaries, and values. Conveniently, the syntax looks very much like Python syntax. You can take a look at `example_data.json` in the stencil code for an example.
:::
For each command-line argument, convert it to a JSON object and use that as an input to the constructor for a Group. For example, if you have two well-formed JSON files as the command-line arguments, you should create two new Group objects, with the contents of each JSON as the input to the constructor. Put all of the constructed Group objects into a list.
:::info
**Example**: Here is an example of reading in a JSON file and working with the resulting object, which is in this case is a list of dictionaries:
```python
with open("example_data.json", "r") as json_file:
person_json = json.load(json_file)
print(person_json[0])
print(person_json[0]["name"])
my_group = Group(person_json) # does not print anything, but runs successfully
```
When we run this code, we get the output:
```
{'name': 'Sebastian', 'time in group': 2.3, 'skills': ['sleeping', 'screeching']}
Sebastian
```
:::
:::info
<details><summary><b><i>Guidelines: </i>Task 4-C</b></summary>
Your code should:
- Ignore any file that doesn’t exist
- Ignore any file that is not parseable as a JSON object
- Ignore any file that does not fit the structure defined in task 3-C
When we say “ignore,” we mean that no `Group` object is created for that command-line argument. It is up to you if you want to print a helpful message when you encounter an issue with the command-line argument, but any command-line argument that is ignored should **not** prevent the well-formed command-line arguments from being processed.
:::
</details>
:::success
<details><summary><b><i>Hint: </i>Task 4-C</b></summary>
You do not need to write much code for this task if you are strategic about using try/except blocks. For the third bullet in particular, remember that your Group constructor is calling the <code>validate_data</code> method you wrote and will pass along any exception it raises.
:::
</details>
### Task 4-D
Next, create a REPL that:
- Prompts the user for a skill (use python's `input` function, described below)
- Prints out the _unique_ names of people from all of the Groups who have that skill (in any order)
- Repeats until the user types “quit”
:::info
**Example**: If <code>example_data2.json</code> also had a Sebastian who had a screeching skill, an example interaction might look like:
```
> python hw4_repl.py example_data.json example_data2.json
What skill are you looking for? screeching
Here are the people with that skill: [Sebastian]
What skill are you looking for? quit
```
Note that, even though both files have a Sebastian that matches the desired skill, we only print Sebastian once, because we are printing out unique names.
:::
You can prompt a user for input using:
```python
user_name = input("What is your name? ")
print("Hello, " + user_name + "!")
```
Running this code will yield:
```
What is your name? Blueno
Hello, Blueno!
```
:::success
<details><summary><b><i>Hint: </i>Task 4-D</b></summary>
Again, this should not take much code. How can you loop through all of the Group objects you created in Task 4-C and find which people in each group have a specific skill? Is there a built-in type that will automatically make sure that elements are unique when you add them to an object of that type?
:::
</details>
Since you weren’t creating methods for this file, do not write unit tests for it. Instead, make some example json file(s) and play around with running the file from the terminal and interacting with it.
# Handin
To hand in your homework, you should submit your work to both the implementation and testing assignments:
1. Submit the following files to **Homework 4: Implementation** on Gradescope:
- `hw4.py`
- `test_hw4.py`
- `hw4_repl.py`
- `hw4_debug.pdf`: your debugging process document from Task 2-B
2. Submit the following files to the **Homework 4: Testing** assignment on Gradescope:
- `test_hw4.py`
***
_Please let us know if you find any mistakes, inconsistencies, or confusing language in this or any other CS200 document by filling out our_ [_anonymous feedback form_](https://forms.gle/8HY7tDdgM98fk2e19)_!_