monopyly 1.4.5__tar.gz → 1.4.7__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.
- {monopyly-1.4.5 → monopyly-1.4.7}/PKG-INFO +16 -5
- monopyly-1.4.7/monopyly/CHANGELOG.md +187 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/README.md +13 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/__init__.py +19 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/_version.py +2 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/accounts.py +1 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/cli/apps.py +25 -4
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/forms/_forms.py +14 -11
- monopyly-1.4.7/monopyly/core/actions.py +121 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/core/blueprint.py +1 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/core/context_processors.py +10 -0
- monopyly-1.4.7/monopyly/core/errors.py +9 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/core/filters.py +4 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/core/routes.py +22 -9
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/actions.py +29 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/routes.py +11 -6
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/statements.py +21 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/transactions/_transactions.py +1 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/css/style.css +161 -69
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/make-payment.js +4 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/auth/login.html +1 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/auth/register.html +1 -1
- monopyly-1.4.7/monopyly/templates/banking/account_summaries.html +35 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/account_summaries_page.html +2 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/account_summary.html +7 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/accounts_page.html +2 -2
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transaction_form/transaction_form.html +1 -1
- monopyly-1.4.7/monopyly/templates/core/credits.html +32 -0
- monopyly-1.4.7/monopyly/templates/core/errors/400.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/401.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/403.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/404.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/405.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/408.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/418.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/425.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/500.html +8 -0
- monopyly-1.4.7/monopyly/templates/core/errors/error.html +31 -0
- {monopyly-1.4.5/monopyly/templates → monopyly-1.4.7/monopyly/templates/core}/profile.html +3 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/card_graphic/card_back.html +1 -1
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/statement_summary.html +9 -4
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/statements.html +7 -6
- {monopyly-1.4.5 → monopyly-1.4.7}/pyproject.toml +2 -2
- monopyly-1.4.5/monopyly/core/actions.py +0 -34
- monopyly-1.4.5/monopyly/templates/banking/account_summaries.html +0 -21
- monopyly-1.4.5/monopyly/templates/credits.html +0 -20
- {monopyly-1.4.5 → monopyly-1.4.7}/.gitignore +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/COPYING +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/LICENSE +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/README.md +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/auth/actions.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/auth/blueprint.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/auth/routes.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/auth/tools.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/actions.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/banks.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/blueprint.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/filters.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/forms.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/routes.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/banking/transactions.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/cli/run.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/forms/__init__.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/forms/fields.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/forms/utils.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/forms/validators.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/transactions.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/common/utils.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/config/__init__.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/config/default_settings.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/config/settings.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/core/internal_transactions.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/accounts.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/blueprint.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/cards.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/forms.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/credit/transactions/__init__.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/database/__init__.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/database/models.py +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/database/preloads.sql +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/database/schema.sql +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/database/views.sql +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/browserconfig.xml +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-114.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-120.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-144.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-150.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-152.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-16.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-160.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-180.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-192.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-310.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-32.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-57.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-60.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-64.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-70.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-72.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-76.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon-96.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon.ico +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/favicon/favicon.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/bank-account-details.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/bank-account-summaries.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/bank-accounts.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/credit-account-details.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/credit-transactions.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/homepage-user.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/homepage.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/about/statement-details.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/cards/chase-card.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/cards/discover-card.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/cards/new-card.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/cards/template-card.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/arrow-down.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/arrow-left.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/arrow-up.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/checkmark.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/delete-orange-thick.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/delete-orange.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/delete-thick.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/delete.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/edit.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/link.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/minus-thick.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/minus.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/plus-thick.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/plus.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/refresh.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/save.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/sort-asc.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/sort-desc.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/statement-pair.svg +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/statement.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/statement.svg +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/icons/x-thick.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/img/statement.png +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/jquery-3.7.0.min.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/add-subtransaction.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/add-transfer.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/autocomplete-transaction.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/bind-tag-actions.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/create-balance-chart.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/define-filter.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/display-new-account-type-inputs.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/display-new-bank-inputs.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/display-new-credit-account-inputs.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/expand-bank-account.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/expand-bank.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/expand-transaction.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/flip-card.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/hide-homepage-block.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/infer-card.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/infer-statement.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/ajax.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/autocomplete-input.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/expand-box-row.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/expand-transaction.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/manage-acquisition-form.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/manage-overlays.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/manage-subforms.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/update-database-widget.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/modules/update-display-ajax.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/show-linked-transaction.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/update-account-statement-parameters.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/update-bank-name.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/update-card-status.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/update-statement-parameters.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/update-statements-display.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/static/js/update-transactions-display.js +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/account_form/account_form.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/account_form/account_form_page_new.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/account_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transaction_form/bank_info_form.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transaction_form/transfer_form.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transactions_table/condensed_row_content.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/banking/transactions_table/transactions.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/form_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transaction_form/subform.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transaction_form/subtransaction_subform.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/linked_bank_transaction.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/linked_credit_transaction.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/linked_transaction_overlay.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/subtransactions.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/transaction_condensed.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/transaction_expanded.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/common/transactions_table/transactions.html +0 -0
- {monopyly-1.4.5/monopyly/templates → monopyly-1.4.7/monopyly/templates/core}/index.html +0 -0
- {monopyly-1.4.5/monopyly/templates → monopyly-1.4.7/monopyly/templates/core}/story.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/account_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/card_form/card_form.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/card_form/card_form_page_new.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/card_form/transfer_statement_inquiry.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/card_graphic/card_front.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/card_submission_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/cards.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/cards_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/statement_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/statements_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/tag_tree/subtag_tree.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/tag_tree/tag_tree.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/tags_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transaction_form/transaction_form.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transaction_form/transaction_form_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transaction_form/transaction_form_page_new.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transaction_form/transaction_form_page_update.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transaction_submission_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transactions_page.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transactions_table/condensed_row_content.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transactions_table/expanded_row_content.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transactions_table/transaction_field_titles.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/credit/transactions_table/transactions.html +0 -0
- {monopyly-1.4.5 → monopyly-1.4.7}/monopyly/templates/layout.html +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: monopyly
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.7
|
|
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
|
|
@@ -25,9 +25,9 @@ Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
|
25
25
|
Requires-Python: <3.11,>=3.10
|
|
26
26
|
Requires-Dist: authanor==1.1.0
|
|
27
27
|
Requires-Dist: flask-wtf==1.2.1
|
|
28
|
-
Requires-Dist: flask==3.0.
|
|
28
|
+
Requires-Dist: flask==3.0.3
|
|
29
29
|
Requires-Dist: fuisce==1.0.2
|
|
30
|
-
Requires-Dist: gunicorn==
|
|
30
|
+
Requires-Dist: gunicorn==22.0.0
|
|
31
31
|
Requires-Dist: markdown==3.6
|
|
32
32
|
Requires-Dist: python-dateutil==2.9.0
|
|
33
33
|
Requires-Dist: rich==13.7.1
|
|
@@ -63,12 +63,23 @@ The package requires a recent version of Python (3.9+).
|
|
|
63
63
|
|
|
64
64
|
## Getting started
|
|
65
65
|
|
|
66
|
-
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):
|
|
66
|
+
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):
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
$ monopyly
|
|
69
|
+
$ monopyly local --browser [--host HOST] [--port PORT]
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
|
|
73
|
+
Other available modes are `development` and `production`, for those looking to either develop the application or host the application on a server.
|
|
74
|
+
|
|
75
|
+
<div class="warning">
|
|
76
|
+
<h5>Use your brain when choosing a mode!</h5>
|
|
77
|
+
<small>
|
|
78
|
+
If you intend to be the only user of the app, served just from your PC, local mode is fine; you will be served well-enough by the built-in Python server, and you do not need to configure secret keys for the application.
|
|
79
|
+
If you plan to host the application, however, <b>DO NOT</b> use local mode (or development mode) and run the app in production mode instead.
|
|
80
|
+
</small>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
72
83
|
By using the `--browser` option in development mode, this will open to an empty homepage with a welcome message.
|
|
73
84
|
|
|
74
85
|
<img class="screenshot" src="https://raw.githubusercontent.com/mitchnegus/monopyly/main/monopyly/static/img/about/homepage.png" alt="user homepage" width="800px">
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
<a class="latest-release" href="#bottom">Latest</a>
|
|
4
|
+
|
|
5
|
+
## 1.0.0
|
|
6
|
+
|
|
7
|
+
- Initial release
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### 1.0.1
|
|
11
|
+
|
|
12
|
+
- Added dependencies to `setup.py` for self-contained installation
|
|
13
|
+
- Added this changelog
|
|
14
|
+
- Added a generally comprehensive [README](README.md)
|
|
15
|
+
- Fixed a few minor bugs with the display interface
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### 1.0.2
|
|
19
|
+
|
|
20
|
+
- Added image support to PyPI description
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### 1.0.3
|
|
24
|
+
|
|
25
|
+
- Implemented (rudimentary) interface for adding and removing transaction tags
|
|
26
|
+
- Added button to add more statements to a transaction (from submission complete page)
|
|
27
|
+
- Fixed bug in updating a transaction's statement date to a new statement
|
|
28
|
+
- Fixed bug where tags where not saved on new transactions
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### 1.0.4
|
|
32
|
+
|
|
33
|
+
- Improved tagging interface with a 'Manage Tags' page
|
|
34
|
+
- Introduced statement level statistics
|
|
35
|
+
- Renamed database files to end with `.sqlite` extension
|
|
36
|
+
- Moved menus on 'Statement Details' and 'Account Details' pages into the sidebar to prevent overlap on collapsed screens
|
|
37
|
+
- Renamed `show_*` route functions to `load_*` to clarify that they are for loading the associated pages (as opposed to just displaying content)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### 1.0.5
|
|
41
|
+
|
|
42
|
+
- Transactions may be split into an arbitrary number of subtransactions, each with a separate subtotal and note
|
|
43
|
+
- Backend properly handles duplicate/ancestor tags; all tags are saved in the database, but only the lowest-level child tag must be entered
|
|
44
|
+
- Removed statement level statistics display (not yet mature)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## 1.1.0
|
|
48
|
+
|
|
49
|
+
- Added banking interface
|
|
50
|
+
- Added interface for displaying linked transactions (including between bank transactions and credit transactions)
|
|
51
|
+
- Grayed out pending transactions
|
|
52
|
+
- Fixed bug where transaction tags appeared in front of header bar
|
|
53
|
+
- Improved modularity of autocomplete JavaScript
|
|
54
|
+
- Improved modularity of transaction table templates (for banking and credit transactions)
|
|
55
|
+
- Minor cosmetic enhancements
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## 1.2.0
|
|
59
|
+
|
|
60
|
+
- Database backend converted to use SQLAlchemy (2.0)
|
|
61
|
+
- Complete test suite created
|
|
62
|
+
- Banking interface updated to use subtransactions
|
|
63
|
+
- Added the ability to transfer statements when a new credit card is added to an account
|
|
64
|
+
- Fixed error on handling subtransactions in credit forms
|
|
65
|
+
- Fixed error updating transaction display from widget bar
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### 1.2.1
|
|
69
|
+
|
|
70
|
+
- Improve development functionality; better/safer cleaning with a separate database and preloaded data
|
|
71
|
+
- Add custom form fields for consistent app-wide form implementations
|
|
72
|
+
- Overhaul documentation formatting with _isort_ and _Black_
|
|
73
|
+
- Fixed bug where multiple subtransactions failed to update properly
|
|
74
|
+
- Fixed error where validation was not raised for blank inputs to a `CustomChoiceSelectField`
|
|
75
|
+
- Fixed errors in field validation for nested subforms with unset values
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
### 1.2.2
|
|
79
|
+
|
|
80
|
+
- Use `pyproject.toml` via Hatch
|
|
81
|
+
- Added version information to app footer
|
|
82
|
+
- Fixed bug where bank name failed to show on 'Credit Account Details' page
|
|
83
|
+
- Improved JavaScript management for acquisition forms
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
### 1.2.3
|
|
87
|
+
|
|
88
|
+
- Add currency symbol to the amount field(s)
|
|
89
|
+
- Allow subform fields to be removed from forms
|
|
90
|
+
- Change 'Vendor' field name to 'Merchant' for better generalizability
|
|
91
|
+
- Remove wildcard imports from the package source code
|
|
92
|
+
- Update form styles to be more browser compatible
|
|
93
|
+
- Impose boolean constraint on boolean/integer database fields
|
|
94
|
+
- Use metaclasses for database handler to avoid stacking classmethod and property (deprecated in Python 3.11)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## 1.3.0
|
|
98
|
+
|
|
99
|
+
- Add a merchant field and tags to bank transactions
|
|
100
|
+
- Add an enhanced autocompletion for assigning transaction tags
|
|
101
|
+
- Add a settings page for allowing bank names to be changed (and eventually allowing passwords to be updated, among other functionality)
|
|
102
|
+
- Leverage SQLAlchemy 2.0 `DeclarativeBase` class and `declared_attr` decorators
|
|
103
|
+
- Simplify `Model` base to avoid explicitly defining column attributes
|
|
104
|
+
- Update the database backup script to work as part of the package
|
|
105
|
+
- Swap the README instructions for the 'About' page (and move the story to a separate page)
|
|
106
|
+
- Improve utilization of `pyroject.toml`
|
|
107
|
+
- Factor out the database handlers (now found in the Authanor package dependency)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### 1.3.1
|
|
111
|
+
|
|
112
|
+
- Upgrade jQuery version to 3.7.0
|
|
113
|
+
- Use recommended `importlib` method for pytest
|
|
114
|
+
- Automatically use the transferring bank as the merchant in bank transfers
|
|
115
|
+
- For transfers between accounts at the same bank, do not show 'Withdrawal' or 'Deposit' headers; just mark as 'Transfer'
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## 1.4.0
|
|
119
|
+
|
|
120
|
+
- Add charts showing historical balances to bank account details page
|
|
121
|
+
- Show projected balances for bank transactions occurring in the future
|
|
122
|
+
- Allow users to remove the welcome block from the homepage
|
|
123
|
+
- Make use of `data-*` HTML tag attributes rather than ID parsing
|
|
124
|
+
- Prevent JavaScript errors on statement pages when buttons are non-existent
|
|
125
|
+
- Clean up transaction table overflow for transaction notes
|
|
126
|
+
- Generalize transaction forms using Jinja `super` blocks
|
|
127
|
+
- Convert JavaScript files to be named using kebab case
|
|
128
|
+
- Use Fuisce in place of Authanor for db/testing interfaces
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
### 1.4.1
|
|
132
|
+
|
|
133
|
+
- Use local time for determining projected balances
|
|
134
|
+
- Show future credit statement payments as "scheduled" (and improve logic for displaying paid notices)
|
|
135
|
+
- Fix incorrect return type in `CreditStatementHandler.infer_statement` method
|
|
136
|
+
- Distinguish between inline code and "fenced" code using a Markdown library extension
|
|
137
|
+
- Use more `data-*` attributes for processing transaction IDs
|
|
138
|
+
- Fix bug where linked transactions did not use jQuery reference
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
### 1.4.2
|
|
142
|
+
|
|
143
|
+
- Configure production mode via Gunicorn
|
|
144
|
+
- Standardize CLI usage messages
|
|
145
|
+
- Make browser usage optional in CLI run script
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
### 1.4.3
|
|
149
|
+
|
|
150
|
+
- Reconfigure makefile for smoother installation
|
|
151
|
+
- Fix bug where transaction toggling failed on filter updates
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
### 1.4.4
|
|
155
|
+
|
|
156
|
+
- Update date-to-timestamp conversion to be timezone invariant
|
|
157
|
+
- Fix database initialization for development mode
|
|
158
|
+
- Fix bug where credit card number was not displayed in card summary
|
|
159
|
+
- Use Beautiful Soup to make tests more robust
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### 1.4.5
|
|
163
|
+
|
|
164
|
+
- Fix bug where a transaction for a new statement was marked as invalid on entry
|
|
165
|
+
- Sort bank account displays by account type and last four digits
|
|
166
|
+
- Use SQLAlchemy 'selectin' loading mode for commonly loaded relationships
|
|
167
|
+
- Give tags a property for identifying their depth in the user's "tag tree"
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
### 1.4.6
|
|
171
|
+
|
|
172
|
+
- Bump dependencies (including patching security vulnerability in gunicorn)
|
|
173
|
+
- Add a function for acquiring a statement and all of its transactions
|
|
174
|
+
- Add a convenience method to the statement handler for getting the preceding statement
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
### 1.4.7
|
|
178
|
+
|
|
179
|
+
- Create custom error pages for error responses (400, 404, 500, etc.)
|
|
180
|
+
- Render the changelog and show it via the app
|
|
181
|
+
- Display bank account type subtotals on the bank account summaries page
|
|
182
|
+
- Set the current date as the default for transaction form inputs
|
|
183
|
+
- Use the `strict` argument to the built-in `zip` function to strengthen tests
|
|
184
|
+
- Refresh the table of transactions after making a payment on a credit card statement
|
|
185
|
+
- Use SVG to handle long values in account/statement summary boxes; fixes bugs in page rendering (long value overflow) and hover actions not happening because of conflicting overlap with the sidebar
|
|
186
|
+
|
|
187
|
+
<a name="bottom" id="bottom"></a>
|
|
@@ -27,12 +27,23 @@ The package requires a recent version of Python (3.9+).
|
|
|
27
27
|
|
|
28
28
|
## Getting started
|
|
29
29
|
|
|
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):
|
|
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):
|
|
31
31
|
|
|
32
32
|
```
|
|
33
|
-
$ monopyly
|
|
33
|
+
$ monopyly local --browser [--host HOST] [--port PORT]
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
|
|
37
|
+
Other available modes are `development` and `production`, for those looking to either develop the application or host the application on a server.
|
|
38
|
+
|
|
39
|
+
<div class="warning">
|
|
40
|
+
<h5>Use your brain when choosing a mode!</h5>
|
|
41
|
+
<small>
|
|
42
|
+
If you intend to be the only user of the app, served just from your PC, local mode is fine; you will be served well-enough by the built-in Python server, and you do not need to configure secret keys for the application.
|
|
43
|
+
If you plan to host the application, however, <b>DO NOT</b> use local mode (or development mode) and run the app in production mode instead.
|
|
44
|
+
</small>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
36
47
|
By using the `--browser` option in development mode, this will open to an empty homepage with a welcome message.
|
|
37
48
|
|
|
38
49
|
<img class="screenshot" src="monopyly/static/img/about/homepage.png" alt="user homepage" width="800px">
|
|
@@ -5,6 +5,7 @@ Run a development server for the Monopyly app.
|
|
|
5
5
|
from flask import Flask
|
|
6
6
|
|
|
7
7
|
from monopyly.config import DevelopmentConfig, ProductionConfig
|
|
8
|
+
from monopyly.core.errors import render_error_template
|
|
8
9
|
from monopyly.database import SQLAlchemy, register_db_cli_commands
|
|
9
10
|
|
|
10
11
|
|
|
@@ -32,6 +33,7 @@ def create_app(test_config=None):
|
|
|
32
33
|
def init_app(app):
|
|
33
34
|
"""Initialize the app."""
|
|
34
35
|
register_blueprints(app)
|
|
36
|
+
register_errorhandlers(app)
|
|
35
37
|
register_db_cli_commands(app)
|
|
36
38
|
|
|
37
39
|
|
|
@@ -57,3 +59,20 @@ def register_blueprints(app):
|
|
|
57
59
|
app.register_blueprint(auth_bp)
|
|
58
60
|
app.register_blueprint(banking_bp)
|
|
59
61
|
app.register_blueprint(credit_bp)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def register_errorhandlers(app):
|
|
65
|
+
"""Register error handlers with the app."""
|
|
66
|
+
handled_error_codes = [
|
|
67
|
+
400,
|
|
68
|
+
401,
|
|
69
|
+
403,
|
|
70
|
+
404,
|
|
71
|
+
405,
|
|
72
|
+
408,
|
|
73
|
+
418,
|
|
74
|
+
# 425 -- not yet supported
|
|
75
|
+
500,
|
|
76
|
+
]
|
|
77
|
+
for code in handled_error_codes:
|
|
78
|
+
app.register_error_handler(code, render_error_template)
|
|
@@ -9,8 +9,17 @@ from .. import create_app
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class LocalApplication:
|
|
12
|
-
"""
|
|
12
|
+
"""
|
|
13
|
+
An object for running the application locally.
|
|
13
14
|
|
|
15
|
+
This application object will run the Flask application using the
|
|
16
|
+
built-in Python server on localhost, just like the Flask development
|
|
17
|
+
mode. However, it will launch from port 5001 to avoid conflicting
|
|
18
|
+
with other Python servers that may attempt to run on the default
|
|
19
|
+
port 5000.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
mode_name = "local"
|
|
14
23
|
default_port = "5001"
|
|
15
24
|
command = ["flask"]
|
|
16
25
|
|
|
@@ -20,7 +29,7 @@ class LocalApplication:
|
|
|
20
29
|
self._port = port
|
|
21
30
|
if options:
|
|
22
31
|
raise NotImplementedError(
|
|
23
|
-
"Options besides `host` and `port` are not handled in
|
|
32
|
+
f"Options besides `host` and `port` are not handled in {self.mode_name} mode."
|
|
24
33
|
)
|
|
25
34
|
|
|
26
35
|
def run(self):
|
|
@@ -34,14 +43,26 @@ class LocalApplication:
|
|
|
34
43
|
|
|
35
44
|
|
|
36
45
|
class DevelopmentApplication(LocalApplication):
|
|
37
|
-
"""
|
|
46
|
+
"""
|
|
47
|
+
An object for running the application in development mode.
|
|
48
|
+
|
|
49
|
+
This application object will run the Flask application using the
|
|
50
|
+
built-in Python server on localhost, just like the Flask development
|
|
51
|
+
mode.
|
|
52
|
+
"""
|
|
38
53
|
|
|
54
|
+
mode_name = "development"
|
|
39
55
|
default_port = "5000"
|
|
40
56
|
command = LocalApplication.command + ["--debug"]
|
|
41
57
|
|
|
42
58
|
|
|
43
59
|
class ProductionApplication(BaseApplication):
|
|
44
|
-
"""
|
|
60
|
+
"""
|
|
61
|
+
An object for running the application in production mode (via Gunicorn).
|
|
62
|
+
|
|
63
|
+
This application object will run the Flask application using a
|
|
64
|
+
Gunicorn server instead of the built-in Python server.
|
|
65
|
+
"""
|
|
45
66
|
|
|
46
67
|
default_port = "8000"
|
|
47
68
|
_default_worker_count = (multiprocessing.cpu_count() * 2) + 1
|
|
@@ -3,6 +3,7 @@ General form constructions.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
+
from datetime import date
|
|
6
7
|
|
|
7
8
|
from flask_wtf import FlaskForm
|
|
8
9
|
from wtforms.fields import FieldList, FormField, SelectField, StringField, SubmitField
|
|
@@ -34,16 +35,6 @@ class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
|
|
|
34
35
|
"""
|
|
35
36
|
Generate a duplicate prepopulated form.
|
|
36
37
|
|
|
37
|
-
Notes
|
|
38
|
-
-----
|
|
39
|
-
WTForms requires that a form be instantiated in order to be
|
|
40
|
-
able to properly introspect fields. Because of this, this method
|
|
41
|
-
will only return a duplicate form matching the type of the
|
|
42
|
-
form instance used to call it. Using the form's process method
|
|
43
|
-
will not properly handle enumeration of field lists, so it
|
|
44
|
-
can not be used as a replacement for populating an existing
|
|
45
|
-
form.
|
|
46
|
-
|
|
47
38
|
Parameters
|
|
48
39
|
----------
|
|
49
40
|
entry : database.models.Model
|
|
@@ -55,6 +46,16 @@ class EntryForm(FlaskForm, metaclass=AbstractEntryFormMixinMeta):
|
|
|
55
46
|
form : EntryForm
|
|
56
47
|
A duplicate form, prepopulated with the collected database
|
|
57
48
|
information.
|
|
49
|
+
|
|
50
|
+
Notes
|
|
51
|
+
-----
|
|
52
|
+
WTForms requires that a form be instantiated in order to be
|
|
53
|
+
able to properly introspect fields. Because of this, this method
|
|
54
|
+
will only return a duplicate form matching the type of the
|
|
55
|
+
form instance used to call it. Using the form's process method
|
|
56
|
+
will not properly handle enumeration of field lists, so it
|
|
57
|
+
can not be used as a replacement for populating an existing
|
|
58
|
+
form.
|
|
58
59
|
"""
|
|
59
60
|
data = self.gather_entry_data(entry)
|
|
60
61
|
return self.__class__(data=data)
|
|
@@ -165,7 +166,9 @@ class TransactionForm(EntryForm):
|
|
|
165
166
|
return data
|
|
166
167
|
|
|
167
168
|
# Fields pertaining to the transaction
|
|
168
|
-
transaction_date = DateField(
|
|
169
|
+
transaction_date = DateField(
|
|
170
|
+
"Transaction Date", validators=[DataRequired()], default=date.today
|
|
171
|
+
)
|
|
169
172
|
# Subtransactions should be defined as a `FieldList` in a subclass
|
|
170
173
|
subtransactions = None
|
|
171
174
|
submit = SubmitField("Save Transaction")
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Module describing logical core actions (to be used in routes)."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
import markdown
|
|
6
|
+
|
|
7
|
+
|
|
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
|
+
class MarkdownConverter:
|
|
14
|
+
|
|
15
|
+
replacements = {
|
|
16
|
+
"src": [
|
|
17
|
+
["monopyly/static", "/static"],
|
|
18
|
+
],
|
|
19
|
+
"href": [
|
|
20
|
+
["README.md", '{{ url_for("core.about") }}'],
|
|
21
|
+
["CHANGELOG.md", '{{ url_for("core.changelog") }}'],
|
|
22
|
+
],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def convert(cls, markdown_path, title, id_="", class_="", extra_content=""):
|
|
27
|
+
"""Given a Markdown file, convert it to a renderable HTML template."""
|
|
28
|
+
raw_markdown = cls._read_markdown(markdown_path)
|
|
29
|
+
html_content = cls._convert_markdown_to_html(raw_markdown)
|
|
30
|
+
return cls._generate_html_template(
|
|
31
|
+
html_content, title, id_=id_, class_=class_, extra_content=extra_content
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def _read_markdown(markdown_path):
|
|
36
|
+
with markdown_path.open(encoding="utf-8") as markdown_file:
|
|
37
|
+
raw_markdown = markdown_file.read()
|
|
38
|
+
return raw_markdown
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def _convert_markdown_to_html(cls, raw_markdown):
|
|
42
|
+
raw_html = markdown.markdown(raw_markdown, extensions=["fenced_code"])
|
|
43
|
+
return cls._replace_links(raw_html)
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def _replace_links(cls, raw_html):
|
|
47
|
+
html = raw_html
|
|
48
|
+
for tag, pairs in cls.replacements.items():
|
|
49
|
+
for original, replacement in pairs:
|
|
50
|
+
html = html.replace(f'{tag}="{original}', f'{tag}="{replacement}')
|
|
51
|
+
return html
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _generate_html_template(content, title, id_="", class_="", extra_content=""):
|
|
55
|
+
# Format the HTML as a valid Jinja template
|
|
56
|
+
html_template = (
|
|
57
|
+
'{% extends "layout.html" %}'
|
|
58
|
+
"{% block title %}"
|
|
59
|
+
f" {title}"
|
|
60
|
+
"{% endblock %}"
|
|
61
|
+
"{% block content %}"
|
|
62
|
+
f' <div id="{id_}" class="{class_}">'
|
|
63
|
+
f" {content}"
|
|
64
|
+
f" {extra_content}"
|
|
65
|
+
"{% endblock %}"
|
|
66
|
+
)
|
|
67
|
+
return html_template
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def convert_readme_to_html_template(readme_path):
|
|
71
|
+
"""Given a README file in Markdown, convert it to a renderable HTML template."""
|
|
72
|
+
return MarkdownConverter.convert(
|
|
73
|
+
readme_path,
|
|
74
|
+
title="About",
|
|
75
|
+
id_="readme",
|
|
76
|
+
class_="about",
|
|
77
|
+
extra_content=(
|
|
78
|
+
'<div class="resource-links">'
|
|
79
|
+
" <h2>Links</h2>"
|
|
80
|
+
' <p><a href="{{ url_for("core.story") }}">Story</a></p>'
|
|
81
|
+
' <p><a href="{{ url_for("core.credits") }}">Credits</a></p>'
|
|
82
|
+
"</div>"
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def convert_changelog_to_html_template(changelog_path):
|
|
88
|
+
"""Given a CHANGELOG file in Markdown, convert it to a renderable HTML template."""
|
|
89
|
+
return MarkdownConverter.convert(
|
|
90
|
+
changelog_path,
|
|
91
|
+
title="Changes",
|
|
92
|
+
id_="changelog",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def determine_summary_balance_svg_viewbox_width(currency_value):
|
|
97
|
+
"""
|
|
98
|
+
Determine the width of the SVG viewBox attribute displayed in summary boxes.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
currency_value : str
|
|
103
|
+
A currency value, displayed in the format output by the
|
|
104
|
+
`core.filters.make_currency` filter function.
|
|
105
|
+
"""
|
|
106
|
+
# Set the per-character width contributions
|
|
107
|
+
digit_width = 55
|
|
108
|
+
punctuation_width = 25
|
|
109
|
+
spacing_width = 25
|
|
110
|
+
# Count the number of commas and non-comma characters in the non-decimal portion
|
|
111
|
+
nondecimal_value = currency_value.rsplit(".", maxsplit=1)[0]
|
|
112
|
+
comma_count = nondecimal_value.count(",")
|
|
113
|
+
digit_count = len(nondecimal_value) - comma_count
|
|
114
|
+
# Width is the total of the following subcomponents
|
|
115
|
+
svg_currency_width_subcomponents = [
|
|
116
|
+
digit_width * digit_count, # ------------- total width of digits/sign
|
|
117
|
+
punctuation_width * comma_count, # ------- total width of commas
|
|
118
|
+
punctuation_width + (2 * digit_width), # - width of the decimal section
|
|
119
|
+
digit_width + spacing_width, # ----------- width of the dollar sign and spacing
|
|
120
|
+
]
|
|
121
|
+
return max(400, sum(svg_currency_width_subcomponents))
|
|
@@ -5,6 +5,7 @@ Filters defined for the application.
|
|
|
5
5
|
from datetime import date
|
|
6
6
|
from importlib import import_module
|
|
7
7
|
|
|
8
|
+
from .actions import determine_summary_balance_svg_viewbox_width
|
|
8
9
|
from .blueprint import bp
|
|
9
10
|
|
|
10
11
|
|
|
@@ -19,6 +20,15 @@ def inject_global_template_variables():
|
|
|
19
20
|
return template_globals
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
@bp.app_context_processor
|
|
24
|
+
def inject_utility_functions():
|
|
25
|
+
"""Inject utility functions globally into the template context."""
|
|
26
|
+
utility_functions = {
|
|
27
|
+
"calculate_summary_balance_width": determine_summary_balance_svg_viewbox_width,
|
|
28
|
+
}
|
|
29
|
+
return utility_functions
|
|
30
|
+
|
|
31
|
+
|
|
22
32
|
def _display_version():
|
|
23
33
|
"""Show the version (without commit information)."""
|
|
24
34
|
try:
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Filters defined for the application.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from math import floor, log10
|
|
6
|
+
|
|
5
7
|
from .blueprint import bp
|
|
6
8
|
|
|
7
9
|
|
|
@@ -32,8 +34,8 @@ def make_ordinal(integer):
|
|
|
32
34
|
|
|
33
35
|
Notes
|
|
34
36
|
-----
|
|
35
|
-
This function is an adaptation of the one proposed by Stack Overflow
|
|
36
|
-
Florian Brucker (https://stackoverflow.com/a/50992575/8754471).
|
|
37
|
+
This function is an adaptation of the one proposed by Stack Overflow
|
|
38
|
+
user Florian Brucker (https://stackoverflow.com/a/50992575/8754471).
|
|
37
39
|
|
|
38
40
|
Parameters
|
|
39
41
|
----------
|
|
@@ -5,15 +5,18 @@ Routes for core functionality.
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from flask import g, render_template, render_template_string, session
|
|
8
|
+
from werkzeug.exceptions import abort
|
|
8
9
|
|
|
9
10
|
from ..auth.tools import login_required
|
|
10
11
|
from ..banking.accounts import BankAccountHandler
|
|
11
12
|
from ..banking.banks import BankHandler
|
|
12
13
|
from ..credit.cards import CreditCardHandler
|
|
13
14
|
from ..credit.statements import CreditStatementHandler
|
|
14
|
-
from .actions import
|
|
15
|
+
from .actions import convert_changelog_to_html_template, convert_readme_to_html_template
|
|
15
16
|
from .blueprint import bp
|
|
16
17
|
|
|
18
|
+
APP_ROOT_DIR = Path(__file__).parents[1]
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
@bp.route("/")
|
|
19
22
|
def index():
|
|
@@ -40,7 +43,7 @@ def index():
|
|
|
40
43
|
session["show_homepage_block"] = True
|
|
41
44
|
bank_accounts, active_cards = None, None
|
|
42
45
|
return render_template(
|
|
43
|
-
"index.html", bank_accounts=bank_accounts, cards=active_cards
|
|
46
|
+
"core/index.html", bank_accounts=bank_accounts, cards=active_cards
|
|
44
47
|
)
|
|
45
48
|
|
|
46
49
|
|
|
@@ -53,22 +56,27 @@ def hide_homepage_block():
|
|
|
53
56
|
|
|
54
57
|
@bp.route("/about")
|
|
55
58
|
def about():
|
|
56
|
-
readme_path =
|
|
57
|
-
|
|
58
|
-
raw_readme_text = readme_file.read()
|
|
59
|
-
about_page_template = format_readme_as_html_template(raw_readme_text)
|
|
59
|
+
readme_path = APP_ROOT_DIR / "README.md"
|
|
60
|
+
about_page_template = convert_readme_to_html_template(readme_path)
|
|
60
61
|
return render_template_string(about_page_template)
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
@bp.route("/changelog")
|
|
65
|
+
def changelog():
|
|
66
|
+
changelog_path = APP_ROOT_DIR / "CHANGELOG.md"
|
|
67
|
+
changelog_page_template = convert_changelog_to_html_template(changelog_path)
|
|
68
|
+
return render_template_string(changelog_page_template)
|
|
69
|
+
|
|
70
|
+
|
|
63
71
|
@bp.route("/story")
|
|
64
72
|
@login_required
|
|
65
73
|
def story():
|
|
66
|
-
return render_template("story.html")
|
|
74
|
+
return render_template("core/story.html")
|
|
67
75
|
|
|
68
76
|
|
|
69
77
|
@bp.route("/credits")
|
|
70
78
|
def credits():
|
|
71
|
-
return render_template("credits.html")
|
|
79
|
+
return render_template("core/credits.html")
|
|
72
80
|
|
|
73
81
|
|
|
74
82
|
@bp.route("/profile")
|
|
@@ -76,4 +84,9 @@ def credits():
|
|
|
76
84
|
def load_profile():
|
|
77
85
|
banks = BankHandler.get_banks()
|
|
78
86
|
# Return banks as a list to allow multiple reuse
|
|
79
|
-
return render_template("profile.html", banks=list(banks))
|
|
87
|
+
return render_template("core/profile.html", banks=list(banks))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@bp.route("/change_password")
|
|
91
|
+
def change_password():
|
|
92
|
+
abort(418)
|