ynab-cli 0.6.2__tar.gz → 0.6.3__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.
Files changed (277) hide show
  1. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/CHANGELOG.md +7 -0
  2. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/PKG-INFO +1 -1
  3. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/pyproject.toml +1 -1
  4. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/settings.py +0 -3
  5. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/use_cases/payees.py +8 -5
  6. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/use_cases/transactions.py +2 -1
  7. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/cli.py +3 -4
  8. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/cli.py +3 -2
  9. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/payees.py +16 -9
  10. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/transactions.py +7 -4
  11. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/constants.py +1 -0
  12. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/app.py +2 -9
  13. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/cli.py +3 -2
  14. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/payees/list_unused.py +5 -2
  15. ynab_cli-0.6.3/src/ynab_cli/host/textual/widgets/payees/normalize_names.py +74 -0
  16. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/transactions/apply_rules.py +4 -1
  17. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/transactions/tabs.py +2 -2
  18. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/use_cases/test_payees.py +9 -3
  19. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/commands/test_payees.py +3 -2
  20. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/commands/test_transactions.py +1 -1
  21. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/test_cli.py +0 -1
  22. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/textual/test_app.py +0 -3
  23. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/uv.lock +1 -1
  24. ynab_cli-0.6.2/src/ynab_cli/host/textual/widgets/payees/normalize_names.py +0 -38
  25. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.env-sample +0 -0
  26. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.github/.copilot-instructions.md +0 -0
  27. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.github/FUNDING.yml +0 -0
  28. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.github/dependabot.yml +0 -0
  29. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.github/workflows/pr.yml +0 -0
  30. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.github/workflows/publish-to-pypi.yml +0 -0
  31. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.gitignore +0 -0
  32. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/.python-version +0 -0
  33. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/LICENSE.md +0 -0
  34. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/Makefile +0 -0
  35. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/README.md +0 -0
  36. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/docs/index.md +0 -0
  37. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/docs/transaction_rules.md +0 -0
  38. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/mkdocs.yml +0 -0
  39. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/__init__.py +0 -0
  40. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/__init__.py +0 -0
  41. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/rich/__init__.py +0 -0
  42. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/rich/io.py +0 -0
  43. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/textual/__init__.py +0 -0
  44. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/textual/io.py +0 -0
  45. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/__init__.py +0 -0
  46. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/__init__.py +0 -0
  47. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/accounts/__init__.py +0 -0
  48. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/accounts/create_account.py +0 -0
  49. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/accounts/get_account_by_id.py +0 -0
  50. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/accounts/get_accounts.py +0 -0
  51. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/budgets/__init__.py +0 -0
  52. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/budgets/get_budget_by_id.py +0 -0
  53. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/budgets/get_budget_settings_by_id.py +0 -0
  54. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/budgets/get_budgets.py +0 -0
  55. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/categories/__init__.py +0 -0
  56. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/categories/get_categories.py +0 -0
  57. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/categories/get_category_by_id.py +0 -0
  58. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/categories/get_month_category_by_id.py +0 -0
  59. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/categories/update_category.py +0 -0
  60. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/categories/update_month_category.py +0 -0
  61. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/months/__init__.py +0 -0
  62. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/months/get_budget_month.py +0 -0
  63. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/months/get_budget_months.py +0 -0
  64. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payee_locations/__init__.py +0 -0
  65. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payee_locations/get_payee_location_by_id.py +0 -0
  66. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payee_locations/get_payee_locations.py +0 -0
  67. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payee_locations/get_payee_locations_by_payee.py +0 -0
  68. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payees/__init__.py +0 -0
  69. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payees/get_payee_by_id.py +0 -0
  70. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payees/get_payees.py +0 -0
  71. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/payees/update_payee.py +0 -0
  72. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/__init__.py +0 -0
  73. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/create_scheduled_transaction.py +0 -0
  74. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/delete_scheduled_transaction.py +0 -0
  75. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/get_scheduled_transaction_by_id.py +0 -0
  76. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/get_scheduled_transactions.py +0 -0
  77. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/scheduled_transactions/update_scheduled_transaction.py +0 -0
  78. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/__init__.py +0 -0
  79. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/create_transaction.py +0 -0
  80. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/delete_transaction.py +0 -0
  81. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/get_transaction_by_id.py +0 -0
  82. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions.py +0 -0
  83. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_account.py +0 -0
  84. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_category.py +0 -0
  85. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_month.py +0 -0
  86. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/get_transactions_by_payee.py +0 -0
  87. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/import_transactions.py +0 -0
  88. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/update_transaction.py +0 -0
  89. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/transactions/update_transactions.py +0 -0
  90. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/user/__init__.py +0 -0
  91. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/api/user/get_user.py +0 -0
  92. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/client.py +0 -0
  93. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/errors.py +0 -0
  94. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/__init__.py +0 -0
  95. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/account.py +0 -0
  96. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/account_response.py +0 -0
  97. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/account_response_data.py +0 -0
  98. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/account_type.py +0 -0
  99. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/accounts_response.py +0 -0
  100. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/accounts_response_data.py +0 -0
  101. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_detail.py +0 -0
  102. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_detail_response.py +0 -0
  103. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_detail_response_data.py +0 -0
  104. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_settings.py +0 -0
  105. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_settings_response.py +0 -0
  106. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_settings_response_data.py +0 -0
  107. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_summary.py +0 -0
  108. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_summary_response.py +0 -0
  109. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/budget_summary_response_data.py +0 -0
  110. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/bulk_response.py +0 -0
  111. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/bulk_response_data.py +0 -0
  112. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/bulk_response_data_bulk.py +0 -0
  113. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/bulk_transactions.py +0 -0
  114. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/categories_response.py +0 -0
  115. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/categories_response_data.py +0 -0
  116. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category.py +0 -0
  117. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_goal_type_type_1.py +0 -0
  118. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_goal_type_type_2_type_1.py +0 -0
  119. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_goal_type_type_3_type_1.py +0 -0
  120. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_group.py +0 -0
  121. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_group_with_categories.py +0 -0
  122. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_response.py +0 -0
  123. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/category_response_data.py +0 -0
  124. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/currency_format_type_0.py +0 -0
  125. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/date_format_type_0.py +0 -0
  126. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/error_detail.py +0 -0
  127. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/error_response.py +0 -0
  128. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/existing_transaction.py +0 -0
  129. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/get_transactions_by_account_type.py +0 -0
  130. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/get_transactions_by_category_type.py +0 -0
  131. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/get_transactions_by_month_type.py +0 -0
  132. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/get_transactions_by_payee_type.py +0 -0
  133. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/get_transactions_type.py +0 -0
  134. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/hybrid_transaction.py +0 -0
  135. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/hybrid_transaction_type.py +0 -0
  136. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/hybrid_transactions_response.py +0 -0
  137. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/hybrid_transactions_response_data.py +0 -0
  138. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/loan_account_periodic_value_type_0.py +0 -0
  139. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/month_detail.py +0 -0
  140. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/month_detail_response.py +0 -0
  141. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/month_detail_response_data.py +0 -0
  142. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/month_summaries_response.py +0 -0
  143. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/month_summaries_response_data.py +0 -0
  144. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/month_summary.py +0 -0
  145. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/new_transaction.py +0 -0
  146. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/patch_category_wrapper.py +0 -0
  147. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/patch_month_category_wrapper.py +0 -0
  148. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/patch_payee_wrapper.py +0 -0
  149. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/patch_transactions_wrapper.py +0 -0
  150. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee.py +0 -0
  151. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_location.py +0 -0
  152. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_location_response.py +0 -0
  153. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_location_response_data.py +0 -0
  154. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_locations_response.py +0 -0
  155. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_locations_response_data.py +0 -0
  156. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_response.py +0 -0
  157. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payee_response_data.py +0 -0
  158. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payees_response.py +0 -0
  159. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/payees_response_data.py +0 -0
  160. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/post_account_wrapper.py +0 -0
  161. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/post_scheduled_transaction_wrapper.py +0 -0
  162. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/post_transactions_wrapper.py +0 -0
  163. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/put_scheduled_transaction_wrapper.py +0 -0
  164. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/put_transaction_wrapper.py +0 -0
  165. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_account.py +0 -0
  166. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_category.py +0 -0
  167. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_category_response.py +0 -0
  168. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_category_response_data.py +0 -0
  169. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_month_category.py +0 -0
  170. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_payee.py +0 -0
  171. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_payee_response.py +0 -0
  172. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_payee_response_data.py +0 -0
  173. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_scheduled_transaction.py +0 -0
  174. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_sub_transaction.py +0 -0
  175. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_transaction_with_id_or_import_id.py +0 -0
  176. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_transaction_with_optional_fields.py +0 -0
  177. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_transactions_response.py +0 -0
  178. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/save_transactions_response_data.py +0 -0
  179. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_sub_transaction.py +0 -0
  180. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_detail.py +0 -0
  181. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_frequency.py +0 -0
  182. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_response.py +0 -0
  183. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_response_data.py +0 -0
  184. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_summary.py +0 -0
  185. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transaction_summary_frequency.py +0 -0
  186. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transactions_response.py +0 -0
  187. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/scheduled_transactions_response_data.py +0 -0
  188. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/sub_transaction.py +0 -0
  189. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_cleared_status.py +0 -0
  190. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_detail.py +0 -0
  191. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_flag_color_type_1.py +0 -0
  192. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_flag_color_type_2_type_1.py +0 -0
  193. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_flag_color_type_3_type_1.py +0 -0
  194. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_response.py +0 -0
  195. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_response_data.py +0 -0
  196. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_summary.py +0 -0
  197. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_1.py +0 -0
  198. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_2_type_1.py +0 -0
  199. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transaction_summary_debt_transaction_type_type_3_type_1.py +0 -0
  200. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transactions_import_response.py +0 -0
  201. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transactions_import_response_data.py +0 -0
  202. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transactions_response.py +0 -0
  203. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/transactions_response_data.py +0 -0
  204. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/user.py +0 -0
  205. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/user_response.py +0 -0
  206. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/models/user_response_data.py +0 -0
  207. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/types.py +0 -0
  208. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/adapters/ynab/util.py +0 -0
  209. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/__init__.py +0 -0
  210. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/constants.py +0 -0
  211. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/models/__init__.py +0 -0
  212. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/models/rules.py +0 -0
  213. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/ports/__init__.py +0 -0
  214. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/ports/io.py +0 -0
  215. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/use_cases/__init__.py +0 -0
  216. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/use_cases/budgets.py +0 -0
  217. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/domain/use_cases/categories.py +0 -0
  218. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/__init__.py +0 -0
  219. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/__init__.py +0 -0
  220. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/__init__.py +0 -0
  221. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/budgets.py +0 -0
  222. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/categories.py +0 -0
  223. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/rich/__init__.py +0 -0
  224. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/commands/rich/progress_table.py +0 -0
  225. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/click/container.py +0 -0
  226. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/container.py +0 -0
  227. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/__init__.py +0 -0
  228. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/app.tcss +0 -0
  229. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/container.py +0 -0
  230. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/__init__.py +0 -0
  231. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/categories/__init__.py +0 -0
  232. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/categories/list_all.py +0 -0
  233. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/categories/list_unused.py +0 -0
  234. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/categories/tabs.py +0 -0
  235. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/common/__init__.py +0 -0
  236. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/common/command_widget.py +0 -0
  237. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/common/dialogs/__init__.py +0 -0
  238. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/common/dialogs/dialog.py +0 -0
  239. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/common/dialogs/form.py +0 -0
  240. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/common/dialogs/screen.py +0 -0
  241. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/payees/__init__.py +0 -0
  242. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/payees/list_all.py +0 -0
  243. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/payees/list_duplicates.py +0 -0
  244. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/payees/tabs.py +0 -0
  245. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/tabs.py +0 -0
  246. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/host/textual/widgets/transactions/__init__.py +0 -0
  247. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/src/ynab_cli/py.typed +0 -0
  248. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/__init__.py +0 -0
  249. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/conftest.py +0 -0
  250. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/factories/__init__.py +0 -0
  251. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/factories/ynab.py +0 -0
  252. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/__init__.py +0 -0
  253. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/adapters/__init__.py +0 -0
  254. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/adapters/rich/__init__.py +0 -0
  255. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/adapters/rich/test_io.py +0 -0
  256. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/adapters/textual/__init__.py +0 -0
  257. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/adapters/textual/test_io.py +0 -0
  258. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/conftest.py +0 -0
  259. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/__init__.py +0 -0
  260. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/models/__init__.py +0 -0
  261. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/models/test_rules.py +0 -0
  262. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/ports/__init__.py +0 -0
  263. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/ports/test_io.py +0 -0
  264. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/use_cases/__init__.py +0 -0
  265. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/use_cases/test_budgets.py +0 -0
  266. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/use_cases/test_categories.py +0 -0
  267. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/domain/use_cases/test_transactions.py +0 -0
  268. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/__init__.py +0 -0
  269. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/__init__.py +0 -0
  270. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/commands/__init__.py +0 -0
  271. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/commands/test_budgets.py +0 -0
  272. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/commands/test_categories.py +0 -0
  273. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/click/conftest.py +0 -0
  274. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/conftest.py +0 -0
  275. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/textual/__init__.py +0 -0
  276. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/textual/conftest.py +0 -0
  277. {ynab_cli-0.6.2 → ynab_cli-0.6.3}/tests/unit/host/textual/test_cli.py +0 -0
@@ -5,6 +5,13 @@ 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.3 (2025-07-11)
9
+
10
+ ### Refactor
11
+
12
+ - clean up the dry_run parameters
13
+ - clean up debug option
14
+
8
15
  ## v0.6.2 (2025-07-09)
9
16
 
10
17
  ### Fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ynab-cli
3
- Version: 0.6.2
3
+ Version: 0.6.3
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/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ynab-cli"
3
- version = "0.6.2"
3
+ version = "0.6.3"
4
4
  description = "YNAB Command Line Interface"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -10,7 +10,4 @@ class YnabSettings:
10
10
 
11
11
  @_attrs_define
12
12
  class Settings:
13
- dry_run: bool = False
14
- debug: bool = False
15
-
16
13
  ynab: YnabSettings = field(factory=YnabSettings)
@@ -41,7 +41,7 @@ def _normalize_name(name: str) -> str:
41
41
 
42
42
 
43
43
  class NormalizeNamesParams(TypedDict):
44
- pass
44
+ dry_run: bool
45
45
 
46
46
 
47
47
  class NormalizeNames:
@@ -76,7 +76,7 @@ class NormalizeNames:
76
76
  if normalized_name != payee.name:
77
77
  yield (payee, normalized_name)
78
78
 
79
- if not settings.dry_run:
79
+ if not params["dry_run"]:
80
80
  await util.run_asyncio_detailed(
81
81
  self._io,
82
82
  update_payee.asyncio_detailed,
@@ -166,6 +166,7 @@ class ListDuplicates:
166
166
 
167
167
 
168
168
  class ListUnusedParams(TypedDict):
169
+ dry_run: bool
169
170
  prefix_unused: bool
170
171
 
171
172
 
@@ -208,18 +209,20 @@ class ListUnused:
208
209
 
209
210
  # List unused payee if no transactions
210
211
  if not num_transactions:
212
+ if params["prefix_unused"]:
213
+ payee.name = f"{UNUSED_PREFIX} {payee.name}"
214
+
211
215
  yield payee
212
216
 
213
217
  # If prefix_unused is True, rename the payee
214
- if not settings.dry_run and params.get("prefix_unused", False):
215
- new_name = f"{UNUSED_PREFIX} {payee.name}"
218
+ if not params["dry_run"] and params["prefix_unused"]:
216
219
  await util.run_asyncio_detailed(
217
220
  self._io,
218
221
  update_payee.asyncio_detailed,
219
222
  settings.ynab.budget_id,
220
223
  str(payee.id),
221
224
  client=self._client,
222
- body=models.PatchPayeeWrapper(payee=models.SavePayee(name=new_name)),
225
+ body=models.PatchPayeeWrapper(payee=models.SavePayee(name=payee.name)),
223
226
  )
224
227
 
225
228
  except Exception as e:
@@ -39,6 +39,7 @@ def _get_save_transaction(
39
39
 
40
40
 
41
41
  class ApplyRulesParams(TypedDict):
42
+ dry_run: bool
42
43
  transaction_rules: rules.TransactionRules
43
44
 
44
45
 
@@ -82,7 +83,7 @@ class ApplyRules:
82
83
  if save_transaction:
83
84
  yield (transaction, save_transaction)
84
85
 
85
- if not settings.dry_run:
86
+ if not params["dry_run"]:
86
87
  save_transactions.append(save_transaction)
87
88
 
88
89
  if save_transactions:
@@ -3,24 +3,23 @@ from click_default_group import DefaultGroup
3
3
 
4
4
  from ynab_cli.domain.settings import Settings
5
5
  from ynab_cli.host.click.cli import run
6
- from ynab_cli.host.constants import CONTEXT_KEY_SETTINGS, ENV_PREFIX
6
+ from ynab_cli.host.constants import CONTEXT_KEY_DEBUG, CONTEXT_KEY_SETTINGS, ENV_PREFIX
7
7
  from ynab_cli.host.textual.cli import tui
8
8
 
9
9
 
10
10
  @click.group(cls=DefaultGroup, default="tui", default_if_no_args=True)
11
- @click.option("--dry-run", is_flag=True, default=False, help="Run without making any changes.")
12
11
  @click.option("--debug", is_flag=True, default=False, help="Enable debug mode for more verbose output.")
13
12
  @click.version_option()
14
13
  @click.pass_context
15
14
  def cli(
16
15
  ctx: click.Context,
17
- dry_run: bool,
18
16
  debug: bool,
19
17
  ) -> None:
20
18
  """Main entrypoint."""
21
19
 
22
20
  ctx.ensure_object(dict)
23
- ctx.obj[CONTEXT_KEY_SETTINGS] = Settings(dry_run=dry_run, debug=debug)
21
+ ctx.obj[CONTEXT_KEY_DEBUG] = debug
22
+ ctx.obj[CONTEXT_KEY_SETTINGS] = Settings()
24
23
 
25
24
 
26
25
  cli.add_command(run)
@@ -5,7 +5,7 @@ from rich.logging import RichHandler
5
5
 
6
6
  from ynab_cli.domain.settings import Settings
7
7
  from ynab_cli.host.click.commands import budgets, categories, payees, transactions
8
- from ynab_cli.host.constants import CONTEXT_KEY_SETTINGS, ENV_PREFIX
8
+ from ynab_cli.host.constants import CONTEXT_KEY_DEBUG, CONTEXT_KEY_SETTINGS, ENV_PREFIX
9
9
 
10
10
 
11
11
  @click.group()
@@ -25,12 +25,13 @@ def run(
25
25
  """Main entrypoint for YNAB CLI commands."""
26
26
 
27
27
  ctx.ensure_object(dict)
28
+ debug: bool = ctx.obj.get(CONTEXT_KEY_DEBUG, False)
28
29
  settings: Settings = ctx.obj.get(CONTEXT_KEY_SETTINGS, Settings())
29
30
  settings.ynab.access_token = access_token
30
31
  ctx.obj[CONTEXT_KEY_SETTINGS] = settings
31
32
 
32
33
  logging.basicConfig(
33
- level="DEBUG" if settings.debug else "WARNING", # NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
34
+ level="DEBUG" if debug else "WARNING", # NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
34
35
  format="%(message)s",
35
36
  datefmt="[%X]",
36
37
  handlers=[RichHandler()],
@@ -19,8 +19,10 @@ class NormalizeNamesCommand:
19
19
  self._progress_table.table.add_column("Payee Name")
20
20
  self._progress_table.table.add_column("Normalized Name")
21
21
 
22
- async def __call__(self, settings: Settings) -> None:
23
- params: use_cases.NormalizeNamesParams = {}
22
+ async def __call__(self, settings: Settings, dry_run: bool) -> None:
23
+ params: use_cases.NormalizeNamesParams = {
24
+ "dry_run": dry_run,
25
+ }
24
26
 
25
27
  console = None
26
28
  with self._progress_table:
@@ -76,8 +78,9 @@ class ListUnusedCommand:
76
78
  self._progress_table.table.add_column("Payee Id")
77
79
  self._progress_table.table.add_column("Payee Name")
78
80
 
79
- async def __call__(self, settings: Settings, prefix_unused: bool) -> None:
81
+ async def __call__(self, settings: Settings, dry_run: bool, prefix_unused: bool) -> None:
80
82
  params: use_cases.ListUnusedParams = {
83
+ "dry_run": dry_run,
81
84
  "prefix_unused": prefix_unused,
82
85
  }
83
86
 
@@ -122,13 +125,14 @@ class ListAllCommand:
122
125
 
123
126
 
124
127
  @containerize
125
- async def _normalize_names(container: Container) -> None:
126
- await container[NormalizeNamesCommand](container[Settings])
128
+ async def _normalize_names(container: Container, dry_run: bool) -> None:
129
+ await container[NormalizeNamesCommand](container[Settings], dry_run)
127
130
 
128
131
 
129
132
  @click.command()
133
+ @click.option("--dry-run", is_flag=True, default=False, help="Run without making any changes.")
130
134
  @click.pass_context
131
- def normalize_names(ctx: click.Context) -> None:
135
+ def normalize_names(ctx: click.Context, dry_run: bool) -> None:
132
136
  """Normalize payee names in the YNAB budget."""
133
137
 
134
138
  ctx.ensure_object(dict)
@@ -138,6 +142,7 @@ def normalize_names(ctx: click.Context) -> None:
138
142
  anyio.run(
139
143
  _normalize_names,
140
144
  settings,
145
+ dry_run,
141
146
  backend_options={"use_uvloop": True},
142
147
  )
143
148
 
@@ -164,14 +169,15 @@ def list_duplicates(ctx: click.Context) -> None:
164
169
 
165
170
 
166
171
  @containerize
167
- async def _list_unused(container: Container, prefix_unused: bool) -> None:
168
- await container[ListUnusedCommand](container[Settings], prefix_unused)
172
+ async def _list_unused(container: Container, dry_run: bool, prefix_unused: bool) -> None:
173
+ await container[ListUnusedCommand](container[Settings], dry_run, prefix_unused)
169
174
 
170
175
 
171
176
  @click.command()
177
+ @click.option("--dry-run", is_flag=True, default=False, help="Run without making any changes.")
172
178
  @click.option("--prefix-unused", is_flag=True, default=False, help="Add a prefix to the unused payee names.")
173
179
  @click.pass_context
174
- def list_unused(ctx: click.Context, prefix_unused: bool) -> None:
180
+ def list_unused(ctx: click.Context, dry_run: bool, prefix_unused: bool) -> None:
175
181
  """List unused payees in the YNAB budget."""
176
182
 
177
183
  ctx.ensure_object(dict)
@@ -181,6 +187,7 @@ def list_unused(ctx: click.Context, prefix_unused: bool) -> None:
181
187
  anyio.run(
182
188
  _list_unused,
183
189
  settings,
190
+ dry_run,
184
191
  prefix_unused,
185
192
  backend_options={"use_uvloop": True},
186
193
  )
@@ -27,8 +27,9 @@ class ApplyRulesCommand:
27
27
  self._progress_table.table.add_column("Transaction Amount")
28
28
  self._progress_table.table.add_column("Transaction Changes")
29
29
 
30
- async def __call__(self, settings: Settings, transaction_rules: rules.TransactionRules) -> None:
30
+ async def __call__(self, settings: Settings, dry_run: bool, transaction_rules: rules.TransactionRules) -> None:
31
31
  params: use_cases.ApplyRulesParams = {
32
+ "dry_run": dry_run,
32
33
  "transaction_rules": transaction_rules,
33
34
  }
34
35
 
@@ -52,14 +53,15 @@ class ApplyRulesCommand:
52
53
 
53
54
 
54
55
  @containerize
55
- async def _apply_rules(container: Container, transaction_rules: rules.TransactionRules) -> None:
56
- await container[ApplyRulesCommand](container[Settings], transaction_rules)
56
+ async def _apply_rules(container: Container, dry_run: bool, transaction_rules: rules.TransactionRules) -> None:
57
+ await container[ApplyRulesCommand](container[Settings], dry_run, transaction_rules)
57
58
 
58
59
 
59
60
  @click.command()
61
+ @click.option("--dry-run", is_flag=True, default=False, help="Run without making any changes.")
60
62
  @click.argument("rules-file", type=click.File())
61
63
  @click.pass_context
62
- def apply_rules(ctx: click.Context, rules_file: IO[Any]) -> None:
64
+ def apply_rules(ctx: click.Context, dry_run: bool, rules_file: IO[Any]) -> None:
63
65
  """Apply transaction rules from a JSON RULES_FILE to transactions in the YNAB budget.
64
66
 
65
67
  RULES_FILE should be a JSON file containing transaction rules.
@@ -74,6 +76,7 @@ def apply_rules(ctx: click.Context, rules_file: IO[Any]) -> None:
74
76
  anyio.run(
75
77
  _apply_rules,
76
78
  settings,
79
+ dry_run,
77
80
  transaction_rules,
78
81
  backend_options={"use_uvloop": True},
79
82
  )
@@ -1,3 +1,4 @@
1
1
  ENV_PREFIX = "YNAB_CLI"
2
2
 
3
+ CONTEXT_KEY_DEBUG = "DEBUG"
3
4
  CONTEXT_KEY_SETTINGS = "SETTINGS"
@@ -5,7 +5,7 @@ from textual.app import App, ComposeResult
5
5
  from textual.binding import Binding, BindingType
6
6
  from textual.containers import Horizontal, Vertical
7
7
  from textual.reactive import reactive
8
- from textual.widgets import Checkbox, Footer, Header, Input, Label, TabbedContent
8
+ from textual.widgets import Footer, Header, Input, Label, TabbedContent
9
9
  from typing_extensions import override
10
10
 
11
11
  from ynab_cli.adapters.ynab.client import AuthenticatedClient
@@ -29,25 +29,18 @@ class SettingsDialogForm(DialogForm[Settings]):
29
29
  with Vertical():
30
30
  yield Label("Budget Id")
31
31
  yield Input(self._settings.ynab.budget_id, placeholder="Budget Id", id="budget_id")
32
- with Horizontal():
33
- yield Checkbox("Debug", self._settings.debug, id="debug")
34
- yield Checkbox("Dry Run", self._settings.dry_run, id="dry_run")
35
32
 
36
33
  @override
37
34
  async def get_result(self) -> Settings:
38
35
  """Get the result from the dialog form."""
39
36
  access_token = self.query_one("#access_token", Input).value.strip()
40
37
  budget_id = self.query_one("#budget_id", Input).value.strip()
41
- debug = self.query_one("#debug", Checkbox).value
42
- dry_run = self.query_one("#dry_run", Checkbox).value
43
38
 
44
39
  return Settings(
45
40
  ynab=YnabSettings(
46
41
  access_token=access_token,
47
42
  budget_id=budget_id,
48
- ),
49
- debug=debug,
50
- dry_run=dry_run,
43
+ )
51
44
  )
52
45
 
53
46
 
@@ -7,7 +7,7 @@ from textual.logging import TextualHandler
7
7
 
8
8
  from ynab_cli.adapters.ynab.client import AuthenticatedClient
9
9
  from ynab_cli.domain.settings import Settings, YnabSettings
10
- from ynab_cli.host.constants import CONTEXT_KEY_SETTINGS, ENV_PREFIX
10
+ from ynab_cli.host.constants import CONTEXT_KEY_DEBUG, CONTEXT_KEY_SETTINGS, ENV_PREFIX
11
11
  from ynab_cli.host.textual.app import YnabCliApp
12
12
  from ynab_cli.host.textual.container import containerize
13
13
 
@@ -32,11 +32,12 @@ def tui(
32
32
  """Run the Textual User Interface (TUI) for YNAB CLI."""
33
33
 
34
34
  ctx.ensure_object(dict)
35
+ debug: bool = ctx.obj.get(CONTEXT_KEY_DEBUG, False)
35
36
  settings: Settings = ctx.obj.get(CONTEXT_KEY_SETTINGS, Settings())
36
37
  settings.ynab = YnabSettings(access_token=access_token, budget_id=budget_id)
37
38
 
38
39
  logging.basicConfig(
39
- level="DEBUG" if settings.debug else "WARNING", # NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
40
+ level="DEBUG" if debug else "WARNING", # NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
40
41
  format="%(message)s",
41
42
  datefmt="[%X]",
42
43
  handlers=[TextualHandler()],
@@ -20,12 +20,14 @@ class ListUnusedParamsDialogForm(DialogForm[use_cases.ListUnusedParams]):
20
20
 
21
21
  @override
22
22
  def compose(self) -> ComposeResult:
23
+ yield Checkbox("Dry Run", self._params["dry_run"], id="dry_run")
23
24
  yield Checkbox("Prefix Unused Payees", self._params["prefix_unused"], id="prefix_unused")
24
25
 
25
26
  @override
26
27
  async def get_result(self) -> use_cases.ListUnusedParams:
27
28
  return {
28
- "prefix_unused": self.query_one(Checkbox).value,
29
+ "dry_run": self.query_one("#dry_run", Checkbox).value,
30
+ "prefix_unused": self.query_one("#prefix_unused", Checkbox).value,
29
31
  }
30
32
 
31
33
 
@@ -33,7 +35,8 @@ class ListUnusedCommand(CommandWidget):
33
35
  def __init__(self) -> None:
34
36
  super().__init__()
35
37
  self._params: use_cases.ListUnusedParams = {
36
- "prefix_unused": False, # Default to not prefixing unused payees
38
+ "dry_run": False,
39
+ "prefix_unused": False,
37
40
  }
38
41
 
39
42
  def on_mount(self) -> None:
@@ -0,0 +1,74 @@
1
+ from typing import TYPE_CHECKING, Any, cast
2
+
3
+ from textual.app import ComposeResult
4
+ from textual.widgets import Checkbox, DataTable, Log, ProgressBar
5
+ from typing_extensions import override
6
+
7
+ from ynab_cli.adapters.textual.io import TextualIO
8
+ from ynab_cli.domain.use_cases import payees as use_cases
9
+ from ynab_cli.host.textual.widgets.common.command_widget import CommandWidget
10
+ from ynab_cli.host.textual.widgets.common.dialogs import CANCELLED, DialogForm, SaveCancelDialogScreen
11
+
12
+ if TYPE_CHECKING:
13
+ from ynab_cli.host.textual.app import YnabCliApp
14
+
15
+
16
+ class NormalizeNamesParamsDialogForm(DialogForm[use_cases.NormalizeNamesParams]):
17
+ def __init__(self, params: use_cases.NormalizeNamesParams, *args: Any, **kwargs: Any) -> None:
18
+ super().__init__(*args, **kwargs)
19
+ self._params: use_cases.NormalizeNamesParams = {**params}
20
+
21
+ @override
22
+ def compose(self) -> ComposeResult:
23
+ yield Checkbox("Dry Run", self._params["dry_run"], id="dry_run")
24
+
25
+ @override
26
+ async def get_result(self) -> use_cases.NormalizeNamesParams:
27
+ return {
28
+ "dry_run": self.query_one("#dry_run", Checkbox).value,
29
+ }
30
+
31
+
32
+ class NormalizeNamesCommand(CommandWidget):
33
+ def __init__(self) -> None:
34
+ super().__init__()
35
+ self._params: use_cases.NormalizeNamesParams = {
36
+ "dry_run": False,
37
+ }
38
+
39
+ def on_mount(self) -> None:
40
+ table = self.query_one(DataTable)
41
+ table.add_columns(
42
+ "Payee Id",
43
+ "Payee Name",
44
+ "Normalized Name",
45
+ )
46
+
47
+ @override
48
+ async def _get_command_params(self) -> None:
49
+ result = await self.app.push_screen_wait(
50
+ SaveCancelDialogScreen(
51
+ NormalizeNamesParamsDialogForm(self._params), title="Payees: Normalize Names Parameters"
52
+ )
53
+ )
54
+ if result is not CANCELLED:
55
+ self._params = result
56
+
57
+ @override
58
+ async def _run_command(self) -> None:
59
+ params: use_cases.NormalizeNamesParams = {
60
+ "dry_run": False,
61
+ }
62
+
63
+ progress_bar = self.query_one(ProgressBar)
64
+ table = self.query_one(DataTable)
65
+ log = self.query_one(Log)
66
+
67
+ async for payee, normalized_name in use_cases.NormalizeNames(
68
+ TextualIO(self.app, log, progress_bar), cast("YnabCliApp", self.app).client
69
+ )(self.settings, params):
70
+ table.add_row(
71
+ payee.id,
72
+ payee.name,
73
+ normalized_name,
74
+ )
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, cast
4
4
  from textual import on, work
5
5
  from textual.app import ComposeResult
6
6
  from textual.containers import ScrollableContainer
7
- from textual.widgets import Button, DataTable, Log, Pretty, ProgressBar
7
+ from textual.widgets import Button, Checkbox, DataTable, Log, Pretty, ProgressBar
8
8
  from textual_fspicker import FileOpen
9
9
  from typing_extensions import override
10
10
 
@@ -25,6 +25,7 @@ class ApplyRulesParamsDialogForm(DialogForm[use_cases.ApplyRulesParams]):
25
25
 
26
26
  @override
27
27
  def compose(self) -> ComposeResult:
28
+ yield Checkbox("Dry Run", self._params["dry_run"], id="dry_run")
28
29
  yield Button("Select Transaction Rules JSON File", id="select_rules", variant="primary")
29
30
  with ScrollableContainer():
30
31
  yield Pretty(self._params["transaction_rules"].to_dict())
@@ -52,6 +53,7 @@ class ApplyRulesParamsDialogForm(DialogForm[use_cases.ApplyRulesParams]):
52
53
  @override
53
54
  async def get_result(self) -> use_cases.ApplyRulesParams:
54
55
  return {
56
+ "dry_run": self.query_one("#dry_run", Checkbox).value,
55
57
  "transaction_rules": self._params["transaction_rules"],
56
58
  }
57
59
 
@@ -60,6 +62,7 @@ class ApplyRulesCommand(CommandWidget):
60
62
  def __init__(self) -> None:
61
63
  super().__init__()
62
64
  self._params: use_cases.ApplyRulesParams = {
65
+ "dry_run": False,
63
66
  "transaction_rules": rules.TransactionRules(transaction_rules=[]),
64
67
  }
65
68
 
@@ -16,13 +16,13 @@ class TransactionsTabs(Widget):
16
16
  @override
17
17
  def compose(self) -> ComposeResult:
18
18
  with TabbedContent():
19
- with TabPane("Transactions", id="transactions"):
19
+ with TabPane("Apply Rules", id="apply_rules"):
20
20
  yield ApplyRulesCommand().data_bind(settings=TransactionsTabs.settings)
21
21
 
22
22
  def active_command(self) -> CommandWidget:
23
23
  tabbed_content = self.query_one(TabbedContent)
24
24
  if tabbed_content.active_pane:
25
- if tabbed_content.active_pane.id == "transactions":
25
+ if tabbed_content.active_pane.id == "apply_rules":
26
26
  return self.query_one(ApplyRulesCommand)
27
27
 
28
28
  raise ValueError("No active command found in TransactionsTabs.")
@@ -131,7 +131,9 @@ async def test_normalize_names(mocker: MockerFixture, mock_io: MagicMock, empty_
131
131
  mock_update_payee.asyncio_detailed = AsyncMock()
132
132
 
133
133
  settings = Settings()
134
- params: use_cases.NormalizeNamesParams = {}
134
+ params: use_cases.NormalizeNamesParams = {
135
+ "dry_run": False,
136
+ }
135
137
 
136
138
  results: list[tuple[models.Payee, str]] = []
137
139
  async for result in use_cases.NormalizeNames(mock_io, MagicMock())(settings, params):
@@ -163,7 +165,9 @@ async def test_normalize_names_exception(
163
165
  mock_get_payees.asyncio_detailed.side_effect = exception
164
166
 
165
167
  settings = Settings()
166
- params: use_cases.NormalizeNamesParams = {}
168
+ params: use_cases.NormalizeNamesParams = {
169
+ "dry_run": False,
170
+ }
167
171
 
168
172
  async for _ in use_cases.NormalizeNames(mock_io, MagicMock())(settings, params):
169
173
  pass
@@ -268,13 +272,14 @@ async def test_list_unused(mocker: MockerFixture, mock_io: MagicMock, empty_uuid
268
272
 
269
273
  settings = Settings()
270
274
  params: use_cases.ListUnusedParams = {
275
+ "dry_run": False,
271
276
  "prefix_unused": True,
272
277
  }
273
278
 
274
279
  results: list[models.Payee] = []
275
280
  async for result in use_cases.ListUnused(mock_io, MagicMock())(settings, params):
276
281
  assert isinstance(result, models.Payee)
277
- assert result.name in ["Payee 1"]
282
+ assert result.name in ["[UNUSED] Payee 1"]
278
283
  results.append(result)
279
284
 
280
285
  assert len(results) == 1
@@ -299,6 +304,7 @@ async def test_list_unused_exception(
299
304
 
300
305
  settings = Settings()
301
306
  params: use_cases.ListUnusedParams = {
307
+ "dry_run": False,
302
308
  "prefix_unused": True,
303
309
  }
304
310
 
@@ -33,7 +33,7 @@ def test_normalize_names(runner: CliRunner, container: Container, empty_uuid: UU
33
33
  assert result.exit_code == 0
34
34
 
35
35
  use_case.assert_called_once_with(
36
- Settings(ynab=YnabSettings(access_token="test_token", budget_id="test_budget")), {}
36
+ Settings(ynab=YnabSettings(access_token="test_token", budget_id="test_budget")), {"dry_run": False}
37
37
  )
38
38
 
39
39
 
@@ -82,7 +82,8 @@ def test_list_unused(runner: CliRunner, container: Container, empty_uuid: UUID)
82
82
 
83
83
  assert result.exit_code == 0
84
84
  use_case.assert_called_once_with(
85
- Settings(ynab=YnabSettings(access_token="test_token", budget_id="test_budget")), {"prefix_unused": False}
85
+ Settings(ynab=YnabSettings(access_token="test_token", budget_id="test_budget")),
86
+ {"dry_run": False, "prefix_unused": False},
86
87
  )
87
88
 
88
89
 
@@ -61,5 +61,5 @@ def test_apply_rules(
61
61
 
62
62
  use_case.assert_called_once_with(
63
63
  Settings(ynab=YnabSettings(access_token="test_token", budget_id="test_budget")),
64
- {"transaction_rules": transaction_rules},
64
+ {"dry_run": False, "transaction_rules": transaction_rules},
65
65
  )
@@ -22,7 +22,6 @@ class TestCli:
22
22
 
23
23
  assert result.exit_code == 0
24
24
  assert "Main entrypoint." in result.output
25
- assert "--dry-run" in result.output
26
25
  assert "--debug" in result.output
27
26
 
28
27
 
@@ -26,7 +26,6 @@ async def test_settings_dialog_form_get_result(monkeypatch: pytest.MonkeyPatch)
26
26
  ("#access_token", Input): mock.Mock(spec=Input, value="newtoken "),
27
27
  ("#budget_id", Input): mock.Mock(spec=Input, value=" newbudget"),
28
28
  ("#debug", Checkbox): mock.Mock(spec=Checkbox, value=True),
29
- ("#dry_run", Checkbox): mock.Mock(spec=Checkbox, value=True),
30
29
  }[(selector, cls)],
31
30
  )
32
31
 
@@ -34,8 +33,6 @@ async def test_settings_dialog_form_get_result(monkeypatch: pytest.MonkeyPatch)
34
33
  assert isinstance(result, Settings)
35
34
  assert result.ynab.access_token == "newtoken"
36
35
  assert result.ynab.budget_id == "newbudget"
37
- assert result.debug is True
38
- assert result.dry_run is True
39
36
 
40
37
 
41
38
  def test_ynab_cli_app_initialization() -> None:
@@ -1711,7 +1711,7 @@ wheels = [
1711
1711
 
1712
1712
  [[package]]
1713
1713
  name = "ynab-cli"
1714
- version = "0.6.2"
1714
+ version = "0.6.3"
1715
1715
  source = { editable = "." }
1716
1716
  dependencies = [
1717
1717
  { name = "anyio" },
@@ -1,38 +0,0 @@
1
- from typing import TYPE_CHECKING, cast
2
-
3
- from textual.widgets import DataTable, Log, ProgressBar
4
- from typing_extensions import override
5
-
6
- from ynab_cli.adapters.textual.io import TextualIO
7
- from ynab_cli.domain.use_cases import payees as use_cases
8
- from ynab_cli.host.textual.widgets.common.command_widget import CommandWidget
9
-
10
- if TYPE_CHECKING:
11
- from ynab_cli.host.textual.app import YnabCliApp
12
-
13
-
14
- class NormalizeNamesCommand(CommandWidget):
15
- def on_mount(self) -> None:
16
- table = self.query_one(DataTable)
17
- table.add_columns(
18
- "Payee Id",
19
- "Payee Name",
20
- "Normalized Name",
21
- )
22
-
23
- @override
24
- async def _run_command(self) -> None:
25
- params: use_cases.NormalizeNamesParams = {}
26
-
27
- progress_bar = self.query_one(ProgressBar)
28
- table = self.query_one(DataTable)
29
- log = self.query_one(Log)
30
-
31
- async for payee, normalized_name in use_cases.NormalizeNames(
32
- TextualIO(self.app, log, progress_bar), cast("YnabCliApp", self.app).client
33
- )(self.settings, params):
34
- table.add_row(
35
- payee.id,
36
- payee.name,
37
- normalized_name,
38
- )
File without changes
File without changes
File without changes