-
Notifications
You must be signed in to change notification settings - Fork 3
Debt tracker #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Debt tracker #23
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
2cf4664
Add .env options for debt tracking
Sha-yol 9916aeb
Add loaded debt parameters to Config
Sha-yol 29b2771
Create strategies skeleton
Sha-yol 6460f65
Create standard strategy
Sha-yol aa4f62b
Add splitwise balance strategy
Sha-yol 8b99546
Remove redundant option
Sha-yol e920fc7
Import and use strategy classes
Sha-yol e5e9878
Use paid amount instead of owed on flag
Sha-yol a219c97
Modify processExpense to use correct strategy
Sha-yol 2c30fa1
Actually use paid amount on flag
Sha-yol 72aa11a
Add strategy testing skeleton
Sha-yol d45b54c
Remove redundant option from main
Sha-yol 1a1d617
Convert str to float for comparison
Sha-yol 9a3959b
minor strategy tests changes
Sha-yol 3419f68
test bugfix
Sha-yol 371db39
Fix processExpense logic
Sha-yol a81f55d
Remove redundant patch
Sha-yol 2b02d23
minor bugfixes
Sha-yol c7771d5
Remove use_payed_amount option from processExpense
Sha-yol 61b2d6d
Use applyExpenseAmountToTransaction in SW balance strategy
Sha-yol 3910040
Correctly make a deposit to the balance account
Sha-yol b76dfac
bug: handle deposit type txns
Sha-yol 525d90c
convert str to float for taking negative
Sha-yol 4fb84d5
handle expenses by others
Sha-yol 7d302d9
unify comment style
Sha-yol f0730e6
refactor for clarity
Sha-yol f7e6196
flatten dictionary to comply with signature
Sha-yol 4522c2d
clarify amount application method usage
Sha-yol bf398ac
minor refactor
Sha-yol 8cd5822
fix test_sw_balance_strategy
Sha-yol a6aa87b
Make test robust to trailing zeros
Sha-yol 0b4fa01
Disable splitwise balance feature in test
Sha-yol bc58995
Extend README to describe debt tracking
Sha-yol File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from abc import ABC, abstractmethod | ||
| from splitwise import Expense | ||
| from splitwise.user import ExpenseUser | ||
|
|
||
| class TransactionStrategy(ABC): | ||
| @abstractmethod | ||
| def create_transactions(self, exp: Expense, myshare: ExpenseUser, data: list[str]) -> list[dict]: | ||
| pass |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| from .base import TransactionStrategy | ||
| from splitwise import Expense | ||
| from splitwise.user import ExpenseUser | ||
|
|
||
| class StandardTransactionStrategy(TransactionStrategy): | ||
| def __init__(self, get_expense_transaction_body) -> None: | ||
| self._get_expense_transaction_body = get_expense_transaction_body | ||
|
|
||
| def create_transactions(self, exp: Expense, myshare: ExpenseUser, data: list[str]) -> list[dict]: | ||
| return [self._get_expense_transaction_body(exp, myshare, data)] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| from .base import TransactionStrategy | ||
| from splitwise import Expense | ||
| from splitwise.user import ExpenseUser | ||
|
|
||
| class SWBalanceTransactionStrategy(TransactionStrategy): | ||
| def __init__(self, get_expense_transaction_body, sw_balance_account, apply_transaction_amount) -> None: | ||
| self._get_expense_transaction_body = get_expense_transaction_body | ||
| self._sw_balance_account = sw_balance_account | ||
| self._apply_transaction_amount = apply_transaction_amount | ||
|
|
||
| def create_transactions(self, exp: Expense, myshare: ExpenseUser, data: list[str]) -> list[dict]: | ||
| txns = {} | ||
| paid_txn = self._get_expense_transaction_body(exp, myshare, data) | ||
| paid_txn = self._apply_transaction_amount(paid_txn, exp, myshare.getPaidShare()) | ||
| if float(paid_txn['amount']) != 0: # I paid; payment txn needed | ||
| txns['paid'] = paid_txn | ||
|
|
||
| balance_txn = paid_txn.copy() | ||
| balance = float(myshare.getNetBalance()) | ||
| if balance != 0: # I owe or am owed; balance txn needed | ||
| txns['balance'] = balance_txn | ||
| if balance > 0: # I am owed; difference credited to balance account | ||
| balance_txn['source_name'] = self._sw_balance_account + " balancer" | ||
adyanth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| balance_txn['destination_name'] = self._sw_balance_account | ||
| balance_txn['type'] = 'deposit' | ||
| balance_txn['description'] = f"Balance transfer for: {paid_txn['description']}" | ||
| balance_txn = self._apply_transaction_amount(balance_txn, exp, balance) | ||
| else: # I owe; difference debited from balance account | ||
| balance_txn['source_name'] = self._sw_balance_account | ||
| balance_txn['destination_name'] = paid_txn['destination_name'] | ||
| balance_txn['type'] = "withdrawal" | ||
| balance_txn['description'] = f"Balance transfer for: {paid_txn['description']}" | ||
| balance_txn = self._apply_transaction_amount(balance_txn, exp, -balance) | ||
| return list(txns.values()) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| import pytest | ||
| from datetime import datetime | ||
| from unittest.mock import Mock, patch | ||
| from strategies.standard import StandardTransactionStrategy | ||
| from strategies.sw_balance import SWBalanceTransactionStrategy | ||
| from splitwise import Expense | ||
| from splitwise.user import ExpenseUser | ||
|
|
||
| # Mock objects | ||
| mock_expense = Mock(spec=Expense) | ||
| mock_expense.getId.return_value = "123" | ||
| mock_expense.getDescription.return_value = "Test Expense" | ||
| mock_expense.getCurrencyCode.return_value = "USD" | ||
| mock_expense.getDate.return_value = "2023-05-01" | ||
| mock_expense.getCreatedAt.return_value = "2023-05-01T12:00:00Z" | ||
|
|
||
| mock_user = Mock(spec=ExpenseUser) | ||
| mock_user.getId.return_value = "456" | ||
| mock_user.getOwedShare.return_value = "60.00" | ||
| mock_user.getPaidShare.return_value = "110.00" | ||
| mock_user.getNetBalance.return_value = "50.00" | ||
|
|
||
| # Mock getExpenseTransactionBody function | ||
| def mock_get_expense_transaction_body(exp, myshare, data): | ||
| amount = myshare.getOwedShare() | ||
| return { | ||
| "amount": amount, | ||
| "description": exp.getDescription(), | ||
| "date": exp.getDate(), | ||
| "source_name": "Test Source", | ||
| "destination_name": "Test Destination", | ||
| "category_name": "Test Category", | ||
| "type": "withdrawal", | ||
| } | ||
|
|
||
| def mock_apply_transaction_amount(txn, exp, amount): | ||
| txn['amount'] = str(amount) | ||
| return txn | ||
|
|
||
| # Tests for StandardTransactionStrategy | ||
| def test_standard_strategy(): | ||
| strategy = StandardTransactionStrategy(mock_get_expense_transaction_body) | ||
| transactions = strategy.create_transactions(mock_expense, mock_user, []) | ||
|
|
||
| assert len(transactions) == 1 | ||
| assert transactions[0]["amount"] == "60.00" | ||
| assert transactions[0]["description"] == "Test Expense" | ||
|
|
||
| # Tests for SWBalanceTransactionStrategy | ||
| def test_sw_balance_strategy(): | ||
| strategy = SWBalanceTransactionStrategy(mock_get_expense_transaction_body, "Splitwise Balance", mock_apply_transaction_amount) | ||
| transactions = strategy.create_transactions(mock_expense, mock_user, []) | ||
|
|
||
| assert len(transactions) == 2 | ||
| assert transactions[0]["amount"] == "110.00" | ||
| assert transactions[0]["description"] == "Test Expense" | ||
| assert float(transactions[1]["amount"]) == float("50.00") | ||
| assert transactions[1]["type"] == "deposit" | ||
| assert transactions[1]["destination_name"] == "Splitwise Balance" | ||
|
|
||
| # Test for processExpense function | ||
| @patch('main.getDate') | ||
| @patch('main.get_transaction_strategy') | ||
| @patch('main.updateTransaction') | ||
| @patch('main.addTransaction') | ||
| @patch('main.searchTransactions') | ||
| @patch('main.getSWUrlForExpense') | ||
| def test_process_expense(mock_get_url, mock_search, mock_add, mock_update, mock_get_strategy, mock_get_date): | ||
| from main import processExpense, Config | ||
|
|
||
| # Mock configuration | ||
| mock_config = Mock(spec=Config) | ||
| mock_config.SW_BALANCE_ACCOUNT = "Splitwise Balance" | ||
|
|
||
| mock_get_date.return_value = datetime.now().astimezone() # Make sure expense registers as new and not updated | ||
|
|
||
| # Set up mock strategy | ||
| mock_strategy = Mock() | ||
| mock_strategy.create_transactions.return_value = [ | ||
| {"amount": "110.00", "description": "Test Expense"}, | ||
| {"amount": "50.00", "description": "Balance transfer for: Test Expense"} | ||
| ] | ||
| mock_get_strategy.return_value = mock_strategy | ||
|
|
||
| # Set up other mocks | ||
| mock_get_url.return_value = "http://example.com/expense/123" | ||
| mock_search.return_value = [] | ||
|
|
||
| # Call processExpense | ||
| processExpense(datetime.now().astimezone(), {}, mock_expense, mock_user, []) | ||
|
|
||
| # Assertions | ||
| assert mock_strategy.create_transactions.called | ||
| assert mock_add.call_count == 2 | ||
| assert mock_update.call_count == 0 | ||
| mock_add.assert_any_call({"amount": "110.00", | ||
| "description": "Test Expense", | ||
| "external_url": "http://example.com/expense/123"}) | ||
| mock_add.assert_any_call({"amount": "50.00", | ||
| "description": 'Balance transfer for: Test Expense', | ||
| "external_url": 'http://example.com/expense/123-balance_transfer-1'}) | ||
|
|
||
| # Test for get_transaction_strategy function | ||
| @patch('requests.request') | ||
| def test_get_transaction_strategy(mock_request): | ||
| mock_request.return_value.json.return_value = {'data': []} | ||
| from main import get_transaction_strategy, Config | ||
|
|
||
| # Test with SW_BALANCE_ACCOUNT = False | ||
| with patch.dict('main.conf', {'SW_BALANCE_ACCOUNT': ''}): | ||
| strategy = get_transaction_strategy() | ||
| assert isinstance(strategy, StandardTransactionStrategy) | ||
|
|
||
| # Test with SW_BALANCE_ACCOUNT = True | ||
| with patch.dict('main.conf', {'SW_BALANCE_ACCOUNT': 'Splitwise Balance'}): | ||
| strategy = get_transaction_strategy() | ||
| assert isinstance(strategy, SWBalanceTransactionStrategy) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.