arthexis 0.1.9__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.26.dist-info/METADATA +272 -0
- arthexis-0.1.26.dist-info/RECORD +111 -0
- {arthexis-0.1.9.dist-info → arthexis-0.1.26.dist-info}/licenses/LICENSE +674 -674
- config/__init__.py +5 -5
- config/active_app.py +15 -15
- config/asgi.py +29 -29
- config/auth_app.py +7 -7
- config/celery.py +32 -25
- config/context_processors.py +67 -68
- config/horologia_app.py +7 -7
- config/loadenv.py +11 -11
- config/logging.py +59 -48
- config/middleware.py +71 -25
- config/offline.py +49 -49
- config/settings.py +676 -492
- config/settings_helpers.py +109 -0
- config/urls.py +228 -159
- config/wsgi.py +17 -17
- core/admin.py +4052 -2066
- core/admin_history.py +50 -50
- core/admindocs.py +192 -151
- core/apps.py +350 -223
- core/auto_upgrade.py +72 -0
- core/backends.py +311 -124
- core/changelog.py +403 -0
- core/entity.py +149 -133
- core/environment.py +60 -43
- core/fields.py +168 -75
- core/form_fields.py +75 -0
- core/github_helper.py +188 -25
- core/github_issues.py +183 -172
- core/github_repos.py +72 -0
- core/lcd_screen.py +78 -78
- core/liveupdate.py +25 -25
- core/log_paths.py +114 -100
- core/mailer.py +89 -83
- core/middleware.py +91 -91
- core/models.py +5041 -2195
- core/notifications.py +105 -105
- core/public_wifi.py +267 -227
- core/reference_utils.py +107 -0
- core/release.py +940 -346
- core/rfid_import_export.py +113 -0
- core/sigil_builder.py +149 -131
- core/sigil_context.py +20 -20
- core/sigil_resolver.py +250 -284
- core/system.py +1425 -230
- core/tasks.py +538 -199
- core/temp_passwords.py +181 -0
- core/test_system_info.py +202 -43
- core/tests.py +2673 -1069
- core/tests_liveupdate.py +17 -17
- core/urls.py +11 -11
- core/user_data.py +681 -495
- core/views.py +2484 -789
- core/widgets.py +213 -51
- nodes/admin.py +2236 -445
- nodes/apps.py +98 -70
- nodes/backends.py +160 -53
- nodes/dns.py +203 -0
- nodes/feature_checks.py +133 -0
- nodes/lcd.py +165 -165
- nodes/models.py +2375 -870
- nodes/reports.py +411 -0
- nodes/rfid_sync.py +210 -0
- nodes/signals.py +18 -0
- nodes/tasks.py +141 -46
- nodes/tests.py +5045 -1489
- nodes/urls.py +29 -13
- nodes/utils.py +172 -73
- nodes/views.py +1768 -304
- ocpp/admin.py +1775 -481
- ocpp/apps.py +25 -25
- ocpp/consumers.py +1843 -630
- ocpp/evcs.py +844 -928
- ocpp/evcs_discovery.py +158 -0
- ocpp/models.py +1417 -640
- ocpp/network.py +398 -0
- ocpp/reference_utils.py +42 -0
- ocpp/routing.py +11 -9
- ocpp/simulator.py +745 -368
- ocpp/status_display.py +26 -0
- ocpp/store.py +603 -403
- ocpp/tasks.py +479 -31
- ocpp/test_export_import.py +131 -130
- ocpp/test_rfid.py +1072 -540
- ocpp/tests.py +5494 -2296
- ocpp/transactions_io.py +197 -165
- ocpp/urls.py +50 -50
- ocpp/views.py +2024 -912
- pages/admin.py +1123 -396
- pages/apps.py +45 -10
- pages/checks.py +40 -40
- pages/context_processors.py +151 -85
- pages/defaults.py +13 -0
- pages/forms.py +221 -0
- pages/middleware.py +213 -153
- pages/models.py +720 -252
- pages/module_defaults.py +156 -0
- pages/site_config.py +137 -0
- pages/tasks.py +74 -0
- pages/tests.py +4009 -1389
- pages/urls.py +38 -20
- pages/utils.py +93 -12
- pages/views.py +1736 -762
- arthexis-0.1.9.dist-info/METADATA +0 -168
- arthexis-0.1.9.dist-info/RECORD +0 -92
- core/workgroup_urls.py +0 -17
- core/workgroup_views.py +0 -94
- nodes/actions.py +0 -70
- {arthexis-0.1.9.dist-info → arthexis-0.1.26.dist-info}/WHEEL +0 -0
- {arthexis-0.1.9.dist-info → arthexis-0.1.26.dist-info}/top_level.txt +0 -0
pages/middleware.py
CHANGED
|
@@ -1,153 +1,213 @@
|
|
|
1
|
-
"""Middleware helpers for the pages application."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import ipaddress
|
|
6
|
-
import logging
|
|
7
|
-
from http import HTTPStatus
|
|
8
|
-
|
|
9
|
-
from django.conf import settings
|
|
10
|
-
from django.urls import Resolver404, resolve
|
|
11
|
-
|
|
12
|
-
from .models import ViewHistory
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
status_text =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
1
|
+
"""Middleware helpers for the pages application."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ipaddress
|
|
6
|
+
import logging
|
|
7
|
+
from http import HTTPStatus
|
|
8
|
+
|
|
9
|
+
from django.conf import settings
|
|
10
|
+
from django.urls import Resolver404, resolve
|
|
11
|
+
|
|
12
|
+
from .models import Landing, LandingLead, ViewHistory
|
|
13
|
+
from .utils import cache_original_referer, get_original_referer, landing_leads_supported
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ViewHistoryMiddleware:
|
|
20
|
+
"""Persist public site visits for analytics."""
|
|
21
|
+
|
|
22
|
+
_EXCLUDED_PREFIXES = ("/admin", "/__debug__", "/healthz", "/status")
|
|
23
|
+
|
|
24
|
+
def __init__(self, get_response):
|
|
25
|
+
self.get_response = get_response
|
|
26
|
+
static_url = getattr(settings, "STATIC_URL", "") or ""
|
|
27
|
+
media_url = getattr(settings, "MEDIA_URL", "") or ""
|
|
28
|
+
self._skipped_prefixes = tuple(
|
|
29
|
+
prefix.rstrip("/") for prefix in (static_url, media_url) if prefix
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def __call__(self, request):
|
|
33
|
+
cache_original_referer(request)
|
|
34
|
+
should_track = self._should_track(request)
|
|
35
|
+
if not should_track:
|
|
36
|
+
return self.get_response(request)
|
|
37
|
+
|
|
38
|
+
error_message = ""
|
|
39
|
+
try:
|
|
40
|
+
response = self.get_response(request)
|
|
41
|
+
except Exception as exc: # pragma: no cover - re-raised for Django
|
|
42
|
+
status_code = getattr(exc, "status_code", 500) or 500
|
|
43
|
+
error_message = str(exc)
|
|
44
|
+
self._record_visit(request, status_code, error_message)
|
|
45
|
+
raise
|
|
46
|
+
else:
|
|
47
|
+
status_code = getattr(response, "status_code", 0) or 0
|
|
48
|
+
self._record_visit(request, status_code, error_message)
|
|
49
|
+
return response
|
|
50
|
+
|
|
51
|
+
def _should_track(self, request) -> bool:
|
|
52
|
+
method = request.method.upper()
|
|
53
|
+
if method not in {"GET", "HEAD"}:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
path = request.path
|
|
57
|
+
if any(path.startswith(prefix) for prefix in self._EXCLUDED_PREFIXES):
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
if any(path.startswith(prefix) for prefix in self._skipped_prefixes):
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
if path.startswith("/favicon") or path.startswith("/robots.txt"):
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
if "djdt" in request.GET:
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def _record_visit(self, request, status_code: int, error_message: str) -> None:
|
|
72
|
+
try:
|
|
73
|
+
status = HTTPStatus(status_code)
|
|
74
|
+
status_text = status.phrase
|
|
75
|
+
except ValueError:
|
|
76
|
+
status_text = ""
|
|
77
|
+
|
|
78
|
+
view_name = self._resolve_view_name(request)
|
|
79
|
+
full_path = request.get_full_path()
|
|
80
|
+
if not error_message and status_code >= HTTPStatus.BAD_REQUEST:
|
|
81
|
+
error_message = status_text or f"HTTP {status_code}"
|
|
82
|
+
|
|
83
|
+
landing = None
|
|
84
|
+
if status_code < HTTPStatus.BAD_REQUEST:
|
|
85
|
+
landing = self._resolve_landing(request)
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
ViewHistory.objects.create(
|
|
89
|
+
path=full_path,
|
|
90
|
+
method=request.method,
|
|
91
|
+
status_code=status_code,
|
|
92
|
+
status_text=status_text,
|
|
93
|
+
error_message=(error_message or "")[:1000],
|
|
94
|
+
view_name=view_name,
|
|
95
|
+
)
|
|
96
|
+
except Exception: # pragma: no cover - best effort logging
|
|
97
|
+
logger.debug(
|
|
98
|
+
"Failed to record ViewHistory for %s", full_path, exc_info=True
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
self._update_user_last_visit_ip(request)
|
|
102
|
+
|
|
103
|
+
if landing is not None:
|
|
104
|
+
self._record_landing_lead(request, landing)
|
|
105
|
+
|
|
106
|
+
def _resolve_landing(self, request):
|
|
107
|
+
path = request.path
|
|
108
|
+
if not path:
|
|
109
|
+
return None
|
|
110
|
+
try:
|
|
111
|
+
return (
|
|
112
|
+
Landing.objects.filter(
|
|
113
|
+
path=path,
|
|
114
|
+
enabled=True,
|
|
115
|
+
is_deleted=False,
|
|
116
|
+
)
|
|
117
|
+
.select_related("module", "module__application", "module__node_role")
|
|
118
|
+
.first()
|
|
119
|
+
)
|
|
120
|
+
except Exception: # pragma: no cover - best effort logging
|
|
121
|
+
logger.debug(
|
|
122
|
+
"Failed to resolve Landing for %s", path, exc_info=True
|
|
123
|
+
)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
def _record_landing_lead(self, request, landing):
|
|
127
|
+
if request.method.upper() != "GET":
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
if not getattr(landing, "track_leads", False):
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
if not landing_leads_supported():
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
referer = get_original_referer(request)
|
|
137
|
+
user_agent = request.META.get("HTTP_USER_AGENT", "") or ""
|
|
138
|
+
ip_address = self._extract_client_ip(request) or None
|
|
139
|
+
user = getattr(request, "user", None)
|
|
140
|
+
if not getattr(user, "is_authenticated", False):
|
|
141
|
+
user = None
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
LandingLead.objects.create(
|
|
145
|
+
landing=landing,
|
|
146
|
+
user=user,
|
|
147
|
+
path=request.get_full_path(),
|
|
148
|
+
referer=referer,
|
|
149
|
+
user_agent=user_agent,
|
|
150
|
+
ip_address=ip_address,
|
|
151
|
+
)
|
|
152
|
+
except Exception: # pragma: no cover - best effort logging
|
|
153
|
+
logger.debug(
|
|
154
|
+
"Failed to record LandingLead for %s", landing.path, exc_info=True
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def _resolve_view_name(self, request) -> str:
|
|
158
|
+
match = getattr(request, "resolver_match", None)
|
|
159
|
+
if match is None:
|
|
160
|
+
try:
|
|
161
|
+
match = resolve(request.path_info)
|
|
162
|
+
except Resolver404:
|
|
163
|
+
return ""
|
|
164
|
+
|
|
165
|
+
if getattr(match, "view_name", ""):
|
|
166
|
+
return match.view_name
|
|
167
|
+
|
|
168
|
+
func = getattr(match, "func", None)
|
|
169
|
+
if func is None:
|
|
170
|
+
return ""
|
|
171
|
+
|
|
172
|
+
module = getattr(func, "__module__", "")
|
|
173
|
+
name = getattr(func, "__name__", "")
|
|
174
|
+
if module and name:
|
|
175
|
+
return f"{module}.{name}"
|
|
176
|
+
return name or module or ""
|
|
177
|
+
|
|
178
|
+
def _extract_client_ip(self, request) -> str:
|
|
179
|
+
forwarded = request.META.get("HTTP_X_FORWARDED_FOR", "")
|
|
180
|
+
candidates = []
|
|
181
|
+
if forwarded:
|
|
182
|
+
candidates.extend(part.strip() for part in forwarded.split(","))
|
|
183
|
+
remote = request.META.get("REMOTE_ADDR", "").strip()
|
|
184
|
+
if remote:
|
|
185
|
+
candidates.append(remote)
|
|
186
|
+
|
|
187
|
+
for candidate in candidates:
|
|
188
|
+
if not candidate:
|
|
189
|
+
continue
|
|
190
|
+
try:
|
|
191
|
+
ipaddress.ip_address(candidate)
|
|
192
|
+
except ValueError:
|
|
193
|
+
continue
|
|
194
|
+
return candidate
|
|
195
|
+
return ""
|
|
196
|
+
|
|
197
|
+
def _update_user_last_visit_ip(self, request) -> None:
|
|
198
|
+
user = getattr(request, "user", None)
|
|
199
|
+
if not getattr(user, "is_authenticated", False) or not getattr(user, "pk", None):
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
ip_address = self._extract_client_ip(request)
|
|
203
|
+
if not ip_address or getattr(user, "last_visit_ip_address", None) == ip_address:
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
user.last_visit_ip_address = ip_address
|
|
208
|
+
user.save(update_fields=["last_visit_ip_address"])
|
|
209
|
+
except Exception: # pragma: no cover - best effort logging
|
|
210
|
+
logger.debug(
|
|
211
|
+
"Failed to update last_visit_ip_address for user %s", user.pk,
|
|
212
|
+
exc_info=True,
|
|
213
|
+
)
|