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
core/notifications.py
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
"""Simple notification helper for a 16x2 LCD display.
|
|
2
|
-
|
|
3
|
-
Messages are written to a lock file read by an independent service that
|
|
4
|
-
updates the LCD. If writing to the lock file fails, a Windows
|
|
5
|
-
notification or log entry is used as a fallback. Each line is truncated
|
|
6
|
-
to 64 characters; scrolling is handled by the LCD service.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import logging
|
|
12
|
-
import sys
|
|
13
|
-
import threading
|
|
14
|
-
from pathlib import Path
|
|
15
|
-
|
|
16
|
-
try: # pragma: no cover - optional dependency
|
|
17
|
-
from plyer import notification as plyer_notification
|
|
18
|
-
except Exception: # pragma: no cover - plyer may not be installed
|
|
19
|
-
plyer_notification = None
|
|
20
|
-
|
|
21
|
-
logger = logging.getLogger(__name__)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def supports_gui_toast() -> bool:
|
|
25
|
-
"""Return ``True`` when a GUI toast notification is available."""
|
|
26
|
-
|
|
27
|
-
if not sys.platform.startswith("win"):
|
|
28
|
-
return False
|
|
29
|
-
notify = getattr(plyer_notification, "notify", None)
|
|
30
|
-
return callable(notify)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class NotificationManager:
|
|
34
|
-
"""Write notifications to a lock file or fall back to GUI/log output."""
|
|
35
|
-
|
|
36
|
-
def __init__(self, lock_file: Path | None = None) -> None:
|
|
37
|
-
base_dir = Path(__file__).resolve().parents[1]
|
|
38
|
-
self.lock_file = lock_file or base_dir / "locks" / "lcd_screen.lck"
|
|
39
|
-
self.lock_file.parent.mkdir(parents=True, exist_ok=True)
|
|
40
|
-
# ``plyer`` is only available on Windows and can fail when used in
|
|
41
|
-
# a non-interactive environment (e.g. service or CI).
|
|
42
|
-
# Any failure will
|
|
43
|
-
|
|
44
|
-
def _write_lock_file(self, subject: str, body: str) -> None:
|
|
45
|
-
self.lock_file.write_text(f"{subject}\n{body}\n", encoding="utf-8")
|
|
46
|
-
|
|
47
|
-
def send(self, subject: str, body: str = "") -> bool:
|
|
48
|
-
"""Store *subject* and *body* in ``lcd_screen.lck`` when available.
|
|
49
|
-
|
|
50
|
-
The method truncates each line to 64 characters. If the lock file is
|
|
51
|
-
missing or writing fails, a GUI/log notification is used instead. In
|
|
52
|
-
either case the function returns ``True`` so callers do not keep
|
|
53
|
-
retrying in a loop when only the fallback is available.
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
if self.lock_file.exists():
|
|
57
|
-
try:
|
|
58
|
-
self._write_lock_file(subject[:64], body[:64])
|
|
59
|
-
return True
|
|
60
|
-
except Exception as exc: # pragma: no cover - filesystem dependent
|
|
61
|
-
logger.warning("LCD lock file write failed: %s", exc)
|
|
62
|
-
else:
|
|
63
|
-
logger.debug("LCD lock file missing; using fallback notification")
|
|
64
|
-
self._gui_display(subject, body)
|
|
65
|
-
return True
|
|
66
|
-
|
|
67
|
-
def send_async(self, subject: str, body: str = "") -> None:
|
|
68
|
-
"""Dispatch :meth:`send` on a background thread."""
|
|
69
|
-
|
|
70
|
-
def _send() -> None:
|
|
71
|
-
try:
|
|
72
|
-
self.send(subject, body)
|
|
73
|
-
except Exception:
|
|
74
|
-
# Notification failures shouldn't affect callers.
|
|
75
|
-
pass
|
|
76
|
-
|
|
77
|
-
threading.Thread(target=_send, daemon=True).start()
|
|
78
|
-
|
|
79
|
-
# GUI/log fallback ------------------------------------------------
|
|
80
|
-
def _gui_display(self, subject: str, body: str) -> None:
|
|
81
|
-
if supports_gui_toast():
|
|
82
|
-
try: # pragma: no cover - depends on platform
|
|
83
|
-
plyer_notification.notify(
|
|
84
|
-
title="Arthexis", message=f"{subject}\n{body}", timeout=6
|
|
85
|
-
)
|
|
86
|
-
return
|
|
87
|
-
except Exception as exc: # pragma: no cover - depends on platform
|
|
88
|
-
logger.warning("Windows notification failed: %s", exc)
|
|
89
|
-
logger.info("%s %s", subject, body)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
# Global manager used throughout the project
|
|
93
|
-
manager = NotificationManager()
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def notify(subject: str, body: str = "") -> bool:
|
|
97
|
-
"""Convenience wrapper using the global :class:`NotificationManager`."""
|
|
98
|
-
|
|
99
|
-
return manager.send(subject=subject, body=body)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def notify_async(subject: str, body: str = "") -> None:
|
|
103
|
-
"""Run :func:`notify` without blocking the caller."""
|
|
104
|
-
|
|
105
|
-
manager.send_async(subject=subject, body=body)
|
|
1
|
+
"""Simple notification helper for a 16x2 LCD display.
|
|
2
|
+
|
|
3
|
+
Messages are written to a lock file read by an independent service that
|
|
4
|
+
updates the LCD. If writing to the lock file fails, a Windows
|
|
5
|
+
notification or log entry is used as a fallback. Each line is truncated
|
|
6
|
+
to 64 characters; scrolling is handled by the LCD service.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import sys
|
|
13
|
+
import threading
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
try: # pragma: no cover - optional dependency
|
|
17
|
+
from plyer import notification as plyer_notification
|
|
18
|
+
except Exception: # pragma: no cover - plyer may not be installed
|
|
19
|
+
plyer_notification = None
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def supports_gui_toast() -> bool:
|
|
25
|
+
"""Return ``True`` when a GUI toast notification is available."""
|
|
26
|
+
|
|
27
|
+
if not sys.platform.startswith("win"):
|
|
28
|
+
return False
|
|
29
|
+
notify = getattr(plyer_notification, "notify", None)
|
|
30
|
+
return callable(notify)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NotificationManager:
|
|
34
|
+
"""Write notifications to a lock file or fall back to GUI/log output."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, lock_file: Path | None = None) -> None:
|
|
37
|
+
base_dir = Path(__file__).resolve().parents[1]
|
|
38
|
+
self.lock_file = lock_file or base_dir / "locks" / "lcd_screen.lck"
|
|
39
|
+
self.lock_file.parent.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
# ``plyer`` is only available on Windows and can fail when used in
|
|
41
|
+
# a non-interactive environment (e.g. service or CI).
|
|
42
|
+
# Any failure will fall back to logging quietly.
|
|
43
|
+
|
|
44
|
+
def _write_lock_file(self, subject: str, body: str) -> None:
|
|
45
|
+
self.lock_file.write_text(f"{subject}\n{body}\n", encoding="utf-8")
|
|
46
|
+
|
|
47
|
+
def send(self, subject: str, body: str = "") -> bool:
|
|
48
|
+
"""Store *subject* and *body* in ``lcd_screen.lck`` when available.
|
|
49
|
+
|
|
50
|
+
The method truncates each line to 64 characters. If the lock file is
|
|
51
|
+
missing or writing fails, a GUI/log notification is used instead. In
|
|
52
|
+
either case the function returns ``True`` so callers do not keep
|
|
53
|
+
retrying in a loop when only the fallback is available.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
if self.lock_file.exists():
|
|
57
|
+
try:
|
|
58
|
+
self._write_lock_file(subject[:64], body[:64])
|
|
59
|
+
return True
|
|
60
|
+
except Exception as exc: # pragma: no cover - filesystem dependent
|
|
61
|
+
logger.warning("LCD lock file write failed: %s", exc)
|
|
62
|
+
else:
|
|
63
|
+
logger.debug("LCD lock file missing; using fallback notification")
|
|
64
|
+
self._gui_display(subject, body)
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
def send_async(self, subject: str, body: str = "") -> None:
|
|
68
|
+
"""Dispatch :meth:`send` on a background thread."""
|
|
69
|
+
|
|
70
|
+
def _send() -> None:
|
|
71
|
+
try:
|
|
72
|
+
self.send(subject, body)
|
|
73
|
+
except Exception:
|
|
74
|
+
# Notification failures shouldn't affect callers.
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
threading.Thread(target=_send, daemon=True).start()
|
|
78
|
+
|
|
79
|
+
# GUI/log fallback ------------------------------------------------
|
|
80
|
+
def _gui_display(self, subject: str, body: str) -> None:
|
|
81
|
+
if supports_gui_toast():
|
|
82
|
+
try: # pragma: no cover - depends on platform
|
|
83
|
+
plyer_notification.notify(
|
|
84
|
+
title="Arthexis", message=f"{subject}\n{body}", timeout=6
|
|
85
|
+
)
|
|
86
|
+
return
|
|
87
|
+
except Exception as exc: # pragma: no cover - depends on platform
|
|
88
|
+
logger.warning("Windows notification failed: %s", exc)
|
|
89
|
+
logger.info("%s %s", subject, body)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Global manager used throughout the project
|
|
93
|
+
manager = NotificationManager()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def notify(subject: str, body: str = "") -> bool:
|
|
97
|
+
"""Convenience wrapper using the global :class:`NotificationManager`."""
|
|
98
|
+
|
|
99
|
+
return manager.send(subject=subject, body=body)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def notify_async(subject: str, body: str = "") -> None:
|
|
103
|
+
"""Run :func:`notify` without blocking the caller."""
|
|
104
|
+
|
|
105
|
+
manager.send_async(subject=subject, body=body)
|