home · contact · privacy
More warning/error/validation code refactoring.
authorChristian Heller <c.heller@plomlompom.de>
Wed, 22 Jan 2025 12:02:33 +0000 (13:02 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 22 Jan 2025 12:02:33 +0000 (13:02 +0100)
ledger.py

index 5fc80a40839f7bb4e499d637241b1b217337c2e8..51c722b4d48f351f3cf94232cd78c159961370e8 100755 (executable)
--- a/ledger.py
+++ b/ledger.py
@@ -71,7 +71,7 @@ class DatLine:
     @property
     def error(self) -> str:
         """Return error if registered on attempt to parse into BookingLine."""
-        return self.booking_line.error if self.booking_line else ''
+        return '; '.join(self.booking_line.errors) if self.booking_line else ''
 
     @property
     def questionable(self) -> bool:
@@ -96,7 +96,7 @@ class BookingLine:
     """Parsed code part of a DatLine belonging to a Booking."""
 
     def __init__(self, booking: 'Booking') -> None:
-        self.error = ''
+        self.errors: list[str] = []
         self.booking = booking
 
 
@@ -106,15 +106,16 @@ class IntroLine(BookingLine):
     def __init__(self, booking: 'Booking', code: str) -> None:
         super().__init__(booking)
         if code[0].isspace():
-            self.error = 'intro line indented'
+            self.errors += ['intro line indented']
         toks = code.lstrip().split(maxsplit=1)
         if len(toks) != 2:
-            self.error = 'illegal number of tokens'
+            self.errors += ['illegal number of tokens']
+        if len(toks) < 1:
             return
         try:
             dt_date.fromisoformat(toks[0])
         except ValueError:
-            self.error = 'not starting with properly formatted legal date'
+            self.errors += [f'not properly formatted legal date: {toks[0]}']
 
 
 class TransferLine(BookingLine):
@@ -123,21 +124,20 @@ class TransferLine(BookingLine):
     def __init__(self, booking: 'Booking', code: str) -> None:
         super().__init__(booking)
         if not code[0].isspace():
-            self.error = 'transfer line not indented'
-            return
+            self.errors += ['transfer line not indented']
         toks = code.lstrip().split()
         self.account = toks[0]
         self.amount: Optional[Decimal] = None
         if 1 == len(toks):
             self.currency = ''
-        elif 3 == len(toks):
+        elif 3 <= len(toks):
             self.currency = toks[2]
             try:
                 self.amount = Decimal(toks[1])
             except DecimalInvalidOperation:
-                self.error = 'improper amount value'
-        else:
-            self.error = 'illegal number of tokens'
+                self.errors += [f'improper amount value: {toks[1]}']
+        if len(toks) not in {1, 3}:
+            self.errors += ['illegal number of tokens']
 
     @property
     def amount_short(self) -> str:
@@ -155,34 +155,32 @@ class Booking:
 
     def __init__(self, id_: int, dat_lines: list[DatLine]) -> None:
         self.id_ = id_
-        self.dat_lines = dat_lines
-        self.dat_lines[0].booking_line = IntroLine(self,
-                                                   self.dat_lines[0].code)
-        for dat_line in self.dat_lines[1:]:
+        dat_lines[0].booking_line = IntroLine(self, dat_lines[0].code)
+        intro_line = dat_lines[0].booking_line
+        transfer_lines = []
+        for dat_line in dat_lines[1:]:
             dat_line.booking_line = TransferLine(self, dat_line.code)
+            transfer_lines += [dat_line.booking_line]
         self.account_changes: dict[str, Wealth] = {}
         self.sink_account = None
         changes = Wealth()
-        for transfer_line in [dl.booking_line for dl in self.dat_lines[1:]]:
-            assert isinstance(transfer_line, TransferLine)
+        for transfer_line in [tl for tl in transfer_lines if not tl.errors]:
             if transfer_line.account not in self.account_changes:
                 self.account_changes[transfer_line.account] = Wealth()
             if transfer_line.amount is None:
                 if self.sink_account:
-                    transfer_line.error = 'too many sinks'
-                    break
+                    transfer_line.errors += ['too many sinks']
                 self.sink_account = transfer_line.account
                 continue
             change = Wealth({transfer_line.currency: transfer_line.amount})
             self.account_changes[transfer_line.account] += change
             changes += change
-        self.questionable = False
         if self.sink_account:
             self.account_changes[self.sink_account] += changes.as_sink
         elif not changes.sink_empty:
-            assert self.dat_lines[-1].booking_line is not None
-            self.dat_lines[-1].booking_line.error = 'needed sink missing'
-        for dat_line in [dl for dl in self.dat_lines if dl.error]:
+            transfer_lines[-1].errors += ['needed sink missing']
+        self.questionable = False
+        for _ in [bl for bl in [intro_line] + transfer_lines if bl.errors]:
             self.questionable = True
             break