Coverage for backend/CoreRoot/settings.py: 80%
88 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
1"""
2Django settings for CoreRoot project.
4Generated by 'django-admin startproject' using Django 4.2.7.
6For more information on this file, see
7https://docs.djangoproject.com/en/4.2/topics/settings/
9For the full list of settings and their values, see
10https://docs.djangoproject.com/en/4.2/ref/settings/
11"""
12import logging
13from pathlib import Path
14from django.db.backends.postgresql.psycopg_any import IsolationLevel
15import os
16import warnings
17from dotenv import load_dotenv
19# Build paths inside the project like this: BASE_DIR / 'subdir'.
20BASE_DIR = Path(__file__).resolve().parent.parent
23load_dotenv(dotenv_path=BASE_DIR / ".env") # optional base
25DEBUG = os.getenv("PRODUCTION", "False").lower() in ('false', '0')
28if DEBUG: 28 ↛ 35line 28 didn't jump to line 35 because the condition on line 28 was always true
29 load_dotenv(dotenv_path=BASE_DIR / ".env.development.local")
31# Quick-start development settings - unsuitable for production
32# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
34# SECURITY WARNING: keep the secret key used in production secret!
35SECRET_KEY = 'django-insecure-6zboi37mw#!#6e^qx2gk#0t@wqv5_3*yh8u@^^smbzgk6enq^('
37# SECURITY WARNING: don't run with debug turned on in production!
40DAPR_APP_API_TOKEN = os.getenv("DAPR_APP_API_TOKEN", None)
41if DEBUG and DAPR_APP_API_TOKEN is None: 41 ↛ 44line 41 didn't jump to line 44 because the condition on line 41 was always true
42 DAPR_APP_API_TOKEN = "BlYMxKQgDWt+NDVa7NsNBw==" # This must be the same as defined in django-dapr's env variable in the docker-compose file
44PROFILING_ENABLED = os.getenv("PROFILING_ENABLED", "False").lower() in ('true', '1')
46# Get string list from comma-separated list of allowed hosts,
47# e.g. "localhost,api.ahuora.co.nz,127.0.0.1" turns into ["localhost", "api.ahuora.co.nz", "127.0.0.1"]
48ALLOWED_HOSTS = list(filter(None, os.getenv("ALLOWED_HOSTS", "").split(",")))
49if DEBUG: 49 ↛ 52line 49 didn't jump to line 52 because the condition on line 49 was always true
50 ALLOWED_HOSTS += ["host.docker.internal", "localhost", "127.0.0.1"]
52CODE_COVERAGE_ENABLED = os.getenv("CODE_COVERAGE_ENABLED", "False").lower() in ('true', '1')
53if CODE_COVERAGE_ENABLED: 53 ↛ 60line 53 didn't jump to line 60 because the condition on line 53 was never true
54 # Code coverage can be run for Django in two ways:
55 # 1. Using `coverage run ...` to start the server
56 # 2. By setting the COVERAGE_PROCESS_START environment variable and manually starting coverage.
57 # Number 1 is used when running tests, while number 2 is used when running the server via Granian,
58 # as `coverage run ...` does not seem to be able to discover Python subprocesses started by a non-Python program.
60 code_coverage_already_running = os.getenv("COVERAGE_PROCESS_CONFIG")
62 if code_coverage_already_running:
63 logging.info("Detected code coverage running via `coverage run ...`")
64 else:
65 import coverage
67 # Set default coverage config file name
68 os.environ.setdefault("COVERAGE_PROCESS_START", ".coveragerc")
70 coverage.process_startup()
71 logging.info("Manually started code coverage measurement")
73 if not DEBUG:
74 warnings.warn("Code coverage measurement running in production mode. This may impact performance.")
76# Application definition
78INSTALLED_APPS = [
79 'django.contrib.auth',
80 'django.contrib.contenttypes',
81 'django.contrib.sessions',
82 'django.contrib.messages',
83 'django.contrib.staticfiles',
84 'rest_framework',
85 'drf_spectacular',
86 'corsheaders',
87 'core',
88 'core.auxiliary',
89 'authentication',
90 'authentication.user',
91 'flowsheetInternals',
92 'flowsheetInternals.unitops',
93 'flowsheetInternals.graphicData',
94 'flowsheetInternals.propertyPackages',
95 'PinchAnalysis',
96 'django_bleach',
97]
99CHANNEL_LAYERS = {
100 "default": {
101 "BACKEND": "channels_redis.core.RedisChannelLayer",
102 "CONFIG": {
103 "hosts": [(
104 os.getenv("CHANNELS_REDIS_HOST", "localhost"),
105 os.getenv("CHANNELS_REDIS_PORT", 6379))
106 ],
107 },
108 },
109}
111REST_FRAMEWORK = {
112 'DEFAULT_AUTHENTICATION_CLASSES': (
113 'authentication.custom_drf_authentication.AhuoraRemoteUserAuthentication',
114 ),
115 'EXCEPTION_HANDLER': 'core.exceptions.otel_trace_exception_handler',
116 'DEFAULT_RENDERER_CLASSES': (
117 'rest_framework.renderers.JSONRenderer',
118 ),
119 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
120 'DEFAULT_PERMISSION_CLASSES': [
121 'rest_framework.permissions.IsAuthenticated',
122 ],
123 'DEFAULT_PARSER_CLASSES': [
124 'rest_framework.parsers.JSONParser',
125 'core.parsers.CloudEventsParser'
126 ]
127}
129SPECTACULAR_SETTINGS = {
130 'TITLE': 'Ahuora API',
131 'DESCRIPTION': 'Your project description',
132 'VERSION': '1.0.0',
133 'SERVE_INCLUDE_SCHEMA': False,
134}
136MIDDLEWARE = [
137 'django.middleware.security.SecurityMiddleware',
138 'django.contrib.sessions.middleware.SessionMiddleware',
139 'corsheaders.middleware.CorsMiddleware',
140 'django.middleware.common.CommonMiddleware',
141 'django.middleware.csrf.CsrfViewMiddleware',
142 'django.contrib.auth.middleware.AuthenticationMiddleware',
143 'authentication.middleware.AhuoraRemoteUserMiddleware',
144 'django.contrib.messages.middleware.MessageMiddleware',
145 'django.middleware.clickjacking.XFrameOptionsMiddleware',
146]
148SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
150CORS_ALLOWED_ORIGINS = [
151 "http://localhost:19006",# Dev server
152 "http://127.0.0.1:19006",
153 "http://localhost:19005",# E2E Test server
154 "http://127.0.0.1:19005",
155 "http://localhost:3000",
156 "http://front-end:19006",
157 "http://172.29.171.74:19006",
158 "https://ahuora.org.nz", # Production server
159 "https://www.ahuora.org.nz",
160 "http://ahuora.org.nz",
161 "http://www.ahuora.org.nz"
162]
164CORS_ALLOW_CREDENTIALS = True
166ROOT_URLCONF = 'CoreRoot.urls'
168TEMPLATES = [
169 {
170 'BACKEND': 'django.template.backends.django.DjangoTemplates',
171 'DIRS': [],
172 'APP_DIRS': True,
173 'OPTIONS': {
174 'context_processors': [
175 'django.template.context_processors.debug',
176 'django.template.context_processors.request',
177 'django.contrib.auth.context_processors.auth',
178 'django.contrib.messages.context_processors.messages',
179 ],
180 },
181 },
182]
184OPEN_TELEMETRY_TRACER_NAME = "ahuora-api"
186WSGI_APPLICATION = 'CoreRoot.wsgi.application'
188FIXTURE_DIRS = [
189 BASE_DIR / '/fixtures'
190]
191#Renewable Ninja Token
193RENEWABLES_NINJA_TOKEN = os.getenv("RENEWABLES_NINJA_TOKEN")
194if not RENEWABLES_NINJA_TOKEN: 194 ↛ 201line 194 didn't jump to line 201 because the condition on line 194 was always true
195 warnings.warn("RENEWABLES_NINJA_TOKEN is not set in environment variables.", UserWarning)
198# Database
199# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
201DATABASES = {
202 'default': {
203 'ENGINE': 'django.db.backends.postgresql',
204 'NAME': os.getenv("POSTGRES_DB", "postgres"),
205 'HOST': os.getenv("POSTGRES_HOST", "localhost"),
206 'USER': os.getenv("POSTGRES_USER", "postgres"),
207 'PASSWORD': os.getenv("POSTGRES_PASSWORD", "postgres"),
208 'OPTIONS': {
209 'isolation_level': IsolationLevel.READ_COMMITTED,
210 }
211 }
212}
214LOGGING = {
215 "version": 1,
216 "disable_existing_loggers": False,
217 "formatters": {
218 'trace_formatter': {
219 'format': '[%(asctime)s] %(levelname)s [%(filename)s:%(lineno)d] [trace_id=%(otelTraceID)s span_id=%(otelSpanID)s] [%(funcName)s] %(message)s', # optional, default is logging.BASIC_FORMAT
220 'datefmt': '%Y-%m-%d %H:%M:%S', # optional, default is '%Y-%m-%d %H:%M:%S'
221 },
222 },
223 "handlers": {
224 "console": {
225 "class": "logging.StreamHandler",
226 "formatter": "trace_formatter",
227 },
228 },
229 "root": {
230 "handlers": ["console"],
231 "level": "INFO",
232 },
233 "loggers": {
234 "django": {
235 "handlers": ["console"],
236 "level": "INFO",
237 "propagate": False,
238 }
239 },
240}
242# Password validation
243# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
245AUTH_PASSWORD_VALIDATORS = [
246 {
247 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
248 },
249 {
250 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
251 },
252 {
253 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
254 },
255 {
256 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
257 },
258]
260# Password hashers/KDFs to enable.
261# The first entry will be used when creating new passwords.
262PASSWORD_HASHERS = [
263 "django.contrib.auth.hashers.Argon2PasswordHasher"
264]
266# Internationalization
267# https://docs.djangoproject.com/en/4.2/topics/i18n/
269LANGUAGE_CODE = 'en-us'
271TIME_ZONE = 'UTC'
273USE_I18N = True
275USE_TZ = True
277# Static files (CSS, JavaScript, Images)
278# https://docs.djangoproject.com/en/4.2/howto/static-files/
280STATIC_URL = 'static/'
282SILKY_META = True
284# Default primary key field type
285# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
287DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
289AUTH_USER_MODEL = 'authentication_user.User'
291AUTHENTICATION_BACKENDS = [
292 "authentication.remote_user_backend.RemoteUserBackendWithEmail",
293]
295PLATFORM_ADMINISTRATORS_GROUP = os.getenv("PLATFORM_ADMINISTRATORS_GROUP", "/PlatformAdministrators")
297REMOTE_USER_HEADER = "HTTP_X_AUTH_REQUEST_USER"
298ASGI_REMOTE_USER_HEADER = "x-auth-request-user"
299REMOTE_USER_EMAIL_HEADER = "HTTP_X_AUTH_REQUEST_EMAIL"
300ASGI_REMOTE_USER_EMAIL_HEADER = "x-auth-request-email"
301REMOTE_USER_GROUPS_HEADER = "HTTP_X_AUTH_REQUEST_GROUPS"
302ASGI_REMOTE_USER_GROUPS_HEADER = "x-auth-request-groups"
303REMOTE_USER_ACCESS_TOKEN_HEADER = "HTTP_X_AUTH_REQUEST_ACCESS_TOKEN"
304ASGI_REMOTE_USER_ACCESS_TOKEN_HEADER = "x-auth-request-access-token"
306def __insert_middleware(middleware_name: str, before_middleware_name: str):
307 index = MIDDLEWARE.index(before_middleware_name)
308 MIDDLEWARE.insert(index, middleware_name)
310def set_dapr_endpoints():
311 # Monkey-patch Dapr SDK config to avoid the need to set environment variables externally when
312 # running the API server. We still check for environment variables to allow for overriding.
313 from dapr.conf import settings
315 if os.getenv("DAPR_HTTP_ENDPOINT") is None:
316 settings.DAPR_HTTP_ENDPOINT = "http://localhost:3501"
318 if os.getenv("DAPR_GRPC_ENDPOINT") is None:
319 settings.DAPR_GRPC_ENDPOINT = "localhost:50001"
321if DEBUG: 321 ↛ 338line 321 didn't jump to line 338 because the condition on line 321 was always true
322 set_dapr_endpoints()
324 # Add the dummy auth header middleware and the remote user middleware to the middleware list
325 __insert_middleware(
326 'authentication.middleware.dummy_auth_header_middleware',
327 'authentication.middleware.AhuoraRemoteUserMiddleware'
328 )
330 # Only allow profiling with Silk if we're both in debug mode and profiling is enabled
331 if PROFILING_ENABLED: 331 ↛ 332line 331 didn't jump to line 332 because the condition on line 331 was never true
332 __insert_middleware(
333 'silk.middleware.SilkyMiddleware',
334 'django.contrib.auth.middleware.AuthenticationMiddleware'
335 )
336 INSTALLED_APPS.append('silk')
338BLEACH_ALLOWED_TAGS = [
339 'a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul',
340]
341BLEACH_ALLOWED_ATTRIBUTES = {
342 '*': ['class', 'id', 'style'],
343 'a': ['href', 'rel'],
344}
345BLEACH_ALLOWED_STYLES = [
346 'color', 'font-weight', 'text-decoration',
347]
348BLEACH_STRIP_TAGS = True
349BLEACH_STRIP_COMMENTS = True