166 lines
5.5 KiB
Python
Executable File
166 lines
5.5 KiB
Python
Executable File
"""CSC110 Fall 2021 Prep 11: Programming Exercises
|
|
|
|
Instructions (READ THIS FIRST!)
|
|
===============================
|
|
|
|
This Python module contains the data class definitions that serve as the foundation
|
|
for our computational model of a food delivery system, as we discussed in this week's
|
|
prep reading.
|
|
|
|
Please read through this file carefully, and follow all instructions described below.
|
|
We have marked each place you need to write code with the word "TODO".
|
|
As you complete your work in this file, delete each TODO comment.
|
|
|
|
|
|
Copyright and Usage Information
|
|
===============================
|
|
|
|
This file is provided solely for the personal and private use of students
|
|
taking CSC110 at the University of Toronto St. George campus. All forms of
|
|
distribution of this code, whether as given or with any changes, are
|
|
expressly prohibited. For more information on copyright for CSC110 materials,
|
|
please consult our Course Syllabus.
|
|
|
|
This file is Copyright (c) 2021 David Liu, Mario Badr, and Tom Fairgrieve.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
import datetime
|
|
from typing import Optional
|
|
|
|
|
|
@dataclass
|
|
class Restaurant:
|
|
"""A place that serves food.
|
|
|
|
Attributes:
|
|
- name: the name of the restaurant
|
|
- address: the address of the restaurant
|
|
- menu: the menu of the restaurant with the name of the dish mapping to
|
|
the price
|
|
- location: the location of the restaurant as (latitude, longitude)
|
|
|
|
Representation Invariants:
|
|
- self.name != ''
|
|
- self.address != ''
|
|
- all(self.menu[item] >= 0 for item in self.menu)
|
|
- -90 <= self.location[0] <= 90
|
|
- -180 <= self.location[1] <= 180
|
|
|
|
Sample Usage:
|
|
>>> mcdonalds = Restaurant(name='McDonalds', address='160 Spadina Ave',\
|
|
menu={'fries': 4.5}, location=(43.649, -79.397))
|
|
"""
|
|
name: str
|
|
address: str
|
|
menu: dict[str, float]
|
|
location: tuple[float, float]
|
|
|
|
|
|
@dataclass
|
|
class Customer:
|
|
"""A person who orders food.
|
|
|
|
Instance Attributes:
|
|
- name: Customer's name
|
|
- location: Customer's position in (latitutde, longitude) format.
|
|
|
|
Representation Invariants:
|
|
- self.name != ''
|
|
- -90 <= self.location[0] <= 90
|
|
- -180 <= self.location[1] <= 180
|
|
|
|
Sample Usage:
|
|
>>> david = Customer('David', (44.649, -79.115))
|
|
"""
|
|
name: str
|
|
location: tuple[float, float]
|
|
|
|
|
|
@dataclass
|
|
class Order:
|
|
"""A food order from a customer.
|
|
|
|
Attributes:
|
|
- customer: the customer who placed this order
|
|
- restaurant: the restaurant the order is placed for
|
|
- food_items: a mapping from names of food to the quantity being ordered
|
|
- start_time: the time the order was placed
|
|
- courier: the courier assigned to this order (initially None)
|
|
- end_time: the time the order was completed by the courier (initially None)
|
|
|
|
Representation Invariants:
|
|
- self.food_items != {}
|
|
- all(self.food_items[item] > 0 for item in self.food_items)
|
|
- all(f in self.restaurant.menu for f in self.food_items)
|
|
|
|
Sample Usage:
|
|
>>> david = Customer('David', (44.649, -79.115))
|
|
>>> mcdonalds = Restaurant(name='McDonalds', address='160 Spadina Ave',\
|
|
menu={'fries': 4.5}, location=(43.649, -79.397))
|
|
>>> order = Order(customer=david, restaurant=mcdonalds,\
|
|
food_items={'fries': 10},\
|
|
start_time=datetime.datetime(2020, 11, 5, 11, 30))
|
|
>>> order.courier is None # Illustrating default values
|
|
True
|
|
>>> order.end_time is None
|
|
True
|
|
"""
|
|
customer: Customer
|
|
restaurant: Restaurant
|
|
food_items: dict[str, int]
|
|
start_time: datetime.datetime
|
|
courier: Optional[Courier] = None
|
|
end_time: Optional[datetime.datetime] = None
|
|
|
|
|
|
@dataclass
|
|
class Courier:
|
|
"""A person who delivers food orders from restaurants to customers.
|
|
|
|
Deliberately left blank (you don't need to do anything here).
|
|
We'll discuss this data class in lecture!
|
|
"""
|
|
|
|
|
|
def total_cost(order: Order) -> float:
|
|
"""Return the total cost of the food items in this order.
|
|
|
|
Done: add one doctest example for this function that:
|
|
1. Creates a valid customer and restaurant, where the restaurant
|
|
has at least two different foods on the menu.
|
|
2. Creates an order for this customer and restaurant that orders
|
|
>= 2 different food items, and >= 3 quantity of each item.
|
|
3. Calculates the total cost of the order.
|
|
(Use math.isclose to avoid rounding error with floats.)
|
|
|
|
>>> import math
|
|
>>> david = Customer('David', (44.649, -79.115))
|
|
>>> mcdonalds = Restaurant(name='McDonalds', address='160 Spadina Ave',\
|
|
menu={'fries': 4.5, 'strawberries': 9999, 'ice cream': 1},\
|
|
location=(43.649, -79.397))
|
|
>>> order = Order(customer=david, restaurant=mcdonalds,\
|
|
food_items={'fries': 10, 'strawberries': 4},\
|
|
start_time=datetime.datetime(2020, 11, 5, 11, 30))
|
|
>>> math.isclose(total_cost(order), 40041.0)
|
|
True
|
|
"""
|
|
return sum([order.food_items[f] * order.restaurant.menu[f] for f in order.food_items])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import python_ta
|
|
python_ta.check_all(config={
|
|
'max-line-length': 100,
|
|
'extra-imports': ['python_ta.contracts', 'dataclasses', 'datetime'],
|
|
'disable': ['R1705', 'C0200'],
|
|
})
|
|
|
|
import python_ta.contracts
|
|
python_ta.contracts.DEBUG_CONTRACTS = False
|
|
python_ta.contracts.check_all_contracts()
|
|
|
|
import doctest
|
|
doctest.testmod()
|