Persists banks information and requisition ID

This patch saves the bank information in the DB, in a new table. It also
adds two new CLI commands, register/unregister, so enter the banking
information. (This should later be done internally).

It also adds new types alias for the DB transaction type and new
converters.
Input `transactions` method renamed to `parse`.

Issue #18
This commit is contained in:
Luís Murta 2022-09-29 23:04:10 +01:00
parent ad3fe02e4f
commit 0a42db8995
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
8 changed files with 150 additions and 32 deletions

View File

@ -155,6 +155,53 @@ def argparser(manager: Manager) -> argparse.ArgumentParser:
)
p_report.set_defaults(func=report)
"""
Register bank
"""
p_register = subparsers.add_parser(
"register",
description="Register a bank",
parents=[help],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
p_register.add_argument(
"bank",
type=str,
nargs=1,
help="bank option help"
)
p_register.add_argument(
"--requisition",
type=str,
nargs=1,
help="requisition option help"
)
p_register.add_argument("--invert", action="store_true")
p_register.add_argument(
"--description",
type=str,
nargs="?",
help="description option help"
)
p_register.set_defaults(func=lambda args: manager.register(vars(args)))
"""
Unregister bank
"""
p_register = subparsers.add_parser(
"unregister",
description="Unregister a bank",
parents=[help],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
p_register.add_argument(
"bank",
type=str,
nargs=1,
help="bank option help"
)
p_register.set_defaults(func=lambda args: manager.unregister(vars(args)))
"""
Nordigen API
"""

View File

@ -9,5 +9,5 @@ class Input(ABC):
self.options = options
@abstractmethod
def transactions(self) -> Transactions:
def parse(self) -> Transactions:
return NotImplemented

View File

@ -1,17 +1,17 @@
import json
from pfbudget.core.input.input import Input
from pfbudget.core.transactions import Transactions
from pfbudget.utils.converters import convert
from pfbudget.utils.utils import parse_decimal
from .input import Input
class JsonParser(Input):
def __init__(self, options):
super().__init__(options)
def transactions(self) -> Transactions:
def parse(self) -> Transactions:
try:
with open(self.options["json"][0], "r") as f:
return [
convert(
@ -26,3 +26,5 @@ class JsonParser(Input):
)
for t in json.load(f)["transactions"]["booked"]
]
except KeyError:
print("No json file defined")

View File

@ -22,7 +22,7 @@ class Client(Input):
self._client.token = self.__token()
def transactions(self) -> Transactions:
def parse(self) -> Transactions:
requisition = self._client.requisition.get_requisition_by_id(self.options["id"])
for acc in requisition["accounts"]:
@ -51,7 +51,7 @@ class Client(Input):
def download(self, id: str):
if len(id) > 0:
return self.transactions(id)
return self.parse(id)
else:
print("you forgot the req id")

View File

@ -2,18 +2,40 @@ from pfbudget.core.input.input import Input
from pfbudget.core.input.parsers import parse_data
from pfbudget.core.transactions import Transaction
from pfbudget.db.client import DatabaseClient
from pfbudget.db.schema import Bank
from pfbudget.utils.converters import convert
class Manager:
def __init__(self, db: str):
self.__db = DatabaseClient(db)
self.db = db
def init(self):
self.__db.init()
client = DatabaseClient(self.db)
client.init()
def register(self, args: dict):
print(args)
client = DatabaseClient(self.db)
client.register_bank(
Bank(
(
args["bank"][0],
args["requisition"][0]
if args["requisition"]
else args["requisition"],
args["invert"],
args["description"],
)
)
)
def unregister(self, args: dict):
client = DatabaseClient(self.db)
client.unregister_bank(args["bank"][0])
def parser(self, parser: Input):
print(parser.transactions())
print(parser.parse())
def parse(self, filename: str, args: dict):
transactions = parse_data(filename, args)

View File

@ -77,9 +77,12 @@ class DatabaseClient:
(
("transactions", Q.CREATE_TRANSACTIONS_TABLE),
("backups", Q.CREATE_BACKUPS_TABLE),
("banks", Q.CREATE_BANKS_TABLE),
)
)
"""Transaction table methods"""
def select_all(self) -> list[Transaction] | None:
logger.info(f"Reading all transactions from {self.db}")
transactions = self.__execute("SELECT * FROM transactions")
@ -184,3 +187,13 @@ class DatabaseClient:
dir.mkdir()
with open(dir / filename, "w", newline="") as f:
csv.writer(f, delimiter="\t").writerows(transactions)
"""Banks table methods"""
def register_bank(self, bank: Q.Bank):
logger.info(f"Registering bank {bank[0]} with req_id={bank[1]}")
self.__execute(Q.ADD_BANK, (bank[0], bank[1], bank[2], bank[3]))
def unregister_bank(self, bank: str):
logger.info(f"Unregistering bank {bank}")
self.__execute(Q.DELETE_BANK, (bank,))

View File

@ -1,3 +1,5 @@
from decimal import Decimal
CREATE_TRANSACTIONS_TABLE = """
CREATE TABLE IF NOT EXISTS "transactions" (
"date" TEXT NOT NULL,
@ -10,6 +12,9 @@ CREATE TABLE IF NOT EXISTS "transactions" (
);
"""
DbTransaction = tuple[str, str | None, str, Decimal, str | None, str | None, str | None]
DbTransactions = list[DbTransaction]
CREATE_BACKUPS_TABLE = """
CREATE TABLE IF NOT EXISTS backups (
datetime TEXT NOT NULL,
@ -18,12 +23,16 @@ CREATE TABLE IF NOT EXISTS backups (
"""
CREATE_BANKS_TABLE = """
CREATE TABLE banks (
CREATE TABLE IF NOT EXISTS banks (
name TEXT NOT NULL PRIMARY KEY,
url TEXT
requisition TEXT,
invert INTEGER,
description TEXT
)
"""
Bank = tuple[str, str, bool]
ADD_TRANSACTION = """
INSERT INTO transactions (date, description, bank, value, category) values (?,?,?,?,?)
"""
@ -83,3 +92,12 @@ WHERE date BETWEEN (?) AND (?)
AND category NOT IN {}
ORDER BY date ASC
"""
ADD_BANK = """
INSERT INTO banks (name, requisition, invert, description) values (?,?,?,?)
"""
DELETE_BANK = """
DELETE FROM banks
WHERE name = (?)
"""

View File

@ -1,6 +1,7 @@
from functools import singledispatch
from pfbudget.core.transactions import Transaction, TransactionError, Transactions
from pfbudget.db.schema import DbTransaction, DbTransactions
@singledispatch
@ -9,20 +10,35 @@ def convert(t):
@convert.register
def _(t: Transaction) -> list:
def _(t: Transaction) -> DbTransaction:
return (t.date, t.description, t.bank, t.value, t.category)
@convert.register
def _(t: list) -> Transaction:
def convert_dbtransaction(db) -> Transaction:
try:
return Transaction(t)
return Transaction(db)
except TransactionError:
print(f"{t} is in the wrong format")
print(f"{db} is in the wrong format")
def convert_transactions(transactions) -> list[list]:
return [convert(c) for c in transactions]
convert.register(type(DbTransaction), convert_dbtransaction)
def convert_transactions(ts: Transactions) -> DbTransactions:
try:
return [convert(t) for t in ts]
except TransactionError:
print(f"{ts} is in the wrong format")
convert.register(type(Transactions), convert_transactions)
def convert_dbtransactions(ts: DbTransactions) -> Transactions:
try:
return [convert(t) for t in ts]
except TransactionError:
print(f"{ts} is in the wrong format")
convert.register(type(DbTransactions), convert_dbtransactions)