arthexis 0.1.14__py3-none-any.whl → 0.1.16__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.
Potentially problematic release.
This version of arthexis might be problematic. Click here for more details.
- {arthexis-0.1.14.dist-info → arthexis-0.1.16.dist-info}/METADATA +3 -2
- {arthexis-0.1.14.dist-info → arthexis-0.1.16.dist-info}/RECORD +41 -39
- config/urls.py +5 -0
- core/admin.py +200 -9
- core/admindocs.py +44 -3
- core/apps.py +1 -1
- core/backends.py +44 -8
- core/entity.py +17 -1
- core/github_issues.py +12 -7
- core/log_paths.py +24 -10
- core/mailer.py +9 -5
- core/models.py +92 -23
- core/release.py +173 -2
- core/system.py +411 -4
- core/tasks.py +5 -1
- core/test_system_info.py +16 -0
- core/tests.py +280 -0
- core/views.py +252 -38
- nodes/admin.py +25 -1
- nodes/models.py +99 -6
- nodes/rfid_sync.py +15 -0
- nodes/tests.py +142 -3
- nodes/utils.py +3 -0
- ocpp/consumers.py +38 -0
- ocpp/models.py +19 -4
- ocpp/tasks.py +156 -2
- ocpp/test_rfid.py +44 -2
- ocpp/tests.py +111 -1
- pages/admin.py +188 -5
- pages/context_processors.py +20 -1
- pages/middleware.py +4 -0
- pages/models.py +39 -1
- pages/module_defaults.py +156 -0
- pages/tasks.py +74 -0
- pages/tests.py +629 -8
- pages/urls.py +2 -0
- pages/utils.py +11 -0
- pages/views.py +106 -38
- {arthexis-0.1.14.dist-info → arthexis-0.1.16.dist-info}/WHEEL +0 -0
- {arthexis-0.1.14.dist-info → arthexis-0.1.16.dist-info}/licenses/LICENSE +0 -0
- {arthexis-0.1.14.dist-info → arthexis-0.1.16.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arthexis
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.1.16
|
|
4
|
+
Summary: Power & Energy Infrastructure
|
|
5
5
|
Author-email: "Rafael J. Guillén-Osorio" <tecnologia@gelectriic.com>
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
7
7
|
Project-URL: Repository, https://github.com/arthexis/arthexis
|
|
@@ -118,6 +118,7 @@ Dynamic: license-file
|
|
|
118
118
|
|
|
119
119
|
[](https://github.com/arthexis/arthexis/actions/workflows/coverage.yml) [](https://github.com/arthexis/arthexis/blob/main/docs/development/ocpp-user-manual.md)
|
|
120
120
|
|
|
121
|
+
|
|
121
122
|
## Purpose
|
|
122
123
|
|
|
123
124
|
Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Narrative) [Django](https://www.djangoproject.com/)-based [software suite](https://en.wikipedia.org/wiki/Software_suite) that centralizes tools for managing [electric vehicle charging infrastructure](https://en.wikipedia.org/wiki/Charging_station) and orchestrating [energy](https://en.wikipedia.org/wiki/Energy)-related [products](https://en.wikipedia.org/wiki/Product_(business)) and [services](https://en.wikipedia.org/wiki/Service_(economics)).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
arthexis-0.1.
|
|
1
|
+
arthexis-0.1.16.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
2
2
|
config/__init__.py,sha256=AwpOX7il-DAOmkdJ5dVfVJ3CWWebn1lHyQNmkw1EkDw,103
|
|
3
3
|
config/active_app.py,sha256=KJqYh-o91nPQjVXPEdbiJHzsI6cN9IZsBZ9O3iZ6Hyc,373
|
|
4
4
|
config/asgi.py,sha256=T-0QSbtieEWKPIDkEcEdd-q6qjK8ZCwwjCaISOBkWdM,1296
|
|
@@ -12,98 +12,100 @@ config/middleware.py,sha256=mDU5tye8H4WCjpJqocwd0vmrzoVEYwdz9WTP4Hcr6dI,719
|
|
|
12
12
|
config/offline.py,sha256=X-yDcyoI4C44Y27lpkUwszY_09GwwFfazEsthKJpQ70,1382
|
|
13
13
|
config/settings.py,sha256=2ANaLD4_Vq3E84rDA2ulqK_DT_hu89Zj4ED5FVEjPBA,21427
|
|
14
14
|
config/settings_helpers.py,sha256=0BdBciUHIkwsWa0vV_RKAd4wDuEzgE7G-42XYiES4YQ,3127
|
|
15
|
-
config/urls.py,sha256=
|
|
15
|
+
config/urls.py,sha256=lXl2KKsbIehjOW0W6FHAsxkZJ-3DAo37f2ICb1dvvz8,5320
|
|
16
16
|
config/wsgi.py,sha256=zU_mKlya6hejQ21PxKacTui3dUWd4ca_-YJNSYAoMX0,433
|
|
17
17
|
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
core/admin.py,sha256=
|
|
18
|
+
core/admin.py,sha256=c3Z5UE3cBXtJR6pQcLQYR0BWe6W7SBNvR0W9lG8b2ZU,143397
|
|
19
19
|
core/admin_history.py,sha256=XZ4b0ryufIka-xcwboK3DzmOL-INSx5Y2fJO-aJdV70,1783
|
|
20
|
-
core/admindocs.py,sha256=
|
|
21
|
-
core/apps.py,sha256=
|
|
20
|
+
core/admindocs.py,sha256=ycD0bJ_VE6rTGf9ebXTiKdYkD8Y8hD2oQ4HxxoBURCM,6756
|
|
21
|
+
core/apps.py,sha256=L_UMYI72-5jTo6nt8mfCbgdLhlP32D-8k76EZw0QyAA,14348
|
|
22
22
|
core/auto_upgrade.py,sha256=1EffHHFylgydWdZM_id6CppV0QqBtdNw7cwBYVdbNdk,1715
|
|
23
|
-
core/backends.py,sha256=
|
|
23
|
+
core/backends.py,sha256=y12jggdsn90bqBDW8L0kGy2lS8-P8Ym2PmPJ8AiPjYc,10343
|
|
24
24
|
core/changelog.py,sha256=grMvuEektkymwvkC1ubXFZF2JFopPybT82k4rUIlfmo,10840
|
|
25
|
-
core/entity.py,sha256=
|
|
25
|
+
core/entity.py,sha256=o4VteOXePGEsIWJFZ3fpq3DZsdWr3hpQ9A6kFbKosSE,4844
|
|
26
26
|
core/environment.py,sha256=JLcvxAwU3OTL8O6kzwcUCFNZ3T28KanHrU_4mDBFamU,1584
|
|
27
27
|
core/fields.py,sha256=d-qGahdcv4SRcO4fwCJ6_-NnEAP5xW0k3kODdAAAHSA,5412
|
|
28
28
|
core/form_fields.py,sha256=h2xT8sO8EWbznsiARkxukFk69yoW6mQwqpgonA-d6aA,2496
|
|
29
29
|
core/github_helper.py,sha256=fkjoUPwOB19zbGuk39LNLJ5AbIVKFf3rNCtnu-JISIc,5733
|
|
30
|
-
core/github_issues.py,sha256=
|
|
30
|
+
core/github_issues.py,sha256=qIygOk1ZCki0eB-9o1poJ2BnaMnbHH4ewVE36hqHUuo,5223
|
|
31
31
|
core/github_repos.py,sha256=8KCxcEiO2Ltgde7UDTAFOyHTm_eBeZYUIZegEbrjkWA,1690
|
|
32
32
|
core/lcd_screen.py,sha256=WtHMlSoZXKOsdM0d-v-f8ul-LSA6FA1bEWFwho1t6s8,2573
|
|
33
33
|
core/liveupdate.py,sha256=22m0ueQ10-6b-9pQJHY0_5WRYA98fysXKEXOWzIr550,691
|
|
34
|
-
core/log_paths.py,sha256=
|
|
35
|
-
core/mailer.py,sha256=
|
|
34
|
+
core/log_paths.py,sha256=lxvgXPgJtVNZ-kYrqV8VFle4GFQrSxG-yRTglqvclmU,3318
|
|
35
|
+
core/mailer.py,sha256=JpW0RnD9uZ4O-wvlqeW7CMw95IFeCSkdvbankJDwHq0,2886
|
|
36
36
|
core/middleware.py,sha256=j19K9SX-Emkv7BDDtAacR9g6RWsxhKHwCc8w23JFvMM,3388
|
|
37
|
-
core/models.py,sha256=
|
|
37
|
+
core/models.py,sha256=cVfk96ujtn7Re7YiHm8CU1Q_URrDjJdsEwi2-3vWyWE,124680
|
|
38
38
|
core/notifications.py,sha256=LYktoKM5k4q7YYWAJuqdeKM-p0Q-3gXgfqdq71qLS68,3916
|
|
39
39
|
core/public_wifi.py,sha256=yydLgxOo9DmJJbM4X_23wGR3gxL3YzHno54v9GssuFA,7213
|
|
40
40
|
core/reference_utils.py,sha256=jeox3V4cZNxzM2Jj31g_mdb3O55zy9S2iXAZu70R1Zc,3627
|
|
41
|
-
core/release.py,sha256=
|
|
41
|
+
core/release.py,sha256=tcRddwl0_TugcmlGlTB_7gPzICfAK2wjGtdWLVg_eaE,29756
|
|
42
42
|
core/rfid_import_export.py,sha256=petyhPvL0WUpehc6uGUDUhjYQ9AVvc6O49zuhDs6YFw,3516
|
|
43
43
|
core/sigil_builder.py,sha256=VLwbrrD7Zr3SHfIDYV-V7uv7LEGiIelCSkeGswHibuc,4843
|
|
44
44
|
core/sigil_context.py,sha256=GCzjfM6fcVvBtSbVNfmE6sx3HU8QnxnXrCIytnNpQzM,439
|
|
45
45
|
core/sigil_resolver.py,sha256=rCsypuX-0oWNfKyM1T9ZLWHY0Ezwhtk4VmI0L3krnsE,11098
|
|
46
|
-
core/system.py,sha256=
|
|
47
|
-
core/tasks.py,sha256=
|
|
46
|
+
core/system.py,sha256=KRIvgEr0XwzdWuvLXmOsK43WRjPoDEuOvPn2nhVz0s4,35704
|
|
47
|
+
core/tasks.py,sha256=PiZ5qKngXP8Q3rVEn_l9zCQ-9tx4Z8v7-t0l5fHnMvA,12535
|
|
48
48
|
core/temp_passwords.py,sha256=FieUnIUeQHmA1DoXvfJ5U6-Ayv3oDz-hSln5s_vNbA4,5271
|
|
49
|
-
core/test_system_info.py,sha256=
|
|
50
|
-
core/tests.py,sha256=
|
|
49
|
+
core/test_system_info.py,sha256=IMPz21KEs6OC5YbL7YaIBdmJVLjRY6MgPuZpldJB5OI,6935
|
|
50
|
+
core/tests.py,sha256=J1ttP5u3UdU242NBoYIfbnpFVzJsmLNI671ChgQQuik,93782
|
|
51
51
|
core/tests_liveupdate.py,sha256=IquU8ztk6zbzC1bQu3Nrr3RzGzuujtPwDkANJHbxg98,510
|
|
52
52
|
core/urls.py,sha256=YPippON1MAP2KeZZ8jHpcLO6mvbnKn1q7fdMv5Vm9dY,425
|
|
53
53
|
core/user_data.py,sha256=02CfvxayELWSWZJCxWpv1Yz7EGg08yEu5MM31Khsi0U,21083
|
|
54
|
-
core/views.py,sha256=
|
|
54
|
+
core/views.py,sha256=N47qQFwSSm0gCKRf2oKJIrYVESt0BIIqnTNh4wMlf6s,85237
|
|
55
55
|
core/widgets.py,sha256=vlR9PlFfZGlkHm5X2cqNXuEBZSj8gmWaR6MO1mMy6kg,6904
|
|
56
56
|
core/workgroup_urls.py,sha256=XR9IqwsSBI8epW7_-hHhWFU9wsyJfZehHwNQBhCgmpM,407
|
|
57
57
|
core/workgroup_views.py,sha256=vtumF3-8YaTD-K6nSd8eYvUyq3ftpvWSEwtcp5B-P6o,2889
|
|
58
58
|
nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
nodes/admin.py,sha256=
|
|
59
|
+
nodes/admin.py,sha256=liGwvusPI3o0RAxhamA8Hs1CMev_DRiFbbNhHWNqIfk,62079
|
|
60
60
|
nodes/apps.py,sha256=AxK-sh9JBJZwNOLjqw9omCQGUQWw-45VRdYH07XhVJU,2732
|
|
61
61
|
nodes/backends.py,sha256=dmmbS0X2YIlCDz2KjoDf_L62dy--nuqZF1rEDoi2JHM,5921
|
|
62
62
|
nodes/dns.py,sha256=D5smXD7Rkh6E4MdL6TBL2WY8GgJg7Rx9z88LZrcMbTw,7048
|
|
63
63
|
nodes/feature_checks.py,sha256=27e4PCkZ8BGWnJCOwMcY2Bo9z7LoeZWiTZuISWGnrzk,3996
|
|
64
64
|
nodes/lcd.py,sha256=iKA8Wmq85KZD52aTzAU8ZmS144_gbdGMOXcE8yuECps,5758
|
|
65
|
-
nodes/models.py,sha256=
|
|
65
|
+
nodes/models.py,sha256=1aoifwRm_VHhYCcP4i7P7ZbiDFRLQkTO57FozknWd9A,62840
|
|
66
66
|
nodes/reports.py,sha256=NRYh3Y0SlZFhx31Zh2K03yO12ZrpxEHEY6T-dODA6WE,12059
|
|
67
|
-
nodes/rfid_sync.py,sha256=
|
|
67
|
+
nodes/rfid_sync.py,sha256=SP_BRUhgYMBH-zXrcM7uShgFSGYmmuIMb1OdcU1e-5U,6956
|
|
68
68
|
nodes/signals.py,sha256=PtOKdQfb08mV1LgSZvn7ZAcfOyy2c3Xkq4AOpBQyUdE,622
|
|
69
69
|
nodes/tasks.py,sha256=ur59ebu9z02idmvy_IvUQt3eu9LWRyyNpkg2szvIHCQ,1522
|
|
70
|
-
nodes/tests.py,sha256=
|
|
70
|
+
nodes/tests.py,sha256=deqjQVAt6sXyI_DdY9zj-Ha3ad1TlnVbhjGhJ-LFqKg,155932
|
|
71
71
|
nodes/urls.py,sha256=HmAxj6sr6nMf0lii_1UX7sNBJUcrkaiKm3R9ofUWhvM,677
|
|
72
|
-
nodes/utils.py,sha256=
|
|
72
|
+
nodes/utils.py,sha256=wt7UuSXGuq79A-g-B6EW3kK49QWJBb7zhhkw4pun4k8,4474
|
|
73
73
|
nodes/views.py,sha256=TyW7exkVaR-o2_XkJXSi9jQ_BygXOE2cQFs4xlI20Xc,22905
|
|
74
74
|
ocpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
75
|
ocpp/admin.py,sha256=9rmECU832lpD7Hcb4_6pkLmQ1D_y9Q6Xl9n8pa3ux8c,31449
|
|
76
76
|
ocpp/apps.py,sha256=i3NqrmIamNEQBT33CIqh7HOSOPmJXCMKrZ-DUd3whqg,842
|
|
77
|
-
ocpp/consumers.py,sha256=
|
|
77
|
+
ocpp/consumers.py,sha256=LgplrJQOfs8CKCtmBcRQLcDVB4Tz7YZpb3I7r2lAorQ,66352
|
|
78
78
|
ocpp/evcs.py,sha256=q1mZrCVSZxXTrtYsDqH6lkeEcJ6tfSC7p9YxkDmpSCw,28883
|
|
79
79
|
ocpp/evcs_discovery.py,sha256=OmrzgaOHwveDRJs8AIhrM3apX8_k2PPXh_oYaYpNW3c,3876
|
|
80
|
-
ocpp/models.py,sha256=
|
|
80
|
+
ocpp/models.py,sha256=QjEaygY7Tl47Q6z2uxP6ftUn4JeD8-JQX2fcwrCaEEg,31631
|
|
81
81
|
ocpp/reference_utils.py,sha256=_UR82GfE93kv4766mHyVIfdhhyYvrT59660r3H6W55M,1072
|
|
82
82
|
ocpp/routing.py,sha256=3kQya-MdJ00778xDmX0esQLBP05P200V45asg-CGNoo,438
|
|
83
83
|
ocpp/simulator.py,sha256=vnyd59QffT79AaPhmfM_jipni_nqfG57X5tXyx1rBoc,28016
|
|
84
84
|
ocpp/status_display.py,sha256=YGFosd5HJETA0DcLdsjvx6EfhZSnI8Aa3cMnHG2WsBE,939
|
|
85
85
|
ocpp/store.py,sha256=rHrP2Iq2ycMFbal1UEJVXb7r4gDtI5yifaE3nT0tjJw,18855
|
|
86
|
-
ocpp/tasks.py,sha256=
|
|
86
|
+
ocpp/tasks.py,sha256=OxIaI4OSLz9AfwLexnXhiBILBimTs3gVrPd197Jguqg,5819
|
|
87
87
|
ocpp/test_export_import.py,sha256=Zp6xUBlRq7XkdKjOs78BhkujNQdklxi4RLxU8c-udWY,4530
|
|
88
|
-
ocpp/test_rfid.py,sha256=
|
|
89
|
-
ocpp/tests.py,sha256=
|
|
88
|
+
ocpp/test_rfid.py,sha256=1DeIfE9diIOV8kJoVH_5HYLOpv6GWQt7_SbZfFlpBZw,34690
|
|
89
|
+
ocpp/tests.py,sha256=2pa6Vw5aMe-ak3LthHyo5jJmibD8I_BpFOapBwWkEdo,177608
|
|
90
90
|
ocpp/transactions_io.py,sha256=YnxI-Tv5UFxv0JuFK3XpoqFYP8eRT8sMuDiqkiMHPtU,7387
|
|
91
91
|
ocpp/urls.py,sha256=3T5O5DSwVk4PbhPx5p4D3UseCWvC5xV5HwJLSM6AfA8,1700
|
|
92
92
|
ocpp/views.py,sha256=LE2mqB5FTno4SYzBWabu9g95o77Ojo2uFtTG6K5W9F0,56311
|
|
93
93
|
pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
|
-
pages/admin.py,sha256=
|
|
94
|
+
pages/admin.py,sha256=f2IYr-nGg9FmafQfDmIRrv01UuXh4mdhFJbnw-ytzHU,27459
|
|
95
95
|
pages/apps.py,sha256=AzUNXQX0yRUX5jus-5EDReDb0nOEY8DBgYaM970u6Io,288
|
|
96
96
|
pages/checks.py,sha256=sM8_hUVM_HOIocvtTb2sY3AaSEvbTnOlO46UchGVd-0,1527
|
|
97
|
-
pages/context_processors.py,sha256=
|
|
97
|
+
pages/context_processors.py,sha256=oINGTI0owXz60FV-XFEjnTkY2xlSDE-W6X1TK8IK800,5072
|
|
98
98
|
pages/defaults.py,sha256=l36APPAZO4ub2A8Pp-lQGujKeOVYcyzU6t7-kOk8VoA,522
|
|
99
99
|
pages/forms.py,sha256=T0atqxdNds3IBP8N-9c5-ACf3iR9FzzmhzK4MOa24e8,7058
|
|
100
|
-
pages/middleware.py,sha256=
|
|
101
|
-
pages/models.py,sha256=
|
|
102
|
-
pages/
|
|
103
|
-
pages/
|
|
104
|
-
pages/
|
|
105
|
-
pages/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
arthexis-0.1.
|
|
109
|
-
arthexis-0.1.
|
|
100
|
+
pages/middleware.py,sha256=6PMLiyuHAHbfLeHwwQxIVy2fJ32ramEO9SHAN05Set4,6967
|
|
101
|
+
pages/models.py,sha256=Sp8e2VB5a7yg4eSUlz_VcsSlAuDVap26xBKYYggxmLM,20952
|
|
102
|
+
pages/module_defaults.py,sha256=R8n6eQDjNRMpO-DW86OFGvyRarju5Bx7Fnb275R_z24,5411
|
|
103
|
+
pages/tasks.py,sha256=ivcba_3wSQ1-cku0oDplzw6vLeQ9hBq3R4TG-LmR5gs,1913
|
|
104
|
+
pages/tests.py,sha256=-4EAtsfW3rmAVOCHaq6X_2rqLj0QEXkvJ5Lr3fY3QRw,125124
|
|
105
|
+
pages/urls.py,sha256=Ne6yYJxgUAMieDpppJ149E-yh-oVi92fARiRPe-n4-s,1166
|
|
106
|
+
pages/utils.py,sha256=CR4D1debgJLGgXsw9kap2ggpe7fIpSoWS_ivbgMNp2k,564
|
|
107
|
+
pages/views.py,sha256=Ye7qGlO7IwkZO0oR1SzCpkEDTtGCJPmDJT-x6QQ8vaQ,45848
|
|
108
|
+
arthexis-0.1.16.dist-info/METADATA,sha256=hQmESfUjXmKX-Sp0jIX99El92v5Q691UVjNobBUflWg,9998
|
|
109
|
+
arthexis-0.1.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
110
|
+
arthexis-0.1.16.dist-info/top_level.txt,sha256=J2a2q8_BWrCZ8H2WFUNMBfO2jz8j2gax6zZh-_1QDac,29
|
|
111
|
+
arthexis-0.1.16.dist-info/RECORD,,
|
config/urls.py
CHANGED
|
@@ -134,6 +134,11 @@ urlpatterns = [
|
|
|
134
134
|
core_views.todo_done,
|
|
135
135
|
name="todo-done",
|
|
136
136
|
),
|
|
137
|
+
path(
|
|
138
|
+
"admin/core/todos/<int:pk>/snapshot/",
|
|
139
|
+
core_views.todo_snapshot,
|
|
140
|
+
name="todo-snapshot",
|
|
141
|
+
),
|
|
137
142
|
path(
|
|
138
143
|
"admin/core/odoo-products/",
|
|
139
144
|
core_views.odoo_products,
|
core/admin.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from io import BytesIO
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
from django import forms
|
|
4
5
|
from django.contrib import admin
|
|
@@ -34,7 +35,8 @@ from import_export.forms import (
|
|
|
34
35
|
from import_export.widgets import ForeignKeyWidget
|
|
35
36
|
from django.contrib.auth.models import Group
|
|
36
37
|
from django.templatetags.static import static
|
|
37
|
-
from django.utils import timezone
|
|
38
|
+
from django.utils import timezone, translation
|
|
39
|
+
from django.utils.formats import date_format
|
|
38
40
|
from django.utils.dateparse import parse_datetime
|
|
39
41
|
from django.utils.html import format_html
|
|
40
42
|
from django.utils.translation import gettext_lazy as _, ngettext
|
|
@@ -55,6 +57,8 @@ from reportlab.pdfbase import pdfmetrics
|
|
|
55
57
|
from reportlab.graphics import renderPDF
|
|
56
58
|
from reportlab.graphics.barcode import qr
|
|
57
59
|
from reportlab.graphics.shapes import Drawing
|
|
60
|
+
from reportlab.lib.styles import getSampleStyleSheet
|
|
61
|
+
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
|
|
58
62
|
from ocpp.models import Transaction
|
|
59
63
|
from ocpp.rfid.utils import build_mode_toggle
|
|
60
64
|
from nodes.models import EmailOutbox
|
|
@@ -424,6 +428,7 @@ class ReleaseManagerAdminForm(forms.ModelForm):
|
|
|
424
428
|
widgets = {
|
|
425
429
|
"pypi_token": forms.Textarea(attrs={"rows": 3, "style": "width: 40em;"}),
|
|
426
430
|
"github_token": forms.Textarea(attrs={"rows": 3, "style": "width: 40em;"}),
|
|
431
|
+
"git_password": forms.Textarea(attrs={"rows": 3, "style": "width: 40em;"}),
|
|
427
432
|
}
|
|
428
433
|
|
|
429
434
|
def __init__(self, *args, **kwargs):
|
|
@@ -448,6 +453,16 @@ class ReleaseManagerAdminForm(forms.ModelForm):
|
|
|
448
453
|
"or an equivalent fine-grained token) and paste it here."
|
|
449
454
|
),
|
|
450
455
|
)
|
|
456
|
+
self.fields["git_username"].help_text = (
|
|
457
|
+
"Username used for HTTPS git pushes (for example, your GitHub username)."
|
|
458
|
+
)
|
|
459
|
+
self.fields["git_password"].help_text = format_html(
|
|
460
|
+
"{} <a href=\"{}\" target=\"_blank\" rel=\"noopener noreferrer\">{}</a>{}",
|
|
461
|
+
"Provide the password or personal access token used for pushing tags. ",
|
|
462
|
+
"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token",
|
|
463
|
+
"docs.github.com/.../creating-a-personal-access-token",
|
|
464
|
+
" If left blank, the GitHub token will be used instead.",
|
|
465
|
+
)
|
|
451
466
|
|
|
452
467
|
|
|
453
468
|
@admin.register(ReleaseManager)
|
|
@@ -460,18 +475,27 @@ class ReleaseManagerAdmin(ProfileAdminMixin, SaveBeforeChangeAction, EntityModel
|
|
|
460
475
|
fieldsets = (
|
|
461
476
|
("Owner", {"fields": ("user", "group")}),
|
|
462
477
|
(
|
|
463
|
-
"
|
|
478
|
+
"PyPI",
|
|
464
479
|
{
|
|
465
480
|
"fields": (
|
|
466
481
|
"pypi_username",
|
|
467
482
|
"pypi_token",
|
|
468
483
|
"pypi_password",
|
|
469
|
-
"github_token",
|
|
470
484
|
"pypi_url",
|
|
471
485
|
"secondary_pypi_url",
|
|
472
486
|
)
|
|
473
487
|
},
|
|
474
488
|
),
|
|
489
|
+
(
|
|
490
|
+
"GitHub",
|
|
491
|
+
{
|
|
492
|
+
"fields": (
|
|
493
|
+
"github_token",
|
|
494
|
+
"git_username",
|
|
495
|
+
"git_password",
|
|
496
|
+
)
|
|
497
|
+
},
|
|
498
|
+
),
|
|
475
499
|
)
|
|
476
500
|
|
|
477
501
|
def owner(self, obj):
|
|
@@ -495,22 +519,39 @@ class ReleaseManagerAdmin(ProfileAdminMixin, SaveBeforeChangeAction, EntityModel
|
|
|
495
519
|
if not creds:
|
|
496
520
|
self.message_user(request, f"{manager} has no credentials", messages.ERROR)
|
|
497
521
|
return
|
|
498
|
-
|
|
522
|
+
env_url = os.environ.get("PYPI_REPOSITORY_URL", "").strip()
|
|
523
|
+
url = env_url or "https://upload.pypi.org/legacy/"
|
|
499
524
|
auth = (
|
|
500
525
|
("__token__", creds.token)
|
|
501
526
|
if creds.token
|
|
502
527
|
else (creds.username, creds.password)
|
|
503
528
|
)
|
|
504
529
|
try:
|
|
505
|
-
resp = requests.
|
|
506
|
-
|
|
530
|
+
resp = requests.post(
|
|
531
|
+
url,
|
|
532
|
+
auth=auth,
|
|
533
|
+
data={"verify_credentials": "1"},
|
|
534
|
+
timeout=10,
|
|
535
|
+
allow_redirects=False,
|
|
536
|
+
)
|
|
537
|
+
status = resp.status_code
|
|
538
|
+
if status in {401, 403}:
|
|
539
|
+
self.message_user(
|
|
540
|
+
request,
|
|
541
|
+
f"{manager} credentials invalid ({status})",
|
|
542
|
+
messages.ERROR,
|
|
543
|
+
)
|
|
544
|
+
elif status <= 400:
|
|
545
|
+
suffix = f" ({status})" if status != 200 else ""
|
|
507
546
|
self.message_user(
|
|
508
|
-
request,
|
|
547
|
+
request,
|
|
548
|
+
f"{manager} credentials valid{suffix}",
|
|
549
|
+
messages.SUCCESS,
|
|
509
550
|
)
|
|
510
551
|
else:
|
|
511
552
|
self.message_user(
|
|
512
553
|
request,
|
|
513
|
-
f"{manager} credentials
|
|
554
|
+
f"{manager} credentials check returned unexpected status {status}",
|
|
514
555
|
messages.ERROR,
|
|
515
556
|
)
|
|
516
557
|
except Exception as exc: # pragma: no cover - admin feedback
|
|
@@ -1249,12 +1290,16 @@ class ReleaseManagerInlineForm(ProfileFormMixin, forms.ModelForm):
|
|
|
1249
1290
|
"pypi_username",
|
|
1250
1291
|
"pypi_token",
|
|
1251
1292
|
"github_token",
|
|
1293
|
+
"git_username",
|
|
1294
|
+
"git_password",
|
|
1252
1295
|
"pypi_password",
|
|
1253
1296
|
"pypi_url",
|
|
1297
|
+
"secondary_pypi_url",
|
|
1254
1298
|
)
|
|
1255
1299
|
widgets = {
|
|
1256
1300
|
"pypi_token": forms.Textarea(attrs={"rows": 3, "style": "width: 40em;"}),
|
|
1257
1301
|
"github_token": forms.Textarea(attrs={"rows": 3, "style": "width: 40em;"}),
|
|
1302
|
+
"git_password": forms.Textarea(attrs={"rows": 3, "style": "width: 40em;"}),
|
|
1258
1303
|
}
|
|
1259
1304
|
|
|
1260
1305
|
|
|
@@ -1622,6 +1667,7 @@ class UserAdmin(UserDatumAdminMixin, DjangoUserAdmin):
|
|
|
1622
1667
|
class EmailCollectorInline(admin.TabularInline):
|
|
1623
1668
|
model = EmailCollector
|
|
1624
1669
|
extra = 0
|
|
1670
|
+
fields = ("name", "subject", "sender")
|
|
1625
1671
|
|
|
1626
1672
|
|
|
1627
1673
|
class EmailCollectorAdmin(EntityModelAdmin):
|
|
@@ -2786,6 +2832,7 @@ class RFIDResource(resources.ModelResource):
|
|
|
2786
2832
|
"energy_accounts",
|
|
2787
2833
|
"reference",
|
|
2788
2834
|
"external_command",
|
|
2835
|
+
"post_auth_command",
|
|
2789
2836
|
"allowed",
|
|
2790
2837
|
"color",
|
|
2791
2838
|
"kind",
|
|
@@ -2799,6 +2846,7 @@ class RFIDResource(resources.ModelResource):
|
|
|
2799
2846
|
"energy_accounts",
|
|
2800
2847
|
"reference",
|
|
2801
2848
|
"external_command",
|
|
2849
|
+
"post_auth_command",
|
|
2802
2850
|
"allowed",
|
|
2803
2851
|
"color",
|
|
2804
2852
|
"kind",
|
|
@@ -2882,6 +2930,7 @@ class RFIDAdmin(EntityModelAdmin, ImportExportModelAdmin):
|
|
|
2882
2930
|
actions = [
|
|
2883
2931
|
"scan_rfids",
|
|
2884
2932
|
"print_card_labels",
|
|
2933
|
+
"print_release_form",
|
|
2885
2934
|
"copy_rfids",
|
|
2886
2935
|
"toggle_selected_user_data",
|
|
2887
2936
|
]
|
|
@@ -3023,6 +3072,7 @@ class RFIDAdmin(EntityModelAdmin, ImportExportModelAdmin):
|
|
|
3023
3072
|
"key_b_verified": source.key_b_verified,
|
|
3024
3073
|
"allowed": source.allowed,
|
|
3025
3074
|
"external_command": source.external_command,
|
|
3075
|
+
"post_auth_command": source.post_auth_command,
|
|
3026
3076
|
"color": source.color,
|
|
3027
3077
|
"kind": source.kind,
|
|
3028
3078
|
"reference": source.reference,
|
|
@@ -3238,6 +3288,141 @@ class RFIDAdmin(EntityModelAdmin, ImportExportModelAdmin):
|
|
|
3238
3288
|
|
|
3239
3289
|
print_card_labels.short_description = _("Print Card Labels")
|
|
3240
3290
|
|
|
3291
|
+
def _render_release_form(self, request, queryset, empty_message, redirect_url):
|
|
3292
|
+
tags = list(queryset)
|
|
3293
|
+
if not tags:
|
|
3294
|
+
self.message_user(request, empty_message, level=messages.WARNING)
|
|
3295
|
+
return HttpResponseRedirect(redirect_url)
|
|
3296
|
+
|
|
3297
|
+
language = getattr(request, "LANGUAGE_CODE", translation.get_language())
|
|
3298
|
+
if not language:
|
|
3299
|
+
language = settings.LANGUAGE_CODE
|
|
3300
|
+
|
|
3301
|
+
with translation.override(language):
|
|
3302
|
+
buffer = BytesIO()
|
|
3303
|
+
document = SimpleDocTemplate(
|
|
3304
|
+
buffer,
|
|
3305
|
+
pagesize=letter,
|
|
3306
|
+
leftMargin=36,
|
|
3307
|
+
rightMargin=36,
|
|
3308
|
+
topMargin=72,
|
|
3309
|
+
bottomMargin=36,
|
|
3310
|
+
)
|
|
3311
|
+
document.title = str(_("RFID Release Form"))
|
|
3312
|
+
|
|
3313
|
+
styles = getSampleStyleSheet()
|
|
3314
|
+
story = []
|
|
3315
|
+
story.append(Paragraph(_("RFID Release Form"), styles["Title"]))
|
|
3316
|
+
story.append(Spacer(1, 12))
|
|
3317
|
+
|
|
3318
|
+
generated_on = timezone.localtime()
|
|
3319
|
+
formatted_generated_on = date_format(generated_on, "DATETIME_FORMAT")
|
|
3320
|
+
if generated_on.tzinfo:
|
|
3321
|
+
formatted_generated_on = _("%(datetime)s %(timezone)s") % {
|
|
3322
|
+
"datetime": formatted_generated_on,
|
|
3323
|
+
"timezone": generated_on.tzname() or "",
|
|
3324
|
+
}
|
|
3325
|
+
generated_text = Paragraph(
|
|
3326
|
+
_("Generated on: %(date)s")
|
|
3327
|
+
% {"date": formatted_generated_on},
|
|
3328
|
+
styles["Normal"],
|
|
3329
|
+
)
|
|
3330
|
+
story.append(generated_text)
|
|
3331
|
+
story.append(Spacer(1, 24))
|
|
3332
|
+
|
|
3333
|
+
table_data = [
|
|
3334
|
+
[
|
|
3335
|
+
_("Label"),
|
|
3336
|
+
_("RFID"),
|
|
3337
|
+
_("Custom label"),
|
|
3338
|
+
_("Color"),
|
|
3339
|
+
_("Type"),
|
|
3340
|
+
]
|
|
3341
|
+
]
|
|
3342
|
+
|
|
3343
|
+
for tag in tags:
|
|
3344
|
+
table_data.append(
|
|
3345
|
+
[
|
|
3346
|
+
tag.label_id or "",
|
|
3347
|
+
tag.rfid or "",
|
|
3348
|
+
tag.custom_label or "",
|
|
3349
|
+
tag.get_color_display() if tag.color else "",
|
|
3350
|
+
tag.get_kind_display() if tag.kind else "",
|
|
3351
|
+
]
|
|
3352
|
+
)
|
|
3353
|
+
|
|
3354
|
+
table = Table(table_data, repeatRows=1, hAlign="LEFT")
|
|
3355
|
+
table.setStyle(
|
|
3356
|
+
TableStyle(
|
|
3357
|
+
[
|
|
3358
|
+
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
|
|
3359
|
+
("TEXTCOLOR", (0, 0), (-1, 0), colors.black),
|
|
3360
|
+
("GRID", (0, 0), (-1, -1), 0.5, colors.grey),
|
|
3361
|
+
("ALIGN", (0, 0), (-1, -1), "LEFT"),
|
|
3362
|
+
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
|
|
3363
|
+
("BOTTOMPADDING", (0, 0), (-1, 0), 8),
|
|
3364
|
+
]
|
|
3365
|
+
)
|
|
3366
|
+
)
|
|
3367
|
+
|
|
3368
|
+
story.append(table)
|
|
3369
|
+
story.append(Spacer(1, 36))
|
|
3370
|
+
|
|
3371
|
+
signature_lines = [
|
|
3372
|
+
[
|
|
3373
|
+
Paragraph(
|
|
3374
|
+
_("Issuer Signature: ______________________________"),
|
|
3375
|
+
styles["Normal"],
|
|
3376
|
+
),
|
|
3377
|
+
Paragraph(
|
|
3378
|
+
_("Receiver Signature: ______________________________"),
|
|
3379
|
+
styles["Normal"],
|
|
3380
|
+
),
|
|
3381
|
+
],
|
|
3382
|
+
[
|
|
3383
|
+
Paragraph(
|
|
3384
|
+
_("Issuer Name: ______________________________"),
|
|
3385
|
+
styles["Normal"],
|
|
3386
|
+
),
|
|
3387
|
+
Paragraph(
|
|
3388
|
+
_("Receiver Name: ______________________________"),
|
|
3389
|
+
styles["Normal"],
|
|
3390
|
+
),
|
|
3391
|
+
],
|
|
3392
|
+
]
|
|
3393
|
+
|
|
3394
|
+
signature_table = Table(
|
|
3395
|
+
signature_lines,
|
|
3396
|
+
colWidths=[document.width / 2.0, document.width / 2.0],
|
|
3397
|
+
hAlign="LEFT",
|
|
3398
|
+
)
|
|
3399
|
+
signature_table.setStyle(
|
|
3400
|
+
TableStyle(
|
|
3401
|
+
[
|
|
3402
|
+
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
|
3403
|
+
("BOTTOMPADDING", (0, 0), (-1, -1), 12),
|
|
3404
|
+
]
|
|
3405
|
+
)
|
|
3406
|
+
)
|
|
3407
|
+
story.append(signature_table)
|
|
3408
|
+
|
|
3409
|
+
document.build(story)
|
|
3410
|
+
buffer.seek(0)
|
|
3411
|
+
|
|
3412
|
+
response = HttpResponse(buffer.getvalue(), content_type="application/pdf")
|
|
3413
|
+
response["Content-Disposition"] = "attachment; filename=rfid-release-form.pdf"
|
|
3414
|
+
return response
|
|
3415
|
+
|
|
3416
|
+
def print_release_form(self, request, queryset):
|
|
3417
|
+
return self._render_release_form(
|
|
3418
|
+
request,
|
|
3419
|
+
queryset,
|
|
3420
|
+
_("Select at least one RFID to print the release form."),
|
|
3421
|
+
request.get_full_path(),
|
|
3422
|
+
)
|
|
3423
|
+
|
|
3424
|
+
print_release_form.short_description = _("Print Release Form")
|
|
3425
|
+
|
|
3241
3426
|
def get_changelist_actions(self, request):
|
|
3242
3427
|
parent = getattr(super(), "get_changelist_actions", None)
|
|
3243
3428
|
actions = []
|
|
@@ -3518,7 +3703,7 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, EntityModelAdmin):
|
|
|
3518
3703
|
list_display_links = ("version",)
|
|
3519
3704
|
actions = ["publish_release", "validate_releases", "test_pypi_connection"]
|
|
3520
3705
|
change_actions = ["publish_release_action", "test_pypi_connection_action"]
|
|
3521
|
-
changelist_actions = ["refresh_from_pypi", "prepare_next_release"]
|
|
3706
|
+
changelist_actions = ["edit_changelog", "refresh_from_pypi", "prepare_next_release"]
|
|
3522
3707
|
readonly_fields = ("pypi_url", "github_url", "release_on", "is_current", "revision")
|
|
3523
3708
|
fields = (
|
|
3524
3709
|
"package",
|
|
@@ -3541,6 +3726,12 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, EntityModelAdmin):
|
|
|
3541
3726
|
|
|
3542
3727
|
revision_short.short_description = "revision"
|
|
3543
3728
|
|
|
3729
|
+
def edit_changelog(self, request, queryset=None):
|
|
3730
|
+
return redirect("admin:system-changelog-report")
|
|
3731
|
+
|
|
3732
|
+
edit_changelog.label = "Edit Changelog"
|
|
3733
|
+
edit_changelog.short_description = "Edit Changelog"
|
|
3734
|
+
|
|
3544
3735
|
def refresh_from_pypi(self, request, queryset):
|
|
3545
3736
|
package = Package.objects.filter(is_active=True).first()
|
|
3546
3737
|
if not package:
|
core/admindocs.py
CHANGED
|
@@ -9,7 +9,11 @@ from django.contrib.admindocs.views import (
|
|
|
9
9
|
BaseAdminDocsView,
|
|
10
10
|
user_has_model_view_permission,
|
|
11
11
|
)
|
|
12
|
+
from django.shortcuts import render
|
|
13
|
+
from django.template import loader
|
|
12
14
|
from django.urls import NoReverseMatch, reverse
|
|
15
|
+
from django.utils.translation import gettext_lazy as _
|
|
16
|
+
from django.test import signals as test_signals
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
class CommandsView(BaseAdminDocsView):
|
|
@@ -56,17 +60,27 @@ class CommandsView(BaseAdminDocsView):
|
|
|
56
60
|
class OrderedModelIndexView(BaseAdminDocsView):
|
|
57
61
|
template_name = "admin_doc/model_index.html"
|
|
58
62
|
|
|
63
|
+
USER_MANUALS_APP = SimpleNamespace(
|
|
64
|
+
label="manuals",
|
|
65
|
+
name="manuals",
|
|
66
|
+
verbose_name=_("User Manuals"),
|
|
67
|
+
)
|
|
68
|
+
|
|
59
69
|
GROUP_OVERRIDES = {
|
|
60
70
|
"ocpp.location": "core",
|
|
61
71
|
"core.rfid": "ocpp",
|
|
62
72
|
"core.package": "teams",
|
|
63
73
|
"core.packagerelease": "teams",
|
|
74
|
+
"core.todo": "teams",
|
|
75
|
+
"pages.usermanual": USER_MANUALS_APP,
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
def _get_docs_app_config(self, meta):
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
79
|
+
override = self.GROUP_OVERRIDES.get(meta.label_lower)
|
|
80
|
+
if override:
|
|
81
|
+
if isinstance(override, str):
|
|
82
|
+
return apps.get_app_config(override)
|
|
83
|
+
return override
|
|
70
84
|
return meta.app_config
|
|
71
85
|
|
|
72
86
|
def get_context_data(self, **kwargs):
|
|
@@ -92,6 +106,33 @@ class OrderedModelIndexView(BaseAdminDocsView):
|
|
|
92
106
|
class ModelGraphIndexView(BaseAdminDocsView):
|
|
93
107
|
template_name = "admin_doc/model_graphs.html"
|
|
94
108
|
|
|
109
|
+
def render_to_response(self, context, **response_kwargs):
|
|
110
|
+
template_name = response_kwargs.pop("template_name", None)
|
|
111
|
+
if template_name is None:
|
|
112
|
+
template_name = self.get_template_names()
|
|
113
|
+
response = render(
|
|
114
|
+
self.request,
|
|
115
|
+
template_name,
|
|
116
|
+
context,
|
|
117
|
+
**response_kwargs,
|
|
118
|
+
)
|
|
119
|
+
if getattr(response, "context", None) is None:
|
|
120
|
+
response.context = context
|
|
121
|
+
if test_signals.template_rendered.receivers:
|
|
122
|
+
if isinstance(template_name, (list, tuple)):
|
|
123
|
+
template = loader.select_template(template_name)
|
|
124
|
+
else:
|
|
125
|
+
template = loader.get_template(template_name)
|
|
126
|
+
signal_context = context
|
|
127
|
+
if self.request is not None and "request" not in signal_context:
|
|
128
|
+
signal_context = {**context, "request": self.request}
|
|
129
|
+
test_signals.template_rendered.send(
|
|
130
|
+
sender=template.__class__,
|
|
131
|
+
template=template,
|
|
132
|
+
context=signal_context,
|
|
133
|
+
)
|
|
134
|
+
return response
|
|
135
|
+
|
|
95
136
|
def get_context_data(self, **kwargs):
|
|
96
137
|
sections = {}
|
|
97
138
|
user = self.request.user
|
core/apps.py
CHANGED
|
@@ -351,6 +351,6 @@ class CoreConfig(AppConfig):
|
|
|
351
351
|
try:
|
|
352
352
|
from .mcp.auto_start import schedule_auto_start
|
|
353
353
|
|
|
354
|
-
schedule_auto_start()
|
|
354
|
+
schedule_auto_start(check_profiles_immediately=False)
|
|
355
355
|
except Exception: # pragma: no cover - defensive
|
|
356
356
|
logger.exception("Failed to schedule MCP auto-start")
|