From 226f5afa968ead223baa5a1b0fafb481f9761f0f Mon Sep 17 00:00:00 2001 From: xengineering Date: Wed, 4 Sep 2024 21:09:42 +0200 Subject: Refactor Flow.integrate() This refactoring introduces a generator function supporting the integration. It creates datetime candidates where it can be checked if they are in the time interval given to the integration method. Furthermore a numerical improvement was made. The Flow.integrate() function now checks candidates from the helper function and sums the matching candidates up as an integer. After checking all candidates the integer is converted to a decimal and multiplied by Flow.amount. This is more accurate because in Python decimal calculation not numbers or calculations are rounded, just the result of a calculation. Looping over the candidates and adding amount each time thus involves a rounding operation per candidate. The new algorithm has exactly one rounding operation. --- finance/flow.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'finance') diff --git a/finance/flow.py b/finance/flow.py index fb28228..a5817af 100644 --- a/finance/flow.py +++ b/finance/flow.py @@ -1,6 +1,7 @@ import dataclasses from datetime import datetime from decimal import Decimal +from typing import Generator @dataclasses.dataclass(kw_only=True, frozen=True) @@ -12,22 +13,22 @@ class Flow: def integrate(self, start: datetime, end: datetime) -> Decimal: """Integrate the flow between two dates to an amount of money""" - retval = Decimal(0.0) + payments: int = 0 - current = datetime(start.year, start.month, 1) - - if start == current: - retval += self.amount + for candidate in monthly_candidates(start): + if start <= candidate <= end: + payments += 1 + if candidate > end: + break - while True: - if current.month == 12: - current = datetime(current.year + 1, 1, 1) - else: - current = datetime(current.year, current.month + 1, 1) + return self.amount * Decimal(payments) - if current <= end: - retval += self.amount - else: - break - return retval +def monthly_candidates(start: datetime) -> Generator[datetime, None, None]: + current = datetime(start.year, start.month, 1) + while True: + yield current + if current.month == 12: + current = datetime(current.year + 1, 1, 1) + else: + current = datetime(current.year, current.month + 1, 1) -- cgit v1.2.3-70-g09d2