#!/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
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."""
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[:]
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."""
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:
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)
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)
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."""