monopyly 1.4.1__tar.gz → 1.4.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 (210) hide show
  1. {monopyly-1.4.1 → monopyly-1.4.3}/.gitignore +2 -1
  2. {monopyly-1.4.1 → monopyly-1.4.3}/PKG-INFO +6 -5
  3. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/README.md +2 -2
  4. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/__init__.py +0 -5
  5. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/_version.py +2 -2
  6. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/auth/routes.py +3 -1
  7. monopyly-1.4.3/monopyly/cli/apps.py +84 -0
  8. monopyly-1.4.3/monopyly/cli/run.py +122 -0
  9. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/config/default_settings.py +21 -0
  10. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/config/settings.py +12 -1
  11. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/core/routes.py +1 -0
  12. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/database/__init__.py +1 -1
  13. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/css/style.css +3 -1
  14. monopyly-1.4.3/monopyly/static/jquery-3.7.0.min.js +3 -0
  15. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/define-filter.js +2 -2
  16. monopyly-1.4.3/monopyly/static/js/expand-transaction.js +18 -0
  17. monopyly-1.4.3/monopyly/static/js/modules/expand-transaction.js +128 -0
  18. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/update-display-ajax.js +15 -8
  19. monopyly-1.4.3/monopyly/static/js/update-transactions-display.js +73 -0
  20. {monopyly-1.4.1 → monopyly-1.4.3}/pyproject.toml +4 -3
  21. monopyly-1.4.1/monopyly/cli/run.py +0 -107
  22. monopyly-1.4.1/monopyly/static/jquery-3.7.0.min.js +0 -2
  23. monopyly-1.4.1/monopyly/static/js/expand-transaction.js +0 -32
  24. monopyly-1.4.1/monopyly/static/js/modules/expand-transaction.js +0 -63
  25. monopyly-1.4.1/monopyly/static/js/update-transactions-display.js +0 -66
  26. {monopyly-1.4.1 → monopyly-1.4.3}/COPYING +0 -0
  27. {monopyly-1.4.1 → monopyly-1.4.3}/LICENSE +0 -0
  28. {monopyly-1.4.1 → monopyly-1.4.3}/README.md +0 -0
  29. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/auth/actions.py +0 -0
  30. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/auth/blueprint.py +0 -0
  31. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/auth/tools.py +0 -0
  32. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/accounts.py +0 -0
  33. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/actions.py +0 -0
  34. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/banks.py +0 -0
  35. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/blueprint.py +0 -0
  36. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/filters.py +0 -0
  37. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/forms.py +0 -0
  38. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/routes.py +0 -0
  39. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/banking/transactions.py +0 -0
  40. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/forms/__init__.py +0 -0
  41. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/forms/_forms.py +0 -0
  42. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/forms/fields.py +0 -0
  43. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/forms/utils.py +0 -0
  44. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/forms/validators.py +0 -0
  45. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/transactions.py +0 -0
  46. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/common/utils.py +0 -0
  47. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/config/__init__.py +0 -0
  48. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/core/actions.py +0 -0
  49. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/core/blueprint.py +0 -0
  50. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/core/context_processors.py +0 -0
  51. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/core/filters.py +0 -0
  52. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/core/internal_transactions.py +0 -0
  53. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/accounts.py +0 -0
  54. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/actions.py +0 -0
  55. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/blueprint.py +0 -0
  56. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/cards.py +0 -0
  57. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/forms.py +0 -0
  58. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/routes.py +0 -0
  59. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/statements.py +0 -0
  60. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/transactions/__init__.py +0 -0
  61. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/credit/transactions/_transactions.py +0 -0
  62. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/database/models.py +0 -0
  63. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/database/preloads.sql +0 -0
  64. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/database/schema.sql +0 -0
  65. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/database/views.sql +0 -0
  66. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/browserconfig.xml +0 -0
  67. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-114.png +0 -0
  68. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-120.png +0 -0
  69. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-144.png +0 -0
  70. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-150.png +0 -0
  71. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-152.png +0 -0
  72. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-16.png +0 -0
  73. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-160.png +0 -0
  74. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-180.png +0 -0
  75. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-192.png +0 -0
  76. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-310.png +0 -0
  77. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-32.png +0 -0
  78. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-57.png +0 -0
  79. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-60.png +0 -0
  80. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-64.png +0 -0
  81. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-70.png +0 -0
  82. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-72.png +0 -0
  83. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-76.png +0 -0
  84. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon-96.png +0 -0
  85. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon.ico +0 -0
  86. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/favicon/favicon.png +0 -0
  87. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/bank-account-details.png +0 -0
  88. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/bank-account-summaries.png +0 -0
  89. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/bank-accounts.png +0 -0
  90. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/credit-account-details.png +0 -0
  91. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/credit-transactions.png +0 -0
  92. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/homepage-user.png +0 -0
  93. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/homepage.png +0 -0
  94. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/about/statement-details.png +0 -0
  95. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/cards/chase-card.png +0 -0
  96. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/cards/discover-card.png +0 -0
  97. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/cards/new-card.png +0 -0
  98. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/cards/template-card.png +0 -0
  99. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/arrow-down.png +0 -0
  100. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/arrow-left.png +0 -0
  101. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/arrow-up.png +0 -0
  102. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/checkmark.png +0 -0
  103. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
  104. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/delete-orange.png +0 -0
  105. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/delete-thick.png +0 -0
  106. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/delete.png +0 -0
  107. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/edit.png +0 -0
  108. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/link.png +0 -0
  109. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/minus-thick.png +0 -0
  110. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/minus.png +0 -0
  111. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/plus-thick.png +0 -0
  112. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/plus.png +0 -0
  113. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/refresh.png +0 -0
  114. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/save.png +0 -0
  115. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/sort-asc.png +0 -0
  116. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/sort-desc.png +0 -0
  117. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/statement-pair.svg +0 -0
  118. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/statement.png +0 -0
  119. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/statement.svg +0 -0
  120. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/icons/x-thick.png +0 -0
  121. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/img/statement.png +0 -0
  122. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/add-subtransaction.js +0 -0
  123. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/add-transfer.js +0 -0
  124. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/autocomplete-transaction.js +0 -0
  125. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/bind-tag-actions.js +0 -0
  126. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/create-balance-chart.js +0 -0
  127. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
  128. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/display-new-bank-inputs.js +0 -0
  129. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
  130. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/expand-bank-account.js +0 -0
  131. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/expand-bank.js +0 -0
  132. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/flip-card.js +0 -0
  133. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/hide-homepage-block.js +0 -0
  134. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/infer-card.js +0 -0
  135. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/infer-statement.js +0 -0
  136. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/make-payment.js +0 -0
  137. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/ajax.js +0 -0
  138. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/autocomplete-input.js +0 -0
  139. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/expand-box-row.js +0 -0
  140. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
  141. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/manage-overlays.js +0 -0
  142. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/manage-subforms.js +0 -0
  143. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/modules/update-database-widget.js +0 -0
  144. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/show-linked-transaction.js +0 -0
  145. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/update-account-statement-parameters.js +0 -0
  146. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/update-bank-name.js +0 -0
  147. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/update-card-status.js +0 -0
  148. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/update-statement-parameters.js +0 -0
  149. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/static/js/update-statements-display.js +0 -0
  150. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/auth/login.html +0 -0
  151. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/auth/register.html +0 -0
  152. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/account_form/account_form.html +0 -0
  153. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
  154. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/account_page.html +0 -0
  155. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/account_summaries.html +0 -0
  156. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/account_summaries_page.html +0 -0
  157. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/account_summary.html +0 -0
  158. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/accounts_page.html +0 -0
  159. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
  160. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transaction_form/transaction_form.html +0 -0
  161. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
  162. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
  163. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
  164. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
  165. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
  166. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
  167. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
  168. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/banking/transactions_table/transactions.html +0 -0
  169. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/form_page.html +0 -0
  170. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transaction_form/subform.html +0 -0
  171. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
  172. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
  173. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +0 -0
  174. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
  175. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
  176. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
  177. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
  178. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
  179. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/common/transactions_table/transactions.html +0 -0
  180. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/account_page.html +0 -0
  181. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/card_form/card_form.html +0 -0
  182. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
  183. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
  184. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/card_graphic/card_back.html +0 -0
  185. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
  186. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/card_submission_page.html +0 -0
  187. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/cards.html +0 -0
  188. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/cards_page.html +0 -0
  189. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/statement_page.html +0 -0
  190. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/statement_summary.html +0 -0
  191. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/statements.html +0 -0
  192. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/statements_page.html +0 -0
  193. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -0
  194. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/tag_tree/tag_tree.html +0 -0
  195. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/tags_page.html +0 -0
  196. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
  197. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
  198. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
  199. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
  200. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transaction_submission_page.html +0 -0
  201. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transactions_page.html +0 -0
  202. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transactions_table/condensed_row_content.html +0 -0
  203. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transactions_table/expanded_row_content.html +0 -0
  204. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
  205. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credit/transactions_table/transactions.html +0 -0
  206. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/credits.html +0 -0
  207. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/index.html +0 -0
  208. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/layout.html +0 -0
  209. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/profile.html +0 -0
  210. {monopyly-1.4.1 → monopyly-1.4.3}/monopyly/templates/story.html +0 -0
@@ -69,8 +69,9 @@ target/
69
69
  # Mac custom attributes file
70
70
  .DS_Store
71
71
 
72
- # Virtual environment
72
+ # Virtual environments
73
73
  monopyly-env/
74
+ monopyly-production-env/
74
75
 
75
76
  # Hatch-generated version file
76
77
  monopyly/_version.py
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: monopyly
3
- Version: 1.4.1
3
+ Version: 1.4.3
4
4
  Summary: A homemade personal finance manager.
5
5
  Project-URL: Download, https://pypi.org/project/monopyly
6
- Project-URL: Homepage, https://github.com/mitchnegus/monopyly
6
+ Project-URL: Homepage, http://monopyly.com
7
7
  Project-URL: Repository, https://github.com/mitchnegus/monopyly
8
8
  Project-URL: Changelog, https://github.com/mitchnegus/monopyly/blob/main/CHANGELOG.md
9
9
  Author-email: Mitch Negus <mitchnegus57@gmail.com>
@@ -22,11 +22,12 @@ 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.11,>=3.9
25
+ Requires-Python: <3.11,>=3.10
26
26
  Requires-Dist: authanor>=1.0.2
27
27
  Requires-Dist: flask-wtf
28
28
  Requires-Dist: flask>=2.2.2
29
29
  Requires-Dist: fuisce>1.0.0
30
+ Requires-Dist: gunicorn>=21.2.0
30
31
  Requires-Dist: markdown>=3.4.3
31
32
  Requires-Dist: python-dateutil
32
33
  Requires-Dist: sqlalchemy>=2.0.0
@@ -64,10 +65,10 @@ The package requires a recent version of Python (3.9+).
64
65
  Once the package is properly installed, run the app from the command line (the default options should be sensible, but you may customize the host and port if necessary):
65
66
 
66
67
  ```
67
- $ monopyly production [--host HOST] [--port PORT]
68
+ $ monopyly development --browser [--host HOST] [--port PORT]
68
69
  ```
69
70
 
70
- This will open to an empty homepage with a welcome message.
71
+ By using the `--browser` option in development mode, this will open to an empty homepage with a welcome message.
71
72
 
72
73
  <img class="screenshot" src="https://raw.githubusercontent.com/mitchnegus/monopyly/main/monopyly/static/img/about/homepage.png" alt="user homepage" width="800px">
73
74
 
@@ -30,10 +30,10 @@ The package requires a recent version of Python (3.9+).
30
30
  Once the package is properly installed, run the app 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 production [--host HOST] [--port PORT]
33
+ $ monopyly development --browser [--host HOST] [--port PORT]
34
34
  ```
35
35
 
36
- This will open to an empty homepage with a welcome message.
36
+ By using the `--browser` option in development mode, this will open to an empty homepage with a welcome message.
37
37
 
38
38
  <img class="screenshot" src="monopyly/static/img/about/homepage.png" alt="user homepage" width="800px">
39
39
 
@@ -1,8 +1,6 @@
1
1
  """
2
2
  Run a development server for the Monopyly app.
3
3
  """
4
- import warnings
5
-
6
4
  from flask import Flask
7
5
 
8
6
  from monopyly.config import DevelopmentConfig, ProductionConfig
@@ -22,9 +20,6 @@ def create_app(test_config=None):
22
20
  config = DevelopmentConfig.configure_for_instance(app.instance_path)
23
21
  else:
24
22
  config = ProductionConfig.configure_for_instance(app.instance_path)
25
- # Give an alert while the secret key remains insecure
26
- warnings.formatwarning = lambda msg, *args, **kwargs: f"\n{msg}\n"
27
- warnings.warn("INSECURE: Production mode has not yet been fully configured")
28
23
  app.config.from_object(config)
29
24
 
30
25
  # Initialize the app, including CLI commands and blueprints
@@ -1,4 +1,4 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '1.4.1'
4
- __version_tuple__ = version_tuple = (1, 4, 1)
3
+ __version__ = version = '1.4.3'
4
+ __version_tuple__ = version_tuple = (1, 4, 3)
@@ -26,7 +26,9 @@ def register():
26
26
  # Get username and passwords from the form
27
27
  username, password = get_username_and_password(request.form)
28
28
  # Check for errors in the accessed information
29
- if not username:
29
+ if not current_app.config["REGISTRATION"]:
30
+ error = "The app is not currently accepting new registrations."
31
+ elif not username:
30
32
  error = "Username is required."
31
33
  elif not password:
32
34
  error = "Password is required."
@@ -0,0 +1,84 @@
1
+ """Application objects for running the app via the CLI."""
2
+ import multiprocessing
3
+ import subprocess
4
+
5
+ from gunicorn.app.base import BaseApplication
6
+
7
+ from .. import create_app
8
+
9
+
10
+ class LocalApplication:
11
+ """An object for running the application locally."""
12
+
13
+ default_port = "5001"
14
+ command = ["flask"]
15
+
16
+ def __init__(self, host=None, port=None, **options):
17
+ """Initialize the application in development mode."""
18
+ self._host = host
19
+ self._port = port
20
+ if options:
21
+ raise NotImplementedError(
22
+ "Options besides `host` and `port` are not handled in development mode."
23
+ )
24
+
25
+ def run(self):
26
+ """Run the Monopyly application in development mode."""
27
+ instruction = self.command + ["run"]
28
+ if self._host:
29
+ instruction += ["--host", self._host]
30
+ if self._port:
31
+ instruction += ["--port", self._port]
32
+ server = subprocess.Popen(instruction)
33
+
34
+
35
+ class DevelopmentApplication(LocalApplication):
36
+ """An object for running the application in development mode."""
37
+
38
+ default_port = "5000"
39
+ command = LocalApplication.command + ["--debug"]
40
+
41
+
42
+ class ProductionApplication(BaseApplication):
43
+ """An object for running the application in production mode (via Gunicorn)."""
44
+
45
+ default_port = "8000"
46
+ _default_worker_count = (multiprocessing.cpu_count() * 2) + 1
47
+
48
+ def __init__(self, host=None, port=None, **options):
49
+ """Initialize the application in production mode."""
50
+ options["bind"] = self._parse_binding(host, port, options.get("bind"))
51
+ options.setdefault("workers", self._default_worker_count)
52
+ self.options = options
53
+ self.application = create_app()
54
+ super().__init__()
55
+
56
+ @staticmethod
57
+ def _parse_binding(host, port, bind_option):
58
+ # Parse any socket binding options
59
+ if (host or port) and bind_option:
60
+ raise ValueError(
61
+ "Neither `host` nor `port` parameters can be specified if the "
62
+ "`bind` option is given."
63
+ )
64
+ bind_values = []
65
+ if host:
66
+ bind_values.append(host)
67
+ if port:
68
+ bind_values.append(port)
69
+ return bind if (bind := ":".join(bind_values)) else bind_option
70
+
71
+ def load_config(self):
72
+ config = {
73
+ key: value
74
+ for key, value in self.options.items()
75
+ if key in self.cfg.settings and value is not None
76
+ }
77
+ for key, value in config.items():
78
+ self.cfg.set(key.lower(), value)
79
+
80
+ def load(self):
81
+ return self.application
82
+
83
+ def run(self, *args, **kwargs):
84
+ return super().run(*args, **kwargs)
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ A script (entry point) to launch the Monopyly application.
4
+ """
5
+ import argparse
6
+ import os
7
+ import signal
8
+ import subprocess
9
+ import time
10
+ import webbrowser
11
+ from pathlib import Path
12
+ from threading import Event
13
+
14
+ from flask import current_app
15
+ from rich.console import Console
16
+
17
+ from .apps import DevelopmentApplication, LocalApplication, ProductionApplication
18
+
19
+ # Set the Flask environment variable
20
+ os.environ["FLASK_APP"] = "monopyly"
21
+
22
+
23
+ def main():
24
+ args = parse_arguments()
25
+ app_launcher = Launcher(args.mode, host=args.host, port=args.port)
26
+ # Initialize the database and run the app
27
+ app_launcher.initialize_database()
28
+ if args.backup:
29
+ app_launcher.backup_database()
30
+ app_launcher.launch()
31
+ if args.mode in ("development", "local"):
32
+ # Enable browser viewing in development mode
33
+ if args.browser:
34
+ app_launcher.open_browser(delay=1)
35
+ # Wait for the exit command to stop
36
+ app_launcher.wait_for_exit()
37
+
38
+
39
+ def parse_arguments():
40
+ parser = argparse.ArgumentParser(description=__doc__)
41
+ parser.add_argument("--host", help="the host address where the app will be run")
42
+ parser.add_argument("--port", help="the port where the app will be accessible")
43
+ parser.add_argument(
44
+ "--backup",
45
+ action="store_true",
46
+ help="a flag indicating if the database should be backed up",
47
+ )
48
+ parser.add_argument(
49
+ "--browser",
50
+ action="store_true",
51
+ help=(
52
+ "a flag indicating if a new browser window should be opened (development "
53
+ "and local modes only)"
54
+ ),
55
+ )
56
+ parser.add_argument(
57
+ "mode",
58
+ help="the runtime mode for the app",
59
+ choices=["development", "local", "production"],
60
+ )
61
+ return parser.parse_args()
62
+
63
+
64
+ class Launcher:
65
+ """A tool to build and execute Flask commands."""
66
+
67
+ _application_types = {
68
+ "development": DevelopmentApplication,
69
+ "local": LocalApplication,
70
+ "production": ProductionApplication,
71
+ }
72
+ _console = Console()
73
+ _exit = Event()
74
+ command = ["flask"]
75
+
76
+ def __init__(self, mode, host=None, port=None):
77
+ app_type = self._application_types[mode]
78
+ self.host = host if host else "127.0.0.1"
79
+ self.port = port if port else app_type.default_port
80
+ self.app = app_type(host=self.host, port=self.port)
81
+
82
+ def initialize_database(self):
83
+ """Run the database initializer."""
84
+ instruction = self.command + ["init-db"]
85
+ self._console.print("[deep_sky_blue1]Initializing the database...")
86
+ subprocess.run(instruction)
87
+ print("\n")
88
+
89
+ def backup_database(self):
90
+ """Back up the app database."""
91
+ self._console.print("[deep_sky_blue1]Backing up the database...")
92
+ instruction = self.command + ["back-up-db"]
93
+ subprocess.run(instruction)
94
+ print("\n")
95
+
96
+ def launch(self):
97
+ """Launch the Monopyly application."""
98
+ self._console.print("[deep_sky_blue1]Running the Monopyly application...\n")
99
+ self.app.run()
100
+
101
+ def open_browser(self, delay=0):
102
+ """Open the default web browser."""
103
+ time.sleep(delay)
104
+ webbrowser.open(f"http://{self.host}:{self.port}/")
105
+
106
+ @classmethod
107
+ def wait_for_exit(cls):
108
+ """Wait for the exit command (e.g., keyboard interrupt) to be issued."""
109
+ for sig in ("TERM", "HUP", "INT"):
110
+ signal.signal(getattr(signal, "SIG" + sig), cls._quit)
111
+ while not cls._exit.is_set():
112
+ cls._exit.wait(1)
113
+
114
+ @classmethod
115
+ def _quit(cls, signo, _frame):
116
+ """Send the signal to quit the app."""
117
+ print("\nClosing the Monopyly app...")
118
+ cls._exit.set()
119
+
120
+
121
+ if __name__ == "__main__":
122
+ main()
@@ -1,11 +1,21 @@
1
1
  """Default configuration settings."""
2
+ import json
2
3
  from pathlib import Path
3
4
 
5
+ CONFIG_FILENAME = f"monopyly-config.json"
6
+ DEFAULT_CONFIG_PATH = Path("/etc", CONFIG_FILENAME)
7
+
4
8
 
5
9
  class Config:
6
10
  """A base configuration object with some default settings."""
7
11
 
12
+ config_filepaths = [DEFAULT_CONFIG_PATH]
13
+ REGISTRATION = True
14
+
8
15
  def __init__(self, db_path=None):
16
+ # Read parameters from the configuration files in order of specificity
17
+ for config_filepath in filter(lambda p: p.exists(), self.config_filepaths):
18
+ self._read_config_json(config_filepath)
9
19
  if db_path:
10
20
  self.DATABASE = db_path
11
21
 
@@ -18,6 +28,13 @@ class Config:
18
28
  # Ensure that the database path is always set as a `pathlib.Path` object
19
29
  self._database = Path(value)
20
30
 
31
+ def _read_config_json(self, config_path):
32
+ # Read keys and values from a configuration JSON
33
+ with config_path.open() as config_json:
34
+ config_mapping = json.load(config_json)
35
+ for key, value in config_mapping.items():
36
+ setattr(self, key, value)
37
+
21
38
 
22
39
  class InstanceBasedConfig(Config):
23
40
  """A base configuration object for app modes using instance directories."""
@@ -30,5 +47,9 @@ class InstanceBasedConfig(Config):
30
47
  """Instantiate the app based out of the given instance directory."""
31
48
  instance_path = Path(instance_path)
32
49
  instance_path.mkdir(parents=True, exist_ok=True)
50
+ cls.config_filepaths = [
51
+ *super().config_filepaths,
52
+ instance_path / CONFIG_FILENAME,
53
+ ]
33
54
  db_path = Path(instance_path, cls.db_name) if cls.db_name else None
34
55
  return cls(db_path=db_path, **kwargs)
@@ -1,4 +1,5 @@
1
1
  """A module containing objects with various configuration settings."""
2
+ import warnings
2
3
  from pathlib import Path
3
4
 
4
5
  from ..database import BASE_DB_NAME
@@ -8,9 +9,19 @@ from .default_settings import Config, InstanceBasedConfig
8
9
  class ProductionConfig(InstanceBasedConfig):
9
10
  """A configuration object with settings for production."""
10
11
 
11
- SECRET_KEY = "INSECURE PRODUCTION TEST KEY"
12
+ SECRET_KEY = "INSECURE"
12
13
  db_name = BASE_DB_NAME
13
14
 
15
+ def __init__(self, db_path=None):
16
+ super().__init__(db_path=db_path)
17
+ if self.SECRET_KEY == "INSECURE":
18
+ # Give an alert while the secret key remains insecure
19
+ warnings.formatwarning = lambda msg, *args, **kwargs: f"\n{msg}\n"
20
+ warnings.warn(
21
+ "INSECURE: Production mode has not yet been fully configured; "
22
+ "a secret key is required."
23
+ )
24
+
14
25
 
15
26
  class DevelopmentConfig(InstanceBasedConfig):
16
27
  """A configuration object with settings for development."""
@@ -60,6 +60,7 @@ def about():
60
60
 
61
61
 
62
62
  @bp.route("/story")
63
+ @login_required
63
64
  def story():
64
65
  return render_template("story.html")
65
66
 
@@ -11,7 +11,7 @@ from fuisce.database import SQLAlchemy as _SQLAlchemy
11
11
 
12
12
  from ..core.actions import get_timestamp
13
13
 
14
- BASE_DB_NAME = "monopyly.sqlite"
14
+ BASE_DB_NAME = f"monopyly.sqlite"
15
15
 
16
16
 
17
17
  class SQLAlchemy(_SQLAlchemy):
@@ -252,8 +252,9 @@ aside.sidebar {
252
252
 
253
253
  .screenshot {
254
254
  width: 100%; /* override the markdown default */
255
+ margin: 20px 0;
255
256
  border: 1px solid #f3f3f3;
256
- box-shadow: 3px 3px 2px #fafafa;
257
+ box-shadow: 3px 3px 5px #bbbbbb;
257
258
  }
258
259
 
259
260
  .grouping {
@@ -1207,6 +1208,7 @@ form .autocomplete-box .item.active {
1207
1208
  .transactions-table .expanded .setting .date {
1208
1209
  display: flex;
1209
1210
  flex-direction: column;
1211
+ justify-content: center;
1210
1212
  align-items: center;
1211
1213
  flex: 2;
1212
1214
  padding-top: 3px;
@@ -0,0 +1,3 @@
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d8f9afbf492e4c139e9d2bcb9ba6ef7c14921eb509fb703bc7a3f911b774eff8
3
+ size 87462
@@ -11,7 +11,7 @@
11
11
 
12
12
  // Identify the card filters
13
13
  const $filterContainer = $('#card-filter');
14
-
14
+
15
15
  // Label inactive cards when they are hovered over
16
16
  const inactiveCardFilters = $filterContainer.find('.inactive.card');
17
17
  let defaultText;
@@ -31,7 +31,7 @@
31
31
  $this.text(defaultText);
32
32
  }
33
33
  );
34
-
34
+
35
35
  // Change the filter status on click
36
36
  const $filters = $filterContainer.find('.card');
37
37
  $filters.on('click', function() {
@@ -0,0 +1,18 @@
1
+ /*
2
+ * Expand a transaction in a table when the plus icon is clicked.
3
+ *
4
+ * Expands a row in the transaction table when the plus icon offering
5
+ * more information is clicked. The expanded row shows transactions in
6
+ * more detail (and a larger font) than the rest of the transaction
7
+ * table. For both credit card and bank transactions, this expanded row
8
+ * shows subtransaction information.
9
+ */
10
+
11
+ import {
12
+ TransactionToggleManager, displaySubtransactions
13
+ } from './modules/expand-transaction.js';
14
+
15
+
16
+ (function() {
17
+ const toggleManager = new TransactionToggleManager(displaySubtransactions)
18
+ })();
@@ -0,0 +1,128 @@
1
+ /*
2
+ * Expand a transaction when the plus icon is clicked.
3
+ *
4
+ * Expands a row in the transaction table when the plus icon offering
5
+ * more information is clicked. The expanded row shows transactions in
6
+ * more detail (and a larger font) than the rest of the transaction
7
+ * table. A user can exit the expanded view by clicking the 'x' icon
8
+ * button that is shown in the expanded view. The 'toggleTransactionRow'
9
+ * function takes an optional callback function which is executed after
10
+ * the row is expanded. The callback function receives the selected
11
+ * '.transaction' element as its only argument.
12
+ */
13
+
14
+ import {
15
+ replaceDisplayContentsAjaxRequest
16
+ } from './update-display-ajax.js';
17
+
18
+
19
+ class TransactionToggleManager {
20
+ /**
21
+ * Create the object to toggle transactions.
22
+ *
23
+ * @param {function} callback - A callback function to execute when toggling
24
+ * a button to get more info.
25
+ */
26
+ constructor(callback = null) {
27
+ // Identify the plus/minus icons
28
+ this.$iconsMoreInfoButtons = $('.transaction .more.button');
29
+ this.$iconsLessInfoButtons = $('.transaction .less.button');
30
+ this.#registerClickExpand(callback);
31
+ this.#registerClickCollapse();
32
+ }
33
+
34
+ getButtonTransaction(button) {
35
+ return $(button).closest('.transaction');
36
+ }
37
+
38
+ #registerClickExpand(callback) {
39
+ const self = this;
40
+ this.$iconsMoreInfoButtons.on('click', function() {
41
+ const $transaction = self.getButtonTransaction(this);
42
+ const toggler = new TransactionToggler($transaction);
43
+ toggler.expand(callback);
44
+ });
45
+ }
46
+
47
+ #registerClickCollapse() {
48
+ const self = this;
49
+ this.$iconsLessInfoButtons.on('click', function() {
50
+ const $transaction = self.getButtonTransaction(this);
51
+ const toggler = new TransactionToggler($transaction);
52
+ toggler.collapse();
53
+ });
54
+ }
55
+ }
56
+
57
+
58
+ class TransactionToggler {
59
+
60
+ /**
61
+ * Create the handler.
62
+ *
63
+ * @param {Object} $transaction - The transaction to be toggled.
64
+ */
65
+ constructor($transaction) {
66
+ // Set timing variables
67
+ this.fadeTime = 200;
68
+ this.slideTime = 250;
69
+ // Identify elements of the row
70
+ this.$transaction = $transaction;
71
+ this.$extendedRow = $transaction.find('.expanded');
72
+ this.$condensedRow = $transaction.find('.condensed');
73
+ }
74
+
75
+ /**
76
+ * Expand the transaction
77
+ *
78
+ * @param {function} callback - A callback function to execute when expanding
79
+ * the transaction information. The callback function takes one argument,
80
+ * the JQuery object representing the transaction.
81
+ */
82
+ expand(callback = null) {
83
+ // Execute the callback function, if given
84
+ if (callback != null) {
85
+ callback(this.$transaction);
86
+ }
87
+ this.#toggleTransaction(this.$condensedRow, this.$extendedRow);
88
+ this.$transaction.addClass('selected');
89
+ }
90
+
91
+ collapse($transaction) {
92
+ this.#toggleTransaction(this.$extendedRow, this.$condensedRow);
93
+ this.$transaction.removeClass('selected');
94
+ }
95
+
96
+ #toggleTransaction($collapser, $expander) {
97
+ this.#hideSummaryRow($collapser);
98
+ this.#showSummaryRow($expander)
99
+ }
100
+
101
+ #hideSummaryRow($row) {
102
+ const self = this;
103
+ $row.fadeTo(self.fadeTime, 0, function() {
104
+ $(this).slideUp(self.slideTime);
105
+ });
106
+ }
107
+
108
+ #showSummaryRow($row) {
109
+ const self = this;
110
+ $row.delay(self.fadeTime).slideDown(self.slideTime, function() {
111
+ $(this).fadeTo(self.fadeTime, 1);
112
+ });
113
+ }
114
+ }
115
+
116
+
117
+ function displaySubtransactions($transaction) {
118
+
119
+ // Execute an AJAX request to get transaction/subtransaction information
120
+ const endpoint = EXPAND_TRANSACTION_ENDPOINT;
121
+ const rawData = $transaction.data("transaction-id");
122
+ const $container = $transaction.find('.subtransaction-container');
123
+ replaceDisplayContentsAjaxRequest(endpoint, rawData, $container);
124
+
125
+ }
126
+
127
+
128
+ export { TransactionToggleManager, displaySubtransactions };
@@ -11,14 +11,21 @@
11
11
  import { executeAjaxRequest } from './ajax.js';
12
12
 
13
13
 
14
- function replaceDisplayContentsAjaxRequest(endpoint, rawData, $display) {
15
-
16
- // The action is to replace the display's contents
17
- function action(response) {
18
- $display.html(response);
19
- }
20
- // Assign the response to the display object
21
- executeAjaxRequest(endpoint, rawData, action);
14
+ function replaceDisplayContentsAjaxRequest(
15
+ endpoint, rawData, $display, callback = null
16
+ ) {
17
+
18
+ // The action is to replace the display's contents
19
+ function action(response) {
20
+ $display.html(response);
21
+ // Execute the callback function, if given
22
+ if (callback != null) {
23
+ callback();
24
+ }
25
+ }
26
+
27
+ // Assign the response to the display object
28
+ executeAjaxRequest(endpoint, rawData, action);
22
29
 
23
30
  }
24
31