aboutsummaryrefslogtreecommitdiff
path: root/src/journal_lib/dataclasses.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/journal_lib/dataclasses.py')
-rw-r--r--src/journal_lib/dataclasses.py135
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)
+