diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..fc069cb --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,76 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +.pytest_cache + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask instance folder +instance/ + +# Sphinx documentation +docs/_build/ + +# MkDocs documentation +/site/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version +venv + +.idea diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..582b02c --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,3 @@ +pytest +approvaltests +pytest-approvaltests diff --git a/python/statement.py b/python/statement.py new file mode 100644 index 0000000..eca947e --- /dev/null +++ b/python/statement.py @@ -0,0 +1,41 @@ +import math + + +def statement(invoice, plays): + total_amount = 0 + volume_credits = 0 + result = f'Statement for {invoice["customer"]}\n' + + def format_as_dollars(amount): + return f"${amount:0,.2f}" + + for perf in invoice['performances']: + play = plays[perf['playID']] + if play['type'] == "tragedy": + this_amount = 40000 + if perf['audience'] > 30: + this_amount += 1000 * (perf['audience'] - 30) + elif play['type'] == "comedy": + this_amount = 30000 + if perf['audience'] > 20: + this_amount += 10000 + 500 * (perf['audience'] - 20) + + this_amount += 300 * perf['audience'] + + else: + raise ValueError(f'unknown type: {play["type"]}') + + # add volume credits + volume_credits += max(perf['audience'] - 30, 0) + # add extra credit for every ten comedy attendees + if "comedy" == play["type"]: + volume_credits += math.floor(perf['audience'] / 5) + # print line for this order + result += f' {play["name"]}: {format_as_dollars(this_amount/100)} ({perf["audience"]} seats)\n' + total_amount += this_amount + + result += f'Amount owed is {format_as_dollars(total_amount/100)}\n' + result += f'You earned {volume_credits} credits\n' + return result + + diff --git a/python/tests/invoice.json b/python/tests/invoice.json new file mode 100644 index 0000000..9782f9b --- /dev/null +++ b/python/tests/invoice.json @@ -0,0 +1,17 @@ +{ + "customer": "BigCo", + "performances": [ + { + "playID": "hamlet", + "audience": 55 + }, + { + "playID": "as-like", + "audience": 35 + }, + { + "playID": "othello", + "audience": 40 + } + ] +} \ No newline at end of file diff --git a/python/tests/invoice_new_plays.json b/python/tests/invoice_new_plays.json new file mode 100644 index 0000000..723f35b --- /dev/null +++ b/python/tests/invoice_new_plays.json @@ -0,0 +1,13 @@ +{ + "customer": "BigCoII", + "performances": [ + { + "playID": "henry-v", + "audience": 53 + }, + { + "playID": "as-like", + "audience": 55 + } + ] +} diff --git a/python/tests/new_plays.json b/python/tests/new_plays.json new file mode 100644 index 0000000..7a4eec6 --- /dev/null +++ b/python/tests/new_plays.json @@ -0,0 +1,4 @@ +{ + "henry-v": {"name": "Henry V", "type": "history"}, + "as-like": {"name": "As You Like It", "type": "pastoral"} +} diff --git a/python/tests/plays.json b/python/tests/plays.json new file mode 100644 index 0000000..bbe3880 --- /dev/null +++ b/python/tests/plays.json @@ -0,0 +1,5 @@ +{ + "hamlet": {"name": "Hamlet", "type": "tragedy"}, + "as-like": {"name": "As You Like It", "type": "comedy"}, + "othello": {"name": "Othello", "type": "tragedy"} +} diff --git a/python/tests/test_statement.py b/python/tests/test_statement.py new file mode 100644 index 0000000..8582f63 --- /dev/null +++ b/python/tests/test_statement.py @@ -0,0 +1,25 @@ +import json + +import pytest +from approvaltests import verify +from approvaltests.utils import get_adjacent_file + +from statement import statement + + +def test_example_statement(): + with open(get_adjacent_file("invoice.json")) as f: + invoice = json.loads(f.read()) + with open(get_adjacent_file("plays.json")) as f: + plays = json.loads(f.read()) + verify(statement(invoice, plays)) + + +def test_statement_with_new_play_types(): + with open(get_adjacent_file("invoice_new_plays.json")) as f: + invoice = json.loads(f.read()) + with open(get_adjacent_file("new_plays.json")) as f: + plays = json.loads(f.read()) + with pytest.raises(ValueError) as exception_info: + statement(invoice, plays) + assert "unknown type" in str(exception_info.value) diff --git a/python/tests/test_statement.test_example_statement.approved.txt b/python/tests/test_statement.test_example_statement.approved.txt new file mode 100644 index 0000000..9afad34 --- /dev/null +++ b/python/tests/test_statement.test_example_statement.approved.txt @@ -0,0 +1,6 @@ +Statement for BigCo + Hamlet: $650.00 (55 seats) + As You Like It: $580.00 (35 seats) + Othello: $500.00 (40 seats) +Amount owed is $1,730.00 +You earned 47 credits