Skip to content

Instantly share code, notes, and snippets.

@mauler
Created February 20, 2019 10:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mauler/15af4c8358237c640d9c454c0a25e115 to your computer and use it in GitHub Desktop.
Save mauler/15af4c8358237c640d9c454c0a25e115 to your computer and use it in GitHub Desktop.
# 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