From 6574165f8fb4ad2a4ab29caa73b5d19684c672ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Murta?= Date: Tue, 16 May 2023 20:09:22 +0100 Subject: [PATCH] Complete export->import cycle Add the transaction ID to the export of transactions and enable the import of SplitTransactions. The ID should function as a stable transaction identifier. Fix tag import on transactions. Fix rules import of date for DBs that need datetime.date format. --- pfbudget/db/model.py | 56 +++++++++++++++++++++++++++++++++++--------- tests/test_backup.py | 2 +- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/pfbudget/db/model.py b/pfbudget/db/model.py index 7c6782a..b556ea0 100644 --- a/pfbudget/db/model.py +++ b/pfbudget/db/model.py @@ -138,6 +138,7 @@ class Transaction(Base, Serializable): } return dict( + id=self.id, date=self.date.isoformat(), description=self.description, amount=str(self.amount), @@ -172,9 +173,9 @@ class Transaction(Base, Serializable): tags: set[TransactionTag] = set() if map["tags"]: - tags = set(t["tag"] for t in map["tags"]) + tags = set(TransactionTag(t["tag"]) for t in map["tags"]) - return cls( + result = cls( dt.date.fromisoformat(map["date"]), map["description"], map["amount"], @@ -184,6 +185,10 @@ class Transaction(Base, Serializable): map["note"], ) + if map["id"]: + result.id = map["id"] + return result + def __lt__(self, other: Transaction): return self.date < other.date @@ -227,11 +232,15 @@ class SplitTransaction(Transaction): __mapper_args__ = {"polymorphic_identity": "split", "polymorphic_load": "inline"} def serialize(self) -> Mapping[str, Any]: - raise AttributeError + map = cast(MutableMapping[str, Any], super().serialize()) + map["original"] = self.original + return map @classmethod def deserialize(cls, map: Mapping[str, Any]) -> Self: - raise AttributeError + transaction = cls._deserialize(map) + transaction.original = map["original"] + return transaction class CategoryGroup(Base, Serializable): @@ -263,8 +272,8 @@ class Category(Base, Serializable, repr=False): for rule in self.rules: rules.append( { - "start": rule.start, - "end": rule.end, + "start": rule.start.isoformat() if rule.start else None, + "end": rule.end.isoformat() if rule.end else None, "description": rule.description, "regex": rule.regex, "bank": rule.bank, @@ -290,10 +299,24 @@ class Category(Base, Serializable, repr=False): @classmethod def deserialize(cls, map: Mapping[str, Any]) -> Self: + rules: list[CategoryRule] = [] + for rule in map["rules"]: + rules.append( + CategoryRule( + dt.date.fromisoformat(rule["start"]) if rule["start"] else None, + dt.date.fromisoformat(rule["end"]) if rule["end"] else None, + rule["description"], + rule["regex"], + rule["bank"], + rule["min"], + rule["max"], + ) + ) + return cls( map["name"], map["group"], - list(CategoryRule(**r) for r in map["rules"]), + rules, CategorySchedule(**map["schedule"]) if map["schedule"] else None, ) @@ -379,10 +402,21 @@ class Tag(Base, Serializable): @classmethod def deserialize(cls, map: Mapping[str, Any]) -> Self: - return cls( - map["name"], - list(TagRule(**r) for r in map["rules"]), - ) + rules: list[TagRule] = [] + for rule in map["rules"]: + rules.append( + TagRule( + dt.date.fromisoformat(rule["start"]) if rule["start"] else None, + dt.date.fromisoformat(rule["end"]) if rule["end"] else None, + rule["description"], + rule["regex"], + rule["bank"], + rule["min"], + rule["max"], + ) + ) + + return cls(map["name"], rules) class TransactionTag(Base, unsafe_hash=True): diff --git a/tests/test_backup.py b/tests/test_backup.py index aadb064..52f306c 100644 --- a/tests/test_backup.py +++ b/tests/test_backup.py @@ -35,6 +35,7 @@ params = [ (transactions.bank, BankTransaction), (transactions.money, Transaction), (transactions.money, MoneyTransaction), + (transactions.split, SplitTransaction), ([banks.checking, banks.cc], Bank), ([categories.category_null, categories.category1, categories.category2], Category), ( @@ -50,7 +51,6 @@ params = [ ] not_serializable = [ - (transactions.split, SplitTransaction), (transactions.simple_transformed, TransactionCategory), (transactions.tagged, TransactionTag), (transactions.noted, Note),