Compare commits

..

6 Commits

Author SHA1 Message Date
5f2cb6fcf5
[Fix] Improves logging for bank/creditcard parser
When the credit card was correct but the bank was wrong, it was
difficul to accertain which one was wrong. Not when one fails, it will
also log what are the available options.
2022-11-22 22:09:40 +00:00
9345530f06
[Debug] Retains the downloaded jsons
on the json/ directory.
2022-11-22 22:07:47 +00:00
a364d7b46d
Improves error handling on Nordigen request
by catches an HTTP timeout and retriyng. Also improves logging.
2022-11-22 22:07:47 +00:00
647db5e86f
Fixes convertion to decimal of transactions amount 2022-11-22 22:07:47 +00:00
c41bfb4bdf
Add offset option to bank
A bank had an offset on the date of the transactions...
It also passes the interal Bank type to the converter, to access it's
additional options before converting.
2022-11-22 22:07:04 +00:00
a0d3af715e
Use name instead of institution for bank choice
Also fixes cli method to pass manager ref to the nordigen input.
2022-11-22 21:56:18 +00:00
6 changed files with 69 additions and 26 deletions

View File

@ -194,19 +194,19 @@ def argparser(manager: Manager) -> argparse.ArgumentParser:
p_nordigen_access.set_defaults(func=lambda args: NordigenInput(manager).token()) p_nordigen_access.set_defaults(func=lambda args: NordigenInput(manager).token())
""" """
Access to Nordigen API (Re)new bank requisition ID
""" """
p_nordigen_access = subparsers.add_parser( p_nordigen_access = subparsers.add_parser(
"renew", "renew",
description="Renew the requisition ID", description="(Re)new the Bank requisition ID",
parents=[help], parents=[help],
formatter_class=argparse.ArgumentDefaultsHelpFormatter, formatter_class=argparse.ArgumentDefaultsHelpFormatter,
) )
p_nordigen_access.add_argument("institution", nargs=1, type=str) p_nordigen_access.add_argument("name", nargs=1, type=str)
p_nordigen_access.add_argument("country", nargs=1, type=str) p_nordigen_access.add_argument("country", nargs=1, type=str)
p_nordigen_access.set_defaults( p_nordigen_access.set_defaults(
func=lambda args: NordigenInput().requisition( func=lambda args: NordigenInput(manager).requisition(
args.institution[0], args.country[0] args.name[0], args.country[0]
) )
) )

View File

@ -27,7 +27,7 @@ class Transaction:
self.description = " ".join(arg[1].split()) self.description = " ".join(arg[1].split())
self.bank = arg[2] self.bank = arg[2]
if type(arg[3]) is float: if type(arg[3]) is float:
self.value = arg[3] self.value = Decimal(str(arg[3]))
else: else:
self.value = Decimal(args[3]) self.value = Decimal(args[3])
self.category = arg[4] self.category = arg[4]
@ -118,6 +118,7 @@ class Bank:
bic: str bic: str
requisition_id: str requisition_id: str
invert: bool invert: bool
offset: int
key: PrimaryKey = PrimaryKey.ID key: PrimaryKey = PrimaryKey.ID

View File

@ -50,7 +50,8 @@ CREATE TABLE IF NOT EXISTS banks (
nordigen_id TEXT, nordigen_id TEXT,
nordigen_name TEXT, nordigen_name TEXT,
requisition_id TEXT, requisition_id TEXT,
invert INTEGER invert INTEGER,
offset INTEGER
) )
""" """
@ -62,7 +63,8 @@ class DbBank:
nordigen_id: str nordigen_id: str
nordigen_name: str nordigen_name: str
requisition_id: str requisition_id: str
invert: bool invert: bool = False
offset: int = 0
def tuple(self): def tuple(self):
return ( return (
@ -72,6 +74,7 @@ class DbBank:
self.nordigen_name, self.nordigen_name,
self.requisition_id, self.requisition_id,
int(self.invert), int(self.invert),
self.offset,
) )

View File

@ -1,9 +1,10 @@
from datetime import date from datetime import date
from time import sleep from time import sleep
from requests import ReadTimeout from requests import HTTPError, ReadTimeout
from dotenv import load_dotenv from dotenv import load_dotenv
from nordigen import NordigenClient from nordigen import NordigenClient
from uuid import uuid4 from uuid import uuid4
import json
import os import os
import webbrowser import webbrowser
@ -64,16 +65,22 @@ class NordigenInput(Input):
break break
except ReadTimeout: except ReadTimeout:
retries += 1 retries += 1
print(f"Request #{retries} timed-out, waiting 1s") print(f"Request #{retries} timed-out, retrying in 1s")
sleep(1)
except HTTPError as e:
retries += 1
print(f"Request #{retries} failed with {e}, retrying in 1s")
sleep(1) sleep(1)
if not downloaded: if not downloaded:
print(f"Couldn't download transactions for {account}") print(f"Couldn't download transactions for {account}")
continue continue
with open("json/" + bank.name + ".json", "w") as f:
json.dump(downloaded, f)
converted = [ converted = [
convert(t, bank.name, bank.invert) convert(t, bank) for t in downloaded["transactions"]["booked"]
for t in downloaded["transactions"]["booked"]
] ]
transactions.extend( transactions.extend(

View File

@ -4,7 +4,7 @@ from importlib import import_module
import datetime as dt import datetime as dt
import yaml import yaml
from pfbudget.common.types import Transaction from pfbudget.common.types import NoBankSelected, Transaction, Transactions
from pfbudget.utils import utils from pfbudget.utils import utils
Index = namedtuple( Index = namedtuple(
@ -43,7 +43,7 @@ Options = namedtuple(
) )
def parse_data(filename: str, args: dict) -> None: def parse_data(filename: str, args: dict) -> Transactions:
cfg: dict = yaml.safe_load(open("parsers.yaml")) cfg: dict = yaml.safe_load(open("parsers.yaml"))
assert ( assert (
"Banks" in cfg "Banks" in cfg
@ -57,17 +57,25 @@ def parse_data(filename: str, args: dict) -> None:
bank = args["bank"][0] bank = args["bank"][0]
creditcard = None if not args["creditcard"] else args["creditcard"][0] creditcard = None if not args["creditcard"] else args["creditcard"][0]
if not creditcard: try:
options: dict = cfg[bank] options: dict = cfg[bank]
else: except KeyError as e:
options: dict = cfg[bank][creditcard] banks = cfg["Banks"]
raise NoBankSelected(f"{e} not a valid bank, try one of {banks}")
if creditcard:
try:
options = options[creditcard]
except KeyError as e:
creditcards = cfg["CreditCards"]
raise NoBankSelected(f"{e} not a valid bank, try one of {creditcards}")
bank += creditcard bank += creditcard
if args["category"]: if args["category"]:
options["category"] = args["category"][0] options["category"] = args["category"][0]
if options.get("additional_parser"): if options.get("additional_parser"):
parser = getattr(import_module("pfbudget.parsers"), bank) parser = getattr(import_module("pfbudget.input.parsers"), bank)
transactions = parser(filename, bank, options).parse() transactions = parser(filename, bank, options).parse()
else: else:
transactions = Parser(filename, bank, options).parse() transactions = Parser(filename, bank, options).parse()

View File

@ -1,3 +1,4 @@
from datetime import timedelta
from functools import singledispatch from functools import singledispatch
from pfbudget.common.types import Bank, Transaction, TransactionError from pfbudget.common.types import Bank, Transaction, TransactionError
@ -13,7 +14,15 @@ def convert(t):
@convert.register @convert.register
def _(t: Transaction) -> DbTransaction: def _(t: Transaction) -> DbTransaction:
return DbTransaction(t.date, t.description, t.bank, t.value, t.category, t.original, t.additional_comment) return DbTransaction(
t.date,
t.description,
t.bank,
t.value,
t.category,
t.original,
t.additional_comment,
)
@convert.register @convert.register
@ -26,23 +35,38 @@ def _(db: DbTransaction) -> Transaction:
@convert.register @convert.register
def _(db: DbBank, key: str = "") -> Bank: def _(db: DbBank, key: str = "") -> Bank:
return Bank(db.name, db.bic, db.requisition_id, db.invert, key=key) bank = Bank(db.name, db.bic, db.requisition_id, db.invert, db.offset, key=key)
if not bank.invert:
bank.invert = False
if not bank.offset:
bank.offset = 0
return bank
@convert.register @convert.register
def _(bank: Bank, key: str = "") -> DbBank: def _(bank: Bank) -> DbBank:
return DbBank(bank.name, bank.bic, "", "", bank.requisition_id, bank.invert) bank = DbBank(
bank.name, bank.bic, "", "", bank.requisition_id, bank.invert, bank.offset
)
if not bank.invert:
bank.invert = False
if not bank.offset:
bank.offset = 0
return bank
@convert.register @convert.register
def _(json: dict, bank: str, invert: bool) -> Transaction: def _(json: dict, bank: Bank) -> Transaction:
i = -1 if invert else 1 i = -1 if bank.invert else 1
try: try:
return Transaction( transaction = Transaction(
json["bookingDate"], json["bookingDate"],
json["remittanceInformationUnstructured"], json["remittanceInformationUnstructured"],
bank, bank.name,
i * parse_decimal(json["transactionAmount"]["amount"]), i * parse_decimal(json["transactionAmount"]["amount"]),
) )
transaction.date += timedelta(days=bank.offset)
return transaction
except TransactionError: except TransactionError:
print(f"{json} is in the wrong format") print(f"{json} is in the wrong format")