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.
- arthexis-0.1.3.dist-info/METADATA +126 -0
- arthexis-0.1.3.dist-info/RECORD +73 -0
- arthexis-0.1.3.dist-info/WHEEL +5 -0
- arthexis-0.1.3.dist-info/licenses/LICENSE +21 -0
- arthexis-0.1.3.dist-info/top_level.txt +5 -0
- config/__init__.py +6 -0
- config/active_app.py +15 -0
- config/asgi.py +29 -0
- config/auth_app.py +8 -0
- config/celery.py +19 -0
- config/context_processors.py +68 -0
- config/loadenv.py +11 -0
- config/logging.py +43 -0
- config/middleware.py +25 -0
- config/offline.py +47 -0
- config/settings.py +374 -0
- config/urls.py +91 -0
- config/wsgi.py +17 -0
- core/__init__.py +0 -0
- core/admin.py +830 -0
- core/apps.py +67 -0
- core/backends.py +82 -0
- core/entity.py +97 -0
- core/environment.py +43 -0
- core/fields.py +70 -0
- core/lcd_screen.py +77 -0
- core/middleware.py +34 -0
- core/models.py +1277 -0
- core/notifications.py +95 -0
- core/release.py +451 -0
- core/system.py +111 -0
- core/tasks.py +100 -0
- core/tests.py +483 -0
- core/urls.py +11 -0
- core/user_data.py +333 -0
- core/views.py +431 -0
- nodes/__init__.py +0 -0
- nodes/actions.py +72 -0
- nodes/admin.py +347 -0
- nodes/apps.py +76 -0
- nodes/lcd.py +151 -0
- nodes/models.py +577 -0
- nodes/tasks.py +50 -0
- nodes/tests.py +1072 -0
- nodes/urls.py +13 -0
- nodes/utils.py +62 -0
- nodes/views.py +262 -0
- ocpp/__init__.py +0 -0
- ocpp/admin.py +392 -0
- ocpp/apps.py +24 -0
- ocpp/consumers.py +267 -0
- ocpp/evcs.py +911 -0
- ocpp/models.py +300 -0
- ocpp/routing.py +9 -0
- ocpp/simulator.py +357 -0
- ocpp/store.py +175 -0
- ocpp/tasks.py +27 -0
- ocpp/test_export_import.py +129 -0
- ocpp/test_rfid.py +345 -0
- ocpp/tests.py +1229 -0
- ocpp/transactions_io.py +119 -0
- ocpp/urls.py +17 -0
- ocpp/views.py +359 -0
- pages/__init__.py +0 -0
- pages/admin.py +231 -0
- pages/apps.py +10 -0
- pages/checks.py +41 -0
- pages/context_processors.py +72 -0
- pages/models.py +224 -0
- pages/tests.py +628 -0
- pages/urls.py +17 -0
- pages/utils.py +13 -0
- 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
|