Coverage for sonar_backend/settings.py: 91%
67 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-10 03:22 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-10 03:22 +0000
1"""
2Django settings for sonar_backend project.
4Generated by 'django-admin startproject' using Django 4.2.4.
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"""
13from datetime import datetime
14import logging
15import os
16from pathlib import Path
18import environ
20from sonar_backend.utils import CustomisedJSONFormatter
22# Initialise environment variables
24env = environ.Env(
25 DEBUG=(bool, False),
26 POSTGRES_USER=str,
27 POSTGRES_PASSWORD=str,
28 POSTGRES_DB=str,
29 POSTGRES_HOST=str,
30 POSTGRES_PORT=str,
31 SECRET_KEY=str,
32 SONAR_DATA_ENTRY_FOLDER=(str, None),
33 SONAR_DATA_PROCESSING_FOLDER=(str, None),
34 SONAR_DATA_ARCHIVE=(str, None),
35 REDIS_URL=(str, None),
36 ALLOWED_HOSTS=(str, None),
37 CORS_ALLOWED_ORIGINS=(str, "http://localhost:5173"),
38 SAMPLE_BATCH_SIZE=(int, 10),
39 PROPERTY_BATCH_SIZE=(int, 1000),
40 PROFILE_IMPORT=(bool, False),
41 KEEP_IMPORTED_DATA_FILES=(bool, False),
42)
44# Build paths inside the project like this: BASE_DIR / 'subdir'.
45BASE_DIR = Path(__file__).resolve().parent.parent
46PROJECT_ROOT = Path(__file__).resolve().parent
47STATIC_ROOT = BASE_DIR / "sonar_server/test/static"
49# Take environment variables from .env file
50environ.Env.read_env(BASE_DIR / ".env")
52# Quick-start development settings - unsuitable for production
53# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
55# SECURITY WARNING: keep the secret key used in production secret!
57SECRET_KEY = env("SECRET_KEY")
59# SECURITY WARNING: don't run with debug turned on in production!
60DEBUG = env("DEBUG")
61# DEBUG = True
63ALLOWED_HOSTS = env("ALLOWED_HOSTS", "").split(",")
65# Application definition
67INSTALLED_APPS = [
68 "corsheaders",
69 "django.contrib.admin",
70 "django.contrib.auth",
71 "django.contrib.contenttypes",
72 "django.contrib.sessions",
73 "django.contrib.messages",
74 "django.contrib.staticfiles",
75 "environ",
76 "rest_api.apps.RestApiConfig",
77 "rest_framework",
78 "django_filters",
79 "django_apscheduler",
80]
81REST_FRAMEWORK = {
82 "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
83 "PAGE_SIZE": 100,
84}
85MIDDLEWARE = [
86 "django.middleware.security.SecurityMiddleware",
87 "whitenoise.middleware.WhiteNoiseMiddleware",
88 "django.contrib.sessions.middleware.SessionMiddleware",
89 "corsheaders.middleware.CorsMiddleware",
90 "django.middleware.common.CommonMiddleware",
91 "django.middleware.csrf.CsrfViewMiddleware",
92 "django.contrib.auth.middleware.AuthenticationMiddleware",
93 "django.contrib.messages.middleware.MessageMiddleware",
94 "django.middleware.clickjacking.XFrameOptionsMiddleware",
95]
96ROOT_URLCONF = "sonar_backend.urls"
97CORS_ALLOWED_ORIGINS = [
98 origin.strip()
99 for origin in env("CORS_ALLOWED_ORIGINS", default="http://localhost:5173").split(
100 ","
101 )
102 if origin.strip()
103]
104TEMPLATES = [
105 {
106 "BACKEND": "django.template.backends.django.DjangoTemplates",
107 "DIRS": [],
108 "APP_DIRS": True,
109 "OPTIONS": {
110 "context_processors": [
111 "django.template.context_processors.debug",
112 "django.template.context_processors.request",
113 "django.contrib.auth.context_processors.auth",
114 "django.contrib.messages.context_processors.messages",
115 ],
116 },
117 },
118]
120WSGI_APPLICATION = "sonar_backend.wsgi.application"
123# Database
124# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
126# NOTE: The `env.db()` method is an alias for `db_url()`.
127# We can use the code like `if not env.db()`.
128# However, we need to set the `DATABASE_URL` in the environment; otherwise,
129# we will get a message like the one below:
130# UserWarning: Engine not recognized from the URL: {'NAME': '', 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', 'ENGINE': ''}
131# As of now, we just ignore the UserWarning message and still set it as a dictionary
132# by using `env("DATABASE_URL")` this way.
133if not os.environ.get("DATABASE_URL"):
134 print("Using the default database.")
135 database_connection = {
136 "ENGINE": "django.db.backends.postgresql",
137 "OPTIONS": {"options": "-c search_path=public"},
138 "NAME": env("POSTGRES_DB"),
139 "USER": env("POSTGRES_USER"),
140 "PASSWORD": env("POSTGRES_PASSWORD"),
141 "HOST": env("POSTGRES_HOST"),
142 "PORT": env("POSTGRES_PORT"),
143 }
144else:
145 print("Using the provided database.")
146 database_connection = env.db("DATABASE_URL")
148DATABASES = {"default": database_connection}
151# Password validation
152# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
154AUTH_PASSWORD_VALIDATORS = [
155 {
156 "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
157 },
158 {
159 "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
160 },
161 {
162 "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
163 },
164 {
165 "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
166 },
167]
170# Internationalization
171# https://docs.djangoproject.com/en/4.2/topics/i18n/
173LANGUAGE_CODE = "en-us"
175TIME_ZONE = "UTC"
177USE_I18N = True
179USE_TZ = True
182# Static files (CSS, JavaScript, Images)
183# https://docs.djangoproject.com/en/4.2/howto/static-files/
185STATIC_URL = "static/"
187# Default primary key field type
188# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
190DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
193DATA_UPLOAD_MAX_MEMORY_SIZE = None
195APSCHEDULER_RUN_NOW_TIMEOUT = 60 * 60 * 5
197# SONAR APP
198PROFILE_IMPORT = env("PROFILE_IMPORT")
199REDIS_URL = env("REDIS_URL")
201SAMPLE_BATCH_SIZE = env("SAMPLE_BATCH_SIZE")
202PROPERTY_BATCH_SIZE = env("PROPERTY_BATCH_SIZE")
204SONAR_DATA_ENTRY_FOLDER = (
205 env("SONAR_DATA_ENTRY_FOLDER")
206 if env("SONAR_DATA_ENTRY_FOLDER")
207 else os.path.join(BASE_DIR, "import_data")
208)
209SONAR_DATA_PROCESSING_FOLDER = (
210 env("SONAR_DATA_PROCESSING_FOLDER")
211 if env("SONAR_DATA_PROCESSING_FOLDER")
212 else os.path.join(BASE_DIR, "processing_data")
213)
214SONAR_DATA_ARCHIVE = (
215 env("SONAR_DATA_ARCHIVE")
216 if env("SONAR_DATA_ARCHIVE")
217 else os.path.join(BASE_DIR, "archive_data")
218)
220# Check if the directory already exists
221if not os.path.exists(SONAR_DATA_ENTRY_FOLDER):
222 os.makedirs(SONAR_DATA_ENTRY_FOLDER, exist_ok=True)
223if not os.path.exists(SONAR_DATA_PROCESSING_FOLDER):
224 os.makedirs(SONAR_DATA_PROCESSING_FOLDER, exist_ok=True)
225if not os.path.exists(SONAR_DATA_ARCHIVE):
226 os.makedirs(SONAR_DATA_ARCHIVE, exist_ok=True)
228# ------------------------------------------
230LOGGER = logging.getLogger(__name__)
232LOG_PATH = env("LOG_PATH")
233LOG_LEVEL = env("LOG_LEVEL")
234if not os.path.exists(LOG_PATH):
235 os.makedirs(LOG_PATH, exist_ok=True)
236LOGGING = {
237 "version": 1,
238 "disable_existing_loggers": False,
239 "formatters": {
240 "json": {
241 "()": CustomisedJSONFormatter,
242 },
243 },
244 "handlers": {
245 "console": {
246 "class": "logging.StreamHandler",
247 },
248 "app_log_file": {
249 "level": LOG_LEVEL,
250 "class": "logging.FileHandler",
251 "filename": os.path.join(
252 LOG_PATH, f"{datetime.today().strftime('%Y_%m_%d')}.log.json"
253 ),
254 "formatter": "json",
255 },
256 },
257 "loggers": {
258 "": {
259 "level": LOG_LEVEL,
260 "handlers": ["console", "app_log_file"],
261 },
262 },
263}
265PERMISSION_RELEVANT_USER_GROUPS = ["admin", "read_only"]
267CACHES = {
268 "default": {
269 "BACKEND": "django_redis.cache.RedisCache",
270 "LOCATION": f"{env('REDIS_URL')}1",
271 "OPTIONS": {
272 "CLIENT_CLASS": "django_redis.client.DefaultClient",
273 },
274 }
275}
276CACHE_OBJECT_TTL = env.int(
277 "CACHE_OBJECT_TTL", default=3600
278) # (default: Time to Live 60 minutes)
280# If true, keep the data files that were sent from the CLI in an archive
281# folder, even after that data has been imported into the database.
282# Defaults to false, so the files are immediately deleted to save space.
283KEEP_IMPORTED_DATA_FILES = env("KEEP_IMPORTED_DATA_FILES")
285# Celery settings
286CELERY_BROKER_URL = f"{env('REDIS_URL')}0"
287CELERY_RESULT_BACKEND = f"{env('REDIS_URL')}0"
289if DEBUG:
290 INTERNAL_IPS = ("127.0.0.1",)
291 INSTALLED_APPS += ["debug_toolbar"]
292 MIDDLEWARE.insert(5, "debug_toolbar.middleware.DebugToolbarMiddleware")
293 DEBUG_TOOLBAR_CONFIG = {
294 "SHOW_TOOLBAR_CALLBACK": lambda request: True,
295 }
296 print(DATABASES)