### Initializers and Properties
#### Customer
- `Customer __init__(self, name)`
- Customer is initialized with a name
- `Customer property name`
- Returns customer’s name
- Names must be of type `str`
- Names must be between 1 and 15 characters, inclusive
- Should **be able** to change after the customer is instantiated
class Customer:
def __init__(self, name):
self.name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if isinstance(value, str) and 1 <= len(value) <= 15:
self._name = value
=
#### Coffee
- `Coffee __init__(self, name)`
- Coffee is initialized with a name
- `Coffee property name`
- Returns the coffee’s name
- Names must be of type `str`
- Names length must be greater or equal to 3 characters
- Should **not be able** to change after the coffee is instantiated
- _hint: `hasattr()`_
class Coffee:
def __init__(self, name):
self.name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if isinstance(value, str) and 3 <= len(value)
and not hasattr(self, 'name'):
self._name = value
=
=
#### Order
- `Order __init__(self, customer, coffee, price)`
- Order is initialized with a `Customer` instance, a `Coffee` instance, and a
price
- `Order property price`
- Returns the price for the order
- Prices must be of type `float`
- Price must be a number between 1.0 and 10.0, inclusive
- Should **not be able** to change after the order is instantiated
- _hint: `hasattr()`_
class Order:
all = []
def __init__(self, customer, coffee, price):
self.customer = customer
self.coffee = coffee
self.price = price
Order.all.append(self)
@property
def price(self):
return self._price
@price.setter
def price(self, price):
if (isinstance(price, float)
and 1 <= price <= 10
and not hasattr(self, 'price')):
self._price = price
=
=
### Object Relationship Properties
#### Order
- `Order property customer`
- Returns the customer object for that order
- Must be of type `Customer`
class Order:
.
.
@property
def customer(self):
return self._customer
@customer.setter
def customer(self, value):
if isinstance(value, Customer):
self._customer = value
- `Order property coffee`
- Returns the coffee object for that order
- Must be of type `Coffee`
class Order:
.
.
@property
def coffee(self):
return self._coffee
@coffee.setter
def coffee(self, value):
if isinstance(value, Coffee):
self._coffee = value
=
### Object Relationship Methods
#### Coffee
- `Coffee orders()`
- Returns a list of all orders for that coffee
- Orders must be of type `Order`
=
class Coffee:
def orders(self):
return [ order for order in Order.all if order.coffee == self ]
=
- `Coffee customers()`
- Returns a **unique** list of all customers who have ordered a particular
coffee.
- Customers must be of type `Customer`
class Coffee:
def customers(self):
all_customers = [ order.customer for order in Order.all if order.coffee == self]
unique_customers = set(all_customers)
unique_list = list(unique_customers)
return unique_list
=
### Object Relationship Methods
#### Customer
- `Customer orders()`
- Returns a list of all orders for that customer
- Orders must be of type `Order`
process
#get all the orders
ipdb> [Order.all]
#get each order
ipdb> [ order for order in Order.all ]
#get customers name from all the orders
ipdb> [ order.customer.name for order in Order.all ]
['Hammurabi', 'Jappy', 'Tyler', 'Hammurabi', 'Jappy', 'Tyler', 'Hammurabi', 'Jappy', 'Tyler']
#get customers' name from all the orders that matches the customer, c1
ipdb> [ order.customer.name for order in Order.all if order.customer == c1]
#get coffee name of the orders from the customer, c1
ipdb> [ order.coffee.name for order
in Order.all if order.customer == c1 ]
so
class Customer:
.
.
.
def orders(self):
return [ order for order in Order.all if order.customer == self ]
- `Customer coffees()`
- Returns a **unique** list of all coffees a customer has ordered
- Coffees must be of type `Coffee`
ipdb> non_unique = [ order.coffee for order in Order.all if order.customer == c1 ]
ipdb> unique = set(non_unique)
ipdb> unique_list = list(unique)
so
class Customer:
.
.
def coffees(self):
non_unique = [ order.coffee for order in Order.all if order.customer == self ]
unique = set(non_unique)
unique_list = list(unique)
return unique_list
=
=
=
### Aggregate and Association Methods
#### Customer
- `Customer create_order(coffee, price)`
- Receives a **coffee object** and a **price number** as arguments
- Creates and returns a new Order instance and associates it with that
customer and the coffee object provided.
class Customer:
.
.
def create_order(self, coffee, price):
return Order(self, coffee, price)
=
=
=
#get all the orders that is coffee, f1
ipdb> [ order for order in Order.all if order.coffee == f1 ]
[<classes.many_to_many.Order object at 0x10660fdf0>, <classes.many_to_many.Order object at 0x10660fe50>, <classes.many_to_many.Order object at 0x10660feb0>]
#print all that coffee name for all the orders that is coffee, f1
ipdb> [ order.coffee.name for order in Order.all if order.coffee == f1 ]
['Cappuccino', 'Cappuccino', 'Cappuccino']
#get the length of it
len([ order.coffee.name for order in Order.all if order.coffee == f1 ])
=
=
=
### Aggregate and Association Methods
#### Coffee
- `Coffee num_orders()`
- Returns the total number of times a coffee has been ordered
- Returns `0` if the coffee has never been ordered
def num_orders(self):
return len(self.orders())
- `Coffee average_price()`
- Returns the average price for a coffee based on its orders
- Returns `0` if the coffee has never been ordered
- Reminder: you can calculate the average by adding up all the orders prices
and dividing by the number of orders
process
# sum([ order.price for order in f1.orders() ])/len(f1.orders())
so
class Coffee:
def average_price(self):
return sum([ order.price for order in self.orders() ])/len(self.orders())
### Bonus: Aggregate and Association Method
- `Customer classmethod most_aficionado(coffee)`
- Receives a **coffee object** argument
- Returns the `Customer` instance that has spent the most money on the coffee
instance provided as argument.
- Returns `None` if there are no customers for the coffee instance provided.
- _hint: will need a way to remember all `Customer` objects_
- Uncomment lines 137–147 in the customer_test file
class Customer:
.
.
.
@classmethod
def total_spent(cls, customer):
# Calculate the total amount spent by a specific customer
return sum(order.price for order in customer.orders())
@classmethod
def most_aficionado(cls, coffee):
# Create a list of customer names who have ordered the provided coffee
customers_for_coffee = [order.customer for order in Order.all if order.coffee == coffee]
# Check if there are no customers for the provided coffee
if not customers_for_coffee:
return None
# Print the list of customer names and the total_spent method (this is for debugging purposes)
print(customers_for_coffee, cls.total_spent)
# Find the customer with the highest total spent on the provided coffee
# The key parameter specifies a function to determine the sorting key
return max(customers_for_coffee, key=cls.total_spent)
lambda answer
@classmethod
def most_aficionado(cls, coffee):
# if not isinstance(coffee, Coffee):
# raise Exception
if coffee_all_orders := [
order for order in Order.all if order.coffee is coffee
]:
return max(
cls.all,
key=lambda customer: sum(
order.price
for order in coffee_all_orders
if order.customer is customer
),
)
return None
### Bonus: For any invalid inputs raise an `Exception`.
- First, **comment out** the following lines
- **customer_test.py**
- lines 25–26, 40–41, and 44–45
- **coffee_test.py**
- lines 34–35
- **order_test.py**
- lines 46–47
- Then, **uncomment** the following lines in the test files
- **customer_test.py**
- lines 31–32, 48–49, and 52–53
- **coffee_test.py**
- lines 22–23, 26–27, and 38–39
- **order_test.py**
- lines 32–33, 36–37, and 50–51