arthexis 0.1.3__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 (73) hide show
  1. arthexis-0.1.3.dist-info/METADATA +126 -0
  2. arthexis-0.1.3.dist-info/RECORD +73 -0
  3. arthexis-0.1.3.dist-info/WHEEL +5 -0
  4. arthexis-0.1.3.dist-info/licenses/LICENSE +21 -0
  5. arthexis-0.1.3.dist-info/top_level.txt +5 -0
  6. config/__init__.py +6 -0
  7. config/active_app.py +15 -0
  8. config/asgi.py +29 -0
  9. config/auth_app.py +8 -0
  10. config/celery.py +19 -0
  11. config/context_processors.py +68 -0
  12. config/loadenv.py +11 -0
  13. config/logging.py +43 -0
  14. config/middleware.py +25 -0
  15. config/offline.py +47 -0
  16. config/settings.py +374 -0
  17. config/urls.py +91 -0
  18. config/wsgi.py +17 -0
  19. core/__init__.py +0 -0
  20. core/admin.py +830 -0
  21. core/apps.py +67 -0
  22. core/backends.py +82 -0
  23. core/entity.py +97 -0
  24. core/environment.py +43 -0
  25. core/fields.py +70 -0
  26. core/lcd_screen.py +77 -0
  27. core/middleware.py +34 -0
  28. core/models.py +1277 -0
  29. core/notifications.py +95 -0
  30. core/release.py +451 -0
  31. core/system.py +111 -0
  32. core/tasks.py +100 -0
  33. core/tests.py +483 -0
  34. core/urls.py +11 -0
  35. core/user_data.py +333 -0
  36. core/views.py +431 -0
  37. nodes/__init__.py +0 -0
  38. nodes/actions.py +72 -0
  39. nodes/admin.py +347 -0
  40. nodes/apps.py +76 -0
  41. nodes/lcd.py +151 -0
  42. nodes/models.py +577 -0
  43. nodes/tasks.py +50 -0
  44. nodes/tests.py +1072 -0
  45. nodes/urls.py +13 -0
  46. nodes/utils.py +62 -0
  47. nodes/views.py +262 -0
  48. ocpp/__init__.py +0 -0
  49. ocpp/admin.py +392 -0
  50. ocpp/apps.py +24 -0
  51. ocpp/consumers.py +267 -0
  52. ocpp/evcs.py +911 -0
  53. ocpp/models.py +300 -0
  54. ocpp/routing.py +9 -0
  55. ocpp/simulator.py +357 -0
  56. ocpp/store.py +175 -0
  57. ocpp/tasks.py +27 -0
  58. ocpp/test_export_import.py +129 -0
  59. ocpp/test_rfid.py +345 -0
  60. ocpp/tests.py +1229 -0
  61. ocpp/transactions_io.py +119 -0
  62. ocpp/urls.py +17 -0
  63. ocpp/views.py +359 -0
  64. pages/__init__.py +0 -0
  65. pages/admin.py +231 -0
  66. pages/apps.py +10 -0
  67. pages/checks.py +41 -0
  68. pages/context_processors.py +72 -0
  69. pages/models.py +224 -0
  70. pages/tests.py +628 -0
  71. pages/urls.py +17 -0
  72. pages/utils.py +13 -0
  73. pages/views.py +191 -0
config/settings.py ADDED
@@ -0,0 +1,374 @@
1
+ """
2
+ Django settings for config project.
3
+
4
+ Generated by 'django-admin startproject' using Django 5.2.4.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/5.2/topics/settings/
8
+
9
+ For the full list of settings and their values, see
10
+ https://docs.djangoproject.com/en/5.2/ref/settings/
11
+ """
12
+
13
+ from pathlib import Path
14
+ import contextlib
15
+ import os
16
+ import sys
17
+ import ipaddress
18
+ import socket
19
+ from django.utils.translation import gettext_lazy as _
20
+ from celery.schedules import crontab
21
+ from django.http import request as http_request
22
+ from django.middleware.csrf import CsrfViewMiddleware
23
+ from django.core.exceptions import DisallowedHost
24
+ from urllib.parse import urlsplit
25
+ import django.utils.encoding as encoding
26
+
27
+ if not hasattr(encoding, "force_text"): # pragma: no cover - Django>=5 compatibility
28
+ from django.utils.encoding import force_str
29
+
30
+ encoding.force_text = force_str
31
+
32
+
33
+ _original_validate_host = http_request.validate_host
34
+
35
+
36
+ def _validate_host_with_subnets(host, allowed_hosts):
37
+ try:
38
+ ip = ipaddress.ip_address(host)
39
+ except ValueError:
40
+ return _original_validate_host(host, allowed_hosts)
41
+ for pattern in allowed_hosts:
42
+ try:
43
+ network = ipaddress.ip_network(pattern)
44
+ except ValueError:
45
+ continue
46
+ if ip in network:
47
+ return True
48
+ return _original_validate_host(host, allowed_hosts)
49
+
50
+
51
+ http_request.validate_host = _validate_host_with_subnets
52
+
53
+ # Build paths inside the project like this: BASE_DIR / 'subdir'.
54
+ BASE_DIR = Path(__file__).resolve().parent.parent
55
+
56
+ ACRONYMS: list[str] = []
57
+ with contextlib.suppress(FileNotFoundError):
58
+ ACRONYMS = [
59
+ line.strip()
60
+ for line in (BASE_DIR / "config" / "data" / "ACRONYMS.txt").read_text().splitlines()
61
+ if line.strip()
62
+ ]
63
+
64
+
65
+ # Quick-start development settings - unsuitable for production
66
+ # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
67
+
68
+ # SECURITY WARNING: keep the secret key used in production secret!
69
+ SECRET_KEY = "django-insecure-16%ed9hv^gg!jj5ff6d4w=t$50k^abkq75+vwl44%^+qyq!m#w"
70
+
71
+ # SECURITY WARNING: don't run with debug turned on in production!
72
+
73
+ # Enable DEBUG and related tooling when running in Terminal mode.
74
+ NODE_ROLE = os.environ.get("NODE_ROLE")
75
+ if NODE_ROLE is None:
76
+ role_lock = BASE_DIR / "locks" / "role.lck"
77
+ NODE_ROLE = role_lock.read_text().strip() if role_lock.exists() else "Terminal"
78
+
79
+ DEBUG = NODE_ROLE == "Terminal"
80
+
81
+ ALLOWED_HOSTS = [
82
+ "localhost",
83
+ "127.0.0.1",
84
+ "testserver",
85
+ "10.42.0.0/16",
86
+ "192.168.0.0/16",
87
+ "arthexis.com",
88
+ ]
89
+
90
+
91
+ # Allow CSRF origin verification for hosts within allowed subnets.
92
+ _original_origin_verified = CsrfViewMiddleware._origin_verified
93
+
94
+
95
+ def _origin_verified_with_subnets(self, request):
96
+ request_origin = request.META["HTTP_ORIGIN"]
97
+ try:
98
+ good_host = request.get_host()
99
+ except DisallowedHost:
100
+ pass
101
+ else:
102
+ good_origin = "%s://%s" % (
103
+ "https" if request.is_secure() else "http",
104
+ good_host,
105
+ )
106
+ if request_origin == good_origin:
107
+ return True
108
+ try:
109
+ origin_host = urlsplit(request_origin).hostname
110
+ origin_ip = ipaddress.ip_address(origin_host)
111
+ request_ip = ipaddress.ip_address(good_host.split(":")[0])
112
+ except ValueError:
113
+ pass
114
+ else:
115
+ for pattern in ALLOWED_HOSTS:
116
+ try:
117
+ network = ipaddress.ip_network(pattern)
118
+ except ValueError:
119
+ continue
120
+ if origin_ip in network and request_ip in network:
121
+ return True
122
+ return _original_origin_verified(self, request)
123
+
124
+
125
+ CsrfViewMiddleware._origin_verified = _origin_verified_with_subnets
126
+
127
+
128
+ # Application definition
129
+
130
+ LOCAL_APPS = [
131
+ "nodes",
132
+ "core",
133
+ "ocpp",
134
+ "awg",
135
+ "pages",
136
+ "app",
137
+ ]
138
+
139
+ INSTALLED_APPS = [
140
+ "django.contrib.admin",
141
+ "django.contrib.admindocs",
142
+ "config.auth_app.AuthConfig",
143
+ "django.contrib.contenttypes",
144
+ "django.contrib.sessions",
145
+ "django.contrib.messages",
146
+ "django.contrib.staticfiles",
147
+ "import_export",
148
+ "django_object_actions",
149
+ "django.contrib.sites",
150
+ "channels",
151
+ "post_office",
152
+ "django_celery_beat",
153
+ ] + LOCAL_APPS
154
+
155
+ if DEBUG:
156
+ try:
157
+ import debug_toolbar # type: ignore
158
+ except ModuleNotFoundError: # pragma: no cover - optional dependency
159
+ pass
160
+ else:
161
+ INSTALLED_APPS += ["debug_toolbar"]
162
+
163
+ SITE_ID = 1
164
+
165
+ MIDDLEWARE = [
166
+ "django.middleware.security.SecurityMiddleware",
167
+ "django.contrib.sessions.middleware.SessionMiddleware",
168
+ "config.middleware.ActiveAppMiddleware",
169
+ "django.middleware.locale.LocaleMiddleware",
170
+ "django.middleware.common.CommonMiddleware",
171
+ "django.middleware.csrf.CsrfViewMiddleware",
172
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
173
+ "core.middleware.AdminHistoryMiddleware",
174
+ "django.contrib.messages.middleware.MessageMiddleware",
175
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
176
+ ]
177
+
178
+ if DEBUG:
179
+ try:
180
+ import debug_toolbar # type: ignore
181
+ except ModuleNotFoundError: # pragma: no cover - optional dependency
182
+ pass
183
+ else:
184
+ MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
185
+ INTERNAL_IPS = ["127.0.0.1", "localhost"]
186
+
187
+ CSRF_FAILURE_VIEW = "pages.views.csrf_failure"
188
+
189
+ ROOT_URLCONF = "config.urls"
190
+
191
+ TEMPLATES = [
192
+ {
193
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
194
+ "DIRS": [BASE_DIR / "pages" / "templates"],
195
+ "APP_DIRS": True,
196
+ "OPTIONS": {
197
+ "context_processors": [
198
+ "django.template.context_processors.request",
199
+ "django.contrib.auth.context_processors.auth",
200
+ "django.template.context_processors.i18n",
201
+ "django.contrib.messages.context_processors.messages",
202
+ "pages.context_processors.nav_links",
203
+ "config.context_processors.site_and_node",
204
+ ],
205
+ },
206
+ },
207
+ ]
208
+
209
+ WSGI_APPLICATION = "config.wsgi.application"
210
+ ASGI_APPLICATION = "config.asgi.application"
211
+
212
+ # Channels configuration
213
+ CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
214
+
215
+
216
+ # Custom user model
217
+ AUTH_USER_MODEL = "core.User"
218
+
219
+ # Enable RFID authentication backend and restrict default admin login to localhost
220
+ AUTHENTICATION_BACKENDS = [
221
+ "core.backends.LocalhostAdminBackend",
222
+ "core.backends.RFIDBackend",
223
+ ]
224
+
225
+
226
+ # Database
227
+ # https://docs.djangoproject.com/en/5.2/ref/settings/#databases
228
+
229
+
230
+ def _postgres_available() -> bool:
231
+ try:
232
+ import psycopg
233
+ except Exception:
234
+ return False
235
+
236
+ params = {
237
+ "dbname": os.environ.get("POSTGRES_DB", "postgres"),
238
+ "user": os.environ.get("POSTGRES_USER", "postgres"),
239
+ "password": os.environ.get("POSTGRES_PASSWORD", ""),
240
+ "host": os.environ.get("POSTGRES_HOST", "localhost"),
241
+ "port": os.environ.get("POSTGRES_PORT", "5432"),
242
+ "connect_timeout": 1,
243
+ }
244
+ try:
245
+ with contextlib.closing(psycopg.connect(**params)):
246
+ return True
247
+ except psycopg.OperationalError:
248
+ return False
249
+
250
+
251
+ if _postgres_available():
252
+ DATABASES = {
253
+ "default": {
254
+ "ENGINE": "django.db.backends.postgresql",
255
+ "NAME": os.environ.get("POSTGRES_DB", "postgres"),
256
+ "USER": os.environ.get("POSTGRES_USER", "postgres"),
257
+ "PASSWORD": os.environ.get("POSTGRES_PASSWORD", ""),
258
+ "HOST": os.environ.get("POSTGRES_HOST", "localhost"),
259
+ "PORT": os.environ.get("POSTGRES_PORT", "5432"),
260
+ "OPTIONS": {"options": "-c timezone=UTC"},
261
+ }
262
+ }
263
+ else:
264
+ DATABASES = {
265
+ "default": {
266
+ "ENGINE": "django.db.backends.sqlite3",
267
+ "NAME": BASE_DIR / "db.sqlite3",
268
+ "OPTIONS": {"timeout": 30},
269
+ }
270
+ }
271
+
272
+
273
+ # Password validation
274
+ # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
275
+
276
+ AUTH_PASSWORD_VALIDATORS = [
277
+ {
278
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
279
+ },
280
+ {
281
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
282
+ },
283
+ {
284
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
285
+ },
286
+ {
287
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
288
+ },
289
+ ]
290
+
291
+
292
+ # Internationalization
293
+ # https://docs.djangoproject.com/en/5.2/topics/i18n/
294
+
295
+ LANGUAGE_CODE = "en-us"
296
+
297
+ LANGUAGES = [
298
+ ("en", _("English")),
299
+ ("es", _("Spanish")),
300
+ ]
301
+
302
+ LOCALE_PATHS = [BASE_DIR / "locale"]
303
+
304
+ TIME_ZONE = "America/Monterrey"
305
+
306
+ USE_I18N = True
307
+
308
+ USE_TZ = True
309
+
310
+
311
+ # Static files (CSS, JavaScript, Images)
312
+ # https://docs.djangoproject.com/en/5.2/howto/static-files/
313
+
314
+ STATIC_URL = "/static/"
315
+ MEDIA_URL = "/media/"
316
+ MEDIA_ROOT = BASE_DIR / "media"
317
+
318
+ # Email settings
319
+ DEFAULT_FROM_EMAIL = "arthexis@gmail.com"
320
+ SERVER_EMAIL = DEFAULT_FROM_EMAIL
321
+
322
+ # Default primary key field type
323
+ # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
324
+
325
+ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
326
+
327
+ # Bluesky domain account (optional)
328
+ BSKY_HANDLE = os.environ.get("BSKY_HANDLE")
329
+ BSKY_APP_PASSWORD = os.environ.get("BSKY_APP_PASSWORD")
330
+
331
+ # Logging configuration
332
+ LOG_DIR = BASE_DIR / "logs"
333
+ LOG_DIR.mkdir(exist_ok=True)
334
+ OLD_LOG_DIR = LOG_DIR / "old"
335
+ OLD_LOG_DIR.mkdir(exist_ok=True)
336
+ LOG_FILE_NAME = "tests.log" if "test" in sys.argv else f"{socket.gethostname()}.log"
337
+
338
+ LOGGING = {
339
+ "version": 1,
340
+ "disable_existing_loggers": False,
341
+ "formatters": {
342
+ "standard": {
343
+ "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
344
+ }
345
+ },
346
+ "handlers": {
347
+ "file": {
348
+ "class": "config.logging.ActiveAppFileHandler",
349
+ "filename": str(LOG_DIR / LOG_FILE_NAME),
350
+ "when": "midnight",
351
+ "backupCount": 7,
352
+ "encoding": "utf-8",
353
+ "formatter": "standard",
354
+ }
355
+ },
356
+ "root": {
357
+ "handlers": ["file"],
358
+ "level": "DEBUG",
359
+ },
360
+ }
361
+
362
+
363
+ # Celery configuration
364
+ CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL", "memory://")
365
+ CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND", "cache+memory://")
366
+ CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
367
+
368
+ CELERY_BEAT_SCHEDULE = {
369
+ "heartbeat": {
370
+ "task": "app.tasks.heartbeat",
371
+ "schedule": crontab(minute="*/5"),
372
+ }
373
+ }
374
+
config/urls.py ADDED
@@ -0,0 +1,91 @@
1
+ """Project URL configuration with automatic app discovery.
2
+
3
+ This module includes URL patterns from any installed application that exposes
4
+ an internal ``urls`` module. This allows new apps with URL configurations to be
5
+ added without editing this file, except for top-level routes such as the admin
6
+ interface or the main pages.
7
+ """
8
+
9
+ from importlib import import_module
10
+ from pathlib import Path
11
+
12
+ from django.apps import apps
13
+ from django.conf import settings
14
+ from django.conf.urls.static import static
15
+ from django.contrib import admin
16
+ from django.urls import include, path
17
+ from django.views.decorators.csrf import csrf_exempt
18
+ from django.views.i18n import set_language
19
+ from django.utils.translation import gettext_lazy as _
20
+ from core import views as core_views
21
+
22
+ admin.site.site_header = _("Constellation")
23
+ admin.site.site_title = _("Constellation")
24
+
25
+ # Apps that require a custom prefix for their URLs
26
+ URL_PREFIX_OVERRIDES = {"core": "api/rfid"}
27
+
28
+
29
+ def autodiscovered_urlpatterns():
30
+ """Collect URL patterns from project apps automatically.
31
+
32
+ Scans all installed apps located inside the project directory. If an app
33
+ exposes a ``urls`` module, it is included under ``/<app_label>/`` unless a
34
+ custom prefix is defined in :data:`URL_PREFIX_OVERRIDES`.
35
+ """
36
+
37
+ patterns = []
38
+ base_dir = Path(settings.BASE_DIR).resolve()
39
+ for app_config in apps.get_app_configs():
40
+ app_path = Path(app_config.path).resolve()
41
+ try:
42
+ app_path.relative_to(base_dir)
43
+ except ValueError:
44
+ # Skip third-party apps outside of the project
45
+ continue
46
+
47
+ if app_config.label == "pages":
48
+ # Root pages URLs are handled explicitly below
49
+ continue
50
+
51
+ module_name = f"{app_config.name}.urls"
52
+ try:
53
+ import_module(module_name)
54
+ except ModuleNotFoundError:
55
+ continue
56
+
57
+ prefix = URL_PREFIX_OVERRIDES.get(app_config.label, app_config.label)
58
+ patterns.append(path(f"{prefix}/", include(module_name)))
59
+
60
+ return patterns
61
+
62
+
63
+ urlpatterns = [
64
+ path("admin/doc/", include("django.contrib.admindocs.urls")),
65
+ path(
66
+ "admin/core/releases/<int:pk>/<str:action>/",
67
+ core_views.release_progress,
68
+ name="release-progress",
69
+ ),
70
+ path("admin/", admin.site.urls),
71
+ path("i18n/setlang/", csrf_exempt(set_language), name="set_language"),
72
+ path("", include("pages.urls")),
73
+ ]
74
+
75
+ urlpatterns += autodiscovered_urlpatterns()
76
+
77
+ if settings.DEBUG:
78
+ try:
79
+ import debug_toolbar
80
+ except ModuleNotFoundError: # pragma: no cover - optional dependency
81
+ pass
82
+ else:
83
+ urlpatterns = [
84
+ path(
85
+ "__debug__/",
86
+ include(("debug_toolbar.urls", "debug_toolbar"), namespace="debug_toolbar"),
87
+ )
88
+ ] + urlpatterns
89
+
90
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
91
+
config/wsgi.py ADDED
@@ -0,0 +1,17 @@
1
+ """
2
+ WSGI config for config project.
3
+
4
+ It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/
8
+ """
9
+
10
+ import os
11
+ from config.loadenv import loadenv
12
+ from django.core.wsgi import get_wsgi_application
13
+
14
+ loadenv()
15
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
16
+
17
+ application = get_wsgi_application()
core/__init__.py ADDED
File without changes