Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 59 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ cog.out(

##### JSON output

We convert the posted and transacted_at, if provided, values into ISO strings.

<!-- [[[cog
import cog
from simplefin import cli
Expand All @@ -145,31 +143,64 @@ cog.out(
]]] -->
```
❯ simplefin transactions "Demo Savings" --format json
[
{
"id": "1738382400",
"posted": "2025-02-01T04:00:00+00:00",
"amount": "1960.00",
"description": "Pay day!",
"payee": "You",
"memo": "PAY DAY - FROM YER JOB"
},
{
"id": "1738396800",
"posted": "2025-02-01T08:00:00+00:00",
"amount": "-05.50",
"description": "Fishing bait",
"payee": "John's Fishin Shack",
"memo": "JOHNS FISHIN SHACK BAIT"
},
{
"id": "1738425600",
"posted": "2025-02-01T16:00:00+00:00",
"amount": "-135.50",
"description": "Grocery store",
"payee": "Grocery store",
"memo": "LOCAL GROCER STORE #1133"
}
]
{
"errors": [],
"accounts": [
{
"org": {
"domain": "beta-bridge.simplefin.org",
"sfin-url": "https://beta-bridge.simplefin.org/simplefin",
"name": "SimpleFIN Demo",
"url": "https://beta-bridge.simplefin.org",
"id": "simplefin.demoorg"
},
"id": "Demo Savings",
"name": "SimpleFIN Savings",
"currency": "USD",
"balance": "115525.50",
"available-balance": "115525.50",
"balance-date": 1738368000,
"transactions": [
{
"id": "1738382400",
"posted": 1738382400,
"amount": "1960.00",
"description": "Pay day!",
"payee": "You",
"memo": "PAY DAY - FROM YER JOB"
},
{
"id": "1738396800",
"posted": 1738396800,
"amount": "-05.50",
"description": "Fishing bait",
"payee": "John's Fishin Shack",
"memo": "JOHNS FISHIN SHACK BAIT"
},
{
"id": "1738425600",
"posted": 1738425600,
"amount": "-135.50",
"description": "Grocery store",
"payee": "Grocery store",
"memo": "LOCAL GROCER STORE #1133"
}
],
"holdings": [
{
"id": "25bc4910-4cb4-437b-9924-ee98003651c5",
"created": 345427200,
"cost_basis": "55.00",
"currency": "USD",
"description": "Shares of Apple",
"market_value": "105884.8",
"purchase_price": "0.10",
"shares": "550.0",
"symbol": "AAPL"
}
]
}
]
}
```
<!-- [[[end]]] -->
26 changes: 18 additions & 8 deletions src/simplefin/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import datetime
import json
import os
from datetime import date

# from datetime import date
import click
from rich.console import Console
from rich.pretty import pprint
Expand All @@ -11,6 +11,10 @@
from simplefin.client import SimpleFINClient


def epoch_to_datetime(epoch: int) -> datetime:
return datetime.datetime.fromtimestamp(epoch, tz=datetime.timezone.utc)


class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
Expand Down Expand Up @@ -89,24 +93,30 @@ def accounts(format: str) -> None:
)
def transactions(account_id: str, format: str, lookback_days: int) -> None:
c = SimpleFINClient(access_url=os.getenv("SIMPLEFIN_ACCESS_URL"))
start_dt = date.today() - datetime.timedelta(days=lookback_days)
transactions = c.get_transactions(account_id, start_dt)
start_dt = datetime.date.today() - datetime.timedelta(days=lookback_days)
resp = c.get_transactions(account_id, start_dt)

console = Console()

if format == "json":
console = Console()
console.print(json.dumps(transactions, indent=4, cls=DateTimeEncoder))
console.print(json.dumps(resp, indent=4))
else:
if len(resp["accounts"]) == 0:
console.print("No transactions found")
return

table = Table(title=f"Transactions for {account_id}")
table.add_column("Date")
table.add_column("Payee")
table.add_column("Amount")

for txn in transactions:
for txn in resp["accounts"][0]["transactions"]:
table.add_row(
txn["posted"].strftime("%d %b %Y"), txn["payee"], str(txn["amount"])
epoch_to_datetime(txn["posted"]).strftime("%d %b %Y"),
txn["payee"],
str(txn["amount"]),
)

console = Console()
console.print(table)


Expand Down
18 changes: 4 additions & 14 deletions src/simplefin/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
from base64 import b64decode
from datetime import date, datetime, timezone
from datetime import date, datetime
from functools import wraps
from typing import Optional

Expand All @@ -9,10 +9,6 @@
logger = logging.getLogger(__name__)


def epoch_to_datetime(epoch: int) -> datetime:
return datetime.fromtimestamp(epoch, tz=timezone.utc)


# TODO: Custom exception, if still required?
def ensure_client_initialized(func):
@wraps(func)
Expand Down Expand Up @@ -71,17 +67,11 @@ def get_transactions(self, account_id: str, start_date: date):
)
response.raise_for_status()

transactions = response.json()["accounts"][0]["transactions"]
# Include errors & holdings?

for transaction in transactions:
if "posted" in transaction.keys():
transaction["posted"] = epoch_to_datetime(transaction["posted"])
if "transacted_at" in transaction.keys():
transaction["transacted_at"] = epoch_to_datetime(
transaction["transacted_at"]
)
resp = response.json()

return transactions
return resp

@ensure_client_initialized
def get_info(self):
Expand Down
10 changes: 6 additions & 4 deletions tests/test_simplefin_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ def test_get_transactions(httpx_mock: pytest_httpx.HTTPXMock):
json=transactions_response,
)

transactions = client.get_transactions(account_id, start_date)
assert len(transactions) == 4
assert transactions[0]["payee"] == "John's Fishin Shack"
assert transactions[1]["payee"] == "Grocery store"
resp = client.get_transactions(account_id, start_date)
assert len(resp["accounts"]) == 1
assert len(resp["accounts"][0]["transactions"]) == 4
assert type(resp["accounts"][0]["transactions"][0]["posted"]) is int
# assert transactions[0]["payee"] == "John's Fishin Shack"
# assert transactions[1]["payee"] == "Grocery store"


def test_get_info(httpx_mock: httpx.MockTransport):
Expand Down