Shopping basket pricing library for a supermarket.
pip install shopping
Documentation and more detailed examples are hosted on Github Pages.
Composition is used to construct a pricer component with a basket, catalogue and list of offers.
@dataclass
class Pricer:
basket: Basket
catalogue: Catalogue
offers: List[Offer]
In order to allow complex offers to be applied the basket must be transformed into a list of price stubs consisting of products and their current prices.
> pricer = Pricer(
Basket({"apple": 2, "orange": 1}),
Catalogue({"apple": 1.0, "orange": 2.0})
)
> pricer.stubs_list
[
PriceStub(name='apple', price=1.0),
PriceStub(name='apple', price=1.0),
PriceStub(name='orange', price=2.0),
]
Offers can be applied to update the final price of each product.
def half_price_oranges(stubs_list: List[PriceStub]) -> List[PriceStub]:
return [
PriceStub(i.name, i.price / 2 if i.name == "orange" else i.price)
for i in stubs_list
]
> offer = Offer("Half price oranges", half_price_oranges)
> offer.transform(pricer.stubs_list)
[
PriceStub(name='apple', price=1.0),
PriceStub(name='apple', price=1.0),
PriceStub(name='orange', price=1.0),
]
This design allows the design of offers to be totally encapsulated by their instances.
T = TypeVar("T")
Transform = Callable[[T], T]
@dataclass
class Offer:
title: str
transform: Transform[List[PriceStub]]
Unit tests are not enough to capture the desired behavior of the pricer in a readable way. Therefore we include some bdd tests. Scenarios are documented in feature files:
Feature: Pricer
Scenario Outline: Pricing a basket with multiple items and offers.
Given I have a basket with <apples> apples and <oranges> oranges.
And Apples cost 1.0 and oranges cost 2.0.
When apples are buy one get one free.
And oranges are half price.
Then The sub_total price should be <sub_total>.
And A discount of <discount> should be applied.
And The total price should be <total>.
Examples Vertical:
| apples | 1 | 1 | 1 | 2 | 2 | 2 | 3 | 3 | 3 |
| oranges | 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 |
| sub_total | 3.0 | 5.0 | 7.0 | 4.0 | 6.0 | 8.0 | 5.0 | 7.0 | 9.0 |
| discount | 1.0 | 2.0 | 3.0 | 2.0 | 3.0 | 4.0 | 2.0 | 4.0 | 4.0 |
| total | 2.0 | 3.0 | 4.0 | 2.0 | 3.0 | 4.0 | 3.0 | 4.0 | 5.0 |
To install dependencies:
yarn install
pip install .[all]
To run tests:
thx test
To generate the documentation locally:
thx docs
To run linters:
thx lint
To run formatters:
thx format
Please read this repository's Code of Conduct which outlines our collaboration standards and the Changelog for details on breaking changes that have been made.
This repository adheres to semantic versioning standards. For more information on semantic versioning visit SemVer.
Bump2version is used to version and tag changes. For example:
bump2version patch
- Joel Lefkowitz - Initial work
Lots of love to the open source community!