Assignment 6

Goals

The goal of this assignment is to work with object-oriented programming in Python.

Instructions

You will be doing your work in a Jupyter notebook for this assignment. You may choose to work on this assignment on a hosted environment (e.g. tiger) or on your own local installation of Jupyter and Python. You should use Python 3.12 for your work. (Older versions may work, but your code will be checked with Python 3.12.) To use tiger, use the credentials you received. If you work remotely, make sure to download the .ipynb file to turn in. If you choose to work locally, Anaconda or miniforge are probably the easiest ways to install and manage Python. If you work locally, you may launch Jupyter Lab either from the Navigator application (anaconda) or via the command-line as jupyter-lab or jupyter lab.

In this assignment, we will be implementing a collection of classes that work together to orchestrate course scheduling in a university setting. There will be a central registrar that coordinates scheduling for students and professors with methods to add and remove persons from courses. You will need to test your classes to make sure they work properly (See Section 6 for a summary of the items to be implemented and some non-comprehensive test code).

Due Date

The assignment is due at 11:59pm on Friday, November 8.

Submission

You should submit the completed notebook file required for this assignment on Blackboard. The filename of the notebook should be a6.ipynb.

Details

Please make sure to follow instructions to receive full credit. Because you will be writing classes and adding to them, you do not need to separate each part of the assignment. Please document any shortcomings with your code. You may put the code for each part into one or more cells. Note that CS 503 Students must complete Section 5 which is optional for CS 490 students.

0. Name & Z-ID (5 pts)

The first cell of your notebook should be a markdown cell with a line for your name and a line for your Z-ID. If you wish to add other information (the assignment name, a description of the assignment), you may do so after these two lines.

1. Academic Classes (20 pts)

Create four classes related to the people involved in university course scheduling: Academic, Student, GraduateStudent, and Instructor. Academic is the base class which has a first name, last name, and campus_id. It also has a field max_credits which should be initialized to a default value based on the type of person (more on this later in the paragraph). While it will be used as an abstract base class (not intended to be instantiated), we didn’t cover the specifics of such a definition so you do not need to define it differently. Student is an Academic that has a level (“Freshman”, “Sophomore”, “Junior”, “Senior”, “Graduate”), and GraduateStudent is a Student whose level is always “Graduate”. Instructor is an Academic that has a rank (“Assistant Professor”, “Associate Professor”, “Professor”, “Instructor”). You must use inheritance properly here. Each student should have a default maximum number of credits of 16, each graduate student has a default maximum of 12, and each instructor has a default maximum of 9. Use a class attribute, MAX_CREDITS, to store this default, but only one place in the code should assign to the max_credits field.

Hints:
  • Make use of the base class’s constructor. Don’t rewrite what it is already doing.
  • Remember when accessing a class attribute, self will look up attributes based on the MRO so with multiple definitions of MAX_CREDITS, it will “do the expected thing”.

2. Course Class (15 pts)

Create a Course class that stores information about a course: department, course number, course name, section, number of credits, times. The course class also keeps track of the instructor (which may be unassigned) and all students enrolled. Note that times will be submitted as a list of tuples of the form (day : str, start_time : int, end_time : int). Add a method to produce a string representation that lists the department, course number, course name, section, number of credits, and times. The class should have enroll and drop methods to add/remove a student from the course, and a change_time method to update the times of the course.

Hints:
  • When fields are tracked but not assigned in the constructor, make sure they are still initialized.

3. Schedule Class (20 pts)

Create a Schedule class that tracks all courses an Academic is involved in. The constructor should take an optional list of courses to be added to the schedule. It should have a credits property (getter only) that returns the sum of the number of credits in all courses. It should have an add_course method that adds the course to the schedule.

Update the Academic class to add a schedule to each instance plus add_course and remove_course methods. The add_course method should first check if the course to be added will exceed the maximum number of credits for the person. If it doesn’t, the course should be added to the schedule. If it does conflict, raise an exception. Override the add_course method in the Student class to enroll (add) the student to the Course and in the Instructor class to set the instructor of the Course. The remove_course method in the Student class should drop a student from the Course, and in the Instructor class should unset the instructor of the course. Also, add support for adding and dropping courses using the + and - operators.

Hints:
  • To ensure that a property cannot be set, do not add a setter.
  • credits can be computed via a single line (think about list comprehensions/generator expressions)
  • Use super() to use the Academic add/remove_course methods while also doing the (different) extra steps for students/instructors.
  • Remember the dunder methods when supporting operators.

4. Registrar Class (15 pts)

Create a Registrar class that orchestrates the scheduling process. The registrar should keep track of all academic persons and all courses. It should have add_persons, add_courses, add_person_to_course, and remove_person_from_course methods. The add/remove person to/from course methods should take a campus id, department, course_number, and section, and then add the person referenced by the id to the course referenced by the three other pieces of information. You should call the add/remove_course methods, but note that polymorphism will be in effect here, meaning the method called will be different for students and instructors.

Hints:
  • Because the add/remove methods take the campus_id and course details (department, course_number, and section), it probably makes sense to store courses and academics in dictionaries.

5. [CS 503 Only] Changing Course Times (15 pts)

We will now add logic to change course times and raises an exception if this is not possible. Add a check_time_conflicts static method to the Course class that given two time structures (list of tuples), checks if any times conflict, returning True if the two lists conflict and False otherwise. Two times conflict if they have meetings on the same day such that the starting time of one meeting is in between the starting and ending times of another meeting. For example, (“Mon”, 11, 13) and (“Mon”, 12, 14) conflict, but (“Mon”, 11, 13) and (“Mon”, 13, 15) do not.

In the Schedule class, update the add_course method to check whether the course conflicts with the person’s current schedule before adding the course, raising an exception if it does.

In the Registrar class, add a change_course_time method that calls the change_time method implemented in the Course class, and that Course method must check that the proposed new time does not cause any conflicts with any currently enrolled students’ or the assigned instructor’s schedules. If it does, the method should raise an exception. If it doesn’t, the time of the course should be changed.

Hints:
  • Registrar.change_course_time must check enrolled students and the instructor.
  • Unlike some other languages, Python allows multiple comparisons without boolean operators: a < b < c checks that b is between a and c.

6. Summary and Testing

The final list of classes and methods/properties to be added. Note that all classes should have constructors that properly initialize objects, and the instance fields are not listed here.

  • Academic:
    • add_course
    • remove_course
  • Student:
    • add_course
    • remove_course (updated 2023-04-03)
  • GraduateStudent
  • Instructor
    • add_course
    • remove_course (updated 2023-04-03)
  • Course:
    • enroll
    • drop
    • change_time
    • [503] check_time_conflicts (static)
    • [support for string representation]
  • Schedule:
    • credits (property)
    • add_course
    • remove_course
    • [support for +]
    • [support for -]
  • Registrar:
    • add_persons
    • add_courses
    • add_person_to_course
    • remove_person_from_course
    • [503] change_course_time

The following code should help you test your work (for 490 students, the errors for conflicts will not show up, and you will not have change_course_time methods):

s1 = Student("z143", "Catherine", "Smith", "Senior")
s2 = Student("z352", "Niraj", "Kumar", "Sophomore")
s3 = GraduateStudent("z785", "Divya", "Bharti")
s4 = GraduateStudent("z982", "James", "O'Brien")

i1 = Instructor("a421", "Jennifer", "Martinez", "Professor")
i2 = Instructor("a572", "Jonathan", "Jones", "Instructor")

c1 = Course("CSCI", 1543, "Programming Principles in Python", 1, 3, [("Mon", 10, 12), ("Wed", 10, 12)])
c2 = Course("CSCI", 1342, "Computer Networks", 2, 4, [("Tue", 14, 16), ("Thu", 14, 16), ("Fri", 12, 13)])
c3 = Course("CSCI", 1352, "Computer Graphics", 1, 3, [("Tue", 10, 12), ("Thu", 10, 12)])
c4 = Course("SOCI", 1230, "Introduction to Sociology", 1, 3, [("Mon", 11, 13), ("Thu", 11, 13)])
c5 = Course("POLS", 1100, "American Politics", 2, 3, [("Tue", 10, 12), ("Thu", 10, 12)])
c6 = Course("SOCI", 1450, "Classical Sociological Theory", 1, 3, [("Mon", 12, 13), ("Wed", 12, 13), ("Fri", 12, 13)])

r = Registrar()
r.add_persons([s1,s2,s3,s4,i1,i2])
r.add_courses([c1,c2,c3,c4,c5, c6])
r.add_person_to_course("a572", "SOCI", 1230, 1)
r.add_person_to_course("a572", "POLS", 1100, 2) # error due to conflict
r.change_course_time("SOCI", 1230, 1, [("Mon", 9, 11), ("Wed", 9, 11)])
r.add_person_to_course("a572", "SOCI", 1450, 1)
r.add_person_to_course("a421", "CSCI", 1543, 1)
r.add_person_to_course("a421", "CSCI", 1342, 2)
r.add_person_to_course("a421", "CSCI", 1352, 1) # error due to max credits
r.add_person_to_course("z785", "SOCI", 1230, 1)
r.add_person_to_course("z785", "CSCI", 1352, 1)
r.add_person_to_course("z143", "CSCI", 1543, 1)
r.add_person_to_course("z143", "CSCI", 1342, 2)
r.add_person_to_course("z143", "SOCI", 1230, 1) # error due to conflict
r.change_course_time( "SOCI", 1230, 1, [("Tue", 11, 13), ("Thu", 11, 13)]) # error due to conflict
r.change_course_time( "SOCI", 1230, 1, [("Tue", 12, 14), ("Thu", 12, 14)])
r.add_person_to_course("z143", "SOCI", 1230, 1)

Extra Credit

  • Add support for printing schedules and course enrollments