Files
enviPy-bayer/envipath/settings.py
Tim Lorsbach d9530ce755 adjusted migration
Initial bayer app

Show Pack Classification

Adjusted docker compose to bayer specifics

Adjusted Dockerfile for Bayer

Adding secret flags to group, add secret pools to packages

Adjusted View for Package creation

Prep configs, added Package Create Modal

wip

More on PES

wip

wip
2026-05-12 13:16:39 +02:00

477 lines
14 KiB
Python

"""
Django settings for envipath project.
Generated by 'django-admin startproject' using Django 4.2.17.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
import json
import os
from pathlib import Path
from dotenv import load_dotenv
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
ENV_PATH = os.environ.get("ENV_PATH", BASE_DIR / ".env.dev")
print(f"Loading env from {ENV_PATH}")
load_dotenv(ENV_PATH, override=False)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("SECRET_KEY", "secret-key")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get("DEBUG", "False") == "True"
ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.postgres",
# 3rd party
"django_extensions",
"oauth2_provider",
# Custom
"epapi", # API endpoints (v1, etc.)
"epdb",
"migration",
]
TENANT = os.environ.get("TENANT", "public")
if TENANT != "public":
INSTALLED_APPS.append(TENANT)
EPDB_PACKAGE_MODEL = os.environ.get("EPDB_PACKAGE_MODEL", "epdb.Package")
def GET_PACKAGE_MODEL():
from django.apps import apps
return apps.get_model(EPDB_PACKAGE_MODEL)
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"oauth2_provider.middleware.OAuth2TokenMiddleware",
]
OAUTH2_PROVIDER = {
"PKCE_REQUIRED": False, # Accept PKCE requests but dont require them
}
if os.environ.get("REGISTRATION_MANDATORY", False) == "True":
MIDDLEWARE.append("epdb.middleware.login_required_middleware.LoginRequiredMiddleware")
ROOT_URLCONF = "envipath.urls"
TEMPLATE_DIRS = [
os.path.join(BASE_DIR, "templates"),
]
# If we have a non-public tenant, we might need to overwrite some templates
# search TENANT folder first...
if TENANT != "public":
TEMPLATE_DIRS.insert(0, os.path.join(BASE_DIR, TENANT, "templates"))
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": TEMPLATE_DIRS,
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"epdb.context_processors.package_context",
],
},
},
]
ALLOWED_HTML_TAGS = {"b", "i", "u", "br", "em", "mark", "p", "s", "strong"}
WSGI_APPLICATION = "envipath.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"USER": os.environ["POSTGRES_USER"],
"NAME": os.environ["POSTGRES_DB"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
"HOST": os.environ["POSTGRES_SERVICE_NAME"],
"PORT": os.environ["POSTGRES_PORT"],
}
}
if os.environ.get("USE_TEMPLATE_DB", False) == "True":
DATABASES["default"]["TEST"] = {
"NAME": f"test_{os.environ['TEMPLATE_DB']}",
"TEMPLATE": os.environ["TEMPLATE_DB"],
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
EMAIL_SUBJECT_PREFIX = "[enviPath] "
if DEBUG:
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
else:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_USE_TLS = True
EMAIL_HOST = "mail.gandi.net"
EMAIL_HOST_USER = os.environ["EMAIL_HOST_USER"]
EMAIL_HOST_PASSWORD = os.environ["EMAIL_HOST_PASSWORD"]
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = os.environ["DEFAULT_FROM_EMAIL"]
SERVER_EMAIL = os.environ["SERVER_EMAIL"]
AUTH_USER_MODEL = "epdb.User"
ADMIN_APPROVAL_REQUIRED = os.environ.get("ADMIN_APPROVAL_REQUIRED", "False") == "True"
# # SESAME
# SESAME_MAX_AGE = 300
# # TODO set to "home"
# LOGIN_REDIRECT_URL = "/"
SERVER_HOST = os.environ.get("SERVER_URL", "http://localhost:8000")
SERVER_PATH = os.environ.get("SERVER_PATH", "")
SERVER_URL = SERVER_HOST
if SERVER_PATH:
SERVER_URL = os.path.join(SERVER_HOST, SERVER_PATH)
LOGIN_URL = "/login/"
if SERVER_PATH:
LOGIN_URL = f"/{SERVER_PATH}/login/"
CSRF_TRUSTED_ORIGINS = [SERVER_HOST]
AMBIT_URL = "http://localhost:9001"
DEFAULT_VALUES = {"description": "no description"}
EP_DATA_DIR = os.environ["EP_DATA_DIR"]
if not os.path.exists(EP_DATA_DIR):
os.mkdir(EP_DATA_DIR)
MODEL_DIR = os.path.join(EP_DATA_DIR, "models")
if not os.path.exists(MODEL_DIR):
os.mkdir(MODEL_DIR)
STATIC_DIR = os.path.join(EP_DATA_DIR, "static")
if not os.path.exists(STATIC_DIR):
os.mkdir(STATIC_DIR)
LOG_DIR = os.path.join(EP_DATA_DIR, "log")
if not os.path.exists(LOG_DIR):
os.mkdir(LOG_DIR)
PLUGIN_DIR = os.path.join(EP_DATA_DIR, "plugins")
if not os.path.exists(PLUGIN_DIR):
os.mkdir(PLUGIN_DIR)
API_PAGINATION_DEFAULT_PAGE_SIZE = int(os.environ.get("API_PAGINATION_DEFAULT_PAGE_SIZE", 50))
PAGINATION_MAX_PER_PAGE_SIZE = int(
os.environ.get("API_PAGINATION_MAX_PAGE_SIZE", 100)
) # Ninja override
# Set this as our static root dir
STATIC_ROOT = STATIC_DIR
STATIC_URL = "/static/"
if SERVER_PATH:
STATIC_URL = f"/{SERVER_PATH}/static/"
# Where the sources are stored...
STATICFILES_DIRS = (BASE_DIR / "static",)
FIXTURE_DIRS = (BASE_DIR / "fixtures",)
# Logging
LOGGING = {
"version": 1,
"disable_existing_loggers": True,
"formatters": {
"simple": {
"format": "[%(asctime)s] %(levelname)s %(module)s - %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"handlers": {
"console": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "simple",
},
"file": {
"level": "DEBUG", # Or higher
"class": "logging.FileHandler",
"filename": os.path.join(LOG_DIR, "debug.log"),
"formatter": "simple",
},
},
"loggers": {
# For everything under epdb/ loaded via getlogger(__name__)
"epdb": {
"handlers": ["file"], # "console",
"propagate": True,
"level": os.environ.get("LOG_LEVEL", "INFO"),
},
# For everything under envipath/ loaded via getlogger(__name__)
"envipath": {
"handlers": ["file", "console"],
"propagate": True,
"level": os.environ.get("LOG_LEVEL", "INFO"),
},
# For everything under utilities/ loaded via getlogger(__name__)
"utilities": {
"handlers": ["file", "console"],
"propagate": True,
"level": os.environ.get("LOG_LEVEL", "INFO"),
},
},
}
# Flags
ENVIFORMER_PRESENT = os.environ.get("ENVIFORMER_PRESENT", "False") == "True"
ENVIFORMER_DEVICE = os.environ.get("ENVIFORMER_DEVICE", "cpu")
# If celery is not present set always eager to true which will cause delayed tasks to block until finished
FLAG_CELERY_PRESENT = os.environ.get("FLAG_CELERY_PRESENT", "False") == "True"
if not FLAG_CELERY_PRESENT:
CELERY_TASK_ALWAYS_EAGER = True
# Celery Configuration Options
CELERY_TIMEZONE = "Europe/Berlin"
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL", "redis://localhost:6379/0")
CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND", "redis://localhost:6379/1")
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"
MODEL_BUILDING_ENABLED = os.environ.get("MODEL_BUILDING_ENABLED", "False") == "True"
APPLICABILITY_DOMAIN_ENABLED = os.environ.get("APPLICABILITY_DOMAIN_ENABLED", "False") == "True"
DEFAULT_RF_MODEL_PARAMS = {
"base_clf": RandomForestClassifier(
n_estimators=100,
max_features="log2",
random_state=42,
criterion="entropy",
ccp_alpha=0.0,
max_depth=3,
min_samples_leaf=1,
),
"num_chains": 10,
}
DEFAULT_MODEL_PARAMS = {
"base_clf": DecisionTreeClassifier(
criterion="entropy",
max_depth=3,
min_samples_split=5,
# min_samples_leaf=5,
max_features="sqrt",
# class_weight='balanced',
random_state=42,
),
"num_chains": 10,
}
DEFAULT_MAX_NUMBER_OF_NODES = 50
DEFAULT_MAX_DEPTH = 8
DEFAULT_MODEL_THRESHOLD = 0.25
# Loading Plugins
PLUGINS_ENABLED = os.environ.get("PLUGINS_ENABLED", "False") == "True"
BASE_PLUGINS = os.environ.get("BASE_PLUGINS", None)
if BASE_PLUGINS:
BASE_PLUGINS = BASE_PLUGINS.split(",")
else:
BASE_PLUGINS = []
CLASSIFIER_PLUGINS = {}
PROPERTY_PLUGINS = {}
DESCRIPTOR_PLUGINS = {}
SENTRY_ENABLED = os.environ.get("SENTRY_ENABLED", "False") == "True"
if SENTRY_ENABLED:
import sentry_sdk
def before_send(event, hint):
# Check if was a handled exception by one of our loggers
if event.get("logger"):
for log_path in LOGGING.get("loggers").keys():
if event["logger"].startswith(log_path):
return None
return event
sentry_sdk.init(
dsn=os.environ.get("SENTRY_DSN"),
# Add data like request headers and IP for users,
# see https://docs.sentry.io/platforms/python/data-management/data-collected/ for more info
send_default_pii=True,
environment=os.environ.get("SENTRY_ENVIRONMENT", "development"),
before_send=before_send,
)
IUCLID_EXPORT_ENABLED = os.environ.get("IUCLID_EXPORT_ENABLED", "False") == "True"
if IUCLID_EXPORT_ENABLED:
INSTALLED_APPS.append("epiuclid")
# compile into digestible flags
FLAGS = {
"MODEL_BUILDING": MODEL_BUILDING_ENABLED,
"CELERY": FLAG_CELERY_PRESENT,
"PLUGINS": PLUGINS_ENABLED,
"SENTRY": SENTRY_ENABLED,
"ENVIFORMER": ENVIFORMER_PRESENT,
"APPLICABILITY_DOMAIN": APPLICABILITY_DOMAIN_ENABLED,
"IUCLID_EXPORT": IUCLID_EXPORT_ENABLED,
}
# path of the URL are checked via "startswith"
# -> /password_reset/done is covered as well
LOGIN_EXEMPT_URLS = [
"/register",
"/api/v1/", # Let API handle its own authentication
"/api/legacy/",
"/o/token/",
"/o/userinfo/",
"/password_reset/",
"/reset/",
"/terms",
"/privacy",
"/cookie-policy",
"/about",
"/contact",
"/careers",
"/cite",
"/legal",
"/entra/",
"/auth/",
]
if SERVER_PATH:
LOGIN_EXEMPT_URLS = [f"/{SERVER_PATH}{x}" for x in LOGIN_EXEMPT_URLS]
# MS AD/Entra
MS_ENTRA_ENABLED = os.environ.get("MS_ENTRA_ENABLED", "False") == "True"
if MS_ENTRA_ENABLED:
# Add app to installed apps
INSTALLED_APPS.append("epauth")
# Set vars required by app
MS_ENTRA_CLIENT_ID = os.environ["MS_CLIENT_ID"]
MS_ENTRA_CLIENT_SECRET = os.environ["MS_CLIENT_SECRET"]
MS_ENTRA_TENANT_ID = os.environ["MS_TENANT_ID"]
MS_ENTRA_AUTHORITY = f"https://login.microsoftonline.com/{MS_ENTRA_TENANT_ID}"
MS_ENTRA_REDIRECT_URI = os.environ["MS_REDIRECT_URI"]
MS_ENTRA_SCOPES = os.environ.get("MS_SCOPES", "").split(",")
# Site ID 10 -> beta.envipath.org
MATOMO_SITE_ID = os.environ.get("MATOMO_SITE_ID", "10")
# CAP
CAP_ENABLED = os.environ.get("CAP_ENABLED", "False") == "True"
CAP_API_BASE = os.environ.get("CAP_API_BASE", None)
CAP_SITE_KEY = os.environ.get("CAP_SITE_KEY", None)
CAP_SECRET_KEY = os.environ.get("CAP_SECRET_KEY", None)
# Biotransformer
BIOTRANSFORMER_ENABLED = os.environ.get("BIOTRANSFORMER_ENABLED", "False") == "True"
FLAGS["BIOTRANSFORMER"] = BIOTRANSFORMER_ENABLED
if BIOTRANSFORMER_ENABLED:
BIOTRANSFORMER_URL = os.environ.get("BIOTRANSFORMER_URL", None)
# PES
PES_API_MAPPING = os.environ.get("PES_API_MAPPING", None)
if PES_API_MAPPING:
import json
PES_API_MAPPING = json.loads(PES_API_MAPPING)
else:
PES_API_MAPPING = {}
# Entra Groups
ENTRA_GROUPS = os.environ.get("ENTRA_GROUPS", None)
if ENTRA_GROUPS:
import json
ENTRA_GROUPS = json.loads(ENTRA_GROUPS)
else:
ENTRA_GROUPS = {}
ENTRA_SECRET_GROUPS = os.environ.get("ENTRA_SECRET_GROUPS", None)
if ENTRA_SECRET_GROUPS:
import json
ENTRA_SECRET_GROUPS = json.loads(ENTRA_SECRET_GROUPS)
else:
ENTRA_SECRET_GROUPS = {}
# PES Data Pools vs Entra Mapping
DATA_POOL_MAPPING = os.environ.get("DATA_POOL_MAPPING", None)
if DATA_POOL_MAPPING:
import json
DATA_POOL_MAPPING = json.loads(DATA_POOL_MAPPING)
else:
DATA_POOL_MAPPING = {}