Adds new JSON parser and moves init to Manager

Adds a new abstract `Input` interface for the manager to use different
input handlers with the same methods. The Nordigen class and a new JSON
parser descend from it. The previous csv parser will also eventually.
New converter for list input also added.

Issue #19
This commit is contained in:
Luís Murta 2022-09-26 22:33:17 +01:00
parent c6cfd52b8b
commit ad3fe02e4f
Signed by: satprog
GPG Key ID: 169EF1BBD7049F94
7 changed files with 121 additions and 29 deletions

View File

@ -2,8 +2,9 @@ from pathlib import Path
import argparse
import re
from pfbudget.core.manager import Manager
from pfbudget.core.categories import categorize_data
from pfbudget.core.manager import Manager
from pfbudget.core.input.json import JsonParser
from pfbudget.db.client import DatabaseClient
import pfbudget.reporting.graph
import pfbudget.reporting.report
@ -74,7 +75,7 @@ def argparser(manager: Manager) -> argparse.ArgumentParser:
parents=[help],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
p_init.set_defaults(func=lambda args: DatabaseClient(args.database).init())
p_init.set_defaults(func=lambda args: manager.init())
"""
Exporting
@ -155,10 +156,10 @@ def argparser(manager: Manager) -> argparse.ArgumentParser:
p_report.set_defaults(func=report)
"""
Init Nordigen session: get new access token
Nordigen API
"""
p_nordigen_access = subparsers.add_parser(
"init",
"token",
description="Get new access token",
parents=[help],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
@ -204,6 +205,20 @@ def argparser(manager: Manager) -> argparse.ArgumentParser:
p_nordigen_list.add_argument("country", nargs=1, type=str)
p_nordigen_list.set_defaults(func=lambda args: Client().banks(args.country[0]))
"""
Nordigen JSONs
"""
p_nordigen_json = subparsers.add_parser(
"json",
description="",
parents=[help],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
p_nordigen_json.add_argument("json", nargs=1, type=str)
p_nordigen_json.add_argument("bank", nargs=1, type=str)
p_nordigen_json.add_argument("--invert", action=argparse.BooleanOptionalAction)
p_nordigen_json.set_defaults(func=lambda args: manager.parser(JsonParser(vars(args))))
return parser
@ -231,11 +246,17 @@ def graph(args):
"""
start, end = pfbudget.utils.parse_args_period(args)
if args.option == "monthly":
pfbudget.reporting.graph.monthly(DatabaseClient(args.database), vars(args), start, end)
pfbudget.reporting.graph.monthly(
DatabaseClient(args.database), vars(args), start, end
)
elif args.option == "discrete":
pfbudget.reporting.graph.discrete(DatabaseClient(args.database), vars(args), start, end)
pfbudget.reporting.graph.discrete(
DatabaseClient(args.database), vars(args), start, end
)
elif args.option == "networth":
pfbudget.reporting.graph.networth(DatabaseClient(args.database), vars(args), start, end)
pfbudget.reporting.graph.networth(
DatabaseClient(args.database), vars(args), start, end
)
def report(args):

View File

@ -0,0 +1,13 @@
from abc import ABC, abstractmethod
from pfbudget.core.transactions import Transactions
class Input(ABC):
@abstractmethod
def __init__(self, options: dict):
self.options = options
@abstractmethod
def transactions(self) -> Transactions:
return NotImplemented

View File

@ -0,0 +1,28 @@
import json
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:
with open(self.options["json"][0], "r") as f:
return [
convert(
[
t["bookingDate"],
t["remittanceInformationUnstructured"],
self.options["bank"][0],
parse_decimal(t["transactionAmount"]["amount"])
if not self.options["invert"]
else -parse_decimal(t["transactionAmount"]["amount"]),
],
)
for t in json.load(f)["transactions"]["booked"]
]

View File

@ -1,15 +1,20 @@
from dotenv import load_dotenv
from json import dump
from nordigen import NordigenClient
from uuid import uuid4
import os
import webbrowser
from .input import Input
from pfbudget.core.transactions import Transactions
from pfbudget.utils.converters import convert
from pfbudget.utils.utils import parse_decimal
load_dotenv()
class Client:
def __init__(self):
class Client(Input):
def __init__(self, options: dict):
super().__init__(options)
self._client = NordigenClient(
secret_key=os.environ.get("SECRET_KEY"),
secret_id=os.environ.get("SECRET_ID"),
@ -17,6 +22,24 @@ class Client:
self._client.token = self.__token()
def transactions(self) -> Transactions:
requisition = self._client.requisition.get_requisition_by_id(self.options["id"])
for acc in requisition["accounts"]:
account = self._client.account_api(acc)
d = account.get_transactions()["transactions"]
return [
convert(
t["bookingDate"],
t["remittanceInformationUnstructured"],
self.options["bank"],
parse_decimal(t["transactionAmount"]["amount"])
if not self.options["invert"]
else -parse_decimal(t["transactionAmount"]["amount"]),
)
for t in d["booked"]
]
def token(self):
token = self._client.generate_token()
print(f"New access token: {token}")
@ -32,19 +55,6 @@ class Client:
else:
print("you forgot the req id")
def transactions(self, id):
requisition = self._client.requisition.get_requisition_by_id(id)
# transactions_list = []
for acc in requisition["accounts"]:
account = self._client.account_api(acc)
print(account.get_metadata())
with open("cetelem.json", "w") as f:
dump(account.get_transactions(), f, indent=4)
# print(dumps(account.get_transactions(), indent=4))
# print(transactions_list)
def banks(self, country: str):
print(self._client.institution.get_institutions(country))

View File

@ -1,3 +1,4 @@
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
@ -8,13 +9,19 @@ class Manager:
def __init__(self, db: str):
self.__db = DatabaseClient(db)
def init(self):
self.__db.init()
def parser(self, parser: Input):
print(parser.transactions())
def parse(self, filename: str, args: dict):
transactions = parse_data(filename, args)
self.add_transactions(transactions)
def transactions() -> list[Transaction]:
pass
def add_transactions(self, transactions: list[Transaction]):
converted = convert(transactions)
self.__db.insert_transactions(converted)
def parse(self, filename: str, args: dict):
transactions = parse_data(filename, args)
self.add_transactions(transactions)

View File

@ -101,3 +101,6 @@ class Transaction:
return "{} {} {}€ at {}".format(
self.date.strftime("%d/%m/%y"), self.category, self.value, self.bank
)
Transactions = list[Transaction]

View File

@ -1,6 +1,6 @@
from functools import singledispatch
from pfbudget.core.transactions import Transaction
from pfbudget.core.transactions import Transaction, TransactionError, Transactions
@singledispatch
@ -14,5 +14,15 @@ def _(t: Transaction) -> list:
@convert.register
def _(transactions: list) -> list[list]:
def _(t: list) -> Transaction:
try:
return Transaction(t)
except TransactionError:
print(f"{t} is in the wrong format")
def convert_transactions(transactions) -> list[list]:
return [convert(c) for c in transactions]
convert.register(type(Transactions), convert_transactions)