We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Gmail is merging [OK] and [Triggered] alert emails
### Issue Summary

In Gmail the alert emails from redash are merged on the same thread, which makes it hard to know if an alert was triggered or solved.

Here is the explanation of the behavior of Gmail:
https://www.quora.com/Why-when-I-edit-subject-in-email-and-add-the-symbols-at-the-beginning-of-the-current-subject-to-keep-email-in-the-same-thread-the-email-sometimes-get-divided-in-a-new-thread-and-sometimes-doesnt/answer/Vineet-Chawla
### Steps to Reproduce
1. Setup an alert, register to receive email updates on a gmail address
2. Trigger the alert
3. Change the query and re-run it so that redash sends the email starting with [OK]
4. In gmail you will see the two emails under the subject starting with [Triggered] like this screenshot: ![screenshot from 2016-06-19 14 24 07](https://cloud.githubusercontent.com/assets/494686/16177359/7c8ddc8a-362a-11e6-8d80-f13202143ea2.png)
5. The expectation is to have two threads, or to have the information about whether the alert was triggered or solved in the body of the email
### Technical details:
- Redash Version: 0.9.2+b1536


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redash/destinations/email.py`
Content:
```
1 import logging
2 
3 from flask_mail import Message
4 from redash import models, mail
5 from redash.destinations import *
6 
7 
8 class Email(BaseDestination):
9 
10     @classmethod
11     def configuration_schema(cls):
12         return {
13             "type": "object",
14             "properties": {
15                 "addresses": {
16                     "type": "string"
17                 },
18             },
19             "required": ["addresses"]
20         }
21 
22     @classmethod
23     def icon(cls):
24         return 'fa-envelope'
25 
26     def notify(self, alert, query, user, new_state, app, host, options):
27         recipients = [email for email in options.get('addresses', '').split(',') if email]
28 
29         if not recipients:
30             logging.warning("No emails given. Skipping send.")
31 
32         html = """
33         Check <a href="{host}/alerts/{alert_id}">alert</a> / check <a href="{host}/queries/{query_id}">query</a>.
34         """.format(host=host, alert_id=alert.id, query_id=query.id)
35         logging.debug("Notifying: %s", recipients)
36 
37         try:
38             with app.app_context():
39                 message = Message(
40                     recipients=recipients,
41                     subject="[{1}] {0}".format(alert.name.encode('utf-8', 'ignore'), new_state.upper()),
42                     html=html
43                 )
44                 mail.send(message)
45         except Exception:
46             logging.exception("Mail send error.")
47 
48 register(Email)
49
```

Path: `redash/settings.py`
Content:
```
1 import json
2 import os
3 import urlparse
4 from funcy import distinct, remove
5 
6 
7 def parse_db_url(url):
8     url_parts = urlparse.urlparse(url)
9     connection = {'threadlocals': True}
10 
11     if url_parts.hostname and not url_parts.path:
12         connection['name'] = url_parts.hostname
13     else:
14         connection['name'] = url_parts.path[1:]
15         connection['host'] = url_parts.hostname
16         connection['port'] = url_parts.port
17         connection['user'] = url_parts.username
18         connection['password'] = url_parts.password
19 
20     return connection
21 
22 
23 def fix_assets_path(path):
24     fullpath = os.path.join(os.path.dirname(__file__), path)
25     return fullpath
26 
27 
28 def array_from_string(str):
29     array = str.split(',')
30     if "" in array:
31         array.remove("")
32 
33     return array
34 
35 
36 def set_from_string(str):
37     return set(array_from_string(str))
38 
39 
40 def parse_boolean(str):
41     return json.loads(str.lower())
42 
43 
44 def all_settings():
45     from types import ModuleType
46 
47     settings = {}
48     for name, item in globals().iteritems():
49         if not callable(item) and not name.startswith("__") and not isinstance(item, ModuleType):
50             settings[name] = item
51 
52     return settings
53 
54 
55 NAME = os.environ.get('REDASH_NAME', 'Redash')
56 LOGO_URL = os.environ.get('REDASH_LOGO_URL', '/images/redash_icon_small.png')
57 
58 REDIS_URL = os.environ.get('REDASH_REDIS_URL', os.environ.get('REDIS_URL', "redis://localhost:6379/0"))
59 PROXIES_COUNT = int(os.environ.get('REDASH_PROXIES_COUNT', "1"))
60 
61 STATSD_HOST = os.environ.get('REDASH_STATSD_HOST', "127.0.0.1")
62 STATSD_PORT = int(os.environ.get('REDASH_STATSD_PORT', "8125"))
63 STATSD_PREFIX = os.environ.get('REDASH_STATSD_PREFIX', "redash")
64 STATSD_USE_TAGS = parse_boolean(os.environ.get('REDASH_STATSD_USE_TAGS', "false"))
65 
66 # Connection settings for re:dash's own database (where we store the queries, results, etc)
67 DATABASE_CONFIG = parse_db_url(os.environ.get("REDASH_DATABASE_URL", os.environ.get('DATABASE_URL', "postgresql://postgres")))
68 
69 # Celery related settings
70 CELERY_BROKER = os.environ.get("REDASH_CELERY_BROKER", REDIS_URL)
71 CELERY_BACKEND = os.environ.get("REDASH_CELERY_BACKEND", CELERY_BROKER)
72 CELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600))
73 
74 # The following enables periodic job (every 5 minutes) of removing unused query results.
75 QUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_ENABLED", "true"))
76 QUERY_RESULTS_CLEANUP_COUNT = int(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_COUNT", "100"))
77 QUERY_RESULTS_CLEANUP_MAX_AGE = int(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_MAX_AGE", "7"))
78 
79 SCHEMAS_REFRESH_SCHEDULE = int(os.environ.get("REDASH_SCHEMAS_REFRESH_SCHEDULE", 30))
80 
81 AUTH_TYPE = os.environ.get("REDASH_AUTH_TYPE", "api_key")
82 PASSWORD_LOGIN_ENABLED = parse_boolean(os.environ.get("REDASH_PASSWORD_LOGIN_ENABLED", "true"))
83 ENFORCE_HTTPS = parse_boolean(os.environ.get("REDASH_ENFORCE_HTTPS", "false"))
84 
85 MULTI_ORG = parse_boolean(os.environ.get("REDASH_MULTI_ORG", "false"))
86 
87 GOOGLE_CLIENT_ID = os.environ.get("REDASH_GOOGLE_CLIENT_ID", "")
88 GOOGLE_CLIENT_SECRET = os.environ.get("REDASH_GOOGLE_CLIENT_SECRET", "")
89 GOOGLE_OAUTH_ENABLED = GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET
90 
91 SAML_METADATA_URL = os.environ.get("REDASH_SAML_METADATA_URL", "")
92 SAML_LOGIN_ENABLED = SAML_METADATA_URL != ""
93 SAML_CALLBACK_SERVER_NAME = os.environ.get("REDASH_SAML_CALLBACK_SERVER_NAME", "")
94 
95 # Enables the use of an externally-provided and trusted remote user via an HTTP
96 # header.  The "user" must be an email address.
97 #
98 # By default the trusted header is X-Forwarded-Remote-User.  You can change
99 # this by setting REDASH_REMOTE_USER_HEADER.
100 #
101 # Enabling this authentication method is *potentially dangerous*, and it is
102 # your responsibility to ensure that only a trusted frontend (usually on the
103 # same server) can talk to the redash backend server, otherwise people will be
104 # able to login as anyone they want by directly talking to the redash backend.
105 # You must *also* ensure that any special header in the original request is
106 # removed or always overwritten by your frontend, otherwise your frontend may
107 # pass it through to the backend unchanged.
108 #
109 # Note that redash will only check the remote user once, upon the first need
110 # for a login, and then set a cookie which keeps the user logged in.  Dropping
111 # the remote user header after subsequent requests won't automatically log the
112 # user out.  Doing so could be done with further work, but usually it's
113 # unnecessary.
114 #
115 # If you also set REDASH_PASSWORD_LOGIN_ENABLED to false, then your
116 # authentication will be seamless.  Otherwise a link will be presented on the
117 # login page to trigger remote user auth.
118 REMOTE_USER_LOGIN_ENABLED = parse_boolean(os.environ.get("REDASH_REMOTE_USER_LOGIN_ENABLED", "false"))
119 REMOTE_USER_HEADER = os.environ.get("REDASH_REMOTE_USER_HEADER", "X-Forwarded-Remote-User")
120 
121 # Usually it will be a single path, but we allow to specify additional ones to override the default assets. Only the
122 # last one will be used for Flask templates.
123 STATIC_ASSETS_PATHS = [fix_assets_path(path) for path in os.environ.get("REDASH_STATIC_ASSETS_PATH", "../rd_ui/app/").split(',')]
124 
125 JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600 * 6))
126 COOKIE_SECRET = os.environ.get("REDASH_COOKIE_SECRET", "c292a0a3aa32397cdb050e233733900f")
127 SESSION_COOKIE_SECURE = parse_boolean(os.environ.get("REDASH_SESSION_COOKIE_SECURE") or str(ENFORCE_HTTPS))
128 
129 LOG_LEVEL = os.environ.get("REDASH_LOG_LEVEL", "INFO")
130 
131 # Mail settings:
132 MAIL_SERVER = os.environ.get('REDASH_MAIL_SERVER', 'localhost')
133 MAIL_PORT = int(os.environ.get('REDASH_MAIL_PORT', 25))
134 MAIL_USE_TLS = parse_boolean(os.environ.get('REDASH_MAIL_USE_TLS', 'false'))
135 MAIL_USE_SSL = parse_boolean(os.environ.get('REDASH_MAIL_USE_SSL', 'false'))
136 MAIL_USERNAME = os.environ.get('REDASH_MAIL_USERNAME', None)
137 MAIL_PASSWORD = os.environ.get('REDASH_MAIL_PASSWORD', None)
138 MAIL_DEFAULT_SENDER = os.environ.get('REDASH_MAIL_DEFAULT_SENDER', None)
139 MAIL_MAX_EMAILS = os.environ.get('REDASH_MAIL_MAX_EMAILS', None)
140 MAIL_ASCII_ATTACHMENTS = parse_boolean(os.environ.get('REDASH_MAIL_ASCII_ATTACHMENTS', 'false'))
141 
142 HOST = os.environ.get('REDASH_HOST', '')
143 
144 # CORS settings for the Query Result API (and possbily future external APIs).
145 # In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN
146 # to the calling domain (or domains in a comma separated list).
147 ACCESS_CONTROL_ALLOW_ORIGIN = set_from_string(os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN", ""))
148 ACCESS_CONTROL_ALLOW_CREDENTIALS = parse_boolean(os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_CREDENTIALS", "false"))
149 ACCESS_CONTROL_REQUEST_METHOD = os.environ.get("REDASH_CORS_ACCESS_CONTROL_REQUEST_METHOD", "GET, POST, PUT")
150 ACCESS_CONTROL_ALLOW_HEADERS = os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_HEADERS", "Content-Type")
151 
152 # Query Runners
153 default_query_runners = [
154     'redash.query_runner.big_query',
155     'redash.query_runner.google_spreadsheets',
156     'redash.query_runner.graphite',
157     'redash.query_runner.mongodb',
158     'redash.query_runner.mysql',
159     'redash.query_runner.pg',
160     'redash.query_runner.url',
161     'redash.query_runner.influx_db',
162     'redash.query_runner.elasticsearch',
163     'redash.query_runner.presto',
164     'redash.query_runner.hive_ds',
165     'redash.query_runner.impala_ds',
166     'redash.query_runner.vertica',
167     'redash.query_runner.treasuredata',
168     'redash.query_runner.sqlite',
169     'redash.query_runner.dynamodb_sql',
170     'redash.query_runner.mssql',
171     'redash.query_runner.jql'
172 ]
173 
174 enabled_query_runners = array_from_string(os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join(default_query_runners)))
175 additional_query_runners = array_from_string(os.environ.get("REDASH_ADDITIONAL_QUERY_RUNNERS", ""))
176 disabled_query_runners = array_from_string(os.environ.get("REDASH_DISABLED_QUERY_RUNNERS", ""))
177 
178 QUERY_RUNNERS = remove(set(disabled_query_runners), distinct(enabled_query_runners + additional_query_runners))
179 
180 # Destinations
181 default_destinations = [
182     'redash.destinations.email',
183     'redash.destinations.slack',
184     'redash.destinations.webhook',
185     'redash.destinations.hipchat',
186 ]
187 
188 enabled_destinations = array_from_string(os.environ.get("REDASH_ENABLED_DESTINATIONS", ",".join(default_destinations)))
189 additional_destinations = array_from_string(os.environ.get("REDASH_ADDITIONAL_DESTINATIONS", ""))
190 
191 DESTINATIONS = distinct(enabled_destinations + additional_destinations)
192 
193 EVENT_REPORTING_WEBHOOKS = array_from_string(os.environ.get("REDASH_EVENT_REPORTING_WEBHOOKS", ""))
194 
195 # Support for Sentry (http://getsentry.com/). Just set your Sentry DSN to enable it:
196 SENTRY_DSN = os.environ.get("REDASH_SENTRY_DSN", "")
197 
198 # Client side toggles:
199 ALLOW_SCRIPTS_IN_USER_INPUT = parse_boolean(os.environ.get("REDASH_ALLOW_SCRIPTS_IN_USER_INPUT", "false"))
200 DATE_FORMAT = os.environ.get("REDASH_DATE_FORMAT", "DD/MM/YY")
201 
202 # Features:
203 FEATURE_ALLOW_ALL_TO_EDIT_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_ALLOW_ALL_TO_EDIT", "true"))
204 VERSION_CHECK = parse_boolean(os.environ.get("REDASH_VERSION_CHECK", "true"))
205 FEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_DISABLE_REFRESH_QUERIES", "false"))
206 FEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT", "true"))
207 
208 # BigQuery
209 BIGQUERY_HTTP_TIMEOUT = int(os.environ.get("REDASH_BIGQUERY_HTTP_TIMEOUT", "600"))
210 
211 # Enhance schema fetching
212 SCHEMA_RUN_TABLE_SIZE_CALCULATIONS = parse_boolean(os.environ.get("REDASH_SCHEMA_RUN_TABLE_SIZE_CALCULATIONS", "false"))
213 
214 # Allow Parameters in Embeds
215 # WARNING: With this option enabled, Redash reads query parameters from the request URL (risk of SQL injection!)
216 ALLOW_PARAMETERS_IN_EMBEDS = parse_boolean(os.environ.get("REDASH_ALLOW_PARAMETERS_IN_EMBEDS", "false"))
217 
218 ### Common Client config
219 COMMON_CLIENT_CONFIG = {
220     'allowScriptsInUserInput': ALLOW_SCRIPTS_IN_USER_INPUT,
221     'dateFormat': DATE_FORMAT,
222     'dateTimeFormat': "{0} HH:mm".format(DATE_FORMAT),
223     'allowAllToEditQueries': FEATURE_ALLOW_ALL_TO_EDIT_QUERIES,
224     'mailSettingsMissing': MAIL_DEFAULT_SENDER is None,
225     'logoUrl': LOGO_URL
226 }
227
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redash/destinations/email.py b/redash/destinations/email.py
--- a/redash/destinations/email.py
+++ b/redash/destinations/email.py
@@ -1,7 +1,7 @@
 import logging
 
 from flask_mail import Message
-from redash import models, mail
+from redash import mail, settings
 from redash.destinations import *
 
 
@@ -15,6 +15,11 @@
                 "addresses": {
                     "type": "string"
                 },
+                "subject_template": {
+                    "type": "string",
+                    "default": settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE,
+                    "title": "Subject Template"
+                }
             },
             "required": ["addresses"]
         }
@@ -36,9 +41,12 @@
 
         try:
             with app.app_context():
+                alert_name = alert.name.encode('utf-8', 'ignore')
+                state = new_state.upper()
+                subject_template = options.get('subject_template', settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE)
                 message = Message(
                     recipients=recipients,
-                    subject="[{1}] {0}".format(alert.name.encode('utf-8', 'ignore'), new_state.upper()),
+                    subject=subject_template.format(alert_name=alert_name, state=state),
                     html=html
                 )
                 mail.send(message)
diff --git a/redash/settings.py b/redash/settings.py
--- a/redash/settings.py
+++ b/redash/settings.py
@@ -141,6 +141,8 @@
 
 HOST = os.environ.get('REDASH_HOST', '')
 
+ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE = os.environ.get('REDASH_ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE', "({state}) {alert_name}")
+
 # CORS settings for the Query Result API (and possbily future external APIs).
 # In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN
 # to the calling domain (or domains in a comma separated list).

{"golden_diff": "diff --git a/redash/destinations/email.py b/redash/destinations/email.py\n--- a/redash/destinations/email.py\n+++ b/redash/destinations/email.py\n@@ -1,7 +1,7 @@\n import logging\n \n from flask_mail import Message\n-from redash import models, mail\n+from redash import mail, settings\n from redash.destinations import *\n \n \n@@ -15,6 +15,11 @@\n                 \"addresses\": {\n                     \"type\": \"string\"\n                 },\n+                \"subject_template\": {\n+                    \"type\": \"string\",\n+                    \"default\": settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE,\n+                    \"title\": \"Subject Template\"\n+                }\n             },\n             \"required\": [\"addresses\"]\n         }\n@@ -36,9 +41,12 @@\n \n         try:\n             with app.app_context():\n+                alert_name = alert.name.encode('utf-8', 'ignore')\n+                state = new_state.upper()\n+                subject_template = options.get('subject_template', settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE)\n                 message = Message(\n                     recipients=recipients,\n-                    subject=\"[{1}] {0}\".format(alert.name.encode('utf-8', 'ignore'), new_state.upper()),\n+                    subject=subject_template.format(alert_name=alert_name, state=state),\n                     html=html\n                 )\n                 mail.send(message)\ndiff --git a/redash/settings.py b/redash/settings.py\n--- a/redash/settings.py\n+++ b/redash/settings.py\n@@ -141,6 +141,8 @@\n \n HOST = os.environ.get('REDASH_HOST', '')\n \n+ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE = os.environ.get('REDASH_ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE', \"({state}) {alert_name}\")\n+\n # CORS settings for the Query Result API (and possbily future external APIs).\n # In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN\n # to the calling domain (or domains in a comma separated list).\n", "issue": "Gmail is merging [OK] and [Triggered] alert emails\n### Issue Summary\n\nIn Gmail the alert emails from redash are merged on the same thread, which makes it hard to know if an alert was triggered or solved.\n\nHere is the explanation of the behavior of Gmail:\nhttps://www.quora.com/Why-when-I-edit-subject-in-email-and-add-the-symbols-at-the-beginning-of-the-current-subject-to-keep-email-in-the-same-thread-the-email-sometimes-get-divided-in-a-new-thread-and-sometimes-doesnt/answer/Vineet-Chawla\n### Steps to Reproduce\n1. Setup an alert, register to receive email updates on a gmail address\n2. Trigger the alert\n3. Change the query and re-run it so that redash sends the email starting with [OK]\n4. In gmail you will see the two emails under the subject starting with [Triggered] like this screenshot: ![screenshot from 2016-06-19 14 24 07](https://cloud.githubusercontent.com/assets/494686/16177359/7c8ddc8a-362a-11e6-8d80-f13202143ea2.png)\n5. The expectation is to have two threads, or to have the information about whether the alert was triggered or solved in the body of the email\n### Technical details:\n- Redash Version: 0.9.2+b1536\n\n", "before_files": [{"content": "import logging\n\nfrom flask_mail import Message\nfrom redash import models, mail\nfrom redash.destinations import *\n\n\nclass Email(BaseDestination):\n\n    @classmethod\n    def configuration_schema(cls):\n        return {\n            \"type\": \"object\",\n            \"properties\": {\n                \"addresses\": {\n                    \"type\": \"string\"\n                },\n            },\n            \"required\": [\"addresses\"]\n        }\n\n    @classmethod\n    def icon(cls):\n        return 'fa-envelope'\n\n    def notify(self, alert, query, user, new_state, app, host, options):\n        recipients = [email for email in options.get('addresses', '').split(',') if email]\n\n        if not recipients:\n            logging.warning(\"No emails given. Skipping send.\")\n\n        html = \"\"\"\n        Check <a href=\"{host}/alerts/{alert_id}\">alert</a> / check <a href=\"{host}/queries/{query_id}\">query</a>.\n        \"\"\".format(host=host, alert_id=alert.id, query_id=query.id)\n        logging.debug(\"Notifying: %s\", recipients)\n\n        try:\n            with app.app_context():\n                message = Message(\n                    recipients=recipients,\n                    subject=\"[{1}] {0}\".format(alert.name.encode('utf-8', 'ignore'), new_state.upper()),\n                    html=html\n                )\n                mail.send(message)\n        except Exception:\n            logging.exception(\"Mail send error.\")\n\nregister(Email)\n", "path": "redash/destinations/email.py"}, {"content": "import json\nimport os\nimport urlparse\nfrom funcy import distinct, remove\n\n\ndef parse_db_url(url):\n    url_parts = urlparse.urlparse(url)\n    connection = {'threadlocals': True}\n\n    if url_parts.hostname and not url_parts.path:\n        connection['name'] = url_parts.hostname\n    else:\n        connection['name'] = url_parts.path[1:]\n        connection['host'] = url_parts.hostname\n        connection['port'] = url_parts.port\n        connection['user'] = url_parts.username\n        connection['password'] = url_parts.password\n\n    return connection\n\n\ndef fix_assets_path(path):\n    fullpath = os.path.join(os.path.dirname(__file__), path)\n    return fullpath\n\n\ndef array_from_string(str):\n    array = str.split(',')\n    if \"\" in array:\n        array.remove(\"\")\n\n    return array\n\n\ndef set_from_string(str):\n    return set(array_from_string(str))\n\n\ndef parse_boolean(str):\n    return json.loads(str.lower())\n\n\ndef all_settings():\n    from types import ModuleType\n\n    settings = {}\n    for name, item in globals().iteritems():\n        if not callable(item) and not name.startswith(\"__\") and not isinstance(item, ModuleType):\n            settings[name] = item\n\n    return settings\n\n\nNAME = os.environ.get('REDASH_NAME', 'Redash')\nLOGO_URL = os.environ.get('REDASH_LOGO_URL', '/images/redash_icon_small.png')\n\nREDIS_URL = os.environ.get('REDASH_REDIS_URL', os.environ.get('REDIS_URL', \"redis://localhost:6379/0\"))\nPROXIES_COUNT = int(os.environ.get('REDASH_PROXIES_COUNT', \"1\"))\n\nSTATSD_HOST = os.environ.get('REDASH_STATSD_HOST', \"127.0.0.1\")\nSTATSD_PORT = int(os.environ.get('REDASH_STATSD_PORT', \"8125\"))\nSTATSD_PREFIX = os.environ.get('REDASH_STATSD_PREFIX', \"redash\")\nSTATSD_USE_TAGS = parse_boolean(os.environ.get('REDASH_STATSD_USE_TAGS', \"false\"))\n\n# Connection settings for re:dash's own database (where we store the queries, results, etc)\nDATABASE_CONFIG = parse_db_url(os.environ.get(\"REDASH_DATABASE_URL\", os.environ.get('DATABASE_URL', \"postgresql://postgres\")))\n\n# Celery related settings\nCELERY_BROKER = os.environ.get(\"REDASH_CELERY_BROKER\", REDIS_URL)\nCELERY_BACKEND = os.environ.get(\"REDASH_CELERY_BACKEND\", CELERY_BROKER)\nCELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600))\n\n# The following enables periodic job (every 5 minutes) of removing unused query results.\nQUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(os.environ.get(\"REDASH_QUERY_RESULTS_CLEANUP_ENABLED\", \"true\"))\nQUERY_RESULTS_CLEANUP_COUNT = int(os.environ.get(\"REDASH_QUERY_RESULTS_CLEANUP_COUNT\", \"100\"))\nQUERY_RESULTS_CLEANUP_MAX_AGE = int(os.environ.get(\"REDASH_QUERY_RESULTS_CLEANUP_MAX_AGE\", \"7\"))\n\nSCHEMAS_REFRESH_SCHEDULE = int(os.environ.get(\"REDASH_SCHEMAS_REFRESH_SCHEDULE\", 30))\n\nAUTH_TYPE = os.environ.get(\"REDASH_AUTH_TYPE\", \"api_key\")\nPASSWORD_LOGIN_ENABLED = parse_boolean(os.environ.get(\"REDASH_PASSWORD_LOGIN_ENABLED\", \"true\"))\nENFORCE_HTTPS = parse_boolean(os.environ.get(\"REDASH_ENFORCE_HTTPS\", \"false\"))\n\nMULTI_ORG = parse_boolean(os.environ.get(\"REDASH_MULTI_ORG\", \"false\"))\n\nGOOGLE_CLIENT_ID = os.environ.get(\"REDASH_GOOGLE_CLIENT_ID\", \"\")\nGOOGLE_CLIENT_SECRET = os.environ.get(\"REDASH_GOOGLE_CLIENT_SECRET\", \"\")\nGOOGLE_OAUTH_ENABLED = GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET\n\nSAML_METADATA_URL = os.environ.get(\"REDASH_SAML_METADATA_URL\", \"\")\nSAML_LOGIN_ENABLED = SAML_METADATA_URL != \"\"\nSAML_CALLBACK_SERVER_NAME = os.environ.get(\"REDASH_SAML_CALLBACK_SERVER_NAME\", \"\")\n\n# Enables the use of an externally-provided and trusted remote user via an HTTP\n# header.  The \"user\" must be an email address.\n#\n# By default the trusted header is X-Forwarded-Remote-User.  You can change\n# this by setting REDASH_REMOTE_USER_HEADER.\n#\n# Enabling this authentication method is *potentially dangerous*, and it is\n# your responsibility to ensure that only a trusted frontend (usually on the\n# same server) can talk to the redash backend server, otherwise people will be\n# able to login as anyone they want by directly talking to the redash backend.\n# You must *also* ensure that any special header in the original request is\n# removed or always overwritten by your frontend, otherwise your frontend may\n# pass it through to the backend unchanged.\n#\n# Note that redash will only check the remote user once, upon the first need\n# for a login, and then set a cookie which keeps the user logged in.  Dropping\n# the remote user header after subsequent requests won't automatically log the\n# user out.  Doing so could be done with further work, but usually it's\n# unnecessary.\n#\n# If you also set REDASH_PASSWORD_LOGIN_ENABLED to false, then your\n# authentication will be seamless.  Otherwise a link will be presented on the\n# login page to trigger remote user auth.\nREMOTE_USER_LOGIN_ENABLED = parse_boolean(os.environ.get(\"REDASH_REMOTE_USER_LOGIN_ENABLED\", \"false\"))\nREMOTE_USER_HEADER = os.environ.get(\"REDASH_REMOTE_USER_HEADER\", \"X-Forwarded-Remote-User\")\n\n# Usually it will be a single path, but we allow to specify additional ones to override the default assets. Only the\n# last one will be used for Flask templates.\nSTATIC_ASSETS_PATHS = [fix_assets_path(path) for path in os.environ.get(\"REDASH_STATIC_ASSETS_PATH\", \"../rd_ui/app/\").split(',')]\n\nJOB_EXPIRY_TIME = int(os.environ.get(\"REDASH_JOB_EXPIRY_TIME\", 3600 * 6))\nCOOKIE_SECRET = os.environ.get(\"REDASH_COOKIE_SECRET\", \"c292a0a3aa32397cdb050e233733900f\")\nSESSION_COOKIE_SECURE = parse_boolean(os.environ.get(\"REDASH_SESSION_COOKIE_SECURE\") or str(ENFORCE_HTTPS))\n\nLOG_LEVEL = os.environ.get(\"REDASH_LOG_LEVEL\", \"INFO\")\n\n# Mail settings:\nMAIL_SERVER = os.environ.get('REDASH_MAIL_SERVER', 'localhost')\nMAIL_PORT = int(os.environ.get('REDASH_MAIL_PORT', 25))\nMAIL_USE_TLS = parse_boolean(os.environ.get('REDASH_MAIL_USE_TLS', 'false'))\nMAIL_USE_SSL = parse_boolean(os.environ.get('REDASH_MAIL_USE_SSL', 'false'))\nMAIL_USERNAME = os.environ.get('REDASH_MAIL_USERNAME', None)\nMAIL_PASSWORD = os.environ.get('REDASH_MAIL_PASSWORD', None)\nMAIL_DEFAULT_SENDER = os.environ.get('REDASH_MAIL_DEFAULT_SENDER', None)\nMAIL_MAX_EMAILS = os.environ.get('REDASH_MAIL_MAX_EMAILS', None)\nMAIL_ASCII_ATTACHMENTS = parse_boolean(os.environ.get('REDASH_MAIL_ASCII_ATTACHMENTS', 'false'))\n\nHOST = os.environ.get('REDASH_HOST', '')\n\n# CORS settings for the Query Result API (and possbily future external APIs).\n# In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN\n# to the calling domain (or domains in a comma separated list).\nACCESS_CONTROL_ALLOW_ORIGIN = set_from_string(os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN\", \"\"))\nACCESS_CONTROL_ALLOW_CREDENTIALS = parse_boolean(os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_ALLOW_CREDENTIALS\", \"false\"))\nACCESS_CONTROL_REQUEST_METHOD = os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_REQUEST_METHOD\", \"GET, POST, PUT\")\nACCESS_CONTROL_ALLOW_HEADERS = os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_ALLOW_HEADERS\", \"Content-Type\")\n\n# Query Runners\ndefault_query_runners = [\n    'redash.query_runner.big_query',\n    'redash.query_runner.google_spreadsheets',\n    'redash.query_runner.graphite',\n    'redash.query_runner.mongodb',\n    'redash.query_runner.mysql',\n    'redash.query_runner.pg',\n    'redash.query_runner.url',\n    'redash.query_runner.influx_db',\n    'redash.query_runner.elasticsearch',\n    'redash.query_runner.presto',\n    'redash.query_runner.hive_ds',\n    'redash.query_runner.impala_ds',\n    'redash.query_runner.vertica',\n    'redash.query_runner.treasuredata',\n    'redash.query_runner.sqlite',\n    'redash.query_runner.dynamodb_sql',\n    'redash.query_runner.mssql',\n    'redash.query_runner.jql'\n]\n\nenabled_query_runners = array_from_string(os.environ.get(\"REDASH_ENABLED_QUERY_RUNNERS\", \",\".join(default_query_runners)))\nadditional_query_runners = array_from_string(os.environ.get(\"REDASH_ADDITIONAL_QUERY_RUNNERS\", \"\"))\ndisabled_query_runners = array_from_string(os.environ.get(\"REDASH_DISABLED_QUERY_RUNNERS\", \"\"))\n\nQUERY_RUNNERS = remove(set(disabled_query_runners), distinct(enabled_query_runners + additional_query_runners))\n\n# Destinations\ndefault_destinations = [\n    'redash.destinations.email',\n    'redash.destinations.slack',\n    'redash.destinations.webhook',\n    'redash.destinations.hipchat',\n]\n\nenabled_destinations = array_from_string(os.environ.get(\"REDASH_ENABLED_DESTINATIONS\", \",\".join(default_destinations)))\nadditional_destinations = array_from_string(os.environ.get(\"REDASH_ADDITIONAL_DESTINATIONS\", \"\"))\n\nDESTINATIONS = distinct(enabled_destinations + additional_destinations)\n\nEVENT_REPORTING_WEBHOOKS = array_from_string(os.environ.get(\"REDASH_EVENT_REPORTING_WEBHOOKS\", \"\"))\n\n# Support for Sentry (http://getsentry.com/). Just set your Sentry DSN to enable it:\nSENTRY_DSN = os.environ.get(\"REDASH_SENTRY_DSN\", \"\")\n\n# Client side toggles:\nALLOW_SCRIPTS_IN_USER_INPUT = parse_boolean(os.environ.get(\"REDASH_ALLOW_SCRIPTS_IN_USER_INPUT\", \"false\"))\nDATE_FORMAT = os.environ.get(\"REDASH_DATE_FORMAT\", \"DD/MM/YY\")\n\n# Features:\nFEATURE_ALLOW_ALL_TO_EDIT_QUERIES = parse_boolean(os.environ.get(\"REDASH_FEATURE_ALLOW_ALL_TO_EDIT\", \"true\"))\nVERSION_CHECK = parse_boolean(os.environ.get(\"REDASH_VERSION_CHECK\", \"true\"))\nFEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(os.environ.get(\"REDASH_FEATURE_DISABLE_REFRESH_QUERIES\", \"false\"))\nFEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(os.environ.get(\"REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT\", \"true\"))\n\n# BigQuery\nBIGQUERY_HTTP_TIMEOUT = int(os.environ.get(\"REDASH_BIGQUERY_HTTP_TIMEOUT\", \"600\"))\n\n# Enhance schema fetching\nSCHEMA_RUN_TABLE_SIZE_CALCULATIONS = parse_boolean(os.environ.get(\"REDASH_SCHEMA_RUN_TABLE_SIZE_CALCULATIONS\", \"false\"))\n\n# Allow Parameters in Embeds\n# WARNING: With this option enabled, Redash reads query parameters from the request URL (risk of SQL injection!)\nALLOW_PARAMETERS_IN_EMBEDS = parse_boolean(os.environ.get(\"REDASH_ALLOW_PARAMETERS_IN_EMBEDS\", \"false\"))\n\n### Common Client config\nCOMMON_CLIENT_CONFIG = {\n    'allowScriptsInUserInput': ALLOW_SCRIPTS_IN_USER_INPUT,\n    'dateFormat': DATE_FORMAT,\n    'dateTimeFormat': \"{0} HH:mm\".format(DATE_FORMAT),\n    'allowAllToEditQueries': FEATURE_ALLOW_ALL_TO_EDIT_QUERIES,\n    'mailSettingsMissing': MAIL_DEFAULT_SENDER is None,\n    'logoUrl': LOGO_URL\n}\n", "path": "redash/settings.py"}], "after_files": [{"content": "import logging\n\nfrom flask_mail import Message\nfrom redash import mail, settings\nfrom redash.destinations import *\n\n\nclass Email(BaseDestination):\n\n    @classmethod\n    def configuration_schema(cls):\n        return {\n            \"type\": \"object\",\n            \"properties\": {\n                \"addresses\": {\n                    \"type\": \"string\"\n                },\n                \"subject_template\": {\n                    \"type\": \"string\",\n                    \"default\": settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE,\n                    \"title\": \"Subject Template\"\n                }\n            },\n            \"required\": [\"addresses\"]\n        }\n\n    @classmethod\n    def icon(cls):\n        return 'fa-envelope'\n\n    def notify(self, alert, query, user, new_state, app, host, options):\n        recipients = [email for email in options.get('addresses', '').split(',') if email]\n\n        if not recipients:\n            logging.warning(\"No emails given. Skipping send.\")\n\n        html = \"\"\"\n        Check <a href=\"{host}/alerts/{alert_id}\">alert</a> / check <a href=\"{host}/queries/{query_id}\">query</a>.\n        \"\"\".format(host=host, alert_id=alert.id, query_id=query.id)\n        logging.debug(\"Notifying: %s\", recipients)\n\n        try:\n            with app.app_context():\n                alert_name = alert.name.encode('utf-8', 'ignore')\n                state = new_state.upper()\n                subject_template = options.get('subject_template', settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE)\n                message = Message(\n                    recipients=recipients,\n                    subject=subject_template.format(alert_name=alert_name, state=state),\n                    html=html\n                )\n                mail.send(message)\n        except Exception:\n            logging.exception(\"Mail send error.\")\n\nregister(Email)\n", "path": "redash/destinations/email.py"}, {"content": "import json\nimport os\nimport urlparse\nfrom funcy import distinct, remove\n\n\ndef parse_db_url(url):\n    url_parts = urlparse.urlparse(url)\n    connection = {'threadlocals': True}\n\n    if url_parts.hostname and not url_parts.path:\n        connection['name'] = url_parts.hostname\n    else:\n        connection['name'] = url_parts.path[1:]\n        connection['host'] = url_parts.hostname\n        connection['port'] = url_parts.port\n        connection['user'] = url_parts.username\n        connection['password'] = url_parts.password\n\n    return connection\n\n\ndef fix_assets_path(path):\n    fullpath = os.path.join(os.path.dirname(__file__), path)\n    return fullpath\n\n\ndef array_from_string(str):\n    array = str.split(',')\n    if \"\" in array:\n        array.remove(\"\")\n\n    return array\n\n\ndef set_from_string(str):\n    return set(array_from_string(str))\n\n\ndef parse_boolean(str):\n    return json.loads(str.lower())\n\n\ndef all_settings():\n    from types import ModuleType\n\n    settings = {}\n    for name, item in globals().iteritems():\n        if not callable(item) and not name.startswith(\"__\") and not isinstance(item, ModuleType):\n            settings[name] = item\n\n    return settings\n\n\nNAME = os.environ.get('REDASH_NAME', 'Redash')\nLOGO_URL = os.environ.get('REDASH_LOGO_URL', '/images/redash_icon_small.png')\n\nREDIS_URL = os.environ.get('REDASH_REDIS_URL', os.environ.get('REDIS_URL', \"redis://localhost:6379/0\"))\nPROXIES_COUNT = int(os.environ.get('REDASH_PROXIES_COUNT', \"1\"))\n\nSTATSD_HOST = os.environ.get('REDASH_STATSD_HOST', \"127.0.0.1\")\nSTATSD_PORT = int(os.environ.get('REDASH_STATSD_PORT', \"8125\"))\nSTATSD_PREFIX = os.environ.get('REDASH_STATSD_PREFIX', \"redash\")\nSTATSD_USE_TAGS = parse_boolean(os.environ.get('REDASH_STATSD_USE_TAGS', \"false\"))\n\n# Connection settings for re:dash's own database (where we store the queries, results, etc)\nDATABASE_CONFIG = parse_db_url(os.environ.get(\"REDASH_DATABASE_URL\", os.environ.get('DATABASE_URL', \"postgresql://postgres\")))\n\n# Celery related settings\nCELERY_BROKER = os.environ.get(\"REDASH_CELERY_BROKER\", REDIS_URL)\nCELERY_BACKEND = os.environ.get(\"REDASH_CELERY_BACKEND\", CELERY_BROKER)\nCELERY_TASK_RESULT_EXPIRES = int(os.environ.get('REDASH_CELERY_TASK_RESULT_EXPIRES', 3600))\n\n# The following enables periodic job (every 5 minutes) of removing unused query results.\nQUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(os.environ.get(\"REDASH_QUERY_RESULTS_CLEANUP_ENABLED\", \"true\"))\nQUERY_RESULTS_CLEANUP_COUNT = int(os.environ.get(\"REDASH_QUERY_RESULTS_CLEANUP_COUNT\", \"100\"))\nQUERY_RESULTS_CLEANUP_MAX_AGE = int(os.environ.get(\"REDASH_QUERY_RESULTS_CLEANUP_MAX_AGE\", \"7\"))\n\nSCHEMAS_REFRESH_SCHEDULE = int(os.environ.get(\"REDASH_SCHEMAS_REFRESH_SCHEDULE\", 30))\n\nAUTH_TYPE = os.environ.get(\"REDASH_AUTH_TYPE\", \"api_key\")\nPASSWORD_LOGIN_ENABLED = parse_boolean(os.environ.get(\"REDASH_PASSWORD_LOGIN_ENABLED\", \"true\"))\nENFORCE_HTTPS = parse_boolean(os.environ.get(\"REDASH_ENFORCE_HTTPS\", \"false\"))\n\nMULTI_ORG = parse_boolean(os.environ.get(\"REDASH_MULTI_ORG\", \"false\"))\n\nGOOGLE_CLIENT_ID = os.environ.get(\"REDASH_GOOGLE_CLIENT_ID\", \"\")\nGOOGLE_CLIENT_SECRET = os.environ.get(\"REDASH_GOOGLE_CLIENT_SECRET\", \"\")\nGOOGLE_OAUTH_ENABLED = GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET\n\nSAML_METADATA_URL = os.environ.get(\"REDASH_SAML_METADATA_URL\", \"\")\nSAML_LOGIN_ENABLED = SAML_METADATA_URL != \"\"\nSAML_CALLBACK_SERVER_NAME = os.environ.get(\"REDASH_SAML_CALLBACK_SERVER_NAME\", \"\")\n\n# Enables the use of an externally-provided and trusted remote user via an HTTP\n# header.  The \"user\" must be an email address.\n#\n# By default the trusted header is X-Forwarded-Remote-User.  You can change\n# this by setting REDASH_REMOTE_USER_HEADER.\n#\n# Enabling this authentication method is *potentially dangerous*, and it is\n# your responsibility to ensure that only a trusted frontend (usually on the\n# same server) can talk to the redash backend server, otherwise people will be\n# able to login as anyone they want by directly talking to the redash backend.\n# You must *also* ensure that any special header in the original request is\n# removed or always overwritten by your frontend, otherwise your frontend may\n# pass it through to the backend unchanged.\n#\n# Note that redash will only check the remote user once, upon the first need\n# for a login, and then set a cookie which keeps the user logged in.  Dropping\n# the remote user header after subsequent requests won't automatically log the\n# user out.  Doing so could be done with further work, but usually it's\n# unnecessary.\n#\n# If you also set REDASH_PASSWORD_LOGIN_ENABLED to false, then your\n# authentication will be seamless.  Otherwise a link will be presented on the\n# login page to trigger remote user auth.\nREMOTE_USER_LOGIN_ENABLED = parse_boolean(os.environ.get(\"REDASH_REMOTE_USER_LOGIN_ENABLED\", \"false\"))\nREMOTE_USER_HEADER = os.environ.get(\"REDASH_REMOTE_USER_HEADER\", \"X-Forwarded-Remote-User\")\n\n# Usually it will be a single path, but we allow to specify additional ones to override the default assets. Only the\n# last one will be used for Flask templates.\nSTATIC_ASSETS_PATHS = [fix_assets_path(path) for path in os.environ.get(\"REDASH_STATIC_ASSETS_PATH\", \"../rd_ui/app/\").split(',')]\n\nJOB_EXPIRY_TIME = int(os.environ.get(\"REDASH_JOB_EXPIRY_TIME\", 3600 * 6))\nCOOKIE_SECRET = os.environ.get(\"REDASH_COOKIE_SECRET\", \"c292a0a3aa32397cdb050e233733900f\")\nSESSION_COOKIE_SECURE = parse_boolean(os.environ.get(\"REDASH_SESSION_COOKIE_SECURE\") or str(ENFORCE_HTTPS))\n\nLOG_LEVEL = os.environ.get(\"REDASH_LOG_LEVEL\", \"INFO\")\n\n# Mail settings:\nMAIL_SERVER = os.environ.get('REDASH_MAIL_SERVER', 'localhost')\nMAIL_PORT = int(os.environ.get('REDASH_MAIL_PORT', 25))\nMAIL_USE_TLS = parse_boolean(os.environ.get('REDASH_MAIL_USE_TLS', 'false'))\nMAIL_USE_SSL = parse_boolean(os.environ.get('REDASH_MAIL_USE_SSL', 'false'))\nMAIL_USERNAME = os.environ.get('REDASH_MAIL_USERNAME', None)\nMAIL_PASSWORD = os.environ.get('REDASH_MAIL_PASSWORD', None)\nMAIL_DEFAULT_SENDER = os.environ.get('REDASH_MAIL_DEFAULT_SENDER', None)\nMAIL_MAX_EMAILS = os.environ.get('REDASH_MAIL_MAX_EMAILS', None)\nMAIL_ASCII_ATTACHMENTS = parse_boolean(os.environ.get('REDASH_MAIL_ASCII_ATTACHMENTS', 'false'))\n\nHOST = os.environ.get('REDASH_HOST', '')\n\nALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE = os.environ.get('REDASH_ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE', \"({state}) {alert_name}\")\n\n# CORS settings for the Query Result API (and possbily future external APIs).\n# In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN\n# to the calling domain (or domains in a comma separated list).\nACCESS_CONTROL_ALLOW_ORIGIN = set_from_string(os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN\", \"\"))\nACCESS_CONTROL_ALLOW_CREDENTIALS = parse_boolean(os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_ALLOW_CREDENTIALS\", \"false\"))\nACCESS_CONTROL_REQUEST_METHOD = os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_REQUEST_METHOD\", \"GET, POST, PUT\")\nACCESS_CONTROL_ALLOW_HEADERS = os.environ.get(\"REDASH_CORS_ACCESS_CONTROL_ALLOW_HEADERS\", \"Content-Type\")\n\n# Query Runners\ndefault_query_runners = [\n    'redash.query_runner.big_query',\n    'redash.query_runner.google_spreadsheets',\n    'redash.query_runner.graphite',\n    'redash.query_runner.mongodb',\n    'redash.query_runner.mysql',\n    'redash.query_runner.pg',\n    'redash.query_runner.url',\n    'redash.query_runner.influx_db',\n    'redash.query_runner.elasticsearch',\n    'redash.query_runner.presto',\n    'redash.query_runner.hive_ds',\n    'redash.query_runner.impala_ds',\n    'redash.query_runner.vertica',\n    'redash.query_runner.treasuredata',\n    'redash.query_runner.sqlite',\n    'redash.query_runner.dynamodb_sql',\n    'redash.query_runner.mssql',\n    'redash.query_runner.jql'\n]\n\nenabled_query_runners = array_from_string(os.environ.get(\"REDASH_ENABLED_QUERY_RUNNERS\", \",\".join(default_query_runners)))\nadditional_query_runners = array_from_string(os.environ.get(\"REDASH_ADDITIONAL_QUERY_RUNNERS\", \"\"))\ndisabled_query_runners = array_from_string(os.environ.get(\"REDASH_DISABLED_QUERY_RUNNERS\", \"\"))\n\nQUERY_RUNNERS = remove(set(disabled_query_runners), distinct(enabled_query_runners + additional_query_runners))\n\n# Destinations\ndefault_destinations = [\n    'redash.destinations.email',\n    'redash.destinations.slack',\n    'redash.destinations.webhook',\n    'redash.destinations.hipchat',\n]\n\nenabled_destinations = array_from_string(os.environ.get(\"REDASH_ENABLED_DESTINATIONS\", \",\".join(default_destinations)))\nadditional_destinations = array_from_string(os.environ.get(\"REDASH_ADDITIONAL_DESTINATIONS\", \"\"))\n\nDESTINATIONS = distinct(enabled_destinations + additional_destinations)\n\nEVENT_REPORTING_WEBHOOKS = array_from_string(os.environ.get(\"REDASH_EVENT_REPORTING_WEBHOOKS\", \"\"))\n\n# Support for Sentry (http://getsentry.com/). Just set your Sentry DSN to enable it:\nSENTRY_DSN = os.environ.get(\"REDASH_SENTRY_DSN\", \"\")\n\n# Client side toggles:\nALLOW_SCRIPTS_IN_USER_INPUT = parse_boolean(os.environ.get(\"REDASH_ALLOW_SCRIPTS_IN_USER_INPUT\", \"false\"))\nDATE_FORMAT = os.environ.get(\"REDASH_DATE_FORMAT\", \"DD/MM/YY\")\n\n# Features:\nFEATURE_ALLOW_ALL_TO_EDIT_QUERIES = parse_boolean(os.environ.get(\"REDASH_FEATURE_ALLOW_ALL_TO_EDIT\", \"true\"))\nVERSION_CHECK = parse_boolean(os.environ.get(\"REDASH_VERSION_CHECK\", \"true\"))\nFEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(os.environ.get(\"REDASH_FEATURE_DISABLE_REFRESH_QUERIES\", \"false\"))\nFEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(os.environ.get(\"REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT\", \"true\"))\n\n# BigQuery\nBIGQUERY_HTTP_TIMEOUT = int(os.environ.get(\"REDASH_BIGQUERY_HTTP_TIMEOUT\", \"600\"))\n\n# Enhance schema fetching\nSCHEMA_RUN_TABLE_SIZE_CALCULATIONS = parse_boolean(os.environ.get(\"REDASH_SCHEMA_RUN_TABLE_SIZE_CALCULATIONS\", \"false\"))\n\n# Allow Parameters in Embeds\n# WARNING: With this option enabled, Redash reads query parameters from the request URL (risk of SQL injection!)\nALLOW_PARAMETERS_IN_EMBEDS = parse_boolean(os.environ.get(\"REDASH_ALLOW_PARAMETERS_IN_EMBEDS\", \"false\"))\n\n### Common Client config\nCOMMON_CLIENT_CONFIG = {\n    'allowScriptsInUserInput': ALLOW_SCRIPTS_IN_USER_INPUT,\n    'dateFormat': DATE_FORMAT,\n    'dateTimeFormat': \"{0} HH:mm\".format(DATE_FORMAT),\n    'allowAllToEditQueries': FEATURE_ALLOW_ALL_TO_EDIT_QUERIES,\n    'mailSettingsMissing': MAIL_DEFAULT_SENDER is None,\n    'logoUrl': LOGO_URL\n}\n", "path": "redash/settings.py"}]}