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

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 

22 

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

24 

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

26 

27 

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") 

30 

31# Quick-start development settings - unsuitable for production 

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

33 

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

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

36 

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

38 

39 

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 

43 

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

45 

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"] 

51 

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. 

59 

60 code_coverage_already_running = os.getenv("COVERAGE_PROCESS_CONFIG") 

61 

62 if code_coverage_already_running: 

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

64 else: 

65 import coverage 

66 

67 # Set default coverage config file name 

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

69 

70 coverage.process_startup() 

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

72 

73 if not DEBUG: 

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

75 

76# Application definition 

77 

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] 

98 

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} 

110 

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} 

128 

129SPECTACULAR_SETTINGS = { 

130 'TITLE': 'Ahuora API', 

131 'DESCRIPTION': 'Your project description', 

132 'VERSION': '1.0.0', 

133 'SERVE_INCLUDE_SCHEMA': False, 

134} 

135 

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] 

147 

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

149 

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] 

163 

164CORS_ALLOW_CREDENTIALS = True 

165 

166ROOT_URLCONF = 'CoreRoot.urls' 

167 

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] 

183 

184OPEN_TELEMETRY_TRACER_NAME = "ahuora-api" 

185 

186WSGI_APPLICATION = 'CoreRoot.wsgi.application' 

187 

188FIXTURE_DIRS = [ 

189 BASE_DIR / '/fixtures' 

190] 

191#Renewable Ninja Token 

192 

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) 

196 

197 

198# Database 

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

200 

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} 

213 

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} 

241 

242# Password validation 

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

244 

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] 

259 

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] 

265 

266# Internationalization 

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

268 

269LANGUAGE_CODE = 'en-us' 

270 

271TIME_ZONE = 'UTC' 

272 

273USE_I18N = True 

274 

275USE_TZ = True 

276 

277# Static files (CSS, JavaScript, Images) 

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

279 

280STATIC_URL = 'static/' 

281 

282SILKY_META = True 

283 

284# Default primary key field type 

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

286 

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

288 

289AUTH_USER_MODEL = 'authentication_user.User' 

290 

291AUTHENTICATION_BACKENDS = [ 

292 "authentication.remote_user_backend.RemoteUserBackendWithEmail", 

293] 

294 

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

296 

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" 

305 

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

307 index = MIDDLEWARE.index(before_middleware_name) 

308 MIDDLEWARE.insert(index, middleware_name) 

309 

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 

314 

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

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

317 

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

319 settings.DAPR_GRPC_ENDPOINT = "localhost:50001" 

320 

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

322 set_dapr_endpoints() 

323 

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 ) 

329 

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

337 

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