1#!/usr/bin/env python3
2
3"""Define the baseline settings available to all projects.
4
5All settings can be overridden by projects, which can also introduce new settings.
6Every setting should be commented where first used.
7"""
8
9import re
10import os
11import sys
12import time
13import runpy
14import socket
15import importlib
16from datetime import datetime
17from datetime import timedelta
18from typing import Optional
19from typing import Union
20
21try:
22 import dotenv
23except ImportError:
24 dotenv = None
25
26# these modules are not allowed to import this module
27<<<hidden due to potential security issue>>>
28from chart.common.path import Path
29from chart.common.env import get_env
30
31try:
32 # Tell Django to use oracledb instead of cx_Oracle if available
33 import oracledb
34 # sanity test is needed to avoid crash while building docker framework image
35 # as for a few steps we're operating with oracledb v1.21
36
37 # Enable thick client mode. Without this we will be unable to access any columns using
38 # national datatypes NCHAR, NVARCHAR2, and NCLOB because the EUM databases were created
39 # using the Oracle specific UTC8 character set instead of the standard UTF-8.
40 # This cannot be changed without a major rebuild of the database.
41 # A future release of oracledb may lift the restriction but as of v1.3.1 there are
42 # no specific plans to implement this.
43 if hasattr(oracledb, 'init_oracle_client'):
44 try:
45 oracledb.init_oracle_client()
46 except oracledb.DatabaseError:
47 pass
48
49 # oracledb.version = "8.3.0"
50 sys.modules['cx_Oracle'] = oracledb
51except ImportError:
52 pass
53
54
55# Section: Initialisation
56# -----------------------
57
58def print_debug(*message):
59 """Show early debug messages if $CHART_DEBUG_SETTINGS is set."""
60 if not get_env('CHART_DEBUG_SETTINGS', bool):
61 return
62
63 print(' '.join(str(s) for s in message))
64
65
66print_debug('Processing core settings {path}'.format(path=__file__))
67
68# Core code home directory
69CORE_HOME_DIR = Path(__file__).absolute().parent
70
71# Top level core dir
72CORE_ROOT_DIR = CORE_HOME_DIR.parent
73
74# Configure Django settings system to use this file
75DJANGO_SETTINGS_MODULE = 'chart.settings'
76os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
77
78# Location of project settings module
79PROJECT_SETTINGS = os.environ.get('CHART_SETTINGS_MODULE', '')
80assert len(PROJECT_SETTINGS) > 0, \
81 '$CHART_SETTINGS_MODULE not set. Check that project.init_*() has been called'
82
83# Top level project code directory
84# if PROJECT_SETTINGS != '':
85# I think this is only needed so doctest can run over the file
86# Normally this will only be included by an actual project
87# project_settings_spec = importlib.util.find_spec(PROJECT_SETTINGS)
88 # if project_settings_spec is None:
89 # raise ValueError('Cannot import {settings} from ${env}'.format(
90 # settings=PROJECT_SETTINGS, env=ENV_PROJECT_SETTINGS))
91# PROJECT_HOME_DIR = Path(project_settings_spec.origin).parent
92# else:
93 # PROJECT_HOME_DIR = CORE_HOME_DIR
94
95# Find location of project from $CHART_SETTINGS_MODULE
96if PROJECT_SETTINGS != 'dummy':
97 # Allow CHART_SETTINGS_MODULE to be set to "dummy" to allow some automated core tests
98 # to complete when run without a project present
99 PROJECT_HOME_DIR = Path(importlib.util.find_spec(PROJECT_SETTINGS).origin).parent
100
101else:
102 PROJECT_HOME_DIR = CORE_HOME_DIR
103
104# Root directory of the project
105PROJECT_ROOT_DIR = PROJECT_HOME_DIR.parent
106
107# Application name used in the website
108# It would be better to set this to None, forcing projects to re-define it, however that
109# causes framework automated tests to fail since they process
110APPNAME = None
111
112# description: Long description of the application
113# datatype: str
114PROJECT_DESCRIPTION = None
115
116# Where to write stack trace files on unhandled exceptions
117STACK_TRACE_FILENAME = Path('trace.dump')
118
119# Directory to contain consolidated static files.
120STATIC_ROOT = Path('~/tmp/static').expand()
121
122# Base URL for reading static resources
123STATIC_URL = '/static/'
124
125# Local machine name
126HOSTNAME = socket.gethostname()
127
128
129# Section: Project directories
130# ----------------------------
131
132# Directory to hold all notification statefiles
133STATEFILE_DIR = None
134
135# Paths for activity XML files
136ACTIVITY_DIRS = [PROJECT_HOME_DIR.joinpath('activities')]
137
138# Locations of timeseries table definition files
139TS_TABLE_DIRS = [PROJECT_HOME_DIR.joinpath('db', 'ts')]
140
141# description: Location of fixed non-satellite specific event packet xmls
142EV_PACKET_DIR = PROJECT_HOME_DIR.joinpath('db', 'packets', 'ev')
143
144# Location of sid specific limts, packets, time-series definition files,
145# Currently only applicable to CHART-EPSSG
146SID_SPECIFIC_DIR = None
147
148# Location of system table definition files
149SYS_TABLE_DIRS = [CORE_HOME_DIR.joinpath('db', 'system')]
150
151# Directory tree for storing zipped working directories containing reports.
152ARCHIVE_DIR = Path('~/tmp/archive').expand()
153
154# Location of named calibration XML files (not per-satellite)
155CALIBRATION_DIR = PROJECT_HOME_DIR.joinpath('db', 'named_cal')
156
157# description: Location of limits XML files (not per-satellite)
158LIMITS_DIR = PROJECT_HOME_DIR.joinpath('db', 'limits')
159
160# Directory where we place Oracle plsql functions (not per-satellite)
161ORACLE_FUNC_DIR = PROJECT_HOME_DIR.joinpath('db', 'oracle')
162
163# Base directory for the worker and jobcontrol to create working directories under
164WORK_AREA = Path('~/tmp/work').expand()
165
166# The timeseries XML files are compiled into this python script.
167# The `ingester` process must be able to write to this file.
168RDR_PY_FILENAME = PROJECT_HOME_DIR.joinpath('db', 'rdr.py')
169RDR_ASYNC_PY_FILENAME = PROJECT_HOME_DIR.joinpath('db', 'rdr_async.py')
170
171# Files and subdirectories within the SID_SPECIFIC_DIR for PUS-based projects
172# using satellite-specific databases
173TS_TABLE_SUBDIR = Path('ts')
174TC_PACKET_SUBDIR = Path('packets/tc')
175TM_PACKET_SUBDIR = Path('packets/tm')
176TM_CRITERIA_FILENAME = Path('packets/tm_criteria.xml')
177CALIBRATION_SUBDIR = Path('named_cal')
178CHOICES_SUBDIR = Path('choices')
179LIMITS_SUBDIR = Path('limits')
180PARAM1_PARAM2_INFO_FILENAME = Path('param1_param2_info.xml')
181SRDB_VERSION_INFO_FILENAME = Path('srdb_version.xml')
182PARAM_SPIDS_INFO_FILENAME = Path('param_spids_info.xml')
183
184# Location of core tools directory for launcher script
185# This is not really used as the launcher hard codes its own settings to
186# avoid importing proper settings so early.
187TOOLS_DIRS = [
188 {'name': 'Core tools', 'dir': CORE_HOME_DIR.joinpath('cmd')},
189]
190
191# For each Widget directory a tuple of (name, dir, show_in_gallery)
192WIDGET_DIRS = [
193 {'name': 'Core widgets', 'dir': CORE_HOME_DIR.joinpath('widgets'), 'show-in-gallery': True},
194 {'name': 'Digest widgets', 'dir': CORE_HOME_DIR.joinpath('widgets/digest'), 'show-in-gallery': False},
195]
196
197# Location of scheduler XML files
198SCHEDULE_DIR = PROJECT_HOME_DIR.joinpath('schedule')
199
200# Location of event class definitions file
201EVENT_CLASSES_FILENAME = PROJECT_HOME_DIR.joinpath('events', 'event_classes.xml')
202
203# Project report themes
204THEME_DIR = PROJECT_HOME_DIR.joinpath('themes')
205
206# List of known satellites
207SATELLITES = PROJECT_HOME_DIR.joinpath('db', 'satellites.xml')
208
209# For projects using the generic SID_XML data source ID
210# link the SID configuration file
211SOURCES = PROJECT_HOME_DIR.joinpath('db', 'sources.xml')
212
213# Location of report template XML files
214REPORT_TEMPLATE_DIR = PROJECT_HOME_DIR.joinpath('reports')
215
216# Module holding algorithm constants
217CONSTANTS_FILE = None
218
219# Location of Relax-NG compact schema files
220SCHEMA_DIR = CORE_HOME_DIR.joinpath('schemas')
221
222<<<hidden due to potential security issue>>>
223<<<hidden due to potential security issue>>>
224KEYRING_FILE = None
225
226<<<hidden due to potential security issue>>>
227<<<hidden due to potential security issue>>>
228<<<hidden due to potential security issue>>>
229GPG_KEYRING_FILE = None
230
231# Optional file defining email distribution lists
232ROLES_FILE = None
233
234# For external report publish function
235REPORT_PUBLISH_HOST = None
236REPORT_PUBLISH_USER = None
237REPORT_PUBLISH_DIR = None
238REPORT_PUBLISH_REMOTE_DB = None
239REPORT_PUBLISH_DB_SYNC_CMD = None
240
241# Report group file location
242REPORT_GROUP_FILE = None
243
244# Location for the auto-generated Sphinx documentation output
245SPHINX_DIR = None
246
247
248# Section: URLs
249# ------------
250
251# Base URL (without trailing slash) for the CHART website.
252# Used by automated tests and to create links in reports.
253CHART_WEB = None # 'https://chart/eps'
254
255# Base URL for external access website
256CHART_EXTERNAL_WEB = None # 'https://chartext.eumetsat.int/eps'
257
258# Base URL to browse core project source files
259# Trailing slash should be omitted. Leave None to use built in viewer.
260# datatype: url
261CORE_BROWSE_URL = None
262
263# URL to browse project source code files. If None we mostly use the built-in
264# file and directory browser. Other values are only useful in unusual cases such as Sphinx
265# documentation.
266# Trailing slash should be omitted.
267PROJECT_BROWSE_URL = None
268
269# Link to a project wiki. It can be referred to as {WIKI_URL} from the <url> element
270# of most .xml files.
271PROJECT_HOMEPAGE_URL = 'http://tctrac/projects/example/wiki'
272
273# Daily digest uses this
274TIMELINE_URL = None
275
276# Link to CDAT cross-project plotting tool
277CDAT_URL = 'https://chart/cdat'
278
279#Exculde spid_param information for hybrid missions
280NON_SPID_PROJECTS = 'S3'
281
282
283# Section: Plot tool configuration
284# --------------------------------
285
286# Filters for the plotting tool data tree
287PLOT_TABLE_FILTERS = []
288
289# Do we show the bar chart side-by-side and overlay options in the plot tool?
290PLOT_INCLUDE_BARCHART = False
291
292# Do we show the Daily Total check box in the plot tool?
293PLOT_INCLUDE_DAILY_TOTAL = False
294
295# Include the View Details button in the plot tool info popup
296PLOT_VIEW_DETAILS = False
297
298# Default sensing start time for plot tool
299PLOT_DEFAULT_SENSING_STOP = datetime.utcnow().replace(second=0, minute=0)
300
301# Default sensing stop time for plot tool
302PLOT_DEFAULT_SENSING_START = PLOT_DEFAULT_SENSING_STOP - timedelta(days=2)
303
304# Format of the plot legend entries.
305# Available placeholders: sid, name, field, unit, description
306PLOT_LEGEND_TEMPLATE = '{name} {field} {unit}'
307
308# Initial SCID for plot and event viewer
309DEFAULT_SCID = None
310
311# Location of CHART master web site to handle proxied requests
312# from external point of presence
313WEB_PROXY = None
314
315# Location of common static files like the geolocation background
316COMMON_WEB_DIR = CORE_HOME_DIR.joinpath('web')
317
318# With Auto sampling selected, threshold where we try to use stats
319# instead of all-points subsampled. Timeseries tables
320PLOT_MAX_AUTO_FITWIDTH_POINTS_TS = 200000
321
322# As PLOT_MAX_AUTO_FITWIDTH_POINTS_TS but key-value stores
323PLOT_MAX_AUTO_FITWIDTH_POINTS_KV = 5000
324
325# Record if stats include standard deviations
326STATS_HAVE_STDDEVS = True
327
328# True to use EPS project geolocator, None to disable geolocation,
329GEOLOC_CONSTRUCTOR = None
330
331# Rescale Y-Axis when plotting single parameter with choices
332# only set true for projects where CHOICE calibrations are not sequential with
333# constant incrementation. Set True in S3 project
334CHOICE_YAXIS_RESCALING = False
335
336# Add a prefix string to plot labels because otherwise in some circumstances
337# (S3 plots) flot renders the tick labels at the same x-position as the axis
338# label, overwriting each other
339PLOT_LABEL_PREFIX = ''
340
341# True for projects that use a numerical region id code in stats tables,
342# False for projects using a string region name
343STATS_USE_NUMERICAL_REGIONS = True
344
345# Control whether the special button in the plot tool is used
346# to show EPS-style shading of ECL events, or JCS/MTG (?) style OOL
347# display, or hidden
348PLOT_AUX_FUNCTION: Optional[Union['ECL', 'OOL']] = None
349
350# Enable restricting visibility of table groups in the plot tool and event viewer
351# by user
352TABLE_SECURITY_RESTRICTIONS = False
353
354
355# Section: Job control
356# --------------------
357
358# Ignore any files older than this when looking for files to ingest
359FILE_SCAN_CUTOFF = timedelta(hours=72)
360
361# Sleep duration for scheduler
362SCHEDULER_SLEEP = timedelta(seconds=5 * 60)
363
364# Sleep duration for worker in seconds
365WORKER_SLEEP = 15 * 60
366
367# Build job chains by finding derived jobs
368WORKER_JOB_CHAIN = 'chart.backend.worker_job_chains'
369
370# Job priorities tuned for EPS project
371WORKER_JOB_PRIORITY = 'chart.backend.worker_next_jobs'
372
373# Expect an ORBITS field in the JOBS table.
374# This is a relic from the EPS project which was written to rely in this.
375# New projects shouldn't need it
376ORBIT_IN_JOBS_TABLE = False
377
378# Use the older GEN_TIME field in AP tables instead of the newer GEN_NUM int
379USE_GEN_TIME = False
380
381# CHART-EPS events include a "gen_method" column but others don't
382USE_GEN_METHOD = False
383
384# Units of time for storing and displaying event start and stop times
385EVENT_TIMESTAMP_ACCURACY = 'us'
386
387# Exclude activities from execution. These are excluded from the workers only.
388# Schedulers and job chains will still create jobs as normal
389EXCLUDE_ACTIVITIES = ()
390
391# Force the dispatcher to run algorithms as subprocesses even if being run
392# interactively. The user must ensure all prerequisites are available since virtualenvs
393# cannot be added automatically.
394DISPATCHER_SUBPROCESS = False
395
396# Dispatcher will terminate any process taking too long.
397DISPATCHER_TIMEOUT = timedelta(minutes=90)
398
399# Tell the generic SF00 ingester to operate in replace mode by default
400REINGEST = False
401
402# Interval to retain delete jobs and work directories
403PURGE_INTERVAL = timedelta(days=90)
404
405# Number of daily log files to retain after log rotation
406LOG_ROTATE_COUNT = 60
407
408
409# Section: Emails
410# ---------------
411
412# ADMINS receive notification emails for a) Django errors, b) Algorithm fails and
413# c) daemon status changes. Emails are only sent when DEBUG=false.
414ADMINS = []
415
416# SYSADMINS only noticiations of daemon status changes.
417SYSADMINS = None
418
419# Send emails for 404 errors
420SEND_BROKEN_LINK_EMAILS = False
421
422# List of people who receive emails when the server responds with a 404 error.
423# This requires additional middleware to be enabled.
424# A better alternative is to monitor the web logfiles
425MANAGERS = []
426
427# SMTP server to send internal CHART emails to
428EMAIL_HOST = 'localhost'
429
430# SMTP port to send internal CHART emails
431EMAIL_PORT = 25
432
433# Sender subject prefix to be used in emails send by CHART
434# Used by pre-Role and Role-base systems
435EMAIL_SUBJECT_PREFIX = ''
436
437# End of email sign-off message
438# Used by pre-Role system only
439EMAIL_MESSAGE_SUFFIX = '\nbest regards, CHART'
440
441# Django setting, can be used to redirect emails to console
442EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
443# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
444
445# Template for a name to appear in the 'to:' field of any emails sent out
446# The {0} is expanded to the name of the module sending the message
447# This is used only in the pre-Roles email code
448EMAIL_NAME = 'CHART-{0}'
449
450# Template for a 'sender' field to appear in any emails sent out
451# Used by pre-Role system only
452EMAIL_ADDRESS = 'chart-{0}@eumetsat.int'
453
454# For the new Roles-based emails use a fixed string as the sender / from address
455# This should replace all references to EMAIL_NAME and EMAIL_ADDRESS
456# as it's simpler to have a single value in local_settings.py that just sets the from details
457# without another layer of templating
458EMAIL_SENDER = 'CHART (no reply) <chart-no-reply@eumetsat.int>'
459
460# Don't send out emails for events older than this
461EMAIL_CUTOFF = timedelta(days=3)
462
463
464# Section: Django templates
465# -------------------------
466
467# If false, Django will send an email alert for web server errors.
468# Note this does not affect log output; see LOG_LEVEL for that
469DEBUG = True
470
471# Normally we use special exception handling in the API
472DEBUG_API = False
473
474<<<hidden due to potential security issue>>>
475# Should only be enabled via environment variable
476<<<hidden due to potential security issue>>>
477
478# Tell Django to log emails to console (not sure if works)
479# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
480
481# If set to an integer, use this value for the PROJECT column of the
482# JOBS table.
483# Workers can optionally be configured to only read from a single PROJECT id
484# Considerable overlap between this and the CATEGORY functionality, which
485# could be merged into a single column
486DATABASE_PROJECT_ID = None
487
488# If True then the job ID of the parent job will be written into the PARENT column
489# of the JOBS table for derived jobs
490DATABASE_JOBS_TABLE_PARENT = True
491
492# If True then the JOBS table includes Log File Analysis (error/warn/info counts)
493# columns
494DATABASE_JOBS_TABLE_LFA = True
495
496
497# Section: Website
498# ----------------
499
500# Django can retain and reuse db connections for this many seconds.
501CONN_MAX_AGE = 3600
502
503# description Django in-memory cache is used for the Events and Plot start pages
504# datatype dict
505# CACHES = {
506 # 'default': {
507 # 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
508 # 'LOCATION': 'unique-snowflake'
509 # }
510# }
511# CACHE_BACKEND = 'locmem://'
512# SESSION_ENGINE = "django.contrib.sessions.backends.cache"
513
514# Port number to use the application web interface
515PORT = 10000
516
517# String that will be prefixed to all URLs.
518# Use '^' for no prefix, otherwise '^PREFIX/'
519PREFIX = ''
520
521# MEDIA_URL = PREFIX[1:] # uploads/'
522
523# Every instance of every project needs a unique SECRET_KEY. Do not check secret keys
524# into public source code repositories. Do not share secret keys between projects or instances of
525# projects.
526# Instead generate unique keys using a method below, then add them to the various
527<<<hidden due to potential security issue>>>
528# Keys can be generated with various tools i.e.
529# https://www.miniwebtool.com/django-secret-key-generator/
530# or
531# > python3
532# >>> from django.utils.crypto import get_random_string
533# >>> chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
534# >>> print(get_random_string(50, chars))
535# If not specified then a new temporary key will be generated on each startup, which means all
536# logged in users will be invalidated.
537# See charteps settings.py file for the command to finally instantiate SECRET_KEY properly
538
539# Read the instance specific secret key
540<<<hidden due to potential security issue>>>
541
542# Helper modules that can manipulate each HTTP response
543# django 2.0 style - sending emails on errors is currently broken
544MIDDLEWARE = [
545 'django.middleware.security.SecurityMiddleware',
546 'django.contrib.sessions.middleware.SessionMiddleware',
547 'django.middleware.common.CommonMiddleware',
548 # 'django.middleware.csrf.CsrfViewMiddleware',
549 # 'chart.web.context.EmailAlerts',
550 'django.contrib.auth.middleware.AuthenticationMiddleware',
551 'django.contrib.messages.middleware.MessageMiddleware',
552 'django.middleware.clickjacking.XFrameOptionsMiddleware',
553 # 'chart.web.lockdown.LockdownMiddleWare', # add this to individual projects to get lockdown
554 ]
555
556# MIDDLEWARE_CLASSES = (
557 # 'django.middleware.gzip.GZipMiddleware',
558 # 'chart.web.django-unslashed.middleware.RemoveSlashMiddleware',
559 # 'unslashed.middleware.RemoveSlashMiddleware',
560 # 'chart.web.context.EmailAlerts',
561 # 'django.middleware.common.CommonMiddleware',
562 # 'django.contrib.sessions.middleware.SessionMiddleware',
563 # 'django.contrib.auth.middleware.AuthenticationMiddleware',
564 # 'django.middleware.doc.XViewMiddleware',
565 # )
566
567# Our authentication system tries to make an ssh connection against
568# a TCE server to before looking in the Django users table
569AUTHENTICATION_HOST = 'concorde'
570
571# Text shown in the log page before user enters their info, and repeated if login fails
572AUTHENTICATION_PROMPT = 'Log in using your TCE credentials'
573
574# Django applications available. Any directories containing Django url.py files must be included
575INSTALLED_APPS = ['django.contrib.auth',
576 'django.contrib.contenttypes',
577 'django.contrib.sessions',
578 'django.contrib.sites',
579 'django.contrib.staticfiles',
580 'django.contrib.humanize',
581 'django.contrib.messages',
582 'chart.alg',
583 'chart.api2',
584 'chart.backend',
585 'chart.db',
586 'chart.events',
587 'chart.plotviewer',
588 'chart.reports',
589 'chart.jobviewer',
590 'chart.reportviewer',
591 'chart.eventviewer2',
592 'chart.schemas',
593 'chart.timeconv',
594 'chart.web',
595 'chart.widgets',
596 ]
597# 'django.contrib.admin',
598
599# Standard Django list of web template locations
600TEMPLATES = [{
601 'BACKEND': 'django.template.backends.django.DjangoTemplates',
602 'DIRS': [CORE_HOME_DIR.joinpath('alg', 'templates'),
603 CORE_HOME_DIR.joinpath('api', 'templates'),
604 CORE_HOME_DIR.joinpath('api2', 'templates'),
605 CORE_HOME_DIR.joinpath('backend', 'templates'),
606 CORE_HOME_DIR.joinpath('db', 'templates'),
607 CORE_HOME_DIR.joinpath('events', 'templates'),
608 CORE_HOME_DIR.joinpath('eventviewer2', 'templates'),
609 CORE_HOME_DIR.joinpath('jobviewer', 'templates'),
610 CORE_HOME_DIR.joinpath('plotviewer', 'templates'),
611 CORE_HOME_DIR.joinpath('reports', 'templates'),
612 CORE_HOME_DIR.joinpath('reportviewer', 'templates'),
613 CORE_HOME_DIR.joinpath('widgets', 'templates'),
614 CORE_HOME_DIR.joinpath('timeconv', 'templates'),
615 CORE_HOME_DIR.joinpath('web', 'templates'),
616 CORE_HOME_DIR.joinpath('browse', 'templates'),
617 CORE_HOME_DIR.joinpath('sids', 'templates')],
618 'OPTIONS': {
619 # 'loaders': ('django.template.loaders.filesystem.Loader',
620 # 'django.template.loaders.app_directories.Loader'),
621 'debug': False,
622 'string_if_invalid': 'MISSING TEMPLATE PARAMETER',
623 'context_processors': (
624 'django.contrib.auth.context_processors.auth',
625 'django.template.context_processors.debug',
626 'django.template.context_processors.i18n',
627 'django.template.context_processors.media',
628 'django.template.context_processors.static',
629 'django.contrib.messages.context_processors.messages',
630 'chart.web.context.add_settings'),
631 'loaders': [
632 ('django.template.loaders.cached.Loader', [
633 'django.template.loaders.filesystem.Loader',
634 'django.template.loaders.app_directories.Loader',
635 ]),
636 ],
637 # proper django 1.9 only
638 'builtins': ['django.templatetags.static'],
639 },
640 }]
641
642# TEMPLATES[0]['OPTIONS']['builtins'] = ['django.templatetags.static']
643
644# How to check user credentials (not really used as we mostly replace the
645# built in system in web/views.py
646AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
647 # 'chart.web.auth.Auth',
648
649# accelerate most web page rendering by caching the content of
650# the Django session and other tables
651SESSION_ENGINE = 'django.contrib.sessions.backends.db'
652# SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
653# SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
654
655# description unknown
656# AUTH_PROFILE_MODULE = "db.UserProfile"
657
658# Local time zone for this installation
659TIME_ZONE = 'Europe/Berlin'
660
661# Language code for this installation
662LANGUAGE_CODE = 'en-gb'
663
664# Django site unique ID
665SITE_ID = 1
666
667# If False Django will make some optimizations so as not to load the internationalization machinery
668USE_I18N = False
669
670# Datetime default text format
671DATETIME_FORMAT = 'Y-m-d H:i:s'
672
673# Time default text format
674TIME_FORMAT = 'H:i:s'
675
676# Required by Django 1.5 otherwise the website only works in debug mode
677ALLOWED_HOSTS = ['*']
678
679# Location of top level urls.py
680# ROOT_URLCONF = 'chart.urls'
681ROOT_URLCONF = None
682
683# Classic style CSS
684# Note sure if used - this should be in a CSS file
685STYLE = {
686 'menu_button': {
687 'top_colour': '#85ADFF',
688 'bottom_colour': '#85ADFF',
689 'hover_colour': '#789CE6'},
690 'menu_label': {
691 'colour': '#FFF',
692 # 'text_shadow': ''},
693 'text_shadow': ('rgba(128,128,128,0.5) -1px 0, rgba(128,128,128,0.3) 0 -1px, '
694 'rgba(192,192,192,0.5) 0 1px, rgba(128,128,128,0.3) -1px -1px')},
695 'title': {
696 'colour': '#85ADFF'},
697 'heading': {
698 'colour': '#85ADFF'},
699 'table_heading': {
700 'colour': '#000000',
701 'background_colour': '#a0a0ff'},
702 'table': {
703 'border': '1px solid #bbbbbb'},
704 'table1': {
705 'background_colour': '#ddf'},
706 'link': {
707 'colour': 'green'},
708 }
709
710# Not sure if this works but it is supposed to make Django handle trailing slashes better
711APPEND_SLASH = True
712
713# Tell the job viewer to retrieve files over an ssh link
714<<<hidden due to potential security issue>>>
715# example: ssh://epsope
716WORK_DIR_SERVER = None
717
718# Log security messages (login attempts) to syslog, as a security requirement
719LOG_TO_SYSLOG_PREFIX = None
720
721# The lockdown features are intended for the restricted EPP external access systems
722# If required login is set, the only page a user can see initially is the login screen and
723# the rest of the site will be available after successful login
724# Lockdown options require the lockdown middleware
725LOCKDOWN_LOGIN_REQUIRED = False
726
727# Optionally remove the login buttons. This is intended for systems
728# that use Apache Digest authentication
729LOCKDOWN_LOGIN_ALLOWED = True
730
731# On the EPP we switch off some pages that external users don't need. This
732# might get used in future to restrict visibility of some reports, tables and parameters too
733LOCKDOWN_RESTRICTED_ACCESS = False
734
735# Enable to show the "TC View" control to select event ordering in the event viewer
736EVENTVIEWER_TIME_FILTER_OPTIONS: bool = False
737
738# Control whether the multitable feature of the event viewer is available.
739# For now it requires a PUS project although a future extension will drop this restriction.
740EVENTVIEWER_MULTITABLE_SUPPORT = False
741
742# maximum number of events for table. Note this can be increased to 500000 for projects
743# which have implemented db performance improvements
744EVENTVIEWER_MAX_TABLE_EVENTS = 250000
745
746
747# Section: Reports
748# ----------------
749
750# Mapping from 'auto' appearance for Graph widgets with a single data series
751DEFAULT_SINGLE_DATAPOINT_APPEARANCE = 'dynrange'
752
753# Mapping from 'auto' appearance for Graph widgets with multiple data series
754DEFAULT_MULTIPLE_DATAPOINT_APPEARANCE = 'dynrange'
755
756# The PDF converter program doesn't work on AIX so we ssh into a Linux host
757# to run it. Set to None for local (wkhtmltopdf should be on PATH)
758PDF_CONVERTER_HOST = None # 'concorde'
759
760# Parameters for using the wkhtmltopdf HTML to PDF converter
761# With the newest wkhtmltopdf we need--enable-local-file-access
762# otherwise all images are missing.
763# Also with latest, --print-media-type, --footer* are ignored
764PDF_CONVERTER_COMMAND = ('wkhtmltopdf',
765 '--enable-local-file-access',
766 # '--print-media-type', # using media type makes the tool use
767 # high resolution (zoomed) versions of report graphs. However
768 # this might be making the PDFs too large to send by email, and for
769 # some graphs causes tick labels to be clipped.
770 # '--page-width', '1000px',
771 # '--page-height', '600px',
772 # '--zoom', '1.5',
773 # '--load-error-handling ignore',
774 '--footer-right', '"Page [page] of [toPage]"',
775 '--footer-spacing', '5',
776 '--footer-font-size', '8',
777 '-q')
778
779# Standard category used by the scheduler, worker and system job status web pages
780DEFAULT_CATEGORY = 'SCHEDULER'
781
782# Name of theme to use unless another is specified at runtime
783DEFAULT_THEME = 'default'
784
785# SSH servers which we can publish reports to
786REPORT_SERVERS = None
787
788# Font size for graph axis label
789GRAPH_FONT_SIZE = 8
790
791# Font size for graph axis label for zoomed graphs
792GRAPH_FONT_SIZE_ZOOM = 8
793
794# Font size for the image title inside Graph and other plot widgets
795# Zero means disabled (title is usually still visible in the HTML text below plot)
796GRAPH_TITLE_FONTSIZE = 0
797
798
799# Section: Database
800# -----------------
801
802# List of available timeseries database connections
803DATABASES = {}
804
805# Default database connection for this project
806DB_NAME = None
807
808# Allow overriding the database connection name for servers
809DB_NAME_WEB = None
810
811# Name of database connection to use for tests which specify that they want a test connection
812DB_NAME_TEST = None
813
814# Redirect all TS table writes to CSV files
815FAKE_WRITES = False
816
817# Echo all raw SQL queries as log messages
818SHOW_QUERIES = False
819
820# Allow certain tables to be accessed from a different database connection
821# A dictionary of table names against DATABASES entries
822DB_SPLIT_TABLES = {}
823
824# The set of automatic auxiliary fields that each table gets in the database
825# options: EPS, PUS
826TABLE_PATTERN = 'PUS'
827
828# The length of the TDEVTMHeader is usually 16 or 20 dependant on padding being enabled
829TDEV_TM_HEADER_LENGTH = 16
830
831# For PUS ingestion give a special offset before the TDEV variable header
832PUS_EXTENDED_BUFFER_OFFSET = 3
833
834# Ingest raw hex dumps into PUS tables
835PUS_INGEST_RAW_HEX = False
836
837# Exclude GS IDs from CCSDS ingestion
838PUS_TM_EXCLUDED_GS_IDS = None
839
840# Remove duplicate rows
841PUS_DROP_DUPLICATES = None
842
843# For PUS ingestion only allow packets to be withheld
844RESTRICTED_TC_PACKETS = []
845
846# PUS ingestion rapid file NIS Header other_data_buffer offset
847NIS_OTHER_DATA_BUFF_LEN = 0
848
849# TM table contains RECEPTION_TIME field
850TM_RECEPTION_TIME = False
851# (this is still optional? obsolete? ?)
852
853# TM table contains TDEV_GS_ID field
854TM_PACKET_TDEV_GS_ID = False
855
856# In PUS-based projects we try to normalise each parameter so it appears in the
857# same type of array in each packet where it appears, i.e. if a particular parameter exists inside
858# a 1-d array in any packet, then we place all instances of that packet in a 1-d array, even in
859# packets where it would normally be scalar
860# TBD: This is currently done at ingestion time, as a hack to reduce development effort.
861# This algorithm should be moved to packet display code.
862PUS_REDUCE_DIMENSIONALITIES = False
863
864# Tell the API /ts function to show empty variable list arrays for PUS tables
865PUS_SHOW_EMPTY_ARRAYS = False
866
867# Source-ID class for the project
868SID_CLASS = None
869
870# description: Maximum length of field names.
871# Please note that ORACLE has a limitation to 26 characters (it would be 30, but 4 characters
872# are reserved by CHART for Stats tables), while POSTGRESQL has a limit at 59 (longer is possible
873# if modifying POSTGRESQL standard config.
874# datatype: int
875MAXIMUM_FIELD_NAME_LENGTH = 26
876
877# Set up a mapping between source environments and filename fragments to SID objects
878INGEST_GS_ENV_MAP = {}
879
880
881# Section: Ingestion
882# ------------------
883
884# Location of the SRDB (SCOS2000 configuration) parameters and packets database.
885# This setting is also used by automated tests to determine whether to run generic PUS tests
886SRDB_DIR = None
887
888# CCSDS header types as defined in Project SRDB MIBs
889# in SRDB/current/tcp.dat file
890# Note this should be moved to an xml file in application db directory and be created
891# by the SRDB tool
892# Standard CCSDS header (very standard TC/TM?)
893CCSDS_STDXCCSD = 'STDXCCSD'
894
895# PUS Header - no CCSDS packet?
896CCSDS_STDXPUSS = 'STDXPUSS'
897
898# Packet with no header
899CCSDS_NO_HDR = 'NO_HDR'
900
901# Allow PUS packets to be filtered by TDEV Ground Station ID value,
902# excluding any in this sequence
903# PUS_TM_EXCLUDED_GS_IDS : Optional[Sequence[int]] = None
904PUS_TM_EXCLUDED_GS_IDS = None
905
906# In application code, drop packets with duplicate primary key fields before ingesting
907# them
908# datatype: Optional[Sequence[str]]
909PUS_DROP_DUPLICATES = None
910
911# CCSDS_HEADER_TYPES = {None: 'STDXCCSD', 'NOT_CCSDS': 'STDXPUSS', 'NO_HEADER': 'NO_HDR'}
912
913# For Polar Orbiting programs only, CCSDS OPS data fields are applicable
914# This controls ingestion of some additional fields in the PUS ingestion
915# This is a quick hack and should be replaced soon, e.g. by testing if the required fields are
916# defined in TC_STORE.xml
917OPS_DATA_APPLICABLE = False
918
919# For ops data ops angle conversion factor - satellite specific values for Polar Orbiting programs
920OPS_ANGLE_FACTOR = 0.0
921
922# TDEV TC Variable header, OPS Data offset in Rapid file TDEVTCVarHeader. Although OPS Data
923# is not applicable to all projects this offset does seem to be applicable to all programs
924TDEVTC_OPS_DATA_OFFSET = 237
925
926# TDEV TC Variable header, MCR data offset in Rapid file TDEVTCVarHeader
927# This appears to be dependant on the version of SCOS MCS the project is using
928# For S3 abs_ops_orbit is NOT defined in the record structure so add only 8 bytes to MCR offset
929# for S6/EPSSG abs_ops_orbit is defined in the record structure so add 12 bytes to MCR offset,
930# this is used as default
931TDEVTC_MRC_DATA_OFFSET = TDEVTC_OPS_DATA_OFFSET + 12
932
933# Set to true to include the VARIABLE_DATA column when writing PUS TC packets to the database.
934# Normally this is fine but the CHART-S3 project uses an Oracle database configured with a maximum
935# length of 4000 characters for this column, which many packets exceed, so is currently disabled
936# for that project. All other projects should use the default value of true
937INC_TC_VARIABLE_DATA = True
938
939
940# Section: Testing
941# ----------------
942
943# Temp dir to use in unit tests. If not set, a subdirectory of
944# /tmp will be used and deleted after the test ends. If TEST_DIR is specified, the
945# unit test will not delete it's directory at the end.
946TEST_DIR = None
947
948
949# Section: Server
950# ---------------
951
952# Type of web worker for the gunicorn server to use.
953# 'sync'|'gevent'|'eventlet'|'tornado'
954GUNICORN_WEB_WORKER_CLASS = 'sync'
955
956# Number of gunicorn worker processes
957GUNICORN_WEB_WORKER_COUNT = 8
958
959
960# Section: Logging
961# ----------------
962
963# Activate logging to a single file, if set.
964SINGLE_LOG_FILE = None
965
966# Activate logging to a daily rotating log files, if not None
967ROTATING_LOG_FILE = None
968
969# This is always a rotating log file if used. It is only used if rotating log file is non-null
970USER_LOG_FILE = 'user.log'
971
972# Set to 'NONE' to disable colourised text. Set to the name of a theme from
973# common/log.py to select that theme.
974COLOUR_THEME = 'blessings'
975
976# Here is an example of a project custom theme
977# from fabulous import color as col
978# COLOUR_THEME= {
979 # None: '{asctime} {levelname} {msg}'.format(
980 # asctime=col.bg256('#202020', col.fg256('#909090', '{r.asctime}')),
981 # levelname=col.green('{r.levelname}'),
982 # msg=col.bold(col.yellow('{r.msg}'))),
983 # 'ERROR': '{asctime} {levelname} {msg}'.format(
984 # asctime=col.bg256('#400000', col.fg256('#909090', '{r.asctime}')),
985 # levelname=col.red('{r.levelname}'),
986 # msg=col.red('{r.msg}'))}
987
988# Debug log file and colour initialisation
989DEBUG_LOG = False
990
991# Max log level
992# 'DEBUG'|'INFO'|'WARNING'|'ERROR'|'CRITICAL'
993LOG_LEVEL = 'DEBUG'
994
995# Send log messages to stderr instead of stdout when writing to terminals
996LOG_TO_STDERR = False
997
998# This value is only used for the docker compose script to pass the CHART_PROXY_LOG_DIR
999# configuration value to the purge.py script
1000PROXY_LOG_DIR = None
1001
1002
1003# Section: Configuration
1004# ----------------------
1005
1006# The following magic environment variables are used in various places in the code
1007# outside of this section:
1008# - CHART_DEBUG_LAUNCHER: Debug launcher and virtualenv (used in project launcher scripts)
1009# - CHART_DEBUG_SETTINGS: Debug environment variables
1010# - CHART_AUTORELOAD: Internal, used by serve.py to avoid double printing messages
1011# - CHART_DISPATCHER: Internal, used by algorithms to detect they are being run by the dispatcher
1012
1013def include(module=None, path=None, exit_on_failure=False):
1014 """Import another module allowing it to change global variables of this module."""
1015 if path:
1016 print_debug('Settings including path {path}'.format(path=path))
1017
1018 try:
1019 globals().update(runpy.run_path(str(path), init_globals=globals()))
1020
1021 except FileNotFoundError:
1022 if exit_on_failure:
1023 raise
1024
1025 else:
1026 print_debug('Settings including module {module}'.format(module=module))
1027
1028 try:
1029 globals().update(runpy.run_module(str(module), init_globals=globals()))
1030
1031 except ImportError:
1032 if exit_on_failure:
1033 raise
1034 print_debug('Done')
1035
1036
1037# Project wide environment variable to change the environment variable
1038# prefix from CHART_ to something else
1039ENV_PREFIX_ENV = None
1040
1041# Declare environment variables which can be used to override settings
1042# datatypes are passed the env.get_env() function
1043# Declare these before importing project settings so projects can add custom
1044# environment variable configurations
1045ENV_OVERRIDES = [
1046 {'env': '{ENV_PREFIX}ARCHIVE_DIR', 'setting': 'ARCHIVE_DIR', 'datatype': Path},
1047 {'env': '{ENV_PREFIX}COLOUR', 'setting': 'COLOUR_THEME', 'datatype': str},
1048 {'env': '{ENV_PREFIX}DB', 'setting': 'DB_NAME', 'datatype': str},
1049 {'env': '{ENV_PREFIX}DB_WEB', 'setting': 'DB_NAME_WEB', 'datatype': str},
1050 {'env': '{ENV_PREFIX}DEBUG', 'setting': ('DEBUG', 'TEMPLATE_DEBUG'), 'datatype': bool},
1051 {'env': '{ENV_PREFIX}DEBUG_LOG', 'setting': 'DEBUG_LOG', 'datatype': bool},
1052 {'env': '{ENV_PREFIX}DEBUG_API', 'setting': 'DEBUG_API', 'datatype': bool},
1053 {'env': '{ENV_PREFIX}DISPATCHER_SUBPROCESS',
1054 'setting': 'DISPATCHER_SUBPROCESS',
1055 'datatype': bool,
1056 'description': 'Tell the dispatcher module to use subprocess instead of running inline'},
1057 {'env': '{ENV_PREFIX}FAKE_WRITES', 'setting': 'FAKE_WRITES', 'datatype': bool},
1058 {'env': '{ENV_PREFIX}LOCAL_EVENTS', 'setting': 'LOCAL_EVENTS', 'datatype': Path},
1059 {'env': '{ENV_PREFIX}LOG_FILE', 'setting': 'SINGLE_LOG_FILE', 'datatype': Path},
1060 {'env': '{ENV_PREFIX}LOG_TO_STDERR', 'setting': 'LOG_TO_STDERR', 'datatype': bool},
1061 {'env': '{ENV_PREFIX}PORT', 'setting': 'PORT', 'datatype': int},
1062 {'env': '{ENV_PREFIX}PREFIX', 'setting': 'PREFIX', 'datatype': str},
1063 {'env': '{ENV_PREFIX}REINGEST', 'setting': 'REINGEST', 'datatype': bool},
1064 {'env': '{ENV_PREFIX}ROTATING_LOG_FILE', 'setting': 'ROTATING_LOG_FILE', 'datatype': Path},
1065 {'env': '{ENV_PREFIX}SHOW_QUERIES', 'setting': 'SHOW_QUERIES', 'datatype': bool},
1066<<<hidden due to potential security issue>>>
1067 {'env': '{ENV_PREFIX}DB_TEST', 'setting': 'DB_NAME_TEST', 'datatype': str},
1068 {'env': '{ENV_PREFIX}TEST_DIR', 'setting': 'TEST_DIR', 'datatype': Path},
1069 {'env': '{ENV_PREFIX}WEB', 'setting': 'CHART_WEB', 'datatype': str},
1070 {'env': '{ENV_PREFIX}WEB_PROXY', 'setting': 'WEB_PROXY', 'datatype': str},
1071 {'env': '{ENV_PREFIX}WORK_DIR', 'setting': 'WORK_DIR', 'datatype': Path},
1072 {'env': '{ENV_PREFIX}STATIC_ROOT', 'setting': 'STATIC_ROOT', 'datatype': Path},
1073 {'env': '{ENV_PREFIX}PROJECT_ID', 'setting': 'DATABASE_PROJECT_ID', 'datatype': int},
1074 {'env': '{ENV_PREFIX}HOMEPAGE', 'setting': 'PROJECT_HOMEPAGE_URL', 'datatype': str},
1075 {'env': '{ENV_PREFIX}APPNAME', 'setting': 'APPNAME', 'datatype': str},
1076 {'env': '{ENV_PREFIX}STATEFILE_DIR', 'setting': 'STATEFILE_DIR', 'datatype': Path},
1077 {'env': '{ENV_PREFIX}WORK_AREA', 'setting': 'WORK_AREA', 'datatype': Path},
1078 {'env': '{ENV_PREFIX}ARCHIVE_DIR', 'setting': 'ARCHIVE_DIR', 'datatype': Path},
1079 {'env': '{ENV_PREFIX}ROLES_FILE', 'setting': 'ROLES_FILE', 'datatype': Path},
1080 {'env': '{ENV_PREFIX}EMAIL_SENDER', 'setting': 'EMAIL_SENDER', 'datatype': str},
1081 {'env': '{ENV_PREFIX}DISPATCHER_TIMEOUT', 'setting': 'DISPATCHER_TIMEOUT', 'datatype': int},
1082 {'env': '{ENV_PREFIX}PROXY_LOG_DIR', 'setting': 'PROXY_LOG_DIR', 'datatype': Path},
1083 {'env': '{ENV_PREFIX}LOCKDOWN_LOGIN_ALLOWED',
1084 'setting': 'LOCKDOWN_LOGIN_ALLOWED',
1085 'datatype': bool},
1086 {'env': '{ENV_PREFIX}LOCKDOWN_LOGIN_REQUIRED',
1087 'setting': 'LOCKDOWN_LOGIN_REQUIRED',
1088 'datatype': bool},
1089 {'env': '{ENV_PREFIX}LOCKDOWN_RESTRICTED_ACCESS',
1090 'setting': 'LOCKDOWN_RESTRICTED_ACCESS',
1091 'datatype': bool},
1092 {'env': '{ENV_PREFIX}AUTHENTICATION_HOST', 'setting': 'AUTHENTICATION_HOST', 'datatype': str,
1093 'allow_none': True},
1094 {'env': '{ENV_PREFIX}AUTHENTICATION_PROMPT',
1095 'setting': 'AUTHENTICATION_PROMPT',
1096 'datatype': str},
1097 {'env': '{ENV_PREFIX}EMAIL_HOST', 'setting': 'EMAIL_HOST', 'datatype': str},
1098 {'env': '{ENV_PREFIX}DEFAULT_TABLESPACE',
1099 'setting': 'DEFAULT_TABLESPACE',
1100 'datatype': str,
1101 'allow_none': True},
1102 {'env': '{ENV_PREFIX}FILE_SCAN_CUTOFF',
1103 'setting': 'FILE_SCAN_CUTOFF',
1104 'datatype': timedelta,
1105 'allow_none': False},
1106 {'env': '{ENV_PREFIX}EVENTVIEWER_MAX_TABLE_EVENTS',
1107 'setting': 'EVENTVIEWER_MAX_TABLE_EVENTS',
1108 'datatype': int,
1109 'allow_none': False},
1110 {'env': '{ENV_PREFIX}CDAT_URL', 'setting': 'CDAT_URL'},
1111]
1112# Pull in project settings
1113include(module=PROJECT_SETTINGS)
1114
1115# Read the local settings file
1116LOCAL_SETTINGS = PROJECT_SETTINGS.replace('project_settings', 'local_settings')
1117include(module=LOCAL_SETTINGS)
1118
1119# Read the user settings file
1120USER_SETTINGS = Path(get_env('CHART_USER_SETTINGS',
1121 str,
1122 '~/.{proj}.settings'.format(
1123 proj=PROJECT_SETTINGS.partition('.')[0]))).expand()
1124include(path=USER_SETTINGS)
1125
1126OBSOLETE_SETTINGS = (
1127 'WORK_DIR', # Remove, it's a subdirectory of WORK_AREA
1128 'PROJ_HOME_DIR', # change to PROJECT_HOME_DIR
1129 'PROJ_ROOT_DIR', # change to PROJECT_ROOT_DIR
1130 'PROJ_TOOLS_DIR', # add to TOOLS_DIRS
1131 'PROJECT_TOOLS_DIR', # add to TOOLS_DIRS
1132 'PROJ_WIDGET_DIR', # projects add to WIDGET_DIRS
1133 'ACTIVITY_DIR', # Add to ACTIVITY_DIRS instead
1134 # 'PROJ_WIKI_URL', # projects change to PROJECT_HOMEPAGE_URL # eps has hundreds of references
1135 # to this one so leave it
1136 'PROJ_BROWSE_URL', # change to PROJECT_BROWSE_URL
1137)
1138if not get_env('CHART_DISABLE_OBSOLETE_SETTINGS_CHECK', bool):
1139 for obsolete_setting in OBSOLETE_SETTINGS:
1140 if obsolete_setting in globals():
1141 raise ValueError('Obsolete setting {o} found in project settings'.format(
1142 o=obsolete_setting))
1143
1144# Read the local.env file into our environment
1145if dotenv is not None:
1146 local_env_file = PROJECT_HOME_DIR.joinpath('local.env')
1147 if local_env_file.exists():
1148 dotenv.load_dotenv(local_env_file)
1149
1150
1151if ENV_PREFIX_ENV is None:
1152 ENV_PREFIX = 'CHART_'
1153
1154else:
1155 ENV_PREFIX = get_env(ENV_PREFIX_ENV, str, 'CHART_')
1156
1157# Apply settings changes from environment variables
1158for override in ENV_OVERRIDES:
1159 envname = override['env'].format(ENV_PREFIX=ENV_PREFIX)
1160 setting = override['setting']
1161 datatype = override.get('datatype', str)
1162 allow_none = override.get('allow_none', False)
1163 if envname in os.environ:
1164 print_debug(('Using ${env} to change setting {set} to {val}'.format(
1165 env=envname, set=setting, val=get_env(envname, datatype))))
1166
1167 if isinstance(setting, str):
1168 # single env, single setting
1169 # setattr(settings[setting] = get_env(envname, datatype)
1170 globals()[setting] = get_env(envname, datatype, allow_none=allow_none)
1171
1172 else:
1173 # single env, multiple settings
1174 for s in setting:
1175 # if debug:
1176 # print('Setting {k} to {v} type {t}'.format(
1177 # k=s, v=get_env(envname, datatype), t=type(get_env(envname, datatype))))
1178 globals()[s] = get_env(envname, datatype)
1179
1180# Apply changes to DATABASES structure from environment variables
1181# CHART_DATABASES_mje-ingester_DESCRIPTION="My dev db"
1182DATABASES_PREFIX = ENV_PREFIX + 'DATABASES_'
1183for dbname_param, value in os.environ.items():
1184 if dbname_param.startswith(DATABASES_PREFIX):
1185 dbname, _, param = dbname_param[len(DATABASES_PREFIX):].partition('_')
1186 if dbname not in DATABASES:
1187 DATABASES[dbname] = {}
1188
1189<<<hidden due to potential security issue>>>
1190<<<hidden due to potential security issue>>>
1191
1192 else:
1193 DATABASES[dbname][param] = value
1194
1195# Read environment variables specifying a INGEST_GS_ENV_MAP
1196INGEST_GS_ENV_MAP_PREFIX = ENV_PREFIX + 'INGEST_GS_MAP_'
1197for gs_scid, sid in os.environ.items():
1198 if gs_scid.startswith(INGEST_GS_ENV_MAP_PREFIX):
1199 gs, scid = gs_scid[len(INGEST_GS_ENV_MAP_PREFIX):].split('_')
1200 if gs not in INGEST_GS_ENV_MAP:
1201 INGEST_GS_ENV_MAP[gs] = {}
1202
1203 if sid == 'None':
1204 INGEST_GS_ENV_MAP[gs][scid] = None
1205
1206 else:
1207 INGEST_GS_ENV_MAP[gs][scid] = sid
1208
1209# Must be unique per project otherwise different poject logins conflict with each other
1210# if hosted on the same domain
1211# Set this after pulling in project and local settins because we set APPAME to null in core settings
1212SESSION_COOKIE_NAME = re.sub('[ ()-]', '', APPNAME if APPNAME is not None else '').lower()
1213
1214# STATIC_URL is the base address the browser uses to access static files.
1215# Must come after PREFIX has been finalised
1216# It is possible some odd project will want to customise this but we can fix that when it happens
1217# if PREFIX == '^':
1218 # STATIC_URL = '/static/'
1219
1220# else:
1221 # STATIC_URL = PREFIX.replace('^', '/') + 'static/'
1222
1223# Set this after applying environment overrides as they can change PREFIX
1224STATIC_URL = '/' + PREFIX + 'static/'
1225
1226# SERVER_EMAIL is required by Django and should be a plain address.
1227# Must come after EMAIL_ADDRESS is finalised
1228# This is not used by our application code.
1229# SERVER_EMAIL = EMAIL_ADDRESS.format('webserver')
1230
1231# MONITOR_EMAIL is used by chart.common.emails.sendmail and can be a tuple of
1232# (name,address) or a string of "name<address>"
1233# Must come after EMAIL_ADDRESS is finalised
1234# MONITOR_EMAIL = (EMAIL_NAME.format('supervisor'),
1235# EMAIL_ADDRESS.format('supervisor'))
1236
1237# Base URL for online plot tool. Used by 'live view' links in reports.
1238# Must be set after CHART_WEB is finalised
1239PLOT_URL = CHART_WEB + '/plots' if CHART_WEB is not None else None
1240
1241# LOGGING_CONFIG = None
1242# LOGGING_CONFIG = 'chart.common.log.init_log'
1243
1244os.environ['TZ'] = 'UTC'
1245time.tzset()
1246
1247
1248def set_db_name(name):
1249 """Allow default database connection to be changed after module load.
1250
1251 `name` may either be a connection name or a comma separated list of items
1252 where each item is either:
1253
1254 - the primary connection name
1255
1256 or
1257
1258 - a table override in the form table=connection
1259
1260 E.g.:
1261
1262 charteps serve --db argus-reporter,EVENTS=chartdev
1263
1264 will set the primary database to argus-reporter but use chartdev for the EVENTS
1265 table.
1266 """
1267 # global DB_NAME
1268 # print('set_db_name ' + name)
1269
1270 def set_primary_db(name):
1271 """Set primary database."""
1272 if name not in DATABASES:
1273 raise ValueError('Database {db} not available. Select from:\n{choices}'.format(
1274 db=name, choices='\n'.join(sorted(DATABASES.keys()))))
1275
1276 # This is the actual structure used by the Django multi-db support
1277 DATABASES['default'] = DATABASES[name]
1278
1279 # dispatcher needs this...
1280 DATABASES['default']['DB_NAME'] = name
1281
1282 if DATABASES['default']['ENGINE'].rpartition('.')[1] == 'postgresql_psycopg2' or\
1283 DATABASES['default']['ENGINE'] == 'django.db.backends.oracle':
1284 # postgres and recent Oracle versions needs this with Django v4 onwards as they
1285<<<hidden due to potential security issue>>>
1286 # We can't do it in our engine code because Django gets there first
1287<<<hidden due to potential security issue>>>
1288 # No idea why this test is suddenly needed
1289<<<hidden due to potential security issue>>>
1290<<<hidden due to potential security issue>>>
1291<<<hidden due to potential security issue>>>
1292
1293 # make sure subprocesses get the same db
1294 os.environ['{ENV_PREFIX}DB'.format(ENV_PREFIX=ENV_PREFIX)] = name
1295
1296 for part in name.split(','):
1297 # Decode the input into a series of tokens.
1298 if '=' not in part:
1299 # Tokens can be simple a connection name to set the primary connection
1300 set_primary_db(part)
1301
1302 else:
1303 # Tokens can also have the form table=name to change the connection for a single
1304 # table
1305 table, _, conn = part.partition('=')
1306
1307 # allow '%' as an additional wildcard char because it avoids shell expansion
1308 if '%' in table:
1309 table = table.replace('%', '*')
1310
1311 # We do wildcard expansion here. It would be neater in the common.tables
1312 # module, and using TableInfo not strings, but this feature is called during early
1313 # initialisation so we avoid pulling in too many imports.
1314 if '*' in table:
1315 for ts_dir in TS_TABLE_DIRS:
1316 for table_name in ts_dir.glob(table.upper() + '.xml'):
1317 DB_SPLIT_TABLES[table_name.stem.upper()] = conn
1318
1319 else:
1320 DB_SPLIT_TABLES[table.upper()] = conn
1321
1322
1323# Assign all the DB related variables that Django needs (must be after final setting of DB_NAME)
1324if DB_NAME is not None:
1325 # from chart.db.name import set_db_name
1326 set_db_name(DB_NAME)
1327
1328# this just helps the error message if client code tries to read a setting that is not found
1329# __name__ = 'settings'