Project 3: Dukes Parking¶
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:
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:
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
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 aSpace
object and append to the list of spaces. In order to construct aSpace
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, callset_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 returnNone
. -
The
remove_car()
method removes the(plate, space)
item from the dictionary. Call the space'sset_car()
method to reset the space's car toNone
. Then return theCar
object that was removed from the parking space. -
The
impound_cars()
method takes adatetime
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 ofCar
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.
- Construct a
- 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 a space is found, print
- Construct a
- 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.
- Parse/split the event details.
- 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.
- The
Using the example Data Files, the simulation output would be:
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.