Adds command line option to add/remove categories
Implements the argument parser, the manager logic and the DB client methods. Encapsulates the DbClient connection under the _db attribute on the manager. Adds verbose option to enable ORM increased logging.
This commit is contained in:
parent
882a77d24c
commit
9d33df78a8
2
main.py
2
main.py
@ -2,4 +2,4 @@ from pfbudget import Manager, run
|
||||
|
||||
if __name__ == "__main__":
|
||||
command, args = run()
|
||||
Manager(command).start(args)
|
||||
Manager(command, args).start()
|
||||
|
||||
@ -2,7 +2,7 @@ from pathlib import Path
|
||||
import argparse
|
||||
import re
|
||||
|
||||
from pfbudget.common.types import Command
|
||||
from pfbudget.common.types import Command, Operation
|
||||
from pfbudget.core.categories import categorize_data
|
||||
from pfbudget.input.json import JsonParser
|
||||
from pfbudget.input.nordigen import NordigenInput
|
||||
@ -40,6 +40,9 @@ def argparser() -> argparse.ArgumentParser:
|
||||
help.add_argument(
|
||||
"-q", "--quiet", action="store_true", help="reduces the amount of verbose"
|
||||
)
|
||||
help.add_argument(
|
||||
"-v", "--verbose", action="store_true", help="increases the amount of verbose"
|
||||
)
|
||||
|
||||
period = argparse.ArgumentParser(add_help=False).add_mutually_exclusive_group()
|
||||
period.add_argument(
|
||||
@ -246,6 +249,56 @@ def argparser() -> argparse.ArgumentParser:
|
||||
# func=lambda args: manager.parser(JsonParser(vars(args)))
|
||||
# )
|
||||
|
||||
# Add category
|
||||
p_categories = subparsers.add_parser(
|
||||
"category",
|
||||
parents=[help],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p_categories_commands = p_categories.add_subparsers(dest="command", required=True)
|
||||
p_categories_add = p_categories_commands.add_parser(
|
||||
"add",
|
||||
parents=[help],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p_categories_add.add_argument("category", nargs="+", type=str)
|
||||
p_categories_add.add_argument("--group", nargs="?", type=str)
|
||||
p_categories_add.set_defaults(command=Command.Category, op=Operation.Add)
|
||||
|
||||
p_categories_remove = p_categories_commands.add_parser(
|
||||
"remove",
|
||||
parents=[help],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p_categories_remove.add_argument("category", nargs="+", type=str)
|
||||
p_categories_remove.add_argument("--group", nargs="?", type=str)
|
||||
p_categories_remove.set_defaults(command=Command.Category, op=Operation.Remove)
|
||||
|
||||
p_categories_addgroup = p_categories_commands.add_parser(
|
||||
"addgroup",
|
||||
parents=[help],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p_categories_addgroup.add_argument("group", nargs="+", type=str)
|
||||
p_categories_addgroup.set_defaults(command=Command.Category, op=Operation.AddGroup)
|
||||
|
||||
p_categories_removegroup = p_categories_commands.add_parser(
|
||||
"removegroup",
|
||||
parents=[help],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p_categories_removegroup.add_argument("group", nargs="+", type=str)
|
||||
p_categories_removegroup.set_defaults(command=Command.Category, op=Operation.RemoveGroup)
|
||||
|
||||
p_categories_updategroup = p_categories_commands.add_parser(
|
||||
"updategroup",
|
||||
parents=[help],
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
p_categories_updategroup.add_argument("category", nargs="+", type=str)
|
||||
p_categories_updategroup.add_argument("--group", nargs=1, type=str)
|
||||
p_categories_updategroup.set_defaults(command=Command.Category, op=Operation.UpdateGroup)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
|
||||
@ -13,6 +13,15 @@ class Command(Enum):
|
||||
Unregister = auto()
|
||||
Token = auto()
|
||||
Renew = auto()
|
||||
Category = auto()
|
||||
|
||||
|
||||
class Operation(Enum):
|
||||
Add = auto()
|
||||
Remove = auto()
|
||||
AddGroup = auto()
|
||||
RemoveGroup = auto()
|
||||
UpdateGroup = auto()
|
||||
|
||||
|
||||
class TransactionError(Exception):
|
||||
|
||||
@ -2,7 +2,8 @@ from pfbudget.input.input import Input
|
||||
from pfbudget.input.nordigen import NordigenClient
|
||||
from pfbudget.input.parsers import parse_data
|
||||
from pfbudget.db.client import DbClient
|
||||
from pfbudget.common.types import Command
|
||||
from pfbudget.db.model import Category, CategoryGroup
|
||||
from pfbudget.common.types import Command, Operation
|
||||
from pfbudget.core.categorizer import Categorizer
|
||||
from pfbudget.utils import convert
|
||||
|
||||
@ -10,8 +11,9 @@ from pfbudget.cli.runnable import download, parse
|
||||
|
||||
|
||||
class Manager:
|
||||
def __init__(self, command: Command):
|
||||
def __init__(self, command: Command, args: dict):
|
||||
self.__command = command
|
||||
self._args = args
|
||||
match (command):
|
||||
case Command.Init:
|
||||
pass
|
||||
@ -29,22 +31,24 @@ class Manager:
|
||||
pass
|
||||
case Command.Renew:
|
||||
pass
|
||||
case Command.Category:
|
||||
pass
|
||||
|
||||
def start(self, args):
|
||||
assert "database" in args, "ArgParser didn't include db"
|
||||
self._db = args["database"]
|
||||
|
||||
def start(self):
|
||||
match (self.__command):
|
||||
case Command.Init:
|
||||
pass
|
||||
case Command.Parse:
|
||||
# TODO this is a monstrosity, remove when possible
|
||||
self._db = DbClient(args["database"])
|
||||
parse(self, args)
|
||||
parse(self, self.args)
|
||||
case Command.Download:
|
||||
# TODO this is a monstrosity, remove when possible
|
||||
self._db = DbClient(args["database"])
|
||||
download(self, args)
|
||||
download(self, self.args)
|
||||
case Command.Categorize:
|
||||
self._db = DbClient(args["database"])
|
||||
self.categorize(args)
|
||||
self.categorize(self.args)
|
||||
case Command.Register:
|
||||
# self._db = DbClient(args["database"])
|
||||
# self.register(args)
|
||||
@ -55,29 +59,72 @@ class Manager:
|
||||
pass
|
||||
case Command.Token:
|
||||
NordigenClient(self).token()
|
||||
|
||||
case Command.Renew:
|
||||
NordigenClient(self).requisition(args["name"], args["country"])
|
||||
NordigenClient(self).requisition(
|
||||
self.args["name"], self.args["country"]
|
||||
)
|
||||
|
||||
case Command.Category:
|
||||
assert "op" in self.args, "category operation not defined"
|
||||
|
||||
with self.db.session() as session:
|
||||
match self.args["op"]:
|
||||
case Operation.Add:
|
||||
for category in self.args["category"]:
|
||||
session.addcategory(
|
||||
Category(name=category, group=self.args["group"])
|
||||
)
|
||||
|
||||
case Operation.Remove:
|
||||
session.removecategory(
|
||||
[
|
||||
Category(name=category)
|
||||
for category in self.args["category"]
|
||||
]
|
||||
)
|
||||
|
||||
case Operation.UpdateGroup:
|
||||
session.updategroup(
|
||||
[
|
||||
Category(name=category)
|
||||
for category in self.args["category"]
|
||||
],
|
||||
self.args["group"][0],
|
||||
)
|
||||
|
||||
case Operation.AddGroup:
|
||||
for group in self.args["group"]:
|
||||
session.addcategorygroup(CategoryGroup(name=group))
|
||||
|
||||
case Operation.RemoveGroup:
|
||||
session.removecategorygroup(
|
||||
[
|
||||
CategoryGroup(name=group)
|
||||
for group in self.args["group"]
|
||||
]
|
||||
)
|
||||
|
||||
# def init(self):
|
||||
# client = DatabaseClient(self.__db)
|
||||
# client.init()
|
||||
|
||||
# def register(self, args: dict):
|
||||
# bank = Bank(args["bank"][0], "", args["requisition"][0], args["invert"])
|
||||
# def register(self):
|
||||
# bank = Bank(self.args["bank"][0], "", self.args["requisition"][0], self.args["invert"])
|
||||
# client = DatabaseClient(self.__db)
|
||||
# client.register_bank(convert(bank))
|
||||
|
||||
# def unregister(self, args: dict):
|
||||
# def unregister(self):
|
||||
# client = DatabaseClient(self.__db)
|
||||
# client.unregister_bank(args["bank"][0])
|
||||
# client.unregister_bank(self.args["bank"][0])
|
||||
|
||||
def parser(self, parser: Input):
|
||||
transactions = parser.parse()
|
||||
print(transactions)
|
||||
# self.add_transactions(transactions)
|
||||
|
||||
# def parse(self, filename: str, args: dict):
|
||||
# transactions = parse_data(filename, args)
|
||||
# def parse(self, filename: str):
|
||||
# transactions = parse_data(filename, self.args)
|
||||
# self.add_transactions(transactions)
|
||||
|
||||
# def transactions() -> list[Transaction]:
|
||||
@ -86,13 +133,11 @@ class Manager:
|
||||
def add_transactions(self, transactions):
|
||||
with self.db.session() as session:
|
||||
session.add(transactions)
|
||||
session.commit()
|
||||
|
||||
def categorize(self, args: dict):
|
||||
def categorize(self):
|
||||
with self.db.session() as session:
|
||||
uncategorized = session.uncategorized()
|
||||
Categorizer().categorize(uncategorized)
|
||||
session.commit()
|
||||
|
||||
# def get_bank_by(self, key: str, value: str) -> Bank:
|
||||
# client = DatabaseClient(self.__db)
|
||||
@ -103,5 +148,13 @@ class Manager:
|
||||
return self.db.get_nordigen_banks()
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return self._db
|
||||
def db(self) -> DbClient:
|
||||
return DbClient(self._db, self.args["verbose"])
|
||||
|
||||
@db.setter
|
||||
def db(self, url: str):
|
||||
self._db = url
|
||||
|
||||
@property
|
||||
def args(self) -> dict:
|
||||
return self._args
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from sqlalchemy import create_engine, select
|
||||
from sqlalchemy import create_engine, delete, select, update
|
||||
from sqlalchemy.orm import Session, joinedload, selectinload
|
||||
|
||||
from pfbudget.db.model import Bank, Category, Transaction
|
||||
from pfbudget.db.model import Bank, Category, CategoryGroup, Transaction
|
||||
|
||||
# import logging
|
||||
|
||||
@ -16,8 +16,8 @@ class DbClient:
|
||||
|
||||
__sessions: list[Session]
|
||||
|
||||
def __init__(self, url: str) -> None:
|
||||
self._engine = create_engine(url)
|
||||
def __init__(self, url: str, echo=False) -> None:
|
||||
self._engine = create_engine(url, echo=echo)
|
||||
|
||||
def get_transactions(self):
|
||||
"""¿Non-optimized? get_transactions, will load the entire Transaction"""
|
||||
@ -65,6 +65,7 @@ class DbClient:
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
self.commit()
|
||||
self.__session.close()
|
||||
|
||||
def commit(self):
|
||||
@ -76,9 +77,32 @@ class DbClient:
|
||||
def addcategory(self, category: Category):
|
||||
self.__session.add(category)
|
||||
|
||||
def removecategory(self, categories: list[Category]):
|
||||
stmt = delete(Category).where(
|
||||
Category.name.in_([cat.name for cat in categories])
|
||||
)
|
||||
self.__session.execute(stmt)
|
||||
|
||||
def updategroup(self, categories: list[Category], group: CategoryGroup):
|
||||
stmt = (
|
||||
update(Category)
|
||||
.where(Category.name.in_([cat.name for cat in categories]))
|
||||
.values(group=group)
|
||||
)
|
||||
self.__session.execute(stmt)
|
||||
|
||||
def addcategorygroup(self, group: CategoryGroup):
|
||||
self.__session.add(group)
|
||||
|
||||
def removecategorygroup(self, groups: list[CategoryGroup]):
|
||||
stmt = delete(CategoryGroup).where(
|
||||
CategoryGroup.name.in_([grp.name for grp in groups])
|
||||
)
|
||||
self.__session.execute(stmt)
|
||||
|
||||
def uncategorized(self) -> list[Transaction]:
|
||||
stmt = select(Transaction).where(~Transaction.category.has())
|
||||
return self.__session.scalars(stmt).all()
|
||||
|
||||
def session(self):
|
||||
def session(self) -> ClientSession:
|
||||
return self.ClientSession(self.engine)
|
||||
|
||||
@ -108,6 +108,11 @@ class Category(Base):
|
||||
cascade="all, delete-orphan", passive_deletes=True
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"Category(name={self.name}, group={self.group}, #rules={len(self.rules)})"
|
||||
)
|
||||
|
||||
|
||||
class TransactionCategory(Base):
|
||||
__tablename__ = "categorized"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user