137 lines
9.4 KiB
Python
137 lines
9.4 KiB
Python
# https://gist.github.com/dperconti/0c3ce81c1bea6593e0989ab58ee411f4
|
|
import datetime
|
|
from pprint import pprint
|
|
from statistics import mean
|
|
|
|
TRANSACTIONS = [{"transaction_id": "tx_78e3fa01b572", "transaction_date": "2023-01-01", "transaction_type": "Purchases", "amount": 5000, "description": "Online shopping"}, {"transaction_id": "tx_d2c0df115b8c", "transaction_date": "2023-01-01", "transaction_type": "Purchases", "amount": 5000, "description": "Electronics purchase"}, {"transaction_id": "tx_225a164814a3", "transaction_date": "2023-01-02", "transaction_type": "Reversals", "reversal_transaction_id": "tx_78e3fa01b572", "amount": -5000, "description": "Merchant reversal"}, {"transaction_id": "tx_9787650c8d88", "transaction_date": "2023-01-03", "transaction_type": "Purchases", "amount": 50000, "description": "Online shopping"}, {"transaction_id": "tx_26a6cf6f5c3b", "transaction_date": "2023-01-03", "transaction_type": "Purchases", "amount": 30000, "description": "Online shopping"}, {"transaction_id": "tx_4c3511be46d0", "transaction_date": "2023-01-03", "transaction_type": "Refunds", "amount": -1500, "description": "Product return"}, {"transaction_id": "tx_39b600979652", "transaction_date": "2023-01-04", "transaction_type": "Payments", "amount": -5000, "description": "Credit card payment"}, {"transaction_id": "tx_b97f4ae1960b", "transaction_date": "2023-01-05", "transaction_type": "Purchases", "amount": 75000, "description": "Grocery shopping"}, {"transaction_id": "tx_3442c1d8c5ad", "transaction_date": "2023-01-05", "transaction_type": "Purchases", "amount": 5000, "description": "Grocery shopping"}, {"transaction_id": "tx_47bf2bc1f339", "transaction_date": "2023-01-06", "transaction_type": "Reversals", "reversal_transaction_id": "", "amount": -5000, "description": "Canceled order"}, {"transaction_id": "tx_44c03c248872", "transaction_date": "2023-01-07", "transaction_type": "Payments", "amount": -8000, "description": "Monthly bill payment"}, {"transaction_id": "tx_ca724c1f4489", "transaction_date": "2023-01-08", "transaction_type": "Purchases", "amount": 3200, "description": "Electronics purchase"}, {"transaction_id": "tx_8de806648612", "transaction_date": "2023-01-09", "transaction_type": "Payments", "amount": -1500, "description": "Partial payment"}, {"transaction_id": "tx_b2da976e7b15", "transaction_date": "2023-01-10", "transaction_type": "Purchases", "amount": 10000, "description": "Flight booking"}, {"transaction_id": "tx_73ab9d775507", "transaction_date": "2023-01-13", "transaction_type": "Purchases", "amount": 2000, "description": "Clothing purchase"}, {"transaction_id": "tx_6e906b3ac60b", "transaction_date": "2023-01-15", "reversal_transaction_id": "tx_b2da976e7b15", "transaction_type": "Reversals", "amount": -1000, "description": "Refunded order"}, {"transaction_id": "tx_7500b7c3f436", "transaction_date": "2023-01-15", "transaction_type": "Purchases", "amount": 5000, "description": "Medical bill payment"}, {"transaction_id": "tx_58e9c4fbe73c", "transaction_date": "2023-01-16", "transaction_type": "Purchases", "amount": 15000, "description": "Restaurant bill"}, {"transaction_id": "tx_c67fcd8b7ca9", "transaction_date": "2023-01-18", "transaction_type": "Payments", "amount": 2000, "description": "Utility bill payment"}, {"transaction_id": "tx_35575c16717f", "transaction_date": "2023-01-18", "transaction_type": "Purchases", "amount": 6000, "description": "Home appliance purchase"}, {"transaction_id": "tx_fe9cdf650dba", "transaction_date": "2023-01-18", "transaction_type": "Refunds", "amount": -1000, "description": "Canceled subscription refund"}, {"transaction_id": "tx_87d993379c5f", "transaction_date": "2023-01-20", "transaction_type": "Purchases", "amount": 3500, "description": "Insurance premium payment"}, {"transaction_id": "tx_ac1e68c1cb15", "transaction_date": "2023-01-21", "transaction_type": "Purchases", "amount": 4500, "description": "Gadget purchase"}, {"transaction_id": "tx_ee1575994652", "transaction_date": "2023-01-23", "transaction_type": "Purchases", "amount": 1200, "description": "Loan installment payment"}, {"transaction_id": "tx_2d6bd63cef9f", "transaction_date": "2023-01-23", "transaction_type": "Purchases", "amount": 1800, "description": "Book purchase"}, {"transaction_id": "tx_90b40faf5710", "transaction_date": "2023-01-23", "transaction_type": "Reversals", "reversal_transaction_id": "tx_ac1e68c1cb15", "amount": -4500, "description": "Gadget Reversal"}, {"transaction_id": "tx_3d6c0567257d", "transaction_date": "2023-01-25", "transaction_type": "Payments", "amount": -9000, "description": "Random repayment"}, {"transaction_id": "tx_39f0d051637a", "transaction_date": "2023-01-26", "transaction_type": "Purchases", "amount": 2500, "description": "Gift purchase"}, {"transaction_id": "tx_d7d59bdc5876", "transaction_date": "2023-01-27", "transaction_type": "Purchases", "amount": 1800, "description": "Education loan payment"}, {"transaction_id": "tx_d2c0df115b8c", "transaction_date": "2023-01-28", "transaction_type": "Purchases", "amount": 4200, "description": "Electronics purchase"}, {"transaction_id": "tx_7254c3c1dc10", "transaction_date": "2023-01-29", "transaction_type": "Payments", "amount": -2500, "description": "Credit card bill payment"}, {"transaction_id": "tx_7254c3c1dc10", "transaction_date": "2023-01-29", "transaction_type": "Payments", "amount": -188700, "description": "Balance Payoff"}]
|
|
YMD = "%Y-%m-%d"
|
|
|
|
|
|
class Transactions:
|
|
def __init__(self, transactions, interest=12.34):
|
|
self.interest = interest / 100
|
|
self.transactions = transactions
|
|
self.start_date = datetime.datetime.strptime("2023-01-01", YMD)
|
|
self.last_date = self._find_last_date()
|
|
self.statement_dates = self._statement_dates()
|
|
self.interest_rate = interest
|
|
self.statements = {}
|
|
self.calculate_statements()
|
|
|
|
def _find_last_date(self):
|
|
dates = sorted(t["transaction_date"] for t in self.transactions)
|
|
if dates:
|
|
return datetime.datetime.strptime(dates[-1], YMD)
|
|
return self.start_date
|
|
|
|
def _statement_dates(self):
|
|
length = 7
|
|
current = self.start_date
|
|
target = self.last_date
|
|
|
|
step = datetime.timedelta(days=(length - 1))
|
|
|
|
dates = []
|
|
while current <= target:
|
|
chunk = [
|
|
current,
|
|
]
|
|
current += step
|
|
if current > target:
|
|
current = target
|
|
chunk.append(current)
|
|
current += datetime.timedelta(days=1)
|
|
dates.append(chunk)
|
|
return list(map(tuple, dates))
|
|
|
|
def balance(self, transactions=None):
|
|
if not transactions:
|
|
# Handle any set of transactions
|
|
transactions = self.transactions
|
|
if not transactions:
|
|
return None
|
|
return sum(t["amount"] for t in transactions)
|
|
|
|
def average(self):
|
|
if not self.transactions:
|
|
return None
|
|
return mean(
|
|
t["amount"]
|
|
for t in self.transactions
|
|
if t["transaction_type"] == "Purchases"
|
|
)
|
|
|
|
def largest(self):
|
|
if not self.transactions:
|
|
return None
|
|
return max(abs(t["amount"]) for t in self.transactions) # Largest by Volume
|
|
# return max(t["amount"] for t in self.transactions if t['amount'] > 0) # Highest amount over zero
|
|
|
|
def calculate_statements(self):
|
|
cursor = 0
|
|
statements = {}
|
|
keys = self.statement_dates
|
|
for k in keys:
|
|
statements[k] = {"transactions": [], "total": 0}
|
|
|
|
for txn in self.transactions:
|
|
txn_date = datetime.datetime.strptime(txn["transaction_date"], YMD)
|
|
if not (keys[cursor][0] <= txn_date <= keys[cursor][1]):
|
|
cursor += 1
|
|
statements[keys[cursor]]["transactions"].append(txn)
|
|
|
|
self.statements = statements
|
|
return statements
|
|
|
|
def calculate_interest(self):
|
|
running_balance = 0
|
|
for dates, statement in self.statements.items():
|
|
total = self.balance(statement["transactions"])
|
|
running_balance += total
|
|
statement["total"] = total
|
|
statement["interest"] = 0
|
|
if total > 0:
|
|
statement["interest"] = self.interest * total
|
|
|
|
def statements_print_formatted(self):
|
|
total = 0
|
|
for start_stop, statement in self.statements.items():
|
|
start = datetime.datetime.strftime(start_stop[0], YMD)
|
|
stop = datetime.datetime.strftime(start_stop[1], YMD)
|
|
out = f"Date Range: {start} -> {stop}\n"
|
|
for txn in statement["transactions"]:
|
|
total += txn["amount"]
|
|
out += (
|
|
f"* {txn['transaction_date']}"
|
|
f": {txn['transaction_id']}"
|
|
f": {txn['transaction_type']: <9}"
|
|
f"\t{txn['amount']}"
|
|
f"\t{total}"
|
|
f"\t{txn['description']}"
|
|
)
|
|
out += "\n"
|
|
out += f"------------- TOTAL:{statement['total']}, {statement['interest']:.2f}\n"
|
|
|
|
print(out)
|
|
|
|
|
|
def interview():
|
|
txns = Transactions(TRANSACTIONS, interest=12.34)
|
|
|
|
balance = txns.balance()
|
|
average = txns.average()
|
|
largest = txns.largest()
|
|
|
|
pprint(locals())
|
|
print()
|
|
|
|
txns.calculate_interest()
|
|
txns.statements_print_formatted()
|
|
|
|
|
|
# Press the green button in the gutter to run the script.
|
|
if __name__ == "__main__":
|
|
interview()
|
|
|
|
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
|