monopyly 1.5.1__tar.gz → 1.5.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.
Files changed (236) hide show
  1. {monopyly-1.5.1 → monopyly-1.5.2}/.gitignore +4 -0
  2. {monopyly-1.5.1 → monopyly-1.5.2}/PKG-INFO +9 -10
  3. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/CHANGELOG.md +12 -0
  4. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/README.md +2 -2
  5. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/__init__.py +22 -27
  6. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/_version.py +2 -2
  7. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/auth/routes.py +1 -1
  8. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/accounts.py +3 -3
  9. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/banks.py +1 -1
  10. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/routes.py +1 -1
  11. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/transactions.py +14 -5
  12. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/transactions.py +17 -7
  13. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/actions.py +0 -7
  14. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/accounts.py +1 -1
  15. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/cards.py +7 -3
  16. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/routes.py +31 -28
  17. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/statements.py +1 -1
  18. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/transactions/_transactions.py +18 -5
  19. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/transactions/activity/reconciliation.py +20 -1
  20. monopyly-1.5.2/monopyly/database/__init__.py +46 -0
  21. monopyly-1.5.2/monopyly/database/models.py +391 -0
  22. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/create-balance-chart.js +1 -1
  23. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +1 -1
  24. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/condensed_row_content.html +0 -1
  25. {monopyly-1.5.1 → monopyly-1.5.2}/pyproject.toml +8 -8
  26. monopyly-1.5.1/monopyly/cli/apps.py +0 -108
  27. monopyly-1.5.1/monopyly/cli/launch.py +0 -135
  28. monopyly-1.5.1/monopyly/config/__init__.py +0 -1
  29. monopyly-1.5.1/monopyly/config/default_settings.py +0 -56
  30. monopyly-1.5.1/monopyly/config/settings.py +0 -59
  31. monopyly-1.5.1/monopyly/database/__init__.py +0 -101
  32. monopyly-1.5.1/monopyly/database/models.py +0 -483
  33. {monopyly-1.5.1 → monopyly-1.5.2}/COPYING +0 -0
  34. {monopyly-1.5.1 → monopyly-1.5.2}/LICENSE +0 -0
  35. {monopyly-1.5.1 → monopyly-1.5.2}/README.md +0 -0
  36. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/auth/actions.py +0 -0
  37. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/auth/blueprint.py +0 -0
  38. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/auth/tools.py +0 -0
  39. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/actions.py +0 -0
  40. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/blueprint.py +0 -0
  41. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/filters.py +0 -0
  42. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/banking/forms.py +0 -0
  43. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/forms/__init__.py +0 -0
  44. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/forms/_forms.py +0 -0
  45. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/forms/fields.py +0 -0
  46. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/forms/utils.py +0 -0
  47. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/forms/validators.py +0 -0
  48. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/common/utils.py +0 -0
  49. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/blueprint.py +0 -0
  50. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/context_processors.py +0 -0
  51. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/errors.py +0 -0
  52. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/filters.py +0 -0
  53. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/internal_transactions.py +0 -0
  54. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/core/routes.py +0 -0
  55. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/actions.py +0 -0
  56. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/blueprint.py +0 -0
  57. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/forms.py +0 -0
  58. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/transactions/__init__.py +0 -0
  59. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/transactions/activity/__init__.py +0 -0
  60. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/transactions/activity/data.py +0 -0
  61. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/credit/transactions/activity/parser.py +0 -0
  62. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/database/preloads.sql +0 -0
  63. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/database/schema.sql +0 -0
  64. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/database/views.sql +0 -0
  65. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/css/style.css +0 -0
  66. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/browserconfig.xml +0 -0
  67. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-114.png +0 -0
  68. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-120.png +0 -0
  69. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-144.png +0 -0
  70. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-150.png +0 -0
  71. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-152.png +0 -0
  72. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-16.png +0 -0
  73. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-160.png +0 -0
  74. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-180.png +0 -0
  75. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-192.png +0 -0
  76. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-310.png +0 -0
  77. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-32.png +0 -0
  78. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-57.png +0 -0
  79. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-60.png +0 -0
  80. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-64.png +0 -0
  81. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-70.png +0 -0
  82. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-72.png +0 -0
  83. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-76.png +0 -0
  84. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon-96.png +0 -0
  85. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon.ico +0 -0
  86. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/favicon/favicon.png +0 -0
  87. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/bank-account-details.png +0 -0
  88. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/bank-account-summaries.png +0 -0
  89. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/bank-accounts.png +0 -0
  90. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/credit-account-details.png +0 -0
  91. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/credit-transactions.png +0 -0
  92. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/homepage-user.png +0 -0
  93. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/homepage.png +0 -0
  94. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/about/statement-details.png +0 -0
  95. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/cards/chase-card.png +0 -0
  96. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/cards/discover-card.png +0 -0
  97. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/cards/new-card.png +0 -0
  98. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/cards/template-card.png +0 -0
  99. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/arrow-down.png +0 -0
  100. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/arrow-left.png +0 -0
  101. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/arrow-up.png +0 -0
  102. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/checkmark.png +0 -0
  103. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
  104. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/delete-orange.png +0 -0
  105. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/delete-thick.png +0 -0
  106. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/delete.png +0 -0
  107. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/edit.png +0 -0
  108. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/link.png +0 -0
  109. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/minus-thick.png +0 -0
  110. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/minus.png +0 -0
  111. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/plus-thick.png +0 -0
  112. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/plus.png +0 -0
  113. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/refresh.png +0 -0
  114. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/save.png +0 -0
  115. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/sort-asc.png +0 -0
  116. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/sort-desc.png +0 -0
  117. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/statement-pair.png +0 -0
  118. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/statement-thick.png +0 -0
  119. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/statement.png +0 -0
  120. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/icons/x-thick.png +0 -0
  121. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/img/statement.png +0 -0
  122. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/jquery-3.7.0.min.js +0 -0
  123. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/add-subtransaction.js +0 -0
  124. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/add-transfer.js +0 -0
  125. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/autocomplete-transaction.js +0 -0
  126. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/bind-tag-actions.js +0 -0
  127. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/create-category-chart.js +0 -0
  128. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/define-filter.js +0 -0
  129. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
  130. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/display-new-bank-inputs.js +0 -0
  131. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
  132. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/expand-bank-account.js +0 -0
  133. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/expand-bank.js +0 -0
  134. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/expand-transaction.js +0 -0
  135. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/flip-card.js +0 -0
  136. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/hide-homepage-block.js +0 -0
  137. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
  138. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/infer-card.js +0 -0
  139. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/infer-statement.js +0 -0
  140. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/make-payment.js +0 -0
  141. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/ajax.js +0 -0
  142. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/autocomplete-input.js +0 -0
  143. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/expand-box-row.js +0 -0
  144. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/expand-transaction.js +0 -0
  145. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/form-suggestions.js +0 -0
  146. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
  147. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/manage-overlays.js +0 -0
  148. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/manage-subforms.js +0 -0
  149. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/update-database-widget.js +0 -0
  150. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/modules/update-display-ajax.js +0 -0
  151. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/show-credit-activity-loader.js +0 -0
  152. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/show-linked-transaction.js +0 -0
  153. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/toggle-navigation.js +0 -0
  154. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/update-account-statement-parameters.js +0 -0
  155. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/update-bank-name.js +0 -0
  156. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/update-card-status.js +0 -0
  157. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/update-statement-parameters.js +0 -0
  158. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/update-statements-display.js +0 -0
  159. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/update-transactions-display.js +0 -0
  160. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/use-suggested-amount.js +0 -0
  161. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/static/js/use-suggested-merchant.js +0 -0
  162. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/auth/change_password.html +0 -0
  163. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/auth/login.html +0 -0
  164. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/auth/register.html +0 -0
  165. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/account_form/account_form.html +0 -0
  166. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
  167. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/account_page.html +0 -0
  168. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/account_summaries.html +0 -0
  169. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/account_summaries_page.html +0 -0
  170. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/account_summary.html +0 -0
  171. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/accounts_page.html +0 -0
  172. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
  173. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
  174. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
  175. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
  176. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
  177. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
  178. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
  179. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
  180. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
  181. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/banking/transactions_table/transactions.html +0 -0
  182. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/form_page.html +0 -0
  183. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transaction_form/subform.html +0 -0
  184. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
  185. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
  186. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +0 -0
  187. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
  188. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
  189. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
  190. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
  191. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
  192. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/common/transactions_table/transactions.html +0 -0
  193. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/credits.html +0 -0
  194. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/400.html +0 -0
  195. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/401.html +0 -0
  196. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/403.html +0 -0
  197. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/404.html +0 -0
  198. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/405.html +0 -0
  199. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/408.html +0 -0
  200. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/418.html +0 -0
  201. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/425.html +0 -0
  202. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/500.html +0 -0
  203. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/errors/error.html +0 -0
  204. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/index.html +0 -0
  205. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/profile.html +0 -0
  206. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/core/story.html +0 -0
  207. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/account_page.html +0 -0
  208. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/card_form/card_form.html +0 -0
  209. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
  210. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
  211. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
  212. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
  213. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/card_submission_page.html +0 -0
  214. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/cards.html +0 -0
  215. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/cards_page.html +0 -0
  216. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_page.html +0 -0
  217. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
  218. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +0 -0
  219. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
  220. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
  221. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statement_summary.html +0 -0
  222. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statements.html +0 -0
  223. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/statements_page.html +0 -0
  224. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -0
  225. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/tag_tree/tag_tree.html +0 -0
  226. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/tags_page.html +0 -0
  227. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
  228. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
  229. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
  230. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
  231. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transaction_submission_page.html +0 -0
  232. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transactions_page.html +0 -0
  233. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/expanded_row_content.html +0 -0
  234. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
  235. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/credit/transactions_table/transactions.html +0 -0
  236. {monopyly-1.5.1 → monopyly-1.5.2}/monopyly/templates/layout.html +0 -0
@@ -1,4 +1,5 @@
1
1
  *.swp
2
+ *vimrc*.vim
2
3
  .ipynb_checkpoints
3
4
 
4
5
  # Byte-compiled / optimized / DLL files
@@ -85,3 +86,6 @@ monopyly/static/favicon/*.ai
85
86
 
86
87
  # Flask instance files/data
87
88
  instance/
89
+
90
+ # PyPI Token
91
+ .TOKEN
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: monopyly
3
- Version: 1.5.1
3
+ Version: 1.5.2
4
4
  Summary: A homemade personal finance manager.
5
5
  Project-URL: Download, https://pypi.org/project/monopyly
6
6
  Project-URL: Homepage, http://monopyly.com
@@ -23,16 +23,15 @@ Classifier: Topic :: Office/Business :: Financial
23
23
  Classifier: Topic :: Office/Business :: Financial :: Accounting
24
24
  Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
25
25
  Requires-Python: <3.11,>=3.10
26
- Requires-Dist: authanor==1.1.1
27
- Requires-Dist: flask-wtf==1.2.1
28
- Requires-Dist: flask==3.0.3
29
- Requires-Dist: fuisce==1.0.2
26
+ Requires-Dist: dry-foundation==1.3.0
27
+ Requires-Dist: flask-wtf==1.2.2
28
+ Requires-Dist: flask==3.1.2
30
29
  Requires-Dist: gunicorn==23.0.0
31
- Requires-Dist: markdown==3.7
30
+ Requires-Dist: markdown==3.9
32
31
  Requires-Dist: nltk==3.9.1
33
32
  Requires-Dist: python-dateutil==2.9.0
34
- Requires-Dist: rich==13.8.1
35
- Requires-Dist: sqlalchemy==2.0.35
33
+ Requires-Dist: rich==14.1.0
34
+ Requires-Dist: sqlalchemy==2.0.43
36
35
  Description-Content-Type: text/markdown
37
36
 
38
37
  <div id="header">
@@ -64,10 +63,10 @@ The package requires a recent version of Python (3.10+).
64
63
 
65
64
  ## Getting started
66
65
 
67
- Once the package is properly installed, run the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
66
+ Once the package is properly installed, launch the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
68
67
 
69
68
  ```
70
- $ monopyly local --browser [--host HOST] [--port PORT]
69
+ $ monopyly launch local --browser [--host HOST] [--port PORT]
71
70
  ```
72
71
 
73
72
  Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
@@ -207,7 +207,19 @@
207
207
  - Return to statement details page after deleting a transaction (rather than returning to the general transactions page)
208
208
  - Allow users to change their password
209
209
  - Warn users before form submission if the configuration currently disallows registration
210
+ - Fix issues with the application launcher not launching the browser; couple application launch process more tightly with click
211
+ - Use type annotations for SQLAlchemy ORM declarative mappings
210
212
  - Increase the flexibility of the credit activity parser
211
213
 
212
214
 
215
+ ### 1.5.2
216
+
217
+ - Use smoothing on charts for up to 100 transactions
218
+ - Improve tokenization normalization for credit reconciliation
219
+ - Remove statement requiring activity files be located in the `Downloads` directory
220
+ - Do not clear the reconciliation info when adding subtransaction fields via POST request
221
+ - Incorporate support for enhanced database handler selection subsets
222
+ - Bump dependencies (including support for recent SQLAlchemy versions)
223
+
224
+
213
225
  <a name="bottom" id="bottom"></a>
@@ -27,10 +27,10 @@ The package requires a recent version of Python (3.10+).
27
27
 
28
28
  ## Getting started
29
29
 
30
- Once the package is properly installed, run the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
30
+ Once the package is properly installed, launch the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
31
31
 
32
32
  ```
33
- $ monopyly local --browser [--host HOST] [--port PORT]
33
+ $ monopyly launch local --browser [--host HOST] [--port PORT]
34
34
  ```
35
35
 
36
36
  Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
@@ -2,39 +2,29 @@
2
2
  Run the Monopyly app.
3
3
  """
4
4
 
5
- from flask import Flask
5
+ from dry_foundation import DryFlask, Factory, interact
6
6
 
7
- from monopyly.config import DevelopmentConfig, ProductionConfig
8
- from monopyly.core.errors import render_error_template
9
- from monopyly.database import SQLAlchemy, register_db_cli_commands
7
+ from .core.errors import render_error_template
8
+ from .database import SQLAlchemy
10
9
 
11
10
 
12
- def create_app(test_config=None, debug=None):
13
- # Create and configure the app
14
- app = Flask(__name__, instance_relative_config=True)
15
-
16
- # Prepare the app configuration
17
- if test_config:
18
- config = test_config
19
- else:
20
- # Load the development/production config when not testing
21
- if app.debug or debug:
22
- config = DevelopmentConfig.configure_for_instance(app.instance_path)
23
- else:
24
- config = ProductionConfig.configure_for_instance(app.instance_path)
25
- app.config.from_object(config)
26
-
27
- # Initialize the app, including CLI commands and blueprints
28
- init_app(app)
29
- return app
30
-
11
+ @Factory(db_interface=SQLAlchemy)
12
+ def create_app(config=None):
13
+ """
14
+ Create the Flask application.
31
15
 
32
- @SQLAlchemy.interface_selector
33
- def init_app(app):
34
- """Initialize the app."""
16
+ Create the Flask app, including configurations as specified. This
17
+ will configure the app using the configuration objects made
18
+ available by the Monopyly application and initialize the app
19
+ by registering app blueprints, routes, and commands.
20
+ """
21
+ # Create and configure the app
22
+ app = DryFlask(__name__, app_name="Monopyly")
23
+ app.configure(config)
24
+ # Register blueprints and error handlers specific to this app
35
25
  register_blueprints(app)
36
26
  register_errorhandlers(app)
37
- register_db_cli_commands(app)
27
+ return app
38
28
 
39
29
 
40
30
  def register_blueprints(app):
@@ -76,3 +66,8 @@ def register_errorhandlers(app):
76
66
  ]
77
67
  for code in handled_error_codes:
78
68
  app.register_error_handler(code, render_error_template)
69
+
70
+
71
+ def main():
72
+ """The entry point to the Monopyly application."""
73
+ interact(__name__)
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.5.1'
16
- __version_tuple__ = version_tuple = (1, 5, 1)
15
+ __version__ = version = '1.5.2'
16
+ __version_tuple__ = version_tuple = (1, 5, 2)
@@ -2,6 +2,7 @@
2
2
  Routes for site authentication.
3
3
  """
4
4
 
5
+ from dry_foundation.database import db_transaction
5
6
  from flask import (
6
7
  current_app,
7
8
  flash,
@@ -12,7 +13,6 @@ from flask import (
12
13
  session,
13
14
  url_for,
14
15
  )
15
- from fuisce.database import db_transaction
16
16
  from sqlalchemy import select
17
17
  from werkzeug.security import check_password_hash, generate_password_hash
18
18
 
@@ -3,7 +3,7 @@ Tools for interacting with bank accounts in the database.
3
3
  """
4
4
 
5
5
  import sqlalchemy.sql.functions as sql_func
6
- from authanor.database.handler import DatabaseViewHandler
6
+ from dry_foundation.database.handler import DatabaseViewHandler
7
7
  from werkzeug.exceptions import abort
8
8
 
9
9
  from ..common.forms.utils import execute_on_form_validation
@@ -229,10 +229,10 @@ class BankAccountHandler(
229
229
  return account
230
230
 
231
231
  @classmethod
232
- def _filter_entries(cls, query, criteria):
232
+ def _filter_entries(cls, query, criteria, offset, limit):
233
233
  # Add a join to enable filtering by bank account type
234
234
  query = query.join(BankAccountTypeView)
235
- return super()._filter_entries(query, criteria)
235
+ return super()._filter_entries(query, criteria, offset, limit)
236
236
 
237
237
  @classmethod
238
238
  def delete_entry(cls, entry_id):
@@ -2,7 +2,7 @@
2
2
  Tools for interacting with banks in the database.
3
3
  """
4
4
 
5
- from authanor.database.handler import DatabaseHandler
5
+ from dry_foundation.database.handler import DatabaseHandler
6
6
 
7
7
  from ..database.models import Bank
8
8
 
@@ -2,8 +2,8 @@
2
2
  Routes for banking financials.
3
3
  """
4
4
 
5
+ from dry_foundation.database import db_transaction
5
6
  from flask import jsonify, redirect, render_template, request, url_for
6
- from fuisce.database import db_transaction
7
7
 
8
8
  from ..auth.tools import login_required
9
9
  from ..common.forms.utils import extend_field_list_for_ajax
@@ -2,7 +2,7 @@
2
2
  Tools for interacting with the bank transactions in the database.
3
3
  """
4
4
 
5
- from authanor.database.handler import DatabaseViewHandler
5
+ from dry_foundation.database.handler import DatabaseViewHandler
6
6
 
7
7
  from ..common.forms.utils import execute_on_form_validation
8
8
  from ..common.transactions import TransactionHandler, TransactionTagHandler
@@ -36,7 +36,9 @@ class BankTransactionHandler(
36
36
 
37
37
  @classmethod
38
38
  @DatabaseViewHandler.view_query
39
- def get_transactions(cls, account_ids=None, active=None, sort_order="DESC"):
39
+ def get_transactions(
40
+ cls, account_ids=None, active=None, sort_order="DESC", offset=None, limit=None
41
+ ):
40
42
  """
41
43
  Get bank transactions from the database.
42
44
 
@@ -61,6 +63,13 @@ class BankTransactionHandler(
61
63
  An indicator of whether the transactions should be ordered
62
64
  in ascending (oldest at top) or descending (newest at top)
63
65
  order. The default is descending order.
66
+ offset : int, optional
67
+ The number of transactions by which to offset the results
68
+ returned by this query. The default is `None`, in which case
69
+ no offset will be added.
70
+ limit : int, optional
71
+ A limit on the number of transactions retrieved from the
72
+ database.
64
73
 
65
74
  Returns
66
75
  -------
@@ -71,7 +80,7 @@ class BankTransactionHandler(
71
80
  criteria.add_match_filter(cls.model, "account_id", account_ids)
72
81
  criteria.add_match_filter(BankAccountView, "active", active)
73
82
  transactions = super()._get_transactions(
74
- criteria=criteria, sort_order=sort_order
83
+ criteria=criteria, sort_order=sort_order, offset=None, limit=None
75
84
  )
76
85
  return transactions
77
86
 
@@ -150,7 +159,7 @@ class BankTagHandler(TransactionTagHandler, model=TransactionTagHandler.model):
150
159
  return tags
151
160
 
152
161
  @classmethod
153
- def _filter_entries(cls, query, criteria):
162
+ def _filter_entries(cls, query, criteria, offset, limit):
154
163
  # Add a join to enable filtering by transaction ID or subtransaction ID
155
164
  join_transaction = BankTransactionView in criteria.discriminators
156
165
  join_subtransaction = (
@@ -160,7 +169,7 @@ class BankTagHandler(TransactionTagHandler, model=TransactionTagHandler.model):
160
169
  query = query.join(bank_tag_link_table).join(BankSubtransaction)
161
170
  if join_transaction:
162
171
  query = query.join(BankTransactionView)
163
- return super()._filter_entries(query, criteria)
172
+ return super()._filter_entries(query, criteria, offset, limit)
164
173
 
165
174
 
166
175
  @execute_on_form_validation
@@ -4,7 +4,7 @@ Tools for building a common transaction interface.
4
4
 
5
5
  from abc import abstractmethod
6
6
 
7
- from authanor.database.handler import DatabaseHandler, DatabaseViewHandler
7
+ from dry_foundation.database.handler import DatabaseHandler, DatabaseViewHandler
8
8
  from flask import current_app
9
9
 
10
10
  from ..database.models import (
@@ -31,17 +31,27 @@ class TransactionHandler(DatabaseViewHandler):
31
31
  """
32
32
 
33
33
  @classmethod
34
- def _customize_entries_query(cls, query, criteria, column_orders):
34
+ def _customize_entries_query(
35
+ cls, query, criteria, column_orders, offset=None, limit=None
36
+ ):
35
37
  # Group transactions and order by transaction date
36
38
  query = query.group_by(cls.model.id)
37
- return super()._customize_entries_query(query, criteria, column_orders)
39
+ return super()._customize_entries_query(
40
+ query, criteria, column_orders, offset=offset, limit=limit
41
+ )
38
42
 
39
43
  @classmethod
40
- def _get_transactions(cls, criteria=None, sort_order="DESC"):
44
+ def _get_transactions(
45
+ cls, criteria=None, sort_order="DESC", offset=None, limit=None
46
+ ):
41
47
  # Specify transaction order
42
48
  column_orders = {cls.model.transaction_date: sort_order}
43
49
  entries = cls.get_entries(
44
- entry_ids=None, criteria=criteria, column_orders=column_orders
50
+ entry_ids=None,
51
+ criteria=criteria,
52
+ column_orders=column_orders,
53
+ offset=offset,
54
+ limit=limit,
45
55
  )
46
56
  return entries
47
57
 
@@ -232,10 +242,10 @@ class TransactionTagHandler(DatabaseHandler, model=TransactionTag):
232
242
  return tags
233
243
 
234
244
  @classmethod
235
- def _filter_entries(cls, query, criteria):
245
+ def _filter_entries(cls, query, criteria, offset, limit):
236
246
  # Only get distinct tag entries
237
247
  query = query.distinct()
238
- return super()._filter_entries(query, criteria)
248
+ return super()._filter_entries(query, criteria, offset, limit)
239
249
 
240
250
  @classmethod
241
251
  def get_subtags(cls, tag):
@@ -1,15 +1,8 @@
1
1
  """Module describing logical core actions (to be used in routes)."""
2
2
 
3
- from datetime import datetime
4
-
5
3
  import markdown
6
4
 
7
5
 
8
- def get_timestamp():
9
- """Get a timestamp for backup filenames."""
10
- return datetime.now().strftime("%Y%m%d_%H%M%S")
11
-
12
-
13
6
  class MarkdownConverter:
14
7
 
15
8
  replacements = {
@@ -2,7 +2,7 @@
2
2
  Tools for interacting with credit accounts in the database.
3
3
  """
4
4
 
5
- from authanor.database.handler import DatabaseHandler
5
+ from dry_foundation.database.handler import DatabaseHandler
6
6
 
7
7
  from ..database.models import CreditAccount
8
8
 
@@ -2,7 +2,7 @@
2
2
  Tools for interacting with credit cards in the database.
3
3
  """
4
4
 
5
- from authanor.database.handler import DatabaseHandler
5
+ from dry_foundation.database.handler import DatabaseHandler
6
6
 
7
7
  from ..common.forms.utils import execute_on_form_validation
8
8
  from ..database.models import Bank, CreditAccount, CreditCard
@@ -99,8 +99,12 @@ class CreditCardHandler(DatabaseHandler, model=CreditCard):
99
99
  return card
100
100
 
101
101
  @classmethod
102
- def _customize_entries_query(cls, query, filters, sort_order):
103
- query = super()._customize_entries_query(query, filters, sort_order)
102
+ def _customize_entries_query(
103
+ cls, query, filters, sort_order, offset=None, limit=None
104
+ ):
105
+ query = super()._customize_entries_query(
106
+ query, filters, sort_order, offset=offset, limit=limit
107
+ )
104
108
  # Order cards by active status (active cards first)
105
109
  query = query.order_by(cls.model.active.desc())
106
110
  return query
@@ -4,6 +4,7 @@ Routes for credit card financials.
4
4
 
5
5
  from itertools import islice
6
6
 
7
+ from dry_foundation.database import db_transaction
7
8
  from flask import (
8
9
  flash,
9
10
  g,
@@ -14,7 +15,6 @@ from flask import (
14
15
  session,
15
16
  url_for,
16
17
  )
17
- from fuisce.database import db_transaction
18
18
  from sqlalchemy.exc import MultipleResultsFound
19
19
  from werkzeug.exceptions import abort
20
20
  from wtforms.validators import ValidationError
@@ -321,6 +321,7 @@ def clear_reconciliation_info():
321
321
  "credit.update_transaction",
322
322
  "credit.infer_statement",
323
323
  "credit.suggest_transaction_autocomplete",
324
+ "credit.add_subtransaction_fields",
324
325
  "credit.delete_transaction",
325
326
  "static",
326
327
  None,
@@ -344,15 +345,33 @@ def load_transactions(card_id):
344
345
  # Get all of the user's transactions for the selected cards
345
346
  sort_order = "DESC"
346
347
  transactions = CreditTransactionHandler.get_transactions(
347
- card_ids=selected_card_ids,
348
- sort_order=sort_order,
348
+ card_ids=selected_card_ids, sort_order=sort_order, limit=100
349
349
  )
350
350
  return render_template(
351
351
  "credit/transactions_page.html",
352
352
  filter_cards=cards,
353
353
  selected_card_ids=selected_card_ids,
354
354
  sort_order=sort_order,
355
- transactions=islice(transactions, 100),
355
+ transactions=transactions,
356
+ )
357
+
358
+
359
+ @bp.route("/_update_transactions_display", methods=("POST",))
360
+ @login_required
361
+ def update_transactions_display():
362
+ # Separate the arguments of the POST method
363
+ post_args = request.get_json()
364
+ card_ids = map(int, post_args["card_ids"])
365
+ sort_order = "ASC" if post_args["sort_order"] == "asc" else "DESC"
366
+ # Filter selected transactions from the database
367
+ transactions = CreditTransactionHandler.get_transactions(
368
+ card_ids=card_ids, sort_order=sort_order, limit=100
369
+ )
370
+ return render_template(
371
+ "credit/transactions_table/transactions.html",
372
+ sort_order=sort_order,
373
+ transactions=transactions,
374
+ full_view=True,
356
375
  )
357
376
 
358
377
 
@@ -385,26 +404,6 @@ def show_linked_transaction():
385
404
  )
386
405
 
387
406
 
388
- @bp.route("/_update_transactions_display", methods=("POST",))
389
- @login_required
390
- def update_transactions_display():
391
- # Separate the arguments of the POST method
392
- post_args = request.get_json()
393
- card_ids = map(int, post_args["card_ids"])
394
- sort_order = "ASC" if post_args["sort_order"] == "asc" else "DESC"
395
- # Filter selected transactions from the database
396
- transactions = CreditTransactionHandler.get_transactions(
397
- card_ids=card_ids,
398
- sort_order=sort_order,
399
- )
400
- return render_template(
401
- "credit/transactions_table/transactions.html",
402
- sort_order=sort_order,
403
- transactions=islice(transactions, 100),
404
- full_view=True,
405
- )
406
-
407
-
408
407
  @bp.route(
409
408
  "/add_transaction",
410
409
  defaults={"card_id": None, "statement_id": None},
@@ -497,10 +496,14 @@ def add_subtransaction_fields():
497
496
  @db_transaction
498
497
  def delete_transaction(transaction_id):
499
498
  CreditTransactionHandler.delete_entry(transaction_id)
500
- if statement_id := session.pop("statement_focus", None):
501
- return redirect(
502
- url_for("credit.load_statement_details", statement_id=statement_id)
503
- )
499
+ if (statement_id := session.pop("statement_focus", None)) is not None:
500
+ statement = CreditStatementHandler.get_entry(statement_id)
501
+ # Delete the statement if it has no more transactions
502
+ if statement.balance is not None:
503
+ return redirect(
504
+ url_for("credit.load_statement_details", statement_id=statement.id)
505
+ )
506
+ CreditStatementHandler.delete_entry(statement.id)
504
507
  return redirect(url_for("credit.load_transactions"))
505
508
 
506
509
 
@@ -2,8 +2,8 @@
2
2
  Tools for interacting with the credit statements in the database.
3
3
  """
4
4
 
5
- from authanor.database.handler import DatabaseViewHandler
6
5
  from dateutil.relativedelta import relativedelta
6
+ from dry_foundation.database.handler import DatabaseViewHandler
7
7
 
8
8
  from ..common.utils import get_next_occurrence_of_day
9
9
  from ..database.models import (
@@ -2,7 +2,7 @@
2
2
  Tools for interacting with the credit transactions in the database.
3
3
  """
4
4
 
5
- from authanor.database.handler import DatabaseHandler, DatabaseViewHandler
5
+ from dry_foundation.database.handler import DatabaseHandler, DatabaseViewHandler
6
6
 
7
7
  from ...common.forms.utils import execute_on_form_validation
8
8
  from ...common.transactions import TransactionHandler, TransactionTagHandler
@@ -38,7 +38,13 @@ class CreditTransactionHandler(
38
38
  @classmethod
39
39
  @DatabaseViewHandler.view_query
40
40
  def get_transactions(
41
- cls, statement_ids=None, card_ids=None, active=None, sort_order="DESC"
41
+ cls,
42
+ statement_ids=None,
43
+ card_ids=None,
44
+ active=None,
45
+ sort_order="DESC",
46
+ offset=None,
47
+ limit=None,
42
48
  ):
43
49
  """
44
50
  Get credit card transactions from the database.
@@ -66,6 +72,13 @@ class CreditTransactionHandler(
66
72
  An indicator of whether the transactions should be ordered
67
73
  in ascending (oldest at top) or descending (newest at top)
68
74
  order.
75
+ offset : int, optional
76
+ The number of transactions by which to offset the results
77
+ returned by this query. The default is `None`, in which case
78
+ no offset will be added.
79
+ limit : int, optional
80
+ A limit on the number of transactions retrieved from the
81
+ database.
69
82
 
70
83
  Returns
71
84
  -------
@@ -77,7 +90,7 @@ class CreditTransactionHandler(
77
90
  criteria.add_match_filter(CreditCard, "id", card_ids)
78
91
  criteria.add_match_filter(CreditCard, "active", active)
79
92
  transactions = super()._get_transactions(
80
- criteria=criteria, sort_order=sort_order
93
+ criteria=criteria, sort_order=sort_order, offset=offset, limit=limit
81
94
  )
82
95
  return transactions
83
96
 
@@ -196,7 +209,7 @@ class CreditTagHandler(TransactionTagHandler, model=TransactionTagHandler.model)
196
209
  return tags
197
210
 
198
211
  @classmethod
199
- def _filter_entries(cls, query, criteria):
212
+ def _filter_entries(cls, query, criteria, offset, limit):
200
213
  # Add a join to enable filtering by transaction ID or subtransaction ID
201
214
  join_transaction = CreditTransactionView in criteria.discriminators
202
215
  join_subtransaction = (
@@ -206,7 +219,7 @@ class CreditTagHandler(TransactionTagHandler, model=TransactionTagHandler.model)
206
219
  query = query.join(credit_tag_link_table).join(CreditSubtransaction)
207
220
  if join_transaction:
208
221
  query = query.join(CreditTransactionView)
209
- return super()._filter_entries(query, criteria)
222
+ return super()._filter_entries(query, criteria, offset, limit)
210
223
 
211
224
 
212
225
  @execute_on_form_validation
@@ -222,7 +222,26 @@ class _Matchmaker(ABC):
222
222
  field : str
223
223
  The string of text to be tokenized.
224
224
  """
225
- return set(wordpunct_tokenize(field.replace("'", "").casefold()))
225
+ replacements = [
226
+ # Remove disruptive punctuation
227
+ ("1-800", "1800"),
228
+ ("-", " "),
229
+ (".", " "),
230
+ (",", " "),
231
+ ("(", " "),
232
+ (")", " "),
233
+ ("'", ""),
234
+ # Standardize characters
235
+ ("&", "and"),
236
+ ("é", "e"),
237
+ ]
238
+ for original, replacement in replacements:
239
+ field = field.replace(original, replacement)
240
+ tokens = set(wordpunct_tokenize(field.replace("'", "").casefold()))
241
+ removals = ["and", "the", "of"]
242
+ for word in removals:
243
+ tokens.discard(word)
244
+ return tokens
226
245
 
227
246
  def _compute_transaction_activity_similarity_score(
228
247
  self, merchant_tokens, notes_tokens, activity_tokens
@@ -0,0 +1,46 @@
1
+ """
2
+ Expose commonly used database functionality to the rest of the package.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ from dry_foundation.database import SQLAlchemy as _SQLAlchemy
8
+ from flask import current_app
9
+
10
+
11
+ class SQLAlchemy(_SQLAlchemy):
12
+ """Store an interface to SQLAlchemy database objects."""
13
+
14
+ def initialize(self, app):
15
+ """
16
+ Initialize the database.
17
+
18
+ Initialize the database by executing the SQL schema to clear
19
+ existing data and create new tables.
20
+
21
+ Parameters
22
+ ----------
23
+ app : flask.Flask
24
+ The app object, which may pass initialization parameters via
25
+ its configuration.
26
+ """
27
+ with app.app_context():
28
+ # Establish a raw connection in order to execute the complete files
29
+ raw_conn = self.engine.raw_connection()
30
+ # Load the tables, table views, and preloaded data
31
+ sql_dir = Path(__file__).parent
32
+ sql_filepaths = [
33
+ sql_dir / path for path in ("schema.sql", "views.sql", "preloads.sql")
34
+ ]
35
+ auxiliary_preload_path = app.config.get("PRELOAD_DATA_PATH")
36
+ if auxiliary_preload_path:
37
+ sql_filepaths.append(auxiliary_preload_path)
38
+ for sql_filepath in sql_filepaths:
39
+ with current_app.open_resource(sql_filepath) as sql_file:
40
+ raw_conn.executescript(sql_file.read().decode("utf8"))
41
+ raw_conn.close()
42
+ # Top level initialization does not overwrite tables, so it goes at the end
43
+ super().initialize(app)
44
+
45
+
46
+ SQLAlchemy.create_default_interface(echo_engine=False)