8.3. Web config file¶
8.3.1. Specimen config file¶
To obtain a specimen file, use
crate_print_demo_crateweb_config
Specimen web config as of 2017-02-28:
#!/usr/bin/env python
# crate_anon/crateweb/specimen_secret_local_settings/crateweb_local_settings.py
"""
===============================================================================
Copyright (C) 2015-2017 Rudolf Cardinal (rudolf@pobox.com).
This file is part of CRATE.
CRATE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CRATE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CRATE. If not, see <http://www.gnu.org/licenses/>.
===============================================================================
Site-specific Django settings for CRATE web front end.
Put the secret stuff here.
SPECIMEN FILE ONLY - edit to your own requirements.
IT WILL NOT WORK until you've edited it.
"""
import os
raise Exception(
"Well done - CRATE has found your crate_local_settings.py file at {}. "
"However, you need to configure it for your institution's set-up, and "
"remove this line.".format(os.path.abspath(__file__)))
# =============================================================================
# Site URL configuration
# =============================================================================
DJANGO_SITE_ROOT_ABSOLUTE_URL = "http://mymachine.mydomain" # example for Apache # noqa
# DJANGO_SITE_ROOT_ABSOLUTE_URL = "http://localhost:8000" # for the Django dev server # noqa
FORCE_SCRIPT_NAME = ""
# FORCE_SCRIPT_NAME = "/crate" # example for CherryPy or Apache non-root hosting # noqa
# =============================================================================
# Site security
# =============================================================================
# FOR SECURITY:
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' # CHANGE THIS! # noqa
# Run crate_generate_new_django_secret_key to generate a new one.
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
# ... when False, note that static files must be served properly
# noinspection PyUnusedLocal
def always_show_toolbar(request):
return True # Always show toolbar, for debugging only.
if DEBUG:
ALLOWED_HOSTS = []
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK': always_show_toolbar,
}
else:
ALLOWED_HOSTS = ['*']
# =============================================================================
# Celery configuration
# =============================================================================
# Override BROKER_URL if you want.
# This will allow you to use multiple virtual hosts, to host multiple
# independent instances (in the unlikely event you'd wat to!)
# See
# http://stackoverflow.com/questions/12209652/multi-celery-projects-with-same-rabbitmq-broker-backend-process # noqa
# Similarly, override BROKER_URL to improve RabbitMQ security.
# =============================================================================
# Database configuration
# =============================================================================
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
# -------------------------------------------------------------------------
# Django database for web site (inc. users, audit).
# -------------------------------------------------------------------------
# Quick SQLite example:
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': '/home/myuser/somewhere/crate_db.sqlite3',
# },
# Quick MySQL example:
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'PORT': 3306, # local
'NAME': 'crate_db',
'USER': 'someuser',
'PASSWORD': 'somepassword',
},
# -------------------------------------------------------------------------
# Anonymised research database
# -------------------------------------------------------------------------
'research': {
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# IT IS CRITICALLY IMPORTANT THAT THIS CONNECTION (i.e. its user's
# access) IS READ-ONLY FOR THE RESEARCH DATABASES [1] AND HAS NO
# ACCESS WHATSOEVER TO SECRET DATABASES (like the 'default' or
# 'secret' databases) [2]. RESEARCHERS ARE GIVEN FULL ABILITY TO
# EXECUTE SQL VIA THIS CONNECTION, AND CAN DO SO FOR ANY DATABASES
# THAT THE CONNECTION PERMITS, NOT JUST THE ONE YOU SPECIFY
# EXPLICITLY.
#
# [1] ... so researchers can't alter/delete research data
# [2] ... so researchers can't see secrets
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'PORT': 3306, # local
'NAME': 'anonymous_output', # will be the default database; use None for no default database # noqa
'USER': 'researcher',
'PASSWORD': 'somepassword',
},
# -------------------------------------------------------------------------
# Secret database for RID/PID mapping
# -------------------------------------------------------------------------
'secret': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1',
'PORT': 3306,
'NAME': 'anonymous_mapping',
'USER': 'anonymiser_system',
'PASSWORD': 'somepassword',
},
# -------------------------------------------------------------------------
# Others, for consent lookup
# -------------------------------------------------------------------------
# Optional: 'cpft_iapt'
# Optional: 'cpft_crs'
# Optional: 'cpft_rio_rcep'
# Optional: 'cpft_rio_crate'
# ... see attributes of PatientLookup in crate_anon/consent/models.py
}
# Database title
RESEARCH_DB_TITLE = "My NHS Trust Research Database"
# Databases/schemas to provide database structure info for, and details on how
# to join within/between them (for the query builder).
# - Note that ALL these databases use the DATABASES['research'] connection
# specified above.
# - Under SQL Server, "database" and "schema" are different levels of
# organization. Specify a schema of "dbo" if you are unsure; this is the
# default.
# - Under MySQL, "database" and "schema" mean the same thing. Here, we'll call
# this a SCHEMA.
# - The first database/schema is the default selected in the query builder.
# - WITHIN a schema, tables will be autojoined on the trid_field.
# - ACROSS schemas, tables will be autojoined on the rid_field if they are in
# the same rid_family (a non-False Python value, e.g. integers starting at
# 1), and on mrid_table.mrid_field otherwise.
# - PostgreSQL can only query a single database via a single connection.
RESEARCH_DB_INFO = [
{
# Database name:
# - BLANK, i.e. '', for MySQL.
# - BLANK, i.e. '', for PostgreSQL.
# - The database name, for SQL Server.
'database': '',
# Schema name:
# - The database=schema name, for MySQL.
# - The schema name, for PostgreSQL (usual default: 'public').
# - The schema name, for SQL Server (usual default: 'dbo').
'schema': 'dbo',
'trid_field': 'trid',
'rid_field': 'brcid',
'rid_family': 1,
'mrid_table': 'patients',
'mrid_field': 'nhshash',
# For the data finder: is there a standard date field for most patient
# tables?
'default_date_field': '',
},
# {
# 'database': 'similar_database',
# 'schema': 'similar_schema',
# 'trid_field': 'trid',
# 'rid_field': 'same_rid',
# 'rid_family': 1,
# 'mrid_table': None,
# 'mrid_field': None,
# 'default_date_field': '',
# },
# {
# 'database': 'different_database',
# 'schema': 'different_schema',
# 'trid_field': 'trid',
# 'rid_field': 'different_rid',
# 'rid_family': 2,
# 'mrid_table': 'hashed_nhs_numbers',
# 'mrid_field': 'nhshash',
# 'default_date_field': '',
# },
]
# For the automatic query generator, we need to know the underlying SQL dialect
# Options are
# - 'mysql' => MySQL
# - 'mssql' => Microsoft SQL Server
RESEARCH_DB_DIALECT = 'mysql'
DISABLE_DJANGO_PYODBC_AZURE_CURSOR_FETCHONE_NEXTSET = True
# Configuration of the secret mapping database (as set during initial
# anonymisation)
SECRET_MAP = {
# Table within 'secret' mapping database containing PID/RID mapping
'TABLENAME': "secret_map",
# PID/RID fieldnames within that table
'PID_FIELD': "patient_id",
'RID_FIELD': "brcid",
'MASTER_PID_FIELD': "nhsnum",
'MASTER_RID_FIELD': "nhshash",
'TRID_FIELD': 'trid',
# Maximum length of the RID fields (containing a hash in a VARCHAR field)
'MAX_RID_LENGTH': 255,
}
# Which of the databases defined above should be used for lookups?
# Must (a) be a key of PatientLookup.DATABASES_CHOICES in consent/models.py;
# (b) be defined in DATABASES, above, UNLESS it is 'dummy_clinical'
CLINICAL_LOOKUP_DB = 'dummy_clinical'
# =============================================================================
# Database extra help file
# =============================================================================
# If specified, this must be a string that is an absolute filename of TRUSTED
# HTML that will be included.
DATABASE_HELP_HTML_FILENAME = None
# =============================================================================
# Local file storage (for PDFs etc).
# =============================================================================
# Where should we store the files? Make this directory (and don't let it
# be served by a generic web server that doesn't check permissions).
PRIVATE_FILE_STORAGE_ROOT = '/srv/crate_filestorage'
# Serve files via Django (inefficient but useful for testing) or via Apache
# with mod_xsendfile (or other web server configured for the X-SendFile
# directive)?
XSENDFILE = False
# How big will we accept?
MAX_UPLOAD_SIZE_BYTES = 10 * 1024 * 1024 # 10 Mb
# =============================================================================
# Outgoing e-mail
# =============================================================================
# General settings for sending e-mail from Django
# https://docs.djangoproject.com/en/1.8/ref/settings/#email-backend
# default backend:
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# bugfix for servers that only support TLSv1:
# EMAIL_BACKEND = 'crate_anon.crateweb.core.mail.SmtpEmailBackendTls1'
EMAIL_HOST = 'smtp.somewhere.nhs.uk'
EMAIL_PORT = 587 # usually 25 (plain SMTP) or 587 (STARTTLS)
# ... see https://www.fastmail.com/help/technical/ssltlsstarttls.html
EMAIL_HOST_USER = 'myuser'
EMAIL_HOST_PASSWORD = 'mypassword'
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
# Who will the e-mails appear to come from?
EMAIL_SENDER = "My NHS Trust Research Database - DO NOT REPLY <noreply@somewhere.nhs.uk>" # noqa
# During development, we route all consent-related e-mails to the developer.
# Switch SAFETY_CATCH_ON to False for production mode.
SAFETY_CATCH_ON = True
DEVELOPER_EMAIL = "testuser@somewhere.nhs.uk"
VALID_RESEARCHER_EMAIL_DOMAINS = []
# ... if empty, no checks are performed (any address is accepted)
# =============================================================================
# Research Database Manager (RDBM) details
# =============================================================================
RDBM_NAME = "John Doe"
RDBM_TITLE = "Research Database Manager"
RDBM_TELEPHONE = "01223-XXXXXX"
RDBM_EMAIL = "research.database@somewhere.nhs.uk"
RDBM_ADDRESS = ["FREEPOST SOMEWHERE_HOSPITAL RESEARCH DATABASE MANAGER"] # a list # noqa
# =============================================================================
# Administrators/managers to be notified of errors
# =============================================================================
# Exceptions get sent to these people.
ADMINS = (
('Mr Administrator', 'mr_admin@somewhere.domain'),
)
# Broken links get sent to these people
SEND_BROKEN_LINK_EMAILS = True
MANAGERS = (
('Mr Administrator', 'mr_admin@somewhere.domain'),
)
# =============================================================================
# PDF creation
# =============================================================================
# WKHTMLTOPDF_FILENAME: for the pdfkit PDF engine, specify a filename for
# wkhtmltopdf that incorporates any need for an X Server (not the default
# /usr/bin/wkhtmltopdf). See http://stackoverflow.com/questions/9604625/ .
# Basically, you can try
# WKHTMLTOPDF_FILENAME = ''
# and if it fails, try
# WKHTMLTOPDF_FILENAME = '/usr/bin/wkhtmltopdf'
# but if that fails, use
# WKHTMLTOPDF_FILENAME = '/path/to/wkhtmltopdf.sh'
# where wkhtmltopdf.sh is an executable script (chmod a+x ...) containing:
# #!/bin/bash
# xvfb-run --auto-servernum --server-args="-screen 0 640x480x16" \
# /usr/bin/wkhtmltopdf "$@"
# For a recent version, fetch one from http://wkhtmltopdf.org/, e.g.
# v0.12.4 for your OS.
WKHTMLTOPDF_FILENAME = ''
# WKHTMLTOPDF_FILENAME = '/usr/bin/wkhtmltopdf'
PDF_LOGO_ABS_URL = 'http://localhost/crate_logo'
# ... path on local machine, read by wkhtmltopdf
# Examples:
# [if you're running a web server] 'http://localhost/crate_logo'
# [Linux root path] file:///home/myuser/myfile.png
# [Windows root path] file:///c:/path/to/myfile.png
PDF_LOGO_WIDTH = "75%"
# ... must be suitable for an <img> tag, but "150mm" isn't working; "75%" is.
# ... tune this to your logo file (see PDF_LOGO_ABS_URL)
# =============================================================================
# Consent-for-contact settings
# =============================================================================
# For how long may we contact discharged patients without specific permission?
# Use 0 for "not at all".
PERMITTED_TO_CONTACT_DISCHARGED_PATIENTS_FOR_N_DAYS = 3 * 365
# Donation to charity for clinician response (regardless of the decision):
CHARITY_AMOUNT_CLINICIAN_RESPONSE = 1.0 # in local currency, e.g. GBP
# Note that using headers/footers requires a version of wkhtmltopdf built using
# "patched Qt". See above.
# Fetch one from http://wkhtmltopdf.org/, e.g. v0.12.4 for your OS.
PDF_LETTER_HEADER_HTML = ''
# PDF_LETTER_HEADER_HTML = '''
# <!DOCTYPE html>
# <head>
# <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
# </head>
# <html>
# <body>
# <div>boo! header</div>
# </body>
# </html>
# '''
PDF_LETTER_FOOTER_HTML = ''
# http://stackoverflow.com/questions/11948158/wkhtmltopdf-how-to-disable-header-on-the-first-page # noqa
# PDF_LETTER_FOOTER_HTML = '''
# <!DOCTYPE html>
# <html>
# <head>
# <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
# <script>
# function restrict_page_display() {
# var vars = {},
# kvp_list = document.location.search.substring(1).split('&'),
# key_value_pair,
# debug_element = document.getElementById("debug"),
# i;
# for (i = 0; i < kvp_list.length; ++i) {
# key_value_pair = kvp_list[i].split('=', 2);
# vars[key_value_pair[0]] = unescape(key_value_pair[1]);
# }
# // debug_element.textContent = kvp_list;
#
# // Turn off footer except on first page
# if (vars['page'] != 1) {
# document.getElementById("footer").style.display = 'none';
# }
# }
# </script>
# <style>
# body {
# color: #005EB8; /* NHS Blue */
# font-family: Arial, Helvetica, sans-serif;
# font-size: small;
# text-align: right;
# }
# </style>
# </head>
# <!-- <body onload="restrict_page_display()"> -->
# <body>
# <div id="footer">
# CPFT
# | HQ: Elizabeth House, Fulbourn Hospital, Fulbourn,
# Cambridge CB21 5EF
# | www.cpft.nhs.uk
# </div>
# <div id="debug"></div>
# </body>
# </html>
# '''
# =============================================================================
# Local information links
# =============================================================================
CHARITY_URL = "http://www.cpft.nhs.uk/research.htm"
CHARITY_URL_SHORT = "www.cpft.nhs.uk/research.htm"
LEAFLET_URL_CPFTRD_CLINRES_SHORT = "www.cpft.nhs.uk/research.htm > CPFT Research Database" # noqa
PUBLIC_RESEARCH_URL_SHORT = "www.cpft.nhs.uk/research.htm"
8.3.2. Django secret key¶
Use this command to generate a new random secret key:
crate_generate_new_django_secret_key
You can use the output for the SECRET_KEY variable in the config file.