Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-25 08:29:11

0001 """
0002 Django settings for swf_monitor_project project.
0003 
0004 Generated by 'django-admin startproject' using Django 4.2.23.
0005 
0006 For more information on this file, see
0007 https://docs.djangoproject.com/en/4.2/topics/settings/
0008 
0009 For the full list of settings and their values, see
0010 https://docs.djangoproject.com/en/4.2/ref/settings/
0011 """
0012 
0013 from pathlib import Path
0014 import os
0015 from decouple import config
0016 
0017 # Build paths inside the project like this: BASE_DIR / 'subdir'.
0018 BASE_DIR = Path(__file__).resolve().parent.parent
0019 
0020 # Rucio client reads these from os.environ directly, so bridge them from the
0021 # .env-loaded config. No-op if unset (e.g. in dev).
0022 for _k in ("X509_USER_PROXY", "RUCIO_ACCOUNT", "RUCIO_HOST", "RUCIO_AUTH_HOST"):
0023     _v = config(_k, default=None)
0024     if _v:
0025         os.environ[_k] = _v
0026 
0027 
0028 # Quick-start development settings - unsuitable for production
0029 # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
0030 
0031 # SECURITY WARNING: keep the secret key used in production secret!
0032 # SECRET_KEY = "django-insecure--np2=%3ftuxijbpmtb8nf0=ux$4_%e)ye&l829((8olx&8z2*d"
0033 SECRET_KEY = config("SECRET_KEY")
0034 
0035 # SECURITY WARNING: don't run with debug turned on in production!
0036 # Set DEBUG to False in production by setting the environment variable DEBUG=False
0037 DEBUG = config('DEBUG', default=True, cast=bool)
0038 
0039 # Define allowed hosts. 
0040 # IMPORTANT: Use SWF_ALLOWED_HOSTS environment variable to configure allowed hosts.
0041 # This is a comma-separated list of domain names/IP addresses that can serve this application.
0042 # Example: SWF_ALLOWED_HOSTS=swf-monitor.example.com,www.swf-monitor.example.com,10.0.0.1
0043 # 
0044 # Security Note: Never hardcode production hostnames in this file. Always use environment variables
0045 # to avoid exposing infrastructure details in the codebase.
0046 ALLOWED_HOSTS = []
0047 
0048 # Get allowed hosts from environment variable SWF_ALLOWED_HOSTS
0049 allowed_hosts_str = config('SWF_ALLOWED_HOSTS', default='')
0050 if allowed_hosts_str:
0051     ALLOWED_HOSTS = [host.strip() for host in allowed_hosts_str.split(',')]
0052 elif DEBUG:
0053     # Only use default localhost values in development when SWF_ALLOWED_HOSTS is not set
0054     ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'testserver']
0055 else:
0056     # In production, SWF_ALLOWED_HOSTS must be set or Django will raise ImproperlyConfigured
0057     # This is a security feature to prevent the site from running with improper host validation
0058     pass
0059 
0060 
0061 # Application definition
0062 
0063 INSTALLED_APPS = [
0064     "daphne",  # Add daphne for ASGI server
0065     "channels",  # Add channels for WebSocket support
0066     # "mcp_app",  # Replaced by mcp_server (proper MCP spec implementation)
0067     "mcp_server",  # django-mcp-server for Model Context Protocol
0068     "oauth2_provider",  # OAuth2 authentication for MCP
0069     "django.contrib.admin",
0070     "django.contrib.auth",
0071     "django.contrib.contenttypes",
0072     "django.contrib.sessions",
0073     "django.contrib.messages",
0074     "django.contrib.staticfiles",
0075     "pcs",  # Physics Configuration System
0076     "monitor_app",  # Changed from "swf_monitor_project.monitor_app"
0077     "django_dbml",  # For schema diagram generation
0078     # Third-party apps
0079     "rest_framework",
0080     "drf_spectacular",
0081     "rest_framework.authtoken",
0082     "django_seed",
0083     "django_extensions",
0084 ]
0085 
0086 MIDDLEWARE = [
0087     "django.middleware.security.SecurityMiddleware",
0088     "whitenoise.middleware.WhiteNoiseMiddleware",
0089     "django.contrib.sessions.middleware.SessionMiddleware",
0090     "django.middleware.common.CommonMiddleware",
0091     "django.middleware.csrf.CsrfViewMiddleware",
0092     "monitor_app.middleware.MCPAuthMiddleware",  # Auth0 OAuth for MCP
0093     "django.contrib.auth.middleware.AuthenticationMiddleware",
0094     "monitor_app.middleware.TunnelAuthMiddleware",
0095     "django.contrib.messages.middleware.MessageMiddleware",
0096     "django.middleware.clickjacking.XFrameOptionsMiddleware",
0097 ]
0098 
0099 ROOT_URLCONF = "swf_monitor_project.urls"
0100 
0101 TEMPLATES = [
0102     {
0103         "BACKEND": "django.template.backends.django.DjangoTemplates",
0104         "DIRS": [os.path.join(BASE_DIR, 'templates')],
0105         "APP_DIRS": True,
0106         "OPTIONS": {
0107             "context_processors": [
0108                 "django.template.context_processors.debug",
0109                 "django.template.context_processors.request",
0110                 "django.contrib.auth.context_processors.auth",
0111                 "django.contrib.messages.context_processors.messages",
0112                 "monitor_app.middleware.tunnel_context",
0113             ],
0114         },
0115     },
0116 ]
0117 
0118 WSGI_APPLICATION = "swf_monitor_project.wsgi.application"
0119 ASGI_APPLICATION = "swf_monitor_project.asgi.application"  # Added ASGI application
0120 
0121 
0122 # Database
0123 # https://docs.djangoproject.com/en/5.0/ref/settings/#databases
0124 
0125 DATABASES = {
0126     "default": {
0127         "ENGINE": "django.db.backends.postgresql",
0128         "NAME": config("DB_NAME", default="swfdb"),
0129         "USER": config("DB_USER", default="admin"),
0130         "PASSWORD": config("DB_PASSWORD", default="dummy"),
0131         "HOST": config("DB_HOST", default="localhost"),
0132         "PORT": config("DB_PORT", default="5432"),
0133     },
0134     "panda": {
0135         "ENGINE": "django.db.backends.postgresql",
0136         "NAME": config("PANDA_DB_NAME", default="panda_db"),
0137         "USER": config("PANDA_DB_USER", default="panda"),
0138         "PASSWORD": config("PANDA_DB_PASSWORD", default="dummy"),
0139         "HOST": config("PANDA_DB_HOST", default="pandadb01.sdcc.bnl.gov"),
0140         "PORT": config("PANDA_DB_PORT", default="5432"),
0141     },
0142     "idds": {
0143         "ENGINE": "django.db.backends.postgresql",
0144         "NAME": config("IDDS_DB_NAME", default="idds_db"),
0145         "USER": config("IDDS_DB_USER", default="idds"),
0146         "PASSWORD": config("IDDS_DB_PASSWORD", default="dummy"),
0147         "HOST": config("IDDS_DB_HOST", default="pandadb01.sdcc.bnl.gov"),
0148         "PORT": config("IDDS_DB_PORT", default="5432"),
0149         "OPTIONS": {
0150             "options": f"-c search_path={config('IDDS_DB_SCHEMA', default='doma_idds')}"
0151         },
0152     }
0153 }
0154 
0155 
0156 # Password validation
0157 # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
0158 
0159 AUTH_PASSWORD_VALIDATORS = [
0160     {
0161         "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
0162     },
0163     {
0164         "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
0165     },
0166     {
0167         "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
0168     },
0169     {
0170         "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
0171     },
0172 ]
0173 
0174 
0175 # Internationalization
0176 # https://docs.djangoproject.com/en/4.2/topics/i18n/
0177 
0178 LANGUAGE_CODE = "en-us"
0179 
0180 TIME_ZONE = config('DISPLAY_TIMEZONE', default='America/New_York')
0181 
0182 USE_I18N = True
0183 
0184 USE_TZ = True
0185 
0186 
0187 # Subpath deployment configuration
0188 # For production deployments served under a subpath (e.g., /swf-monitor/)
0189 DEPLOYMENT_SUBPATH = config('SWF_DEPLOYMENT_SUBPATH', default='')
0190 
0191 if DEPLOYMENT_SUBPATH:
0192     # Production subpath deployment
0193     FORCE_SCRIPT_NAME = DEPLOYMENT_SUBPATH
0194     STATIC_URL = config('SWF_STATIC_URL_BASE', default=f'{DEPLOYMENT_SUBPATH}/static/')
0195     LOGIN_REDIRECT_URL = config('SWF_LOGIN_REDIRECT', default=f'{DEPLOYMENT_SUBPATH}/prod/')
0196     LOGOUT_REDIRECT_URL = config('SWF_LOGOUT_REDIRECT', default=DEPLOYMENT_SUBPATH + '/prod/')
0197 else:
0198     # Development defaults (no subpath)
0199     STATIC_URL = "static/"
0200     LOGIN_REDIRECT_URL = '/prod/'
0201     LOGOUT_REDIRECT_URL = '/prod/'
0202 
0203 # Static files (CSS, JavaScript, Images)
0204 # https://docs.djangoproject.com/en/4.2/howto/static-files/
0205 
0206 STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
0207 STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
0208 
0209 # WhiteNoise configuration
0210 STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
0211 
0212 LOGIN_URL = 'login'
0213 
0214 
0215 # Default primary key field type
0216 # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
0217 
0218 DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
0219 
0220 
0221 # Django REST framework settings
0222 REST_FRAMEWORK = {
0223     # Use Django's standard `django.contrib.auth` permissions,
0224     # or allow read-only access for unauthenticated users.
0225     "DEFAULT_PERMISSION_CLASSES": [
0226         "rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly"
0227     ],
0228     "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
0229     "DEFAULT_RENDERER_CLASSES": [
0230         "rest_framework.renderers.JSONRenderer",
0231         "rest_framework.renderers.BrowsableAPIRenderer",
0232     ],
0233 }
0234 
0235 SPECTACULAR_SETTINGS = {
0236     "TITLE": "SWF Monitor API",
0237     "DESCRIPTION": "API for the ePIC Streaming Workflow Testbed Monitor",
0238     "VERSION": "1.0.0",
0239     "SERVE_INCLUDE_SCHEMA": False,
0240     # OTHER SETTINGS
0241 }
0242 
0243 # OAuth2 Provider settings for MCP authentication
0244 OAUTH2_PROVIDER = {
0245     "SCOPES": {
0246         "read": "Read access to MCP tools",
0247         "write": "Write access to MCP tools",
0248     },
0249     "ACCESS_TOKEN_EXPIRE_SECONDS": 3600,
0250 }
0251 
0252 # Auth0 OAuth 2.1 Configuration (for Claude.ai MCP integration)
0253 # Leave empty to disable OAuth and allow direct MCP access
0254 AUTH0_DOMAIN = config("AUTH0_DOMAIN", default="")
0255 AUTH0_CLIENT_ID = config("AUTH0_CLIENT_ID", default="")
0256 AUTH0_CLIENT_SECRET = config("AUTH0_CLIENT_SECRET", default="")
0257 AUTH0_API_IDENTIFIER = config("AUTH0_API_IDENTIFIER", default="")
0258 AUTH0_ALGORITHMS = ["RS256"]
0259 
0260 # Django MCP Server configuration
0261 DJANGO_MCP_GLOBAL_SERVER_CONFIG = {
0262     "name": "swf-testbed",
0263     "instructions": """Streaming workflow orchestration testbed for the ePIC experiment at the Electron Ion Collider.
0264 
0265 KEY CONCEPTS:
0266 - Namespaces: Isolation boundaries for different users' workflow runs (e.g., 'torre1', 'wenauseic')
0267 - Runs: Data-taking periods identified by run_number, containing multiple STF files
0268 - STF files: Super Time Frame files - primary data units from the detector DAQ system
0269 - TF slices: Small processing units (~15 per STF) for fast monitoring workflow
0270 - Agents: Processes that execute workflows (daq_simulator, data_agent, processing_agent, etc.)
0271 - Workflow executions: Instances of running workflows, tracked by execution_id
0272 
0273 COMMON QUERIES:
0274 - What's running now? → swf_list_workflow_executions(currently_running=True)
0275 - Any errors? → swf_list_logs(level='ERROR')
0276 - System health? → swf_get_system_state()
0277 - Activity in a namespace? → swf_get_namespace(namespace='name')
0278 - Failed workflows? → swf_list_workflow_executions(status='failed')
0279 - Send announcement/test message? → swf_send_message(message='...', message_type='announcement')
0280 - What's PanDA doing? → panda_get_activity() — compact overview, no individual records
0281 - PanDA task overview? → panda_list_tasks(days=7)
0282 - Failed PanDA tasks? → panda_list_tasks(status='failed')
0283 - EIC experiment tasks? → panda_list_tasks(workinggroup='EIC')
0284 - Production tasks? → panda_list_tasks(processingtype='epicproduction')
0285 - Top errors? → panda_error_summary(days=7)
0286 - Errors for a user? → panda_error_summary(username='someone')
0287 - Deep dive on a failed job? → panda_study_job(pandaid=130497)
0288 - EIC queues? → panda_list_queues(vo='eic')
0289 - Queue config? → panda_get_queue(panda_queue='NERSC_Perlmutter_epic')
0290 - Core-hours this month? → panda_resource_usage(days=30)
0291 - Core-hours on Perlmutter? → panda_resource_usage(days=30, site='NERSC_Perlmutter%')
0292 - What is PCS? → PCS = Physics Configuration System, manages configuration of production tasks based on physics inputs
0293 - List physics tags? → pcs_list_tags(tag_type='p')
0294 - What is tag p1001? → pcs_get_tag(tag_label='p1001')
0295 - Reco tags? → pcs_list_tags(tag_type='r')
0296 - Photoproduction tags? → pcs_search_tags(query='photoproduction')
0297 - DIS tags? → pcs_list_tags(tag_type='p', category='DIS')
0298 - Tags using pythia8? → pcs_search_tags(query='pythia8')
0299 
0300 PCS (Physics Configuration System):
0301 PCS manages the configuration of production tasks based on physics inputs for ePIC Monte Carlo simulation campaigns. Configurations are
0302 organized as tags — named parameter sets for each pipeline stage:
0303 - Physics tags (p): process, beam energies, species, Q2 range (e.g. p1001 = DIS NC 10x100 ep)
0304 - EvGen tags (e): event generator and version (e.g. e1 = pythia8 8.310)
0305 - Simu tags (s): detector simulation config (e.g. s1 = npsim 26.02.0)
0306 - Reco tags (r): reconstruction config (e.g. r1 = eicrecon 26.02.0)
0307 Physics tags are grouped by category: DIS (p1xxx), DVCS (p2xxx), SIDIS (p3xxx), EXCLUSIVE (p4xxx).
0308 Tags are draft (editable) or locked (immutable, for production use).
0309 
0310 AFTER swf_start_workflow — ACTIVELY POLL, DO NOT SLEEP:
0311 Poll swf_get_workflow_monitor(execution_id) every 10-15s until completion.
0312 Report progress to user as it evolves. Check swf_list_logs(level='ERROR') after.
0313 
0314 FILTERING:
0315 - All list tools support start_time/end_time parameters (ISO datetime strings)
0316 - Status filters are case-insensitive
0317 - Context cascades: run_number → stf_filename → tf_filename
0318 
0319 Use swf_list_available_tools() to see all available tools with descriptions.""",
0320 }
0321 
0322 # MCP endpoint path (empty string since we mount at /mcp/ in urls.py)
0323 DJANGO_MCP_ENDPOINT = ""
0324 
0325 # MCP authentication - start with no auth for development, enable OAuth2 for production
0326 # DJANGO_MCP_AUTHENTICATION_CLASSES = [
0327 #     "oauth2_provider.contrib.rest_framework.OAuth2Authentication",
0328 # ]
0329 
0330 # ActiveMQ Settings
0331 ACTIVEMQ_HOST = config('ACTIVEMQ_HOST', default='localhost')
0332 ACTIVEMQ_PORT = config('ACTIVEMQ_PORT', default=61612, cast=int)
0333 ACTIVEMQ_USER = config('ACTIVEMQ_USER', default='admin')
0334 ACTIVEMQ_PASSWORD = config('ACTIVEMQ_PASSWORD', default='admin')
0335 ACTIVEMQ_HEARTBEAT_TOPIC = config('ACTIVEMQ_HEARTBEAT_TOPIC', default='/topic/heartbeat') # Updated to working topic
0336 ACTIVEMQ_USE_SSL = config('ACTIVEMQ_USE_SSL', default=False, cast=bool)
0337 ACTIVEMQ_SSL_CERT_FILE = config('ACTIVEMQ_SSL_CERT_FILE', default='')
0338 ACTIVEMQ_SSL_KEY_FILE = config('ACTIVEMQ_SSL_KEY_FILE', default='')
0339 ACTIVEMQ_SSL_CA_CERTS = config('ACTIVEMQ_SSL_CA_CERTS', default='')
0340 
0341 # Channel layer settings
0342 # Use Redis in production if REDIS_URL is set; otherwise fall back to in-memory (single process only)
0343 REDIS_URL = config('REDIS_URL', default='')
0344 if REDIS_URL:
0345     CHANNEL_LAYERS = {
0346         "default": {
0347             "BACKEND": "channels_redis.core.RedisChannelLayer",
0348             "CONFIG": {
0349                 "hosts": [REDIS_URL],
0350             },
0351         }
0352     }
0353 else:
0354     CHANNEL_LAYERS = {
0355         "default": {
0356             "BACKEND": "channels.layers.InMemoryChannelLayer"
0357         }
0358     }
0359 
0360 # SSE relay group name
0361 SSE_CHANNEL_GROUP = config('SSE_CHANNEL_GROUP', default='workflow_events')
0362 
0363 # Basic Logging Configuration
0364 # Set DJANGO_LOGGING_MODE='none' to disable all logging configuration (useful for schema generation, etc.)
0365 LOGGING_MODE = os.getenv('DJANGO_LOGGING_MODE', 'normal')
0366 
0367 if LOGGING_MODE == 'none':
0368     # Minimal logging configuration with no external dependencies
0369     LOGGING = {
0370         'version': 1,
0371         'disable_existing_loggers': False,
0372         'handlers': {
0373             'null': {
0374                 'class': 'logging.NullHandler',
0375             },
0376         },
0377         'root': {
0378             'handlers': ['null'],
0379         },
0380     }
0381 else:
0382     # Normal logging configuration
0383     LOGGING = {
0384         'version': 1,
0385         'disable_existing_loggers': False,
0386         'formatters': {
0387             'json': {
0388                 '()': 'pythonjsonlogger.json.JsonFormatter',
0389                 'format': '%(asctime)s %(name)s %(levelname)s %(module)s %(funcName)s %(lineno)d %(message)s',
0390                 'rename_fields': {
0391                     'funcName': 'funcname'
0392                 }
0393             },
0394         },
0395         'handlers': {
0396             'console': {
0397                 'class': 'logging.StreamHandler',
0398                 'formatter': 'json',
0399             },
0400             'rest': {
0401                 'class': 'swf_common_lib.logging_utils.RestLogHandler',
0402                 'url': 'http://localhost:8002/api/logs/',
0403                 'formatter': 'json',
0404             },
0405             'db': {
0406                 'class': 'monitor_app.db_log_handler.DbLogHandler',
0407                 'level': 'INFO',
0408             },
0409         },
0410         'root': {
0411             'handlers': ['console'],
0412             'level': 'INFO',
0413         },
0414         'loggers': {
0415             'django': {
0416                 'handlers': ['console'],
0417                 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
0418                 'propagate': False,
0419             },
0420             'monitor_app': {
0421                 'handlers': ['db', 'console'],
0422                 'level': 'INFO',
0423                 'propagate': False,
0424             },
0425         },
0426     }