arthexis 0.1.8__py3-none-any.whl → 0.1.9__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.8.dist-info → arthexis-0.1.9.dist-info}/METADATA +42 -4
- arthexis-0.1.9.dist-info/RECORD +92 -0
- arthexis-0.1.9.dist-info/licenses/LICENSE +674 -0
- config/__init__.py +0 -1
- config/auth_app.py +0 -1
- config/celery.py +1 -2
- config/context_processors.py +1 -1
- config/offline.py +2 -0
- config/settings.py +133 -16
- config/urls.py +65 -6
- core/admin.py +1226 -191
- core/admin_history.py +50 -0
- core/admindocs.py +108 -1
- core/apps.py +158 -3
- core/backends.py +46 -4
- core/entity.py +62 -48
- core/fields.py +6 -1
- core/github_helper.py +25 -0
- core/github_issues.py +172 -0
- core/lcd_screen.py +1 -0
- core/liveupdate.py +25 -0
- core/log_paths.py +100 -0
- core/mailer.py +83 -0
- core/middleware.py +57 -0
- core/models.py +1071 -264
- core/notifications.py +11 -1
- core/public_wifi.py +227 -0
- core/release.py +27 -20
- core/sigil_builder.py +131 -0
- core/sigil_context.py +20 -0
- core/sigil_resolver.py +284 -0
- core/system.py +129 -10
- core/tasks.py +118 -19
- core/test_system_info.py +22 -0
- core/tests.py +358 -63
- core/tests_liveupdate.py +17 -0
- core/urls.py +2 -2
- core/user_data.py +329 -167
- core/views.py +383 -57
- core/widgets.py +51 -0
- core/workgroup_urls.py +7 -3
- core/workgroup_views.py +43 -6
- nodes/actions.py +0 -2
- nodes/admin.py +159 -284
- nodes/apps.py +9 -15
- nodes/backends.py +53 -0
- nodes/lcd.py +24 -10
- nodes/models.py +375 -178
- nodes/tasks.py +1 -5
- nodes/tests.py +524 -129
- nodes/utils.py +13 -2
- nodes/views.py +66 -23
- ocpp/admin.py +150 -61
- ocpp/apps.py +1 -1
- ocpp/consumers.py +432 -69
- ocpp/evcs.py +25 -8
- ocpp/models.py +408 -68
- ocpp/simulator.py +13 -6
- ocpp/store.py +258 -30
- ocpp/tasks.py +11 -7
- ocpp/test_export_import.py +8 -7
- ocpp/test_rfid.py +211 -16
- ocpp/tests.py +1198 -135
- ocpp/transactions_io.py +68 -22
- ocpp/urls.py +35 -2
- ocpp/views.py +654 -101
- pages/admin.py +173 -13
- pages/checks.py +0 -1
- pages/context_processors.py +19 -6
- pages/middleware.py +153 -0
- pages/models.py +37 -9
- pages/tests.py +759 -40
- pages/urls.py +3 -0
- pages/utils.py +0 -1
- pages/views.py +576 -25
- arthexis-0.1.8.dist-info/RECORD +0 -80
- arthexis-0.1.8.dist-info/licenses/LICENSE +0 -21
- config/workgroup_app.py +0 -7
- core/checks.py +0 -29
- {arthexis-0.1.8.dist-info → arthexis-0.1.9.dist-info}/WHEEL +0 -0
- {arthexis-0.1.8.dist-info → arthexis-0.1.9.dist-info}/top_level.txt +0 -0
config/auth_app.py
CHANGED
config/celery.py
CHANGED
|
@@ -9,7 +9,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
|
9
9
|
|
|
10
10
|
# When running on production-oriented nodes, avoid Celery debug mode.
|
|
11
11
|
NODE_ROLE = os.environ.get("NODE_ROLE", "")
|
|
12
|
-
if NODE_ROLE in {"Constellation", "Satellite"
|
|
12
|
+
if NODE_ROLE in {"Constellation", "Satellite"}:
|
|
13
13
|
for var in ["CELERY_TRACE_APP", "CELERY_DEBUG"]:
|
|
14
14
|
os.environ.pop(var, None)
|
|
15
15
|
os.environ.setdefault("CELERY_LOG_LEVEL", "INFO")
|
|
@@ -23,4 +23,3 @@ app.autodiscover_tasks()
|
|
|
23
23
|
def debug_task(self): # pragma: no cover - debug helper
|
|
24
24
|
"""A simple debug task."""
|
|
25
25
|
print(f"Request: {self.request!r}")
|
|
26
|
-
|
config/context_processors.py
CHANGED
|
@@ -13,7 +13,7 @@ def site_and_node(request: HttpRequest):
|
|
|
13
13
|
``badge_node`` is a ``Node`` instance or ``None`` if no match.
|
|
14
14
|
``badge_site_color`` and ``badge_node_color`` provide the configured colors.
|
|
15
15
|
"""
|
|
16
|
-
host = request.get_host().split(
|
|
16
|
+
host = request.get_host().split(":")[0]
|
|
17
17
|
site = Site.objects.filter(domain__iexact=host).first()
|
|
18
18
|
|
|
19
19
|
node = None
|
config/offline.py
CHANGED
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
import functools
|
|
3
3
|
import asyncio
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
class OfflineError(RuntimeError):
|
|
6
7
|
"""Raised when a network operation is attempted in offline mode."""
|
|
7
8
|
|
|
@@ -21,6 +22,7 @@ def requires_network(func):
|
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
if asyncio.iscoroutinefunction(func):
|
|
25
|
+
|
|
24
26
|
@functools.wraps(func)
|
|
25
27
|
async def async_wrapper(*args, **kwargs):
|
|
26
28
|
if _is_offline():
|
config/settings.py
CHANGED
|
@@ -16,11 +16,15 @@ import os
|
|
|
16
16
|
import sys
|
|
17
17
|
import ipaddress
|
|
18
18
|
import socket
|
|
19
|
+
from core.log_paths import select_log_dir
|
|
19
20
|
from django.utils.translation import gettext_lazy as _
|
|
20
21
|
from celery.schedules import crontab
|
|
21
22
|
from django.http import request as http_request
|
|
22
23
|
from django.middleware.csrf import CsrfViewMiddleware
|
|
23
24
|
from django.core.exceptions import DisallowedHost
|
|
25
|
+
from django.contrib.sites import shortcuts as sites_shortcuts
|
|
26
|
+
from django.contrib.sites.requests import RequestSite
|
|
27
|
+
from django.core.management.utils import get_random_secret_key
|
|
24
28
|
from urllib.parse import urlsplit
|
|
25
29
|
import django.utils.encoding as encoding
|
|
26
30
|
|
|
@@ -57,7 +61,9 @@ ACRONYMS: list[str] = []
|
|
|
57
61
|
with contextlib.suppress(FileNotFoundError):
|
|
58
62
|
ACRONYMS = [
|
|
59
63
|
line.strip()
|
|
60
|
-
for line in (BASE_DIR / "config" / "data" / "ACRONYMS.txt")
|
|
64
|
+
for line in (BASE_DIR / "config" / "data" / "ACRONYMS.txt")
|
|
65
|
+
.read_text()
|
|
66
|
+
.splitlines()
|
|
61
67
|
if line.strip()
|
|
62
68
|
]
|
|
63
69
|
|
|
@@ -66,7 +72,29 @@ with contextlib.suppress(FileNotFoundError):
|
|
|
66
72
|
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
|
67
73
|
|
|
68
74
|
# SECURITY WARNING: keep the secret key used in production secret!
|
|
69
|
-
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _load_secret_key() -> str:
|
|
78
|
+
for env_var in ("DJANGO_SECRET_KEY", "SECRET_KEY"):
|
|
79
|
+
value = os.environ.get(env_var)
|
|
80
|
+
if value:
|
|
81
|
+
return value
|
|
82
|
+
|
|
83
|
+
secret_file = BASE_DIR / "locks" / "django-secret.key"
|
|
84
|
+
with contextlib.suppress(OSError):
|
|
85
|
+
stored_key = secret_file.read_text(encoding="utf-8").strip()
|
|
86
|
+
if stored_key:
|
|
87
|
+
return stored_key
|
|
88
|
+
|
|
89
|
+
generated_key = get_random_secret_key()
|
|
90
|
+
with contextlib.suppress(OSError):
|
|
91
|
+
secret_file.parent.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
secret_file.write_text(generated_key, encoding="utf-8")
|
|
93
|
+
|
|
94
|
+
return generated_key
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
SECRET_KEY = _load_secret_key()
|
|
70
98
|
|
|
71
99
|
# SECURITY WARNING: don't run with debug turned on in production!
|
|
72
100
|
|
|
@@ -89,6 +117,39 @@ ALLOWED_HOSTS = [
|
|
|
89
117
|
]
|
|
90
118
|
|
|
91
119
|
|
|
120
|
+
def _iter_local_hostnames(hostname: str, fqdn: str | None = None) -> list[str]:
|
|
121
|
+
"""Return unique hostname variants for the current machine."""
|
|
122
|
+
|
|
123
|
+
hostnames: list[str] = []
|
|
124
|
+
seen: set[str] = set()
|
|
125
|
+
|
|
126
|
+
def _append(candidate: str | None) -> None:
|
|
127
|
+
if not candidate:
|
|
128
|
+
return
|
|
129
|
+
normalized = candidate.strip()
|
|
130
|
+
if not normalized or normalized in seen:
|
|
131
|
+
return
|
|
132
|
+
hostnames.append(normalized)
|
|
133
|
+
seen.add(normalized)
|
|
134
|
+
|
|
135
|
+
_append(hostname)
|
|
136
|
+
_append(fqdn)
|
|
137
|
+
if hostname and "." not in hostname:
|
|
138
|
+
_append(f"{hostname}.local")
|
|
139
|
+
|
|
140
|
+
return hostnames
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
_local_hostname = socket.gethostname().strip()
|
|
144
|
+
_local_fqdn = ""
|
|
145
|
+
with contextlib.suppress(Exception):
|
|
146
|
+
_local_fqdn = socket.getfqdn().strip()
|
|
147
|
+
|
|
148
|
+
for host in _iter_local_hostnames(_local_hostname, _local_fqdn):
|
|
149
|
+
if host not in ALLOWED_HOSTS:
|
|
150
|
+
ALLOWED_HOSTS.append(host)
|
|
151
|
+
|
|
152
|
+
|
|
92
153
|
# Allow CSRF origin verification for hosts within allowed subnets.
|
|
93
154
|
_original_origin_verified = CsrfViewMiddleware._origin_verified
|
|
94
155
|
|
|
@@ -134,10 +195,12 @@ LOCAL_APPS = [
|
|
|
134
195
|
"ocpp",
|
|
135
196
|
"awg",
|
|
136
197
|
"pages",
|
|
137
|
-
"
|
|
198
|
+
"man",
|
|
199
|
+
"teams",
|
|
138
200
|
]
|
|
139
201
|
|
|
140
202
|
INSTALLED_APPS = [
|
|
203
|
+
"whitenoise.runserver_nostatic",
|
|
141
204
|
"django.contrib.admin",
|
|
142
205
|
"django.contrib.admindocs",
|
|
143
206
|
"config.auth_app.AuthConfig",
|
|
@@ -149,7 +212,6 @@ INSTALLED_APPS = [
|
|
|
149
212
|
"django_object_actions",
|
|
150
213
|
"django.contrib.sites",
|
|
151
214
|
"channels",
|
|
152
|
-
"config.workgroup_app.WorkgroupConfig",
|
|
153
215
|
"config.horologia_app.HorologiaConfig",
|
|
154
216
|
] + LOCAL_APPS
|
|
155
217
|
|
|
@@ -163,8 +225,25 @@ if DEBUG:
|
|
|
163
225
|
|
|
164
226
|
SITE_ID = 1
|
|
165
227
|
|
|
228
|
+
_original_get_current_site = sites_shortcuts.get_current_site
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _get_current_site_with_request_fallback(request=None):
|
|
232
|
+
try:
|
|
233
|
+
return _original_get_current_site(request)
|
|
234
|
+
except Exception as exc:
|
|
235
|
+
from django.contrib.sites.models import Site
|
|
236
|
+
|
|
237
|
+
if request is not None and isinstance(exc, Site.DoesNotExist):
|
|
238
|
+
return RequestSite(request)
|
|
239
|
+
raise
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
sites_shortcuts.get_current_site = _get_current_site_with_request_fallback
|
|
243
|
+
|
|
166
244
|
MIDDLEWARE = [
|
|
167
245
|
"django.middleware.security.SecurityMiddleware",
|
|
246
|
+
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
168
247
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
169
248
|
"config.middleware.ActiveAppMiddleware",
|
|
170
249
|
"django.middleware.locale.LocaleMiddleware",
|
|
@@ -172,6 +251,8 @@ MIDDLEWARE = [
|
|
|
172
251
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
173
252
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
174
253
|
"core.middleware.AdminHistoryMiddleware",
|
|
254
|
+
"core.middleware.SigilContextMiddleware",
|
|
255
|
+
"pages.middleware.ViewHistoryMiddleware",
|
|
175
256
|
"django.contrib.messages.middleware.MessageMiddleware",
|
|
176
257
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
177
258
|
]
|
|
@@ -214,6 +295,31 @@ ASGI_APPLICATION = "config.asgi.application"
|
|
|
214
295
|
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
|
|
215
296
|
|
|
216
297
|
|
|
298
|
+
# MCP sigil resolver configuration
|
|
299
|
+
def _env_int(name: str, default: int) -> int:
|
|
300
|
+
try:
|
|
301
|
+
return int(os.environ.get(name, default))
|
|
302
|
+
except (TypeError, ValueError): # pragma: no cover - defensive
|
|
303
|
+
return default
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _split_env_list(name: str) -> list[str]:
|
|
307
|
+
raw = os.environ.get(name)
|
|
308
|
+
if not raw:
|
|
309
|
+
return []
|
|
310
|
+
return [item.strip() for item in raw.split(",") if item.strip()]
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
MCP_SIGIL_SERVER = {
|
|
314
|
+
"host": os.environ.get("MCP_SIGIL_HOST", "127.0.0.1"),
|
|
315
|
+
"port": _env_int("MCP_SIGIL_PORT", 8800),
|
|
316
|
+
"api_keys": _split_env_list("MCP_SIGIL_API_KEYS"),
|
|
317
|
+
"required_scopes": ["sigils:read"],
|
|
318
|
+
"issuer_url": os.environ.get("MCP_SIGIL_ISSUER_URL"),
|
|
319
|
+
"resource_server_url": os.environ.get("MCP_SIGIL_RESOURCE_URL"),
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
217
323
|
# Custom user model
|
|
218
324
|
AUTH_USER_MODEL = "core.User"
|
|
219
325
|
|
|
@@ -223,7 +329,6 @@ AUTHENTICATION_BACKENDS = [
|
|
|
223
329
|
"core.backends.RFIDBackend",
|
|
224
330
|
]
|
|
225
331
|
|
|
226
|
-
|
|
227
332
|
# Database
|
|
228
333
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
|
229
334
|
|
|
@@ -240,7 +345,7 @@ def _postgres_available() -> bool:
|
|
|
240
345
|
"password": os.environ.get("POSTGRES_PASSWORD", ""),
|
|
241
346
|
"host": os.environ.get("POSTGRES_HOST", "localhost"),
|
|
242
347
|
"port": os.environ.get("POSTGRES_PORT", "5432"),
|
|
243
|
-
"connect_timeout":
|
|
348
|
+
"connect_timeout": 10,
|
|
244
349
|
}
|
|
245
350
|
try:
|
|
246
351
|
with contextlib.closing(psycopg.connect(**params)):
|
|
@@ -259,6 +364,9 @@ if _postgres_available():
|
|
|
259
364
|
"HOST": os.environ.get("POSTGRES_HOST", "localhost"),
|
|
260
365
|
"PORT": os.environ.get("POSTGRES_PORT", "5432"),
|
|
261
366
|
"OPTIONS": {"options": "-c timezone=UTC"},
|
|
367
|
+
"TEST": {
|
|
368
|
+
"NAME": f"{os.environ.get('POSTGRES_DB', 'postgres')}_test",
|
|
369
|
+
},
|
|
262
370
|
}
|
|
263
371
|
}
|
|
264
372
|
else:
|
|
@@ -266,7 +374,8 @@ else:
|
|
|
266
374
|
"default": {
|
|
267
375
|
"ENGINE": "django.db.backends.sqlite3",
|
|
268
376
|
"NAME": BASE_DIR / "db.sqlite3",
|
|
269
|
-
"OPTIONS": {"timeout":
|
|
377
|
+
"OPTIONS": {"timeout": 60},
|
|
378
|
+
"TEST": {"NAME": BASE_DIR / "test_db.sqlite3"},
|
|
270
379
|
}
|
|
271
380
|
}
|
|
272
381
|
|
|
@@ -298,6 +407,8 @@ LANGUAGE_CODE = "en-us"
|
|
|
298
407
|
LANGUAGES = [
|
|
299
408
|
("en", _("English")),
|
|
300
409
|
("es", _("Spanish")),
|
|
410
|
+
("fr", _("French")),
|
|
411
|
+
("ru", _("Russian")),
|
|
301
412
|
]
|
|
302
413
|
|
|
303
414
|
LOCALE_PATHS = [BASE_DIR / "locale"]
|
|
@@ -313,10 +424,13 @@ USE_TZ = True
|
|
|
313
424
|
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
|
314
425
|
|
|
315
426
|
STATIC_URL = "/static/"
|
|
427
|
+
STATIC_ROOT = BASE_DIR / "static"
|
|
428
|
+
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
|
316
429
|
MEDIA_URL = "/media/"
|
|
317
430
|
MEDIA_ROOT = BASE_DIR / "media"
|
|
318
431
|
|
|
319
432
|
# Email settings
|
|
433
|
+
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
|
320
434
|
DEFAULT_FROM_EMAIL = "arthexis@gmail.com"
|
|
321
435
|
SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
|
322
436
|
|
|
@@ -325,15 +439,15 @@ SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
|
|
325
439
|
|
|
326
440
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
327
441
|
|
|
328
|
-
#
|
|
329
|
-
|
|
330
|
-
|
|
442
|
+
# GitHub issue reporting
|
|
443
|
+
GITHUB_ISSUE_REPORTING_ENABLED = True
|
|
444
|
+
GITHUB_ISSUE_REPORTING_COOLDOWN = 3600 # seconds
|
|
331
445
|
|
|
332
446
|
# Logging configuration
|
|
333
|
-
LOG_DIR = BASE_DIR
|
|
334
|
-
|
|
447
|
+
LOG_DIR = select_log_dir(BASE_DIR)
|
|
448
|
+
os.environ.setdefault("ARTHEXIS_LOG_DIR", str(LOG_DIR))
|
|
335
449
|
OLD_LOG_DIR = LOG_DIR / "old"
|
|
336
|
-
OLD_LOG_DIR.mkdir(exist_ok=True)
|
|
450
|
+
OLD_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
337
451
|
LOG_FILE_NAME = "tests.log" if "test" in sys.argv else f"{socket.gethostname()}.log"
|
|
338
452
|
|
|
339
453
|
LOGGING = {
|
|
@@ -368,8 +482,11 @@ CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
|
|
|
368
482
|
|
|
369
483
|
CELERY_BEAT_SCHEDULE = {
|
|
370
484
|
"heartbeat": {
|
|
371
|
-
"task": "
|
|
485
|
+
"task": "core.tasks.heartbeat",
|
|
372
486
|
"schedule": crontab(minute="*/5"),
|
|
373
|
-
}
|
|
487
|
+
},
|
|
488
|
+
"birthday_greetings": {
|
|
489
|
+
"task": "core.tasks.birthday_greetings",
|
|
490
|
+
"schedule": crontab(hour=9, minute=0),
|
|
491
|
+
},
|
|
374
492
|
}
|
|
375
|
-
|
config/urls.py
CHANGED
|
@@ -13,15 +13,21 @@ from django.apps import apps
|
|
|
13
13
|
from django.conf import settings
|
|
14
14
|
from django.conf.urls.static import static
|
|
15
15
|
from django.contrib import admin
|
|
16
|
-
from django.contrib.admin import autodiscover
|
|
17
16
|
from django.urls import include, path
|
|
17
|
+
import teams.admin # noqa: F401
|
|
18
18
|
from django.views.decorators.csrf import csrf_exempt
|
|
19
|
+
from django.views.generic import RedirectView
|
|
19
20
|
from django.views.i18n import set_language
|
|
20
21
|
from django.utils.translation import gettext_lazy as _
|
|
21
22
|
from core import views as core_views
|
|
22
|
-
from core.admindocs import
|
|
23
|
+
from core.admindocs import (
|
|
24
|
+
CommandsView,
|
|
25
|
+
ModelGraphIndexView,
|
|
26
|
+
OrderedModelIndexView,
|
|
27
|
+
)
|
|
28
|
+
from man import views as man_views
|
|
29
|
+
from pages import views as pages_views
|
|
23
30
|
|
|
24
|
-
autodiscover()
|
|
25
31
|
admin.site.site_header = _("Constellation")
|
|
26
32
|
admin.site.site_title = _("Constellation")
|
|
27
33
|
|
|
@@ -64,17 +70,69 @@ def autodiscovered_urlpatterns():
|
|
|
64
70
|
|
|
65
71
|
|
|
66
72
|
urlpatterns = [
|
|
73
|
+
path(
|
|
74
|
+
"admin/doc/manuals/",
|
|
75
|
+
man_views.admin_manual_list,
|
|
76
|
+
name="django-admindocs-manuals",
|
|
77
|
+
),
|
|
78
|
+
path(
|
|
79
|
+
"admin/doc/manuals/<slug:slug>/",
|
|
80
|
+
man_views.admin_manual_detail,
|
|
81
|
+
name="django-admindocs-manual-detail",
|
|
82
|
+
),
|
|
83
|
+
path(
|
|
84
|
+
"admin/doc/manuals/<slug:slug>/pdf/",
|
|
85
|
+
man_views.manual_pdf,
|
|
86
|
+
name="django-admindocs-manual-pdf",
|
|
87
|
+
),
|
|
67
88
|
path(
|
|
68
89
|
"admin/doc/commands/",
|
|
69
90
|
CommandsView.as_view(),
|
|
70
91
|
name="django-admindocs-commands",
|
|
71
92
|
),
|
|
72
|
-
path(
|
|
93
|
+
path(
|
|
94
|
+
"admin/doc/commands/",
|
|
95
|
+
RedirectView.as_view(pattern_name="django-admindocs-commands"),
|
|
96
|
+
),
|
|
97
|
+
path(
|
|
98
|
+
"admin/doc/model-graphs/",
|
|
99
|
+
ModelGraphIndexView.as_view(),
|
|
100
|
+
name="django-admindocs-model-graphs",
|
|
101
|
+
),
|
|
102
|
+
path(
|
|
103
|
+
"admindocs/model-graphs/",
|
|
104
|
+
RedirectView.as_view(pattern_name="django-admindocs-model-graphs"),
|
|
105
|
+
),
|
|
106
|
+
path(
|
|
107
|
+
"admindocs/models/",
|
|
108
|
+
OrderedModelIndexView.as_view(),
|
|
109
|
+
name="django-admindocs-models-index",
|
|
110
|
+
),
|
|
111
|
+
path("admindocs/", include("django.contrib.admindocs.urls")),
|
|
112
|
+
path(
|
|
113
|
+
"admin/doc/",
|
|
114
|
+
RedirectView.as_view(pattern_name="django-admindocs-docroot"),
|
|
115
|
+
),
|
|
116
|
+
path(
|
|
117
|
+
"admin/model-graph/<str:app_label>/",
|
|
118
|
+
admin.site.admin_view(pages_views.admin_model_graph),
|
|
119
|
+
name="admin-model-graph",
|
|
120
|
+
),
|
|
73
121
|
path(
|
|
74
122
|
"admin/core/releases/<int:pk>/<str:action>/",
|
|
75
123
|
core_views.release_progress,
|
|
76
124
|
name="release-progress",
|
|
77
125
|
),
|
|
126
|
+
path(
|
|
127
|
+
"admin/core/todos/<int:pk>/done/",
|
|
128
|
+
core_views.todo_done,
|
|
129
|
+
name="todo-done",
|
|
130
|
+
),
|
|
131
|
+
path(
|
|
132
|
+
"admin/core/odoo-products/",
|
|
133
|
+
core_views.odoo_products,
|
|
134
|
+
name="odoo-products",
|
|
135
|
+
),
|
|
78
136
|
path("admin/", admin.site.urls),
|
|
79
137
|
path("i18n/setlang/", csrf_exempt(set_language), name="set_language"),
|
|
80
138
|
path("api/", include("core.workgroup_urls")),
|
|
@@ -92,9 +150,10 @@ if settings.DEBUG:
|
|
|
92
150
|
urlpatterns = [
|
|
93
151
|
path(
|
|
94
152
|
"__debug__/",
|
|
95
|
-
include(
|
|
153
|
+
include(
|
|
154
|
+
("debug_toolbar.urls", "debug_toolbar"), namespace="debug_toolbar"
|
|
155
|
+
),
|
|
96
156
|
)
|
|
97
157
|
] + urlpatterns
|
|
98
158
|
|
|
99
159
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
100
|
-
|