Assignment 7 - Representing a Course Schedule

Due: Thursday, May 9, 2024, at 10pm

You may work alone or with a partner, but you must type up the code yourself. You may also discuss the assignment at a high level with other students. You should list any student with whom you discussed each part, and the manner of discussion (high-level, partner, etc.) in a comment at the top of each file. You should only have one partner for an entire assignment.

You should submit your assignment as an a7.zip file on Moodle.

Parts of this assignment:

You will work in two different files for this assignment.

  • Part 1: skeleton code. You should save the code file as course.py.
  • Parts 2+3: skeleton code. You should save the code file as courseSchedule.py.

There is also a data file for testing, and a Python file used to generated it:

Comments and collaboration

As with all assignments in this course, for each file in this assignment, you are expected to provide top-level comments (lines that start with # at the top of the file) with your name and a collaboration statement. For this assignment, you have multiple programs; each needs a similar prelude.

You need a collaboration statement, even if just to say that you worked alone.

Note on style:

The following style guidelines are expected moving forward, and will typically constitute 5-10 points of each assignment (out of 100 points).

  • Variable names should be clear and easy to understand, should not start with a capital letter, and should only be a single letter when appropriate (usually for i, j, and k as indices, potentially for x and y as coordinates, and maybe p as a point, c for a circle, r for a rectangle, etc.).
  • It’s good to use empty lines to break code into logical chunks.
  • Comments should be used for anything complex, and typically for chunks of 3-5 lines of code, but not every line.
  • Don’t leave extra print statements in the code, even if you left them commented out.
  • Make sure not to have code that computes the right answer by doing extra work (e.g., leaving a computation in a for loop when it could have occurred after the for loop, only once).
  • Avoid having tons of lines of code immediately after another that could have been in a loop.
  • Use lowercase first letters for variables and methods, and uppercase first letters for classes.

Part 1: Representing a course

# You should be fully equipped to complete this problem after Lesson 18 (Monday May 6).

In this problem, you will complete the implementation of the Course class to represent courses. Each course has a name, a number of credits, an amount of effort required, and a value due to taking the course.

Here is the specification of the Course class:

  • The constructor takes in four normal parameters: the name, the credits, the effort, and the value.
  • Each of the four instance variables has an accessor method: getName(), getCredits(), getEffort(), and getValue().
  • There should be a __str__() method that is used to generate a string representation of a Course instance. (More info on this below.)

Fill in the class definition for Course in course.py. As an exercise in test-driven developent, there is testing code provided for you in an if __name__ == "__main__": block at the bottom of this file. Once your implementation is complete, the tests should all pass:

Testing Course.getName(): pass
Testing Course.getCredits(): pass
Testing Course.getEffort(): pass
Testing Course.getValue(): pass
Testing Course.__str__(): pass

Most of the methods you’ll implement for this part are straightforward accessor methods. However, you’ll also be implementing the __str__() method. Remember that in Python double underscores (__) have special meaning. When a function __str__() is defined in Python, this is what gets called when the str() function is used (including when you try to call print() on an object). The __str__ function should take only one formal parameter (self), and return a string. Here is an example for our MultiSidedDie class:

    def __str__(self):
        return str(self.numSides) + "-sided die (val=" + \
               str(self.val) + ")"

We can use it like this:

    d4 = MultiSidedDie(4) # 4-sided die
    d4.roll()
    print(d4) # prints: 4-sided die (val=3)

As an aside, recall that there is an easier way to build strings using “f-strings” in Python. If you precede a string with a letter f, you can use {} with variable names to plug them in directly. We could rewrite the MultiSidedDie.__str__ function cleanly like this:

    def __str__(self):
        return f"{self.numSides}-sided die (val={self.value})"

Part 2: Representing a course schedule

# You should be fully equipped to complete this problem after Lesson 18 (Monday May 6).

In addition to representing an individual course, we’ll implement another class to represent a course schedule.

For this part, you’ll complete the following class definition:

from course import Course

class CourseSchedule:
    def __init__(self, courses = []):
        self.courses = courses[:] # copy the courses

    def addCourse(self, course):
        pass # TODO

    def getNumCourses(self):
        return 0 # TODO

    def getTotalCredits(self):
        return 0 # TODO

    def getAverageEffort(self):
        return 0 # TODO

    def getMedianValue(self):
        return 0 # TODO

The CourseSchedule class represents the schedule as a list of Course objects. It includes the following functions:

  • addCourse: add a new Course object to the schedule
  • getNumCourses: return the number of Course objects in the schedule
  • getTotalCredits: calculates (and returns) the total number of credits among all Courses in the schedule
  • getAverageEffort: calculates (and returns) the average effort among all Courses in the schedule
  • getMedianValue: calculates (and returns) the median value among all Courses in the schedule

Here are a few helpful notes:

  • Recall that the average of items in a list is the sum of those items divided by the number of items. Depending on your approach, you may find the sum() function useful.
  • The median is the center of a sorted list. This method should be slightly more challenging than the others, but it doesn’t need to be much more challenging. Remember that the median of an odd number of items is the middle, and the median of an even number is the average of the two middle items. You may find the sorted() function helpful.

Here’s an example using sorted():

# Build a list of numbers
mylist = [3, 1, 4, 1, 5, 9]
print(mylist) # [3, 1, 4, 1, 5, 9]

# Sort a copy of the list
mylist_sorted = sorted(mylist)
print(mylist_sorted) # [1, 1, 3, 4, 5, 9]

# The original is unchanged :)
print(mylist) # [3, 1, 4, 1, 5, 9]

This is the expected output once you’ve completed this class definition, given the test code in courseSchedule.py:

Number of courses: 4
Total credits:     19
Average effort:    5.5
Median value:      6.0

Number of courses: 5
Total credits:     21
Average effort:    5.2
Median value:      7

Part 3: Reading in a course schedule

# You should be fully equipped to complete this problem after you complete Parts 1 and 2; you already know how to parse CSV files and just need the two classes implemented.

To give you a little more practice working with CSV files, you should fill in the function readScheduleFromCSV to parse a given file into a CourseSchedule object. The CSV file can be assumed to have each row contain one course’s information in the order name,credits,effort,value.

I’ve generated a CSV file, roughly corresponding to Carleton’s CS course offerings. Note: these are entirely made up randomly, except for CS 111 which has suspiciously high value for the amount of effort…

CS 100,6,5,4
CS 111,6,2,10
CS 200,6,5,9
CS 201,6,8,8
CS 202,6,2,3
CS 208,6,10,6

For example, in this file, the course with name “CS 111” is for 6 credits, with effort=2 and value=10.

Once you implement this function, you can change the if __name__ == "__main__": block at the end of the file to call testReadScheduleFromCSV, which should then generate this output:

# Testing readScheduleFromCSV:
------------------------------
Number of courses: 36
Total credits:     210
Average effort:    5.444444444444445
Median value:      4.5

Reflection

# You should be equipped to complete this part after finishing your assignment.

Were there any particular issues or challenges you dealt with in completing this assignment? How long did you spend on this assignment? Write a brief discussion (a sentence or two is fine) in your readme.txt file.

Grading

This assignment will be graded out of 100 points, as follows:

  • 5 points - submit a valid a7.zip file with all files correctly named

  • 5 points - all code files contain top-level comments with file name, purpose, and author names

  • 5 points - each code files’ top-level comments contain collaboration statement

  • 10 points - code style enables readable programs

  • 25 points - Course class implementation works correctly (Part 1)

  • 35 points - CourseSchedule class implementation works correctly (Part 2)

  • 10 points - readScheduleFromCSV function reads in Course data from a specified CSV file and returns a CourseSchedule of those courses (Part 3)

  • 5 points - readme.txt file contains reflection

What you should submit

You should submit a single .zip file on Moodle. It should contain the following files:

  • readme.txt (reflection)
  • course.py (Part 1)
  • courseSchedule.py (Parts 2+3)