diff options
Diffstat (limited to 'src/journal_lib/dataclasses.py')
-rw-r--r-- | src/journal_lib/dataclasses.py | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/src/journal_lib/dataclasses.py b/src/journal_lib/dataclasses.py new file mode 100644 index 0000000..227cabe --- /dev/null +++ b/src/journal_lib/dataclasses.py @@ -0,0 +1,135 @@ +import sys +from dataclasses import dataclass + +FORCE_NOCOLOR = False + +def set_force_nocolor(value: bool): + global FORCE_NOCOLOR + FORCE_NOCOLOR = value + +def anesc(code: str): + return "" if FORCE_NOCOLOR or not sys.stdout.isatty() else f"\u001b[{code}" + +def format_amount(amount: str, currency: str | None = None) -> str: + if currency is None: + return amount + if currency in ['NOK']: + return f"{amount} {currency}" + if currency in ['$']: + return f"{currency}{amount}" + return f"{amount} {currency}" + +@dataclass +class JournalEntryTransaction: + account: str + currency: str | None + amount: str | None + comment: str | None + +@dataclass +class JournalEntry: + date: str + cleared: bool + pending: bool + title: str + effective_date: str | None + transactions: list[JournalEntryTransaction] + comments: list[str] + + def __str__(self): + s = f"{anesc('34m')}{self.date}{anesc('0m')}" + if self.effective_date is not None: + s += f"={anesc('36m')}{self.effective_date}{anesc('0m')}" + if self.cleared: + s += f" {anesc('31m')}*{anesc('0m')}" + if self.pending: + s += f" {anesc('31m')}!{anesc('0m')}" + s += f" {anesc('36m')}{self.title}{anesc('0m')}\n" + + max_account_len = max(len(x.account) for x in self.transactions) + for transaction in self.transactions: + t = f" {anesc('33m')}{transaction.account:{max_account_len}}{anesc('0m')}" + if transaction.amount is not None: + amount_code = "32m" + if (transaction.amount[0] == "-"): + amount_code = "31m" + t += f" {anesc(amount_code)}{format_amount(transaction.amount, transaction.currency)}{anesc('0m')}" + if transaction.comment is not None: + t += f" {anesc('38m')}{transaction.comment}{anesc('0m')}" + s += t + f"{anesc('0m')}\n" + + for comment in self.comments: + s += f" {anesc('38m')}{comment}{anesc('0m')}\n" + return s + "\n" + +@dataclass +class JournalAccountDef: + account: str + comment: str | None + + def __str__(self): + s = f"{anesc('38m')}account {anesc('33m')}{self.account}{anesc('0m')}" + if self.comment is not None: + s += f" {anesc('38m')}{self.comment}{anesc('0m')}" + return s + "\n" + +@dataclass +class JournalCommodityDef: + commodity: str + format: str | None = None + note: str | None = None + nomarket: bool = False + default: bool = False + + def __str__(self): + s = f"{anesc('38m')}commodity {anesc('33m')}{self.commodity}{anesc('0m')}\n" + for attr in ["format", "note", "nomarket", "default"]: + if hasattr(self, attr) and getattr(self, attr) is not None: + if isinstance(getattr(self, attr), bool) and not getattr(self, attr): + continue + s += f" {anesc('38m')}{attr}{anesc('0m')}" + if isinstance(getattr(self, attr), str): + s += f" {anesc('36m')}{getattr(self, attr)}{anesc('0m')}" + s += "\n" + return s + +@dataclass +class Journal: + entries: list[JournalEntry] + accounts: list[JournalAccountDef] + commodities: list[JournalCommodityDef] + + def __str__(self): + s = "" + + for account in self.accounts: + s += f"{account}" + s += "\n" + + for commodity in self.commodities: + s += f"{commodity}" + s += "\n" + + for entry in self.entries: + s += f"{entry}" + + return s + + @staticmethod + def from_elements(elements: list[str | JournalEntry]): + entries = [] + accounts = [] + commodities = [] + + for element in elements: + if isinstance(element, JournalEntry): + entries.append(element) + elif isinstance(element, JournalAccountDef): + accounts.append(element) + elif isinstance(element, JournalCommodityDef): + commodities.append(element) + else: + print(f"INVALID ELEMENT {element}") + + return Journal(entries=entries, accounts=accounts, commodities=commodities) + |