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

version 1.0 #676

Open
wants to merge 4 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
17 changes: 17 additions & 0 deletions app/car.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import annotations


class Car:
def __init__(self, brand: str, fuel_consumption: float) -> None:
self.brand = brand
self.fuel_consumption = fuel_consumption

@classmethod
def create_car(cls, car_info: dict[str, str | float | int]) -> Car:

Choose a reason for hiding this comment

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

The type hint for car_info should be dict[str, Union[str, float]] instead of dict[str, str | float | int] to match the expected types for brand and fuel_consumption.

return cls(
brand=car_info["brand"],
fuel_consumption=car_info["fuel_consumption"]
Comment on lines +12 to +13

Choose a reason for hiding this comment

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

Ensure that the keys 'brand' and 'fuel_consumption' are always present in the car_info dictionary to avoid KeyError. Consider using the get method with default values or handling exceptions.

Comment on lines +12 to +13

Choose a reason for hiding this comment

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

To prevent KeyError exceptions, consider using the get method with default values when accessing dictionary keys, such as car_info.get('brand', 'default_brand') and car_info.get('fuel_consumption', 0.0). This will make the code more robust against missing keys.

)

def calculate_fuel_consumption(self, distance: float) -> float:
return (self.fuel_consumption / 100) * distance
66 changes: 66 additions & 0 deletions app/customer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import annotations

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


class Customer:
def __init__(
self,
name: str,
product_cart: dict[str, int],
location: Point,
money: float,
car: Car
) -> None:
self.name = name
self.product_cart = product_cart or {}
self.location = location
self.money = money
self.car = car

@classmethod
def create_customer(
cls,
source: dict[str, str | dict[str, int] | list[int] | int]

Choose a reason for hiding this comment

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

The type hint for source should be dict[str, Union[str, dict[str, int], list[int], float]] instead of dict[str, str | dict[str, int] | list[int] | int] to match the expected types for name, product_cart, location, money, and car.

) -> Customer:
return cls(
source["name"],
source["product_cart"],
Point.create_from_location(source["location"]),
source["money"],
Car.create_car(source["car"])
Comment on lines +29 to +33

Choose a reason for hiding this comment

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

Ensure that the keys 'name', 'product_cart', 'location', 'money', and 'car' are always present in the source dictionary to avoid KeyError. Consider using the get method with default values or handling exceptions.

Comment on lines +29 to +33

Choose a reason for hiding this comment

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

To prevent KeyError exceptions, consider using the get method with default values when accessing dictionary keys, such as source.get('name', 'default_name'), source.get('product_cart', {}), source.get('location', [0, 0]), source.get('money', 0.0), and source.get('car', {}). This will make the code more robust against missing keys.

)

def choose_shop(self, shops: list) -> Shop | None:
for shop in shops:
print(f"{self.name}'s trip to the {shop.name} "
f"costs {self._calculate_total_expenses(shop)}")

cheapest_shop = min(
shops,
key=lambda shop: self._calculate_total_expenses(shop)
)
total_expenses = self._calculate_total_expenses(cheapest_shop)
if self.money < total_expenses:
return None
self.money -= total_expenses
return cheapest_shop

def _calculate_total_expenses(self, shop: Shop) -> float:
distance = self.location.calculate_distance_to(shop.location)
fuel_expenses = (
(self.car.calculate_fuel_consumption(distance) * 2) * 2.4
)
groceries_expenses = (
self._calculate_groceries_expenses(shop.products)
)

return round(fuel_expenses + groceries_expenses, 2)

def _calculate_groceries_expenses(self, prices: dict) -> float:
return sum(
prices[product_name] * amount
for product_name, amount in self.product_cart.items()
)
33 changes: 30 additions & 3 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
def shop_trip():
# write your code here
pass
import json
from typing import Callable

from app.shop import Shop
from app.customer import Customer


def get_data(info_key: str, creation_func: Callable) -> list:
with open("app/config.json", "r") as source:
data = json.load(source)

Choose a reason for hiding this comment

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

Ensure that the path to the JSON file 'app/config.json' is correct and that the file exists in the specified location.

return [creation_func(info) for info in data[info_key]]

Choose a reason for hiding this comment

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

Ensure that the keys 'customers' and 'shops' are present in the JSON data to avoid KeyError. Consider using the get method with default values or handling exceptions.

Choose a reason for hiding this comment

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

To prevent KeyError exceptions, consider checking if info_key exists in data before accessing it. You can use data.get(info_key, []) to provide a default empty list if the key is missing. This will make the code more robust against missing keys.


def shop_trip() -> None:
customers = get_data("customers", Customer.create_customer)
shops = get_data("shops", Shop.create_shop)

for client in customers:
print(f"{client.name} has {client.money} dollars")

client_shop_choice = client.choose_shop(shops)
if client_shop_choice is None:
print(f"{client.name} doesn't have enough "
f"money to make a purchase in any shop")
return
print(f"{client.name} rides to {client_shop_choice.name}\n")
client_shop_choice.sell(client)


shop_trip()
19 changes: 19 additions & 0 deletions app/point.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from __future__ import annotations

import math


class Point:
def __init__(self, cor_x: int, cor_y: int) -> None:
self.x = cor_x
self.y = cor_y

@classmethod
def create_from_location(cls, location: list[int]) -> Point:
return cls(cor_x=location[0], cor_y=location[1])

Choose a reason for hiding this comment

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

To prevent IndexError exceptions, consider checking if the location list has the required number of elements before accessing its indices. You can use a condition like if len(location) >= 2 to ensure that both location[0] and location[1] are accessible. This will make the code more robust against lists with insufficient elements.


def calculate_distance_to(self, destination: Point) -> float:
return math.sqrt(
math.pow(destination.x - self.x, 2)
+ math.pow(destination.y - self.y, 2)
)
45 changes: 45 additions & 0 deletions app/shop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations
from typing import Any

from app.point import Point


class Shop:
def __init__(
self,
name: str,
location: Point,
products: dict[str, float | int]
) -> None:
self.name = name
self.location = location
self.products = products

@classmethod
def create_shop(
cls,
shop_info: dict[str, str | list[int] | dict[str, (int | float)]]

Choose a reason for hiding this comment

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

The type hint for shop_info should be dict[str, Union[str, list[int], dict[str, float]]] instead of dict[str, str | list[int] | dict[str, (int | float)]] to match the expected types for name, location, and products.

) -> Shop:
return cls(
name=shop_info["name"],
location=Point.create_from_location(shop_info["location"]),
products=shop_info["products"]
Comment on lines +24 to +26

Choose a reason for hiding this comment

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

Ensure that the keys 'name', 'location', and 'products' are always present in the shop_info dictionary to avoid KeyError. Consider using the get method with default values or handling exceptions.

Comment on lines +24 to +26

Choose a reason for hiding this comment

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

To prevent KeyError exceptions, consider using the get method with default values when accessing dictionary keys, such as shop_info.get('name', 'default_name'), shop_info.get('location', [0, 0]), and shop_info.get('products', {}). This will make the code more robust against missing keys.

)

def sell(self, customer: Any) -> None:
print("Date: 04/01/2021 12:33:41")
print(f"Thanks, {customer.name}, for your purchase!")
print("You have bought:")
for name, amount in customer.product_cart.items():
product_price = amount * self.products[name]

Choose a reason for hiding this comment

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

To prevent KeyError exceptions when accessing self.products[name], consider checking if name exists in self.products before accessing it. You can use self.products.get(name, 0) to provide a default value if the key is missing. This will make the code more robust against missing product names.

if product_price % int(product_price) == 0:
product_price = int(product_price)
print(f"{amount} {name}s for {product_price} dollars")
total_cost = sum(
amount * self.products[name]
for name, amount in customer.product_cart.items()
)
print(f"Total cost is {round(total_cost, 2)} dollars")
print("See you again!\n")
print(f"{customer.name} rides home")
print(f"{customer.name} now has {customer.money} dollars\n")
Loading