From 0f6ee08f46ebb64dec8865a884018d229a02f05a Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Wed, 2 Oct 2024 18:56:10 +0300 Subject: [PATCH 1/7] Add method to get account currency from account name --- main.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/main.py b/main.py index 3e941ed..fe70e57 100644 --- a/main.py +++ b/main.py @@ -360,6 +360,20 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") return newTxn +def getAccountCurrency(account_name: str) -> str: + # FIXME: could be a performance bottleneck, should split api call and search. + """Get the currency of an account on Firefly. + + :param account: The account name + :return: The currency code + :raises: ValueError if the account is not found + """ + ff_accounts = callApi("accounts/", method="GET", params={"type": "asset"}).json() + for acc in ff_accounts: + if acc["attributes"]["name"] == account_name: + return acc["attributes"]["currency_code"] + raise ValueError(f"Account {account_name} not found in asset accounts.") + if __name__ == "__main__": """ From 8cff32643c8eda31f11a9d8d46e9dce6a3dee49b Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Wed, 2 Oct 2024 19:08:09 +0300 Subject: [PATCH 2/7] Handle different expense and account currencies --- main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index fe70e57..b170be2 100644 --- a/main.py +++ b/main.py @@ -348,7 +348,6 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str "category_name": category, "type": "withdrawal", "amount": myshare.getOwedShare(), - "currency_code": exp.getCurrencyCode(), "date": getDate(exp.getCreatedAt()).isoformat(), "payment_date": getDate(exp.getDate()).isoformat(), "description": description, @@ -356,6 +355,11 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str "notes": notes, "external_url": getSWUrlForExpense(exp), } + if getAccountCurrency(source) != exp.getCurrencyCode(): + newTxn["foreign_currency_code"] = exp.getCurrencyCode() + newTxn["foreign_amount"] = myshare.getOwedShare() + newTxn["amount"] = 0 + newTxn["tags"] = ["fixme/foreign-currency"] print( f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") return newTxn From caa1ed7f8199115c1d59119bc9dd177d11c18c56 Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Mon, 7 Oct 2024 21:35:39 +0300 Subject: [PATCH 3/7] Improve naming --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index b170be2..fad58b6 100644 --- a/main.py +++ b/main.py @@ -355,7 +355,7 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str "notes": notes, "external_url": getSWUrlForExpense(exp), } - if getAccountCurrency(source) != exp.getCurrencyCode(): + if getAccountCurrencyCode(source) != exp.getCurrencyCode(): newTxn["foreign_currency_code"] = exp.getCurrencyCode() newTxn["foreign_amount"] = myshare.getOwedShare() newTxn["amount"] = 0 @@ -364,7 +364,7 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") return newTxn -def getAccountCurrency(account_name: str) -> str: +def getAccountCurrencyCode(account_name: str) -> str: # FIXME: could be a performance bottleneck, should split api call and search. """Get the currency of an account on Firefly. From e0b03da8102d6b475e81143968601780291c6fd3 Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Mon, 7 Oct 2024 21:41:01 +0300 Subject: [PATCH 4/7] Control tag with .env file --- .env.example | 1 + main.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index f84682c..c41b397 100644 --- a/.env.example +++ b/.env.example @@ -6,3 +6,4 @@ FIREFLY_DEFAULT_TRXFER_ACCOUNT=Charles Schwab FIREFLY_DEFAULT_CATEGORY=Groceries FIREFLY_DRY_RUN=true SPLITWISE_DAYS=1 +FOREIGN_CURRENCY_TAG=fixme/foreign-currency diff --git a/main.py b/main.py index fad58b6..89d93a6 100644 --- a/main.py +++ b/main.py @@ -28,6 +28,7 @@ def load_config() -> Config: "FIREFLY_DEFAULT_TRXFR_ACCOUNT": os.getenv("FIREFLY_DEFAULT_TRXFR_ACCOUNT", "Chase Checking"), "FIREFLY_DRY_RUN": bool(os.getenv("FIREFLY_DRY_RUN", True)), "SPLITWISE_DAYS": int(os.getenv("SPLITWISE_DAYS", 1)), + "FOREIGN_CURRENCY_TAG": os.getenv("FOREIGN_CURRENCY_TAG") } time_now = datetime.now().astimezone() @@ -359,7 +360,7 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str newTxn["foreign_currency_code"] = exp.getCurrencyCode() newTxn["foreign_amount"] = myshare.getOwedShare() newTxn["amount"] = 0 - newTxn["tags"] = ["fixme/foreign-currency"] + newTxn["tags"] = [conf["FOREIGN_CURRENCY_TAG"]] print( f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") return newTxn From 907269fbf642e79166bf5a9ed16e77c50feda3fb Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Mon, 7 Oct 2024 21:53:03 +0300 Subject: [PATCH 5/7] Minor bugfixes --- main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index 89d93a6..2b06a1d 100644 --- a/main.py +++ b/main.py @@ -359,7 +359,7 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str if getAccountCurrencyCode(source) != exp.getCurrencyCode(): newTxn["foreign_currency_code"] = exp.getCurrencyCode() newTxn["foreign_amount"] = myshare.getOwedShare() - newTxn["amount"] = 0 + newTxn["amount"] = 0.1 newTxn["tags"] = [conf["FOREIGN_CURRENCY_TAG"]] print( f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") @@ -373,7 +373,7 @@ def getAccountCurrencyCode(account_name: str) -> str: :return: The currency code :raises: ValueError if the account is not found """ - ff_accounts = callApi("accounts/", method="GET", params={"type": "asset"}).json() + ff_accounts = callApi("accounts/", method="GET", params={"type": "asset"}).json()['data'] for acc in ff_accounts: if acc["attributes"]["name"] == account_name: return acc["attributes"]["currency_code"] From e0995d78a9658d7afbdb30e343879ef6a0a6d4e0 Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Tue, 8 Oct 2024 09:47:53 +0300 Subject: [PATCH 6/7] Rename FOREIGN_CURRENCY_TAG to FOREIGN_CURRENCY_TOFIX_TAG --- .env.example | 2 +- main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index c41b397..f52c14e 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,4 @@ FIREFLY_DEFAULT_TRXFER_ACCOUNT=Charles Schwab FIREFLY_DEFAULT_CATEGORY=Groceries FIREFLY_DRY_RUN=true SPLITWISE_DAYS=1 -FOREIGN_CURRENCY_TAG=fixme/foreign-currency +FOREIGN_CURRENCY_TOFIX_TAG=fixme/foreign-currency diff --git a/main.py b/main.py index 2b06a1d..68d51be 100644 --- a/main.py +++ b/main.py @@ -28,7 +28,7 @@ def load_config() -> Config: "FIREFLY_DEFAULT_TRXFR_ACCOUNT": os.getenv("FIREFLY_DEFAULT_TRXFR_ACCOUNT", "Chase Checking"), "FIREFLY_DRY_RUN": bool(os.getenv("FIREFLY_DRY_RUN", True)), "SPLITWISE_DAYS": int(os.getenv("SPLITWISE_DAYS", 1)), - "FOREIGN_CURRENCY_TAG": os.getenv("FOREIGN_CURRENCY_TAG") + "FOREIGN_CURRENCY_TOFIX_TAG": os.getenv("FOREIGN_CURRENCY_TOFIX_TAG") } time_now = datetime.now().astimezone() @@ -360,7 +360,7 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str newTxn["foreign_currency_code"] = exp.getCurrencyCode() newTxn["foreign_amount"] = myshare.getOwedShare() newTxn["amount"] = 0.1 - newTxn["tags"] = [conf["FOREIGN_CURRENCY_TAG"]] + newTxn["tags"] = [conf["FOREIGN_CURRENCY_TOFIX_TAG"]] print( f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") return newTxn From c5d7f6fc3abcd6e21fab9954d3f95baf70b59688 Mon Sep 17 00:00:00 2001 From: "LAPTOP-8G8AGL8G\\yotam" Date: Tue, 8 Oct 2024 23:03:40 +0300 Subject: [PATCH 7/7] Cache api call results to improve performance --- main.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 68d51be..9ea28bf 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ from splitwise import Splitwise, Expense, User, Comment from splitwise.user import ExpenseUser from typing import Generator, TypedDict +from functools import wraps import os import requests @@ -365,19 +366,35 @@ def getExpenseTransactionBody(exp: Expense, myshare: ExpenseUser, data: list[str f"Processing {category} {formatExpense(exp, myshare)} from {source} to {dest}") return newTxn +def getAccounts(account_type: str="asset") -> list: + return callApi("accounts/", method="GET", params={"type": account_type}).json()['data'] + +def cache_account_currency(function): + account_name_currency = dict( + map( + lambda x: (x["attributes"]["name"], x["attributes"]["currency_code"]), + getAccounts("asset"), + ) + ) + + @wraps(function) + def cached(account_name: str) -> str: + try: + return account_name_currency[account_name] + except KeyError: + raise ValueError(f"Account {account_name} not found in asset accounts.") + + return cached + +@cache_account_currency def getAccountCurrencyCode(account_name: str) -> str: - # FIXME: could be a performance bottleneck, should split api call and search. """Get the currency of an account on Firefly. :param account: The account name :return: The currency code :raises: ValueError if the account is not found """ - ff_accounts = callApi("accounts/", method="GET", params={"type": "asset"}).json()['data'] - for acc in ff_accounts: - if acc["attributes"]["name"] == account_name: - return acc["attributes"]["currency_code"] - raise ValueError(f"Account {account_name} not found in asset accounts.") + raise Exception("Will not be called") if __name__ == "__main__":