monopyly 1.5.2__py3-none-any.whl → 1.6.1__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 +22 -0
- monopyly/README.md +1 -1
- monopyly/__init__.py +1 -1
- monopyly/_version.py +2 -2
- monopyly/auth/blueprint.py +2 -0
- monopyly/auth/routes.py +1 -2
- monopyly/banking/accounts.py +4 -4
- monopyly/banking/actions.py +20 -17
- monopyly/banking/blueprint.py +2 -0
- monopyly/banking/filters.py +6 -6
- monopyly/banking/forms.py +7 -5
- monopyly/banking/routes.py +71 -9
- monopyly/banking/transactions.py +2 -3
- monopyly/common/forms/__init__.py +8 -0
- monopyly/common/forms/_forms.py +6 -16
- monopyly/common/forms/fields.py +0 -11
- monopyly/common/forms/utils.py +4 -4
- monopyly/common/transactions.py +72 -7
- monopyly/core/actions.py +2 -1
- monopyly/core/blueprint.py +2 -0
- monopyly/core/context_processors.py +3 -21
- monopyly/core/filters.py +0 -2
- monopyly/core/routes.py +1 -1
- monopyly/credit/actions.py +4 -5
- monopyly/credit/blueprint.py +2 -0
- monopyly/credit/cards.py +1 -1
- monopyly/credit/forms.py +9 -7
- monopyly/credit/routes.py +37 -62
- monopyly/credit/transactions/__init__.py +2 -0
- monopyly/credit/transactions/_transactions.py +1 -4
- monopyly/credit/transactions/activity/__init__.py +6 -0
- monopyly/credit/transactions/activity/parser.py +0 -1
- monopyly/credit/transactions/activity/reconciliation.py +5 -3
- monopyly/database/__init__.py +0 -3
- monopyly/database/models.py +63 -49
- monopyly/database/preloads.sql +6 -1
- monopyly/scripts/screenshot_application.py +100 -0
- monopyly/static/chartist-1.5.0.min.js +8 -0
- monopyly/static/css/style.css +39 -14
- monopyly/static/img/about/bank-account-details.png +0 -0
- monopyly/static/img/about/bank-account-summaries.png +0 -0
- monopyly/static/img/about/bank-accounts.png +0 -0
- monopyly/static/img/about/credit-account-details.png +0 -0
- monopyly/static/img/about/credit-statement-details.png +0 -0
- monopyly/static/img/about/credit-transactions.png +0 -0
- monopyly/static/img/about/homepage-user.png +0 -0
- monopyly/static/img/about/homepage.png +0 -0
- monopyly/static/jquery-3.7.1.min.js +2 -0
- monopyly/static/js/add-transfer.js +8 -9
- monopyly/static/js/bind-tag-actions.js +6 -0
- monopyly/static/js/create-balance-chart.js +1 -1
- monopyly/static/js/create-category-chart.js +1 -1
- monopyly/static/js/load-more-transactions.js +27 -0
- monopyly/static/js/modules/expand-transaction.js +7 -6
- monopyly/static/js/modules/update-display-ajax.js +20 -1
- monopyly/static/js/update-transactions-display.js +8 -2
- monopyly/templates/banking/account_form/account_form_page_new.html +10 -8
- monopyly/templates/banking/account_page.html +32 -33
- monopyly/templates/banking/account_summaries.html +2 -2
- monopyly/templates/banking/account_summary.html +1 -1
- monopyly/templates/banking/accounts_page.html +10 -8
- monopyly/templates/banking/transactions_table/table.html +3 -0
- monopyly/templates/banking/transactions_table/transactions.html +0 -1
- monopyly/templates/common/tag_tree.html +25 -0
- monopyly/templates/{credit → common}/tags_page.html +17 -13
- monopyly/templates/common/transactions_table/linked_bank_transaction.html +2 -2
- monopyly/templates/common/transactions_table/table.html +6 -0
- monopyly/templates/common/transactions_table/transactions.html +9 -15
- monopyly/templates/core/index.html +124 -109
- monopyly/templates/core/profile.html +19 -20
- monopyly/templates/credit/account_page.html +17 -17
- monopyly/templates/credit/card_form/card_form_page_new.html +8 -5
- monopyly/templates/credit/statement_page.html +45 -46
- monopyly/templates/credit/statement_reconciliation/statement_reconciliation_page.html +22 -22
- monopyly/templates/credit/statement_summary.html +2 -2
- monopyly/templates/credit/statements_page.html +13 -13
- monopyly/templates/credit/transaction_form/transaction_form_page_update.html +9 -9
- monopyly/templates/credit/transaction_submission_page.html +3 -3
- monopyly/templates/credit/transactions_page.html +38 -22
- monopyly/templates/credit/transactions_table/condensed_row_content.html +2 -2
- monopyly/templates/credit/transactions_table/expanded_row_content.html +5 -5
- monopyly/templates/credit/transactions_table/table.html +3 -0
- monopyly/templates/credit/transactions_table/transactions.html +0 -1
- monopyly/templates/layout.html +16 -9
- {monopyly-1.5.2.dist-info → monopyly-1.6.1.dist-info}/METADATA +8 -8
- {monopyly-1.5.2.dist-info → monopyly-1.6.1.dist-info}/RECORD +90 -84
- monopyly-1.6.1.dist-info/entry_points.txt +3 -0
- monopyly/static/jquery-3.7.0.min.js +0 -2
- monopyly/templates/credit/tag_tree/subtag_tree.html +0 -22
- monopyly/templates/credit/tag_tree/tag_tree.html +0 -13
- monopyly-1.5.2.dist-info/entry_points.txt +0 -2
- {monopyly-1.5.2.dist-info → monopyly-1.6.1.dist-info}/WHEEL +0 -0
- {monopyly-1.5.2.dist-info → monopyly-1.6.1.dist-info}/licenses/COPYING +0 -0
- {monopyly-1.5.2.dist-info → monopyly-1.6.1.dist-info}/licenses/LICENSE +0 -0
monopyly/database/models.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
from typing import List, Optional
|
|
3
2
|
|
|
4
3
|
from dry_foundation.database.models import AuthorizedAccessMixin, Model
|
|
5
|
-
from sqlalchemy import Column,
|
|
6
|
-
from sqlalchemy.ext.hybrid import hybrid_property
|
|
4
|
+
from sqlalchemy import Column, ForeignKey, Integer, Table
|
|
7
5
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
8
6
|
|
|
9
7
|
|
|
@@ -15,7 +13,7 @@ class User(Model):
|
|
|
15
13
|
password: Mapped[str]
|
|
16
14
|
# Relationships
|
|
17
15
|
banks: Mapped["Bank"] = relationship(back_populates="user", cascade="all, delete")
|
|
18
|
-
|
|
16
|
+
bank_account_type_views: Mapped[list["BankAccountTypeView"]] = relationship(
|
|
19
17
|
back_populates="user", viewonly=True
|
|
20
18
|
)
|
|
21
19
|
|
|
@@ -25,16 +23,23 @@ class InternalTransaction(Model):
|
|
|
25
23
|
# Columns
|
|
26
24
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
27
25
|
# Relationships
|
|
28
|
-
bank_transactions: Mapped[
|
|
26
|
+
bank_transactions: Mapped[list["BankTransaction"]] = relationship(
|
|
29
27
|
back_populates="internal_transaction"
|
|
30
28
|
)
|
|
31
|
-
|
|
29
|
+
bank_transaction_views: Mapped[list["BankTransactionView"]] = relationship(
|
|
30
|
+
back_populates="internal_transaction", viewonly=True
|
|
31
|
+
)
|
|
32
|
+
credit_transactions: Mapped[list["CreditTransaction"]] = relationship(
|
|
32
33
|
back_populates="internal_transaction"
|
|
33
34
|
)
|
|
35
|
+
credit_transaction_views: Mapped[list["CreditTransactionView"]] = relationship(
|
|
36
|
+
back_populates="internal_transaction", viewonly=True
|
|
37
|
+
)
|
|
34
38
|
|
|
39
|
+
@property
|
|
40
|
+
def transaction_views(self):
|
|
41
|
+
return self.bank_transaction_views + self.credit_transaction_views
|
|
35
42
|
|
|
36
|
-
# Not sure why these tables are necessary, because they should already be reflected;
|
|
37
|
-
# perhaps explore or wait until sqlalchemy 2.0
|
|
38
43
|
|
|
39
44
|
bank_tag_link_table = Table(
|
|
40
45
|
"bank_tag_links",
|
|
@@ -62,20 +67,21 @@ credit_tag_link_table = Table(
|
|
|
62
67
|
|
|
63
68
|
class TransactionTag(AuthorizedAccessMixin, Model):
|
|
64
69
|
__tablename__ = "transaction_tags"
|
|
70
|
+
_alt_authorized_ids = (0,)
|
|
65
71
|
# Columns
|
|
66
72
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
67
73
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
|
|
68
|
-
parent_id: Mapped[
|
|
74
|
+
parent_id: Mapped[int | None] = mapped_column(ForeignKey("transaction_tags.id"))
|
|
69
75
|
tag_name: Mapped[str]
|
|
70
76
|
# Relationships
|
|
71
77
|
parent: Mapped["TransactionTag"] = relationship(
|
|
72
78
|
back_populates="children", remote_side=[id]
|
|
73
79
|
)
|
|
74
|
-
children: Mapped[
|
|
75
|
-
bank_subtransactions: Mapped[
|
|
80
|
+
children: Mapped[list["TransactionTag"]] = relationship(back_populates="parent")
|
|
81
|
+
bank_subtransactions: Mapped[list["BankSubtransaction"]] = relationship(
|
|
76
82
|
back_populates="tags", secondary=bank_tag_link_table
|
|
77
83
|
)
|
|
78
|
-
credit_subtransactions: Mapped[
|
|
84
|
+
credit_subtransactions: Mapped[list["CreditSubtransaction"]] = relationship(
|
|
79
85
|
back_populates="tags", secondary=credit_tag_link_table
|
|
80
86
|
)
|
|
81
87
|
|
|
@@ -95,10 +101,10 @@ class Bank(AuthorizedAccessMixin, Model):
|
|
|
95
101
|
bank_name: Mapped[str]
|
|
96
102
|
# Relationships
|
|
97
103
|
user: Mapped["User"] = relationship(back_populates="banks")
|
|
98
|
-
|
|
104
|
+
bank_account_views: Mapped[list["BankAccountView"]] = relationship(
|
|
99
105
|
back_populates="bank", cascade="all, delete", viewonly=True
|
|
100
106
|
)
|
|
101
|
-
credit_accounts: Mapped[
|
|
107
|
+
credit_accounts: Mapped[list["CreditAccount"]] = relationship(
|
|
102
108
|
back_populates="bank", cascade="all, delete"
|
|
103
109
|
)
|
|
104
110
|
|
|
@@ -110,7 +116,7 @@ class BankAccountType(AuthorizedAccessMixin, Model):
|
|
|
110
116
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
111
117
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
|
|
112
118
|
type_name: Mapped[str]
|
|
113
|
-
type_abbreviation: Mapped[
|
|
119
|
+
type_abbreviation: Mapped[str | None]
|
|
114
120
|
# Relationships
|
|
115
121
|
view: Mapped["BankAccountTypeView"] = relationship(
|
|
116
122
|
back_populates="account_type", uselist=False, viewonly=True
|
|
@@ -124,13 +130,13 @@ class BankAccountTypeView(AuthorizedAccessMixin, Model):
|
|
|
124
130
|
id = mapped_column(Integer, ForeignKey("bank_account_types.id"), primary_key=True)
|
|
125
131
|
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
|
|
126
132
|
type_name: Mapped[str]
|
|
127
|
-
type_abbreviation: Mapped[
|
|
133
|
+
type_abbreviation: Mapped[str | None]
|
|
128
134
|
type_common_name: Mapped[str]
|
|
129
135
|
# Relationships
|
|
130
136
|
account_type: Mapped["BankAccountType"] = relationship(back_populates="view")
|
|
131
|
-
user: Mapped["User"] = relationship(back_populates="
|
|
132
|
-
|
|
133
|
-
back_populates="
|
|
137
|
+
user: Mapped["User"] = relationship(back_populates="bank_account_type_views")
|
|
138
|
+
account_views: Mapped[list["BankAccountView"]] = relationship(
|
|
139
|
+
back_populates="account_type_view", viewonly=True
|
|
134
140
|
)
|
|
135
141
|
|
|
136
142
|
|
|
@@ -166,12 +172,12 @@ class BankAccountView(AuthorizedAccessMixin, Model):
|
|
|
166
172
|
projected_balance: Mapped[float]
|
|
167
173
|
# Relationships
|
|
168
174
|
account: Mapped["BankAccount"] = relationship(back_populates="view")
|
|
169
|
-
bank: Mapped["Bank"] = relationship(back_populates="
|
|
170
|
-
|
|
171
|
-
back_populates="
|
|
175
|
+
bank: Mapped["Bank"] = relationship(back_populates="bank_account_views")
|
|
176
|
+
account_type_view: Mapped["BankAccountTypeView"] = relationship(
|
|
177
|
+
back_populates="account_views", viewonly=True
|
|
172
178
|
)
|
|
173
|
-
|
|
174
|
-
back_populates="
|
|
179
|
+
transaction_views: Mapped[list["BankTransactionView"]] = relationship(
|
|
180
|
+
back_populates="account_view", viewonly=True
|
|
175
181
|
)
|
|
176
182
|
|
|
177
183
|
|
|
@@ -182,16 +188,19 @@ class BankTransaction(AuthorizedAccessMixin, Model):
|
|
|
182
188
|
subtype = "bank"
|
|
183
189
|
# Columns
|
|
184
190
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
185
|
-
internal_transaction_id: Mapped[
|
|
191
|
+
internal_transaction_id: Mapped[int | None] = mapped_column(
|
|
186
192
|
ForeignKey("internal_transactions.id")
|
|
187
193
|
)
|
|
188
194
|
account_id: Mapped[int] = mapped_column(ForeignKey("bank_accounts_view.id"))
|
|
189
195
|
transaction_date: Mapped[datetime.date]
|
|
190
|
-
merchant: Mapped[
|
|
196
|
+
merchant: Mapped[str | None]
|
|
191
197
|
# Relationships
|
|
192
198
|
view: Mapped["BankTransactionView"] = relationship(
|
|
193
199
|
back_populates="transaction", uselist=False, viewonly=True
|
|
194
200
|
)
|
|
201
|
+
internal_transaction: Mapped["InternalTransaction"] = relationship(
|
|
202
|
+
back_populates="bank_transactions"
|
|
203
|
+
)
|
|
195
204
|
|
|
196
205
|
|
|
197
206
|
class BankTransactionView(AuthorizedAccessMixin, Model):
|
|
@@ -203,23 +212,25 @@ class BankTransactionView(AuthorizedAccessMixin, Model):
|
|
|
203
212
|
id: Mapped[int] = mapped_column(
|
|
204
213
|
ForeignKey("bank_transactions.id"), primary_key=True
|
|
205
214
|
)
|
|
206
|
-
internal_transaction_id: Mapped[
|
|
215
|
+
internal_transaction_id: Mapped[int | None] = mapped_column(
|
|
207
216
|
ForeignKey("internal_transactions.id")
|
|
208
217
|
)
|
|
209
218
|
account_id: Mapped[int] = mapped_column(ForeignKey("bank_accounts_view.id"))
|
|
210
219
|
transaction_date: Mapped[datetime.date]
|
|
211
|
-
merchant: Mapped[
|
|
220
|
+
merchant: Mapped[str | None]
|
|
212
221
|
total: Mapped[float]
|
|
213
|
-
notes: Mapped[
|
|
222
|
+
notes: Mapped[str | None]
|
|
214
223
|
balance: Mapped[float]
|
|
215
224
|
# Relationships
|
|
216
225
|
transaction: Mapped["BankTransaction"] = relationship(back_populates="view")
|
|
217
226
|
internal_transaction: Mapped["InternalTransaction"] = relationship(
|
|
218
|
-
back_populates="
|
|
227
|
+
back_populates="bank_transaction_views"
|
|
228
|
+
)
|
|
229
|
+
account_view: Mapped["BankAccountView"] = relationship(
|
|
230
|
+
back_populates="transaction_views", viewonly=True
|
|
219
231
|
)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
back_populates="transaction", lazy="selectin"
|
|
232
|
+
subtransactions: Mapped[list["BankSubtransaction"]] = relationship(
|
|
233
|
+
back_populates="transaction_view", lazy="selectin", cascade="all, delete"
|
|
223
234
|
)
|
|
224
235
|
|
|
225
236
|
|
|
@@ -232,10 +243,10 @@ class BankSubtransaction(AuthorizedAccessMixin, Model):
|
|
|
232
243
|
subtotal: Mapped[float]
|
|
233
244
|
note: Mapped[str]
|
|
234
245
|
# Relationships
|
|
235
|
-
|
|
246
|
+
transaction_view: Mapped["BankTransactionView"] = relationship(
|
|
236
247
|
back_populates="subtransactions", viewonly=True
|
|
237
248
|
)
|
|
238
|
-
tags: Mapped[
|
|
249
|
+
tags: Mapped[list["TransactionTag"]] = relationship(
|
|
239
250
|
back_populates="bank_subtransactions",
|
|
240
251
|
secondary=bank_tag_link_table,
|
|
241
252
|
lazy="selectin",
|
|
@@ -253,7 +264,7 @@ class CreditAccount(AuthorizedAccessMixin, Model):
|
|
|
253
264
|
# ((Should probably have an 'active' field))
|
|
254
265
|
# Relationships
|
|
255
266
|
bank: Mapped["Bank"] = relationship(back_populates="credit_accounts")
|
|
256
|
-
cards: Mapped[
|
|
267
|
+
cards: Mapped[list["CreditCard"]] = relationship(
|
|
257
268
|
back_populates="account", cascade="all, delete"
|
|
258
269
|
)
|
|
259
270
|
|
|
@@ -268,7 +279,7 @@ class CreditCard(AuthorizedAccessMixin, Model):
|
|
|
268
279
|
active: Mapped[int]
|
|
269
280
|
# Relationships
|
|
270
281
|
account: Mapped["CreditAccount"] = relationship(back_populates="cards")
|
|
271
|
-
|
|
282
|
+
statement_views: Mapped[list["CreditStatementView"]] = relationship(
|
|
272
283
|
back_populates="card", viewonly=True
|
|
273
284
|
)
|
|
274
285
|
|
|
@@ -301,9 +312,9 @@ class CreditStatementView(AuthorizedAccessMixin, Model):
|
|
|
301
312
|
payment_date: Mapped[datetime.date]
|
|
302
313
|
# Relationships
|
|
303
314
|
statement: Mapped["CreditStatement"] = relationship(back_populates="view")
|
|
304
|
-
card: Mapped["CreditCard"] = relationship(back_populates="
|
|
305
|
-
|
|
306
|
-
back_populates="
|
|
315
|
+
card: Mapped["CreditCard"] = relationship(back_populates="statement_views")
|
|
316
|
+
transaction_views: Mapped[list["CreditTransactionView"]] = relationship(
|
|
317
|
+
back_populates="statement_view", viewonly=True
|
|
307
318
|
)
|
|
308
319
|
|
|
309
320
|
|
|
@@ -314,7 +325,7 @@ class CreditTransaction(AuthorizedAccessMixin, Model):
|
|
|
314
325
|
subtype = "credit"
|
|
315
326
|
# Columns
|
|
316
327
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
317
|
-
internal_transaction_id: Mapped[
|
|
328
|
+
internal_transaction_id: Mapped[int | None] = mapped_column(
|
|
318
329
|
ForeignKey("internal_transactions.id")
|
|
319
330
|
)
|
|
320
331
|
statement_id: Mapped[int] = mapped_column(ForeignKey("credit_statements_view.id"))
|
|
@@ -324,6 +335,9 @@ class CreditTransaction(AuthorizedAccessMixin, Model):
|
|
|
324
335
|
view: Mapped["CreditTransactionView"] = relationship(
|
|
325
336
|
back_populates="transaction", uselist=False, viewonly=True
|
|
326
337
|
)
|
|
338
|
+
internal_transaction: Mapped["InternalTransaction"] = relationship(
|
|
339
|
+
back_populates="credit_transactions"
|
|
340
|
+
)
|
|
327
341
|
|
|
328
342
|
|
|
329
343
|
class CreditTransactionView(AuthorizedAccessMixin, Model):
|
|
@@ -335,7 +349,7 @@ class CreditTransactionView(AuthorizedAccessMixin, Model):
|
|
|
335
349
|
id: Mapped[int] = mapped_column(
|
|
336
350
|
ForeignKey("credit_transactions.id"), primary_key=True
|
|
337
351
|
)
|
|
338
|
-
internal_transaction_id: Mapped[
|
|
352
|
+
internal_transaction_id: Mapped[int | None] = mapped_column(
|
|
339
353
|
ForeignKey("internal_transactions.id")
|
|
340
354
|
)
|
|
341
355
|
statement_id: Mapped[int] = mapped_column(ForeignKey("credit_statements_view.id"))
|
|
@@ -348,13 +362,13 @@ class CreditTransactionView(AuthorizedAccessMixin, Model):
|
|
|
348
362
|
back_populates="view", uselist=False
|
|
349
363
|
)
|
|
350
364
|
internal_transaction: Mapped["InternalTransaction"] = relationship(
|
|
351
|
-
back_populates="
|
|
365
|
+
back_populates="credit_transaction_views"
|
|
352
366
|
)
|
|
353
|
-
|
|
354
|
-
back_populates="
|
|
367
|
+
statement_view: Mapped["CreditStatementView"] = relationship(
|
|
368
|
+
back_populates="transaction_views", viewonly=True
|
|
355
369
|
)
|
|
356
|
-
subtransactions: Mapped[
|
|
357
|
-
back_populates="
|
|
370
|
+
subtransactions: Mapped[list["CreditSubtransaction"]] = relationship(
|
|
371
|
+
back_populates="transaction_view", lazy="selectin", cascade="all, delete"
|
|
358
372
|
)
|
|
359
373
|
|
|
360
374
|
|
|
@@ -375,10 +389,10 @@ class CreditSubtransaction(AuthorizedAccessMixin, Model):
|
|
|
375
389
|
subtotal: Mapped[float]
|
|
376
390
|
note: Mapped[str]
|
|
377
391
|
# Relationships
|
|
378
|
-
|
|
392
|
+
transaction_view: Mapped["CreditTransactionView"] = relationship(
|
|
379
393
|
back_populates="subtransactions", viewonly=True
|
|
380
394
|
)
|
|
381
|
-
tags: Mapped[
|
|
395
|
+
tags: Mapped[list["TransactionTag"]] = relationship(
|
|
382
396
|
back_populates="credit_subtransactions",
|
|
383
397
|
secondary=credit_tag_link_table,
|
|
384
398
|
lazy="selectin",
|
monopyly/database/preloads.sql
CHANGED
|
@@ -4,7 +4,12 @@ INSERT INTO users
|
|
|
4
4
|
VALUES
|
|
5
5
|
(0, 'global', 'n/a');
|
|
6
6
|
|
|
7
|
-
/* Set
|
|
7
|
+
/* Set a default transaction tag for credit payments */
|
|
8
|
+
INSERT INTO transaction_tags
|
|
9
|
+
(user_id, parent_id, tag_name)
|
|
10
|
+
VALUES (0, NULL, 'Credit payments');
|
|
11
|
+
|
|
12
|
+
/* Set some default account types */
|
|
8
13
|
INSERT INTO bank_account_types
|
|
9
14
|
(user_id, type_name, type_abbreviation)
|
|
10
15
|
VALUES
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script generates screenshots for the Monopyly application.
|
|
3
|
+
|
|
4
|
+
This is a script to automatically take screenshots of pages in the
|
|
5
|
+
Monopyly interface. It relies on entries in the development database
|
|
6
|
+
(and described in `tests/data.sql`); however, to take production-style
|
|
7
|
+
screenshots, Monopyly should be run in local mode:
|
|
8
|
+
|
|
9
|
+
monopyly launch local
|
|
10
|
+
|
|
11
|
+
To use the development database, replace the local database
|
|
12
|
+
(e.g., `instance/monopyly.sqlite`) with a copy of the development
|
|
13
|
+
database.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from urllib.parse import urljoin
|
|
18
|
+
|
|
19
|
+
from selenium import webdriver
|
|
20
|
+
from selenium.webdriver.common.action_chains import ActionChains
|
|
21
|
+
from selenium.webdriver.common.by import By
|
|
22
|
+
from selenium.webdriver.firefox.options import Options
|
|
23
|
+
|
|
24
|
+
SCRIPTS_DIR = Path(__file__).parent
|
|
25
|
+
PACKAGE_DIR = SCRIPTS_DIR.parent
|
|
26
|
+
SCREENSHOTS_DIR = PACKAGE_DIR / "static/img/about"
|
|
27
|
+
MONOPYLY_URL = "http://localhost:5001"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
# Set Firefox launch options and create the webdriver
|
|
32
|
+
options = Options()
|
|
33
|
+
options.add_argument("--headless")
|
|
34
|
+
options.add_argument("--width=1520")
|
|
35
|
+
options.add_argument("--height=1080")
|
|
36
|
+
driver = webdriver.Firefox(options=options)
|
|
37
|
+
driver.implicitly_wait(3)
|
|
38
|
+
|
|
39
|
+
# Take screenshot of the app homepage
|
|
40
|
+
photographer = Photographer(driver, SCREENSHOTS_DIR)
|
|
41
|
+
photographer.screenshot("homepage.png", "/")
|
|
42
|
+
# Login
|
|
43
|
+
driver.get(urljoin(MONOPYLY_URL, "/auth/login"))
|
|
44
|
+
username = driver.find_element(By.ID, "username")
|
|
45
|
+
password = driver.find_element(By.ID, "password")
|
|
46
|
+
username.send_keys("mr.monopyly")
|
|
47
|
+
password.send_keys("MONOPYLY")
|
|
48
|
+
submit_button = driver.find_element(By.CSS_SELECTOR, "input.button")
|
|
49
|
+
submit_button.click()
|
|
50
|
+
# Take a screenshot of the (logged in) app homepage
|
|
51
|
+
photographer.screenshot("homepage-user.png", "/")
|
|
52
|
+
# Take screenshots of the bank account functionality
|
|
53
|
+
photographer.screenshot("bank-accounts.png", "/banking/accounts")
|
|
54
|
+
photographer.screenshot(
|
|
55
|
+
"bank-account-summaries.png", "/banking/account_summaries/2"
|
|
56
|
+
)
|
|
57
|
+
photographer.screenshot("bank-account-details.png", "/banking/account/2")
|
|
58
|
+
# Take screenshots of the credit card functionality
|
|
59
|
+
photographer.screenshot("credit-account-details.png", "/credit/account/3")
|
|
60
|
+
photographer.get("/credit/transactions")
|
|
61
|
+
element = driver.find_element(By.CSS_SELECTOR, "#transaction-5 .more.button")
|
|
62
|
+
photographer.screenshot(
|
|
63
|
+
"credit-transactions.png",
|
|
64
|
+
actions=[photographer.define_click_action(element).pause(1)],
|
|
65
|
+
)
|
|
66
|
+
photographer.screenshot("credit-statement-details.png", "/credit/statement/7")
|
|
67
|
+
driver.quit()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Photographer:
|
|
71
|
+
"""An object to photographs/screenshots of the web application."""
|
|
72
|
+
|
|
73
|
+
_base_url = MONOPYLY_URL
|
|
74
|
+
|
|
75
|
+
def __init__(self, driver, output_dir):
|
|
76
|
+
self._driver = driver
|
|
77
|
+
self._output_dir = output_dir
|
|
78
|
+
|
|
79
|
+
def get(self, url):
|
|
80
|
+
"""Get the given URL."""
|
|
81
|
+
self._driver.get(urljoin(self._base_url, url))
|
|
82
|
+
|
|
83
|
+
def screenshot(self, filename, url=None, actions=()):
|
|
84
|
+
"""Take a screenshot of the given URL."""
|
|
85
|
+
if url:
|
|
86
|
+
self.get(url)
|
|
87
|
+
for action in actions:
|
|
88
|
+
action.perform()
|
|
89
|
+
output_filepath = self._output_dir / filename
|
|
90
|
+
self._driver.save_full_page_screenshot(str(output_filepath))
|
|
91
|
+
|
|
92
|
+
def define_click_action(self, element):
|
|
93
|
+
"""Define an action that clicks the element."""
|
|
94
|
+
action = ActionChains(self._driver).move_to_element(element)
|
|
95
|
+
action.click()
|
|
96
|
+
return action
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
main()
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skipped minification because the original files appears to be already minified.
|
|
3
|
+
* Original file: /npm/chartist@1.5.0/dist/index.umd.js
|
|
4
|
+
*
|
|
5
|
+
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
|
6
|
+
*/
|
|
7
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Chartist={})}(this,(function(e){"use strict";const t={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns/",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",ct:"http://gionkunz.github.com/chartist-js/ct"},s={"&":"&","<":"<",">":">",'"':""","'":"'"};function i(e,t){return"number"==typeof e?e+t:e}function n(e){if("string"==typeof e){const t=/^(\d+)\s*(.*)$/g.exec(e);return{value:t?+t[1]:0,unit:(null==t?void 0:t[2])||void 0}}return{value:Number(e)}}function r(e){return String.fromCharCode(97+e%26)}const a=2221e-19;function o(e){return Math.floor(Math.log(Math.abs(e))/Math.LN10)}function l(e,t,s){return t/s.range*e}function h(e,t){const s=Math.pow(10,t||8);return Math.round(e*s)/s}function c(e){if(1===e)return e;function t(e,s){return e%s==0?s:t(s,e%s)}function s(e){return e*e+1}let i,n=2,r=2;if(e%2==0)return 2;do{n=s(n)%e,r=s(s(r))%e,i=t(Math.abs(n-r),e)}while(1===i);return i}function u(e,t,s,i){const n=(i-90)*Math.PI/180;return{x:e+s*Math.cos(n),y:t+s*Math.sin(n)}}function d(e,t,s){let i=arguments.length>3&&void 0!==arguments[3]&&arguments[3];const n={high:t.high,low:t.low,valueRange:0,oom:0,step:0,min:0,max:0,range:0,numberOfSteps:0,values:[]};n.valueRange=n.high-n.low,n.oom=o(n.valueRange),n.step=Math.pow(10,n.oom),n.min=Math.floor(n.low/n.step)*n.step,n.max=Math.ceil(n.high/n.step)*n.step,n.range=n.max-n.min,n.numberOfSteps=Math.round(n.range/n.step);const r=l(e,n.step,n),u=r<s,d=i?c(n.range):0;if(i&&l(e,1,n)>=s)n.step=1;else if(i&&d<n.step&&l(e,d,n)>=s)n.step=d;else{let t=0;for(;;){if(u&&l(e,n.step,n)<=s)n.step*=2;else{if(u||!(l(e,n.step/2,n)>=s))break;if(n.step/=2,i&&n.step%1!=0){n.step*=2;break}}if(t++>1e3)throw new Error("Exceeded maximum number of iterations while optimizing scale step!")}}function m(e,t){return e===(e+=t)&&(e*=1+(t>0?a:-a)),e}n.step=Math.max(n.step,a);let p=n.min,f=n.max;for(;p+n.step<=n.low;)p=m(p,n.step);for(;f-n.step>=n.high;)f=m(f,-n.step);n.min=p,n.max=f,n.range=n.max-n.min;const g=[];for(let e=n.min;e<=n.max;e=m(e,n.step)){const t=h(e);t!==g[g.length-1]&&g.push(t)}return n.values=g,n}function m(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};for(var t=arguments.length,s=new Array(t>1?t-1:0),i=1;i<t;i++)s[i-1]=arguments[i];for(let t=0;t<s.length;t++){const i=s[t],n=Object.getPrototypeOf(e);for(const t in i){if(null!==n&&t in n)continue;const s=i[t];e[t]="object"!=typeof s||null===s||s instanceof Array?s:m(e[t],s)}}return e}const p=e=>e;function f(e,t){return Array.from({length:e},t?(e,s)=>t(s):()=>{})}const g=(e,t)=>e+(t||0),x=(e,t)=>f(Math.max(...e.map((e=>e.length))),(s=>t(...e.map((e=>e[s])))));function v(e,t){return null!==e&&"object"==typeof e&&Reflect.has(e,t)}function y(e){return null!==e&&isFinite(e)}function w(e){return!e&&0!==e}function b(e){return y(e)?Number(e):void 0}function E(e){return!!Array.isArray(e)&&e.every(Array.isArray)}function A(e,t){let s=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=0;e[s?"reduceRight":"reduce"](((e,s,n)=>t(s,i++,n)),void 0)}function M(e,t){const s=Array.isArray(e)?e[t]:v(e,"data")?e.data[t]:null;return v(s,"meta")?s.meta:void 0}function N(e){return null==e||"number"==typeof e&&isNaN(e)}function C(e){return Array.isArray(e)&&e.every((e=>Array.isArray(e)||v(e,"data")))}function O(e){return"object"==typeof e&&null!==e&&(Reflect.has(e,"x")||Reflect.has(e,"y"))}function L(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"y";return O(e)&&v(e,t)?b(e[t]):b(e)}function S(e,t,s){const i={high:void 0===(t={...t,...s?"x"===s?t.axisX:t.axisY:{}}).high?-Number.MAX_VALUE:+t.high,low:void 0===t.low?Number.MAX_VALUE:+t.low},n=void 0===t.high,r=void 0===t.low;return(n||r)&&function e(t){if(!N(t))if(Array.isArray(t))for(let s=0;s<t.length;s++)e(t[s]);else{const e=Number(s&&v(t,s)?t[s]:t);n&&e>i.high&&(i.high=e),r&&e<i.low&&(i.low=e)}}(e),(t.referenceValue||0===t.referenceValue)&&(i.high=Math.max(t.referenceValue,i.high),i.low=Math.min(t.referenceValue,i.low)),i.high<=i.low&&(0===i.low?i.high=1:i.low<0?i.high=0:(i.high>0||(i.high=1),i.low=0)),i}function B(e){let t,s=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=arguments.length>2?arguments[2]:void 0,n=arguments.length>3?arguments[3]:void 0;const r={labels:(e.labels||[]).slice(),series:z(e.series,i,n)},a=r.labels.length;return E(r.series)?(t=Math.max(a,...r.series.map((e=>e.length))),r.series.forEach((e=>{e.push(...f(Math.max(0,t-e.length)))}))):t=r.series.length,r.labels.push(...f(Math.max(0,t-a),(()=>""))),s&&k(r),r}function k(e){var t;null===(t=e.labels)||void 0===t||t.reverse(),e.series.reverse();for(const t of e.series)v(t,"data")?t.data.reverse():Array.isArray(t)&&t.reverse()}function _(e,t){if(!N(e))return t?function(e,t){let s,i;if("object"!=typeof e){const n=b(e);"x"===t?s=n:i=n}else v(e,"x")&&(s=b(e.x)),v(e,"y")&&(i=b(e.y));if(void 0!==s||void 0!==i)return{x:s,y:i}}(e,t):b(e)}function j(e,t){return Array.isArray(e)?e.map((e=>v(e,"value")?_(e.value,t):_(e,t))):j(e.data,t)}function z(e,t,s){if(C(e))return e.map((e=>j(e,t)));const i=j(e,t);return s?i.map((e=>[e])):i}function I(e,t,s){const i={increasingX:!1,fillHoles:!1,...s},n=[];let r=!0;for(let s=0;s<e.length;s+=2)void 0===L(t[s/2].value)?i.fillHoles||(r=!0):(i.increasingX&&s>=2&&e[s]<=e[s-2]&&(r=!0),r&&(n.push({pathCoordinates:[],valueData:[]}),r=!1),n[n.length-1].pathCoordinates.push(e[s],e[s+1]),n[n.length-1].valueData.push(t[s/2]));return n}function P(e){let t="";return null==e?e:(t="number"==typeof e?""+e:"object"==typeof e?JSON.stringify({data:e}):String(e),Object.keys(s).reduce(((e,t)=>e.replaceAll(t,s[t])),t))}class X{call(e,t){return this.svgElements.forEach((s=>Reflect.apply(s[e],s,t))),this}attr(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("attr",t)}elem(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("elem",t)}root(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("root",t)}getNode(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("getNode",t)}foreignObject(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("foreignObject",t)}text(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("text",t)}empty(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("empty",t)}remove(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("remove",t)}addClass(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("addClass",t)}removeClass(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("removeClass",t)}removeAllClasses(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("removeAllClasses",t)}animate(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return this.call("animate",t)}constructor(e){this.svgElements=[];for(let t=0;t<e.length;t++)this.svgElements.push(new R(e[t]))}}const V={easeInSine:[.47,0,.745,.715],easeOutSine:[.39,.575,.565,1],easeInOutSine:[.445,.05,.55,.95],easeInQuad:[.55,.085,.68,.53],easeOutQuad:[.25,.46,.45,.94],easeInOutQuad:[.455,.03,.515,.955],easeInCubic:[.55,.055,.675,.19],easeOutCubic:[.215,.61,.355,1],easeInOutCubic:[.645,.045,.355,1],easeInQuart:[.895,.03,.685,.22],easeOutQuart:[.165,.84,.44,1],easeInOutQuart:[.77,0,.175,1],easeInQuint:[.755,.05,.855,.06],easeOutQuint:[.23,1,.32,1],easeInOutQuint:[.86,0,.07,1],easeInExpo:[.95,.05,.795,.035],easeOutExpo:[.19,1,.22,1],easeInOutExpo:[1,0,0,1],easeInCirc:[.6,.04,.98,.335],easeOutCirc:[.075,.82,.165,1],easeInOutCirc:[.785,.135,.15,.86],easeInBack:[.6,-.28,.735,.045],easeOutBack:[.175,.885,.32,1.275],easeInOutBack:[.68,-.55,.265,1.55]};function Y(e,t,s){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0;const{easing:o,...l}=s,h={};let c,u;o&&(c=Array.isArray(o)?o:V[o]),l.begin=i(l.begin,"ms"),l.dur=i(l.dur,"ms"),c&&(l.calcMode="spline",l.keySplines=c.join(" "),l.keyTimes="0;1"),r&&(l.fill="freeze",h[t]=l.from,e.attr(h),u=n(l.begin||0).value,l.begin="indefinite");const d=e.elem("animate",{attributeName:t,...l});r&&setTimeout((()=>{try{d._node.beginElement()}catch(s){h[t]=l.to,e.attr(h),d.remove()}}),u);const m=d.getNode();a&&m.addEventListener("beginEvent",(()=>a.emit("animationBegin",{element:e,animate:m,params:s}))),m.addEventListener("endEvent",(()=>{a&&a.emit("animationEnd",{element:e,animate:m,params:s}),r&&(h[t]=l.to,e.attr(h),d.remove())}))}class R{attr(e,s){return"string"==typeof e?s?this._node.getAttributeNS(s,e):this._node.getAttribute(e):(Object.keys(e).forEach((s=>{if(void 0!==e[s])if(-1!==s.indexOf(":")){const i=s.split(":");this._node.setAttributeNS(t[i[0]],s,String(e[s]))}else this._node.setAttribute(s,String(e[s]))})),this)}elem(e,t,s){return new R(e,t,s,this,arguments.length>3&&void 0!==arguments[3]&&arguments[3])}parent(){return this._node.parentNode instanceof SVGElement?new R(this._node.parentNode):null}root(){let e=this._node;for(;"svg"!==e.nodeName&&e.parentElement;)e=e.parentElement;return new R(e)}querySelector(e){const t=this._node.querySelector(e);return t?new R(t):null}querySelectorAll(e){const t=this._node.querySelectorAll(e);return new X(t)}getNode(){return this._node}foreignObject(e,s,i){let n,r=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if("string"==typeof e){const t=document.createElement("div");t.innerHTML=e,n=t.firstChild}else n=e;n instanceof Element&&n.setAttribute("xmlns",t.xmlns);const a=this.elem("foreignObject",s,i,r);return a._node.appendChild(n),a}text(e){return this._node.appendChild(document.createTextNode(e)),this}empty(){for(;this._node.firstChild;)this._node.removeChild(this._node.firstChild);return this}remove(){var e;return null===(e=this._node.parentNode)||void 0===e||e.removeChild(this._node),this.parent()}replace(e){var t;return null===(t=this._node.parentNode)||void 0===t||t.replaceChild(e._node,this._node),e}append(e){return arguments.length>1&&void 0!==arguments[1]&&arguments[1]&&this._node.firstChild?this._node.insertBefore(e._node,this._node.firstChild):this._node.appendChild(e._node),this}classes(){const e=this._node.getAttribute("class");return e?e.trim().split(/\s+/):[]}addClass(e){return this._node.setAttribute("class",this.classes().concat(e.trim().split(/\s+/)).filter((function(e,t,s){return s.indexOf(e)===t})).join(" ")),this}removeClass(e){const t=e.trim().split(/\s+/);return this._node.setAttribute("class",this.classes().filter((e=>-1===t.indexOf(e))).join(" ")),this}removeAllClasses(){return this._node.setAttribute("class",""),this}height(){return this._node.clientHeight}width(){return this._node.clientWidth}animate(e){let t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],s=arguments.length>2?arguments[2]:void 0;return Object.keys(e).forEach((i=>{const n=e[i];Array.isArray(n)?n.forEach((e=>Y(this,i,e,!1,s))):Y(this,i,n,t,s)})),this}constructor(e,s,i,n,r=!1){e instanceof Element?this._node=e:(this._node=document.createElementNS(t.svg,e),"svg"===e&&this.attr({"xmlns:ct":t.ct})),s&&this.attr(s),i&&this.addClass(i),n&&(r&&n._node.firstChild?n._node.insertBefore(this._node,n._node.firstChild):n._node.appendChild(this._node))}}function G(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"100%",i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"100%",n=arguments.length>3?arguments[3]:void 0,r=arguments.length>4?arguments[4]:void 0;if(!e)throw new Error("Container element is not found");Array.from(e.querySelectorAll("svg")).filter((e=>e.getAttributeNS(t.xmlns,"ct"))).forEach((t=>e.removeChild(t)));const a=new R("svg").attr({width:s,height:i}).attr({style:"width: ".concat(s,"; height: ").concat(i,";")});return n&&a.addClass(n),r&&a.attr({viewBox:"0 0 ".concat(r.width," ").concat(r.height)}),e.appendChild(a.getNode()),a}function D(e){return"number"==typeof e?{top:e,right:e,bottom:e,left:e}:void 0===e?{top:0,right:0,bottom:0,left:0}:{top:"number"==typeof e.top?e.top:0,right:"number"==typeof e.right?e.right:0,bottom:"number"==typeof e.bottom?e.bottom:0,left:"number"==typeof e.left?e.left:0}}function U(e,t){var s,i,r,a,o,l;const h=Boolean(t.axisX||t.axisY),c=(null===(s=t.axisY)||void 0===s?void 0:s.offset)||0,u=(null===(i=t.axisX)||void 0===i?void 0:i.offset)||0,d=null===(r=t.axisY)||void 0===r?void 0:r.position,m=null===(a=t.axisX)||void 0===a?void 0:a.position;let p=(null===(o=t.viewBox)||void 0===o?void 0:o.width)||e.width()||n(t.width).value||0,f=(null===(l=t.viewBox)||void 0===l?void 0:l.height)||e.height()||n(t.height).value||0;const g=D(t.chartPadding);p=Math.max(p,c+g.left+g.right),f=Math.max(f,u+g.top+g.bottom);const x={x1:0,x2:0,y1:0,y2:0,padding:g,width(){return this.x2-this.x1},height(){return this.y1-this.y2}};return h?("start"===m?(x.y2=g.top+u,x.y1=Math.max(f-g.bottom,x.y2+1)):(x.y2=g.top,x.y1=Math.max(f-g.bottom-u,x.y2+1)),"start"===d?(x.x1=g.left+c,x.x2=Math.max(p-g.right,x.x1+1)):(x.x1=g.left,x.x2=Math.max(p-g.right-c,x.x1+1))):(x.x1=g.left,x.x2=Math.max(p-g.right,x.x1+1),x.y2=g.top,x.y1=Math.max(f-g.bottom,x.y2+1)),x}function H(e,t,s,i,n,r,a,o){const l={["".concat(s.units.pos,"1")]:e,["".concat(s.units.pos,"2")]:e,["".concat(s.counterUnits.pos,"1")]:i,["".concat(s.counterUnits.pos,"2")]:i+n},h=r.elem("line",l,a.join(" "));o.emit("draw",{type:"grid",axis:s,index:t,group:r,element:h,...l})}function T(e,t,s,i){const n=e.elem("rect",{x:t.x1,y:t.y2,width:t.width(),height:t.height()},s,!0);i.emit("draw",{type:"gridBackground",group:e,element:n})}function Q(e,t,s,i,n,r,a,o,l,h){const c={[n.units.pos]:e+a[n.units.pos],[n.counterUnits.pos]:a[n.counterUnits.pos],[n.units.len]:t,[n.counterUnits.len]:Math.max(0,r-10)},u=Math.round(c[n.units.len]),d=Math.round(c[n.counterUnits.len]),m=document.createElement("span");m.className=l.join(" "),m.style[n.units.len]=u+"px",m.style[n.counterUnits.len]=d+"px",m.textContent=String(i);const p=o.foreignObject(m,{style:"overflow: visible;",...c});h.emit("draw",{type:"label",axis:n,index:s,group:o,element:p,text:i,...c})}function F(e,t,s){let i;const n=[];function r(n){const r=i;i=m({},e),t&&t.forEach((e=>{window.matchMedia(e[0]).matches&&(i=m({},i,e[1]))})),s&&n&&s.emit("optionsChanged",{previousOptions:r,currentOptions:i})}if(!window.matchMedia)throw new Error("window.matchMedia not found! Make sure you're using a polyfill.");return t&&t.forEach((e=>{const t=window.matchMedia(e[0]);t.addEventListener("change",r),n.push(t)})),r(),{removeMediaQueryListeners:function(){n.forEach((e=>e.removeEventListener("change",r)))},getCurrentOptions:()=>i}}R.Easing=V;const q={m:["x","y"],l:["x","y"],c:["x1","y1","x2","y2","x","y"],a:["rx","ry","xAr","lAf","sf","x","y"]},W={accuracy:3};function Z(e,t,s,i,n,r){const a={command:n?e.toLowerCase():e.toUpperCase(),...t,...r?{data:r}:{}};s.splice(i,0,a)}function $(e,t){e.forEach(((s,i)=>{q[s.command.toLowerCase()].forEach(((n,r)=>{t(s,n,i,r,e)}))}))}class J{static join(e){const t=new J(arguments.length>1&&void 0!==arguments[1]&&arguments[1],arguments.length>2?arguments[2]:void 0);for(let s=0;s<e.length;s++){const i=e[s];for(let e=0;e<i.pathElements.length;e++)t.pathElements.push(i.pathElements[e])}return t}position(e){return void 0!==e?(this.pos=Math.max(0,Math.min(this.pathElements.length,e)),this):this.pos}remove(e){return this.pathElements.splice(this.pos,e),this}move(e,t){let s=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=arguments.length>3?arguments[3]:void 0;return Z("M",{x:+e,y:+t},this.pathElements,this.pos++,s,i),this}line(e,t){let s=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=arguments.length>3?arguments[3]:void 0;return Z("L",{x:+e,y:+t},this.pathElements,this.pos++,s,i),this}curve(e,t,s,i,n,r){let a=arguments.length>6&&void 0!==arguments[6]&&arguments[6],o=arguments.length>7?arguments[7]:void 0;return Z("C",{x1:+e,y1:+t,x2:+s,y2:+i,x:+n,y:+r},this.pathElements,this.pos++,a,o),this}arc(e,t,s,i,n,r,a){let o=arguments.length>7&&void 0!==arguments[7]&&arguments[7],l=arguments.length>8?arguments[8]:void 0;return Z("A",{rx:e,ry:t,xAr:s,lAf:i,sf:n,x:r,y:a},this.pathElements,this.pos++,o,l),this}parse(e){const t=e.replace(/([A-Za-z])(-?[0-9])/g,"$1 $2").replace(/([0-9])([A-Za-z])/g,"$1 $2").split(/[\s,]+/).reduce(((e,t)=>(t.match(/[A-Za-z]/)&&e.push([]),e[e.length-1].push(t),e)),[]);"Z"===t[t.length-1][0].toUpperCase()&&t.pop();const s=t.map((e=>{const t=e.shift(),s=q[t.toLowerCase()];return{command:t,...s.reduce(((t,s,i)=>(t[s]=+e[i],t)),{})}}));return this.pathElements.splice(this.pos,0,...s),this.pos+=s.length,this}stringify(){const e=Math.pow(10,this.options.accuracy);return this.pathElements.reduce(((t,s)=>{const i=q[s.command.toLowerCase()].map((t=>{const i=s[t];return this.options.accuracy?Math.round(i*e)/e:i}));return t+s.command+i.join(",")}),"")+(this.close?"Z":"")}scale(e,t){return $(this.pathElements,((s,i)=>{s[i]*="x"===i[0]?e:t})),this}translate(e,t){return $(this.pathElements,((s,i)=>{s[i]+="x"===i[0]?e:t})),this}transform(e){return $(this.pathElements,((t,s,i,n,r)=>{const a=e(t,s,i,n,r);(a||0===a)&&(t[s]=a)})),this}clone(){const e=new J(arguments.length>0&&void 0!==arguments[0]&&arguments[0]||this.close);return e.pos=this.pos,e.pathElements=this.pathElements.slice().map((e=>({...e}))),e.options={...this.options},e}splitByCommand(e){const t=[new J];return this.pathElements.forEach((s=>{s.command===e.toUpperCase()&&0!==t[t.length-1].pathElements.length&&t.push(new J),t[t.length-1].pathElements.push(s)})),t}constructor(e=!1,t){this.close=e,this.pathElements=[],this.pos=0,this.options={...W,...t}}}function K(e){const t={fillHoles:!1,...e};return function(e,s){const i=new J;let n=!0;for(let r=0;r<e.length;r+=2){const a=e[r],o=e[r+1],l=s[r/2];void 0!==L(l.value)?(n?i.move(a,o,!1,l):i.line(a,o,!1,l),n=!1):t.fillHoles||(n=!0)}return i}}function ee(e){const t={fillHoles:!1,...e};return function e(s,i){const n=I(s,i,{fillHoles:t.fillHoles,increasingX:!0});if(n.length){if(n.length>1)return J.join(n.map((t=>e(t.pathCoordinates,t.valueData))));{if(s=n[0].pathCoordinates,i=n[0].valueData,s.length<=4)return K()(s,i);const e=[],t=[],r=s.length/2,a=[],o=[],l=[],h=[];for(let i=0;i<r;i++)e[i]=s[2*i],t[i]=s[2*i+1];for(let s=0;s<r-1;s++)l[s]=t[s+1]-t[s],h[s]=e[s+1]-e[s],o[s]=l[s]/h[s];a[0]=o[0],a[r-1]=o[r-2];for(let e=1;e<r-1;e++)0===o[e]||0===o[e-1]||o[e-1]>0!=o[e]>0?a[e]=0:(a[e]=3*(h[e-1]+h[e])/((2*h[e]+h[e-1])/o[e-1]+(h[e]+2*h[e-1])/o[e]),isFinite(a[e])||(a[e]=0));const c=(new J).move(e[0],t[0],!1,i[0]);for(let s=0;s<r-1;s++)c.curve(e[s]+h[s]/3,t[s]+a[s]*h[s]/3,e[s+1]-h[s]/3,t[s+1]-a[s+1]*h[s]/3,e[s+1],t[s+1],!1,i[s+1]);return c}}return K()([],[])}}var te=Object.freeze({__proto__:null,none:K,simple:function(e){const t={divisor:2,fillHoles:!1,...e},s=1/Math.max(1,t.divisor);return function(e,i){const n=new J;let r,a=0,o=0;for(let l=0;l<e.length;l+=2){const h=e[l],c=e[l+1],u=(h-a)*s,d=i[l/2];void 0!==d.value?(void 0===r?n.move(h,c,!1,d):n.curve(a+u,o,h-u,c,h,c,!1,d),a=h,o=c,r=d):t.fillHoles||(a=o=0,r=void 0)}return n}},step:function(e){const t={postpone:!0,fillHoles:!1,...e};return function(e,s){const i=new J;let n,r=0,a=0;for(let o=0;o<e.length;o+=2){const l=e[o],h=e[o+1],c=s[o/2];void 0!==c.value?(void 0===n?i.move(l,h,!1,c):(t.postpone?i.line(l,a,!1,n):i.line(r,h,!1,c),i.line(l,h,!1,c)),r=l,a=h,n=c):t.fillHoles||(r=a=0,n=void 0)}return i}},cardinal:function(e){const t={tension:1,fillHoles:!1,...e},s=Math.min(1,Math.max(0,t.tension)),i=1-s;return function e(n,r){const a=I(n,r,{fillHoles:t.fillHoles});if(a.length){if(a.length>1)return J.join(a.map((t=>e(t.pathCoordinates,t.valueData))));{if(n=a[0].pathCoordinates,r=a[0].valueData,n.length<=4)return K()(n,r);const e=(new J).move(n[0],n[1],!1,r[0]),t=!1;for(let a=0,o=n.length;o-2*Number(!t)>a;a+=2){const t=[{x:+n[a-2],y:+n[a-1]},{x:+n[a],y:+n[a+1]},{x:+n[a+2],y:+n[a+3]},{x:+n[a+4],y:+n[a+5]}];o-4===a?t[3]=t[2]:a||(t[0]={x:+n[a],y:+n[a+1]}),e.curve(s*(-t[0].x+6*t[1].x+t[2].x)/6+i*t[2].x,s*(-t[0].y+6*t[1].y+t[2].y)/6+i*t[2].y,s*(t[1].x+6*t[2].x-t[3].x)/6+i*t[2].x,s*(t[1].y+6*t[2].y-t[3].y)/6+i*t[2].y,t[2].x,t[2].y,!1,r[(a+2)/2])}return e}}return K()([],[])}},monotoneCubic:ee});class se{on(e,t){const{allListeners:s,listeners:i}=this;"*"===e?s.add(t):(i.has(e)||i.set(e,new Set),i.get(e).add(t))}off(e,t){const{allListeners:s,listeners:i}=this;if("*"===e)t?s.delete(t):s.clear();else if(i.has(e)){const s=i.get(e);t?s.delete(t):s.clear(),s.size||i.delete(e)}}emit(e,t){const{allListeners:s,listeners:i}=this;i.has(e)&&i.get(e).forEach((e=>e(t))),s.forEach((s=>s(e,t)))}constructor(){this.listeners=new Map,this.allListeners=new Set}}const ie=new WeakMap;class ne{update(e,t){let s=arguments.length>2&&void 0!==arguments[2]&&arguments[2];var i;(e&&(this.data=e||{},this.data.labels=this.data.labels||[],this.data.series=this.data.series||[],this.eventEmitter.emit("data",{type:"update",data:this.data})),t)&&(this.options=m({},s?this.options:this.defaultOptions,t),this.initializeTimeoutId||(null===(i=this.optionsProvider)||void 0===i||i.removeMediaQueryListeners(),this.optionsProvider=F(this.options,this.responsiveOptions,this.eventEmitter)));return!this.initializeTimeoutId&&this.optionsProvider&&this.createChart(this.optionsProvider.getCurrentOptions()),this}detach(){var e;this.initializeTimeoutId?window.clearTimeout(this.initializeTimeoutId):(window.removeEventListener("resize",this.resizeListener),null===(e=this.optionsProvider)||void 0===e||e.removeMediaQueryListeners());return ie.delete(this.container),this}on(e,t){return this.eventEmitter.on(e,t),this}off(e,t){return this.eventEmitter.off(e,t),this}initialize(){window.addEventListener("resize",this.resizeListener),this.optionsProvider=F(this.options,this.responsiveOptions,this.eventEmitter),this.eventEmitter.on("optionsChanged",(()=>this.update())),this.options.plugins&&this.options.plugins.forEach((e=>{Array.isArray(e)?e[0](this,e[1]):e(this)})),this.eventEmitter.emit("data",{type:"initial",data:this.data}),this.createChart(this.optionsProvider.getCurrentOptions()),this.initializeTimeoutId=null}constructor(e,t,s,i,n){this.data=t,this.defaultOptions=s,this.options=i,this.responsiveOptions=n,this.eventEmitter=new se,this.resizeListener=()=>this.update(),this.initializeTimeoutId=setTimeout((()=>this.initialize()),0);const r="string"==typeof e?document.querySelector(e):e;if(!r)throw new Error("Target element ".concat("string"==typeof e?'"'.concat(e,'"'):""," is not found"));this.container=r;const a=ie.get(r);a&&a.detach(),ie.set(r,this)}}const re={x:{pos:"x",len:"width",dir:"horizontal",rectStart:"x1",rectEnd:"x2",rectOffset:"y2"},y:{pos:"y",len:"height",dir:"vertical",rectStart:"y2",rectEnd:"y1",rectOffset:"x1"}};class ae{createGridAndLabels(e,t,s,i){const n="x"===this.units.pos?s.axisX:s.axisY,r=this.ticks.map(((e,t)=>this.projectValue(e,t))),a=this.ticks.map(n.labelInterpolationFnc);r.forEach(((o,l)=>{const h=a[l],c={x:0,y:0};let u;u=r[l+1]?r[l+1]-o:Math.max(this.axisLength-o,this.axisLength/this.ticks.length),""!==h&&w(h)||("x"===this.units.pos?(o=this.chartRect.x1+o,c.x=s.axisX.labelOffset.x,"start"===s.axisX.position?c.y=this.chartRect.padding.top+s.axisX.labelOffset.y+5:c.y=this.chartRect.y1+s.axisX.labelOffset.y+5):(o=this.chartRect.y1-o,c.y=s.axisY.labelOffset.y-u,"start"===s.axisY.position?c.x=this.chartRect.padding.left+s.axisY.labelOffset.x:c.x=this.chartRect.x2+s.axisY.labelOffset.x+10),n.showGrid&&H(o,l,this,this.gridOffset,this.chartRect[this.counterUnits.len](),e,[s.classNames.grid,s.classNames[this.units.dir]],i),n.showLabel&&Q(o,u,l,h,this,n.offset,c,t,[s.classNames.label,s.classNames[this.units.dir],"start"===n.position?s.classNames[n.position]:s.classNames.end],i))}))}constructor(e,t,s){this.units=e,this.chartRect=t,this.ticks=s,this.counterUnits=e===re.x?re.y:re.x,this.axisLength=t[this.units.rectEnd]-t[this.units.rectStart],this.gridOffset=t[this.units.rectOffset]}}class oe extends ae{projectValue(e){const t=Number(L(e,this.units.pos));return this.axisLength*(t-this.bounds.min)/this.bounds.range}constructor(e,t,s,i){const n=i.highLow||S(t,i,e.pos),r=d(s[e.rectEnd]-s[e.rectStart],n,i.scaleMinSpace||20,i.onlyInteger),a={min:r.min,max:r.max};super(e,s,r.values),this.bounds=r,this.range=a}}class le extends ae{projectValue(e,t){return this.stepLength*t}constructor(e,t,s,i){const n=i.ticks||[];super(e,s,n);const r=Math.max(1,n.length-(i.stretch?1:0));this.stepLength=this.axisLength/r,this.stretch=Boolean(i.stretch)}}function he(e,t,s){var i;if(v(e,"name")&&e.name&&(null===(i=t.series)||void 0===i?void 0:i[e.name])){const i=(null==t?void 0:t.series[e.name])[s];return void 0===i?t[s]:i}return t[s]}const ce={axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:p,type:void 0},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:p,type:void 0,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,showLine:!0,showPoint:!0,showArea:!1,areaBase:0,lineSmooth:!0,showGridBackground:!1,low:void 0,high:void 0,chartPadding:{top:15,right:15,bottom:5,left:10},fullWidth:!1,reverseData:!1,classNames:{chart:"ct-chart-line",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",line:"ct-line",point:"ct-point",area:"ct-area",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}};const ue={axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:p,scaleMinSpace:30,onlyInteger:!1},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:p,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,high:void 0,low:void 0,referenceValue:0,chartPadding:{top:15,right:15,bottom:5,left:10},seriesBarDistance:15,stackBars:!1,stackMode:"accumulate",horizontalBars:!1,distributeSeries:!1,reverseData:!1,showGridBackground:!1,classNames:{chart:"ct-chart-bar",horizontalBars:"ct-horizontal-bars",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",bar:"ct-bar",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}};const de={width:void 0,height:void 0,chartPadding:5,classNames:{chartPie:"ct-chart-pie",chartDonut:"ct-chart-donut",series:"ct-series",slicePie:"ct-slice-pie",sliceDonut:"ct-slice-donut",label:"ct-label"},startAngle:0,total:void 0,donut:!1,donutWidth:60,showLabel:!0,labelOffset:0,labelPosition:"inside",labelInterpolationFnc:p,labelDirection:"neutral",ignoreEmptyValues:!1,preventOverlappingLabelOffset:0};function me(e,t,s){const i=t.x>e.x;return i&&"explode"===s||!i&&"implode"===s?"start":i&&"implode"===s||!i&&"explode"===s?"end":"middle"}e.AutoScaleAxis=oe,e.Axis=ae,e.BarChart=class extends ne{createChart(e){const{data:t}=this,s=B(t,e.reverseData,e.horizontalBars?"x":"y",!0),i=G(this.container,e.width,e.height,e.classNames.chart+(e.horizontalBars?" "+e.classNames.horizontalBars:""),e.viewBox),n=e.stackBars&&!0!==e.stackMode&&s.series.length?S([(a=s.series,x(a,(function(){for(var e=arguments.length,t=new Array(e),s=0;s<e;s++)t[s]=arguments[s];return Array.from(t).reduce(((e,t)=>({x:e.x+(v(t,"x")?t.x:0),y:e.y+(v(t,"y")?t.y:0)})),{x:0,y:0})})))],e,e.horizontalBars?"x":"y"):S(s.series,e,e.horizontalBars?"x":"y");var a;this.svg=i;const o=i.elem("g").addClass(e.classNames.gridGroup),l=i.elem("g"),h=i.elem("g").addClass(e.classNames.labelGroup);"number"==typeof e.high&&(n.high=e.high),"number"==typeof e.low&&(n.low=e.low);const c=U(i,e);let u;const d=e.distributeSeries&&e.stackBars?s.labels.slice(0,1):s.labels;let m,p,f;e.horizontalBars?(u=p=void 0===e.axisX.type?new oe(re.x,s.series,c,{...e.axisX,highLow:n,referenceValue:0}):new e.axisX.type(re.x,s.series,c,{...e.axisX,highLow:n,referenceValue:0}),m=f=void 0===e.axisY.type?new le(re.y,s.series,c,{ticks:d}):new e.axisY.type(re.y,s.series,c,e.axisY)):(m=p=void 0===e.axisX.type?new le(re.x,s.series,c,{ticks:d}):new e.axisX.type(re.x,s.series,c,e.axisX),u=f=void 0===e.axisY.type?new oe(re.y,s.series,c,{...e.axisY,highLow:n,referenceValue:0}):new e.axisY.type(re.y,s.series,c,{...e.axisY,highLow:n,referenceValue:0}));const g=e.horizontalBars?c.x1+u.projectValue(0):c.y1-u.projectValue(0),w="accumulate"===e.stackMode,b="accumulate-relative"===e.stackMode,E=[],N=[];let C=E;m.createGridAndLabels(o,h,e,this.eventEmitter),u.createGridAndLabels(o,h,e,this.eventEmitter),e.showGridBackground&&T(o,c,e.classNames.gridBackground,this.eventEmitter),A(t.series,((i,n)=>{const a=n-(t.series.length-1)/2;let o;o=e.distributeSeries&&!e.stackBars?m.axisLength/s.series.length/2:e.distributeSeries&&e.stackBars?m.axisLength/2:m.axisLength/s.series[n].length/2;const h=l.elem("g"),d=v(i,"name")&&i.name,x=v(i,"className")&&i.className,A=v(i,"meta")?i.meta:void 0;d&&h.attr({"ct:series-name":d}),A&&h.attr({"ct:meta":P(A)}),h.addClass([e.classNames.series,x||"".concat(e.classNames.series,"-").concat(r(n))].join(" ")),s.series[n].forEach(((t,r)=>{const l=v(t,"x")&&t.x,d=v(t,"y")&&t.y;let x,A;x=e.distributeSeries&&!e.stackBars?n:e.distributeSeries&&e.stackBars?0:r,A=e.horizontalBars?{x:c.x1+u.projectValue(l||0,r,s.series[n]),y:c.y1-m.projectValue(d||0,x,s.series[n])}:{x:c.x1+m.projectValue(l||0,x,s.series[n]),y:c.y1-u.projectValue(d||0,r,s.series[n])},m instanceof le&&(m.stretch||(A[m.units.pos]+=o*(e.horizontalBars?-1:1)),A[m.units.pos]+=e.stackBars||e.distributeSeries?0:a*e.seriesBarDistance*(e.horizontalBars?-1:1)),b&&(C=d>=0||l>=0?E:N);const O=C[r]||g;if(C[r]=O-(g-A[m.counterUnits.pos]),void 0===t)return;const L={["".concat(m.units.pos,"1")]:A[m.units.pos],["".concat(m.units.pos,"2")]:A[m.units.pos]};e.stackBars&&(w||b||!e.stackMode)?(L["".concat(m.counterUnits.pos,"1")]=O,L["".concat(m.counterUnits.pos,"2")]=C[r]):(L["".concat(m.counterUnits.pos,"1")]=g,L["".concat(m.counterUnits.pos,"2")]=A[m.counterUnits.pos]),L.x1=Math.min(Math.max(L.x1,c.x1),c.x2),L.x2=Math.min(Math.max(L.x2,c.x1),c.x2),L.y1=Math.min(Math.max(L.y1,c.y2),c.y1),L.y2=Math.min(Math.max(L.y2,c.y2),c.y1);const S=M(i,r),B=h.elem("line",L,e.classNames.bar).attr({"ct:value":[l,d].filter(y).join(","),"ct:meta":P(S)});this.eventEmitter.emit("draw",{type:"bar",value:t,index:r,meta:S,series:i,seriesIndex:n,axisX:p,axisY:f,chartRect:c,group:h,element:B,...L})}))}),e.reverseData),this.eventEmitter.emit("created",{chartRect:c,axisX:p,axisY:f,svg:i,options:e})}constructor(e,t,s,i){super(e,t,ue,m({},ue,s),i),this.data=t}},e.BaseChart=ne,e.EPSILON=a,e.EventEmitter=se,e.FixedScaleAxis=class extends ae{projectValue(e){const t=Number(L(e,this.units.pos));return this.axisLength*(t-this.range.min)/(this.range.max-this.range.min)}constructor(e,t,s,i){const n=i.highLow||S(t,i,e.pos),r=i.divisor||1,a=(i.ticks||f(r,(e=>n.low+(n.high-n.low)/r*e))).sort(((e,t)=>Number(e)-Number(t))),o={min:n.low,max:n.high};super(e,s,a),this.range=o}},e.Interpolation=te,e.LineChart=class extends ne{createChart(e){const{data:t}=this,s=B(t,e.reverseData,!0),i=G(this.container,e.width,e.height,e.classNames.chart,e.viewBox);this.svg=i;const n=i.elem("g").addClass(e.classNames.gridGroup),a=i.elem("g"),o=i.elem("g").addClass(e.classNames.labelGroup),l=U(i,e);let h,c;h=void 0===e.axisX.type?new le(re.x,s.series,l,{...e.axisX,ticks:s.labels,stretch:e.fullWidth}):new e.axisX.type(re.x,s.series,l,e.axisX),c=void 0===e.axisY.type?new oe(re.y,s.series,l,{...e.axisY,high:y(e.high)?e.high:e.axisY.high,low:y(e.low)?e.low:e.axisY.low}):new e.axisY.type(re.y,s.series,l,e.axisY),h.createGridAndLabels(n,o,e,this.eventEmitter),c.createGridAndLabels(n,o,e,this.eventEmitter),e.showGridBackground&&T(n,l,e.classNames.gridBackground,this.eventEmitter),A(t.series,((t,i)=>{const n=a.elem("g"),o=v(t,"name")&&t.name,u=v(t,"className")&&t.className,d=v(t,"meta")?t.meta:void 0;o&&n.attr({"ct:series-name":o}),d&&n.attr({"ct:meta":P(d)}),n.addClass([e.classNames.series,u||"".concat(e.classNames.series,"-").concat(r(i))].join(" "));const m=[],p=[];s.series[i].forEach(((e,n)=>{const r={x:l.x1+h.projectValue(e,n,s.series[i]),y:l.y1-c.projectValue(e,n,s.series[i])};m.push(r.x,r.y),p.push({value:e,valueIndex:n,meta:M(t,n)})}));const f={lineSmooth:he(t,e,"lineSmooth"),showPoint:he(t,e,"showPoint"),showLine:he(t,e,"showLine"),showArea:he(t,e,"showArea"),areaBase:he(t,e,"areaBase")};let g;g="function"==typeof f.lineSmooth?f.lineSmooth:f.lineSmooth?ee():K();const x=g(m,p);if(f.showPoint&&x.pathElements.forEach((s=>{const{data:r}=s,a=n.elem("line",{x1:s.x,y1:s.y,x2:s.x+.01,y2:s.y},e.classNames.point);if(r){let e,t;v(r.value,"x")&&(e=r.value.x),v(r.value,"y")&&(t=r.value.y),a.attr({"ct:value":[e,t].filter(y).join(","),"ct:meta":P(r.meta)})}this.eventEmitter.emit("draw",{type:"point",value:null==r?void 0:r.value,index:(null==r?void 0:r.valueIndex)||0,meta:null==r?void 0:r.meta,series:t,seriesIndex:i,axisX:h,axisY:c,group:n,element:a,x:s.x,y:s.y,chartRect:l})})),f.showLine){const r=n.elem("path",{d:x.stringify()},e.classNames.line,!0);this.eventEmitter.emit("draw",{type:"line",values:s.series[i],path:x.clone(),chartRect:l,index:i,series:t,seriesIndex:i,meta:d,axisX:h,axisY:c,group:n,element:r})}if(f.showArea&&c.range){const r=Math.max(Math.min(f.areaBase,c.range.max),c.range.min),a=l.y1-c.projectValue(r);x.splitByCommand("M").filter((e=>e.pathElements.length>1)).map((e=>{const t=e.pathElements[0],s=e.pathElements[e.pathElements.length-1];return e.clone(!0).position(0).remove(1).move(t.x,a).line(t.x,t.y).position(e.pathElements.length+1).line(s.x,a)})).forEach((r=>{const a=n.elem("path",{d:r.stringify()},e.classNames.area,!0);this.eventEmitter.emit("draw",{type:"area",values:s.series[i],path:r.clone(),series:t,seriesIndex:i,axisX:h,axisY:c,chartRect:l,index:i,group:n,element:a,meta:d})}))}}),e.reverseData),this.eventEmitter.emit("created",{chartRect:l,axisX:h,axisY:c,svg:i,options:e})}constructor(e,t,s,i){super(e,t,ce,m({},ce,s),i),this.data=t}},e.PieChart=class extends ne{moveLabel(e,t,s,i){e.y>t.y-s&&e.y<t.y+s&&e.x>t.x-i*s&&e.x<t.x+i*s&&(e.y-=s,e.x-=s,this.moveLabel(e,t,s,i))}createChart(e){const{data:t}=this,s=B(t),i=[];let a;const o=[];let l,h=e.startAngle;const c=G(this.container,e.width,e.height,e.donut?e.classNames.chartDonut:e.classNames.chartPie,e.viewBox);this.svg=c;const d=U(c,e);let m=Math.min(d.width()/2,d.height()/2);const p=e.total||s.series.reduce(g,0),f=n(e.donutWidth);"%"===f.unit&&(f.value*=m/100),m-=e.donut?f.value/2:0,l="outside"===e.labelPosition||e.donut?m:"center"===e.labelPosition?0:m/2,e.labelOffset&&(l+=e.labelOffset);const x={x:d.x1+d.width()/2,y:d.y2+d.height()/2},y=1===t.series.filter((e=>v(e,"value")?0!==e.value:0!==e)).length;t.series.forEach(((e,t)=>i[t]=c.elem("g"))),e.showLabel&&(a=c.elem("g")),t.series.forEach(((n,c)=>{var g,b;if(0===s.series[c]&&e.ignoreEmptyValues)return;const E=v(n,"name")&&n.name,A=v(n,"className")&&n.className,M=v(n,"meta")?n.meta:void 0;E&&i[c].attr({"ct:series-name":E}),i[c].addClass([null===(g=e.classNames)||void 0===g?void 0:g.series,A||"".concat(null===(b=e.classNames)||void 0===b?void 0:b.series,"-").concat(r(c))].join(" "));let N=p>0?h+s.series[c]/p*360:0;const C=Math.max(0,h-(0===c||y?0:.2));N-C>=359.99&&(N=C+359.99);const O=u(x.x,x.y,m,C),L=u(x.x,x.y,m,N),S=new J(!e.donut).move(L.x,L.y).arc(m,m,0,Number(N-h>180),0,O.x,O.y);e.donut||S.line(x.x,x.y);const B=i[c].elem("path",{d:S.stringify()},e.donut?e.classNames.sliceDonut:e.classNames.slicePie);if(B.attr({"ct:value":s.series[c],"ct:meta":P(M)}),e.donut&&B.attr({style:"stroke-width: "+f.value+"px"}),this.eventEmitter.emit("draw",{type:"slice",value:s.series[c],totalDataSum:p,index:c,meta:M,series:n,group:i[c],element:B,path:S.clone(),center:x,radius:m,startAngle:h,endAngle:N,chartRect:d}),e.showLabel){let i,r;i=1===t.series.length?{x:x.x,y:x.y}:u(x.x,x.y,l,h+(N-h)/2),r=s.labels&&!w(s.labels[c])?s.labels[c]:s.series[c];const m=e.labelInterpolationFnc(r,c);if(m||0===m){if(e.preventOverlappingLabelOffset){const t=e.preventOverlappingLabelOffset,n=String(s.labels[c]).length;o.forEach((e=>{this.moveLabel(i,e,t,n)})),o.push(i)}const t=a.elem("text",{dx:i.x,dy:i.y,"text-anchor":me(x,i,e.labelDirection)},e.classNames.label).text(String(m));this.eventEmitter.emit("draw",{type:"label",index:c,group:a,element:t,text:""+m,chartRect:d,series:n,meta:M,...i})}}h=N})),this.eventEmitter.emit("created",{chartRect:d,svg:c,options:e})}constructor(e,t,s,i){super(e,t,de,m({},de,s),i),this.data=t}},e.StepAxis=le,e.Svg=R,e.SvgList=X,e.SvgPath=J,e.alphaNumerate=r,e.axisUnits=re,e.createChartRect=U,e.createGrid=H,e.createGridBackground=T,e.createLabel=Q,e.createSvg=G,e.deserialize=function(e){if("string"!=typeof e)return e;if("NaN"===e)return NaN;let t=e=Object.keys(s).reduce(((e,t)=>e.replaceAll(s[t],t)),e);if("string"==typeof e)try{t=JSON.parse(e),t=void 0!==t.data?t.data:t}catch(e){}return t},e.determineAnchorPosition=me,e.each=A,e.easings=V,e.ensureUnit=i,e.escapingMap=s,e.extend=m,e.getBounds=d,e.getHighLow=S,e.getMetaData=M,e.getMultiValue=L,e.getNumberOrUndefined=b,e.getSeriesOption=he,e.isArrayOfArrays=E,e.isArrayOfSeries=C,e.isDataHoleValue=N,e.isFalseyButZero=w,e.isMultiValue=O,e.isNumeric=y,e.namespaces=t,e.noop=p,e.normalizeData=B,e.normalizePadding=D,e.optionsProvider=F,e.orderOfMagnitude=o,e.polarToCartesian=u,e.precision=8,e.projectLength=l,e.quantity=n,e.rho=c,e.roundWithPrecision=h,e.safeHasProperty=v,e.serialMap=x,e.serialize=P,e.splitIntoSegments=I,e.sum=g,e.times=f,Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
8
|
+
//# sourceMappingURL=index.umd.js.map
|
monopyly/static/css/style.css
CHANGED
|
@@ -1340,7 +1340,34 @@ form .autocomplete-box .item.active {
|
|
|
1340
1340
|
/*
|
|
1341
1341
|
* Style tables for presenting transaction details
|
|
1342
1342
|
*/
|
|
1343
|
+
.transactions-container {
|
|
1344
|
+
display: flex;
|
|
1345
|
+
flex-direction: column;
|
|
1346
|
+
align-items: center;
|
|
1347
|
+
width: 100%;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.transactions-container #more-transactions.button {
|
|
1351
|
+
display: flex;
|
|
1352
|
+
justify-content: space-between;
|
|
1353
|
+
align-items: center;
|
|
1354
|
+
margin: 20px 0;
|
|
1355
|
+
padding: 8px 12px;
|
|
1356
|
+
border: 1px solid var(--border-gray);
|
|
1357
|
+
border-radius: 10px;
|
|
1358
|
+
background-color: #fafafa;
|
|
1359
|
+
color: #666666;
|
|
1360
|
+
font-size: 9pt;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
.transactions-container #more-transactions.button img {
|
|
1364
|
+
height: 12px;
|
|
1365
|
+
width: 12px;
|
|
1366
|
+
margin-right: 10px;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1343
1369
|
.transactions-table {
|
|
1370
|
+
width: 100%;
|
|
1344
1371
|
font-size: 9pt;
|
|
1345
1372
|
}
|
|
1346
1373
|
|
|
@@ -2386,6 +2413,8 @@ form .autocomplete-box .item.active {
|
|
|
2386
2413
|
#homepage-panels {
|
|
2387
2414
|
display: flex;
|
|
2388
2415
|
justify-content: space-between;
|
|
2416
|
+
align-items: stretch;
|
|
2417
|
+
gap: 20px;
|
|
2389
2418
|
width: 100%;
|
|
2390
2419
|
}
|
|
2391
2420
|
@media screen and (max-width: 600px) {
|
|
@@ -2395,29 +2424,21 @@ form .autocomplete-box .item.active {
|
|
|
2395
2424
|
}
|
|
2396
2425
|
}
|
|
2397
2426
|
|
|
2427
|
+
#homepage-panels .panel-column {
|
|
2428
|
+
display: flex;
|
|
2429
|
+
flex-direction: column;
|
|
2430
|
+
flex-grow: 1;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2398
2433
|
#homepage-panels .panel {
|
|
2399
2434
|
display: flex;
|
|
2400
2435
|
flex-direction: column;
|
|
2401
2436
|
flex-grow: 1;
|
|
2402
|
-
margin: 20px;
|
|
2403
2437
|
padding: 50px;
|
|
2404
2438
|
border: 1px solid #f5f5f5;
|
|
2405
2439
|
box-shadow: 2px 2px 5px var(--silver-dollar);
|
|
2406
2440
|
background-color: #fafafa;
|
|
2407
2441
|
}
|
|
2408
|
-
@media screen and (max-width: 600px) {
|
|
2409
|
-
/* Mobile layout */
|
|
2410
|
-
#homepage-panels .panel {
|
|
2411
|
-
margin: 20px 0;
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
|
-
#homepage-panels div:first-of-type {
|
|
2416
|
-
margin-left: 0;
|
|
2417
|
-
}
|
|
2418
|
-
#homepage-panels div:last-of-type {
|
|
2419
|
-
margin-right: 0;
|
|
2420
|
-
}
|
|
2421
2442
|
|
|
2422
2443
|
#homepage-panels .panel h2 {
|
|
2423
2444
|
margin: 0 0 15px;
|
|
@@ -3460,6 +3481,10 @@ form#pay #make-payment[type="submit"] #prompt {
|
|
|
3460
3481
|
min-height: 400px;
|
|
3461
3482
|
}
|
|
3462
3483
|
|
|
3484
|
+
#category-chart.ct-chart svg {
|
|
3485
|
+
overflow: visible;
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3463
3488
|
#category-chart text.ct-label {
|
|
3464
3489
|
transition: fill 0.1s ease;
|
|
3465
3490
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|