Skip to content

Commit 45981db

Browse files
committed
feat: assign1
Signed-off-by: fabio <[email protected]>
1 parent 0dea4d9 commit 45981db

File tree

11 files changed

+1163
-0
lines changed

11 files changed

+1163
-0
lines changed

assign1/README.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Assignment 1: SimpleEnroll
2+
3+
Due Monday, April 25th, at 11:59PM
4+
5+
## Overview
6+
7+
It’s that time of the quarter again; time to use SimpleEnroll 🤗 Wootwoot.
8+
One thing everyone realizes in their Stanford career at one point is that they
9+
have to eventually graduate — and so enrolling in classes becomes a strategic
10+
endeavor to maximize the XP towards graduation, while also being able to sleep
11+
more than 4 hours a night!
12+
13+
In this hopefully short assignment, we’re going to use data from the
14+
ExploreCourses API to figure out which CS classes on ExploreCourses are
15+
offered this year, and which are not! We’ll be taking advantage of streams, while also exercising initialization and references in C++. Lets jump in ʕ•́ᴥ•̀ʔっ
16+
17+
There are only two files you should need to care about:
18+
19+
* `main.cpp`: All your code goes here 😀!
20+
* `utils.cpp`: Contains some utility functions. You'll use functions defined in this file, but you don't otherwise need to modify it.
21+
22+
## Running your code
23+
24+
To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assign1/` directory and run:
25+
26+
```sh
27+
g++ -std=c++20 main.cpp -o main
28+
```
29+
30+
Assuming that your code compiles without any compiler errors, you can now do:
31+
32+
```sh
33+
./main
34+
```
35+
36+
which will actually run the `main` function in `main.cpp`. This will execute your code and then run an autograder that will check that your code is correct.
37+
38+
As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track!
39+
40+
> [!NOTE]
41+
> ### Note for Windows
42+
> On Windows, you may need to compile your code using
43+
> ```sh
44+
> g++ -static-libstdc++ -std=c++20 main.cpp -o main
45+
> ```
46+
> in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with:
47+
> ```sh
48+
> ./main.exe
49+
> ```
50+
51+
## Part 0: Read the code and fill in the `Course` struct
52+
53+
1. In this assignment, we'll be using the `Course` struct to represent records pulled from ExploreCourses in C++. Take a look at the (incomplete) definition of the `Course` struct in `main.cpp` and fill in the field definitions. Ultimately, we'll be using streams to generate `Course`s --- remember what types streams deal with?
54+
55+
2. Take a look at the `main` function in `main.cpp`, and take special notice of how `courses` is passed into `parse_csv`, `write_courses_offered`,
56+
and `write_courses_not_offered`. Think about what these functions are doing. Do you need to change anything in the function definition? Spoiler, you do.
57+
58+
## Part 1: `parse_csv`
59+
60+
Check out `courses.csv`, it is a CSV file, with three columns: Title, Number of
61+
Units, and Quarter. Implement `parse_csv` so that, for each line in the csv file, it creates a struct `Course` containing the Title, Number of Units, and Quarter for that line.
62+
63+
A couple of things you need to think about:
64+
1. How are you going to read in `courses.csv`? Muahahaha, perhaps a
65+
stream 😏?
66+
2. How will you get each line in the file?
67+
68+
### Hints
69+
70+
1. Take a look at the `split` function we provide in `utils.cpp`. It may come in handy!
71+
* Feel free to check out the implementation of `split` and ask us any questions about it – you
72+
should be able to reason about it since it’s using a `stringstream`.
73+
2. Each **line** is a record! *This is important, so we're saying it again :>)*
74+
3. In CSV files (and specifically in `courses.csv`), the first line is usually a row that defines the column names (a column header row). This line doesn't actually correspond to a `Course`, so you'll need to skip it somehow!
75+
76+
## Part 2: `write_courses_offered`
77+
78+
Ok. Now you have a populated `courses` vector which has all of the records
79+
of the `courses.csv` file neatly stored in a `Course` struct! You find yourself
80+
interested in only the courses that are offered, right? **A course is considered offered if its Quarter field is not the string `“null”`.** In this function, write out to `“student_output/courses_offered.csv”` all the courses that don’t have
81+
`“null”` in the quarter field.
82+
83+
> [!IMPORTANT]
84+
> When writing out to the CSV file, please follow this format:
85+
> ```
86+
> <Title>,<Number of Units>,<Quarter>
87+
> ```
88+
> Note that there are **no spaces** between the commas! The autograder will not be happy if this format is not followed!
89+
>
90+
> Also, **make sure to write out the column header row** as the first line in the output. This is the same line you had to skip in `courses.csv` for the previous step!
91+
92+
Once `write_courses_offered` has been called, we expect that all of the offered courses (and consequently all the courses you wrote to the output file) will be removed from the `all_courses` vector. **This means that after this
93+
function runs, `all_courses` should ONLY contain courses that are
94+
not offered!**
95+
96+
One way to do this is to keep track of the courses that are offered perhaps with another vector and delete them from `all_courses`. Just like in Python and many other languages, it is a bad idea to remove elements from a data structure while you are iterating over it, so you'll probably want to do this *after* you have written all offered courses to file.
97+
98+
## Part 3: `write_courses_not_offered`
99+
100+
So you’re curious about courses that aren’t offered... In the
101+
`write_courses_not_offered` function, write out to
102+
`“student_output/courses_not_offered.csv”` the courses in
103+
`unlisted_courses`. Remember since you deleted the courses that are
104+
offered in the previous step, `unlisted_courses` trivially contains ONLY courses that are not offered – lucky you. So this step should look really similar to Part 2 except shorter and a *tiny* bit simpler.
105+
106+
## 🚀 Submission Instructions
107+
108+
After compiling and running, if your autograder looks like this:
109+
110+
![An image showing a terminal window where the autograder has run with all tests passing](docs/autograder.png)
111+
112+
then you have finished the assignment! Woot woot. To submit the assignment, please complete the feedback form [at this link](https://forms.gle/9CBz5HRYRADP3RW28). Once you submit the form, a link to the submission page will show up in the form submission confirmation.
113+
114+
Your deliverable should be:
115+
116+
- `main.cpp`

assign1/autograder/autograder.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from utils import Autograder
2+
3+
import base64
4+
from colorama import Fore
5+
import difflib
6+
import pickle
7+
import re
8+
import os
9+
10+
PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
11+
AUTOGRADER_DIR = os.path.join(PATH, "autograder")
12+
13+
SOLN_NOT_OFFERED_BIN = os.path.join(AUTOGRADER_DIR, "courses_not_offered.bin")
14+
SOLN_OFFERED_BIN = os.path.join(AUTOGRADER_DIR, "courses_offered.bin")
15+
SOLN_OFFERED = os.path.join(AUTOGRADER_DIR, "courses_offered.csv")
16+
SOLN_NOT_OFFERED = os.path.join(AUTOGRADER_DIR, "courses_not_offered.csv")
17+
18+
19+
def binary_to_csv(binary_filename, csv_filename):
20+
with open(binary_filename, "rb") as pickle_file:
21+
encoded_data = pickle.load(pickle_file)
22+
decoded_data = base64.b64decode(encoded_data).decode("utf-8")
23+
with open(csv_filename, "w") as output_file:
24+
output_file.write(decoded_data)
25+
26+
27+
def csv_to_binary(csv_filename, binary_filename):
28+
with open(csv_filename, "r") as file:
29+
lines = file.read()
30+
encoded_data = base64.b64encode(lines.encode("utf-8"))
31+
with open(binary_filename, "wb") as pickle_file:
32+
pickle.dump(encoded_data, pickle_file)
33+
34+
35+
def check_files_equal(expected, actual):
36+
if not os.path.exists(expected):
37+
raise RuntimeError(
38+
f"Solution file '{os.path.relpath(expected, PATH)}' does not exist"
39+
)
40+
if not os.path.exists(actual):
41+
raise RuntimeError(
42+
f"Output file '{os.path.relpath(actual, PATH)}' does not exist"
43+
)
44+
45+
with open(expected, "r") as f1, open(actual, "r") as f2:
46+
expected_lines = f1.readlines()
47+
actual_lines = f2.readlines()
48+
49+
if expected_lines != actual_lines:
50+
diff = list(
51+
difflib.unified_diff(
52+
expected_lines, actual_lines, fromfile=expected, tofile=actual
53+
)
54+
)
55+
56+
if len(diff) > 10:
57+
diff = diff[:10] + ["... (truncated, showing first 10 lines of diff) ...\n"]
58+
59+
diff = [f"\t{l}" for l in diff]
60+
diff_output = "".join(diff)
61+
62+
diff_output = re.sub(r"^\s*-+", lambda match: f"{Fore.RED}{match.group(0)}{Fore.RESET}", diff_output, flags=re.MULTILINE)
63+
diff_output = re.sub(r"^\s*\++", lambda match: f"{Fore.GREEN}{match.group(0)}{Fore.RESET}", diff_output, flags=re.MULTILINE)
64+
65+
raise RuntimeError(
66+
f"Contents of '{os.path.relpath(actual, PATH)}' do not equal solution:\n{diff_output}"
67+
)
68+
69+
70+
def setup():
71+
binary_to_csv(SOLN_NOT_OFFERED_BIN, SOLN_NOT_OFFERED)
72+
binary_to_csv(SOLN_OFFERED_BIN, SOLN_OFFERED)
73+
74+
75+
def teardown():
76+
os.remove(SOLN_OFFERED)
77+
os.remove(SOLN_NOT_OFFERED)
78+
79+
80+
def test_write_courses_offered():
81+
student = os.path.join(PATH, "student_output/courses_offered.csv")
82+
check_files_equal(SOLN_OFFERED, student)
83+
84+
85+
def test_write_courses_not_offered():
86+
student = os.path.join(PATH, "student_output/courses_not_offered.csv")
87+
check_files_equal(SOLN_NOT_OFFERED, student)
88+
89+
90+
if __name__ == "__main__":
91+
grader = Autograder()
92+
grader.add_part("write_course_offered", test_write_courses_offered)
93+
grader.add_part("write_course_not_offered", test_write_courses_not_offered)
94+
95+
grader.setup = setup
96+
grader.teardown = teardown
97+
grader.run()
8.94 KB
Binary file not shown.
13.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)