Created
February 20, 2019 10:22
-
-
Save mauler/15af4c8358237c640d9c454c0a25e115 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://codekata.com/kata/kata09-back-to-the-checkout/ implemenation using python3 with type-hints and mypy | |
from collections import defaultdict | |
from typing import NamedTuple, Dict, List | |
import unittest | |
class Product(NamedTuple): | |
price: int | |
class Discount(NamedTuple): | |
price: int | |
quantity: int | |
class Store: | |
def __init__(self, | |
products: Dict[str, Product], | |
discounts: Dict[str, Discount]): | |
self.products = products | |
self.discounts: Dict[Product, Discount] = {} | |
for sku, discount in discounts.items(): | |
product = products[sku] | |
self.discounts[product] = discount | |
class Cart: | |
def __init__(self, store: Store): | |
self.store = store | |
self.products: List[Product] = [] | |
self.total = 0 | |
self._sum_products() | |
def _sum_products(self): | |
co = make_cart_checkout(self) | |
self.total = co.total | |
def scan(self, sku: str): | |
self.products.append(self.store.products[sku]) | |
self._sum_products() | |
class Checkout(NamedTuple): | |
quantities: Dict[Product, int] | |
discounts: List[Discount] = [] | |
total: int = 0 | |
def make_cart_checkout(cart: Cart) -> Checkout: | |
quantities: Dict[Product, int] = defaultdict(lambda: 0) | |
for product in cart.products: | |
quantities[product] += 1 | |
total = 0 | |
discounts: List[Discount] = [] | |
for product, qty in quantities.items(): | |
if product in cart.store.discounts: | |
discount = cart.store.discounts[product] | |
discountable, qty = divmod(qty, discount.quantity) | |
discounts += [discount] * discountable | |
total += discountable * discount.price | |
total += product.price * qty | |
return Checkout(quantities, discounts, total) | |
class TestKata09(unittest.TestCase): | |
def setUp(self): | |
self.store = Store( | |
products={ | |
'A': Product(price=50), | |
'B': Product(price=30), | |
'C': Product(price=20), | |
'D': Product(price=15), | |
}, | |
discounts={ | |
'A': Discount(price=130, quantity=3), | |
'B': Discount(price=45, quantity=2), | |
}) | |
def _make_checkout(self, skus): | |
cart = Cart(store=self.store) | |
for sku in skus: | |
cart.scan(sku) | |
return cart.total | |
def test_totals(self): | |
self.assertEqual(0, self._make_checkout("")) | |
self.assertEqual(50, self._make_checkout("A")) | |
self.assertEqual(80, self._make_checkout("AB")) | |
self.assertEqual(115, self._make_checkout("CDBA")) | |
self.assertEqual(100, self._make_checkout("AA")) | |
self.assertEqual(130, self._make_checkout("AAA")) | |
self.assertEqual(180, self._make_checkout("AAAA")) | |
self.assertEqual(230, self._make_checkout("AAAAA")) | |
self.assertEqual(260, self._make_checkout("AAAAAA")) | |
self.assertEqual(160, self._make_checkout("AAAB")) | |
self.assertEqual(175, self._make_checkout("AAABB")) | |
self.assertEqual(190, self._make_checkout("AAABBD")) | |
self.assertEqual(190, self._make_checkout("DABABA")) | |
def test_incremental(self): | |
co = Cart(self.store) | |
co.scan("A") | |
self.assertEqual(50, co.total) | |
co.scan("B") | |
self.assertEqual(80, co.total) | |
co.scan("A") | |
self.assertEqual(130, co.total) | |
co.scan("A") | |
self.assertEqual(160, co.total) | |
co.scan("B") | |
self.assertEqual(175, co.total) | |
if __name__ == '__main__': | |
# mypy.main() | |
unittest.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment