monopyly 1.7.2__tar.gz → 1.7.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 (244) hide show
  1. {monopyly-1.7.2 → monopyly-1.7.3}/PKG-INFO +7 -7
  2. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/CHANGELOG.md +7 -0
  3. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/_version.py +2 -2
  4. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/forms.py +13 -10
  5. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/common/forms/_forms.py +3 -3
  6. monopyly-1.7.3/monopyly/common/forms/fields.py +47 -0
  7. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/common/utils.py +1 -2
  8. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/forms.py +12 -12
  9. {monopyly-1.7.2 → monopyly-1.7.3}/pyproject.toml +6 -6
  10. monopyly-1.7.2/monopyly/common/forms/fields.py +0 -101
  11. monopyly-1.7.2/monopyly/common/forms/validators.py +0 -51
  12. {monopyly-1.7.2 → monopyly-1.7.3}/.gitignore +0 -0
  13. {monopyly-1.7.2 → monopyly-1.7.3}/COPYING +0 -0
  14. {monopyly-1.7.2 → monopyly-1.7.3}/LICENSE +0 -0
  15. {monopyly-1.7.2 → monopyly-1.7.3}/README.md +0 -0
  16. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/README.md +0 -0
  17. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/__init__.py +0 -0
  18. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/analytics/actions.py +0 -0
  19. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/analytics/blueprint.py +0 -0
  20. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/analytics/routes.py +0 -0
  21. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/analytics/tools.py +0 -0
  22. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/auth/actions.py +0 -0
  23. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/auth/blueprint.py +0 -0
  24. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/auth/routes.py +0 -0
  25. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/auth/tools.py +0 -0
  26. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/accounts.py +0 -0
  27. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/actions.py +0 -0
  28. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/banks.py +0 -0
  29. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/blueprint.py +0 -0
  30. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/filters.py +0 -0
  31. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/routes.py +0 -0
  32. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/banking/transactions.py +0 -0
  33. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/common/forms/__init__.py +0 -0
  34. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/common/forms/utils.py +0 -0
  35. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/common/transactions.py +0 -0
  36. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/actions.py +0 -0
  37. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/blueprint.py +0 -0
  38. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/context_processors.py +0 -0
  39. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/errors.py +0 -0
  40. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/filters.py +0 -0
  41. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/internal_transactions.py +0 -0
  42. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/core/routes.py +0 -0
  43. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/accounts.py +0 -0
  44. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/actions.py +0 -0
  45. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/blueprint.py +0 -0
  46. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/cards.py +0 -0
  47. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/routes.py +0 -0
  48. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/statements.py +0 -0
  49. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/transactions/__init__.py +0 -0
  50. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/transactions/_transactions.py +0 -0
  51. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/transactions/activity/__init__.py +0 -0
  52. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/transactions/activity/data.py +0 -0
  53. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/transactions/activity/parser.py +0 -0
  54. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/credit/transactions/activity/reconciliation.py +0 -0
  55. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/database/__init__.py +0 -0
  56. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/database/models.py +0 -0
  57. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/database/preloads.sql +0 -0
  58. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/database/schema.sql +0 -0
  59. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/database/views.sql +0 -0
  60. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/scripts/screenshot_application.py +0 -0
  61. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/chartist-1.5.0.min.js +0 -0
  62. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/css/style.css +0 -0
  63. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/browserconfig.xml +0 -0
  64. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-114.png +0 -0
  65. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-120.png +0 -0
  66. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-144.png +0 -0
  67. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-150.png +0 -0
  68. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-152.png +0 -0
  69. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-16.png +0 -0
  70. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-160.png +0 -0
  71. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-180.png +0 -0
  72. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-192.png +0 -0
  73. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-310.png +0 -0
  74. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-32.png +0 -0
  75. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-57.png +0 -0
  76. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-60.png +0 -0
  77. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-64.png +0 -0
  78. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-70.png +0 -0
  79. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-72.png +0 -0
  80. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-76.png +0 -0
  81. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon-96.png +0 -0
  82. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon.ico +0 -0
  83. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/favicon/favicon.png +0 -0
  84. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/bank-account-details.png +0 -0
  85. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/bank-account-summaries.png +0 -0
  86. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/bank-accounts.png +0 -0
  87. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/credit-account-details.png +0 -0
  88. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/credit-statement-details.png +0 -0
  89. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/credit-transactions.png +0 -0
  90. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/homepage-user.png +0 -0
  91. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/homepage.png +0 -0
  92. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/about/statement-details.png +0 -0
  93. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/cards/chase-card.png +0 -0
  94. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/cards/discover-card.png +0 -0
  95. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/cards/new-card.png +0 -0
  96. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/cards/template-card.png +0 -0
  97. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/arrow-down.png +0 -0
  98. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/arrow-left.png +0 -0
  99. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/arrow-up.png +0 -0
  100. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/checkmark.png +0 -0
  101. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
  102. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/delete-orange.png +0 -0
  103. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/delete-thick.png +0 -0
  104. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/delete.png +0 -0
  105. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/edit.png +0 -0
  106. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/link.png +0 -0
  107. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/minus-thick.png +0 -0
  108. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/minus.png +0 -0
  109. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/plus-thick.png +0 -0
  110. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/plus.png +0 -0
  111. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/refresh.png +0 -0
  112. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/save.png +0 -0
  113. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/sort-asc.png +0 -0
  114. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/sort-desc.png +0 -0
  115. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/statement-pair.png +0 -0
  116. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/statement-thick.png +0 -0
  117. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/statement.png +0 -0
  118. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/icons/x-thick.png +0 -0
  119. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/img/statement.png +0 -0
  120. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/jquery-3.7.1.min.js +0 -0
  121. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/add-subtransaction.js +0 -0
  122. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/add-transfer.js +0 -0
  123. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/autocomplete-transaction.js +0 -0
  124. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/bind-tag-actions.js +0 -0
  125. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/create-balance-chart.js +0 -0
  126. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/create-category-chart.js +0 -0
  127. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/create-tag-stats-chart.js +0 -0
  128. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/define-filter.js +0 -0
  129. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
  130. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/display-new-bank-inputs.js +0 -0
  131. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
  132. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/expand-bank-account.js +0 -0
  133. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/expand-bank.js +0 -0
  134. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/expand-transaction.js +0 -0
  135. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/flip-card.js +0 -0
  136. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/hide-homepage-block.js +0 -0
  137. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/highlight-discrepant-transactions.js +0 -0
  138. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/infer-card.js +0 -0
  139. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/infer-statement.js +0 -0
  140. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/load-more-transactions.js +0 -0
  141. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/make-payment.js +0 -0
  142. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/expand-box-row.js +0 -0
  143. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/expand-transaction.js +0 -0
  144. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/form-suggestions.js +0 -0
  145. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
  146. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/manage-overlays.js +0 -0
  147. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/manage-subforms.js +0 -0
  148. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/update-database-widget.js +0 -0
  149. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/modules/update-display-request.js +0 -0
  150. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/show-credit-activity-loader.js +0 -0
  151. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/show-linked-transaction.js +0 -0
  152. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/toggle-navigation.js +0 -0
  153. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-account-statement-parameters.js +0 -0
  154. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-bank-name.js +0 -0
  155. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-card-status.js +0 -0
  156. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-statement-parameters.js +0 -0
  157. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-statements-display.js +0 -0
  158. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-tag-statistics.js +0 -0
  159. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/update-transactions-display.js +0 -0
  160. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/use-suggested-amount.js +0 -0
  161. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/static/js/use-suggested-merchant.js +0 -0
  162. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/analytics/net_worth.html +0 -0
  163. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/analytics/tag_statistics/tag_selection.html +0 -0
  164. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/analytics/tag_statistics/tag_statistics.html +0 -0
  165. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/analytics/tag_statistics/tag_statistics_page.html +0 -0
  166. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/auth/change_password.html +0 -0
  167. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/auth/login.html +0 -0
  168. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/auth/register.html +0 -0
  169. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/account_form/account_form.html +0 -0
  170. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
  171. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/account_page.html +0 -0
  172. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/account_summaries.html +0 -0
  173. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/account_summaries_page.html +0 -0
  174. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/account_summary.html +0 -0
  175. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/accounts_page.html +0 -0
  176. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
  177. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
  178. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
  179. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
  180. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
  181. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
  182. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
  183. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
  184. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transactions_table/table.html +0 -0
  185. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
  186. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/banking/transactions_table/transactions.html +0 -0
  187. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/form_page.html +0 -0
  188. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/tag_tree.html +0 -0
  189. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/tags_page.html +0 -0
  190. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transaction_form/subform.html +0 -0
  191. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
  192. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
  193. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +0 -0
  194. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
  195. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
  196. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
  197. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/table.html +0 -0
  198. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
  199. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
  200. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/common/transactions_table/transactions.html +0 -0
  201. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/credits.html +0 -0
  202. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/400.html +0 -0
  203. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/401.html +0 -0
  204. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/403.html +0 -0
  205. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/404.html +0 -0
  206. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/405.html +0 -0
  207. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/408.html +0 -0
  208. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/418.html +0 -0
  209. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/425.html +0 -0
  210. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/500.html +0 -0
  211. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/errors/error.html +0 -0
  212. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/index.html +0 -0
  213. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/profile.html +0 -0
  214. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/core/story.html +0 -0
  215. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/account_page.html +0 -0
  216. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/card_form/card_form.html +0 -0
  217. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
  218. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
  219. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
  220. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
  221. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/card_submission_page.html +0 -0
  222. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/cards.html +0 -0
  223. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/cards_page.html +0 -0
  224. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_page.html +0 -0
  225. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_reconciliation/discrepant_records.html +0 -0
  226. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +0 -0
  227. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +0 -0
  228. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_reconciliation/summary.html +0 -0
  229. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html +0 -0
  230. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statement_summary.html +0 -0
  231. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statements.html +0 -0
  232. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/statements_page.html +0 -0
  233. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
  234. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
  235. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
  236. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
  237. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transaction_submission_page.html +0 -0
  238. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transactions_page.html +0 -0
  239. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transactions_table/condensed_row_content.html +0 -0
  240. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transactions_table/expanded_row_content.html +0 -0
  241. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transactions_table/table.html +0 -0
  242. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
  243. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/credit/transactions_table/transactions.html +0 -0
  244. {monopyly-1.7.2 → monopyly-1.7.3}/monopyly/templates/layout.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: monopyly
3
- Version: 1.7.2
3
+ Version: 1.7.3
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
@@ -22,16 +22,16 @@ Classifier: Programming Language :: Python
22
22
  Classifier: Topic :: Office/Business :: Financial
23
23
  Classifier: Topic :: Office/Business :: Financial :: Accounting
24
24
  Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
25
- Requires-Python: >=3.10
26
- Requires-Dist: dry-foundation==2.0.0
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: dry-foundation==2.2.0
27
27
  Requires-Dist: flask-wtf==1.3.0
28
28
  Requires-Dist: flask==3.1.3
29
29
  Requires-Dist: gunicorn==26.2.0
30
- Requires-Dist: markdown==3.10.3
31
- Requires-Dist: nltk==3.10.3
32
- Requires-Dist: python-dateutil==2.9.0.post0
30
+ Requires-Dist: markdown==3.11.2
31
+ Requires-Dist: nltk==3.10.4
32
+ Requires-Dist: python-dateutil==3.0.0
33
33
  Requires-Dist: rich==15.0.0
34
- Requires-Dist: sqlalchemy==2.0.52
34
+ Requires-Dist: sqlalchemy==2.1.6
35
35
  Description-Content-Type: text/markdown
36
36
 
37
37
  <div id="header">
@@ -280,4 +280,11 @@
280
280
  - Prevent summation of statement balances returning `None` (continued)
281
281
 
282
282
 
283
+ ### 1.7.3
284
+
285
+ - Update timezone handling via datetime `tzinfo`
286
+ - Rely on foundations package for (universal) form capabilities
287
+ - Bump dependencies and minimum Python version (3.11)
288
+
289
+
283
290
  <a name="bottom" id="bottom"></a>
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.7.2'
32
- __version_tuple__ = version_tuple = (1, 7, 2)
31
+ __version__ = version = '1.7.3'
32
+ __version_tuple__ = version_tuple = (1, 7, 3)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -2,15 +2,12 @@
2
2
  Generate banking forms for the user to complete.
3
3
  """
4
4
 
5
+ from dry_foundation.forms.fields import CustomDatabaseSelectField
5
6
  from wtforms.fields import BooleanField, FieldList, FormField, SubmitField
6
7
  from wtforms.validators import DataRequired, Optional
7
8
 
8
9
  from ..common.forms import AcquisitionSubform, EntryForm, EntrySubform, TransactionForm
9
- from ..common.forms.fields import (
10
- CustomChoiceSelectField,
11
- LastFourDigitsField,
12
- StringField,
13
- )
10
+ from ..common.forms.fields import LastFourDigitsField, StringField
14
11
  from ..common.forms.utils import Autocompleter
15
12
  from ..database.models import (
16
13
  Bank,
@@ -25,7 +22,7 @@ from .accounts import BankAccountRepository, BankAccountTypeRepository
25
22
  from .banks import BankRepository
26
23
 
27
24
 
28
- class BankSelectField(CustomChoiceSelectField):
25
+ class BankSelectField(CustomDatabaseSelectField):
29
26
  """Bank field that uses the database to prepare field choices."""
30
27
 
31
28
  _db_repo = BankRepository
@@ -38,8 +35,11 @@ class BankSelectField(CustomChoiceSelectField):
38
35
  display_name = bank.bank_name
39
36
  return display_name
40
37
 
38
+ def _format_new_choice(self):
39
+ return f"New {self.label.text.lower()}"
40
+
41
41
 
42
- class BankAccountTypeSelectField(CustomChoiceSelectField):
42
+ class BankAccountTypeSelectField(CustomDatabaseSelectField):
43
43
  """Account type field that uses the database to prepare field choices."""
44
44
 
45
45
  _db_repo = BankAccountTypeRepository
@@ -55,6 +55,9 @@ class BankAccountTypeSelectField(CustomChoiceSelectField):
55
55
  display_name += f" ({account_type.type_common_name})"
56
56
  return display_name
57
57
 
58
+ def _format_new_choice(self):
59
+ return f"New {self.label.text.lower()}"
60
+
58
61
 
59
62
  class BankSubform(AcquisitionSubform):
60
63
  """Form to input/edit bank identification."""
@@ -83,7 +86,7 @@ class BankSubform(AcquisitionSubform):
83
86
  """Gather data for the form from the given database entry."""
84
87
  if isinstance(entry, Bank):
85
88
  data = {
86
- "bank_id": entry.id,
89
+ "bank_id": str(entry.id),
87
90
  "bank_name": entry.bank_name,
88
91
  }
89
92
  else:
@@ -135,8 +138,8 @@ class BankAccountForm(EntryForm):
135
138
  bank = self.bank_info.get_bank()
136
139
  account_type = self.account_type_info.get_account_type()
137
140
  account_data = {
138
- "bank_id": bank.id,
139
- "account_type_id": account_type.id,
141
+ "bank_id": str(bank.id),
142
+ "account_type_id": str(account_type.id),
140
143
  "last_four_digits": self["last_four_digits"].data,
141
144
  "active": self["active"].data,
142
145
  }
@@ -6,6 +6,7 @@ from abc import abstractmethod
6
6
  from datetime import date
7
7
 
8
8
  from dry_foundation.forms import AbstractFlaskForm, FlaskSubform
9
+ from dry_foundation.forms.fields import CustomDatabaseSelectField
9
10
  from wtforms.fields import StringField, SubmitField
10
11
  from wtforms.validators import DataRequired
11
12
 
@@ -102,15 +103,14 @@ class AcquisitionSubform(EntrySubform):
102
103
  def _produce_entry_from_field(self, form_entry_id_field_name):
103
104
  """Produce an entry matching an ID field in the subform data."""
104
105
  form_entry_id_field = getattr(self, form_entry_id_field_name)
105
- form_entry_id = int(form_entry_id_field.data)
106
106
  # If the entry is a new entry, create it from the form data
107
- if form_entry_id == 0:
107
+ if form_entry_id_field.data == CustomDatabaseSelectField.NEW:
108
108
  data = self._prepare_mapping()
109
109
  entry = self._db_repo.add_entry(**data)
110
110
  # Set the form field to match the newly added value
111
111
  form_entry_id_field.data = entry.id
112
112
  else:
113
- entry = self._db_repo.get_entry(form_entry_id)
113
+ entry = self._db_repo.get_entry(int(form_entry_id_field.data))
114
114
  return entry
115
115
 
116
116
  @abstractmethod
@@ -0,0 +1,47 @@
1
+ """
2
+ General form constructions.
3
+ """
4
+
5
+ from dry_foundation.forms.fields import StrippedFieldMixin
6
+ from dry_foundation.forms.validators import NumeralsOnly
7
+ from wtforms import fields as wtforms_fields
8
+ from wtforms.validators import Length
9
+ from wtforms.widgets import NumberInput
10
+
11
+ from ..utils import parse_date
12
+
13
+
14
+ class DateField(wtforms_fields.DateField):
15
+ """A date field with better parsing abilities than the WTForms default."""
16
+
17
+ def __init__(self, *args, filters=(), **kwargs):
18
+ # The date field should normally handle date conversion, but this
19
+ # filter remains as a backup for browsers not supporting locales
20
+ if parse_date not in filters:
21
+ filters = list(filters)
22
+ filters.append(parse_date)
23
+ super().__init__(*args, filters=filters, **kwargs)
24
+
25
+
26
+ class CurrencyField(wtforms_fields.DecimalField):
27
+ """A decimal field with currency-specific customizations."""
28
+
29
+ widget = NumberInput(step=0.01)
30
+
31
+ def __init__(self, *args, filters=(), **kwargs):
32
+ filters = list(filters)
33
+ filters.append(lambda x: float(round(x, 2)) if x else None)
34
+ super().__init__(*args, filters=filters, places=2, **kwargs)
35
+
36
+
37
+ class StringField(StrippedFieldMixin, wtforms_fields.StringField):
38
+ """A custom string field."""
39
+
40
+
41
+ class LastFourDigitsField(StringField):
42
+ """A custom field for collecting the last four digits of cards/accounts."""
43
+
44
+ def __init__(self, *args, validators=(), **kwargs):
45
+ validators = list(validators)
46
+ validators.extend([Length(4, 4), NumeralsOnly()])
47
+ super().__init__(*args, validators=validators, **kwargs)
@@ -4,7 +4,6 @@ General utility objects.
4
4
 
5
5
  import datetime
6
6
  from collections import Counter
7
- from datetime import timezone
8
7
 
9
8
  from dateutil.relativedelta import relativedelta
10
9
 
@@ -128,7 +127,7 @@ def convert_date_to_midnight_timestamp(date, milliseconds=False):
128
127
  The timestamp corresponding to the given date (at midnight UTC).
129
128
  """
130
129
  midnight = datetime.datetime.min.time()
131
- utc_datetime = datetime.datetime.combine(date, midnight, tzinfo=timezone.utc)
130
+ utc_datetime = datetime.datetime.combine(date, midnight, tzinfo=datetime.UTC)
132
131
  timestamp = int(utc_datetime.timestamp())
133
132
  if milliseconds:
134
133
  timestamp *= 1000
@@ -2,6 +2,7 @@
2
2
  Generate credit card forms for the user to complete.
3
3
  """
4
4
 
5
+ from dry_foundation.forms.fields import CustomDatabaseSelectField
5
6
  from flask import abort
6
7
  from wtforms.fields import (
7
8
  BooleanField,
@@ -16,11 +17,7 @@ from wtforms.validators import DataRequired, Optional
16
17
 
17
18
  from ..banking.forms import BankSubform
18
19
  from ..common.forms import AcquisitionSubform, EntryForm, EntrySubform, TransactionForm
19
- from ..common.forms.fields import (
20
- CustomChoiceSelectField,
21
- LastFourDigitsField,
22
- StringField,
23
- )
20
+ from ..common.forms.fields import LastFourDigitsField, StringField
24
21
  from ..common.forms.utils import Autocompleter
25
22
  from ..database.models import (
26
23
  Bank,
@@ -38,7 +35,7 @@ from .transactions import CreditTransactionRepository
38
35
  from .transactions.activity.reconciliation import ActivityMatchmaker
39
36
 
40
37
 
41
- class CreditAccountSelectField(CustomChoiceSelectField):
38
+ class CreditAccountSelectField(CustomDatabaseSelectField):
42
39
  """Account field that uses the database to prepare field choices."""
43
40
 
44
41
  _db_repo = CreditAccountRepository
@@ -55,6 +52,9 @@ class CreditAccountSelectField(CustomChoiceSelectField):
55
52
  display_name = f"{bank_name} (cards: {', '.join(digits)})"
56
53
  return display_name
57
54
 
55
+ def _format_new_choice(self):
56
+ return f"New {self.label.text.lower()}"
57
+
58
58
 
59
59
  class CreditCardForm(EntryForm):
60
60
  """Form to input/edit credit cards."""
@@ -76,13 +76,13 @@ class CreditCardForm(EntryForm):
76
76
 
77
77
  Notes
78
78
  -----
79
- Unset values on the ``CustomChoiceSelectField`` will take a
79
+ Unset values on the ``CustomDatabaseSelectField`` will take a
80
80
  default value that will be considered to be invalid. They
81
81
  must be replaced (after processing) with the inferred value.
82
82
  """
83
- if self.bank_info.bank_id.data == -1:
83
+ if self.bank_info.bank_id.data == self.bank_info.bank_id.UNSET:
84
84
  account = self.get_account()
85
- self.bank_info.bank_id.data = account.bank.id
85
+ self.bank_info.bank_id.data = str(account.bank.id)
86
86
  return super().validate(extra_validators=extra_validators)
87
87
 
88
88
  def get_account(self):
@@ -93,7 +93,7 @@ class CreditCardForm(EntryForm):
93
93
  bank = self.bank_info.get_bank()
94
94
  # Mapping must match format for `credit_accounts` database table
95
95
  account_data = {
96
- "bank_id": bank.id,
96
+ "bank_id": str(bank.id),
97
97
  "statement_issue_day": self.statement_issue_day.data,
98
98
  "statement_due_day": self.statement_due_day.data,
99
99
  }
@@ -103,7 +103,7 @@ class CreditCardForm(EntryForm):
103
103
  """Gather data for the form from the given database entry."""
104
104
  if isinstance(entry, CreditAccount):
105
105
  data = {
106
- "account_id": entry.id,
106
+ "account_id": str(entry.id),
107
107
  "statement_issue_day": entry.statement_issue_day,
108
108
  "statement_due_day": entry.statement_due_day,
109
109
  }
@@ -126,7 +126,7 @@ class CreditCardForm(EntryForm):
126
126
  """Produce a dictionary corresponding to a database card."""
127
127
  account = self.account_info.get_account()
128
128
  card_data = {
129
- "account_id": account.id,
129
+ "account_id": str(account.id),
130
130
  "last_four_digits": self.last_four_digits.data,
131
131
  "active": self.active.data,
132
132
  }
@@ -9,17 +9,17 @@ authors = [
9
9
  ]
10
10
  description = 'A homemade personal finance manager.'
11
11
  license = { text = 'GNU GPLv3' }
12
- requires-python = '>=3.10'
12
+ requires-python = '>=3.11'
13
13
  dependencies = [
14
- 'dry-foundation==2.0.0',
14
+ 'dry-foundation==2.2.0',
15
15
  'Flask==3.1.3',
16
16
  'Flask-WTF==1.3.0',
17
17
  'gunicorn==26.2.0',
18
- 'Markdown==3.10.3',
19
- 'nltk==3.10.3',
20
- 'python-dateutil==2.9.0.post0',
18
+ 'Markdown==3.11.2',
19
+ 'nltk==3.10.4',
20
+ 'python-dateutil==3.0.0',
21
21
  'rich==15.0.0',
22
- 'SQLAlchemy==2.0.52',
22
+ 'SQLAlchemy==2.1.6',
23
23
  ]
24
24
  keywords = [
25
25
  'Finance',
@@ -1,101 +0,0 @@
1
- """
2
- General form constructions.
3
- """
4
-
5
- from abc import ABC, abstractmethod
6
-
7
- from wtforms import fields as wtforms_fields
8
- from wtforms.validators import Length
9
- from wtforms.widgets import NumberInput
10
-
11
- from ..utils import parse_date
12
- from .validators import NumeralsOnly, SelectionNotBlank
13
-
14
-
15
- class DateField(wtforms_fields.DateField):
16
- """A date field with better parsing abilities than the WTForms default."""
17
-
18
- def __init__(self, *args, filters=(), **kwargs):
19
- # The date field should normally handle date conversion, but this
20
- # filter remains as a backup for browsers not supporting locales
21
- if parse_date not in filters:
22
- filters = list(filters)
23
- filters.append(parse_date)
24
- super().__init__(*args, filters=filters, **kwargs)
25
-
26
-
27
- class CurrencyField(wtforms_fields.DecimalField):
28
- """A decimal field with currency-specific customizations."""
29
-
30
- widget = NumberInput(step=0.01)
31
-
32
- def __init__(self, *args, filters=(), **kwargs):
33
- filters = list(filters)
34
- filters.append(lambda x: float(round(x, 2)) if x else None)
35
- super().__init__(*args, filters=filters, places=2, **kwargs)
36
-
37
-
38
- class StringField(wtforms_fields.StringField):
39
- """A custom string field."""
40
-
41
- def __init__(self, *args, filters=(), **kwargs):
42
- filters = list(filters)
43
- filters.append(lambda x: x.strip() if x else None)
44
- super().__init__(*args, filters=filters, **kwargs)
45
-
46
-
47
- class LastFourDigitsField(StringField):
48
- """A custom field for collecting the last four digits of cards/accounts."""
49
-
50
- def __init__(self, *args, validators=(), **kwargs):
51
- validators = list(validators)
52
- validators.extend([Length(4, 4), NumeralsOnly()])
53
- super().__init__(*args, validators=validators, **kwargs)
54
-
55
-
56
- class CustomChoiceSelectField(wtforms_fields.SelectField, ABC):
57
- """A select field that can auto-prepare choices."""
58
-
59
- def __init__(self, label=None, validators=None, coerce=int, **kwargs):
60
- if not validators:
61
- validators = [SelectionNotBlank()]
62
- super().__init__(label=label, validators=validators, coerce=int, **kwargs)
63
- self.prepare_field_choices()
64
-
65
- def process_data(self, value):
66
- """Process incoming data, and set the field default value."""
67
- value = -1 if value is None else value
68
- super().process_data(value)
69
-
70
- @property
71
- @abstractmethod
72
- def _db_repo(self):
73
- raise NotImplementedError("Define the attribute in a subclass.")
74
-
75
- def prepare_field_choices(self):
76
- """
77
- Prepare choices for a field in the form using the database.
78
-
79
- Using a reference to a database repository, this method queries the
80
- database for entries belonging to the user, and then uses those
81
- entries to populate the list of choices that may be selected
82
- for the field value.
83
- """
84
- # Collect all available user entries
85
- entries = self._db_repo.get_entries()
86
- # Set default choice values
87
- self.choices = [(-1, "-"), (0, f"New {self.label.text.lower()}")]
88
- # Set the user choices (for consistency, arbitrarily sort by entry ID)
89
- for entry in sorted(entries, key=lambda entry: entry.id):
90
- display_name = self._format_choice(entry)
91
- self.choices.insert(-1, (entry.id, display_name))
92
-
93
- @abstractmethod
94
- def _format_choice(self, entry):
95
- """
96
- Format a database entry field to display as a form field choice.
97
-
98
- Accepts an entry returned by the database repository, and extract a
99
- name to be displayed as a choice in the list of choices.
100
- """
101
- raise NotImplementedError("Define the formatting for a choice in a subclass.")
@@ -1,51 +0,0 @@
1
- """
2
- Commonly shared form validators.
3
- """
4
-
5
- from wtforms.validators import ValidationError
6
-
7
-
8
- class NumeralsOnly:
9
- """
10
- Validates text contains only numerals.
11
-
12
- Parameters
13
- ----------
14
- message : str
15
- Error message to raise in case of a validation error.
16
- """
17
-
18
- def __init__(self, message=None):
19
- if not message:
20
- message = "Field can only contain numerals."
21
- self.message = message
22
-
23
- def __call__(self, form, field):
24
- try:
25
- int(field.data)
26
- except ValueError:
27
- raise ValidationError(self.message)
28
-
29
-
30
- class SelectionNotBlank:
31
- """
32
- Validates that a selection is not a blank submission.
33
-
34
- Parameters
35
- ----------
36
- blank :
37
- The value representing a blank selection. The default is the
38
- integer `-1`.
39
- message : str
40
- Error message to raise in case of a validation error.
41
- """
42
-
43
- def __init__(self, blank=-1, message=None):
44
- self.blank = blank
45
- if not message:
46
- message = "A selection must be made."
47
- self.message = message
48
-
49
- def __call__(self, form, field):
50
- if field.data == self.blank:
51
- raise ValidationError(self.message)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes