From a355ec3642f91e7cea559675dc8ae2c1e04d0783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Murta?= Date: Tue, 2 May 2023 23:07:44 +0100 Subject: [PATCH] Initial work on a full backup option Creates the Backup command and a general serializer. --- pfbudget/common/types.py | 6 ++++++ pfbudget/core/command.py | 40 ++++++++++++++++++++++++++++++++++++ pfbudget/utils/serializer.py | 21 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 pfbudget/core/command.py create mode 100644 pfbudget/utils/serializer.py diff --git a/pfbudget/common/types.py b/pfbudget/common/types.py index 7f50ef5..0f50d5f 100644 --- a/pfbudget/common/types.py +++ b/pfbudget/common/types.py @@ -51,6 +51,12 @@ class Operation(Enum): ImportCategoryGroups = auto() +class ExportFormat(Enum): + CSV = auto() + JSON = auto() + pickle = auto() + + class TransactionError(Exception): pass diff --git a/pfbudget/core/command.py b/pfbudget/core/command.py new file mode 100644 index 0000000..0d01e92 --- /dev/null +++ b/pfbudget/core/command.py @@ -0,0 +1,40 @@ +from abc import ABC, abstractmethod +import csv +import json +from pathlib import Path +import pickle +from typing import Any, Type + +from pfbudget.common.types import ExportFormat +from pfbudget.db.client import Client +from pfbudget.utils.serializer import serialize + + +class Command(ABC): + @abstractmethod + def execute(self) -> None: + raise NotImplementedError + + def undo(self) -> None: + raise NotImplementedError + + +class ExportCommand(Command): + def __init__(self, client: Client, what: Type[Any], fn: Path, format: ExportFormat): + self.__client = client + self.what = what + self.fn = fn + self.format = format + + def execute(self) -> None: + values = self.__client.select(self.what) + match self.format: + case ExportFormat.CSV: + with open(self.fn, "w", newline="") as f: + csv.writer(f).writerows([serialize(e) for e in values]) + case ExportFormat.JSON: + with open(self.fn, "w", newline="") as f: + json.dump([serialize(e) for e in values], f, indent=4, default=str) + case ExportFormat.pickle: + with open(self.fn, "wb") as f: + pickle.dump([serialize(e) for e in values], f) diff --git a/pfbudget/utils/serializer.py b/pfbudget/utils/serializer.py new file mode 100644 index 0000000..6cb5267 --- /dev/null +++ b/pfbudget/utils/serializer.py @@ -0,0 +1,21 @@ +from collections.abc import Mapping +from dataclasses import fields +from functools import singledispatch +from typing import Any + +from pfbudget.db.model import Transaction, TransactionCategory + + +@singledispatch +def serialize(obj: Any) -> Mapping[str, Any]: + raise NotImplementedError + + +@serialize.register +def _(obj: Transaction) -> Mapping[str, Any]: + return dict((field.name, getattr(obj, field.name)) for field in fields(obj)) + + +@serialize.register +def _(obj: TransactionCategory) -> Mapping[str, Any]: + return dict((field.name, getattr(obj, field.name)) for field in fields(obj))