Python 009 — CC review : Coffee Shop

Greem
5 min readJan 17, 2024

code:https://github.com/learn-co-curriculum/python-p3-mock-challenge-coffee-shop/blob/solution/lib/classes/many_to_many.py

### 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

--

--