monopyly 1.5.1__py3-none-any.whl → 1.5.2__py3-none-any.whl
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/CHANGELOG.md +12 -0
- monopyly/README.md +2 -2
- monopyly/__init__.py +22 -27
- monopyly/_version.py +2 -2
- monopyly/auth/routes.py +1 -1
- monopyly/banking/accounts.py +3 -3
- monopyly/banking/banks.py +1 -1
- monopyly/banking/routes.py +1 -1
- monopyly/banking/transactions.py +14 -5
- monopyly/common/transactions.py +17 -7
- monopyly/core/actions.py +0 -7
- monopyly/credit/accounts.py +1 -1
- monopyly/credit/cards.py +7 -3
- monopyly/credit/routes.py +31 -28
- monopyly/credit/statements.py +1 -1
- monopyly/credit/transactions/_transactions.py +18 -5
- monopyly/credit/transactions/activity/reconciliation.py +20 -1
- monopyly/database/__init__.py +1 -56
- monopyly/database/models.py +181 -273
- monopyly/static/js/create-balance-chart.js +1 -1
- monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html +1 -1
- monopyly/templates/credit/transactions_table/condensed_row_content.html +0 -1
- {monopyly-1.5.1.dist-info → monopyly-1.5.2.dist-info}/METADATA +9 -10
- {monopyly-1.5.1.dist-info → monopyly-1.5.2.dist-info}/RECORD +28 -33
- monopyly-1.5.2.dist-info/entry_points.txt +2 -0
- monopyly/cli/apps.py +0 -108
- monopyly/cli/launch.py +0 -135
- monopyly/config/__init__.py +0 -1
- monopyly/config/default_settings.py +0 -56
- monopyly/config/settings.py +0 -59
- monopyly-1.5.1.dist-info/entry_points.txt +0 -2
- {monopyly-1.5.1.dist-info → monopyly-1.5.2.dist-info}/WHEEL +0 -0
- {monopyly-1.5.1.dist-info → monopyly-1.5.2.dist-info}/licenses/COPYING +0 -0
- {monopyly-1.5.1.dist-info → monopyly-1.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: monopyly
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: A homemade personal finance manager.
|
|
5
5
|
Project-URL: Download, https://pypi.org/project/monopyly
|
|
6
6
|
Project-URL: Homepage, http://monopyly.com
|
|
@@ -23,16 +23,15 @@ Classifier: Topic :: Office/Business :: Financial
|
|
|
23
23
|
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
24
24
|
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
25
25
|
Requires-Python: <3.11,>=3.10
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist: flask-wtf==1.2.
|
|
28
|
-
Requires-Dist: flask==3.
|
|
29
|
-
Requires-Dist: fuisce==1.0.2
|
|
26
|
+
Requires-Dist: dry-foundation==1.3.0
|
|
27
|
+
Requires-Dist: flask-wtf==1.2.2
|
|
28
|
+
Requires-Dist: flask==3.1.2
|
|
30
29
|
Requires-Dist: gunicorn==23.0.0
|
|
31
|
-
Requires-Dist: markdown==3.
|
|
30
|
+
Requires-Dist: markdown==3.9
|
|
32
31
|
Requires-Dist: nltk==3.9.1
|
|
33
32
|
Requires-Dist: python-dateutil==2.9.0
|
|
34
|
-
Requires-Dist: rich==
|
|
35
|
-
Requires-Dist: sqlalchemy==2.0.
|
|
33
|
+
Requires-Dist: rich==14.1.0
|
|
34
|
+
Requires-Dist: sqlalchemy==2.0.43
|
|
36
35
|
Description-Content-Type: text/markdown
|
|
37
36
|
|
|
38
37
|
<div id="header">
|
|
@@ -64,10 +63,10 @@ The package requires a recent version of Python (3.10+).
|
|
|
64
63
|
|
|
65
64
|
## Getting started
|
|
66
65
|
|
|
67
|
-
Once the package is properly installed,
|
|
66
|
+
Once the package is properly installed, launch the app in local mode from the command line (the default options should be sensible, but you may customize the host and port if necessary):
|
|
68
67
|
|
|
69
68
|
```
|
|
70
|
-
$ monopyly local --browser [--host HOST] [--port PORT]
|
|
69
|
+
$ monopyly launch local --browser [--host HOST] [--port PORT]
|
|
71
70
|
```
|
|
72
71
|
|
|
73
72
|
Local mode indicates that the app is just going to be run using a locally hosted server, accessible to just your machine.
|
|
@@ -1,53 +1,48 @@
|
|
|
1
|
-
monopyly/CHANGELOG.md,sha256=
|
|
2
|
-
monopyly/README.md,sha256=
|
|
3
|
-
monopyly/__init__.py,sha256=
|
|
4
|
-
monopyly/_version.py,sha256=
|
|
1
|
+
monopyly/CHANGELOG.md,sha256=n8WvbwwvHWzmOzhiSYAIAHzk_E7Tb0zqWkrbVfrKFWI,8802
|
|
2
|
+
monopyly/README.md,sha256=zmgiCKvJWPTQManbRHiKU9mcDz8FCMu5vYDZLbRIcUE,9038
|
|
3
|
+
monopyly/__init__.py,sha256=2VZ89v-CbtDbuIL6TnvXxgUm3CcANWTEwO91GHGwV_U,2027
|
|
4
|
+
monopyly/_version.py,sha256=P-JlE1bO3FGbbntvKHqxjgKk7ORvjFhybkK-h8R2s5g,411
|
|
5
5
|
monopyly/auth/actions.py,sha256=uwXg0LVz3QVJxKkiI-YCJMT8OSjUK7R-aEy-XTM0Ghs,702
|
|
6
6
|
monopyly/auth/blueprint.py,sha256=sFbmTvSBhjv1pn2gJLH5J6zanPLuemmWbgEj5ZKCiTY,244
|
|
7
|
-
monopyly/auth/routes.py,sha256=
|
|
7
|
+
monopyly/auth/routes.py,sha256=b2heNwtDe91aJEEEQh1ucWXmJhJECB-qAYO-GfQQiVc,3383
|
|
8
8
|
monopyly/auth/tools.py,sha256=CDMcvRY0A-cJxvKUKOUkDzZHDxveHl-8TzhKyOzsXEs,792
|
|
9
|
-
monopyly/banking/accounts.py,sha256=
|
|
9
|
+
monopyly/banking/accounts.py,sha256=guOC-yBcTCr74vvxTFZyY7oeBDizKV6UDGTpxayvUL4,9864
|
|
10
10
|
monopyly/banking/actions.py,sha256=NvButozWJNRuNIiyM-Gy88XLTpIjNcEGSQcLMfEwOUg,3258
|
|
11
|
-
monopyly/banking/banks.py,sha256=
|
|
11
|
+
monopyly/banking/banks.py,sha256=a-GaBtLu2v-8Bl5Q3lOVKUhK9dgpHdsr1sMs0uYcLOg,1751
|
|
12
12
|
monopyly/banking/blueprint.py,sha256=PDsqe4DmiT7cDYW9IxxTfizu9Lqol1Lhp7ajMK6fUuI,255
|
|
13
13
|
monopyly/banking/filters.py,sha256=wCsAUh592B5BmGFC9NqmrJZ0NdQZi21hvy0QsKqhgLg,685
|
|
14
14
|
monopyly/banking/forms.py,sha256=p-YGZgdKugk3VKILLiQNEH5KQmXop5G8P1Bs4sUqbK0,10506
|
|
15
|
-
monopyly/banking/routes.py,sha256=
|
|
16
|
-
monopyly/banking/transactions.py,sha256=
|
|
17
|
-
monopyly/
|
|
18
|
-
monopyly/cli/launch.py,sha256=nPCW3Yx4e35sAQ_K6YH3pqmWCdvnt-NHG90hUV7QKPg,4103
|
|
19
|
-
monopyly/common/transactions.py,sha256=AEe86_XoDHzTaGEN5ByzPo0dQD6tA9dq3M9TCHMYPvE,18080
|
|
15
|
+
monopyly/banking/routes.py,sha256=pjIKqflTaOHA4jkZRBQdvVOnCyLR-s-qPZvr0xiilXI,8240
|
|
16
|
+
monopyly/banking/transactions.py,sha256=_jZFKuY9zIus1yao_Y31lg30u3-7uvv2z7EDjKLSioY,8886
|
|
17
|
+
monopyly/common/transactions.py,sha256=V7YcFY0r8kl1tdy9oFSGPsQ-C3P9k3m-k_D-NiChXGI,18321
|
|
20
18
|
monopyly/common/utils.py,sha256=BjXhfNXGWkAPt-ebldvmZ2z00P8MhKJzjxrJJ6mzRQY,5867
|
|
21
19
|
monopyly/common/forms/__init__.py,sha256=6iFTlcoaQmCbrjS1KfFhWXQUfyqnA-vKPMVxLyJMlXY,120
|
|
22
20
|
monopyly/common/forms/_forms.py,sha256=wlLY9pBtFYcOEnFL_dnt62JFtbs88u_B9LXN2eTe_PA,10130
|
|
23
21
|
monopyly/common/forms/fields.py,sha256=XgvkfszpUAZyIs-osHGFADmzuo0Ni_e78NXtG-grBdI,4082
|
|
24
22
|
monopyly/common/forms/utils.py,sha256=QbA6253xL4J01DQNQXL-BM13MgAr_HAM93H8a9Nwh6k,5660
|
|
25
23
|
monopyly/common/forms/validators.py,sha256=C5_NN8ktvBw6MrSXwv_x9_SQohCNmnp4brNQFELHBlI,1205
|
|
26
|
-
monopyly/
|
|
27
|
-
monopyly/config/default_settings.py,sha256=jtWz4ElasnAbfBCiRfbsYyy--L6N6jTOGRh3PNWnVj4,1829
|
|
28
|
-
monopyly/config/settings.py,sha256=YEctSb8mRDvuMA1tTs1N7862Z2bMY7DPf0xo4Ist2pg,1984
|
|
29
|
-
monopyly/core/actions.py,sha256=jkrR5bBN1RG1S4Gtumnk51TkCNf4gfsUZkTi8LKHVHA,4172
|
|
24
|
+
monopyly/core/actions.py,sha256=M0guQPwGacA7aHBhYh8FRN6kXdi6wK2VSsiLoWWTsjU,4018
|
|
30
25
|
monopyly/core/blueprint.py,sha256=pZyK8ly0UR1BbHx4xwYk1UzakzkVCS8wiMSM8XdCG2U,260
|
|
31
26
|
monopyly/core/context_processors.py,sha256=ByQGQpOJIpxG3WM3Kv34LbBY0H8HVeR81TDYkdOwJgs,1166
|
|
32
27
|
monopyly/core/errors.py,sha256=xF4gA3Hv99op3EMV_f2CskHWVLrV-9ckaHKJMWR2h7o,200
|
|
33
28
|
monopyly/core/filters.py,sha256=uBkNjQyqW-9RkiQG23vngnI1MZXFeh4Rhe5MQqklAEQ,1284
|
|
34
29
|
monopyly/core/internal_transactions.py,sha256=PImeViMU9rdDDPLXld1xC6bdAoulzRDr0zci4pUQY8I,459
|
|
35
30
|
monopyly/core/routes.py,sha256=FhXL8JrqsumxdUiGsmzSG7fim4Kz5rDxr0Az-3SmI90,2639
|
|
36
|
-
monopyly/credit/accounts.py,sha256=
|
|
31
|
+
monopyly/credit/accounts.py,sha256=6BvQiPvFMWqdDHp-heYXmcjgzzaGMvrHmwCoNQ3CcvM,1904
|
|
37
32
|
monopyly/credit/actions.py,sha256=eNtowA16zfP8gmo93H9_LbARJ0JKxhihqy3OhX0bwpQ,7211
|
|
38
33
|
monopyly/credit/blueprint.py,sha256=XbrMhtyCp2732uWPB2kjn_W8P8pH8RVTwo9P8Pt--Is,251
|
|
39
|
-
monopyly/credit/cards.py,sha256=
|
|
34
|
+
monopyly/credit/cards.py,sha256=8fOK_Wpgdn484OV95_JJ7hX_oyFDTHkDhETuRdKfVWU,5709
|
|
40
35
|
monopyly/credit/forms.py,sha256=aGARysyBNY04hF43IgbwZfBB6ByG51oqbOivmBXOxsk,13614
|
|
41
|
-
monopyly/credit/routes.py,sha256=
|
|
42
|
-
monopyly/credit/statements.py,sha256=
|
|
36
|
+
monopyly/credit/routes.py,sha256=LZznSwjmyaHFaeHrKAq4zNePixRXn9dLBpy1LUQi2bk,22262
|
|
37
|
+
monopyly/credit/statements.py,sha256=_kJDqF8WT_bFaVcuklSXiqaLUtP7XSCtVmCfCEILo34,7225
|
|
43
38
|
monopyly/credit/transactions/__init__.py,sha256=1Exn6T--HtwNCHzS_hDJ0ba7qCB7w_8C-yY3KSxvFKg,165
|
|
44
|
-
monopyly/credit/transactions/_transactions.py,sha256=
|
|
39
|
+
monopyly/credit/transactions/_transactions.py,sha256=cdatQbL2bUtUjnuoRgdcSXvyG8t0hA4YoDstpk83XEE,9692
|
|
45
40
|
monopyly/credit/transactions/activity/__init__.py,sha256=Iq5KLXdCf1UCfM9qDRXbYCaz1NmgJMCApK4kGDNJISo,139
|
|
46
41
|
monopyly/credit/transactions/activity/data.py,sha256=6ND7fz5L1aH2g_2drGz2Xf7maUaJ6RyItLdBeWXBIlA,5849
|
|
47
42
|
monopyly/credit/transactions/activity/parser.py,sha256=yBEP3jG36iixhA0KN8TXdvE55Jp54L6TMR8xlOjqJMY,11624
|
|
48
|
-
monopyly/credit/transactions/activity/reconciliation.py,sha256=
|
|
49
|
-
monopyly/database/__init__.py,sha256=
|
|
50
|
-
monopyly/database/models.py,sha256=
|
|
43
|
+
monopyly/credit/transactions/activity/reconciliation.py,sha256=Fs6cQfEbzzYD_PkEBSyHUHJRD4flgOViQGMEOtBidSs,19374
|
|
44
|
+
monopyly/database/__init__.py,sha256=I3iWKiK-3o6uODwFeLOeDO4nGCfNiwvooth9qZfJ5Ok,1639
|
|
45
|
+
monopyly/database/models.py,sha256=O51vxQ_bexli0YTylMqaePOUy_EglFkrk78MSJMTzo8,14046
|
|
51
46
|
monopyly/database/preloads.sql,sha256=KBS_WYRofFdOVN-IgHzZhfyJrY6ZOwHeExN-fQr3htU,363
|
|
52
47
|
monopyly/database/schema.sql,sha256=cdl9b5CnYrnQ-lck147GtwArx_JJbX1vF9YYLivTeyw,4660
|
|
53
48
|
monopyly/database/views.sql,sha256=UTO2QRkVTfQ12bMVkR-O6Qv80DvYo8hngPHn2A1_1F8,3853
|
|
@@ -113,7 +108,7 @@ monopyly/static/js/add-subtransaction.js,sha256=BoLvpahd__YHOwodqJXg10OCY7CJJX0k
|
|
|
113
108
|
monopyly/static/js/add-transfer.js,sha256=_ywQju13wGWTeZMqJXJzVbfquQJhcbDnJQ9EC3R01xM,1384
|
|
114
109
|
monopyly/static/js/autocomplete-transaction.js,sha256=pZQ7v04WEHcJ7riPparhdF70W3ZDSz9Z0j0N_OVgZ9k,2753
|
|
115
110
|
monopyly/static/js/bind-tag-actions.js,sha256=ReOHoY9Ngsqe7rvH0x0TvgNNbmEuAOKx3HIdLSYXMhk,3619
|
|
116
|
-
monopyly/static/js/create-balance-chart.js,sha256=
|
|
111
|
+
monopyly/static/js/create-balance-chart.js,sha256=tQasqDQBJFmK5KdLXEz3hdXMRvldBMhZGKGaBRigZiY,2447
|
|
117
112
|
monopyly/static/js/create-category-chart.js,sha256=h17FlFj2eo1MK2VXwuw7U58dWDj4iukYuLnllMOuGhU,514
|
|
118
113
|
monopyly/static/js/define-filter.js,sha256=nz64sha6G9QXOyVEqxd_xYK0F_fXxzXxIHiQfpvUo7A,1133
|
|
119
114
|
monopyly/static/js/display-new-account-type-inputs.js,sha256=iHh7zSnyHc_-FIPO1Uuffjd8mFvfqfECMzO34FB_WF4,555
|
|
@@ -212,7 +207,7 @@ monopyly/templates/credit/card_form/transfer_statement_inquiry.html,sha256=Luxie
|
|
|
212
207
|
monopyly/templates/credit/card_graphic/card_back.html,sha256=gfr3975mziF0PCd_f2Jy83Y4ktFeFnZ1icVVpiVwoQA,889
|
|
213
208
|
monopyly/templates/credit/card_graphic/card_front.html,sha256=I53u0WvrQj-m2D-fkcga0GRketfpMuMXNM6Rs0e5CNA,679
|
|
214
209
|
monopyly/templates/credit/statement_reconciliation/discrepant_records.html,sha256=IMnLShpED8CcXYA3jrQoz4Fc2v0s6i1Fl1OAUY9pjGw,837
|
|
215
|
-
monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html,sha256=
|
|
210
|
+
monopyly/templates/credit/statement_reconciliation/statement_reconciliation_inquiry.html,sha256=4c8kZXF6-A2Q5wVgr0rjLjfoEXTIl646QpoVJ99DfLw,785
|
|
216
211
|
monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html,sha256=B1mWqXvLIc9tPod-24HsVWVCsIKp6vBp71a5Mo322gA,2779
|
|
217
212
|
monopyly/templates/credit/statement_reconciliation/summary.html,sha256=yDNREcWbCbvmVRSbMVst2YnyLoLrd-5sgG2bWzsNAF4,1804
|
|
218
213
|
monopyly/templates/credit/statement_reconciliation/unrecorded_activities.html,sha256=wCgqgaih413A9RkyARGIQ9OMqaA2POkL57P-wkfG47k,836
|
|
@@ -222,13 +217,13 @@ monopyly/templates/credit/transaction_form/transaction_form.html,sha256=_2tYKMqm
|
|
|
222
217
|
monopyly/templates/credit/transaction_form/transaction_form_page.html,sha256=x_gwHkqWVuiVIm2Ol_Cv7ce2EODJS1Ktn5S2kI4gG1o,795
|
|
223
218
|
monopyly/templates/credit/transaction_form/transaction_form_page_new.html,sha256=zIKYO4NyuzxFQltbyJg2FnfOinMHessSdaMpuBTbNxI,312
|
|
224
219
|
monopyly/templates/credit/transaction_form/transaction_form_page_update.html,sha256=VDlNFZPvuwNOfD53iPkpoi4WOyZYRHeIVy_7gpTJuew,513
|
|
225
|
-
monopyly/templates/credit/transactions_table/condensed_row_content.html,sha256
|
|
220
|
+
monopyly/templates/credit/transactions_table/condensed_row_content.html,sha256=-VVTlxkMZg2r0hIb3gnsX6bbO_Gn70mh1qXoKD0izvM,638
|
|
226
221
|
monopyly/templates/credit/transactions_table/expanded_row_content.html,sha256=SwS-YVDQDv0rHK2g_vLs0RWePKKo5972UEOG1JwuSVI,2061
|
|
227
222
|
monopyly/templates/credit/transactions_table/transaction_field_titles.html,sha256=km-3YEDJaygwcKV-rwYnrE7xF8u4Z7jCBsk0Ia-LO-M,758
|
|
228
223
|
monopyly/templates/credit/transactions_table/transactions.html,sha256=tcppv0qNV-Qq6U5jfxoNyGKPXmyV9h9nR6rw4jXfBsY,481
|
|
229
|
-
monopyly-1.5.
|
|
230
|
-
monopyly-1.5.
|
|
231
|
-
monopyly-1.5.
|
|
232
|
-
monopyly-1.5.
|
|
233
|
-
monopyly-1.5.
|
|
234
|
-
monopyly-1.5.
|
|
224
|
+
monopyly-1.5.2.dist-info/METADATA,sha256=rmlxVy7c1jVhghQgY0YrsJqNnIMY_bMzKncgXfVRzko,10967
|
|
225
|
+
monopyly-1.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
226
|
+
monopyly-1.5.2.dist-info/entry_points.txt,sha256=RdDBtx_pqZDsQR8T5RmdWgrjfplhsEMz-sOaRkgiBDo,43
|
|
227
|
+
monopyly-1.5.2.dist-info/licenses/COPYING,sha256=5X8cMguM-HmKfS_4Om-eBqM6A1hfbgZf6pfx2G24QFI,35150
|
|
228
|
+
monopyly-1.5.2.dist-info/licenses/LICENSE,sha256=94rIicMccmTPhqXiRLV9JsU8P2ocMuEUUtUpp6LPKiE,253
|
|
229
|
+
monopyly-1.5.2.dist-info/RECORD,,
|
monopyly/cli/apps.py
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"""Application objects for running the app via the CLI."""
|
|
2
|
-
|
|
3
|
-
import multiprocessing
|
|
4
|
-
import subprocess
|
|
5
|
-
|
|
6
|
-
from gunicorn.app.base import BaseApplication
|
|
7
|
-
|
|
8
|
-
from .. import create_app
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class LocalApplication:
|
|
12
|
-
"""
|
|
13
|
-
An object for running the application locally.
|
|
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"
|
|
23
|
-
default_port = "5001"
|
|
24
|
-
_debug = None
|
|
25
|
-
|
|
26
|
-
def __init__(self, host=None, port=None, **options):
|
|
27
|
-
"""Initialize the application in development mode."""
|
|
28
|
-
self._host = host
|
|
29
|
-
self._port = port or self.default_port
|
|
30
|
-
if options:
|
|
31
|
-
raise NotImplementedError(
|
|
32
|
-
f"Options besides `host` and `port` are not handled in {self.mode_name} mode."
|
|
33
|
-
)
|
|
34
|
-
self.application = create_app(debug=self._debug)
|
|
35
|
-
|
|
36
|
-
def run(self):
|
|
37
|
-
"""Run the Monopyly application in development mode."""
|
|
38
|
-
self.application.run(
|
|
39
|
-
host=self._host,
|
|
40
|
-
port=self._port,
|
|
41
|
-
debug=self._debug,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class DevelopmentApplication(LocalApplication):
|
|
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
|
-
"""
|
|
53
|
-
|
|
54
|
-
mode_name = "development"
|
|
55
|
-
default_port = None # traditionally 5000
|
|
56
|
-
_debug = True
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class ProductionApplication(BaseApplication):
|
|
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
|
-
"""
|
|
66
|
-
|
|
67
|
-
default_port = None # traditionally 8000
|
|
68
|
-
_default_worker_count = (multiprocessing.cpu_count() * 2) + 1
|
|
69
|
-
|
|
70
|
-
def __init__(self, host=None, port=None, **options):
|
|
71
|
-
"""Initialize the application in production mode."""
|
|
72
|
-
if port and not host:
|
|
73
|
-
raise ValueError("A host must be specified when the port is given.")
|
|
74
|
-
self._host = host
|
|
75
|
-
self._port = port or self.default_port
|
|
76
|
-
self.options = options
|
|
77
|
-
self.options["bind"] = self._determine_binding(options.get("bind"))
|
|
78
|
-
self.options.setdefault("workers", self._default_worker_count)
|
|
79
|
-
self.application = create_app()
|
|
80
|
-
super().__init__()
|
|
81
|
-
|
|
82
|
-
def _determine_binding(self, bind_option):
|
|
83
|
-
# Parse any socket binding options
|
|
84
|
-
if self._host and bind_option:
|
|
85
|
-
raise ValueError(
|
|
86
|
-
"The `host` may not be specified directly if the `bind` option is used."
|
|
87
|
-
)
|
|
88
|
-
if self._host:
|
|
89
|
-
bind_values = [self._host]
|
|
90
|
-
if self._port:
|
|
91
|
-
bind_values.append(self._port)
|
|
92
|
-
bind_option = ":".join(bind_values)
|
|
93
|
-
return bind_option
|
|
94
|
-
|
|
95
|
-
def load_config(self):
|
|
96
|
-
config = {
|
|
97
|
-
key: value
|
|
98
|
-
for key, value in self.options.items()
|
|
99
|
-
if key in self.cfg.settings and value is not None
|
|
100
|
-
}
|
|
101
|
-
for key, value in config.items():
|
|
102
|
-
self.cfg.set(key.lower(), value)
|
|
103
|
-
|
|
104
|
-
def load(self):
|
|
105
|
-
return self.application
|
|
106
|
-
|
|
107
|
-
def run(self, *args, **kwargs):
|
|
108
|
-
return super().run(*args, **kwargs)
|
monopyly/cli/launch.py
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
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 (to specify the app to use)
|
|
20
|
-
os.environ["FLASK_APP"] = "monopyly"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def main(mode, host=None, port=None, backup=False, browser=False):
|
|
24
|
-
app_launcher = Launcher(mode, host=host, port=port)
|
|
25
|
-
# Initialize the database and run the app
|
|
26
|
-
app_launcher.initialize_database()
|
|
27
|
-
if backup:
|
|
28
|
-
app_launcher.backup_database()
|
|
29
|
-
app_launcher.launch()
|
|
30
|
-
if mode in ("development", "local"):
|
|
31
|
-
# Enable browser viewing in development mode
|
|
32
|
-
if browser:
|
|
33
|
-
app_launcher.open_browser(delay=1)
|
|
34
|
-
# Wait for the exit command to stop
|
|
35
|
-
app_launcher.wait_for_exit()
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def main_cli():
|
|
39
|
-
"""Run the app as a command line program."""
|
|
40
|
-
args = parse_arguments()
|
|
41
|
-
main(
|
|
42
|
-
args.mode,
|
|
43
|
-
host=args.host,
|
|
44
|
-
port=args.port,
|
|
45
|
-
backup=args.backup,
|
|
46
|
-
browser=args.browser,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def parse_arguments():
|
|
51
|
-
parser = argparse.ArgumentParser(description=__doc__)
|
|
52
|
-
parser.add_argument("--host", help="the host address where the app will be run")
|
|
53
|
-
parser.add_argument("--port", help="the port where the app will be accessible")
|
|
54
|
-
parser.add_argument(
|
|
55
|
-
"--backup",
|
|
56
|
-
action="store_true",
|
|
57
|
-
help="a flag indicating if the database should be backed up",
|
|
58
|
-
)
|
|
59
|
-
parser.add_argument(
|
|
60
|
-
"--browser",
|
|
61
|
-
action="store_true",
|
|
62
|
-
help=(
|
|
63
|
-
"a flag indicating if a new browser window should be opened (development "
|
|
64
|
-
"and local modes only)"
|
|
65
|
-
),
|
|
66
|
-
)
|
|
67
|
-
parser.add_argument(
|
|
68
|
-
"mode",
|
|
69
|
-
help="the runtime mode for the app",
|
|
70
|
-
choices=["development", "local", "production"],
|
|
71
|
-
)
|
|
72
|
-
return parser.parse_args()
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class Launcher:
|
|
76
|
-
"""A tool to build and execute Flask commands."""
|
|
77
|
-
|
|
78
|
-
_application_types = {
|
|
79
|
-
"development": DevelopmentApplication,
|
|
80
|
-
"local": LocalApplication,
|
|
81
|
-
"production": ProductionApplication,
|
|
82
|
-
}
|
|
83
|
-
_console = Console()
|
|
84
|
-
_exit = Event()
|
|
85
|
-
command = ["flask"]
|
|
86
|
-
|
|
87
|
-
def __init__(self, mode, host=None, port=None):
|
|
88
|
-
app_type = self._application_types[mode]
|
|
89
|
-
if mode == "development":
|
|
90
|
-
self.command = self.command + ["--debug"]
|
|
91
|
-
self.host = host if host else "127.0.0.1"
|
|
92
|
-
self.port = port if port else app_type.default_port
|
|
93
|
-
self.app = app_type(host=self.host, port=self.port)
|
|
94
|
-
|
|
95
|
-
def initialize_database(self):
|
|
96
|
-
"""Run the database initializer."""
|
|
97
|
-
instruction = self.command + ["init-db"]
|
|
98
|
-
self._console.print("[deep_sky_blue1]Initializing the database...")
|
|
99
|
-
subprocess.run(instruction)
|
|
100
|
-
print("\n")
|
|
101
|
-
|
|
102
|
-
def backup_database(self):
|
|
103
|
-
"""Back up the app database."""
|
|
104
|
-
self._console.print("[deep_sky_blue1]Backing up the database...")
|
|
105
|
-
instruction = self.command + ["back-up-db"]
|
|
106
|
-
subprocess.run(instruction)
|
|
107
|
-
print("\n")
|
|
108
|
-
|
|
109
|
-
def launch(self):
|
|
110
|
-
"""Launch the Monopyly application."""
|
|
111
|
-
self._console.print("[deep_sky_blue1]Running the Monopyly application...\n")
|
|
112
|
-
self.app.run()
|
|
113
|
-
|
|
114
|
-
def open_browser(self, delay=0):
|
|
115
|
-
"""Open the default web browser."""
|
|
116
|
-
time.sleep(delay)
|
|
117
|
-
webbrowser.open(f"http://{self.host}:{self.port}/")
|
|
118
|
-
|
|
119
|
-
@classmethod
|
|
120
|
-
def wait_for_exit(cls):
|
|
121
|
-
"""Wait for the exit command (e.g., keyboard interrupt) to be issued."""
|
|
122
|
-
for sig in ("TERM", "HUP", "INT"):
|
|
123
|
-
signal.signal(getattr(signal, "SIG" + sig), cls._quit)
|
|
124
|
-
while not cls._exit.is_set():
|
|
125
|
-
cls._exit.wait(1)
|
|
126
|
-
|
|
127
|
-
@classmethod
|
|
128
|
-
def _quit(cls, signo, _frame):
|
|
129
|
-
"""Send the signal to quit the app."""
|
|
130
|
-
print("\nClosing the Monopyly app...")
|
|
131
|
-
cls._exit.set()
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if __name__ == "__main__":
|
|
135
|
-
main_cli()
|
monopyly/config/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .settings import DevelopmentConfig, ProductionConfig, TestingConfig
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"""Default configuration settings."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
CONFIG_FILENAME = f"monopyly-config.json"
|
|
7
|
-
DEFAULT_CONFIG_PATH = Path("/etc", CONFIG_FILENAME)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Config:
|
|
11
|
-
"""A base configuration object with some default settings."""
|
|
12
|
-
|
|
13
|
-
config_filepaths = [DEFAULT_CONFIG_PATH]
|
|
14
|
-
REGISTRATION = True
|
|
15
|
-
|
|
16
|
-
def __init__(self, db_path=None):
|
|
17
|
-
# Read parameters from the configuration files in order of specificity
|
|
18
|
-
for config_filepath in filter(lambda p: p.exists(), self.config_filepaths):
|
|
19
|
-
self._read_config_json(config_filepath)
|
|
20
|
-
if db_path:
|
|
21
|
-
self.DATABASE = db_path
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def DATABASE(self):
|
|
25
|
-
return self._database
|
|
26
|
-
|
|
27
|
-
@DATABASE.setter
|
|
28
|
-
def DATABASE(self, value):
|
|
29
|
-
# Ensure that the database path is always set as a `pathlib.Path` object
|
|
30
|
-
self._database = Path(value)
|
|
31
|
-
|
|
32
|
-
def _read_config_json(self, config_path):
|
|
33
|
-
# Read keys and values from a configuration JSON
|
|
34
|
-
with config_path.open() as config_json:
|
|
35
|
-
config_mapping = json.load(config_json)
|
|
36
|
-
for key, value in config_mapping.items():
|
|
37
|
-
setattr(self, key, value)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class InstanceBasedConfig(Config):
|
|
41
|
-
"""A base configuration object for app modes using instance directories."""
|
|
42
|
-
|
|
43
|
-
TESTING = False
|
|
44
|
-
db_name = None
|
|
45
|
-
|
|
46
|
-
@classmethod
|
|
47
|
-
def configure_for_instance(cls, instance_path, **kwargs):
|
|
48
|
-
"""Instantiate the app based out of the given instance directory."""
|
|
49
|
-
instance_path = Path(instance_path)
|
|
50
|
-
instance_path.mkdir(parents=True, exist_ok=True)
|
|
51
|
-
cls.config_filepaths = [
|
|
52
|
-
*super().config_filepaths,
|
|
53
|
-
instance_path / CONFIG_FILENAME,
|
|
54
|
-
]
|
|
55
|
-
db_path = Path(instance_path, cls.db_name) if cls.db_name else None
|
|
56
|
-
return cls(db_path=db_path, **kwargs)
|
monopyly/config/settings.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""A module containing objects with various configuration settings."""
|
|
2
|
-
|
|
3
|
-
import warnings
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
from ..database import BASE_DB_NAME
|
|
7
|
-
from .default_settings import Config, InstanceBasedConfig
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ProductionConfig(InstanceBasedConfig):
|
|
11
|
-
"""A configuration object with settings for production."""
|
|
12
|
-
|
|
13
|
-
SECRET_KEY = "INSECURE"
|
|
14
|
-
db_name = BASE_DB_NAME
|
|
15
|
-
|
|
16
|
-
def __init__(self, db_path=None):
|
|
17
|
-
super().__init__(db_path=db_path)
|
|
18
|
-
if self.SECRET_KEY == "INSECURE":
|
|
19
|
-
# Give an alert while the secret key remains insecure
|
|
20
|
-
warnings.formatwarning = lambda msg, *args, **kwargs: f"\n{msg}\n"
|
|
21
|
-
warnings.warn(
|
|
22
|
-
"INSECURE: Production mode has not yet been fully configured; "
|
|
23
|
-
"a secret key is required."
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class DevelopmentConfig(InstanceBasedConfig):
|
|
28
|
-
"""A configuration object with settings for development."""
|
|
29
|
-
|
|
30
|
-
DEBUG = True
|
|
31
|
-
SECRET_KEY = "development key"
|
|
32
|
-
db_name = f"dev-{BASE_DB_NAME}"
|
|
33
|
-
|
|
34
|
-
def __init__(self, db_path=None, preload_data_path=None):
|
|
35
|
-
super().__init__(db_path=db_path)
|
|
36
|
-
self.PRELOAD_DATA_PATH = preload_data_path
|
|
37
|
-
|
|
38
|
-
@classmethod
|
|
39
|
-
def configure_for_instance(cls, instance_path, **kwargs):
|
|
40
|
-
"""Instantiate the app based out of the given instance directory."""
|
|
41
|
-
dev_data_path = Path(instance_path, "dev_data.sql")
|
|
42
|
-
preload_data_path = dev_data_path if dev_data_path.exists() else None
|
|
43
|
-
return super().configure_for_instance(
|
|
44
|
-
instance_path, preload_data_path=preload_data_path, **kwargs
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class TestingConfig(Config):
|
|
49
|
-
"""A configuration object with settings for testing."""
|
|
50
|
-
|
|
51
|
-
TESTING = True
|
|
52
|
-
SECRET_KEY = "testing key"
|
|
53
|
-
DATABASE_INTERFACE_ARGS = ()
|
|
54
|
-
DATABASE_INTERFACE_KWARGS = {}
|
|
55
|
-
WTF_CSRF_ENABLED = False
|
|
56
|
-
|
|
57
|
-
def __init__(self, db_path=None, preload_data_path=None):
|
|
58
|
-
super().__init__(db_path=db_path)
|
|
59
|
-
self.PRELOAD_DATA_PATH = preload_data_path
|
|
File without changes
|
|
File without changes
|
|
File without changes
|