Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution? #553

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/car.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from dataclasses import dataclass


@dataclass
class Car:
brand: str = None
fuel_consumption: float = None
112 changes: 112 additions & 0 deletions app/customer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import datetime
from math import sqrt

from app.car import Car
from app.shop import Shop


class Customer:
def __init__(self,
name: str,
product_cart: dict,
location: list,
money: float,
car: dict) -> None:
self.name = name
self.product_cart = product_cart
self.location = location
self.money = money
self.car = Car(**car)

def choose_the_cheapest_shop_visit(
self,
shops: list[Shop],
fuel_price: float
) -> tuple:

print(f"{self.name} has {self.money} dollars")
best_price = best_shop = None

for shop in shops:
total_trip_price_shop = self.money_spent_on_products(shop)
total_trip_price_shop += self.money_spent_on_fuel(shop, fuel_price)

print(f"{self.name}'s trip to the {shop.name} "
f"costs {total_trip_price_shop}")

if (
not best_price
or total_trip_price_shop
< best_price
):
best_price = total_trip_price_shop
best_shop = shop
return best_price, best_shop

def ride_home(self, best_shop_visit_price: float) -> None:
print(f"{self.name} rides home\n{self.name} now has "
f"{self.money - best_shop_visit_price} dollars\n")

def money_spent_on_products(self, shop: Shop) -> float:
total_products_price = 0
for money_customer_need, shop_item_price in (
zip(self.product_cart.values(), shop.products.values())
):
total_products_price += money_customer_need * shop_item_price
return total_products_price

def money_spent_on_fuel(self, shop: Shop, fuel_price: float) -> float:
one_way_distance = sqrt(
(self.location[0] - shop.location[0]) ** 2
+ (self.location[1] - shop.location[1]) ** 2
)
money_fuel_spent = round(self.car.fuel_consumption
* fuel_price / 100 * one_way_distance * 2, 2)
return money_fuel_spent

def calculate_if_enough_money(
self,
best_shop_visit_price: float,
best_shop_instance: Shop
) -> bool:
if self.money >= best_shop_visit_price:
print(f"{self.name} rides to {best_shop_instance.name}\n")
return True

print(f"{self.name} doesn't have enough "
f"money to make a purchase in any shop")
return False

def interaction_with_cashier(self, best_shop_instance: Shop) -> None:
str_date = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
print(f"Date: {str_date}\n"
f"Thanks, {self.name}, for your purchase!\n"
"You have bought:")

paper_check, cart_price = self._checkout_for_products(
shop=best_shop_instance
)

for products_amount_and_price in paper_check:
print(products_amount_and_price)

print(f"Total cost is {cart_price} dollars\nSee you again!\n")

def _checkout_for_products(self, shop: Shop) -> tuple[list[str], float]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your Customer class has a lot of methods some of which could be moved to a different class. Not a mistake it's just a not so good practice to have one class that does it all and the others do nothing. Her for example you could move interaction with cashier to the shop because it doesn't really have to do anything with the customer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was figthing with circular imports, so decided to do it all in customer :(

total_cart_price = 0
full_paper_check = []

for products_amount_customer_need, (product_name, product_price) in (
zip(self.product_cart.values(), shop.products.items())
):
full_product_price = products_amount_customer_need * product_price
# tests fail without it, they want rounded if it can be rounded
if full_product_price == int(full_product_price):
full_product_price = int(full_product_price)
total_cart_price += full_product_price

paper_check = (f"{products_amount_customer_need} {product_name}s "
f"for {full_product_price} dollars")
full_paper_check.append(paper_check)

return full_paper_check, total_cart_price
7 changes: 7 additions & 0 deletions app/load_jsons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import json


def load_complete_json(filepath: str) -> dict:
with open(filepath, "r") as file:
data = json.load(file)
return data
26 changes: 23 additions & 3 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
def shop_trip():
# write your code here
pass
from app.customer import Customer
from app.load_jsons import load_complete_json
from app.shop import Shop


def shop_trip() -> None:
data = load_complete_json("app/config.json")
shops = [Shop(**shop) for shop in data.get("shops")]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have general questions about the structure of your code:

  1. The whole OOP concept in application is about creating classes with methods bound to them. So here you create classes but you don't bound any methods to them. IF you did your code would be much cleaner and well structured.
  2. This function is way too long most of the logic can be rewritten as methods that you will call on your classes. Example:
    You could put most of this logic as methods in class Customer - methods such as calculate_shop_trip_cost, get_customer_budget, buy_products etc and then in your main function you just call customer.calculate_shop_trip_cost and it will make code so much cleaner.
  3. On the same note you can add new classes such as Location and put logic of calcualting distance between two locations there and then reuse it in other code. Or you could define a method in Car to calculate the cost of fuel for the ride.
    Think how you can refactor your code so that your final shop_trip() function has about 10 lines of code max - the rest should be handled simply by calling one or two methods on customer instance and the rest should happen under the hood.
    Let me know if you have questions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the hardest part is to refactor the code after you already wrote it and it's all over the modules, so you need to tear it appart again :D

fuel_price = data.get("FUEL_PRICE")

for customer_data in data.get("customers"):
customer = Customer(**customer_data)
best_price, best_shop = customer.choose_the_cheapest_shop_visit(
shops=shops, fuel_price=fuel_price

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here you could move all those methods calls on customer to a separate method (e.g. complete_shop_trip ) and call these methods in a chain inside of the class. Here you would just call complete_shop_trip on Customer and thats it.
This way you will cleanup this function

)
is_enough_money = customer.calculate_if_enough_money(
best_shop_visit_price=best_price, best_shop_instance=best_shop
)
if not is_enough_money:
continue

customer.interaction_with_cashier(best_shop_instance=best_shop)
customer.ride_home(best_shop_visit_price=best_price)
8 changes: 8 additions & 0 deletions app/shop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from dataclasses import dataclass


@dataclass
class Shop:
name: str = None
location: list[int] = None
products: dict[str:float] = None
Loading