Coverage for backend/django/CoreRoot/settings.py: 85%

94 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-02-12 01:47 +0000

1""" 

2Django settings for CoreRoot project. 

3 

4Generated by 'django-admin startproject' using Django 4.2.7. 

5 

6For more information on this file, see 

7https://docs.djangoproject.com/en/4.2/topics/settings/ 

8 

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 

18 

19# Build paths inside the project like this: BASE_DIR / 'subdir'. 

20BASE_DIR = Path(__file__).resolve().parent.parent 

21 

22load_dotenv(dotenv_path=BASE_DIR / ".env") # optional base 

23 

24DEBUG = os.getenv("PRODUCTION", "False").lower() in ('false', '0') 

25 

26if DEBUG: 26 ↛ 29line 26 didn't jump to line 29 because the condition on line 26 was always true

27 load_dotenv(dotenv_path=BASE_DIR / ".env.development.local") 

28 

29DIAGNOSTICS_RULES_PATH = os.getenv( 

30 "DIAGNOSTICS_RULES_PATH", 

31 str((BASE_DIR / "diagnostics" / "rules" / "validation_rules.jdm").resolve()), 

32) 

33 

34# Quick-start development settings - unsuitable for production 

35# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ 

36 

37# SECURITY WARNING: keep the secret key used in production secret! 

38SECRET_KEY = 'django-insecure-6zboi37mw#!#6e^qx2gk#0t@wqv5_3*yh8u@^^smbzgk6enq^(' 

39 

40# SECURITY WARNING: don't run with debug turned on in production! 

41 

42 

43DAPR_APP_API_TOKEN = os.getenv("DAPR_APP_API_TOKEN", None) 

44if DEBUG and DAPR_APP_API_TOKEN is None: 

45 DAPR_APP_API_TOKEN = "BlYMxKQgDWt+NDVa7NsNBw==" # This must be the same as defined in django-dapr's env variable in the docker-compose file 

46 

47PROFILING_ENABLED = os.getenv("PROFILING_ENABLED", "False").lower() in ('true', '1') 

48 

49# Get string list from comma-separated list of allowed hosts,  

50# e.g. "localhost,api.ahuora.co.nz,127.0.0.1" turns into ["localhost", "api.ahuora.co.nz", "127.0.0.1"] 

51ALLOWED_HOSTS = list(filter(None, os.getenv("ALLOWED_HOSTS", "").split(","))) 

52if DEBUG: 52 ↛ 55line 52 didn't jump to line 55 because the condition on line 52 was always true

53 ALLOWED_HOSTS += ["host.docker.internal", "localhost", "127.0.0.1"] 

54 

55CODE_COVERAGE_ENABLED = os.getenv("CODE_COVERAGE_ENABLED", "False").lower() in ('true', '1') 

56if CODE_COVERAGE_ENABLED: 

57 # Code coverage can be run for Django in two ways: 

58 # 1. Using `coverage run ...` to start the server 

59 # 2. By setting the COVERAGE_PROCESS_START environment variable and manually starting coverage. 

60 # Number 1 is used when running tests, while number 2 is used when running the server via Granian, 

61 # as `coverage run ...` does not seem to be able to discover Python subprocesses started by a non-Python program. 

62 

63 direct_code_coverage_active = os.getenv("COVERAGE_PROCESS_CONFIG") 

64 indirect_code_coverage_active = os.getenv("COVERAGE_PROCESS_START") 

65 

66 if direct_code_coverage_active: 66 ↛ 68line 66 didn't jump to line 68 because the condition on line 66 was always true

67 logging.info("Detected code coverage running via `coverage run ...`") 

68 elif indirect_code_coverage_active: 

69 logging.info("Detected code coverage running via COVERAGE_PROCESS_START environment variable (likely sitepackages script injection)") 

70 else: 

71 import coverage 

72 

73 # Set default coverage config file name 

74 os.environ.setdefault("COVERAGE_PROCESS_START", ".coveragerc") 

75 

76 coverage.process_startup() 

77 logging.info("Manually started code coverage measurement") 

78 

79 if not DEBUG: 79 ↛ 80line 79 didn't jump to line 80 because the condition on line 79 was never true

80 warnings.warn("Code coverage measurement running in production mode. This may impact performance.") 

81 

82# Application definition 

83 

84INSTALLED_APPS = [ 

85 'django.contrib.auth', 

86 'django.contrib.contenttypes', 

87 'django.contrib.sessions', 

88 'django.contrib.messages', 

89 'django.contrib.staticfiles', 

90 'rest_framework', 

91 'drf_spectacular', 

92 'corsheaders', 

93 'core', 

94 'core.auxiliary', 

95 'authentication', 

96 'authentication.user', 

97 'flowsheetInternals', 

98 'flowsheetInternals.unitops', 

99 'flowsheetInternals.graphicData', 

100 'flowsheetInternals.propertyPackages', 

101 'PinchAnalysis', 

102 'django_bleach', 

103 'diagnostics', 

104] 

105 

106CHANNEL_LAYERS = { 

107 "default": { 

108 "BACKEND": "channels_redis.core.RedisChannelLayer", 

109 "CONFIG": { 

110 "hosts": [( 

111 os.getenv("CHANNELS_REDIS_HOST", "localhost"), 

112 os.getenv("CHANNELS_REDIS_PORT", 6379)) 

113 ], 

114 }, 

115 }, 

116} 

117 

118REST_FRAMEWORK = { 

119 'DEFAULT_AUTHENTICATION_CLASSES': ( 

120 'authentication.custom_drf_authentication.AhuoraRemoteUserAuthentication', 

121 ), 

122 'EXCEPTION_HANDLER': 'core.exceptions.otel_trace_exception_handler', 

123 'DEFAULT_RENDERER_CLASSES': ( 

124 'rest_framework.renderers.JSONRenderer', 

125 ), 

126 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 

127 'DEFAULT_PERMISSION_CLASSES': [ 

128 'rest_framework.permissions.IsAuthenticated', 

129 ], 

130 'DEFAULT_PARSER_CLASSES': [ 

131 'rest_framework.parsers.JSONParser', 

132 'core.parsers.CloudEventsParser' 

133 ] 

134} 

135 

136SPECTACULAR_SETTINGS = { 

137 'TITLE': 'Ahuora API', 

138 'DESCRIPTION': 'Your project description', 

139 'VERSION': '1.0.0', 

140 'SERVE_INCLUDE_SCHEMA': False, 

141} 

142 

143MIDDLEWARE = [ 

144 'django.middleware.security.SecurityMiddleware', 

145 'django.contrib.sessions.middleware.SessionMiddleware', 

146 'corsheaders.middleware.CorsMiddleware', 

147 'django.middleware.common.CommonMiddleware', 

148 'django.middleware.csrf.CsrfViewMiddleware', 

149 'django.contrib.auth.middleware.AuthenticationMiddleware', 

150 'authentication.middleware.AhuoraRemoteUserMiddleware', 

151 'django.contrib.messages.middleware.MessageMiddleware', 

152 'django.middleware.clickjacking.XFrameOptionsMiddleware', 

153] 

154 

155SESSION_ENGINE = 'django.contrib.sessions.backends.cache' 

156 

157CORS_ALLOWED_ORIGINS = [ 

158 "http://localhost:19006",# Dev server 

159 "http://127.0.0.1:19006", 

160 "http://localhost:19005",# E2E Test server 

161 "http://127.0.0.1:19005", 

162 "http://localhost:3000", 

163 "http://front-end:19006", 

164 "http://172.29.171.74:19006", 

165 "https://ahuora.org.nz", # Production server 

166 "https://www.ahuora.org.nz", 

167 "http://ahuora.org.nz", 

168 "http://www.ahuora.org.nz" 

169] 

170 

171CORS_ALLOW_CREDENTIALS = True 

172 

173ROOT_URLCONF = 'CoreRoot.urls' 

174 

175TEMPLATES = [ 

176 { 

177 'BACKEND': 'django.template.backends.django.DjangoTemplates', 

178 'DIRS': [], 

179 'APP_DIRS': True, 

180 'OPTIONS': { 

181 'context_processors': [ 

182 'django.template.context_processors.debug', 

183 'django.template.context_processors.request', 

184 'django.contrib.auth.context_processors.auth', 

185 'django.contrib.messages.context_processors.messages', 

186 ], 

187 }, 

188 }, 

189] 

190 

191OPEN_TELEMETRY_TRACER_NAME = "ahuora-api" 

192 

193WSGI_APPLICATION = 'CoreRoot.wsgi.application' 

194 

195FIXTURE_DIRS = [ 

196 BASE_DIR / '/fixtures' 

197] 

198#Renewable Ninja Token 

199 

200RENEWABLES_NINJA_TOKEN = os.getenv("RENEWABLES_NINJA_TOKEN") 

201if not RENEWABLES_NINJA_TOKEN: 201 ↛ 208line 201 didn't jump to line 208 because the condition on line 201 was always true

202 warnings.warn("RENEWABLES_NINJA_TOKEN is not set in environment variables.", UserWarning) 

203 

204 

205# Database 

206# https://docs.djangoproject.com/en/4.2/ref/settings/#databases 

207 

208DATABASES = { 

209 'default': { 

210 'ENGINE': 'django.db.backends.postgresql', 

211 'NAME': os.getenv("POSTGRES_DB", "postgres"), 

212 'HOST': os.getenv("POSTGRES_HOST", "localhost"), 

213 'USER': os.getenv("POSTGRES_USER", "postgres"), 

214 'PASSWORD': os.getenv("POSTGRES_PASSWORD", "postgres"), 

215 'OPTIONS': { 

216 'isolation_level': IsolationLevel.READ_COMMITTED, 

217 } 

218 } 

219} 

220 

221LOGGING = { 

222 "version": 1, 

223 "disable_existing_loggers": False, 

224 "formatters": { 

225 "trace_formatter": { 

226 '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 

227 'datefmt': '%Y-%m-%d %H:%M:%S', # optional, default is '%Y-%m-%d %H:%M:%S' 

228 }, 

229 }, 

230 "handlers": { 

231 "console": { 

232 "class": "logging.StreamHandler", 

233 "formatter": "trace_formatter", 

234 }, 

235 }, 

236 "root": { 

237 "handlers": ["console"], 

238 "level": "INFO", 

239 }, 

240 "loggers": { 

241 "django": { 

242 "handlers": ["console"], 

243 "level": "INFO", 

244 "propagate": False, 

245 } 

246 }, 

247} 

248 

249# Password validation 

250# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators 

251 

252AUTH_PASSWORD_VALIDATORS = [ 

253 { 

254 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 

255 }, 

256 { 

257 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 

258 }, 

259 { 

260 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 

261 }, 

262 { 

263 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 

264 }, 

265] 

266 

267# Password hashers/KDFs to enable. 

268# The first entry will be used when creating new passwords. 

269PASSWORD_HASHERS = [ 

270 "django.contrib.auth.hashers.Argon2PasswordHasher" 

271] 

272 

273# Internationalization 

274# https://docs.djangoproject.com/en/4.2/topics/i18n/ 

275 

276LANGUAGE_CODE = 'en-us' 

277 

278TIME_ZONE = 'UTC' 

279 

280USE_I18N = True 

281 

282USE_TZ = True 

283 

284# Static files (CSS, JavaScript, Images) 

285# https://docs.djangoproject.com/en/4.2/howto/static-files/ 

286 

287STATIC_URL = 'static/' 

288 

289SILKY_META = True 

290 

291# Default primary key field type 

292# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field 

293 

294DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 

295 

296AUTH_USER_MODEL = 'authentication_user.User' 

297 

298AUTHENTICATION_BACKENDS = [ 

299 "authentication.remote_user_backend.RemoteUserBackendWithEmail", 

300] 

301 

302PLATFORM_TEST_EMAIL = os.getenv("PLATFORM_TEST_EMAIL", "test@ahuoratech.app") 

303 

304PLATFORM_ADMINISTRATORS_GROUP = os.getenv("PLATFORM_ADMINISTRATORS_GROUP", "/PlatformAdministrators") 

305PLATFORM_TESTERS_GROUP = os.getenv("PLATFORM_TESTERS_GROUP", "/PlatformTesters") 

306 

307REMOTE_USER_HEADER = "HTTP_X_AUTH_REQUEST_USER" 

308ASGI_REMOTE_USER_HEADER = "x-auth-request-user" 

309REMOTE_USER_EMAIL_HEADER = "HTTP_X_AUTH_REQUEST_EMAIL" 

310ASGI_REMOTE_USER_EMAIL_HEADER = "x-auth-request-email" 

311REMOTE_USER_GROUPS_HEADER = "HTTP_X_AUTH_REQUEST_GROUPS" 

312ASGI_REMOTE_USER_GROUPS_HEADER = "x-auth-request-groups" 

313REMOTE_USER_ACCESS_TOKEN_HEADER = "HTTP_X_AUTH_REQUEST_ACCESS_TOKEN" 

314ASGI_REMOTE_USER_ACCESS_TOKEN_HEADER = "x-auth-request-access-token" 

315 

316def __insert_middleware(middleware_name: str, before_middleware_name: str): 

317 index = MIDDLEWARE.index(before_middleware_name) 

318 MIDDLEWARE.insert(index, middleware_name) 

319 

320def set_dapr_endpoints(): 

321 # Monkey-patch Dapr SDK config to avoid the need to set environment variables externally when 

322 # running the API server. We still check for environment variables to allow for overriding. 

323 from dapr.conf import settings 

324 

325 if os.getenv("DAPR_HTTP_ENDPOINT") is None: 

326 settings.DAPR_HTTP_ENDPOINT = "http://localhost:3501" 

327 

328 if os.getenv("DAPR_GRPC_ENDPOINT") is None: 

329 settings.DAPR_GRPC_ENDPOINT = "localhost:50001" 

330 

331if DEBUG: 331 ↛ 348line 331 didn't jump to line 348 because the condition on line 331 was always true

332 set_dapr_endpoints() 

333 

334 # Add the dummy auth header middleware and the remote user middleware to the middleware list 

335 __insert_middleware( 

336 'authentication.middleware.dummy_auth_header_middleware', 

337 'authentication.middleware.AhuoraRemoteUserMiddleware' 

338 ) 

339 

340 # Only allow profiling with Silk if we're both in debug mode and profiling is enabled 

341 if PROFILING_ENABLED: 341 ↛ 342line 341 didn't jump to line 342 because the condition on line 341 was never true

342 __insert_middleware( 

343 'silk.middleware.SilkyMiddleware', 

344 'django.contrib.auth.middleware.AuthenticationMiddleware' 

345 ) 

346 INSTALLED_APPS.append('silk') 

347 

348BLEACH_ALLOWED_TAGS = [ 

349 'a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul', 'p', 'br', 'u' 

350] 

351BLEACH_ALLOWED_ATTRIBUTES = { 

352 '*': ['class', 'id', 'style'], 

353 'a': ['href', 'rel'], 

354} 

355BLEACH_ALLOWED_STYLES = [ 

356 'color', 'font-weight', 'text-decoration', 

357] 

358BLEACH_STRIP_TAGS = True 

359BLEACH_STRIP_COMMENTS = True