From: Christian Heller Date: Tue, 4 Feb 2025 13:29:37 +0000 (+0100) Subject: Refactor .as_dict properties. X-Git-Url: https://plomlompom.com/repos/%7B%7B%20web_path%20%7D%7D/cards/%7B%7B%20card_id%20%7D%7D/static/processes?a=commitdiff_plain;p=plomledger Refactor .as_dict properties. --- diff --git a/ledger.py b/ledger.py index 30f93dd..614fef5 100755 --- a/ledger.py +++ b/ledger.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 """Viewer for ledger .dat files.""" -from abc import ABC, abstractmethod from datetime import date as dt_date from decimal import Decimal, InvalidOperation as DecimalInvalidOperation from os import environ @@ -26,6 +25,24 @@ LEDGER_STRUCT = f'{PREFIX_LEDGER}{TOK_STRUCT}' LEDGER_RAW = f'{PREFIX_LEDGER}{TOK_RAW}' +class Dictable: + """Line abstraction featuring .as_dict property.""" + dictables: set[str] = set() + + @property + def as_dict(self) -> dict[str, Any]: + """Return as JSON-ready dict attributes listed in .dictables.""" + d = {} + for name in self.dictables: + value = getattr(self, name) + if hasattr(value, 'as_dict'): + value = value.as_dict + elif not isinstance(value, (str, int)): + value = str(value) + d[name] = value + return d + + class Wealth: """Collects amounts mapped to currencies.""" @@ -98,17 +115,9 @@ class Account: return sorted(list(names)) -class Line(ABC): - """Line abstraction featuring .as_dict property.""" - - @property - @abstractmethod - def as_dict(self) -> dict: - """Return as JSON-ready dict.""" - - -class DatLine(Line): +class DatLine(Dictable): """Line of .dat file parsed into comments and machine-readable data.""" + dictables = {'booking_line', 'code', 'comment', 'error', 'is_intro'} def __init__(self, line: str) -> None: self.raw = line[:] @@ -117,13 +126,6 @@ class DatLine(Line): self.code = halves[0] self.booking_line: Optional[BookingLine] = None - @property - def as_dict(self) -> dict: - assert self.booking_line is not None - return {'comment': self.comment, 'code': self.code, - 'is_intro': self.is_intro, 'error': self.error, - 'booking_line': self.booking_line.as_dict} - @property def is_intro(self) -> bool: """Return if intro line of a Booking.""" @@ -163,7 +165,7 @@ class DatLine(Line): return self.raw.replace(' ', ' ') -class BookingLine(Line): +class BookingLine(Dictable): """Parsed code part of a DatLine belonging to a Booking.""" def __init__(self, booking: 'Booking') -> None: @@ -174,6 +176,7 @@ class BookingLine(Line): class IntroLine(BookingLine): """First line of a Booking, expected to carry date etc.""" + dictables = {'date', 'target'} def __init__(self, booking: 'Booking', code: str) -> None: super().__init__(booking) @@ -189,13 +192,10 @@ class IntroLine(BookingLine): except ValueError: self.errors += [f'not properly formatted legal date: {self.date}'] - @property - def as_dict(self) -> dict: - return {'date': self.date, 'target': self.target} - class TransferLine(BookingLine): """Non-first Booking line, expected to carry value movement.""" + dictables = {'amount', 'account', 'currency'} def __init__(self, booking: 'Booking', code: str, idx: int) -> None: super().__init__(booking) @@ -224,11 +224,6 @@ class TransferLine(BookingLine): return f'{self.amount:.1f}…' if exp < -2 else f'{self.amount:.2f}' return '' - @property - def as_dict(self) -> dict: - return {'account': self.account, 'currency': self.currency, - 'amount': str(self.amount)} - class Booking: """Represents lines of individual booking."""