ynab-cli 0.6.0__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/CHANGELOG.md +16 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/Makefile +1 -1
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/PKG-INFO +3 -2
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/pyproject.toml +3 -2
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/rich/io.py +5 -5
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/client.py +10 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/util.py +2 -1
- ynab_cli-0.6.2/src/ynab_cli/domain/use_cases/budgets.py +47 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/use_cases/categories.py +48 -47
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/use_cases/payees.py +103 -87
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/use_cases/transactions.py +26 -24
- ynab_cli-0.6.2/src/ynab_cli/host/click/commands/budgets.py +69 -0
- ynab_cli-0.6.2/src/ynab_cli/host/click/commands/categories.py +129 -0
- ynab_cli-0.6.2/src/ynab_cli/host/click/commands/payees.py +225 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/click/commands/rich/progress_table.py +2 -2
- ynab_cli-0.6.2/src/ynab_cli/host/click/commands/transactions.py +94 -0
- ynab_cli-0.6.2/src/ynab_cli/host/click/container.py +88 -0
- ynab_cli-0.6.2/src/ynab_cli/host/container.py +68 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/app.py +13 -1
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/cli.py +8 -5
- ynab_cli-0.6.2/src/ynab_cli/host/textual/container.py +40 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/categories/list_all.py +8 -1
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/categories/list_unused.py +8 -1
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/payees/list_all.py +8 -1
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/payees/list_duplicates.py +8 -3
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/payees/list_unused.py +7 -2
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/payees/normalize_names.py +8 -3
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/transactions/apply_rules.py +7 -4
- ynab_cli-0.6.2/tests/factories/ynab.py +18 -0
- ynab_cli-0.6.2/tests/unit/adapters/rich/test_io.py +177 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/use_cases/test_budgets.py +4 -2
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/use_cases/test_categories.py +4 -4
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/use_cases/test_payees.py +8 -8
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/use_cases/test_transactions.py +2 -2
- ynab_cli-0.6.2/tests/unit/host/click/commands/test_budgets.py +25 -0
- ynab_cli-0.6.2/tests/unit/host/click/commands/test_categories.py +64 -0
- ynab_cli-0.6.2/tests/unit/host/click/commands/test_payees.py +107 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/click/commands/test_transactions.py +26 -28
- ynab_cli-0.6.2/tests/unit/host/click/conftest.py +21 -0
- ynab_cli-0.6.2/tests/unit/host/conftest.py +13 -0
- ynab_cli-0.6.2/tests/unit/host/textual/conftest.py +21 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/textual/test_app.py +6 -1
- ynab_cli-0.6.2/tests/unit/host/textual/test_cli.py +19 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/uv.lock +191 -165
- ynab_cli-0.6.0/src/ynab_cli/domain/use_cases/budgets.py +0 -45
- ynab_cli-0.6.0/src/ynab_cli/host/click/commands/budgets.py +0 -57
- ynab_cli-0.6.0/src/ynab_cli/host/click/commands/categories.py +0 -103
- ynab_cli-0.6.0/src/ynab_cli/host/click/commands/payees.py +0 -177
- ynab_cli-0.6.0/src/ynab_cli/host/click/commands/transactions.py +0 -85
- ynab_cli-0.6.0/tests/factories/ynab.py +0 -18
- ynab_cli-0.6.0/tests/unit/adapters/rich/test_io.py +0 -128
- ynab_cli-0.6.0/tests/unit/host/click/commands/test_budgets.py +0 -28
- ynab_cli-0.6.0/tests/unit/host/click/commands/test_categories.py +0 -66
- ynab_cli-0.6.0/tests/unit/host/click/commands/test_payees.py +0 -108
- ynab_cli-0.6.0/tests/unit/host/textual/test_cli.py +0 -21
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.env-sample +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.github/.copilot-instructions.md +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.github/FUNDING.yml +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.github/dependabot.yml +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.github/workflows/pr.yml +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.github/workflows/publish-to-pypi.yml +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.gitignore +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/.python-version +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/LICENSE.md +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/README.md +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/docs/index.md +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/docs/transaction_rules.md +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/mkdocs.yml +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/rich/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/textual/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/textual/io.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/accounts/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/accounts/create_account.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/accounts/get_account_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/accounts/get_accounts.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/budgets/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/budgets/get_budget_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/budgets/get_budget_settings_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/budgets/get_budgets.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/categories/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/categories/get_categories.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/categories/get_category_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/categories/get_month_category_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/categories/update_category.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/categories/update_month_category.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/months/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/months/get_budget_month.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/months/get_budget_months.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payee_locations/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payee_locations/get_payee_location_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payee_locations/get_payee_locations.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payee_locations/get_payee_locations_by_payee.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payees/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payees/get_payee_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payees/get_payees.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/payees/update_payee.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/create_scheduled_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/delete_scheduled_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/get_scheduled_transaction_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/get_scheduled_transactions.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/update_scheduled_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/create_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/delete_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/get_transaction_by_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_account.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_category.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_month.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_payee.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/import_transactions.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/update_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/transactions/update_transactions.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/user/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/api/user/get_user.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/errors.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/account.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/account_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/account_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/account_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/accounts_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/accounts_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_detail.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_detail_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_detail_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_settings.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_settings_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_settings_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_summary.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_summary_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/budget_summary_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/bulk_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/bulk_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/bulk_response_data_bulk.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/bulk_transactions.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/categories_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/categories_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_goal_type_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_goal_type_type_2_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_goal_type_type_3_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_group.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_group_with_categories.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/category_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/currency_format_type_0.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/date_format_type_0.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/error_detail.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/error_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/existing_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/get_transactions_by_account_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/get_transactions_by_category_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/get_transactions_by_month_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/get_transactions_by_payee_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/get_transactions_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/hybrid_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/hybrid_transaction_type.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/hybrid_transactions_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/hybrid_transactions_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/loan_account_periodic_value_type_0.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/month_detail.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/month_detail_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/month_detail_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/month_summaries_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/month_summaries_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/month_summary.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/new_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/patch_category_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/patch_month_category_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/patch_payee_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/patch_transactions_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_location.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_location_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_location_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_locations_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_locations_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payee_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payees_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/payees_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/post_account_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/post_scheduled_transaction_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/post_transactions_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/put_scheduled_transaction_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/put_transaction_wrapper.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_account.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_category.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_category_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_category_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_month_category.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_payee.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_payee_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_payee_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_scheduled_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_sub_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_transaction_with_id_or_import_id.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_transaction_with_optional_fields.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_transactions_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/save_transactions_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_sub_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_detail.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_frequency.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_summary.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_summary_frequency.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transactions_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/scheduled_transactions_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/sub_transaction.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_cleared_status.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_detail.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_flag_color_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_flag_color_type_2_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_flag_color_type_3_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_summary.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_2_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_3_type_1.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transactions_import_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transactions_import_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transactions_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/transactions_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/user.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/user_response.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/models/user_response_data.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/adapters/ynab/types.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/constants.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/models/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/models/rules.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/ports/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/ports/io.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/settings.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/domain/use_cases/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/cli.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/click/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/click/cli.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/click/commands/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/click/commands/rich/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/constants.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/app.tcss +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/categories/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/categories/tabs.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/common/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/common/command_widget.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/common/dialogs/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/common/dialogs/dialog.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/common/dialogs/form.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/common/dialogs/screen.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/payees/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/payees/tabs.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/tabs.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/transactions/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/host/textual/widgets/transactions/tabs.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/src/ynab_cli/py.typed +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/conftest.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/factories/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/adapters/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/adapters/rich/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/adapters/textual/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/adapters/textual/test_io.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/conftest.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/models/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/models/test_rules.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/ports/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/ports/test_io.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/domain/use_cases/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/click/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/click/commands/__init__.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/test_cli.py +0 -0
- {ynab_cli-0.6.0 → ynab_cli-0.6.2}/tests/unit/host/textual/__init__.py +0 -0
|
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v0.6.2 (2025-07-09)
|
|
9
|
+
|
|
10
|
+
### Fix
|
|
11
|
+
|
|
12
|
+
- **tui**: update client token when settings are changed
|
|
13
|
+
|
|
14
|
+
## v0.6.1 (2025-07-09)
|
|
15
|
+
|
|
16
|
+
### Fix
|
|
17
|
+
|
|
18
|
+
- fix issue with providing new token
|
|
19
|
+
|
|
20
|
+
### Refactor
|
|
21
|
+
|
|
22
|
+
- work towards using dependency injection container
|
|
23
|
+
|
|
8
24
|
## v0.6.0 (2025-06-28)
|
|
9
25
|
|
|
10
26
|
### Feat
|
|
@@ -10,7 +10,7 @@ lint: .venv # run linters and type checkers
|
|
|
10
10
|
uv run mypy .
|
|
11
11
|
|
|
12
12
|
test: .venv lint # run tests with coverage
|
|
13
|
-
uv run pytest --cov=src tests
|
|
13
|
+
uv run pytest --cov-report term-missing:skip-covered --cov=src tests
|
|
14
14
|
|
|
15
15
|
clean: # clean up build artifacts and caches
|
|
16
16
|
rm -f .coverage
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ynab-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: YNAB Command Line Interface
|
|
5
5
|
Project-URL: Changelog, https://github.com/jeffsawatzky/ynab-cli/blob/main/CHANGELOG.md
|
|
6
6
|
Project-URL: Documentation, https://jeffsawatzky.github.io/ynab-cli/
|
|
@@ -24,12 +24,13 @@ Requires-Dist: attrs~=25.3
|
|
|
24
24
|
Requires-Dist: click-default-group~=1.2
|
|
25
25
|
Requires-Dist: click~=8.2
|
|
26
26
|
Requires-Dist: httpx~=0.28
|
|
27
|
+
Requires-Dist: lagom~=2.7
|
|
27
28
|
Requires-Dist: python-dotenv~=1.1
|
|
28
29
|
Requires-Dist: rapidfuzz~=3.13
|
|
29
30
|
Requires-Dist: rich~=14.0
|
|
30
31
|
Requires-Dist: rule-engine~=4.5
|
|
31
32
|
Requires-Dist: textual-fspicker~=0.4
|
|
32
|
-
Requires-Dist: textual~=3.
|
|
33
|
+
Requires-Dist: textual~=3.6
|
|
33
34
|
Requires-Dist: typing-extensions~=4.14
|
|
34
35
|
Requires-Dist: uvloop~=0.21
|
|
35
36
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ynab-cli"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.2"
|
|
4
4
|
description = "YNAB Command Line Interface"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -38,12 +38,13 @@ dependencies = [
|
|
|
38
38
|
"click-default-group~=1.2",
|
|
39
39
|
"click~=8.2",
|
|
40
40
|
"httpx~=0.28",
|
|
41
|
+
"lagom~=2.7",
|
|
41
42
|
"python-dotenv~=1.1",
|
|
42
43
|
"rapidfuzz~=3.13",
|
|
43
44
|
"rich~=14.0",
|
|
44
45
|
"rule-engine~=4.5",
|
|
45
46
|
"textual-fspicker~=0.4",
|
|
46
|
-
"textual~=3.
|
|
47
|
+
"textual~=3.6",
|
|
47
48
|
"typing-extensions~=4.14",
|
|
48
49
|
"uvloop~=0.21",
|
|
49
50
|
]
|
|
@@ -6,8 +6,9 @@ from ynab_cli.domain.ports.io import IO, Progress
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class RichProgress(Progress):
|
|
9
|
-
def __init__(self,
|
|
10
|
-
self._progress
|
|
9
|
+
def __init__(self, progress: rich_progress.Progress) -> None:
|
|
10
|
+
self._progress = progress
|
|
11
|
+
self._task_id = progress.add_task("Loading...")
|
|
11
12
|
|
|
12
13
|
@override
|
|
13
14
|
async def update(
|
|
@@ -20,10 +21,9 @@ class RichProgress(Progress):
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class RichIO(IO):
|
|
23
|
-
def __init__(self,
|
|
24
|
-
progress, _ = progress_info
|
|
24
|
+
def __init__(self, progress: rich_progress.Progress) -> None:
|
|
25
25
|
self._progress = progress
|
|
26
|
-
self.progress = RichProgress(
|
|
26
|
+
self.progress = RichProgress(self._progress)
|
|
27
27
|
|
|
28
28
|
@override
|
|
29
29
|
async def prompt(self, prompt: str, password: bool = False) -> str:
|
|
@@ -203,6 +203,16 @@ class AuthenticatedClient:
|
|
|
203
203
|
self._async_client.timeout = timeout
|
|
204
204
|
return evolve(self, timeout=timeout)
|
|
205
205
|
|
|
206
|
+
def update_token(self, token: str) -> None:
|
|
207
|
+
"""Update the token used for authentication"""
|
|
208
|
+
self.token = token
|
|
209
|
+
|
|
210
|
+
auth_header = f"{self.prefix} {self.token}" if self.prefix else self.token
|
|
211
|
+
if self._client is not None:
|
|
212
|
+
self._client.headers[self.auth_header_name] = auth_header
|
|
213
|
+
if self._async_client is not None:
|
|
214
|
+
self._async_client.headers[self.auth_header_name] = auth_header
|
|
215
|
+
|
|
206
216
|
def set_httpx_client(self, client: httpx.Client) -> "AuthenticatedClient":
|
|
207
217
|
"""Manually set the underlying httpx.Client
|
|
208
218
|
|
|
@@ -58,7 +58,8 @@ async def run_asyncio_detailed(
|
|
|
58
58
|
if not new_access_token:
|
|
59
59
|
raise e
|
|
60
60
|
|
|
61
|
-
client.
|
|
61
|
+
client.update_token(new_access_token)
|
|
62
|
+
|
|
62
63
|
response = await asyncio_detailed(*args, **kwargs)
|
|
63
64
|
parsed_data = get_parsed_response_data(response)
|
|
64
65
|
else:
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from collections.abc import AsyncIterator
|
|
2
|
+
from typing import TypedDict
|
|
3
|
+
|
|
4
|
+
from ynab_cli.adapters import ynab
|
|
5
|
+
from ynab_cli.adapters.ynab import models, util
|
|
6
|
+
from ynab_cli.adapters.ynab.api.budgets import get_budgets
|
|
7
|
+
from ynab_cli.domain import ports
|
|
8
|
+
from ynab_cli.domain.settings import Settings
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ListAllParams(TypedDict):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ListAll:
|
|
16
|
+
"""Use case for listing all budgets."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
19
|
+
self._io = io
|
|
20
|
+
self._client = client
|
|
21
|
+
|
|
22
|
+
async def __call__(self, settings: Settings, params: ListAllParams) -> AsyncIterator[models.BudgetSummary]:
|
|
23
|
+
try:
|
|
24
|
+
progress_total = 0
|
|
25
|
+
|
|
26
|
+
budgets = (
|
|
27
|
+
await util.get_asyncio_detailed(
|
|
28
|
+
self._io, get_budgets.asyncio_detailed, include_accounts=False, client=self._client
|
|
29
|
+
)
|
|
30
|
+
).data.budgets
|
|
31
|
+
budgets.sort(key=lambda b: b.name)
|
|
32
|
+
|
|
33
|
+
progress_total = len(budgets)
|
|
34
|
+
await self._io.progress.update(total=progress_total)
|
|
35
|
+
for budget in budgets:
|
|
36
|
+
await self._io.progress.update(advance=1)
|
|
37
|
+
yield budget
|
|
38
|
+
|
|
39
|
+
except Exception as e:
|
|
40
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
41
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
42
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
43
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
44
|
+
else:
|
|
45
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
46
|
+
finally:
|
|
47
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|
|
@@ -6,7 +6,6 @@ from ynab_cli.adapters.ynab import models, util
|
|
|
6
6
|
from ynab_cli.adapters.ynab.api.categories import get_categories
|
|
7
7
|
from ynab_cli.adapters.ynab.api.transactions import get_transactions_by_category
|
|
8
8
|
from ynab_cli.domain import ports
|
|
9
|
-
from ynab_cli.domain.constants import YNAB_API_URL
|
|
10
9
|
from ynab_cli.domain.settings import Settings
|
|
11
10
|
|
|
12
11
|
|
|
@@ -20,47 +19,48 @@ class ListUnusedParams(TypedDict):
|
|
|
20
19
|
pass
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
) -> AsyncIterator[models.Category]:
|
|
26
|
-
try:
|
|
27
|
-
progress_total = 0
|
|
22
|
+
class ListUnused:
|
|
23
|
+
"""Use case for listing unused categories."""
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
26
|
+
self._io = io
|
|
27
|
+
self._client = client
|
|
28
|
+
|
|
29
|
+
async def __call__(self, settings: Settings, params: ListUnusedParams) -> AsyncIterator[models.Category]:
|
|
30
|
+
try:
|
|
31
|
+
progress_total = 0
|
|
31
32
|
|
|
32
|
-
async with client:
|
|
33
33
|
category_groups = (
|
|
34
34
|
await util.get_asyncio_detailed(
|
|
35
|
-
|
|
35
|
+
self._io, get_categories.asyncio_detailed, settings.ynab.budget_id, client=self._client
|
|
36
36
|
)
|
|
37
37
|
).data.category_groups
|
|
38
38
|
category_groups.sort(key=lambda cg: cg.name)
|
|
39
39
|
|
|
40
40
|
progress_total = len(category_groups)
|
|
41
|
-
await
|
|
41
|
+
await self._io.progress.update(total=progress_total)
|
|
42
42
|
for category_group in category_groups:
|
|
43
|
-
await
|
|
43
|
+
await self._io.progress.update(advance=1)
|
|
44
44
|
|
|
45
45
|
if _should_skip_category_or_group(category_or_group=category_group):
|
|
46
46
|
continue
|
|
47
47
|
|
|
48
48
|
progress_total += len(category_group.categories)
|
|
49
|
-
await
|
|
49
|
+
await self._io.progress.update(total=progress_total)
|
|
50
50
|
category_group.categories.sort(key=lambda c: c.name)
|
|
51
51
|
for category in category_group.categories:
|
|
52
|
-
await
|
|
52
|
+
await self._io.progress.update(advance=1)
|
|
53
53
|
|
|
54
54
|
if _should_skip_category_or_group(category_or_group=category):
|
|
55
55
|
continue
|
|
56
56
|
|
|
57
57
|
transactions = (
|
|
58
58
|
await util.get_asyncio_detailed(
|
|
59
|
-
|
|
59
|
+
self._io,
|
|
60
60
|
get_transactions_by_category.asyncio_detailed,
|
|
61
61
|
settings.ynab.budget_id,
|
|
62
62
|
str(category.id),
|
|
63
|
-
client=
|
|
63
|
+
client=self._client,
|
|
64
64
|
)
|
|
65
65
|
).data.transactions
|
|
66
66
|
num_transactions = len(transactions)
|
|
@@ -69,63 +69,64 @@ async def list_unused(
|
|
|
69
69
|
if not num_transactions:
|
|
70
70
|
yield category
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
except Exception as e:
|
|
73
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
74
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
75
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
76
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
77
|
+
else:
|
|
78
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
79
|
+
finally:
|
|
80
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
class ListAllParams(TypedDict):
|
|
84
84
|
pass
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
class ListAll:
|
|
88
|
+
"""Use case for listing all categories."""
|
|
89
|
+
|
|
90
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
91
|
+
self._io = io
|
|
92
|
+
self._client = client
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
async def __call__(self, settings: Settings, params: ListAllParams) -> AsyncIterator[models.Category]:
|
|
95
|
+
try:
|
|
96
|
+
progress_total = 0
|
|
95
97
|
|
|
96
|
-
async with client:
|
|
97
98
|
category_groups = (
|
|
98
99
|
await util.get_asyncio_detailed(
|
|
99
|
-
|
|
100
|
+
self._io, get_categories.asyncio_detailed, settings.ynab.budget_id, client=self._client
|
|
100
101
|
)
|
|
101
102
|
).data.category_groups
|
|
102
103
|
category_groups.sort(key=lambda cg: cg.name)
|
|
103
104
|
|
|
104
105
|
progress_total = len(category_groups)
|
|
105
|
-
await
|
|
106
|
+
await self._io.progress.update(total=progress_total)
|
|
106
107
|
for category_group in category_groups:
|
|
107
|
-
await
|
|
108
|
+
await self._io.progress.update(advance=1)
|
|
108
109
|
|
|
109
110
|
if _should_skip_category_or_group(category_or_group=category_group):
|
|
110
111
|
continue
|
|
111
112
|
|
|
112
113
|
progress_total += len(category_group.categories)
|
|
113
|
-
await
|
|
114
|
+
await self._io.progress.update(total=progress_total)
|
|
114
115
|
category_group.categories.sort(key=lambda c: c.name)
|
|
115
116
|
for category in category_group.categories:
|
|
116
|
-
await
|
|
117
|
+
await self._io.progress.update(advance=1)
|
|
117
118
|
|
|
118
119
|
if _should_skip_category_or_group(category_or_group=category):
|
|
119
120
|
continue
|
|
120
121
|
|
|
121
122
|
yield category
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
except Exception as e:
|
|
125
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
126
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
127
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
128
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
129
|
+
else:
|
|
130
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
131
|
+
finally:
|
|
132
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|
|
@@ -10,7 +10,7 @@ from ynab_cli.adapters.ynab import models, util
|
|
|
10
10
|
from ynab_cli.adapters.ynab.api.payees import get_payees, update_payee
|
|
11
11
|
from ynab_cli.adapters.ynab.api.transactions import get_transactions_by_payee
|
|
12
12
|
from ynab_cli.domain import ports
|
|
13
|
-
from ynab_cli.domain.constants import UNUSED_PREFIX
|
|
13
|
+
from ynab_cli.domain.constants import UNUSED_PREFIX
|
|
14
14
|
from ynab_cli.domain.settings import Settings
|
|
15
15
|
|
|
16
16
|
|
|
@@ -44,25 +44,30 @@ class NormalizeNamesParams(TypedDict):
|
|
|
44
44
|
pass
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
) -> AsyncIterator[tuple[models.Payee, str]]:
|
|
50
|
-
try:
|
|
51
|
-
progress_total = 0
|
|
47
|
+
class NormalizeNames:
|
|
48
|
+
"""Use case for normalizing payee names."""
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
51
|
+
self._io = io
|
|
52
|
+
self._client = client
|
|
53
|
+
|
|
54
|
+
async def __call__(
|
|
55
|
+
self, settings: Settings, params: NormalizeNamesParams
|
|
56
|
+
) -> AsyncIterator[tuple[models.Payee, str]]:
|
|
57
|
+
try:
|
|
58
|
+
progress_total = 0
|
|
55
59
|
|
|
56
|
-
async with client:
|
|
57
60
|
payees = (
|
|
58
|
-
await util.get_asyncio_detailed(
|
|
61
|
+
await util.get_asyncio_detailed(
|
|
62
|
+
self._io, get_payees.asyncio_detailed, settings.ynab.budget_id, client=self._client
|
|
63
|
+
)
|
|
59
64
|
).data.payees
|
|
60
65
|
payees.sort(key=lambda p: p.name)
|
|
61
66
|
|
|
62
67
|
progress_total = len(payees)
|
|
63
|
-
await
|
|
68
|
+
await self._io.progress.update(total=progress_total)
|
|
64
69
|
for payee in payees:
|
|
65
|
-
await
|
|
70
|
+
await self._io.progress.update(advance=1)
|
|
66
71
|
|
|
67
72
|
if _should_skip_payee(payee=payee):
|
|
68
73
|
continue
|
|
@@ -73,50 +78,55 @@ async def normalize_names(
|
|
|
73
78
|
|
|
74
79
|
if not settings.dry_run:
|
|
75
80
|
await util.run_asyncio_detailed(
|
|
76
|
-
|
|
81
|
+
self._io,
|
|
77
82
|
update_payee.asyncio_detailed,
|
|
78
83
|
settings.ynab.budget_id,
|
|
79
84
|
str(payee.id),
|
|
80
|
-
client=
|
|
85
|
+
client=self._client,
|
|
81
86
|
body=models.PatchPayeeWrapper(payee=models.SavePayee(name=normalized_name)),
|
|
82
87
|
)
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
except Exception as e:
|
|
90
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
91
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
92
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
93
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
94
|
+
else:
|
|
95
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
96
|
+
finally:
|
|
97
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|
|
93
98
|
|
|
94
99
|
|
|
95
100
|
class ListDuplicatesParams(TypedDict):
|
|
96
101
|
pass
|
|
97
102
|
|
|
98
103
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
class ListDuplicates:
|
|
105
|
+
"""Use case for listing duplicate payees."""
|
|
106
|
+
|
|
107
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
108
|
+
self._io = io
|
|
109
|
+
self._client = client
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
async def __call__(
|
|
112
|
+
self, settings: Settings, params: ListDuplicatesParams
|
|
113
|
+
) -> AsyncIterator[tuple[models.Payee, models.Payee]]:
|
|
114
|
+
try:
|
|
115
|
+
progress_total = 0
|
|
107
116
|
|
|
108
|
-
async with client:
|
|
109
117
|
possible_duplicates: dict[tuple[UUID, str], list[tuple[UUID, str]]] = {}
|
|
110
118
|
|
|
111
119
|
payees = (
|
|
112
|
-
await util.get_asyncio_detailed(
|
|
120
|
+
await util.get_asyncio_detailed(
|
|
121
|
+
self._io, get_payees.asyncio_detailed, settings.ynab.budget_id, client=self._client
|
|
122
|
+
)
|
|
113
123
|
).data.payees
|
|
114
124
|
payees.sort(key=lambda p: p.name)
|
|
115
125
|
|
|
116
126
|
progress_total = len(payees)
|
|
117
|
-
await
|
|
127
|
+
await self._io.progress.update(total=progress_total)
|
|
118
128
|
for idx, payee in enumerate(payees):
|
|
119
|
-
await
|
|
129
|
+
await self._io.progress.update(advance=1)
|
|
120
130
|
|
|
121
131
|
if _should_skip_payee(payee=payee):
|
|
122
132
|
continue
|
|
@@ -144,51 +154,54 @@ async def list_duplicates(
|
|
|
144
154
|
|
|
145
155
|
yield (payee, filtered_payee)
|
|
146
156
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
except Exception as e:
|
|
158
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
159
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
160
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
161
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
162
|
+
else:
|
|
163
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
164
|
+
finally:
|
|
165
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|
|
156
166
|
|
|
157
167
|
|
|
158
168
|
class ListUnusedParams(TypedDict):
|
|
159
169
|
prefix_unused: bool
|
|
160
170
|
|
|
161
171
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
) -> AsyncIterator[models.Payee]:
|
|
165
|
-
try:
|
|
166
|
-
progress_total = 0
|
|
172
|
+
class ListUnused:
|
|
173
|
+
"""Use case for listing unused payees."""
|
|
167
174
|
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
176
|
+
self._io = io
|
|
177
|
+
self._client = client
|
|
178
|
+
|
|
179
|
+
async def __call__(self, settings: Settings, params: ListUnusedParams) -> AsyncIterator[models.Payee]:
|
|
180
|
+
try:
|
|
181
|
+
progress_total = 0
|
|
170
182
|
|
|
171
|
-
async with client:
|
|
172
183
|
payees = (
|
|
173
|
-
await util.get_asyncio_detailed(
|
|
184
|
+
await util.get_asyncio_detailed(
|
|
185
|
+
self._io, get_payees.asyncio_detailed, settings.ynab.budget_id, client=self._client
|
|
186
|
+
)
|
|
174
187
|
).data.payees
|
|
175
188
|
payees.sort(key=lambda p: p.name)
|
|
176
189
|
|
|
177
190
|
progress_total = len(payees)
|
|
178
|
-
await
|
|
191
|
+
await self._io.progress.update(total=progress_total)
|
|
179
192
|
for payee in payees:
|
|
180
|
-
await
|
|
193
|
+
await self._io.progress.update(advance=1)
|
|
181
194
|
|
|
182
195
|
if _should_skip_payee(payee=payee):
|
|
183
196
|
continue
|
|
184
197
|
|
|
185
198
|
transactions = (
|
|
186
199
|
await util.get_asyncio_detailed(
|
|
187
|
-
|
|
200
|
+
self._io,
|
|
188
201
|
get_transactions_by_payee.asyncio_detailed,
|
|
189
202
|
settings.ynab.budget_id,
|
|
190
203
|
str(payee.id),
|
|
191
|
-
client=
|
|
204
|
+
client=self._client,
|
|
192
205
|
)
|
|
193
206
|
).data.transactions
|
|
194
207
|
num_transactions = len(transactions)
|
|
@@ -201,60 +214,63 @@ async def list_unused(
|
|
|
201
214
|
if not settings.dry_run and params.get("prefix_unused", False):
|
|
202
215
|
new_name = f"{UNUSED_PREFIX} {payee.name}"
|
|
203
216
|
await util.run_asyncio_detailed(
|
|
204
|
-
|
|
217
|
+
self._io,
|
|
205
218
|
update_payee.asyncio_detailed,
|
|
206
219
|
settings.ynab.budget_id,
|
|
207
220
|
str(payee.id),
|
|
208
|
-
client=
|
|
221
|
+
client=self._client,
|
|
209
222
|
body=models.PatchPayeeWrapper(payee=models.SavePayee(name=new_name)),
|
|
210
223
|
)
|
|
211
224
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
225
|
+
except Exception as e:
|
|
226
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
227
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
228
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
229
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
230
|
+
else:
|
|
231
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
232
|
+
finally:
|
|
233
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|
|
221
234
|
|
|
222
235
|
|
|
223
236
|
class ListAllParams(TypedDict):
|
|
224
237
|
pass
|
|
225
238
|
|
|
226
239
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
240
|
+
class ListAll:
|
|
241
|
+
"""Use case for listing all payees."""
|
|
242
|
+
|
|
243
|
+
def __init__(self, io: ports.IO, client: ynab.AuthenticatedClient):
|
|
244
|
+
self._io = io
|
|
245
|
+
self._client = client
|
|
232
246
|
|
|
233
|
-
|
|
234
|
-
|
|
247
|
+
async def __call__(self, settings: Settings, params: ListAllParams) -> AsyncIterator[models.Payee]:
|
|
248
|
+
try:
|
|
249
|
+
progress_total = 0
|
|
235
250
|
|
|
236
|
-
async with client:
|
|
237
251
|
payees = (
|
|
238
|
-
await util.get_asyncio_detailed(
|
|
252
|
+
await util.get_asyncio_detailed(
|
|
253
|
+
self._io, get_payees.asyncio_detailed, settings.ynab.budget_id, client=self._client
|
|
254
|
+
)
|
|
239
255
|
).data.payees
|
|
240
256
|
payees.sort(key=lambda p: p.name)
|
|
241
257
|
|
|
242
258
|
progress_total = len(payees)
|
|
243
|
-
await
|
|
259
|
+
await self._io.progress.update(total=progress_total)
|
|
244
260
|
for payee in payees:
|
|
245
|
-
await
|
|
261
|
+
await self._io.progress.update(advance=1)
|
|
246
262
|
|
|
247
263
|
if _should_skip_payee(payee=payee):
|
|
248
264
|
continue
|
|
249
265
|
|
|
250
266
|
yield payee
|
|
251
267
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
268
|
+
except Exception as e:
|
|
269
|
+
if isinstance(e, util.ApiError) and e.status_code == 401:
|
|
270
|
+
await self._io.print("Invalid or expired access token. Please update your settings.")
|
|
271
|
+
elif isinstance(e, util.ApiError) and e.status_code == 429:
|
|
272
|
+
await self._io.print("API rate limit exceeded. Try again later, or get a new access token.")
|
|
273
|
+
else:
|
|
274
|
+
await self._io.print(f"Exception when calling YNAB: {e}")
|
|
275
|
+
finally:
|
|
276
|
+
await self._io.progress.update(total=progress_total, completed=progress_total)
|