Expand serializer to more types and test

This commit is contained in:
Luís Murta 2024-01-22 21:27:15 +00:00
parent 6057ec59b4
commit 60c7028f0b
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
7 changed files with 166 additions and 8 deletions

View File

@ -30,7 +30,7 @@ class ExportCommand(Command):
match self.format: match self.format:
case ExportFormat.JSON: case ExportFormat.JSON:
with open(self.fn, "w", newline="") as f: with open(self.fn, "w", newline="") as f:
json.dump([serialize(e) for e in values], f, indent=4, default=str) json.dump([serialize(e) for e in values], f, indent=4)
case ExportFormat.pickle: case ExportFormat.pickle:
with open(self.fn, "wb") as f: with open(self.fn, "wb") as f:
pickle.dump([serialize(e) for e in values], f) pickle.dump(values, f)

View File

@ -3,19 +3,52 @@ from dataclasses import fields
from functools import singledispatch from functools import singledispatch
from typing import Any from typing import Any
from pfbudget.db.model import Transaction, TransactionCategory from pfbudget.db.model import Transaction, TransactionCategory, TransactionTag, Note
class NotSerializableError(Exception):
pass
@singledispatch @singledispatch
def serialize(obj: Any) -> Mapping[str, Any]: def serialize(obj: Any) -> Mapping[str, Any]:
raise NotImplementedError return {field.name: getattr(obj, field.name) for field in fields(obj)}
@serialize.register @serialize.register
def _(obj: Transaction) -> Mapping[str, Any]: def _(obj: Transaction) -> Mapping[str, Any]:
return dict((field.name, getattr(obj, field.name)) for field in fields(obj)) category = None
if obj.category:
category = {
"name": obj.category.name,
"selector": str(obj.category.selector.selector)
if obj.category.selector
else None,
}
return dict(
id=obj.id,
date=obj.date.isoformat(),
description=obj.description,
amount=str(obj.amount),
split=obj.split,
category=category if category else None,
tags=[{"tag": tag.tag} for tag in obj.tags],
note=obj.note,
type=obj.type,
)
@serialize.register @serialize.register
def _(obj: TransactionCategory) -> Mapping[str, Any]: def _(_: TransactionCategory) -> Mapping[str, Any]:
return dict((field.name, getattr(obj, field.name)) for field in fields(obj)) raise NotSerializableError("TransactionCategory")
@serialize.register
def _(_: TransactionTag) -> Mapping[str, Any]:
raise NotSerializableError("TransactionTag")
@serialize.register
def _(_: Note) -> Mapping[str, Any]:
raise NotSerializableError("Note")

4
tests/mocks/banks.py Normal file
View File

@ -0,0 +1,4 @@
from pfbudget.db.model import AccountType, Bank
checking = Bank("bank", "BANK", AccountType.checking)

View File

@ -1,3 +1,6 @@
from pfbudget.extract.nordigen import NordigenCredentials
id = "3fa85f64-5717-4562-b3fc-2c963f66afa6" id = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
accounts_id = { accounts_id = {
@ -80,3 +83,5 @@ requisitions_id = {
"account_selection": False, "account_selection": False,
"redirect_immediate": False, "redirect_immediate": False,
} }
credentials = NordigenCredentials("ID", "KEY", "TOKEN")

View File

@ -0,0 +1,33 @@
from datetime import date
from decimal import Decimal
from pfbudget.db.model import (
CategorySelector,
Selector_T,
Transaction,
TransactionCategory,
)
simple = [
Transaction(date(2023, 1, 1), "", Decimal("-10")),
Transaction(date(2023, 1, 2), "", Decimal("-50")),
]
simple_transformed = [
Transaction(
date(2023, 1, 1),
"",
Decimal("-10"),
category=TransactionCategory(
"category#1", CategorySelector(Selector_T.algorithm)
),
),
Transaction(
date(2023, 1, 2),
"",
Decimal("-50"),
category=TransactionCategory(
"category#2", CategorySelector(Selector_T.algorithm)
),
),
]

78
tests/test_command.py Normal file
View File

@ -0,0 +1,78 @@
from collections.abc import Sequence
import json
from pathlib import Path
import pickle
from typing import Any
import mocks.transactions
from pfbudget.common.types import ExportFormat
from pfbudget.core.command import ExportCommand
from pfbudget.db.client import Client
from pfbudget.db.model import Transaction
from pfbudget.utils.serializer import serialize
class FakeClient(Client):
def __init__(self):
self._transactions = mocks.transactions.simple
def select(self, what: Any, *_) -> Sequence[Any]:
if what == Transaction:
return self.transactions
return []
def insert(self, *_):
pass
def update(self, *_):
pass
def delete(self, *_):
pass
@property
def transactions(self):
return self._transactions
@transactions.setter
def transactions(self, value: Sequence[Transaction]):
self._transactions = value
class TestCommand:
def test_export_json(self, tmp_path: Path):
client = FakeClient()
file = tmp_path / "test.json"
command = ExportCommand(client, Transaction, file, ExportFormat.JSON)
command.execute()
with open(file, newline="") as f:
result = json.load(f)
assert result == [serialize(t) for t in mocks.transactions.simple]
client.transactions = mocks.transactions.simple_transformed
command.execute()
with open(file, newline="") as f:
result = json.load(f)
assert result == [
serialize(t) for t in mocks.transactions.simple_transformed
]
def test_export_pickle(self, tmp_path: Path):
client = FakeClient()
file = tmp_path / "test.pickle"
command = ExportCommand(client, Transaction, file, ExportFormat.pickle)
command.execute()
with open(file, "rb") as f:
result = pickle.load(f)
assert result == mocks.transactions.simple
client.transactions = mocks.transactions.simple_transformed
command.execute()
with open(file, "rb") as f:
result = pickle.load(f)
assert result == mocks.transactions.simple_transformed

View File

@ -88,7 +88,12 @@ class TestExtractPSD2:
with pytest.raises(requests.Timeout): with pytest.raises(requests.Timeout):
extractor.extract(bank) extractor.extract(bank)
def test_extract(self, extractor: Extractor, bank: Bank): def test_extract(
self, monkeypatch: pytest.MonkeyPatch, extractor: Extractor, bank: Bank
):
monkeypatch.setattr(
"pfbudget.extract.nordigen.NordigenClient.dump", lambda *args: None
)
assert extractor.extract(bank) == [ assert extractor.extract(bank) == [
BankTransaction( BankTransaction(
dt.date(2023, 1, 14), "string", Decimal("328.18"), bank="Bank#1" dt.date(2023, 1, 14), "string", Decimal("328.18"), bank="Bank#1"