Skip to content

Project 3: Dukes Parking

Entrance of the Grace Street parking deck

Image credit: breezejmu

Introduction (Fictional)

JMU Parking Services has contracted with the College of Integrated Science and Engineering to develop a new parking management system for the on-campus parking garages. The following description has been provided.

Problem Specification

When a car enters the garage, an automated license plate scanner will note the plate number and arrival time. The system will also measure the car's approximate dimensions (length, width, height). The incoming car is then assigned to the nearest appropriate space (by dimensions), if one is available.

When a car exits the garage, the parking fee will be determined automatically based on the license plate. The fee is $1/hour (rounded up to whole hours), with a $20 daily limit. For example:

  • A car that parks for 2 hours and 5 minutes will be charged $3.
  • A car that parks for exactly 24 hours will be charged only $20.
  • A car that parks for 24 hours and 1 second will be charged $21.

Cars that are parked for more than one week may be impounded, that is, towed from the garage and placed in locked facility off campus.

The Engineering department will develop the hardware for the system, and the Computer Science department will develop the software. That's where you come into the story!

Data Files

The system will be provided with a garage data file describing all parking spaces. The ordering of the spaces in the file will correspond to their proximity to the garage entrance. For example, the first three lines of the file might look like the following:

garage_small.txt
A 0,18,9,8
A 1,20,12,9
A 3,20,10,8

The file contains four comma-separated values: the label of the parking space, followed by the length, width, and height of the space. In this example, the closest space to the entrance is labeled A 0, and the dimensions of the space are 22x14x8.

To test the system, we will simulate cars arriving and departing using an events data file. For example, four lines of the events file might look like the following:

events_small.txt
2023-01-18 09:10:00,Arrive,JMU 149,18x6x7
2023-04-03 08:27:14,Arrive,ABC 123,14x6x6
2023-04-03 13:05:36,Depart,ABC 123,14x6x6
2023-05-04 15:50:00,Arrive,TOO BIG,99x9x9

The file contains four comma-separated values: the timestamp of the event, the type of gate (Arrive or Depart), the car's license plate, and the size of the car. In this example, the first car ABC 123 parked for about five hours.

Hint

All timestamps are in ISO 8601 format, which is used by Python's built-in datetime class. Subtracting one datetime object from another results in a timedelta object. Refer to the documentation for these data types as needed.

System Design

The following UML diagram summarizes the classes you will implement:

classDiagram

  class Car {
    - _plate: str
    - _dimensions: tuple
    - _arrival: datetime
    + Car(plate: str, dimensions: tuple, arrival: datetime)
    + get_plate(): str
    + get_dimensions(): tuple
    + get_arrival(): datetime
    + __str__(): str
  }

  class Space {
    - _label: str
    - _dimensions: tuple
    - _car: Car
    + Space(label: str, dimensions: tuple, car: Car)
    + get_label(): str
    + get_dimensions(): tuple
    + get_car(): Car
    + set_car(car: Car)
    + is_occupied(): bool
    + can_park(car: Car): bool
    + __str__(): str
  }

  class Garage {
    - _spaces: list
    - _cars: dict
    + Garage(data: list)
    + get_capacity(): int
    + get_occupied(): int
    + assign_space(car: Car): Space
    + remove_car(plate: str): Car
    + impound_cars(now: datetime): list
    + __str__(): str
  }

  class DukesParking {
    - _garage_data: list
    - _events_data: list
    + DukesParking(garage_file: str, events_file: str)
    + run()
    + payment(arrive: datetime, depart: datetime)$ int
  }

  Space --> Car
  Space --o Garage
  Garage ..> Car
  DukesParking ..> Car
  DukesParking ..> Garage

Provided Code

Stubs for the above four classes are provided. Download PA3.zip and unzip the file. Write your name at the top of each file. Replace all pass statements with your solution.

Docstrings are not required on this assignment. The flake8 setup.cfg included in PA3.zip does not look for docstrings. Gradescope will use the same setup.cfg file.

You may add docstrings if doing so is helpful to you. But please keep them short (ideally one line) so that grading is easier.

Requirements

We recommend you implement (and test!) the classes in the following order.

1. Car Class

A Car object represents a single car that has been detected by the scanner. Each Car has a license plate, dimensions (length, width, height), and arrival date and time.

The Car class is fairly straightforward. Implement the constructor and getters as usual.

The __str__() method uses the following format:

Car: ABC 123, 14x6x6, 2023-04-03 08:27:14

2. Space Class

A Space object represents a physical parking space. Each Space has a label that refers to the location in the garage, dimensions (length, width, height), and a reference to the Car assigned to the space (or None if the space is not occupied).

  • The can_park() method returns True if the provided car will fit in the space, False otherwise. For a car to fit, the car's length have to be less than or equal to the space's length. Additionally, at least two feet of space must remain on each side of the car. Ex: If a car is 6 feet wide, the space must be at least 10 feet wide. Finally, at least one foot of space must remain above the car.

The __str__() method uses the following format:

Space: A 3, 20x10x8, ABC 123
If the space is not occupied, the format should be:
Space: A 3, 20x10x8, not occupied

3. Garage Class

The Garage class stores a list of all spaces and a dictionary of parked cars. The dictionary maps a license plate (string) to the parking space assigned to that car. Notice in the UML diagram that the Space object refers to the Car object. However, the dictionary key is the license plate.

  • The __init__() method takes a list of strings that were read from a garage data file. For each line of data, construct a Space object and append to the list of spaces. In order to construct a Space object, you will need to convert part of each line into a tuple of integers.

  • The get_capacity() method returns the number of spaces in the garage.

  • The get_occupied() method returns the number of cars parked in the garage.

  • The assign_space() method searches for an unoccupied space that is large enough for the car. If one is found, call set_car() on the space, add the car to the _cars dictionary, and return the space. If no space is found (i.e., all spaces are either occupied or too small), then return None.

  • The remove_car() method removes the (plate, space) item from the dictionary. Call the space's set_car() method to reset the space's car to None. Then return the Car object that was removed from the parking space.

  • The impound_cars() method takes a datetime object representing the current date and time. Any car that has been parked for over one week (seven days) must be removed from the garage. The method returns a list of Car objects that were impounded (towed).

The __str__() method uses the following format:

Garage: 3 spaces available

4. DukesParking

The DukesParking class is a simulation of how the garage might work. The __init__() method provided with the stubs is complete and should not be edited. A DukesParking object stores the garage and event data to simulate.

The payment() method calculates the parking fee based on a car's arrival and departure times. Parameters indicate the hourly rate (default of $1/hour) and daily limit (default of $20/day). Refer to the Introduction section for examples of how to compute the parking fee. Your code should be based on the parameters (don't use the numbers 1 and 20 in your code).

Most of the work for this class is the run() method. Implement the following outline:

  • Create a Garage object based on the garage data.
  • For each line in the events data:
    • Parse/split the event details.
      • Construct a datetime object, and create a tuple of integers.
    • If the type of gate is Arrive, then:
      • Construct a Car object based on the event.
      • Print ARRIVAL followed by the car's string.
      • Attempt to assign a Space to the car.
        • If a space is found, print PARK_IN followed by the space's string
        • Otherwise (if a space is not found), print NO SPACES AVAILABLE.
    • If the type of gate is Depart, then:
      • Remove the car from the garage.
      • Print EXITING followed by the car's string.
      • Calculate the parking fee for the car.
      • Print PAYMENT, followed by a dollar sign and the parking fee formatted to two decimal places.
  • After all events are processed, call impound_cars().
    • The now argument should be the timestamp of the last event.
    • For each car returned, print IMPOUND followed by the car's string.

Using the example Data Files, the simulation output would be:

output_small.txt
ARRIVAL Car: JMU 149, 18x6x7, 2023-01-18 09:10:00
PARK_IN Space: A 1, 20x12x9, JMU 149
ARRIVAL Car: ABC 123, 14x6x6, 2023-04-03 08:27:14
PARK_IN Space: A 3, 20x10x8, ABC 123
EXITING Car: ABC 123, 14x6x6, 2023-04-03 08:27:14
PAYMENT $5.00
ARRIVAL Car: TOO BIG, 99x9x9, 2023-05-04 15:50:00
NO SPACES AVAILABLE
IMPOUND Car: JMU 149, 18x6x7, 2023-01-18 09:10:00

Unit Tests

Here are example modules you can use to test your code:

These tests are not exhaustive. Most methods are called only once, using only one car.

You are encouraged to write additional test cases, especially for the following functions:

  • test_space_can_park()
  • test_garage_assign_space()
  • test_garage_remove_car()
  • test_garage_impound_cars()
  • test_dukes_parking_payment()

In order to run the test_dukes_parking module linked above, you will also need to download and extract testdata.zip. This zip archive contains a testdata folder with small, medium, and large files. Store the testdata folder in the same location as dukes_parking.py (i.e., testdata should be a subfolder of your project folder).

Submitting

Read this document carefully, along with all of the provided code. Once you have a clear understanding of the expectations for this assignment, complete the required classes.

Unit testing: You are strongly encouraged to test your code incrementally. You probably won't succeed on this assignment if you attempt to write all the code before doing any testing!

Upload all four files (car.py, dukes_parking.py, garage.py, space.py) to Gradescope by Tuesday, May 2nd. Late submissions will incur a penalty of 15% per day for up to two days. No submissions will be accepted after Thursday, May 4th (the last day of class).

All files you submit should conform to PEP-8. Before submitting, make sure your code runs without error, passes flake8, and passes your own tests. You may submit up to 30 times.

Grading

Your submission will be graded using the following criteria:

Criterion Points
PEP 8 5
Car 10
Space 15
Garage 30
DukesParking 20
Style/Elegance 20

The style/elegance points will be given based on the quality of your code, such as using good variable names, not accessing internal variables (outside a class), and using good algorithms.

Honor Code

This assignment must be completed individually. Your submission must conform to the Honor Code. Authorized help is limited to general discussion on Piazza, the lab assistants assigned to CS 149, and the instructor. Copying work from another student, person, or the Internet is an honor code violation and will be grounds for a reduced or failing grade in the course.