Compare commits

..

4 Commits

Author SHA1 Message Date
bd7fe90caf
chore: update dependencies 2026-01-01 19:15:14 +00:00
cee5fe77e1
feat: improve GoCardless error handling
Handle 409 when retriving transactions, which signals the account data
is still being processed, and 429, which signals the rate lite was
exceeded.
2026-01-01 19:15:14 +00:00
46f6ee2af7
feat: log GoCardless responses to file 2026-01-01 19:15:14 +00:00
1aae7d8748
fix: skip command in interactive
Also replace old DB `get` method with `select` and fix issues caught by
mypy.
2026-01-01 19:15:14 +00:00
4 changed files with 1295 additions and 885 deletions

View File

@ -36,24 +36,24 @@ class Interactive:
uncategorized = session.select(
Transaction, lambda: ~Transaction.category.has()
)
uncategorized.sort()
list(uncategorized).sort()
n = len(uncategorized)
print(f"{n} left to categorize")
i = 0
new = []
while (command := input("$ ")) != "quit" and i < len(uncategorized):
while i < len(uncategorized):
current = uncategorized[i] if len(new) == 0 else new.pop()
print(current)
command = input("$ ")
match command:
case "help":
print(self.help)
case "skip":
if len(uncategorized) == 0:
i += 1
i += 1
case "quit":
break
@ -89,7 +89,7 @@ class Interactive:
for tag in tags:
if tag not in [t.name for t in self.tags]:
session.insert([Tag(tag)])
self.tags = session.get(Tag)
self.tags = session.select(Tag)
current.tags.add(TransactionTag(tag))
@ -98,7 +98,7 @@ class Interactive:
def split(self, original: Transaction) -> list[SplitTransaction]:
total = original.amount
new = []
new: list[SplitTransaction] = []
done = False
while not done:
@ -114,7 +114,7 @@ class Interactive:
amount = decimal.Decimal(input("amount: "))
new.append(
SplitTransaction(
original.date, original.description, amount, original.id
original.date, original.description, amount, original=original.id
)
)

View File

@ -1,6 +1,7 @@
from dataclasses import dataclass
import datetime as dt
import dotenv
import json
import nordigen
import os
import requests
@ -60,11 +61,33 @@ class NordigenClient:
retries += 1
print(f"Request #{retries} timed-out, retrying in 1s")
time.sleep(1)
except requests.HTTPError as e:
if (
e.response.status_code == 409
and e.response.type == "AccountProcessing"
):
timeout = 1
while account.get_metadata()["status"] != "READY":
print(f"Waiting for status ready, retrying in {timeout}s")
time.sleep(timeout)
timeout *= 2
elif e.response.status_code == 429:
print("Rate limit exceeded for today, aborting")
break
else:
raise DownloadError(e)
if not downloaded:
print(f"Couldn't download transactions for {account.get_metadata()}")
continue
with open(
f"logs/{dt.datetime.now().isoformat()}_{requisition_id}.json",
"w",
encoding="utf-8",
) as f:
json.dump(downloaded, f, ensure_ascii=False, indent=4)
if (
"transactions" not in downloaded
or "booked" not in downloaded["transactions"]

2141
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ pytest = "^7.3.0"
pytest-cov = "^4.0.0"
pytest-mock = "^3.10.0"
sqlalchemy = {extras = ["mypy"], version = "^2.0.9"}
ruff = "^0.0.267"
ruff = "^0.11.2"
[build-system]
requires = ["poetry-core"]