about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--wqflask/base/data_set/__init__.py2
-rw-r--r--wqflask/base/data_set/datasetgroup.py3
-rw-r--r--wqflask/gn2_main.py60
-rw-r--r--wqflask/run_gunicorn.py2
-rw-r--r--wqflask/runserver.py2
-rw-r--r--wqflask/utility/authentication_tools.py4
-rw-r--r--wqflask/utility/startup_config.py4
-rw-r--r--wqflask/wqflask/__init__.py172
-rw-r--r--wqflask/wqflask/app_errors.py5
-rw-r--r--wqflask/wqflask/oauth2/client.py34
-rw-r--r--wqflask/wqflask/oauth2/request_utils.py7
-rw-r--r--wqflask/wqflask/templates/base.html18
-rw-r--r--wqflask/wqflask/templates/search_result_page.html24
-rw-r--r--wqflask/wqflask/templates/show_trait.html30
-rw-r--r--wqflask/wqflask/templates/tool_buttons.html2
-rw-r--r--wqflask/wqflask/views.py1
16 files changed, 188 insertions, 182 deletions
diff --git a/wqflask/base/data_set/__init__.py b/wqflask/base/data_set/__init__.py
index ad51e47e..c2c0aef1 100644
--- a/wqflask/base/data_set/__init__.py
+++ b/wqflask/base/data_set/__init__.py
@@ -115,7 +115,7 @@ def datasets(group_name, this_group=None, redis_conn=Redis()):
                 dataset_menu.append(dict(tissue=tissue_name,
                                          datasets=[(dataset, dataset_short)]))
 
-    if get_setting_bool("USE_REDIS"):
+    if get_setting_bool(app, "USE_REDIS"):
         redis_conn.set(key, pickle.dumps(dataset_menu, pickle.HIGHEST_PROTOCOL))
         redis_conn.expire(key, 60 * 5)
 
diff --git a/wqflask/base/data_set/datasetgroup.py b/wqflask/base/data_set/datasetgroup.py
index 90c59a1e..a44cf18d 100644
--- a/wqflask/base/data_set/datasetgroup.py
+++ b/wqflask/base/data_set/datasetgroup.py
@@ -15,6 +15,7 @@ from wqflask.database import database_connection
 from utility.configuration import (
     locate,
     flat_files,
+    get_setting,
     flat_file_exists,
     get_setting_bool,
     locate_ignore_error)
@@ -128,7 +129,7 @@ class DatasetGroup:
         return study_samples
 
     def get_genofiles(self):
-        jsonfile = "%s/%s.json" % (webqtlConfig.GENODIR, self.name)
+        jsonfile = "%s/%s.json" % (get_setting(app, "WEBQTL_GENODIR"), self.name)
         try:
             f = open(jsonfile)
         except:
diff --git a/wqflask/gn2_main.py b/wqflask/gn2_main.py
new file mode 100644
index 00000000..923a35d3
--- /dev/null
+++ b/wqflask/gn2_main.py
@@ -0,0 +1,60 @@
+"""Main app creation module"""
+import time
+
+from flask import g, session, request
+
+from wqflask import create_app
+from wqflask.user_session import UserSession
+from gn3.authentication import DataRole, AdminRole
+
+app = create_app()
+
+@app.before_request
+def before_request():
+    g.request_start_time = time.time()
+    g.request_time = lambda: "%.5fs" % (time.time() - g.request_start_time)
+
+    token = session.get("oauth2_token", False)
+    if token and not bool(session.get("user_details", False)):
+        config = current_app.config
+        client = OAuth2Session(
+            config["OAUTH2_CLIENT_ID"], config["OAUTH2_CLIENT_SECRET"],
+            token=token)
+        resp = client.get(
+            urljoin(config["GN_SERVER_URL"], "oauth2/user"))
+        user_details = resp.json()
+        session["user_details"] = user_details
+
+        if user_details.get("error") == "invalid_token":
+            flash(user_details["error_description"], "alert-danger")
+            flash("You are now logged out.", "alert-info")
+            session.pop("user_details", None)
+            session.pop("oauth2_token", None)
+
+@app.context_processor
+def include_admin_role_class():
+    return {'AdminRole': AdminRole}
+
+
+@app.context_processor
+def include_data_role_class():
+    return {'DataRole': DataRole}
+
+@app.before_request
+def get_user_session():
+    g.user_session = UserSession()
+    # I think this should solve the issue of deleting the cookie and redirecting to the home page when a user's session has expired
+    if not g.user_session:
+        response = make_response(redirect(url_for('login')))
+        response.set_cookie('session_id_v2', '', expires=0)
+        return response
+
+@app.after_request
+def set_user_session(response):
+    if hasattr(g, 'user_session'):
+        if not request.cookies.get(g.user_session.cookie_name):
+            response.set_cookie(g.user_session.cookie_name,
+                                g.user_session.cookie)
+    else:
+        response.set_cookie('session_id_v2', '', expires=0)
+    return response
diff --git a/wqflask/run_gunicorn.py b/wqflask/run_gunicorn.py
index 03f310eb..af117466 100644
--- a/wqflask/run_gunicorn.py
+++ b/wqflask/run_gunicorn.py
@@ -9,7 +9,7 @@
 
 print("===> Starting up Gunicorn process")
 
-from wqflask import app
+from gn2_main import app
 from utility.startup_config import app_config
 
 app_config()
diff --git a/wqflask/runserver.py b/wqflask/runserver.py
index 8f01425e..fed3d356 100644
--- a/wqflask/runserver.py
+++ b/wqflask/runserver.py
@@ -7,7 +7,7 @@
 #
 # /sbin/iptables -A INPUT -p tcp -i eth0 -s ! 71.236.239.43 --dport 5003 -j DROP
 
-from wqflask import app
+from gn2_main import app
 from utility.startup_config import app_config
 from utility.configuration import get_setting, get_setting_int
 
diff --git a/wqflask/utility/authentication_tools.py b/wqflask/utility/authentication_tools.py
index 3d732228..0f48d26f 100644
--- a/wqflask/utility/authentication_tools.py
+++ b/wqflask/utility/authentication_tools.py
@@ -37,7 +37,7 @@ def check_resource_availability(dataset, user_id, trait_id=None):
         return webqtlConfig.SUPER_PRIVILEGES
 
     response = None
-    the_url = f"{get_setting('GN_PROXY_URL')}available?resource={resource_id}&user={user_id}"
+    the_url = f"{get_setting(app, 'GN_PROXY_URL')}available?resource={resource_id}&user={user_id}"
     try:
         response = json.loads(requests.get(the_url).content)
     except:
@@ -94,7 +94,7 @@ def get_group_code(dataset):
 
 def check_admin(resource_id=None):
     the_url = (
-        f"{get_setting('GN_PROXY_URL')}available?resource={resource_id}"
+        f"{get_setting(app, 'GN_PROXY_URL')}available?resource={resource_id}"
         f"&user={g.user_session.user_id}")
     try:
         response = json.loads(requests.get(the_url).content)['admin']
diff --git a/wqflask/utility/startup_config.py b/wqflask/utility/startup_config.py
index 5ab43b1a..59f78d55 100644
--- a/wqflask/utility/startup_config.py
+++ b/wqflask/utility/startup_config.py
@@ -1,6 +1,4 @@
-
-from wqflask import app
-
+"""Utility to print out some startup messages."""
 from utility.configuration import show_settings
 from utility.configuration import get_setting_int
 from utility.configuration import get_setting
diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py
index 86370183..3b7551e6 100644
--- a/wqflask/wqflask/__init__.py
+++ b/wqflask/wqflask/__init__.py
@@ -17,8 +17,6 @@ from utility.tools import set_mandatory_settings
 from utility.hmac import data_hmac, url_for_hmac
 from utility.configuration import tempdir, override_from_envvars
 
-from gn3.authentication import DataRole, AdminRole
-
 from wqflask.database import parse_db_url
 from wqflask.user_session import UserSession
 from wqflask.group_manager import group_management
@@ -42,115 +40,6 @@ from wqflask.oauth2.request_utils import user_details, authserver_authorise_uri
 
 from wqflask.jupyter_notebooks import jupyter_notebooks
 
-app = Flask(__name__)
-
-
-# See http://flask.pocoo.org/docs/config/#configuring-from-files
-# Note no longer use the badly named WQFLASK_OVERRIDES (nyi)
-app.config.from_envvar('GN2_SETTINGS')
-
-DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT = parse_db_url(app.config.get('SQL_URI'))
-app.config["DB_HOST"] = DB_HOST
-app.config["DB_USER"] = DB_USER
-app.config["DB_PASS"] = DB_PASS
-app.config["DB_NAME"] = DB_NAME
-app.config["DB_PORT"] = DB_PORT
-
-app.jinja_env.globals.update(
-    undefined=jinja2.StrictUndefined,
-    numify=formatting.numify,
-    logged_in=user_logged_in,
-    authserver_authorise_uri=authserver_authorise_uri,
-    user_details=user_details,
-    num_collections=num_collections,
-    url_for_hmac=url_for_hmac,
-    data_hmac=data_hmac)
-
-app.config["SESSION_REDIS"] = redis.from_url(app.config["REDIS_URL"])
-
-# Override settings
-app = override_from_envvars(app)
-app = set_mandatory_settings(app)
-app = webqtlConfig.init_app(app)
-
-# Registering blueprints
-app.register_blueprint(toplevel)
-app.register_blueprint(glossary_blueprint, url_prefix="/glossary")
-app.register_blueprint(references_blueprint, url_prefix="/references")
-app.register_blueprint(links_blueprint, url_prefix="/links")
-app.register_blueprint(policies_blueprint, url_prefix="/policies")
-app.register_blueprint(environments_blueprint, url_prefix="/environments")
-app.register_blueprint(facilities_blueprint, url_prefix="/facilities")
-app.register_blueprint(blogs_blueprint, url_prefix="/blogs")
-app.register_blueprint(news_blueprint, url_prefix="/news")
-app.register_blueprint(jupyter_notebooks, url_prefix="/jupyter_notebooks")
-
-app.register_blueprint(resource_management, url_prefix="/resource-management")
-app.register_blueprint(metadata_edit, url_prefix="/datasets/")
-app.register_blueprint(group_management, url_prefix="/group-management")
-app.register_blueprint(jobs_bp, url_prefix="/jobs")
-app.register_blueprint(oauth2, url_prefix="/oauth2")
-
-from wqflask.decorators import AuthorisationError
-from wqflask.app_errors import (
-    handle_generic_exceptions, handle_authorisation_error)
-app.register_error_handler(Exception, handle_generic_exceptions)
-app.register_error_handler(AuthorisationError, handle_authorisation_error)
-
-server_session = Session(app)
-
-@app.before_request
-def before_request():
-    g.request_start_time = time.time()
-    g.request_time = lambda: "%.5fs" % (time.time() - g.request_start_time)
-
-    token = session.get("oauth2_token", False)
-    if token and not bool(session.get("user_details", False)):
-        config = current_app.config
-        client = OAuth2Session(
-            config["OAUTH2_CLIENT_ID"], config["OAUTH2_CLIENT_SECRET"],
-            token=token)
-        resp = client.get(
-            urljoin(config["GN_SERVER_URL"], "oauth2/user"))
-        user_details = resp.json()
-        session["user_details"] = user_details
-
-        if user_details.get("error") == "invalid_token":
-            flash(user_details["error_description"], "alert-danger")
-            flash("You are now logged out.", "alert-info")
-            session.pop("user_details", None)
-            session.pop("oauth2_token", None)
-
-@app.context_processor
-def include_admin_role_class():
-    return {'AdminRole': AdminRole}
-
-
-@app.context_processor
-def include_data_role_class():
-    return {'DataRole': DataRole}
-
-@app.before_request
-def get_user_session():
-    g.user_session = UserSession()
-    # I think this should solve the issue of deleting the cookie and redirecting to the home page when a user's session has expired
-    if not g.user_session:
-        response = make_response(redirect(url_for('login')))
-        response.set_cookie('session_id_v2', '', expires=0)
-        return response
-
-
-@app.after_request
-def set_user_session(response):
-    if hasattr(g, 'user_session'):
-        if not request.cookies.get(g.user_session.cookie_name):
-            response.set_cookie(g.user_session.cookie_name,
-                                g.user_session.cookie)
-    else:
-        response.set_cookie('session_id_v2', '', expires=0)
-    return response
-
-
 from wqflask import group_manager
 from wqflask import resource_manager
 from wqflask import search_results
@@ -160,5 +49,62 @@ from wqflask import update_search_results
 from wqflask import docs
 from wqflask import db_info
 
-import wqflask.views
-import wqflask.partial_correlations_views
+def create_app():
+    """Create the application object."""
+    app = Flask(__name__)
+
+
+    # See http://flask.pocoo.org/docs/config/#configuring-from-files
+    # Note no longer use the badly named WQFLASK_OVERRIDES (nyi)
+    app.config.from_envvar('GN2_SETTINGS')
+
+    DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT = parse_db_url(app.config.get('SQL_URI'))
+    app.config["DB_HOST"] = DB_HOST
+    app.config["DB_USER"] = DB_USER
+    app.config["DB_PASS"] = DB_PASS
+    app.config["DB_NAME"] = DB_NAME
+    app.config["DB_PORT"] = DB_PORT
+
+    app.jinja_env.globals.update(
+        undefined=jinja2.StrictUndefined,
+        numify=formatting.numify,
+        logged_in=user_logged_in,
+        authserver_authorise_uri=authserver_authorise_uri,
+        user_details=user_details,
+        num_collections=num_collections,
+        url_for_hmac=url_for_hmac,
+        data_hmac=data_hmac)
+
+    app.config["SESSION_REDIS"] = redis.from_url(app.config["REDIS_URL"])
+
+    # Override settings
+    app = override_from_envvars(app)
+    app = set_mandatory_settings(app)
+    app = webqtlConfig.init_app(app)
+
+    # Registering blueprints
+    app.register_blueprint(toplevel)
+    app.register_blueprint(glossary_blueprint, url_prefix="/glossary")
+    app.register_blueprint(references_blueprint, url_prefix="/references")
+    app.register_blueprint(links_blueprint, url_prefix="/links")
+    app.register_blueprint(policies_blueprint, url_prefix="/policies")
+    app.register_blueprint(environments_blueprint, url_prefix="/environments")
+    app.register_blueprint(facilities_blueprint, url_prefix="/facilities")
+    app.register_blueprint(blogs_blueprint, url_prefix="/blogs")
+    app.register_blueprint(news_blueprint, url_prefix="/news")
+    app.register_blueprint(jupyter_notebooks, url_prefix="/jupyter_notebooks")
+
+    app.register_blueprint(resource_management, url_prefix="/resource-management")
+    app.register_blueprint(metadata_edit, url_prefix="/datasets/")
+    app.register_blueprint(group_management, url_prefix="/group-management")
+    app.register_blueprint(jobs_bp, url_prefix="/jobs")
+    app.register_blueprint(oauth2, url_prefix="/oauth2")
+
+    from wqflask.decorators import AuthorisationError
+    from wqflask.app_errors import (
+        handle_generic_exceptions, handle_authorisation_error)
+    app.register_error_handler(Exception, handle_generic_exceptions)
+    app.register_error_handler(AuthorisationError, handle_authorisation_error)
+
+    server_session = Session(app)
+    return app
diff --git a/wqflask/wqflask/app_errors.py b/wqflask/wqflask/app_errors.py
index c24fc9be..0a1f87ec 100644
--- a/wqflask/wqflask/app_errors.py
+++ b/wqflask/wqflask/app_errors.py
@@ -1,9 +1,12 @@
 """Handle errors at the application's top-level"""
+import os
+import sys
+import random
 import datetime
 import traceback
 
 import werkzeug
-from flask import request, render_template, current_app as app
+from flask import request, make_response, render_template, current_app as app
 
 from wqflask.decorators import AuthorisationError
 
diff --git a/wqflask/wqflask/oauth2/client.py b/wqflask/wqflask/oauth2/client.py
index 2a06b156..91cbb3e9 100644
--- a/wqflask/wqflask/oauth2/client.py
+++ b/wqflask/wqflask/oauth2/client.py
@@ -9,6 +9,8 @@ from pymonad.maybe import Just, Maybe, Nothing
 from pymonad.either import Left, Right, Either
 from authlib.integrations.requests_client import OAuth2Session
 
+from utility.configuration import get_setting
+
 from wqflask.oauth2 import session
 from wqflask.oauth2.checks import user_logged_in
 
@@ -17,10 +19,9 @@ SCOPE = ("profile group role resource register-client user masquerade "
 
 def oauth2_client():
     def __client__(token) -> OAuth2Session:
-        from utility.tools import (
-            GN_SERVER_URL, OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET)
         return OAuth2Session(
-            OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET,
+            get_setting(app, "OAUTH2_CLIENT_ID"),
+            get_setting(app, "OAUTH2_CLIENT_SECRET"),
             scope=SCOPE, token_endpoint_auth_method="client_secret_post",
             token=token)
     return session.user_token().either(
@@ -39,13 +40,12 @@ def __no_token__(_err) -> Left:
 
 def oauth2_get(uri_path: str, data: dict = {}, **kwargs) -> Either:
     def __get__(token) -> Either:
-        from utility.tools import (
-            GN_SERVER_URL, OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET)
         client = OAuth2Session(
-            OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET,
+            get_setting(app, "OAUTH2_CLIENT_ID"),
+            get_setting(app, "OAUTH2_CLIENT_SECRET"),
             token=token, scope=SCOPE)
         resp = client.get(
-            urljoin(GN_SERVER_URL, uri_path),
+            urljoin(get_setting(app, "GN_SERVER_URL"), uri_path),
             data=data,
             **kwargs)
         if resp.status_code == 200:
@@ -59,13 +59,13 @@ def oauth2_post(
         uri_path: str, data: Optional[dict] = None, json: Optional[dict] = None,
         **kwargs) -> Either:
     def __post__(token) -> Either:
-        from utility.tools import (
-            GN_SERVER_URL, OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET)
         client = OAuth2Session(
-            OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET,
+            get_setting(app, "OAUTH2_CLIENT_ID"),
+            get_setting(app, "OAUTH2_CLIENT_SECRET"),
             token=token, scope=SCOPE)
         resp = client.post(
-            urljoin(GN_SERVER_URL, uri_path), data=data, json=json,
+            urljoin(get_setting(app, "GN_SERVER_URL"), uri_path),
+            data=data, json=json,
             **kwargs)
         if resp.status_code == 200:
             return Right(resp.json())
@@ -75,22 +75,20 @@ def oauth2_post(
     return session.user_token().either(__no_token__, __post__)
 
 def no_token_get(uri_path: str, **kwargs) -> Either:
-    from utility.tools import GN_SERVER_URL
-    resp = requests.get(urljoin(GN_SERVER_URL, uri_path), **kwargs)
+    resp = requests.get(urljoin(
+        get_setting(app, "GN_SERVER_URL"), uri_path), **kwargs)
     if resp.status_code == 200:
         return Right(resp.json())
     return Left(resp)
 
 def no_token_post(uri_path: str, **kwargs) -> Either:
-    from utility.tools import (
-        GN_SERVER_URL, OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET)
     data = kwargs.get("data", {})
     the_json = kwargs.get("json", {})
     request_data = {
         **data,
         **the_json,
-        "client_id": OAUTH2_CLIENT_ID,
-        "client_secret": OAUTH2_CLIENT_SECRET
+        "client_id": get_setting(app, "OAUTH2_CLIENT_ID"),
+        "client_secret": get_setting(app, "OAUTH2_CLIENT_SECRET")
     }
     new_kwargs = {
         **{
@@ -99,7 +97,7 @@ def no_token_post(uri_path: str, **kwargs) -> Either:
         },
         ("data" if bool(data) else "json"): request_data
     }
-    resp = requests.post(urljoin(GN_SERVER_URL, uri_path),
+    resp = requests.post(urljoin(get_setting(app, "GN_SERVER_URL"), uri_path),
                          **new_kwargs)
     if resp.status_code == 200:
         return Right(resp.json())
diff --git a/wqflask/wqflask/oauth2/request_utils.py b/wqflask/wqflask/oauth2/request_utils.py
index 992720f1..b7d57ad8 100644
--- a/wqflask/wqflask/oauth2/request_utils.py
+++ b/wqflask/wqflask/oauth2/request_utils.py
@@ -7,16 +7,17 @@ from flask import (
     flash, request, session, url_for, redirect, Response, render_template,
     current_app as app)
 
+from utility.configuration import get_setting
+
 from .client import SCOPE, oauth2_get
 
 def authserver_authorise_uri():
-    from utility.tools import GN_SERVER_URL, OAUTH2_CLIENT_ID
     req_baseurl = urlparse(request.base_url)
     host_uri = f"{req_baseurl.scheme}://{req_baseurl.netloc}/"
     return urljoin(
-        GN_SERVER_URL,
+        get_setting(app, "GN_SERVER_URL"),
         "oauth2/authorise?response_type=code"
-        f"&client_id={OAUTH2_CLIENT_ID}"
+        f"&client_id={get_setting(app, 'OAUTH2_CLIENT_ID')}"
         f"&redirect_uri={urljoin(host_uri, 'oauth2/code')}")
 
 def raise_unimplemented():
diff --git a/wqflask/wqflask/templates/base.html b/wqflask/wqflask/templates/base.html
index 1328d229..5a881f51 100644
--- a/wqflask/wqflask/templates/base.html
+++ b/wqflask/wqflask/templates/base.html
@@ -12,7 +12,7 @@
     </script>
     <link rel="icon" type="image/png" sizes="64x64" href="/static/new/images/CITGLogo.png">
     <link rel="apple-touch-icon" type="image/png" sizes="64x64" href="/static/new/images/CITGLogo.png">
-    <link REL="stylesheet" TYPE="text/css" href="{{ url_for('css', filename='bootstrap/css/bootstrap.css') }}" />
+    <link REL="stylesheet" TYPE="text/css" href="{{ url_for('toplevel.main_views.css', filename='bootstrap/css/bootstrap.css') }}" />
     <link REL="stylesheet" TYPE="text/css" href="/static/new/css/bootstrap-custom.css" />
     <link REL="stylesheet" TYPE="text/css" href="/static/new/css/non-responsive.css" />
     <link REL="stylesheet" TYPE="text/css" href="/static/new/css/docs.css" />
@@ -30,10 +30,10 @@
             border-radius: 1rem;
         }
         table.dataTable thead .sorting_asc {
-            background-image: url({{ url_for("js", filename="DataTables/images/sort_asc_disabled.png") }});
+            background-image: url({{ url_for("toplevel.main_views.js", filename="DataTables/images/sort_asc_disabled.png") }});
         }
         table.dataTable thead .sorting_desc {
-            background-image: url({{ url_for("js", filename="DataTables/images/sort_desc_disabled.png") }});
+            background-image: url({{ url_for("toplevel.main_views.js", filename="DataTables/images/sort_desc_disabled.png") }});
         }
 
 
@@ -336,8 +336,8 @@
         <!--  <button class="btn btn-primary">Save changes</button>-->
         <!--</div>-->
     </div>
-    <script src="{{ url_for('js', filename='jquery/jquery.min.js') }}" type="text/javascript"></script>
-    <script src="{{ url_for('js', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
+    <script src="{{ url_for('toplevel.main_views.js', filename='jquery/jquery.min.js') }}" type="text/javascript"></script>
+    <script src="{{ url_for('toplevel.main_views.js', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
     <script src="/static/new/javascript/search_autocomplete.js"></script>
     <script>
     //http://stackoverflow.com/questions/11521763/bootstrap-scrollspy-not-working
@@ -389,11 +389,11 @@
 
     });
     </script>
-    <script src="{{ url_for('js', filename='jquery-cookie/jquery.cookie.js') }}" type="text/javascript"></script>
+    <script src="{{ url_for('toplevel.main_views.js', filename='jquery-cookie/jquery.cookie.js') }}" type="text/javascript"></script>
     <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
-    <!-- <script src="{{ url_for('js', filename='jquery-ui/jquery-ui.min.js') }}" type="text/javascript"></script> -->
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='colorbox/jquery.colorbox-min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='js_alt/parsley.min.js') }}"></script>
+    <!-- <script src="{{ url_for('toplevel.main_views.js', filename='jquery-ui/jquery-ui.min.js') }}" type="text/javascript"></script> -->
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='colorbox/jquery.colorbox-min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='js_alt/parsley.min.js') }}"></script>
     {% block js %}
     {% endblock %}
     <script type="text/javascript">
diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html
index 495c5669..fb92b9ed 100644
--- a/wqflask/wqflask/templates/search_result_page.html
+++ b/wqflask/wqflask/templates/search_result_page.html
@@ -1,10 +1,10 @@
 {% extends "base.html" %}
 {% block title %}Search Results{% endblock %}
 {% block css %}
-    <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
-    <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='fontawesome/css/font-awesome.min.css') }}" />
-    <link rel="stylesheet" type="text/css" href="{{ url_for('js', filename='DataTablesExtensions/buttonStyles/css/buttons.dataTables.min.css') }}">
-    <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='fontawesome/css/all.min.css') }}"/>
+    <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.css', filename='DataTables/css/jquery.dataTables.css') }}" />
+    <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.css', filename='fontawesome/css/font-awesome.min.css') }}" />
+    <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/buttonStyles/css/buttons.dataTables.min.css') }}">
+    <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.css', filename='fontawesome/css/all.min.css') }}"/>
     <link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
     <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
     <link rel="stylesheet" type="text/css" href="static/new/css/trait_list.css" />
@@ -153,14 +153,14 @@
 {% endblock %}
 
 {% block js %}
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='js_alt/md5.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/scroller/js/dataTables.scroller.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='jszip/jszip.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/plugins/sorting/natural.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/buttons/js/dataTables.buttons.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/buttons/js/buttons.colVis.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='fontawesome/js/all.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='js_alt/md5.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/scroller/js/dataTables.scroller.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='jszip/jszip.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/plugins/sorting/natural.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/buttons/js/dataTables.buttons.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/buttons/js/buttons.colVis.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='fontawesome/js/all.min.js') }}"></script>
 
     <script language="javascript" type="text/javascript" src="/static/new/javascript/search_results.js"></script>
     <script language="javascript" type="text/javascript" src="/static/new/javascript/table_functions.js"></script>
diff --git a/wqflask/wqflask/templates/show_trait.html b/wqflask/wqflask/templates/show_trait.html
index 84231d34..3b3a8ac7 100644
--- a/wqflask/wqflask/templates/show_trait.html
+++ b/wqflask/wqflask/templates/show_trait.html
@@ -8,9 +8,9 @@
   <link rel="stylesheet" type="text/css" href="/static/new/css/box_plot.css" />
   <link rel="stylesheet" type="text/css" href="/static/new/css/prob_plot.css" />
   <link rel="stylesheet" type="text/css" href="/static/new/css/scatter-matrix.css" />
-  <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='d3-tip/d3-tip.css') }}" />
-  <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
-  <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='nouislider/nouislider.min.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.css', filename='d3-tip/d3-tip.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.css', filename='DataTables/css/jquery.dataTables.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('toplevel.main_views.css', filename='nouislider/nouislider.min.css') }}" />
   <link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
   <link rel="stylesheet" type="text/css" href="/static/new/css/trait_list.css" />
   <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
@@ -139,13 +139,13 @@
         $('.collapse').collapse()
     </script>
 
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='d3js/d3.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='js_alt/underscore.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='underscore-string/underscore.string.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='d3-tip/d3-tip.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='js_alt/jstat.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='shapiro-wilk/shapiro-wilk.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='plotly/plotly.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='d3js/d3.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='js_alt/underscore.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='underscore-string/underscore.string.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='d3-tip/d3-tip.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='js_alt/jstat.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='shapiro-wilk/shapiro-wilk.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='plotly/plotly.min.js') }}"></script>
     <script language="javascript" type="text/javascript" src="/static/new/javascript/colorbrewer.js"></script>
 
     <script language="javascript" type="text/javascript" src="/static/new/javascript/stats.js"></script>
@@ -154,11 +154,11 @@
     <script language="javascript" type="text/javascript" src="/static/new/javascript/compare_traits_scatterplot.js"></script>
 
 
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/plugins/sorting/scientific.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/plugins/sorting/natural.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/scroller/js/dataTables.scroller.min.js') }}"></script>
-    <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='nouislider/nouislider.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTables/js/jquery.dataTables.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/plugins/sorting/scientific.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/plugins/sorting/natural.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='DataTablesExtensions/scroller/js/dataTables.scroller.min.js') }}"></script>
+    <script language="javascript" type="text/javascript" src="{{ url_for('toplevel.main_views.js', filename='nouislider/nouislider.js') }}"></script>
     <script language="javascript" type="text/javascript" src="/static/new/javascript/table_functions.js"></script>
     <script language="javascript" type="text/javascript" src="/static/new/javascript/create_datatable.js"></script>
     <script language="javascript" type="text/javascript" src="/static/new/javascript/initialize_show_trait_tables.js"></script>
diff --git a/wqflask/wqflask/templates/tool_buttons.html b/wqflask/wqflask/templates/tool_buttons.html
index c6d1476c..95064dbb 100644
--- a/wqflask/wqflask/templates/tool_buttons.html
+++ b/wqflask/wqflask/templates/tool_buttons.html
@@ -32,7 +32,7 @@
 
 <button id="partial-correlations"
 	class="btn btn-primary submit_special"
-	data-url="{{url_for('partial_correlations')}}"
+	data-url="{{url_for('toplevel.partial_correlations.partial_correlations')}}"
 	title="Run partial correlations with the selected traits">
     Partial Correlations
 </button>
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index ebd273af..689e3436 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -30,7 +30,6 @@ from flask import current_app as app
 from flask import g
 from flask import Response
 from flask import request
-from flask import make_response
 from flask import render_template
 from flask import send_from_directory
 from flask import redirect