arthexis 0.1.7__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.9.dist-info/METADATA +168 -0
- 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 +134 -16
- config/urls.py +71 -3
- core/admin.py +1331 -165
- core/admin_history.py +50 -0
- core/admindocs.py +151 -0
- 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 +1136 -259
- 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 +445 -58
- 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 +17 -0
- core/workgroup_views.py +94 -0
- 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 +4 -3
- 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.7.dist-info/METADATA +0 -126
- arthexis-0.1.7.dist-info/RECORD +0 -77
- arthexis-0.1.7.dist-info/licenses/LICENSE +0 -21
- config/workgroup_app.py +0 -7
- core/checks.py +0 -29
- {arthexis-0.1.7.dist-info → arthexis-0.1.9.dist-info}/WHEEL +0 -0
- {arthexis-0.1.7.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
|
|
|
@@ -85,9 +113,43 @@ ALLOWED_HOSTS = [
|
|
|
85
113
|
"10.42.0.0/16",
|
|
86
114
|
"192.168.0.0/16",
|
|
87
115
|
"arthexis.com",
|
|
116
|
+
"www.arthexis.com",
|
|
88
117
|
]
|
|
89
118
|
|
|
90
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
|
+
|
|
91
153
|
# Allow CSRF origin verification for hosts within allowed subnets.
|
|
92
154
|
_original_origin_verified = CsrfViewMiddleware._origin_verified
|
|
93
155
|
|
|
@@ -133,10 +195,12 @@ LOCAL_APPS = [
|
|
|
133
195
|
"ocpp",
|
|
134
196
|
"awg",
|
|
135
197
|
"pages",
|
|
136
|
-
"
|
|
198
|
+
"man",
|
|
199
|
+
"teams",
|
|
137
200
|
]
|
|
138
201
|
|
|
139
202
|
INSTALLED_APPS = [
|
|
203
|
+
"whitenoise.runserver_nostatic",
|
|
140
204
|
"django.contrib.admin",
|
|
141
205
|
"django.contrib.admindocs",
|
|
142
206
|
"config.auth_app.AuthConfig",
|
|
@@ -148,7 +212,6 @@ INSTALLED_APPS = [
|
|
|
148
212
|
"django_object_actions",
|
|
149
213
|
"django.contrib.sites",
|
|
150
214
|
"channels",
|
|
151
|
-
"config.workgroup_app.WorkgroupConfig",
|
|
152
215
|
"config.horologia_app.HorologiaConfig",
|
|
153
216
|
] + LOCAL_APPS
|
|
154
217
|
|
|
@@ -162,8 +225,25 @@ if DEBUG:
|
|
|
162
225
|
|
|
163
226
|
SITE_ID = 1
|
|
164
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
|
+
|
|
165
244
|
MIDDLEWARE = [
|
|
166
245
|
"django.middleware.security.SecurityMiddleware",
|
|
246
|
+
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
167
247
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
168
248
|
"config.middleware.ActiveAppMiddleware",
|
|
169
249
|
"django.middleware.locale.LocaleMiddleware",
|
|
@@ -171,6 +251,8 @@ MIDDLEWARE = [
|
|
|
171
251
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|
172
252
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
173
253
|
"core.middleware.AdminHistoryMiddleware",
|
|
254
|
+
"core.middleware.SigilContextMiddleware",
|
|
255
|
+
"pages.middleware.ViewHistoryMiddleware",
|
|
174
256
|
"django.contrib.messages.middleware.MessageMiddleware",
|
|
175
257
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
176
258
|
]
|
|
@@ -213,6 +295,31 @@ ASGI_APPLICATION = "config.asgi.application"
|
|
|
213
295
|
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}
|
|
214
296
|
|
|
215
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
|
+
|
|
216
323
|
# Custom user model
|
|
217
324
|
AUTH_USER_MODEL = "core.User"
|
|
218
325
|
|
|
@@ -222,7 +329,6 @@ AUTHENTICATION_BACKENDS = [
|
|
|
222
329
|
"core.backends.RFIDBackend",
|
|
223
330
|
]
|
|
224
331
|
|
|
225
|
-
|
|
226
332
|
# Database
|
|
227
333
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
|
228
334
|
|
|
@@ -239,7 +345,7 @@ def _postgres_available() -> bool:
|
|
|
239
345
|
"password": os.environ.get("POSTGRES_PASSWORD", ""),
|
|
240
346
|
"host": os.environ.get("POSTGRES_HOST", "localhost"),
|
|
241
347
|
"port": os.environ.get("POSTGRES_PORT", "5432"),
|
|
242
|
-
"connect_timeout":
|
|
348
|
+
"connect_timeout": 10,
|
|
243
349
|
}
|
|
244
350
|
try:
|
|
245
351
|
with contextlib.closing(psycopg.connect(**params)):
|
|
@@ -258,6 +364,9 @@ if _postgres_available():
|
|
|
258
364
|
"HOST": os.environ.get("POSTGRES_HOST", "localhost"),
|
|
259
365
|
"PORT": os.environ.get("POSTGRES_PORT", "5432"),
|
|
260
366
|
"OPTIONS": {"options": "-c timezone=UTC"},
|
|
367
|
+
"TEST": {
|
|
368
|
+
"NAME": f"{os.environ.get('POSTGRES_DB', 'postgres')}_test",
|
|
369
|
+
},
|
|
261
370
|
}
|
|
262
371
|
}
|
|
263
372
|
else:
|
|
@@ -265,7 +374,8 @@ else:
|
|
|
265
374
|
"default": {
|
|
266
375
|
"ENGINE": "django.db.backends.sqlite3",
|
|
267
376
|
"NAME": BASE_DIR / "db.sqlite3",
|
|
268
|
-
"OPTIONS": {"timeout":
|
|
377
|
+
"OPTIONS": {"timeout": 60},
|
|
378
|
+
"TEST": {"NAME": BASE_DIR / "test_db.sqlite3"},
|
|
269
379
|
}
|
|
270
380
|
}
|
|
271
381
|
|
|
@@ -297,6 +407,8 @@ LANGUAGE_CODE = "en-us"
|
|
|
297
407
|
LANGUAGES = [
|
|
298
408
|
("en", _("English")),
|
|
299
409
|
("es", _("Spanish")),
|
|
410
|
+
("fr", _("French")),
|
|
411
|
+
("ru", _("Russian")),
|
|
300
412
|
]
|
|
301
413
|
|
|
302
414
|
LOCALE_PATHS = [BASE_DIR / "locale"]
|
|
@@ -312,10 +424,13 @@ USE_TZ = True
|
|
|
312
424
|
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
|
313
425
|
|
|
314
426
|
STATIC_URL = "/static/"
|
|
427
|
+
STATIC_ROOT = BASE_DIR / "static"
|
|
428
|
+
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
|
315
429
|
MEDIA_URL = "/media/"
|
|
316
430
|
MEDIA_ROOT = BASE_DIR / "media"
|
|
317
431
|
|
|
318
432
|
# Email settings
|
|
433
|
+
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
|
|
319
434
|
DEFAULT_FROM_EMAIL = "arthexis@gmail.com"
|
|
320
435
|
SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
|
321
436
|
|
|
@@ -324,15 +439,15 @@ SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
|
|
324
439
|
|
|
325
440
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
326
441
|
|
|
327
|
-
#
|
|
328
|
-
|
|
329
|
-
|
|
442
|
+
# GitHub issue reporting
|
|
443
|
+
GITHUB_ISSUE_REPORTING_ENABLED = True
|
|
444
|
+
GITHUB_ISSUE_REPORTING_COOLDOWN = 3600 # seconds
|
|
330
445
|
|
|
331
446
|
# Logging configuration
|
|
332
|
-
LOG_DIR = BASE_DIR
|
|
333
|
-
|
|
447
|
+
LOG_DIR = select_log_dir(BASE_DIR)
|
|
448
|
+
os.environ.setdefault("ARTHEXIS_LOG_DIR", str(LOG_DIR))
|
|
334
449
|
OLD_LOG_DIR = LOG_DIR / "old"
|
|
335
|
-
OLD_LOG_DIR.mkdir(exist_ok=True)
|
|
450
|
+
OLD_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
336
451
|
LOG_FILE_NAME = "tests.log" if "test" in sys.argv else f"{socket.gethostname()}.log"
|
|
337
452
|
|
|
338
453
|
LOGGING = {
|
|
@@ -367,8 +482,11 @@ CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
|
|
|
367
482
|
|
|
368
483
|
CELERY_BEAT_SCHEDULE = {
|
|
369
484
|
"heartbeat": {
|
|
370
|
-
"task": "
|
|
485
|
+
"task": "core.tasks.heartbeat",
|
|
371
486
|
"schedule": crontab(minute="*/5"),
|
|
372
|
-
}
|
|
487
|
+
},
|
|
488
|
+
"birthday_greetings": {
|
|
489
|
+
"task": "core.tasks.birthday_greetings",
|
|
490
|
+
"schedule": crontab(hour=9, minute=0),
|
|
491
|
+
},
|
|
373
492
|
}
|
|
374
|
-
|
config/urls.py
CHANGED
|
@@ -14,10 +14,19 @@ from django.conf import settings
|
|
|
14
14
|
from django.conf.urls.static import static
|
|
15
15
|
from django.contrib import admin
|
|
16
16
|
from django.urls import include, path
|
|
17
|
+
import teams.admin # noqa: F401
|
|
17
18
|
from django.views.decorators.csrf import csrf_exempt
|
|
19
|
+
from django.views.generic import RedirectView
|
|
18
20
|
from django.views.i18n import set_language
|
|
19
21
|
from django.utils.translation import gettext_lazy as _
|
|
20
22
|
from core import views as core_views
|
|
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
|
|
21
30
|
|
|
22
31
|
admin.site.site_header = _("Constellation")
|
|
23
32
|
admin.site.site_title = _("Constellation")
|
|
@@ -61,14 +70,72 @@ def autodiscovered_urlpatterns():
|
|
|
61
70
|
|
|
62
71
|
|
|
63
72
|
urlpatterns = [
|
|
64
|
-
path(
|
|
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
|
+
),
|
|
88
|
+
path(
|
|
89
|
+
"admin/doc/commands/",
|
|
90
|
+
CommandsView.as_view(),
|
|
91
|
+
name="django-admindocs-commands",
|
|
92
|
+
),
|
|
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
|
+
),
|
|
65
121
|
path(
|
|
66
122
|
"admin/core/releases/<int:pk>/<str:action>/",
|
|
67
123
|
core_views.release_progress,
|
|
68
124
|
name="release-progress",
|
|
69
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
|
+
),
|
|
70
136
|
path("admin/", admin.site.urls),
|
|
71
137
|
path("i18n/setlang/", csrf_exempt(set_language), name="set_language"),
|
|
138
|
+
path("api/", include("core.workgroup_urls")),
|
|
72
139
|
path("", include("pages.urls")),
|
|
73
140
|
]
|
|
74
141
|
|
|
@@ -83,9 +150,10 @@ if settings.DEBUG:
|
|
|
83
150
|
urlpatterns = [
|
|
84
151
|
path(
|
|
85
152
|
"__debug__/",
|
|
86
|
-
include(
|
|
153
|
+
include(
|
|
154
|
+
("debug_toolbar.urls", "debug_toolbar"), namespace="debug_toolbar"
|
|
155
|
+
),
|
|
87
156
|
)
|
|
88
157
|
] + urlpatterns
|
|
89
158
|
|
|
90
159
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
91
|
-
|