arthexis 0.1.21__py3-none-any.whl → 0.1.22__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arthexis
3
- Version: 0.1.21
3
+ Version: 0.1.22
4
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
@@ -26,14 +26,14 @@ Requires-Dist: celery==5.5.3
26
26
  Requires-Dist: certifi==2025.7.14
27
27
  Requires-Dist: cffi==2.0.0
28
28
  Requires-Dist: channels==4.1.0
29
- Requires-Dist: charset-normalizer==3.4.2
29
+ Requires-Dist: charset-normalizer==3.4.4
30
30
  Requires-Dist: click==8.2.1
31
31
  Requires-Dist: click-didyoumean==0.3.1
32
32
  Requires-Dist: click-plugins==1.1.1.2
33
33
  Requires-Dist: click-repl==0.3.0
34
34
  Requires-Dist: colorama==0.4.6
35
35
  Requires-Dist: constantly==23.10.4
36
- Requires-Dist: cron-descriptor==1.4.5
36
+ Requires-Dist: cron-descriptor==2.0.6
37
37
  Requires-Dist: cryptography==45.0.5
38
38
  Requires-Dist: daphne==4.2.1
39
39
  Requires-Dist: diff-match-patch==20241021
@@ -56,15 +56,15 @@ Requires-Dist: hyperlink==21.0.0
56
56
  Requires-Dist: idna==3.11
57
57
  Requires-Dist: incremental==24.7.2
58
58
  Requires-Dist: kombu==5.5.4
59
- Requires-Dist: libipld==3.1.1
60
- Requires-Dist: Markdown==3.8.2
59
+ Requires-Dist: libipld==3.2.0
60
+ Requires-Dist: Markdown==3.9
61
61
  Requires-Dist: mdx_truly_sane_lists==1.3
62
62
  Requires-Dist: mfrc522==0.0.7; sys_platform == "linux"
63
63
  Requires-Dist: outcome==1.3.0.post0
64
64
  Requires-Dist: packaging==25.0
65
65
  Requires-Dist: pillow==11.3.0
66
66
  Requires-Dist: prompt_toolkit==3.0.51
67
- Requires-Dist: psutil==7.1.1
67
+ Requires-Dist: psutil==7.1.2
68
68
  Requires-Dist: psycopg==3.2.9
69
69
  Requires-Dist: psycopg-binary==3.2.12
70
70
  Requires-Dist: pyasn1==0.6.1
@@ -79,7 +79,7 @@ Requires-Dist: python-crontab==3.3.0
79
79
  Requires-Dist: python-dateutil==2.9.0.post0
80
80
  Requires-Dist: python-dotenv==1.1.1
81
81
  Requires-Dist: qrcode==8.2
82
- Requires-Dist: redis==5.0.8
82
+ Requires-Dist: redis==7.0.1
83
83
  Requires-Dist: reportlab==4.2.2
84
84
  Requires-Dist: requests==2.32.5
85
85
  Requires-Dist: selenium==4.34.2
@@ -103,7 +103,7 @@ Requires-Dist: typing_extensions==4.14.1
103
103
  Requires-Dist: tzdata==2025.2
104
104
  Requires-Dist: urllib3==2.5.0
105
105
  Requires-Dist: vine==5.1.0
106
- Requires-Dist: wcwidth==0.2.13
106
+ Requires-Dist: wcwidth==0.2.14
107
107
  Requires-Dist: webencodings==0.5.1
108
108
  Requires-Dist: websocket-client==1.8.0
109
109
  Requires-Dist: websockets==13.1
@@ -1,4 +1,4 @@
1
- arthexis-0.1.21.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
1
+ arthexis-0.1.22.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=Z2HjWrxOxVU9BXcqS7dMEfOGJC48H-WPwFwokRdermY,774
@@ -10,12 +10,12 @@ config/loadenv.py,sha256=CjXx-wBaTt1wixub4GJ5CMSMFqtiK5JURc7cPXpqO7s,287
10
10
  config/logging.py,sha256=1cIbPgRshHuMKnVEEH0jKpRAlJSpewvLFbYDz7sCBG4,2104
11
11
  config/middleware.py,sha256=zF8Cma0n5G8NNdh2LVeNJi7Hgl1G4mF9msRE2eRi1RU,2328
12
12
  config/offline.py,sha256=X-yDcyoI4C44Y27lpkUwszY_09GwwFfazEsthKJpQ70,1382
13
- config/settings.py,sha256=ILOI8CROVwchUeJUsdqqXwaFCn26YSjyNof_t0VFqFo,20622
13
+ config/settings.py,sha256=b27y7mmQMJmcIFuceNxrX-9yO_R1GqbIf_wFdZNvTWo,20789
14
14
  config/settings_helpers.py,sha256=0BdBciUHIkwsWa0vV_RKAd4wDuEzgE7G-42XYiES4YQ,3127
15
- config/urls.py,sha256=REDaNai8fhGnQd_3vRrFIPECB3YZ5FX5Y2tO6IpIUPs,5413
15
+ config/urls.py,sha256=979WyL05v7nb-Lz_3Qf6Z2QzGtWpXADbIKZ28Zm12ts,5535
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=o2nEhTHLHL03fVVe8uMTcIP5kXg4OPYF-BGipyNeyoo,142635
18
+ core/admin.py,sha256=dzM1-jibV1bzpcXGvydoR_8_WMclKYS4ohLreHgipvU,146524
19
19
  core/admin_history.py,sha256=XZ4b0ryufIka-xcwboK3DzmOL-INSx5Y2fJO-aJdV70,1783
20
20
  core/admindocs.py,sha256=ycD0bJ_VE6rTGf9ebXTiKdYkD8Y8hD2oQ4HxxoBURCM,6756
21
21
  core/apps.py,sha256=S6fySxtxUzfvz8FI9dii0KI4wSyLhh5API_oeERLIsc,14084
@@ -23,7 +23,7 @@ core/auto_upgrade.py,sha256=1EffHHFylgydWdZM_id6CppV0QqBtdNw7cwBYVdbNdk,1715
23
23
  core/backends.py,sha256=okrNW5Z-zDhJB9Al529aTOHGsTLACeQ6v_hMl4cyfnA,10704
24
24
  core/changelog.py,sha256=SRn37i5N-qb-RYV4Gpu9fg7Kv8gu4TH8ZwEmDRgN-Vo,12594
25
25
  core/entity.py,sha256=o4VteOXePGEsIWJFZ3fpq3DZsdWr3hpQ9A6kFbKosSE,4844
26
- core/environment.py,sha256=z8LJW9bfVSaqWJT_4bXqUMSGTXVi3XGdsu9s_GH3_J0,11039
26
+ core/environment.py,sha256=4beGOYE1BC8q9vBmTLZzFo75nj_3tBekzMqhsNEZVbU,1653
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
@@ -34,7 +34,7 @@ core/liveupdate.py,sha256=22m0ueQ10-6b-9pQJHY0_5WRYA98fysXKEXOWzIr550,691
34
34
  core/log_paths.py,sha256=lxvgXPgJtVNZ-kYrqV8VFle4GFQrSxG-yRTglqvclmU,3318
35
35
  core/mailer.py,sha256=JpW0RnD9uZ4O-wvlqeW7CMw95IFeCSkdvbankJDwHq0,2886
36
36
  core/middleware.py,sha256=j19K9SX-Emkv7BDDtAacR9g6RWsxhKHwCc8w23JFvMM,3388
37
- core/models.py,sha256=hbI63Rho16lJ6L1AQ3azxQKUrzZhtZ_g6SygFPW_21A,128915
37
+ core/models.py,sha256=9cwmW1onK7tAg7NQyhSavm2-eXV4nqDewyJUIHuMjZc,143176
38
38
  core/notifications.py,sha256=jNLSuSCrhb8x5cDu_APeDlkrmbMejufk5eJOhssAC4I,3917
39
39
  core/public_wifi.py,sha256=yydLgxOo9DmJJbM4X_23wGR3gxL3YzHno54v9GssuFA,7213
40
40
  core/reference_utils.py,sha256=tffCoyE1w4_SmYzXVWOsW8aR_ZVVTSPzrGhBq8K2xzA,3631
@@ -43,68 +43,68 @@ core/rfid_import_export.py,sha256=petyhPvL0WUpehc6uGUDUhjYQ9AVvc6O49zuhDs6YFw,35
43
43
  core/sigil_builder.py,sha256=nMuhYlw3j3LosrK85Q0pYsMcfGWCmrmdnv8UG7GTq_o,4856
44
44
  core/sigil_context.py,sha256=GCzjfM6fcVvBtSbVNfmE6sx3HU8QnxnXrCIytnNpQzM,439
45
45
  core/sigil_resolver.py,sha256=rCsypuX-0oWNfKyM1T9ZLWHY0Ezwhtk4VmI0L3krnsE,11098
46
- core/system.py,sha256=RyBqooWezM0Li_KCRskchD5Lub0cdqBmrnU6ilC8MPE,39823
46
+ core/system.py,sha256=6ndxYDPswKkC3ySTwbgXzH0CdQYCZJytfA-99smyv_Q,42249
47
47
  core/tasks.py,sha256=d0MQP5fmn5pA2VCFGxDMEX0xppDIIh8IAzPfGdnk8J4,12340
48
48
  core/temp_passwords.py,sha256=FieUnIUeQHmA1DoXvfJ5U6-Ayv3oDz-hSln5s_vNbA4,5271
49
49
  core/test_system_info.py,sha256=IMPz21KEs6OC5YbL7YaIBdmJVLjRY6MgPuZpldJB5OI,6935
50
- core/tests.py,sha256=F-aC_wal9R9vPR_VvaeTzVZzRFtLYXmzNSdob1_1tc8,98456
50
+ core/tests.py,sha256=eKKoUkF-zI4pCXpjMCuIKCXnv_KgLO8q3_9W0f2lcws,104199
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=raBaNQIQhEb_4_wm1V3imHG8FuJaiTU2Jh6nIS8gNY4,86768
54
+ core/views.py,sha256=oq3_INVD6VwgeRfZ3-UXV0aCUYKj4Kh7Hjx1B9eHVbA,87819
55
55
  core/widgets.py,sha256=vlR9PlFfZGlkHm5X2cqNXuEBZSj8gmWaR6MO1mMy6kg,6904
56
56
  nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- nodes/admin.py,sha256=Unv5FSNTnheMQ4Ez7Ww8eSpbFKaqTJ1XpAnlhOvPjGE,67871
58
- nodes/apps.py,sha256=AxK-sh9JBJZwNOLjqw9omCQGUQWw-45VRdYH07XhVJU,2732
57
+ nodes/admin.py,sha256=GDptKIbz5JTu57UxEFiKIq0aqDBFSXilg_zYxc4Tgew,72336
58
+ nodes/apps.py,sha256=oi_M2Ya8CAR8N_MoYU68u7_9u-9SlIMelzLOgYM9tDs,3059
59
59
  nodes/backends.py,sha256=dmmbS0X2YIlCDz2KjoDf_L62dy--nuqZF1rEDoi2JHM,5921
60
60
  nodes/dns.py,sha256=D5smXD7Rkh6E4MdL6TBL2WY8GgJg7Rx9z88LZrcMbTw,7048
61
61
  nodes/feature_checks.py,sha256=27e4PCkZ8BGWnJCOwMcY2Bo9z7LoeZWiTZuISWGnrzk,3996
62
62
  nodes/lcd.py,sha256=iKA8Wmq85KZD52aTzAU8ZmS144_gbdGMOXcE8yuECps,5758
63
- nodes/models.py,sha256=S2Gc68Q2_8r8RO1ZKxASN5mEOj1q4UWtxdum5A-p0-4,71693
63
+ nodes/models.py,sha256=3ym5od7ykGokkxOrvXhU8h439e7EaOGsIbG89x7BYz8,72598
64
64
  nodes/reports.py,sha256=NRYh3Y0SlZFhx31Zh2K03yO12ZrpxEHEY6T-dODA6WE,12059
65
65
  nodes/rfid_sync.py,sha256=oeblawcp6xeLApdIuhsJS83OAk58Eu7pVVmgpAc0Nt8,6953
66
66
  nodes/signals.py,sha256=PtOKdQfb08mV1LgSZvn7ZAcfOyy2c3Xkq4AOpBQyUdE,622
67
67
  nodes/tasks.py,sha256=7m9pKO-iI6JDdfPQ-GWRGown4mdyKrcroOnhbiWN7dY,5246
68
- nodes/tests.py,sha256=54FARHRm4GFT1aP29DYItGsvPVGchoqj7ZpG94PN4t4,178320
68
+ nodes/tests.py,sha256=5aP2byahZXzKtUY87XtNc9Zv0P9ex2WUrkpNPK0BtJI,186035
69
69
  nodes/urls.py,sha256=-o9_pLo6XHerKMQwL0TW80wm6wmtVZqyNWcUhpdq9vk,915
70
70
  nodes/utils.py,sha256=wt7UuSXGuq79A-g-B6EW3kK49QWJBb7zhhkw4pun4k8,4474
71
- nodes/views.py,sha256=IaTyEbS0GDlLLpX7SwRwNSY57mJEjQ2-knOvdP5MDdQ,38508
71
+ nodes/views.py,sha256=EByqVc8Nj1nGdLgu-2a8AcxcSqbUtXyuLCF_tRSDTSk,38564
72
72
  ocpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- ocpp/admin.py,sha256=FZzhq8vfpglZXfdkwwGSTB7gtkShZAvO6PTELl4fkEs,40008
73
+ ocpp/admin.py,sha256=FWYLzZ51-ArcIOZlvI8vwk0ohdPVTAXwurlCOhRhWnc,40502
74
74
  ocpp/apps.py,sha256=i3NqrmIamNEQBT33CIqh7HOSOPmJXCMKrZ-DUd3whqg,842
75
- ocpp/consumers.py,sha256=wt9tauYSlpWnGbaGsJKIPt4D13HyloLIHwmlK8iqA0Q,71674
75
+ ocpp/consumers.py,sha256=7PYlOkSlHSlIz2FUcBjui4uLFEIHOBWIHfnYpvITrMY,71719
76
76
  ocpp/evcs.py,sha256=q1mZrCVSZxXTrtYsDqH6lkeEcJ6tfSC7p9YxkDmpSCw,28883
77
77
  ocpp/evcs_discovery.py,sha256=OmrzgaOHwveDRJs8AIhrM3apX8_k2PPXh_oYaYpNW3c,3876
78
- ocpp/models.py,sha256=J7JiXNyiFZ48l6_QNY3OHG1QPFVye-4u0qxmunwXc-U,35936
78
+ ocpp/models.py,sha256=XMTKNu8K03_GId5Z9ucHUxMKlIw8crFnn4aTUHZYW8s,36763
79
79
  ocpp/reference_utils.py,sha256=_UR82GfE93kv4766mHyVIfdhhyYvrT59660r3H6W55M,1072
80
80
  ocpp/routing.py,sha256=3kQya-MdJ00778xDmX0esQLBP05P200V45asg-CGNoo,438
81
81
  ocpp/simulator.py,sha256=vnyd59QffT79AaPhmfM_jipni_nqfG57X5tXyx1rBoc,28016
82
82
  ocpp/status_display.py,sha256=YGFosd5HJETA0DcLdsjvx6EfhZSnI8Aa3cMnHG2WsBE,939
83
83
  ocpp/store.py,sha256=gLCSaP9KKF7li2ALlE3O3RW5eVJtoe-_YHfKhdf0VOM,18943
84
- ocpp/tasks.py,sha256=n2axf1Oo7brZtXRe-uCwt6K0f57ZlHUNvp6c5W0Gdzo,6035
84
+ ocpp/tasks.py,sha256=VSna6Mi1jp3zjYTo4y0c5N-n-4efIq5FRKu3z2y8Oio,8950
85
85
  ocpp/test_export_import.py,sha256=ouQbTCp4mxfqoK6gondlu3PPcyrT9jSbWAX5gqqgaNk,4561
86
86
  ocpp/test_rfid.py,sha256=IhFSlvsI8A8D3S32sRE298nYfrmqxbv7GfVErtNU3DQ,39137
87
- ocpp/tests.py,sha256=PaQcsX8nJNAaQUixw6JjNiEnPy2LJGeM60qi3DEyhQ4,193151
87
+ ocpp/tests.py,sha256=5PWaNY3gUPxQmkoEkkjU5AXlryzyeWkDL6MUfsydEyg,203144
88
88
  ocpp/transactions_io.py,sha256=p2aUsKlCDYnZ4ZBrOM7pxXoW_w3Tbm-tvRFSjnR3x24,7738
89
89
  ocpp/urls.py,sha256=5ZomUtznJe3kfs8E-DtVp12eFva5jUuJdpTEczIsQ5w,1730
90
- ocpp/views.py,sha256=vGWio3Hv4M8ScKhZEBVhCf3IeumTeJdCONExWBQbF24,66218
90
+ ocpp/views.py,sha256=AsdHwLe_6gdLy6DQcwH5ioSbFATqmlpdKkesGoWD7QI,76346
91
91
  pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
- pages/admin.py,sha256=E-jcxUI-89gFgqGI38HMSHbfDw1inpSUiRR83utJ0fc,32512
93
- pages/apps.py,sha256=o8gQP-VdZOk9LXIEo6IDmOSqX3TP8XypBvKGGWLoQ0k,351
92
+ pages/admin.py,sha256=VbxkwgjrFx7lXVhwbZuPSvCkS7EC-flBpwOztHejWtE,35834
93
+ pages/apps.py,sha256=0qcTFKVX9_QgqexJtGeph1sHRqq7khJf4x5ZtkWwblg,1424
94
94
  pages/checks.py,sha256=sM8_hUVM_HOIocvtTb2sY3AaSEvbTnOlO46UchGVd-0,1527
95
95
  pages/context_processors.py,sha256=vrgMu4vYCOonZ8eZ27gQvGU74PBpMi47T512Lu1__sA,5297
96
96
  pages/defaults.py,sha256=3tjv3nFPxwpFu6poJ1Ez1MP92Q6ZvyRluftKHlU-zeI,522
97
- pages/forms.py,sha256=T0atqxdNds3IBP8N-9c5-ACf3iR9FzzmhzK4MOa24e8,7058
97
+ pages/forms.py,sha256=r3JM5qp3_4RR01-u6XV8WDOaeiRe4OvCN8Y52FcsAwI,7909
98
98
  pages/middleware.py,sha256=MYd5Nko4AnFg3orY6MuyvvNg_I6GCIf8mDW8znSOgvQ,7042
99
- pages/models.py,sha256=Ms_m_tzzstNghN_JOzyfwsbllDBIl_AKnYdTRTJthqM,22173
99
+ pages/models.py,sha256=9LdIoIK2Epp3YDUk8LUWyhLW5pJ-NiuYTzO_-xKjg0c,23636
100
100
  pages/module_defaults.py,sha256=rCAY8aTyxYNL0M5zDr393rX-Gi-svXqKtuLXm0rILrQ,5444
101
101
  pages/site_config.py,sha256=f1Me0GFdHeGbIeyMlQNzD2e6hym59YHqbz92U_ppffY,4057
102
102
  pages/tasks.py,sha256=ivcba_3wSQ1-cku0oDplzw6vLeQ9hBq3R4TG-LmR5gs,1913
103
- pages/tests.py,sha256=vMTVK_wCOsf08KmeOiWoYibW5zbfvrl96oWu7Z8D_cM,139914
104
- pages/urls.py,sha256=neNFYT4fEsiiNAEXao_CklOLnd2FIWFf1hxlM_DVsHo,1231
103
+ pages/tests.py,sha256=OFyYVFeAfsnHXL27zL3ma821pE4cXGVtOrtIO2YGjsk,153460
104
+ pages/urls.py,sha256=2PVF8--cKi3h230CZl7VQsoVaQrxvWg3GPAKiQowQB0,1354
105
105
  pages/utils.py,sha256=CR4D1debgJLGgXsw9kap2ggpe7fIpSoWS_ivbgMNp2k,564
106
- pages/views.py,sha256=6vriwnTgS-jeH3cNsJyz0ec6F1enZJAUJ7vRVmqILv8,50790
107
- arthexis-0.1.21.dist-info/METADATA,sha256=rO5YIGveESngYUQQ3mDUM8xAoWRHVjXYFoKlS9XV0uw,11726
108
- arthexis-0.1.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
- arthexis-0.1.21.dist-info/top_level.txt,sha256=J2a2q8_BWrCZ8H2WFUNMBfO2jz8j2gax6zZh-_1QDac,29
110
- arthexis-0.1.21.dist-info/RECORD,,
106
+ pages/views.py,sha256=7F3zvXUMBH320rY-UtnxOiUz8HFWTRrGkxwdjZlOdbg,60863
107
+ arthexis-0.1.22.dist-info/METADATA,sha256=72h6hoADl-yJFWfojMeYpb2qo3cW56E9a5UyMdSdxn4,11724
108
+ arthexis-0.1.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
+ arthexis-0.1.22.dist-info/top_level.txt,sha256=J2a2q8_BWrCZ8H2WFUNMBfO2jz8j2gax6zZh-_1QDac,29
110
+ arthexis-0.1.22.dist-info/RECORD,,
config/settings.py CHANGED
@@ -662,4 +662,8 @@ CELERY_BEAT_SCHEDULE = {
662
662
  "task": "core.tasks.heartbeat",
663
663
  "schedule": crontab(minute="*/5"),
664
664
  },
665
+ "ocpp_configuration_check": {
666
+ "task": "ocpp.tasks.schedule_daily_charge_point_configuration_checks",
667
+ "schedule": crontab(minute=0, hour=0),
668
+ },
665
669
  }
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>/delete/",
139
+ core_views.todo_delete,
140
+ name="todo-delete",
141
+ ),
137
142
  path(
138
143
  "admin/core/todos/<int:pk>/snapshot/",
139
144
  core_views.todo_snapshot,
core/admin.py CHANGED
@@ -83,6 +83,7 @@ from .models import (
83
83
  OdooProfile,
84
84
  OpenPayProfile,
85
85
  EmailInbox,
86
+ GoogleCalendarProfile,
86
87
  SocialProfile,
87
88
  EmailCollector,
88
89
  Package,
@@ -1005,6 +1006,41 @@ class OpenPayProfileAdminForm(forms.ModelForm):
1005
1006
  )
1006
1007
 
1007
1008
 
1009
+ class GoogleCalendarProfileAdminForm(forms.ModelForm):
1010
+ """Admin form for :class:`core.models.GoogleCalendarProfile`."""
1011
+
1012
+ api_key = forms.CharField(
1013
+ widget=forms.PasswordInput(render_value=True),
1014
+ required=False,
1015
+ help_text="Leave blank to keep the current key.",
1016
+ )
1017
+
1018
+ class Meta:
1019
+ model = GoogleCalendarProfile
1020
+ fields = "__all__"
1021
+
1022
+ def __init__(self, *args, **kwargs):
1023
+ super().__init__(*args, **kwargs)
1024
+ if self.instance.pk:
1025
+ self.fields["api_key"].initial = ""
1026
+ self.initial["api_key"] = ""
1027
+ else:
1028
+ self.fields["api_key"].required = True
1029
+
1030
+ def clean_api_key(self):
1031
+ key = self.cleaned_data.get("api_key")
1032
+ if not key and self.instance.pk:
1033
+ return keep_existing("api_key")
1034
+ return key
1035
+
1036
+ def _post_clean(self):
1037
+ super()._post_clean()
1038
+ _restore_sigil_values(
1039
+ self,
1040
+ ["calendar_id", "api_key", "display_name", "timezone"],
1041
+ )
1042
+
1043
+
1008
1044
  class MaskedPasswordFormMixin:
1009
1045
  """Mixin that hides stored passwords while allowing updates."""
1010
1046
 
@@ -1222,6 +1258,15 @@ class OpenPayProfileInlineForm(ProfileFormMixin, OpenPayProfileAdminForm):
1222
1258
  return cleaned
1223
1259
 
1224
1260
 
1261
+ class GoogleCalendarProfileInlineForm(
1262
+ ProfileFormMixin, GoogleCalendarProfileAdminForm
1263
+ ):
1264
+ profile_fields = GoogleCalendarProfile.profile_fields
1265
+
1266
+ class Meta(GoogleCalendarProfileAdminForm.Meta):
1267
+ exclude = ("user", "group")
1268
+
1269
+
1225
1270
  class EmailInboxInlineForm(ProfileFormMixin, EmailInboxAdminForm):
1226
1271
  profile_fields = EmailInbox.profile_fields
1227
1272
 
@@ -1346,6 +1391,16 @@ PROFILE_INLINE_CONFIG = {
1346
1391
  ),
1347
1392
  "readonly_fields": ("verified_on", "verification_reference"),
1348
1393
  },
1394
+ GoogleCalendarProfile: {
1395
+ "form": GoogleCalendarProfileInlineForm,
1396
+ "fields": (
1397
+ "display_name",
1398
+ "calendar_id",
1399
+ "api_key",
1400
+ "max_events",
1401
+ "timezone",
1402
+ ),
1403
+ },
1349
1404
  EmailInbox: {
1350
1405
  "form": EmailInboxInlineForm,
1351
1406
  "fields": (
@@ -1817,6 +1872,44 @@ class OpenPayProfileAdmin(ProfileAdminMixin, SaveBeforeChangeAction, EntityModel
1817
1872
  verify_credentials_action.short_description = _("Test credentials")
1818
1873
 
1819
1874
 
1875
+ class GoogleCalendarProfileAdmin(
1876
+ ProfileAdminMixin, SaveBeforeChangeAction, EntityModelAdmin
1877
+ ):
1878
+ form = GoogleCalendarProfileAdminForm
1879
+ list_display = ("owner", "calendar_identifier", "max_events")
1880
+ search_fields = (
1881
+ "display_name",
1882
+ "calendar_id",
1883
+ "user__username",
1884
+ "group__name",
1885
+ )
1886
+ changelist_actions = ["my_profile"]
1887
+ change_actions = ["my_profile_action"]
1888
+ fieldsets = (
1889
+ (_("Owner"), {"fields": ("user", "group")}),
1890
+ (
1891
+ _("Calendar"),
1892
+ {
1893
+ "fields": (
1894
+ "display_name",
1895
+ "calendar_id",
1896
+ "api_key",
1897
+ "max_events",
1898
+ "timezone",
1899
+ )
1900
+ },
1901
+ ),
1902
+ )
1903
+
1904
+ @admin.display(description=_("Owner"))
1905
+ def owner(self, obj):
1906
+ return obj.owner_display()
1907
+
1908
+ @admin.display(description=_("Calendar"))
1909
+ def calendar_identifier(self, obj):
1910
+ display = obj.get_display_name()
1911
+ return display or obj.resolved_calendar_id()
1912
+
1820
1913
  class EmailSearchForm(forms.Form):
1821
1914
  subject = forms.CharField(
1822
1915
  required=False, widget=forms.TextInput(attrs={"style": "width: 40em;"})
@@ -2416,8 +2509,30 @@ class ProductAdmin(EntityModelAdmin):
2416
2509
  ],
2417
2510
  limit=0,
2418
2511
  )
2419
- except Exception:
2512
+ except Exception as exc:
2513
+ logger.exception(
2514
+ "Failed to fetch Odoo products for user %s (profile_id=%s, host=%s, database=%s)",
2515
+ getattr(getattr(request, "user", None), "pk", None),
2516
+ getattr(profile, "pk", None),
2517
+ getattr(profile, "host", None),
2518
+ getattr(profile, "database", None),
2519
+ )
2420
2520
  context["error"] = _("Unable to fetch products from Odoo.")
2521
+ if getattr(request.user, "is_superuser", False):
2522
+ fault = getattr(exc, "faultString", "")
2523
+ message = str(exc)
2524
+ details = [
2525
+ f"Host: {getattr(profile, 'host', '')}",
2526
+ f"Database: {getattr(profile, 'database', '')}",
2527
+ f"User ID: {getattr(profile, 'odoo_uid', '')}",
2528
+ ]
2529
+ if fault and fault != message:
2530
+ details.append(f"Fault: {fault}")
2531
+ if message:
2532
+ details.append(f"Exception: {type(exc).__name__}: {message}")
2533
+ else:
2534
+ details.append(f"Exception type: {type(exc).__name__}")
2535
+ context["debug_error"] = "\n".join(details)
2421
2536
  return context, []
2422
2537
 
2423
2538
  context["has_credentials"] = True
@@ -2697,7 +2812,7 @@ class RFIDAdmin(EntityModelAdmin, ImportExportModelAdmin):
2697
2812
  "user_data_flag",
2698
2813
  "color",
2699
2814
  "kind",
2700
- "endianness",
2815
+ "endianness_short",
2701
2816
  "released",
2702
2817
  "allowed",
2703
2818
  "last_seen_on",
@@ -2762,6 +2877,14 @@ class RFIDAdmin(EntityModelAdmin, ImportExportModelAdmin):
2762
2877
  def user_data_flag(self, obj):
2763
2878
  return getattr(obj, "is_user_data", False)
2764
2879
 
2880
+ @admin.display(description=_("End"), ordering="endianness")
2881
+ def endianness_short(self, obj):
2882
+ labels = {
2883
+ RFID.BIG_ENDIAN: _("Big"),
2884
+ RFID.LITTLE_ENDIAN: _("Little"),
2885
+ }
2886
+ return labels.get(obj.endianness, obj.get_endianness_display())
2887
+
2765
2888
  def scan_rfids(self, request, queryset):
2766
2889
  return redirect("admin:core_rfid_scan")
2767
2890
 
@@ -3715,9 +3838,9 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, EntityModelAdmin):
3715
3838
  self.message_user(request, str(exc), messages.ERROR)
3716
3839
  return
3717
3840
  releases = resp.json().get("releases", {})
3718
- created = 0
3719
3841
  updated = 0
3720
3842
  restored = 0
3843
+ missing: list[str] = []
3721
3844
 
3722
3845
  for version, files in releases.items():
3723
3846
  release_on = self._release_on_from_files(files)
@@ -3742,23 +3865,11 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, EntityModelAdmin):
3742
3865
  if update_fields:
3743
3866
  release.save(update_fields=update_fields)
3744
3867
  continue
3745
- PackageRelease.objects.create(
3746
- package=package,
3747
- release_manager=package.release_manager,
3748
- version=version,
3749
- revision="",
3750
- pypi_url=f"https://pypi.org/project/{package.name}/{version}/",
3751
- release_on=release_on,
3752
- )
3753
- created += 1
3868
+ missing.append(version)
3754
3869
 
3755
- if created or updated or restored:
3870
+ if updated or restored:
3756
3871
  PackageRelease.dump_fixture()
3757
3872
  message_parts = []
3758
- if created:
3759
- message_parts.append(
3760
- f"Created {created} release{'s' if created != 1 else ''} from PyPI"
3761
- )
3762
3873
  if updated:
3763
3874
  message_parts.append(
3764
3875
  f"Updated release date for {updated} release"
@@ -3769,8 +3880,17 @@ class PackageReleaseAdmin(SaveBeforeChangeAction, EntityModelAdmin):
3769
3880
  f"Restored {restored} release{'s' if restored != 1 else ''}"
3770
3881
  )
3771
3882
  self.message_user(request, "; ".join(message_parts), messages.SUCCESS)
3772
- else:
3773
- self.message_user(request, "No new releases found", messages.INFO)
3883
+ elif not missing:
3884
+ self.message_user(request, "No matching releases found", messages.INFO)
3885
+
3886
+ if missing:
3887
+ versions = ", ".join(sorted(missing))
3888
+ count = len(missing)
3889
+ message = (
3890
+ "Manual creation required for "
3891
+ f"{count} release{'s' if count != 1 else ''}: {versions}"
3892
+ )
3893
+ self.message_user(request, message, messages.WARNING)
3774
3894
 
3775
3895
  refresh_from_pypi.label = "Refresh from PyPI"
3776
3896
  refresh_from_pypi.short_description = "Refresh from PyPI"