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:
parent
ad3fe02e4f
commit
0a42db8995
@ -155,6 +155,53 @@ def argparser(manager: Manager) -> argparse.ArgumentParser:
|
|||||||
)
|
)
|
||||||
p_report.set_defaults(func=report)
|
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
|
Nordigen API
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -9,5 +9,5 @@ class Input(ABC):
|
|||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def transactions(self) -> Transactions:
|
def parse(self) -> Transactions:
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
|
from pfbudget.core.input.input import Input
|
||||||
from pfbudget.core.transactions import Transactions
|
from pfbudget.core.transactions import Transactions
|
||||||
from pfbudget.utils.converters import convert
|
from pfbudget.utils.converters import convert
|
||||||
from pfbudget.utils.utils import parse_decimal
|
from pfbudget.utils.utils import parse_decimal
|
||||||
|
|
||||||
from .input import Input
|
|
||||||
|
|
||||||
|
|
||||||
class JsonParser(Input):
|
class JsonParser(Input):
|
||||||
def __init__(self, options):
|
def __init__(self, options):
|
||||||
super().__init__(options)
|
super().__init__(options)
|
||||||
|
|
||||||
def transactions(self) -> Transactions:
|
def parse(self) -> Transactions:
|
||||||
|
try:
|
||||||
with open(self.options["json"][0], "r") as f:
|
with open(self.options["json"][0], "r") as f:
|
||||||
return [
|
return [
|
||||||
convert(
|
convert(
|
||||||
@ -26,3 +26,5 @@ class JsonParser(Input):
|
|||||||
)
|
)
|
||||||
for t in json.load(f)["transactions"]["booked"]
|
for t in json.load(f)["transactions"]["booked"]
|
||||||
]
|
]
|
||||||
|
except KeyError:
|
||||||
|
print("No json file defined")
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class Client(Input):
|
|||||||
|
|
||||||
self._client.token = self.__token()
|
self._client.token = self.__token()
|
||||||
|
|
||||||
def transactions(self) -> Transactions:
|
def parse(self) -> Transactions:
|
||||||
requisition = self._client.requisition.get_requisition_by_id(self.options["id"])
|
requisition = self._client.requisition.get_requisition_by_id(self.options["id"])
|
||||||
|
|
||||||
for acc in requisition["accounts"]:
|
for acc in requisition["accounts"]:
|
||||||
@ -51,7 +51,7 @@ class Client(Input):
|
|||||||
|
|
||||||
def download(self, id: str):
|
def download(self, id: str):
|
||||||
if len(id) > 0:
|
if len(id) > 0:
|
||||||
return self.transactions(id)
|
return self.parse(id)
|
||||||
else:
|
else:
|
||||||
print("you forgot the req id")
|
print("you forgot the req id")
|
||||||
|
|
||||||
|
|||||||
@ -2,18 +2,40 @@ from pfbudget.core.input.input import Input
|
|||||||
from pfbudget.core.input.parsers import parse_data
|
from pfbudget.core.input.parsers import parse_data
|
||||||
from pfbudget.core.transactions import Transaction
|
from pfbudget.core.transactions import Transaction
|
||||||
from pfbudget.db.client import DatabaseClient
|
from pfbudget.db.client import DatabaseClient
|
||||||
|
from pfbudget.db.schema import Bank
|
||||||
from pfbudget.utils.converters import convert
|
from pfbudget.utils.converters import convert
|
||||||
|
|
||||||
|
|
||||||
class Manager:
|
class Manager:
|
||||||
def __init__(self, db: str):
|
def __init__(self, db: str):
|
||||||
self.__db = DatabaseClient(db)
|
self.db = db
|
||||||
|
|
||||||
def init(self):
|
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):
|
def parser(self, parser: Input):
|
||||||
print(parser.transactions())
|
print(parser.parse())
|
||||||
|
|
||||||
def parse(self, filename: str, args: dict):
|
def parse(self, filename: str, args: dict):
|
||||||
transactions = parse_data(filename, args)
|
transactions = parse_data(filename, args)
|
||||||
|
|||||||
@ -77,9 +77,12 @@ class DatabaseClient:
|
|||||||
(
|
(
|
||||||
("transactions", Q.CREATE_TRANSACTIONS_TABLE),
|
("transactions", Q.CREATE_TRANSACTIONS_TABLE),
|
||||||
("backups", Q.CREATE_BACKUPS_TABLE),
|
("backups", Q.CREATE_BACKUPS_TABLE),
|
||||||
|
("banks", Q.CREATE_BANKS_TABLE),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
"""Transaction table methods"""
|
||||||
|
|
||||||
def select_all(self) -> list[Transaction] | None:
|
def select_all(self) -> list[Transaction] | None:
|
||||||
logger.info(f"Reading all transactions from {self.db}")
|
logger.info(f"Reading all transactions from {self.db}")
|
||||||
transactions = self.__execute("SELECT * FROM transactions")
|
transactions = self.__execute("SELECT * FROM transactions")
|
||||||
@ -184,3 +187,13 @@ class DatabaseClient:
|
|||||||
dir.mkdir()
|
dir.mkdir()
|
||||||
with open(dir / filename, "w", newline="") as f:
|
with open(dir / filename, "w", newline="") as f:
|
||||||
csv.writer(f, delimiter="\t").writerows(transactions)
|
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,))
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
CREATE_TRANSACTIONS_TABLE = """
|
CREATE_TRANSACTIONS_TABLE = """
|
||||||
CREATE TABLE IF NOT EXISTS "transactions" (
|
CREATE TABLE IF NOT EXISTS "transactions" (
|
||||||
"date" TEXT NOT NULL,
|
"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_BACKUPS_TABLE = """
|
||||||
CREATE TABLE IF NOT EXISTS backups (
|
CREATE TABLE IF NOT EXISTS backups (
|
||||||
datetime TEXT NOT NULL,
|
datetime TEXT NOT NULL,
|
||||||
@ -18,12 +23,16 @@ CREATE TABLE IF NOT EXISTS backups (
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
CREATE_BANKS_TABLE = """
|
CREATE_BANKS_TABLE = """
|
||||||
CREATE TABLE banks (
|
CREATE TABLE IF NOT EXISTS banks (
|
||||||
name TEXT NOT NULL PRIMARY KEY,
|
name TEXT NOT NULL PRIMARY KEY,
|
||||||
url TEXT
|
requisition TEXT,
|
||||||
|
invert INTEGER,
|
||||||
|
description TEXT
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
Bank = tuple[str, str, bool]
|
||||||
|
|
||||||
ADD_TRANSACTION = """
|
ADD_TRANSACTION = """
|
||||||
INSERT INTO transactions (date, description, bank, value, category) values (?,?,?,?,?)
|
INSERT INTO transactions (date, description, bank, value, category) values (?,?,?,?,?)
|
||||||
"""
|
"""
|
||||||
@ -83,3 +92,12 @@ WHERE date BETWEEN (?) AND (?)
|
|||||||
AND category NOT IN {}
|
AND category NOT IN {}
|
||||||
ORDER BY date ASC
|
ORDER BY date ASC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ADD_BANK = """
|
||||||
|
INSERT INTO banks (name, requisition, invert, description) values (?,?,?,?)
|
||||||
|
"""
|
||||||
|
|
||||||
|
DELETE_BANK = """
|
||||||
|
DELETE FROM banks
|
||||||
|
WHERE name = (?)
|
||||||
|
"""
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from functools import singledispatch
|
from functools import singledispatch
|
||||||
|
|
||||||
from pfbudget.core.transactions import Transaction, TransactionError, Transactions
|
from pfbudget.core.transactions import Transaction, TransactionError, Transactions
|
||||||
|
from pfbudget.db.schema import DbTransaction, DbTransactions
|
||||||
|
|
||||||
|
|
||||||
@singledispatch
|
@singledispatch
|
||||||
@ -9,20 +10,35 @@ def convert(t):
|
|||||||
|
|
||||||
|
|
||||||
@convert.register
|
@convert.register
|
||||||
def _(t: Transaction) -> list:
|
def _(t: Transaction) -> DbTransaction:
|
||||||
return (t.date, t.description, t.bank, t.value, t.category)
|
return (t.date, t.description, t.bank, t.value, t.category)
|
||||||
|
|
||||||
|
|
||||||
@convert.register
|
def convert_dbtransaction(db) -> Transaction:
|
||||||
def _(t: list) -> Transaction:
|
|
||||||
try:
|
try:
|
||||||
return Transaction(t)
|
return Transaction(db)
|
||||||
except TransactionError:
|
except TransactionError:
|
||||||
print(f"{t} is in the wrong format")
|
print(f"{db} is in the wrong format")
|
||||||
|
|
||||||
|
|
||||||
def convert_transactions(transactions) -> list[list]:
|
convert.register(type(DbTransaction), convert_dbtransaction)
|
||||||
return [convert(c) for c in transactions]
|
|
||||||
|
|
||||||
|
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)
|
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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user