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.

Files changed (112) hide show
  1. arthexis-0.1.26.dist-info/METADATA +272 -0
  2. arthexis-0.1.26.dist-info/RECORD +111 -0
  3. {arthexis-0.1.9.dist-info → arthexis-0.1.26.dist-info}/licenses/LICENSE +674 -674
  4. config/__init__.py +5 -5
  5. config/active_app.py +15 -15
  6. config/asgi.py +29 -29
  7. config/auth_app.py +7 -7
  8. config/celery.py +32 -25
  9. config/context_processors.py +67 -68
  10. config/horologia_app.py +7 -7
  11. config/loadenv.py +11 -11
  12. config/logging.py +59 -48
  13. config/middleware.py +71 -25
  14. config/offline.py +49 -49
  15. config/settings.py +676 -492
  16. config/settings_helpers.py +109 -0
  17. config/urls.py +228 -159
  18. config/wsgi.py +17 -17
  19. core/admin.py +4052 -2066
  20. core/admin_history.py +50 -50
  21. core/admindocs.py +192 -151
  22. core/apps.py +350 -223
  23. core/auto_upgrade.py +72 -0
  24. core/backends.py +311 -124
  25. core/changelog.py +403 -0
  26. core/entity.py +149 -133
  27. core/environment.py +60 -43
  28. core/fields.py +168 -75
  29. core/form_fields.py +75 -0
  30. core/github_helper.py +188 -25
  31. core/github_issues.py +183 -172
  32. core/github_repos.py +72 -0
  33. core/lcd_screen.py +78 -78
  34. core/liveupdate.py +25 -25
  35. core/log_paths.py +114 -100
  36. core/mailer.py +89 -83
  37. core/middleware.py +91 -91
  38. core/models.py +5041 -2195
  39. core/notifications.py +105 -105
  40. core/public_wifi.py +267 -227
  41. core/reference_utils.py +107 -0
  42. core/release.py +940 -346
  43. core/rfid_import_export.py +113 -0
  44. core/sigil_builder.py +149 -131
  45. core/sigil_context.py +20 -20
  46. core/sigil_resolver.py +250 -284
  47. core/system.py +1425 -230
  48. core/tasks.py +538 -199
  49. core/temp_passwords.py +181 -0
  50. core/test_system_info.py +202 -43
  51. core/tests.py +2673 -1069
  52. core/tests_liveupdate.py +17 -17
  53. core/urls.py +11 -11
  54. core/user_data.py +681 -495
  55. core/views.py +2484 -789
  56. core/widgets.py +213 -51
  57. nodes/admin.py +2236 -445
  58. nodes/apps.py +98 -70
  59. nodes/backends.py +160 -53
  60. nodes/dns.py +203 -0
  61. nodes/feature_checks.py +133 -0
  62. nodes/lcd.py +165 -165
  63. nodes/models.py +2375 -870
  64. nodes/reports.py +411 -0
  65. nodes/rfid_sync.py +210 -0
  66. nodes/signals.py +18 -0
  67. nodes/tasks.py +141 -46
  68. nodes/tests.py +5045 -1489
  69. nodes/urls.py +29 -13
  70. nodes/utils.py +172 -73
  71. nodes/views.py +1768 -304
  72. ocpp/admin.py +1775 -481
  73. ocpp/apps.py +25 -25
  74. ocpp/consumers.py +1843 -630
  75. ocpp/evcs.py +844 -928
  76. ocpp/evcs_discovery.py +158 -0
  77. ocpp/models.py +1417 -640
  78. ocpp/network.py +398 -0
  79. ocpp/reference_utils.py +42 -0
  80. ocpp/routing.py +11 -9
  81. ocpp/simulator.py +745 -368
  82. ocpp/status_display.py +26 -0
  83. ocpp/store.py +603 -403
  84. ocpp/tasks.py +479 -31
  85. ocpp/test_export_import.py +131 -130
  86. ocpp/test_rfid.py +1072 -540
  87. ocpp/tests.py +5494 -2296
  88. ocpp/transactions_io.py +197 -165
  89. ocpp/urls.py +50 -50
  90. ocpp/views.py +2024 -912
  91. pages/admin.py +1123 -396
  92. pages/apps.py +45 -10
  93. pages/checks.py +40 -40
  94. pages/context_processors.py +151 -85
  95. pages/defaults.py +13 -0
  96. pages/forms.py +221 -0
  97. pages/middleware.py +213 -153
  98. pages/models.py +720 -252
  99. pages/module_defaults.py +156 -0
  100. pages/site_config.py +137 -0
  101. pages/tasks.py +74 -0
  102. pages/tests.py +4009 -1389
  103. pages/urls.py +38 -20
  104. pages/utils.py +93 -12
  105. pages/views.py +1736 -762
  106. arthexis-0.1.9.dist-info/METADATA +0 -168
  107. arthexis-0.1.9.dist-info/RECORD +0 -92
  108. core/workgroup_urls.py +0 -17
  109. core/workgroup_views.py +0 -94
  110. nodes/actions.py +0 -70
  111. {arthexis-0.1.9.dist-info → arthexis-0.1.26.dist-info}/WHEEL +0 -0
  112. {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 fallback 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)
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)