From 8760f5a0a4853f59c093ded6d21850a82c62c264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Murta?= Date: Mon, 23 Jan 2023 00:06:36 +0000 Subject: [PATCH] Export/Import categories and groups --- pfbudget/__main__.py | 4 ++++ pfbudget/cli/runnable.py | 16 ++++++++++++++++ pfbudget/common/types.py | 4 ++++ pfbudget/core/manager.py | 36 ++++++++++++++++++++++++++++++++++++ pfbudget/db/model.py | 28 +++++++++++++++++++++++++--- 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/pfbudget/__main__.py b/pfbudget/__main__.py index 0ceb1cc..06259ac 100644 --- a/pfbudget/__main__.py +++ b/pfbudget/__main__.py @@ -241,6 +241,10 @@ if __name__ == "__main__": | pfbudget.Operation.ImportCategoryRules | pfbudget.Operation.ExportTagRules | pfbudget.Operation.ImportTagRules + | pfbudget.Operation.ExportCategories + | pfbudget.Operation.ImportCategories + | pfbudget.Operation.ExportCategoryGroups + | pfbudget.Operation.ImportCategoryGroups ): keys = {"file"} assert args.keys() >= keys, f"missing {args.keys() - keys}" diff --git a/pfbudget/cli/runnable.py b/pfbudget/cli/runnable.py index 207848f..e174224 100644 --- a/pfbudget/cli/runnable.py +++ b/pfbudget/cli/runnable.py @@ -286,6 +286,14 @@ def category(parser: argparse.ArgumentParser): group = commands.add_parser("group") category_group(group) + export = commands.add_parser("export") + export.set_defaults(op=Operation.ExportCategories) + export_args(export) + + pimport = commands.add_parser("import") + pimport.set_defaults(op=Operation.ImportCategories) + export_args(pimport) + def category_group(parser: argparse.ArgumentParser): commands = parser.add_subparsers(required=True) @@ -298,6 +306,14 @@ def category_group(parser: argparse.ArgumentParser): remove.set_defaults(op=Operation.GroupRemove) remove.add_argument("group", nargs="+", type=str) + export = commands.add_parser("export") + export.set_defaults(op=Operation.ExportCategoryGroups) + export_args(export) + + pimport = commands.add_parser("import") + pimport.set_defaults(op=Operation.ImportCategoryGroups) + export_args(pimport) + def category_rule(parser: argparse.ArgumentParser): commands = parser.add_subparsers(required=True) diff --git a/pfbudget/common/types.py b/pfbudget/common/types.py index ae533c2..0ce7d54 100644 --- a/pfbudget/common/types.py +++ b/pfbudget/common/types.py @@ -43,6 +43,10 @@ class Operation(Enum): ImportCategoryRules = auto() ExportTagRules = auto() ImportTagRules = auto() + ExportCategories = auto() + ImportCategories = auto() + ExportCategoryGroups = auto() + ImportCategoryGroups = auto() class TransactionError(Exception): diff --git a/pfbudget/core/manager.py b/pfbudget/core/manager.py index f50eec9..ccae418 100644 --- a/pfbudget/core/manager.py +++ b/pfbudget/core/manager.py @@ -11,6 +11,7 @@ from pfbudget.db.model import ( Category, CategoryGroup, CategoryRule, + CategorySchedule, CategorySelector, Link, MoneyTransaction, @@ -255,6 +256,41 @@ class Manager: with self.db.session() as session: session.add(rules) + case Operation.ExportCategories: + with self.db.session() as session: + self.dump(params[0], session.get(Category)) + + case Operation.ImportCategories: + # rules = [Category(**row) for row in self.load(params[0])] + categories = [] + for row in self.load(params[0]): + category = Category(row["name"], row["group"]) + if len(row["rules"]) > 0: + # Only category rules could have been created with a rule + rules = row["rules"] + for rule in rules: + del rule["type"] + + category.rules = set(CategoryRule(**rule) for rule in rules) + if row["schedule"]: + category.schedule = CategorySchedule(**row["schedule"]) + categories.append(category) + + if self.certify(categories): + with self.db.session() as session: + session.add(categories) + + case Operation.ExportCategoryGroups: + with self.db.session() as session: + self.dump(params[0], session.get(CategoryGroup)) + + case Operation.ImportCategoryGroups: + groups = [CategoryGroup(**row) for row in self.load(params[0])] + + if self.certify(groups): + with self.db.session() as session: + session.add(groups) + def parse(self, filename: Path, args: dict): return parse_data(filename, args) diff --git a/pfbudget/db/model.py b/pfbudget/db/model.py index a1e888c..a00dec4 100644 --- a/pfbudget/db/model.py +++ b/pfbudget/db/model.py @@ -146,13 +146,17 @@ class SplitTransaction(Transaction): return super().format | dict(original=self.original) -class CategoryGroup(Base): +class CategoryGroup(Base, Export): __tablename__ = "categories_groups" name: Mapped[str] = mapped_column(primary_key=True) + @property + def format(self) -> dict[str, Any]: + return dict(name=self.name) -class Category(Base): + +class Category(Base, Export): __tablename__ = "categories_available" name: Mapped[str] = mapped_column(primary_key=True) @@ -170,6 +174,15 @@ class Category(Base): def __repr__(self) -> str: return f"Category(name={self.name}, group={self.group}, #rules={len(self.rules)}, schedule={self.schedule})" + @property + def format(self) -> dict[str, Any]: + return dict( + name=self.name, + group=self.group.format if self.group else None, + rules=[rule.format for rule in self.rules], + schedule=self.schedule.format if self.schedule else None, + ) + catfk = Annotated[ str, @@ -282,7 +295,7 @@ class Period(enum.Enum): scheduleperiod = Annotated[Selector, mapped_column(Enum(Period, inherit_schema=True))] -class CategorySchedule(Base): +class CategorySchedule(Base, Export): __tablename__ = "categories_schedules" name: Mapped[catfk] = mapped_column(primary_key=True) @@ -290,6 +303,15 @@ class CategorySchedule(Base): period_multiplier: Mapped[Optional[int]] amount: Mapped[Optional[int]] + @property + def format(self) -> dict[str, Any]: + return dict( + name=self.name, + period=self.period, + period_multiplier=self.period_multiplier, + amount=self.amount, + ) + class Link(Base): __tablename__ = "links"