arthexis 0.1.16__py3-none-any.whl → 0.1.26__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.16.dist-info → arthexis-0.1.26.dist-info}/METADATA +84 -35
- arthexis-0.1.26.dist-info/RECORD +111 -0
- config/asgi.py +1 -15
- config/middleware.py +47 -1
- config/settings.py +15 -30
- config/urls.py +53 -1
- core/admin.py +540 -450
- core/apps.py +0 -6
- core/auto_upgrade.py +19 -4
- core/backends.py +13 -3
- core/changelog.py +66 -5
- core/environment.py +4 -5
- core/models.py +1566 -203
- core/notifications.py +1 -1
- core/reference_utils.py +10 -11
- core/release.py +55 -7
- core/sigil_builder.py +2 -2
- core/sigil_resolver.py +1 -66
- core/system.py +268 -2
- core/tasks.py +174 -48
- core/tests.py +314 -16
- core/user_data.py +42 -2
- core/views.py +278 -183
- nodes/admin.py +557 -65
- nodes/apps.py +11 -0
- nodes/models.py +658 -113
- nodes/rfid_sync.py +1 -1
- nodes/tasks.py +97 -2
- nodes/tests.py +1212 -116
- nodes/urls.py +15 -1
- nodes/utils.py +51 -3
- nodes/views.py +1239 -154
- ocpp/admin.py +979 -152
- ocpp/consumers.py +268 -28
- ocpp/models.py +488 -3
- ocpp/network.py +398 -0
- ocpp/store.py +6 -4
- ocpp/tasks.py +296 -2
- ocpp/test_export_import.py +1 -0
- ocpp/test_rfid.py +121 -4
- ocpp/tests.py +950 -11
- ocpp/transactions_io.py +9 -1
- ocpp/urls.py +3 -3
- ocpp/views.py +596 -51
- pages/admin.py +262 -30
- pages/apps.py +35 -0
- pages/context_processors.py +26 -21
- pages/defaults.py +1 -1
- pages/forms.py +31 -8
- pages/middleware.py +6 -2
- pages/models.py +77 -2
- pages/module_defaults.py +5 -5
- pages/site_config.py +137 -0
- pages/tests.py +885 -109
- pages/urls.py +13 -2
- pages/utils.py +70 -0
- pages/views.py +558 -55
- arthexis-0.1.16.dist-info/RECORD +0 -111
- core/workgroup_urls.py +0 -17
- core/workgroup_views.py +0 -94
- {arthexis-0.1.16.dist-info → arthexis-0.1.26.dist-info}/WHEEL +0 -0
- {arthexis-0.1.16.dist-info → arthexis-0.1.26.dist-info}/licenses/LICENSE +0 -0
- {arthexis-0.1.16.dist-info → arthexis-0.1.26.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arthexis
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.26
|
|
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
|
|
@@ -15,7 +15,7 @@ Requires-Dist: amqp==5.3.1
|
|
|
15
15
|
Requires-Dist: annotated-types==0.7.0
|
|
16
16
|
Requires-Dist: anyio==4.9.0
|
|
17
17
|
Requires-Dist: asgiref==3.10.0
|
|
18
|
-
Requires-Dist: atproto
|
|
18
|
+
Requires-Dist: atproto<0.1.0,>=0.0.63
|
|
19
19
|
Requires-Dist: attrs==25.3.0
|
|
20
20
|
Requires-Dist: autobahn==24.4.2
|
|
21
21
|
Requires-Dist: Automat==25.4.16
|
|
@@ -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.
|
|
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==
|
|
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
|
|
@@ -41,46 +41,47 @@ Requires-Dist: defusedxml==0.7.1
|
|
|
41
41
|
Requires-Dist: Django==5.2.7
|
|
42
42
|
Requires-Dist: django-celery-beat==2.8.1
|
|
43
43
|
Requires-Dist: django-debug-toolbar==6.0.0
|
|
44
|
-
Requires-Dist: django-import-export==4.3.
|
|
44
|
+
Requires-Dist: django-import-export==4.3.12
|
|
45
45
|
Requires-Dist: django-object-actions==5.0.0
|
|
46
46
|
Requires-Dist: django-otp==1.5.4
|
|
47
47
|
Requires-Dist: django-timezone-field==7.1
|
|
48
48
|
Requires-Dist: dnspython==2.7.0
|
|
49
49
|
Requires-Dist: docutils==0.22.2
|
|
50
50
|
Requires-Dist: gpiozero==2.0.1; sys_platform == "linux"
|
|
51
|
+
Requires-Dist: graphene-django==3.2.2
|
|
51
52
|
Requires-Dist: graphviz==0.21
|
|
53
|
+
Requires-Dist: geckodriver-autoinstaller==0.1.0
|
|
52
54
|
Requires-Dist: h11==0.16.0
|
|
53
55
|
Requires-Dist: httpcore==1.0.9
|
|
54
56
|
Requires-Dist: httpx==0.28.1
|
|
55
57
|
Requires-Dist: hyperlink==21.0.0
|
|
56
|
-
Requires-Dist: idna==3.
|
|
58
|
+
Requires-Dist: idna==3.11
|
|
57
59
|
Requires-Dist: incremental==24.7.2
|
|
58
60
|
Requires-Dist: kombu==5.5.4
|
|
59
|
-
Requires-Dist: libipld==3.
|
|
60
|
-
Requires-Dist: Markdown==3.
|
|
61
|
+
Requires-Dist: libipld==3.2.0
|
|
62
|
+
Requires-Dist: Markdown==3.9
|
|
61
63
|
Requires-Dist: mdx_truly_sane_lists==1.3
|
|
62
|
-
Requires-Dist: mcp==1.16.0
|
|
63
64
|
Requires-Dist: mfrc522==0.0.7; sys_platform == "linux"
|
|
64
65
|
Requires-Dist: outcome==1.3.0.post0
|
|
65
66
|
Requires-Dist: packaging==25.0
|
|
66
67
|
Requires-Dist: pillow==11.3.0
|
|
67
68
|
Requires-Dist: prompt_toolkit==3.0.51
|
|
68
|
-
Requires-Dist: psutil==
|
|
69
|
+
Requires-Dist: psutil==7.1.2
|
|
69
70
|
Requires-Dist: psycopg==3.2.9
|
|
70
|
-
Requires-Dist: psycopg-binary==3.2.
|
|
71
|
+
Requires-Dist: psycopg-binary==3.2.12
|
|
71
72
|
Requires-Dist: pyasn1==0.6.1
|
|
72
73
|
Requires-Dist: pyasn1_modules==0.4.2
|
|
73
74
|
Requires-Dist: pycparser==2.22
|
|
74
|
-
Requires-Dist: pydantic==2.
|
|
75
|
-
Requires-Dist: pydantic_core==2.
|
|
75
|
+
Requires-Dist: pydantic==2.12.3
|
|
76
|
+
Requires-Dist: pydantic_core==2.41.4
|
|
76
77
|
Requires-Dist: pyOpenSSL==25.1.0
|
|
77
|
-
Requires-Dist: pyperclip==1.
|
|
78
|
+
Requires-Dist: pyperclip==1.11.0
|
|
78
79
|
Requires-Dist: PySocks==1.7.1
|
|
79
80
|
Requires-Dist: python-crontab==3.3.0
|
|
80
81
|
Requires-Dist: python-dateutil==2.9.0.post0
|
|
81
82
|
Requires-Dist: python-dotenv==1.1.1
|
|
82
83
|
Requires-Dist: qrcode==8.2
|
|
83
|
-
Requires-Dist: redis==
|
|
84
|
+
Requires-Dist: redis==7.0.1
|
|
84
85
|
Requires-Dist: reportlab==4.2.2
|
|
85
86
|
Requires-Dist: requests==2.32.5
|
|
86
87
|
Requires-Dist: selenium==4.34.2
|
|
@@ -99,19 +100,19 @@ Requires-Dist: trio-websocket==0.12.2
|
|
|
99
100
|
Requires-Dist: Twisted==25.5.0
|
|
100
101
|
Requires-Dist: twine==6.1.0
|
|
101
102
|
Requires-Dist: txaio==25.6.1
|
|
102
|
-
Requires-Dist: typing-inspection==0.4.
|
|
103
|
+
Requires-Dist: typing-inspection==0.4.2
|
|
103
104
|
Requires-Dist: typing_extensions==4.14.1
|
|
104
105
|
Requires-Dist: tzdata==2025.2
|
|
105
106
|
Requires-Dist: urllib3==2.5.0
|
|
106
107
|
Requires-Dist: vine==5.1.0
|
|
107
|
-
Requires-Dist: wcwidth==0.2.
|
|
108
|
+
Requires-Dist: wcwidth==0.2.14
|
|
108
109
|
Requires-Dist: webencodings==0.5.1
|
|
109
110
|
Requires-Dist: websocket-client==1.8.0
|
|
110
|
-
Requires-Dist: websockets==
|
|
111
|
-
Requires-Dist: whitenoise==6.
|
|
111
|
+
Requires-Dist: websockets==15.0.1
|
|
112
|
+
Requires-Dist: whitenoise==6.11.0
|
|
112
113
|
Requires-Dist: plyer==2.1.0; sys_platform == "win32"
|
|
113
114
|
Requires-Dist: wsproto==1.2.0
|
|
114
|
-
Requires-Dist: zope.interface==
|
|
115
|
+
Requires-Dist: zope.interface==8.0.1
|
|
115
116
|
Dynamic: license-file
|
|
116
117
|
|
|
117
118
|
# Arthexis Constellation
|
|
@@ -125,19 +126,39 @@ Arthexis Constellation is a [narrative-driven](https://en.wikipedia.org/wiki/Nar
|
|
|
125
126
|
|
|
126
127
|
## Current Features
|
|
127
128
|
|
|
128
|
-
- Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/) central system
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
129
|
+
- Compatible with the [Open Charge Point Protocol (OCPP) 1.6](https://www.openchargealliance.org/protocols/ocpp-16/) central system. Supported actions are summarized below.
|
|
130
|
+
|
|
131
|
+
**Charge point → CSMS**
|
|
132
|
+
|
|
133
|
+
| Action | What we do |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `Authorize` | Validate RFID or token authorization requests before a session starts. |
|
|
136
|
+
| `BootNotification` | Register the charge point and update identity, firmware, and status details. |
|
|
137
|
+
| `DataTransfer` | Accept vendor-specific payloads and record the results. |
|
|
138
|
+
| `DiagnosticsStatusNotification` | Track the progress of diagnostic uploads kicked off from the back office. |
|
|
139
|
+
| `FirmwareStatusNotification` | Track firmware update lifecycle events from charge points. |
|
|
140
|
+
| `Heartbeat` | Keep the websocket session alive and update last-seen timestamps. |
|
|
141
|
+
| `MeterValues` | Persist periodic energy and power readings while a transaction is active. |
|
|
142
|
+
| `StartTransaction` | Create charging sessions with initial meter values and identification data. |
|
|
143
|
+
| `StatusNotification` | Reflect connector availability and fault states in real time. |
|
|
144
|
+
| `StopTransaction` | Close charging sessions, capturing closing meter values and stop reasons. |
|
|
145
|
+
|
|
146
|
+
**CSMS → Charge point**
|
|
147
|
+
|
|
148
|
+
| Action | What we do |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `ChangeAvailability` | Switch connectors or the whole station between operative and inoperative states. |
|
|
151
|
+
| `DataTransfer` | Send vendor-specific commands and log the charge point response. |
|
|
152
|
+
| `GetConfiguration` | Poll the device for the current values of tracked configuration keys. |
|
|
153
|
+
| `RemoteStartTransaction` | Initiate a charging session remotely for an identified customer or token. |
|
|
154
|
+
| `RemoteStopTransaction` | Terminate active charging sessions from the control center. |
|
|
155
|
+
| `ReserveNow` | Reserve connectors for upcoming sessions with automatic connector selection and confirmation tracking. |
|
|
156
|
+
| `Reset` | Request a soft or hard reboot to recover from faults. |
|
|
157
|
+
| `TriggerMessage` | Ask the device to send an immediate update (for example status or diagnostics). |
|
|
158
|
+
|
|
159
|
+
**OCPP 1.6 roadmap.** The following catalogue actions are in our backlog: `CancelReservation`, `ChangeConfiguration`, `ClearCache`, `ClearChargingProfile`, `GetCompositeSchedule`, `GetDiagnostics`, `GetLocalListVersion`, `SendLocalList`, `SetChargingProfile`, `UnlockConnector`, `UpdateFirmware`.
|
|
160
|
+
|
|
161
|
+
- Charge point reservations with automated connector assignment, energy account and RFID linkage, and EVCS confirmation tracking.
|
|
141
162
|
- [API](https://en.wikipedia.org/wiki/API) integration with [Odoo](https://www.odoo.com/), syncing:
|
|
142
163
|
- Employee credentials via `res.users`
|
|
143
164
|
- Product catalog lookups via `product.product`
|
|
@@ -171,7 +192,7 @@ Arthexis Constellation ships in four node roles tailored to different deployment
|
|
|
171
192
|
<td valign="top"><strong>Multi-Device Edge, Network & Data Acquisition</strong><br />Features: AP Router, Celery Queue, NGINX Server, RFID Scanner</td>
|
|
172
193
|
</tr>
|
|
173
194
|
<tr>
|
|
174
|
-
<td valign="top"><strong>
|
|
195
|
+
<td valign="top"><strong>Watchtower</strong></td>
|
|
175
196
|
<td valign="top"><strong>Multi-User Cloud & Orchestration</strong><br />Features: Celery Queue, NGINX Server</td>
|
|
176
197
|
</tr>
|
|
177
198
|
</tbody>
|
|
@@ -184,7 +205,7 @@ Arthexis Constellation ships in four node roles tailored to different deployment
|
|
|
184
205
|
- **[Windows](https://en.wikipedia.org/wiki/Microsoft_Windows)**: open [PowerShell](https://learn.microsoft.com/powershell/) or [Git Bash](https://gitforwindows.org/) and run the same command.
|
|
185
206
|
|
|
186
207
|
### 2. Start and stop
|
|
187
|
-
Terminal nodes can start directly with the scripts below without installing; Control, Satellite, and
|
|
208
|
+
Terminal nodes can start directly with the scripts below without installing; Control, Satellite, and Watchtower roles require installation first. Both approaches listen on [`http://localhost:8000/`](http://localhost:8000/) by default.
|
|
188
209
|
|
|
189
210
|
- **[VS Code](https://code.visualstudio.com/)**
|
|
190
211
|
- Open the folder and go to the **Run and Debug** panel (`Ctrl+Shift+D`).
|
|
@@ -204,6 +225,7 @@ Terminal nodes can start directly with the scripts below without installing; Con
|
|
|
204
225
|
- `--constellation` – enables the multi-user orchestration stack.
|
|
205
226
|
- Use `./install.sh --help` to list every available flag if you need to customize the node beyond the role defaults.
|
|
206
227
|
- Upgrade with [`./upgrade.sh`](upgrade.sh).
|
|
228
|
+
- Consult the [Install & Lifecycle Scripts Manual](docs/development/install-lifecycle-scripts-manual.md) for complete flag descriptions and operational notes.
|
|
207
229
|
|
|
208
230
|
- **Windows:**
|
|
209
231
|
- Run [`install.bat`](install.bat) to install (Terminal role) and [`upgrade.bat`](upgrade.bat) to upgrade.
|
|
@@ -212,10 +234,37 @@ Terminal nodes can start directly with the scripts below without installing; Con
|
|
|
212
234
|
### 4. Administration
|
|
213
235
|
Visit [`http://localhost:8000/admin/`](http://localhost:8000/admin/) for the [Django admin](https://docs.djangoproject.com/en/stable/ref/contrib/admin/) and [`http://localhost:8000/admindocs/`](http://localhost:8000/admindocs/) for the [admindocs](https://docs.djangoproject.com/en/stable/ref/contrib/admin/admindocs/). Use `--port` with the start scripts or installer when you need to expose a different port.
|
|
214
236
|
|
|
237
|
+
## Sigils
|
|
238
|
+
|
|
239
|
+
Sigils are bracketed tokens such as `[ENV.SMTP_PASSWORD]` that Arthexis expands at runtime. They make it possible to reference configuration secrets, system metadata, or records stored in other apps without duplicating values across the project.
|
|
240
|
+
|
|
241
|
+
### Syntax at a glance
|
|
242
|
+
|
|
243
|
+
- `[PREFIX.KEY]` — returns a field or attribute. Hyphens and casing are normalized automatically.
|
|
244
|
+
- `[PREFIX=IDENTIFIER.FIELD]` — selects a specific record by primary key or any unique field.
|
|
245
|
+
- `[PREFIX:FIELD=VALUE.ATTRIBUTE]` — filters by a custom field instead of the primary key.
|
|
246
|
+
- `[PREFIX.FIELD=[OTHER.SIGIL]]` — nests sigils so the value after `=` resolves before the outer token.
|
|
247
|
+
- `[PREFIX]` — for entity prefixes, returns the serialized object in JSON; for configuration prefixes, resolves to an empty string when the key is missing.
|
|
248
|
+
|
|
249
|
+
The platform ships with three configuration prefixes:
|
|
250
|
+
|
|
251
|
+
- `ENV` reads environment variables.
|
|
252
|
+
- `CONF` reads Django settings.
|
|
253
|
+
- `SYS` exposes computed system information such as build metadata.
|
|
254
|
+
|
|
255
|
+
Additional prefixes are defined through **Sigil Roots**, which map a short code (for example `ROLE`, `ODOO`, or `USER`) to a Django model. You can review them from **Admin → Sigil Builder** (`/admin/sigil-builder/`), where a test console is also available.
|
|
256
|
+
|
|
257
|
+
Unknown prefixes remain in place (e.g. `[UNKNOWN.VALUE]`) and are logged.
|
|
258
|
+
|
|
215
259
|
## Support
|
|
216
260
|
|
|
217
261
|
Contact us at [tecnologia@gelectriic.com](mailto:tecnologia@gelectriic.com) or visit our [web page](https://www.gelectriic.com/) for [professional services](https://en.wikipedia.org/wiki/Professional_services) and [commercial support](https://en.wikipedia.org/wiki/Technical_support).
|
|
218
262
|
|
|
263
|
+
## Project Guidelines
|
|
264
|
+
|
|
265
|
+
- [AGENTS](AGENTS.md) – operating handbook for repository workflows, testing, and release management.
|
|
266
|
+
- [DESIGN](DESIGN.md) – visual, UX, and branding guidance that all interfaces must follow.
|
|
267
|
+
|
|
219
268
|
## About Me
|
|
220
269
|
|
|
221
270
|
> "What, you want to know about me too? Well, I enjoy [developing software](https://en.wikipedia.org/wiki/Software_development), [role-playing games](https://en.wikipedia.org/wiki/Role-playing_game), long walks on the [beach](https://en.wikipedia.org/wiki/Beach) and a fourth secret thing."
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
arthexis-0.1.26.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
2
|
+
config/__init__.py,sha256=AwpOX7il-DAOmkdJ5dVfVJ3CWWebn1lHyQNmkw1EkDw,103
|
|
3
|
+
config/active_app.py,sha256=KJqYh-o91nPQjVXPEdbiJHzsI6cN9IZsBZ9O3iZ6Hyc,373
|
|
4
|
+
config/asgi.py,sha256=Z2HjWrxOxVU9BXcqS7dMEfOGJC48H-WPwFwokRdermY,774
|
|
5
|
+
config/auth_app.py,sha256=cLlKgFYV4VWsMACinKrpP-nZhXXbWZp1aj1RayDszRc,198
|
|
6
|
+
config/celery.py,sha256=c8fPkjfhKw0UQBr6FolzfdvRX2MQAV-dMokSlJ1Afgg,819
|
|
7
|
+
config/context_processors.py,sha256=p74ocuzPRFI9vKSeIaJ42Vu0V2GtGph1t-2DkRo4NMw,2449
|
|
8
|
+
config/horologia_app.py,sha256=puO_hObEYcLvE7PqcY_sGv1thnxJ018YKHKZWqNXha4,187
|
|
9
|
+
config/loadenv.py,sha256=CjXx-wBaTt1wixub4GJ5CMSMFqtiK5JURc7cPXpqO7s,287
|
|
10
|
+
config/logging.py,sha256=1cIbPgRshHuMKnVEEH0jKpRAlJSpewvLFbYDz7sCBG4,2104
|
|
11
|
+
config/middleware.py,sha256=zF8Cma0n5G8NNdh2LVeNJi7Hgl1G4mF9msRE2eRi1RU,2328
|
|
12
|
+
config/offline.py,sha256=X-yDcyoI4C44Y27lpkUwszY_09GwwFfazEsthKJpQ70,1382
|
|
13
|
+
config/settings.py,sha256=1vKOC0VwPOMXXnpUr4OIDRS1C_l_PXY-yyiDo7jmflM,20970
|
|
14
|
+
config/settings_helpers.py,sha256=0BdBciUHIkwsWa0vV_RKAd4wDuEzgE7G-42XYiES4YQ,3127
|
|
15
|
+
config/urls.py,sha256=RtoLsMBdsuKi_hxrlUW3GFCtiAZI_zGxrgXCxqpVVYE,6949
|
|
16
|
+
config/wsgi.py,sha256=zU_mKlya6hejQ21PxKacTui3dUWd4ca_-YJNSYAoMX0,433
|
|
17
|
+
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
core/admin.py,sha256=IT9nfHKNRWSMmQeCVtCaNCU--aLdAGkViwIx-MZaZJ8,145219
|
|
19
|
+
core/admin_history.py,sha256=XZ4b0ryufIka-xcwboK3DzmOL-INSx5Y2fJO-aJdV70,1783
|
|
20
|
+
core/admindocs.py,sha256=ycD0bJ_VE6rTGf9ebXTiKdYkD8Y8hD2oQ4HxxoBURCM,6756
|
|
21
|
+
core/apps.py,sha256=S6fySxtxUzfvz8FI9dii0KI4wSyLhh5API_oeERLIsc,14084
|
|
22
|
+
core/auto_upgrade.py,sha256=8JpTWPH3-bQpmtOnTa5h3wdTVAbdthr0IqJze4deDUk,2242
|
|
23
|
+
core/backends.py,sha256=O6QzNsX3OXi0QeIO7PCXI57VLz7HoY72ouBGTpzsSPM,10793
|
|
24
|
+
core/changelog.py,sha256=SRn37i5N-qb-RYV4Gpu9fg7Kv8gu4TH8ZwEmDRgN-Vo,12594
|
|
25
|
+
core/entity.py,sha256=o4VteOXePGEsIWJFZ3fpq3DZsdWr3hpQ9A6kFbKosSE,4844
|
|
26
|
+
core/environment.py,sha256=QoiMjFVmwCEln2DEa10Hht6Ymop0H6wbDsyO6-ebXp4,1651
|
|
27
|
+
core/fields.py,sha256=d-qGahdcv4SRcO4fwCJ6_-NnEAP5xW0k3kODdAAAHSA,5412
|
|
28
|
+
core/form_fields.py,sha256=h2xT8sO8EWbznsiARkxukFk69yoW6mQwqpgonA-d6aA,2496
|
|
29
|
+
core/github_helper.py,sha256=fkjoUPwOB19zbGuk39LNLJ5AbIVKFf3rNCtnu-JISIc,5733
|
|
30
|
+
core/github_issues.py,sha256=qIygOk1ZCki0eB-9o1poJ2BnaMnbHH4ewVE36hqHUuo,5223
|
|
31
|
+
core/github_repos.py,sha256=8KCxcEiO2Ltgde7UDTAFOyHTm_eBeZYUIZegEbrjkWA,1690
|
|
32
|
+
core/lcd_screen.py,sha256=WtHMlSoZXKOsdM0d-v-f8ul-LSA6FA1bEWFwho1t6s8,2573
|
|
33
|
+
core/liveupdate.py,sha256=22m0ueQ10-6b-9pQJHY0_5WRYA98fysXKEXOWzIr550,691
|
|
34
|
+
core/log_paths.py,sha256=lxvgXPgJtVNZ-kYrqV8VFle4GFQrSxG-yRTglqvclmU,3318
|
|
35
|
+
core/mailer.py,sha256=JpW0RnD9uZ4O-wvlqeW7CMw95IFeCSkdvbankJDwHq0,2886
|
|
36
|
+
core/middleware.py,sha256=j19K9SX-Emkv7BDDtAacR9g6RWsxhKHwCc8w23JFvMM,3388
|
|
37
|
+
core/models.py,sha256=-vZjeIEFplX0uvFuh0Ctl1oxYLvx0GOvBvSZa1zZwe0,174650
|
|
38
|
+
core/notifications.py,sha256=jNLSuSCrhb8x5cDu_APeDlkrmbMejufk5eJOhssAC4I,3917
|
|
39
|
+
core/public_wifi.py,sha256=yydLgxOo9DmJJbM4X_23wGR3gxL3YzHno54v9GssuFA,7213
|
|
40
|
+
core/reference_utils.py,sha256=tffCoyE1w4_SmYzXVWOsW8aR_ZVVTSPzrGhBq8K2xzA,3631
|
|
41
|
+
core/release.py,sha256=tEMcM7qubmFGmER3TD_MG5j4RceHcwtBjczArZWXEWE,31357
|
|
42
|
+
core/rfid_import_export.py,sha256=petyhPvL0WUpehc6uGUDUhjYQ9AVvc6O49zuhDs6YFw,3516
|
|
43
|
+
core/sigil_builder.py,sha256=SmmYRjl11u2GpU8rt3va-TP5hwAOobJTWTUGqVAikDk,4854
|
|
44
|
+
core/sigil_context.py,sha256=GCzjfM6fcVvBtSbVNfmE6sx3HU8QnxnXrCIytnNpQzM,439
|
|
45
|
+
core/sigil_resolver.py,sha256=06Zt4g3KCJSeQJAg_NYCIbXhNejpfFclosweur0C4W4,9182
|
|
46
|
+
core/system.py,sha256=RVQA66t43Vt-jn5jIZUzYZ63FANs4onk8bXNJRl-rWo,44104
|
|
47
|
+
core/tasks.py,sha256=nhXLn_kE5r7VMraMIG22tBNgKrVTMnvUpuVyHlmUoHg,16176
|
|
48
|
+
core/temp_passwords.py,sha256=FieUnIUeQHmA1DoXvfJ5U6-Ayv3oDz-hSln5s_vNbA4,5271
|
|
49
|
+
core/test_system_info.py,sha256=IMPz21KEs6OC5YbL7YaIBdmJVLjRY6MgPuZpldJB5OI,6935
|
|
50
|
+
core/tests.py,sha256=PuxoarDS4reHNV4EDIyVRW7xIOFxZJYou1K_LI9ZNHY,105265
|
|
51
|
+
core/tests_liveupdate.py,sha256=IquU8ztk6zbzC1bQu3Nrr3RzGzuujtPwDkANJHbxg98,510
|
|
52
|
+
core/urls.py,sha256=YPippON1MAP2KeZZ8jHpcLO6mvbnKn1q7fdMv5Vm9dY,425
|
|
53
|
+
core/user_data.py,sha256=4pheHB5RqLJtmWMql30CLaCpuVqSyShXb7Sy-crRk_4,22400
|
|
54
|
+
core/views.py,sha256=s_a2tu9xwOpVSo7vo0iDDV4RN23jxGZNR-o7JNHs1Wk,88256
|
|
55
|
+
core/widgets.py,sha256=vlR9PlFfZGlkHm5X2cqNXuEBZSj8gmWaR6MO1mMy6kg,6904
|
|
56
|
+
nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
+
nodes/admin.py,sha256=H4wgxrVOvMhtFcl4JbLsyLklaM8WUAjS1xdF0ROkGAA,79911
|
|
58
|
+
nodes/apps.py,sha256=oi_M2Ya8CAR8N_MoYU68u7_9u-9SlIMelzLOgYM9tDs,3059
|
|
59
|
+
nodes/backends.py,sha256=dmmbS0X2YIlCDz2KjoDf_L62dy--nuqZF1rEDoi2JHM,5921
|
|
60
|
+
nodes/dns.py,sha256=D5smXD7Rkh6E4MdL6TBL2WY8GgJg7Rx9z88LZrcMbTw,7048
|
|
61
|
+
nodes/feature_checks.py,sha256=27e4PCkZ8BGWnJCOwMcY2Bo9z7LoeZWiTZuISWGnrzk,3996
|
|
62
|
+
nodes/lcd.py,sha256=iKA8Wmq85KZD52aTzAU8ZmS144_gbdGMOXcE8yuECps,5758
|
|
63
|
+
nodes/models.py,sha256=x_s7b2smjEsHE6O-fAz6u8gPxXM5QzwaswhLp56nm1E,83613
|
|
64
|
+
nodes/reports.py,sha256=NRYh3Y0SlZFhx31Zh2K03yO12ZrpxEHEY6T-dODA6WE,12059
|
|
65
|
+
nodes/rfid_sync.py,sha256=oeblawcp6xeLApdIuhsJS83OAk58Eu7pVVmgpAc0Nt8,6953
|
|
66
|
+
nodes/signals.py,sha256=PtOKdQfb08mV1LgSZvn7ZAcfOyy2c3Xkq4AOpBQyUdE,622
|
|
67
|
+
nodes/tasks.py,sha256=TKSUE4eILV644iPtn2Xr_UL3ZFYgzjzSAIGUYmhg3Sk,5111
|
|
68
|
+
nodes/tests.py,sha256=-cnhtAh8bWepy9RZU_j_fFROen9GNLe-bQI4qLyngPk,199544
|
|
69
|
+
nodes/urls.py,sha256=9uMhDq-b-EWZz0u-NvPRVSPXJeXfuS-BAACvsCs6gaE,1267
|
|
70
|
+
nodes/utils.py,sha256=x7l8Fz8UwWBumeNrHemmLXXRW_5bkMHaaRnE-VLZFwo,6337
|
|
71
|
+
nodes/views.py,sha256=4uktDPCpkN779Uj5LSnH1PSx4cNim7UfUz8q9eX69NI,60081
|
|
72
|
+
ocpp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
+
ocpp/admin.py,sha256=BSfS4bwqxcC0a57xXOyIw9hFRxB0y7bLsF2gP5rRMfg,60175
|
|
74
|
+
ocpp/apps.py,sha256=i3NqrmIamNEQBT33CIqh7HOSOPmJXCMKrZ-DUd3whqg,842
|
|
75
|
+
ocpp/consumers.py,sha256=XfrtwJR8OLvYzxj0NIIEa_ky36UBjHROUtoEFqFK39I,75577
|
|
76
|
+
ocpp/evcs.py,sha256=q1mZrCVSZxXTrtYsDqH6lkeEcJ6tfSC7p9YxkDmpSCw,28883
|
|
77
|
+
ocpp/evcs_discovery.py,sha256=OmrzgaOHwveDRJs8AIhrM3apX8_k2PPXh_oYaYpNW3c,3876
|
|
78
|
+
ocpp/models.py,sha256=z5uy3zuTCICkrIg7rvBdnnYJzwHUyFrUP1AE-8LX8mk,47889
|
|
79
|
+
ocpp/network.py,sha256=N3je0wXckSqlHLJNQazpxrBvv0yAR7DdjfAR-hTcWDk,14149
|
|
80
|
+
ocpp/reference_utils.py,sha256=_UR82GfE93kv4766mHyVIfdhhyYvrT59660r3H6W55M,1072
|
|
81
|
+
ocpp/routing.py,sha256=3kQya-MdJ00778xDmX0esQLBP05P200V45asg-CGNoo,438
|
|
82
|
+
ocpp/simulator.py,sha256=vnyd59QffT79AaPhmfM_jipni_nqfG57X5tXyx1rBoc,28016
|
|
83
|
+
ocpp/status_display.py,sha256=YGFosd5HJETA0DcLdsjvx6EfhZSnI8Aa3cMnHG2WsBE,939
|
|
84
|
+
ocpp/store.py,sha256=gLCSaP9KKF7li2ALlE3O3RW5eVJtoe-_YHfKhdf0VOM,18943
|
|
85
|
+
ocpp/tasks.py,sha256=AgxSpWQtal6va_KbJsoNEOWug3NRn571ycwsSmwuJC4,15203
|
|
86
|
+
ocpp/test_export_import.py,sha256=ouQbTCp4mxfqoK6gondlu3PPcyrT9jSbWAX5gqqgaNk,4561
|
|
87
|
+
ocpp/test_rfid.py,sha256=IhFSlvsI8A8D3S32sRE298nYfrmqxbv7GfVErtNU3DQ,39137
|
|
88
|
+
ocpp/tests.py,sha256=pEVega4N2gJctjOiO8CSvMqGKrcrUtZrLSLBNSigRGU,214558
|
|
89
|
+
ocpp/transactions_io.py,sha256=p2aUsKlCDYnZ4ZBrOM7pxXoW_w3Tbm-tvRFSjnR3x24,7738
|
|
90
|
+
ocpp/urls.py,sha256=5ZomUtznJe3kfs8E-DtVp12eFva5jUuJdpTEczIsQ5w,1730
|
|
91
|
+
ocpp/views.py,sha256=6p-xSN6umchsDt-Vub_rUeykC5mWAWRnV4AchkrU0wM,77246
|
|
92
|
+
pages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
93
|
+
pages/admin.py,sha256=VbxkwgjrFx7lXVhwbZuPSvCkS7EC-flBpwOztHejWtE,35834
|
|
94
|
+
pages/apps.py,sha256=0qcTFKVX9_QgqexJtGeph1sHRqq7khJf4x5ZtkWwblg,1424
|
|
95
|
+
pages/checks.py,sha256=sM8_hUVM_HOIocvtTb2sY3AaSEvbTnOlO46UchGVd-0,1527
|
|
96
|
+
pages/context_processors.py,sha256=vrgMu4vYCOonZ8eZ27gQvGU74PBpMi47T512Lu1__sA,5297
|
|
97
|
+
pages/defaults.py,sha256=3tjv3nFPxwpFu6poJ1Ez1MP92Q6ZvyRluftKHlU-zeI,522
|
|
98
|
+
pages/forms.py,sha256=r3JM5qp3_4RR01-u6XV8WDOaeiRe4OvCN8Y52FcsAwI,7909
|
|
99
|
+
pages/middleware.py,sha256=-tXFju1siXvzVsHcgjClfTtryw-5-PwW0171DQQxKu4,7115
|
|
100
|
+
pages/models.py,sha256=9LdIoIK2Epp3YDUk8LUWyhLW5pJ-NiuYTzO_-xKjg0c,23636
|
|
101
|
+
pages/module_defaults.py,sha256=rCAY8aTyxYNL0M5zDr393rX-Gi-svXqKtuLXm0rILrQ,5444
|
|
102
|
+
pages/site_config.py,sha256=f1Me0GFdHeGbIeyMlQNzD2e6hym59YHqbz92U_ppffY,4057
|
|
103
|
+
pages/tasks.py,sha256=ivcba_3wSQ1-cku0oDplzw6vLeQ9hBq3R4TG-LmR5gs,1913
|
|
104
|
+
pages/tests.py,sha256=_bVEijMfjVq46hNeGRDZprbQXAAUyS2LhXqZ5_Tkryg,155739
|
|
105
|
+
pages/urls.py,sha256=Oe88tm67iVHRFcGJLSBidZ0rkRQPRZ_vRt6ahxNqPek,1499
|
|
106
|
+
pages/utils.py,sha256=vEFrXSzN-3wsK2H687_oVKSwsSOP_NB7DXg1hHwHink,2471
|
|
107
|
+
pages/views.py,sha256=Yd7JRD0OQhhvYsYZLVDUxJz9zjba84jLmyhZ1K1RE0w,65286
|
|
108
|
+
arthexis-0.1.26.dist-info/METADATA,sha256=F6dYnw0KaarTV4KJZ19kzT5p9WR2lMNLewUNSEpEumQ,13888
|
|
109
|
+
arthexis-0.1.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
110
|
+
arthexis-0.1.26.dist-info/top_level.txt,sha256=J2a2q8_BWrCZ8H2WFUNMBfO2jz8j2gax6zZh-_1QDac,29
|
|
111
|
+
arthexis-0.1.26.dist-info/RECORD,,
|
config/asgi.py
CHANGED
|
@@ -9,35 +9,21 @@ https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/
|
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
11
|
from config.loadenv import loadenv
|
|
12
|
-
from typing import Any, Awaitable, Callable, Dict, MutableMapping
|
|
13
12
|
from channels.auth import AuthMiddlewareStack
|
|
14
13
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
|
15
14
|
from django.core.asgi import get_asgi_application
|
|
16
15
|
import ocpp.routing
|
|
17
16
|
|
|
18
|
-
from core.mcp.asgi import application as mcp_application
|
|
19
|
-
from core.mcp.asgi import is_mcp_scope
|
|
20
|
-
|
|
21
17
|
loadenv()
|
|
22
18
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
23
19
|
|
|
24
20
|
django_asgi_app = get_asgi_application()
|
|
25
21
|
|
|
26
|
-
Scope = MutableMapping[str, Any]
|
|
27
|
-
Receive = Callable[[], Awaitable[Dict[str, Any]]]
|
|
28
|
-
Send = Callable[[Dict[str, Any]], Awaitable[None]]
|
|
29
|
-
|
|
30
22
|
websocket_patterns = ocpp.routing.websocket_urlpatterns
|
|
31
23
|
|
|
32
|
-
async def http_application(scope: Scope, receive: Receive, send: Send) -> None:
|
|
33
|
-
if is_mcp_scope(scope):
|
|
34
|
-
await mcp_application(scope, receive, send)
|
|
35
|
-
else:
|
|
36
|
-
await django_asgi_app(scope, receive, send)
|
|
37
|
-
|
|
38
24
|
application = ProtocolTypeRouter(
|
|
39
25
|
{
|
|
40
|
-
"http":
|
|
26
|
+
"http": django_asgi_app,
|
|
41
27
|
"websocket": AuthMiddlewareStack(URLRouter(websocket_patterns)),
|
|
42
28
|
}
|
|
43
29
|
)
|
config/middleware.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
from utils.sites import get_site
|
|
2
1
|
import socket
|
|
2
|
+
from django.core.exceptions import DisallowedHost
|
|
3
|
+
from django.http import HttpResponsePermanentRedirect
|
|
3
4
|
from nodes.models import Node
|
|
5
|
+
from utils.sites import get_site
|
|
4
6
|
|
|
5
7
|
from .active_app import set_active_app
|
|
6
8
|
|
|
@@ -17,9 +19,53 @@ class ActiveAppMiddleware:
|
|
|
17
19
|
role_name = node.role.name if node and node.role else "Terminal"
|
|
18
20
|
active = site.name or role_name
|
|
19
21
|
set_active_app(active)
|
|
22
|
+
request.site = site
|
|
20
23
|
request.active_app = active
|
|
21
24
|
try:
|
|
22
25
|
response = self.get_response(request)
|
|
23
26
|
finally:
|
|
24
27
|
set_active_app(socket.gethostname())
|
|
25
28
|
return response
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _is_https_request(request) -> bool:
|
|
32
|
+
if request.is_secure():
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
forwarded_proto = request.META.get("HTTP_X_FORWARDED_PROTO", "")
|
|
36
|
+
if forwarded_proto:
|
|
37
|
+
candidate = forwarded_proto.split(",")[0].strip().lower()
|
|
38
|
+
if candidate == "https":
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
forwarded_header = request.META.get("HTTP_FORWARDED", "")
|
|
42
|
+
for forwarded_part in forwarded_header.split(","):
|
|
43
|
+
for element in forwarded_part.split(";"):
|
|
44
|
+
key, _, value = element.partition("=")
|
|
45
|
+
if key.strip().lower() == "proto" and value.strip().strip('"').lower() == "https":
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SiteHttpsRedirectMiddleware:
|
|
52
|
+
"""Redirect HTTP traffic to HTTPS for sites that require it."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, get_response):
|
|
55
|
+
self.get_response = get_response
|
|
56
|
+
|
|
57
|
+
def __call__(self, request):
|
|
58
|
+
site = getattr(request, "site", None)
|
|
59
|
+
if site is None:
|
|
60
|
+
site = get_site(request)
|
|
61
|
+
request.site = site
|
|
62
|
+
|
|
63
|
+
if getattr(site, "require_https", False) and not _is_https_request(request):
|
|
64
|
+
try:
|
|
65
|
+
host = request.get_host()
|
|
66
|
+
except DisallowedHost: # pragma: no cover - defensive guard
|
|
67
|
+
host = request.META.get("HTTP_HOST", "")
|
|
68
|
+
redirect_url = f"https://{host}{request.get_full_path()}"
|
|
69
|
+
return HttpResponsePermanentRedirect(redirect_url)
|
|
70
|
+
|
|
71
|
+
return self.get_response(request)
|
config/settings.py
CHANGED
|
@@ -18,6 +18,8 @@ import ipaddress
|
|
|
18
18
|
import socket
|
|
19
19
|
from core.log_paths import select_log_dir
|
|
20
20
|
from django.utils.translation import gettext_lazy as _
|
|
21
|
+
from datetime import timedelta
|
|
22
|
+
|
|
21
23
|
from celery.schedules import crontab
|
|
22
24
|
from django.http import request as http_request
|
|
23
25
|
from django.http.request import split_domain_port
|
|
@@ -333,6 +335,7 @@ CsrfViewMiddleware._check_referer = _check_referer_with_forwarded
|
|
|
333
335
|
# Application definition
|
|
334
336
|
|
|
335
337
|
LOCAL_APPS = [
|
|
338
|
+
"api",
|
|
336
339
|
"nodes",
|
|
337
340
|
"core",
|
|
338
341
|
"ocpp",
|
|
@@ -390,6 +393,7 @@ MIDDLEWARE = [
|
|
|
390
393
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
391
394
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
392
395
|
"config.middleware.ActiveAppMiddleware",
|
|
396
|
+
"config.middleware.SiteHttpsRedirectMiddleware",
|
|
393
397
|
"django.middleware.locale.LocaleMiddleware",
|
|
394
398
|
"django.middleware.common.CommonMiddleware",
|
|
395
399
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
@@ -443,32 +447,6 @@ ASGI_APPLICATION = "config.asgi.application"
|
|
|
443
447
|
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
|
|
444
448
|
|
|
445
449
|
|
|
446
|
-
# MCP sigil resolver configuration
|
|
447
|
-
def _env_int(name: str, default: int) -> int:
|
|
448
|
-
try:
|
|
449
|
-
return int(os.environ.get(name, default))
|
|
450
|
-
except (TypeError, ValueError): # pragma: no cover - defensive
|
|
451
|
-
return default
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
def _split_env_list(name: str) -> list[str]:
|
|
455
|
-
raw = os.environ.get(name)
|
|
456
|
-
if not raw:
|
|
457
|
-
return []
|
|
458
|
-
return [item.strip() for item in raw.split(",") if item.strip()]
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
MCP_SIGIL_SERVER = {
|
|
462
|
-
"host": os.environ.get("MCP_SIGIL_HOST", "127.0.0.1"),
|
|
463
|
-
"port": _env_int("MCP_SIGIL_PORT", 8800),
|
|
464
|
-
"api_keys": _split_env_list("MCP_SIGIL_API_KEYS"),
|
|
465
|
-
"required_scopes": ["sigils:read"],
|
|
466
|
-
"issuer_url": os.environ.get("MCP_SIGIL_ISSUER_URL"),
|
|
467
|
-
"resource_server_url": os.environ.get("MCP_SIGIL_RESOURCE_URL"),
|
|
468
|
-
"mount_path": os.environ.get("MCP_SIGIL_MOUNT_PATH", "/mcp"),
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
450
|
# Custom user model
|
|
473
451
|
AUTH_USER_MODEL = "core.User"
|
|
474
452
|
|
|
@@ -480,6 +458,9 @@ AUTHENTICATION_BACKENDS = [
|
|
|
480
458
|
"core.backends.RFIDBackend",
|
|
481
459
|
]
|
|
482
460
|
|
|
461
|
+
# Use the custom login view for all authentication redirects.
|
|
462
|
+
LOGIN_URL = "pages:login"
|
|
463
|
+
|
|
483
464
|
# Issuer name used when generating otpauth URLs for authenticator apps.
|
|
484
465
|
OTP_TOTP_ISSUER = os.environ.get("OTP_TOTP_ISSUER", "Arthexis")
|
|
485
466
|
|
|
@@ -581,7 +562,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|
|
581
562
|
LANGUAGE_CODE = "en-us"
|
|
582
563
|
|
|
583
564
|
LANGUAGES = [
|
|
584
|
-
("es", _("Spanish")),
|
|
565
|
+
("es", _("Spanish (Latin America)")),
|
|
585
566
|
("en", _("English")),
|
|
586
567
|
("it", _("Italian")),
|
|
587
568
|
("de", _("German")),
|
|
@@ -684,8 +665,12 @@ CELERY_BEAT_SCHEDULE = {
|
|
|
684
665
|
"task": "core.tasks.heartbeat",
|
|
685
666
|
"schedule": crontab(minute="*/5"),
|
|
686
667
|
},
|
|
687
|
-
"
|
|
688
|
-
"task": "
|
|
689
|
-
"schedule": crontab(
|
|
668
|
+
"ocpp_configuration_check": {
|
|
669
|
+
"task": "ocpp.tasks.schedule_daily_charge_point_configuration_checks",
|
|
670
|
+
"schedule": crontab(minute=0, hour=0),
|
|
671
|
+
},
|
|
672
|
+
"ocpp_forwarding_push": {
|
|
673
|
+
"task": "ocpp.tasks.push_forwarded_charge_points",
|
|
674
|
+
"schedule": timedelta(seconds=5),
|
|
690
675
|
},
|
|
691
676
|
}
|
config/urls.py
CHANGED
|
@@ -14,6 +14,8 @@ from django.conf import settings
|
|
|
14
14
|
from django.conf.urls.static import static
|
|
15
15
|
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
|
16
16
|
from django.contrib import admin
|
|
17
|
+
from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
|
|
18
|
+
from django.db.utils import DatabaseError, OperationalError, ProgrammingError
|
|
17
19
|
from django.urls import include, path
|
|
18
20
|
import teams.admin # noqa: F401
|
|
19
21
|
from django.views.decorators.csrf import csrf_exempt
|
|
@@ -28,6 +30,44 @@ from core.admindocs import (
|
|
|
28
30
|
)
|
|
29
31
|
from pages import views as pages_views
|
|
30
32
|
|
|
33
|
+
try: # Gate optional GraphQL dependency for roles that do not install it
|
|
34
|
+
from api.views import EnergyGraphQLView
|
|
35
|
+
except ModuleNotFoundError as exc: # pragma: no cover - dependency intentionally optional
|
|
36
|
+
if exc.name in {"graphene_django", "graphene"}:
|
|
37
|
+
EnergyGraphQLView = None # type: ignore[assignment]
|
|
38
|
+
else: # pragma: no cover - unrelated import error
|
|
39
|
+
raise
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _graphql_feature_enabled() -> bool:
|
|
43
|
+
"""Return ``True`` when the GraphQL endpoint should be exposed."""
|
|
44
|
+
|
|
45
|
+
if EnergyGraphQLView is None:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
from nodes.models import Node, NodeFeature
|
|
50
|
+
except (ModuleNotFoundError, AppRegistryNotReady, ImproperlyConfigured):
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
feature = NodeFeature.objects.filter(slug="graphql").first()
|
|
55
|
+
except (DatabaseError, OperationalError, ProgrammingError):
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
if feature is None:
|
|
59
|
+
return True
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
node = Node.get_local()
|
|
63
|
+
except (DatabaseError, OperationalError, ProgrammingError):
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
if node and not node.has_feature("graphql"):
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
31
71
|
admin.site.site_header = _("Constellation")
|
|
32
72
|
admin.site.site_title = _("Constellation")
|
|
33
73
|
|
|
@@ -134,6 +174,11 @@ urlpatterns = [
|
|
|
134
174
|
core_views.todo_done,
|
|
135
175
|
name="todo-done",
|
|
136
176
|
),
|
|
177
|
+
path(
|
|
178
|
+
"admin/core/todos/<int:pk>/delete/",
|
|
179
|
+
core_views.todo_delete,
|
|
180
|
+
name="todo-delete",
|
|
181
|
+
),
|
|
137
182
|
path(
|
|
138
183
|
"admin/core/todos/<int:pk>/snapshot/",
|
|
139
184
|
core_views.todo_snapshot,
|
|
@@ -149,12 +194,19 @@ urlpatterns = [
|
|
|
149
194
|
core_views.odoo_quote_report,
|
|
150
195
|
name="odoo-quote-report",
|
|
151
196
|
),
|
|
197
|
+
path(
|
|
198
|
+
"admin/request-temp-password/",
|
|
199
|
+
core_views.request_temp_password,
|
|
200
|
+
name="admin-request-temp-password",
|
|
201
|
+
),
|
|
152
202
|
path("admin/", admin.site.urls),
|
|
153
203
|
path("i18n/setlang/", csrf_exempt(set_language), name="set_language"),
|
|
154
|
-
path("api/", include("core.workgroup_urls")),
|
|
155
204
|
path("", include("pages.urls")),
|
|
156
205
|
]
|
|
157
206
|
|
|
207
|
+
if _graphql_feature_enabled():
|
|
208
|
+
urlpatterns.append(path("graphql/", EnergyGraphQLView.as_view(), name="graphql"))
|
|
209
|
+
|
|
158
210
|
urlpatterns += autodiscovered_urlpatterns()
|
|
159
211
|
|
|
160
212
|
if settings.DEBUG:
|