about summary refs log tree commit diff
diff options
context:
space:
mode:
authorzsloan2018-03-29 15:46:45 +0000
committerzsloan2018-03-29 15:46:45 +0000
commitfef4b723d0e9d9d0b5f40bd51c6a2cd31410285b (patch)
tree22268c6f4db65f637070319a497de24b82843f0f
parent6ff7df2360d1a6d0461980c938809165982583e8 (diff)
parentb215b5fe5c6d13f0ed445106230e1e38db71c918 (diff)
downloadgenenetwork2-fef4b723d0e9d9d0b5f40bd51c6a2cd31410285b.tar.gz
Resolved conflict in views.py
-rw-r--r--RELEASE-NOTES.md17
-rwxr-xr-xbin/genenetwork256
-rwxr-xr-xbin/mechnical-rob111
-rwxr-xr-xbin/test-website114
-rw-r--r--etc/default_settings.py18
-rw-r--r--test/requests/link_checker.py63
-rw-r--r--test/requests/main_web_functionality.py40
-rw-r--r--test/requests/mapping_tests.py43
-rw-r--r--test/requests/parametrized_test.py27
-rw-r--r--test/requests/run-integration-tests.py34
-rwxr-xr-xtest/requests/test-website.py71
-rw-r--r--test/requests/test_login_github.py47
-rw-r--r--test/requests/test_login_local.py60
-rw-r--r--test/requests/test_login_orcid.py47
-rw-r--r--test/requests/test_registration.py41
-rw-r--r--test/unittest/test_registration.py27
-rw-r--r--wqflask/flask_security/__init__.py25
-rw-r--r--wqflask/flask_security/changeable.py45
-rw-r--r--wqflask/flask_security/confirmable.py83
-rw-r--r--wqflask/flask_security/core.py382
-rw-r--r--wqflask/flask_security/datastore.py261
-rw-r--r--wqflask/flask_security/decorators.py207
-rw-r--r--wqflask/flask_security/forms.py286
-rw-r--r--wqflask/flask_security/passwordless.py59
-rw-r--r--wqflask/flask_security/recoverable.py80
-rw-r--r--wqflask/flask_security/registerable.py44
-rw-r--r--wqflask/flask_security/script.py130
-rw-r--r--wqflask/flask_security/signals.py29
-rw-r--r--wqflask/flask_security/templates/.DS_Storebin6148 -> 0 bytes
-rw-r--r--wqflask/flask_security/templates/security/.DS_Storebin6148 -> 0 bytes
-rw-r--r--wqflask/flask_security/templates/security/_macros.html16
-rw-r--r--wqflask/flask_security/templates/security/_menu.html15
-rw-r--r--wqflask/flask_security/templates/security/_messages.html9
-rw-r--r--wqflask/flask_security/templates/security/change_password.html11
-rw-r--r--wqflask/flask_security/templates/security/email/change_notice.html4
-rw-r--r--wqflask/flask_security/templates/security/email/change_notice.txt5
-rw-r--r--wqflask/flask_security/templates/security/email/confirmation_instructions.html3
-rw-r--r--wqflask/flask_security/templates/security/email/confirmation_instructions.txt3
-rw-r--r--wqflask/flask_security/templates/security/email/login_instructions.html5
-rw-r--r--wqflask/flask_security/templates/security/email/login_instructions.txt5
-rw-r--r--wqflask/flask_security/templates/security/email/reset_instructions.html1
-rw-r--r--wqflask/flask_security/templates/security/email/reset_instructions.txt3
-rw-r--r--wqflask/flask_security/templates/security/email/reset_notice.html1
-rw-r--r--wqflask/flask_security/templates/security/email/reset_notice.txt1
-rw-r--r--wqflask/flask_security/templates/security/email/welcome.html7
-rw-r--r--wqflask/flask_security/templates/security/email/welcome.txt7
-rw-r--r--wqflask/flask_security/templates/security/forgot_password.html9
-rw-r--r--wqflask/flask_security/templates/security/login_user.html12
-rw-r--r--wqflask/flask_security/templates/security/register_user.html13
-rw-r--r--wqflask/flask_security/templates/security/reset_password.html10
-rw-r--r--wqflask/flask_security/templates/security/send_confirmation.html9
-rw-r--r--wqflask/flask_security/templates/security/send_login.html9
-rw-r--r--wqflask/flask_security/utils.py379
-rw-r--r--wqflask/flask_security/views.py359
-rw-r--r--wqflask/mock/__init__.py0
-rw-r--r--wqflask/mock/es_double.py15
-rw-r--r--wqflask/run_gunicorn.py5
-rw-r--r--wqflask/runserver.py17
-rw-r--r--wqflask/utility/elasticsearch_tools.py59
-rw-r--r--wqflask/utility/startup_config.py39
-rw-r--r--wqflask/utility/tools.py42
-rw-r--r--wqflask/utility/type_checking.py42
-rw-r--r--wqflask/wqflask/__init__.py4
-rw-r--r--wqflask/wqflask/correlation/show_corr_results.py42
-rw-r--r--wqflask/wqflask/gsearch.py6
-rw-r--r--wqflask/wqflask/marker_regression/marker_regression.py2
-rw-r--r--wqflask/wqflask/search_results.py23
-rw-r--r--wqflask/wqflask/templates/new_security/login_user.html49
-rw-r--r--wqflask/wqflask/user_manager.py317
-rw-r--r--wqflask/wqflask/views.py71
70 files changed, 1211 insertions, 2865 deletions
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
new file mode 100644
index 00000000..d15cad1a
--- /dev/null
+++ b/RELEASE-NOTES.md
@@ -0,0 +1,17 @@
+## ChangeLog v2.11 (date unknown)
+
+This is a massive bug fix release with many improvements. For contributions
+see
+[contributors](https://github.com/genenetwork/genenetwork2/contributors)
+and
+[commits](https://github.com/genenetwork/genenetwork2/commits/master).
+
+### Added GEMMA support
+
+* Front-end support 
+
+### Added test framework and unit tests
+
+* Added python integration and unit tests
+
+
diff --git a/bin/genenetwork2 b/bin/genenetwork2
index 8886e4bc..3f06e7f9 100755
--- a/bin/genenetwork2
+++ b/bin/genenetwork2
@@ -68,32 +68,36 @@ if [ "$1" = "-c" -o "$1" = "-gunicorn" ]; then
     echo "Can not use $1 switch without default settings file"
     exit 1
 fi
-# Handle settings parameter (can be .py or .json)
-if [ ! -z $1 ]; then
-    settings=$(realpath "$1")
-    if [ ! -e $settings ]; then
-        settings=$GN2_BASE_DIR/etc/default_settings.py
-    else
-        shift
-    fi
-fi
 
-ext="${settings##*.}"
-if [ "$ext" = "json" -o "$ext" = "JSON" ]; then
-    overrides=$settings
+settings=$1
+if [ -z $settings ]; then
+    settings=$GN2_BASE_DIR/etc/default_settings.py
 else
-    echo $settings
+    shift
 fi
+settings=$(realpath $settings)
+
+# ext="${settings##*.}"
+# if [ "$ext" = "json" -o "$ext" = "JSON" ]; then
+#     overrides=$settings
+# else
+#     echo $settings
+# fi
 
 if [ ! -e $settings ]; then
     echo "ERROR: can not locate settings file - pass it in the command line"
     exit 1
 fi
-export WQFLASK_SETTINGS=$settings     # Python
-export WQFLASK_OVERRIDES=$overrides   # JSON
 
-echo WQFLASK_SETTINGS=$settings
-echo WQFLASK_OVERRIDES=$overrides
+export GN2_SETTINGS=$settings     # Python
+echo GN2_SETTINGS=$settings
+
+# This is a temporary hack to inject ES - should have added python2-elasticsearch package to guix instead
+# if [ -z $ELASTICSEARCH_PROFILE ]; then
+#     echo -e "WARNING: Elastic Search profile has not been set - use ELASTICSEARCH_PROFILE";
+# else
+#     PYTHONPATH="$PYTHONPATH${PYTHONPATH:+:}$ELASTICSEARCH_PROFILE/lib/python2.7/site-packages"
+# fi
 
 if [ -z $GN2_PROFILE ] ; then
     echo "WARNING: GN2_PROFILE has not been set - you need the environment, so I hope you know what you are doing!"
@@ -108,7 +112,7 @@ if [ -z $GN2_PROFILE ]; then
     read -p "PRESS [ENTER] TO CONTINUE..."
 else
     export PATH=$GN2_PROFILE/bin:$PATH
-    export PYTHONPATH=$GN2_PROFILE/lib/python2.7/site-packages
+    export PYTHONPATH="$GN2_PROFILE/lib/python2.7/site-packages" # never inject another PYTHONPATH!!
     export R_LIBS_SITE=$GN2_PROFILE/site-library
     export GEM_PATH=$GN2_PROFILE/lib/ruby/gems/2.4.0
     export JS_GUIX_PATH=$GN2_PROFILE/share/genenetwork2/javascript
@@ -124,7 +128,11 @@ else
     if [ -z $GEMMA_WRAPPER_COMMAND ]; then
         export GEMMA_WRAPPER_COMMAND="$GN2_PROFILE/bin/gemma-wrapper"
     fi
-    if [ ! -d $PYTHONPATH ] ; then echo "PYTHONPATH not valid "$PYTHONPATH ; exit 1 ; fi
+    while IFS=":" read -ra PPATH; do
+	for PPART in "${PPATH[@]}"; do
+	    if [ ! -d $PPART ] ; then echo "$PPART in PYTHONPATH not valid $PYTHONPATH" ; exit 1 ; fi
+	done
+    done <<< "$PYTHONPATH"
     if [ ! -d $R_LIBS_SITE ] ; then echo "R_LIBS_SITE not valid "$R_LIBS_SITE ; exit 1 ; fi
     if [ ! -d $GEM_PATH ] ; then echo "GEM_PATH not valid "$GEM_PATH ; exit 1 ; fi
 fi
@@ -157,8 +165,9 @@ if [ "$1" = '-c' ] ; then
     cd $GN2_BASE_DIR/wqflask
     cmd=${2#wqflask/}
     echo PYTHONPATH=$PYTHONPATH
-    echo RUNNING COMMAND $cmd
-    python $cmd
+    shift ; shift
+    echo RUNNING COMMAND $cmd $*
+    python $cmd $*
     exit $?
 fi
 # Now handle command parameter -cli which runs in bash
@@ -167,8 +176,9 @@ if [ "$1" = "-cli" ] ; then
     cd $GN2_BASE_DIR/wqflask
     cmd=$2
     echo PYTHONPATH=$PYTHONPATH
-    echo RUNNING COMMAND $cmd
-    $cmd
+    shift ; shift
+    echo RUNNING COMMAND $cmd $*
+    $cmd $*
     exit $?
 fi
 if [ "$1" = '-gunicorn' ] ; then
diff --git a/bin/mechnical-rob b/bin/mechnical-rob
new file mode 100755
index 00000000..be223d94
--- /dev/null
+++ b/bin/mechnical-rob
@@ -0,0 +1,111 @@
+#!/usr/bin/env ruby
+
+
+USAGE = <<EOT
+This is Mechanical-Rob - an automated web server tester for
+                         Genenetwork.org that uses the brilliant
+                         mechanize gem with minitest.
+
+To use this software you need to install mechanize. Run it from
+the root of the genenetwork2 source tree as, for example,
+
+  ./bin/test-website http://localhost:5003/ (default)
+
+If you are using the small deployment database you can use
+
+  ./bin/test-website --skip -n
+
+To run all tests
+
+  ./bin/test-website --all
+
+To run individual tests on localhost you can do
+
+  ruby -Itest -Itest/lib test/lib/mapping.rb --name="/Mapping/"
+
+For more information see http://genenetwork.org/
+
+EOT
+$stderr.print USAGE
+
+require 'optparse'
+
+options = { database: :small, link_checker: false}
+opts = OptionParser.new do |o|
+  o.banner = "Usage: #{File.basename($0)} [options] URL"
+
+  o.on('-d','--database', String, 'Use database (default db_webqtl_s)') do |s|
+    options[:database] =
+      case s
+      when 'xx'
+        :unknown
+      else
+        :small
+      end
+  end
+
+  o.on('--all', 'Run all tests') do
+    options[:all] = true
+  end
+
+  o.on('-l','--link-checker', 'Check for dead links') do
+    options[:link_checker] = true
+  end
+
+  o.on('--navigation', 'Check for navigation') do
+    options[:navigation] = true
+  end
+
+  o.on('--mapping', 'Check for mapping') do
+    options[:mapping] = true
+  end
+
+  o.on('--skip-broken', 'Skip tests that are known to be broken') do
+    options[:skip_broken] = true
+  end
+
+  o.separator ""
+  o.on_tail('-h', '--help', 'display this help and exit') do
+    options[:show_help] = true
+  end
+end
+
+opts.parse!(ARGV)
+
+if options[:show_help]
+  print opts
+  exit 1
+end
+
+$options = options  # we are using a global here
+$host =
+  if ARGV.size>0
+    ARGV.shift
+  else
+    "http://localhost:5003"
+  end
+
+$stderr.print "Testing <",$host,">\n"
+
+require 'mechanize'
+require 'minitest/spec'
+require 'minitest/autorun'
+
+# These are the actual testing modules
+
+libpath = File.dirname(File.dirname(__FILE__))
+$: << File.join(libpath,'test/lib')
+
+require 'main_web_functionality'
+
+if options[:all] or options[:mapping]
+  require 'mapping'
+end
+
+if options[:all] or options[:link_checker]
+  require 'link_checker'
+end
+
+if options[:all] or options[:navigation]
+  require 'navigation'
+end
diff --git a/bin/test-website b/bin/test-website
index be223d94..5935f016 100755
--- a/bin/test-website
+++ b/bin/test-website
@@ -1,111 +1,7 @@
-#!/usr/bin/env ruby
+#! /bin/bash
 
-
-USAGE = <<EOT
-This is Mechanical-Rob - an automated web server tester for
-                         Genenetwork.org that uses the brilliant
-                         mechanize gem with minitest.
-
-To use this software you need to install mechanize. Run it from
-the root of the genenetwork2 source tree as, for example,
-
-  ./bin/test-website http://localhost:5003/ (default)
-
-If you are using the small deployment database you can use
-
-  ./bin/test-website --skip -n
-
-To run all tests
-
-  ./bin/test-website --all
-
-To run individual tests on localhost you can do
-
-  ruby -Itest -Itest/lib test/lib/mapping.rb --name="/Mapping/"
-
-For more information see http://genenetwork.org/
-
-EOT
-$stderr.print USAGE
-
-require 'optparse'
-
-options = { database: :small, link_checker: false}
-opts = OptionParser.new do |o|
-  o.banner = "Usage: #{File.basename($0)} [options] URL"
-
-  o.on('-d','--database', String, 'Use database (default db_webqtl_s)') do |s|
-    options[:database] =
-      case s
-      when 'xx'
-        :unknown
-      else
-        :small
-      end
-  end
-
-  o.on('--all', 'Run all tests') do
-    options[:all] = true
-  end
-
-  o.on('-l','--link-checker', 'Check for dead links') do
-    options[:link_checker] = true
-  end
-
-  o.on('--navigation', 'Check for navigation') do
-    options[:navigation] = true
-  end
-
-  o.on('--mapping', 'Check for mapping') do
-    options[:mapping] = true
-  end
-
-  o.on('--skip-broken', 'Skip tests that are known to be broken') do
-    options[:skip_broken] = true
-  end
-
-  o.separator ""
-  o.on_tail('-h', '--help', 'display this help and exit') do
-    options[:show_help] = true
-  end
-end
-
-opts.parse!(ARGV)
-
-if options[:show_help]
-  print opts
+if [ -z $GN2_PROFILE ]; then
+  echo "Run request tests with something like"
+  echo env GN2_PROFILE=/home/wrk/opt/gn-latest ./bin/genenetwork2 ./etc/default_settings.py -c ../test/requests/test-website.py http://localhost:5003
   exit 1
-end
-
-$options = options  # we are using a global here
-$host =
-  if ARGV.size>0
-    ARGV.shift
-  else
-    "http://localhost:5003"
-  end
-
-$stderr.print "Testing <",$host,">\n"
-
-require 'mechanize'
-require 'minitest/spec'
-require 'minitest/autorun'
-
-# These are the actual testing modules
-
-libpath = File.dirname(File.dirname(__FILE__))
-$: << File.join(libpath,'test/lib')
-
-require 'main_web_functionality'
-
-if options[:all] or options[:mapping]
-  require 'mapping'
-end
-
-if options[:all] or options[:link_checker]
-  require 'link_checker'
-end
-
-if options[:all] or options[:navigation]
-  require 'navigation'
-end
+fi
diff --git a/etc/default_settings.py b/etc/default_settings.py
index 699d21f1..a70d8aec 100644
--- a/etc/default_settings.py
+++ b/etc/default_settings.py
@@ -41,6 +41,24 @@ SECURITY_POST_LOGIN_VIEW = "/thank_you"
 SERVER_PORT = 5003          # running on localhost
 SECRET_HMAC_CODE = '\x08\xdf\xfa\x93N\x80\xd9\\H@\\\x9f`\x98d^\xb4a;\xc6OM\x946a\xbc\xfc\x80:*\xebc'
 
+GITHUB_CLIENT_ID = "UNKNOWN"
+GITHUB_CLIENT_SECRET = "UNKNOWN"
+GITHUB_AUTH_URL = "UNKNOWN"
+GITHUB_API_URL = "UNKNOWN"
+
+ORCID_CLIENT_ID = "UNKNOWN"
+ORCID_CLIENT_SECRET = "UNKNOWN"
+ORCID_AUTH_URL = "UNKNOWN"
+ORCID_TOKEN_URL = "UNKNOWN"
+
+ELASTICSEARCH_HOST = "localhost"
+ELASTICSEARCH_PORT = '9200'
+
+SMTP_CONNECT = "UNKNOWN"
+SMTP_USERNAME = "UNKNOWN"
+SMTP_PASSWORD = "UNKNOWN"
+
+
 # ---- Behavioural settings (defaults) note that logger and log levels can
 #      be overridden at the module level and with enviroment settings
 WEBSERVER_MODE  = 'DEV'     # Python webserver mode (DEBUG|DEV|PROD)
diff --git a/test/requests/link_checker.py b/test/requests/link_checker.py
new file mode 100644
index 00000000..256bf6ef
--- /dev/null
+++ b/test/requests/link_checker.py
@@ -0,0 +1,63 @@
+from __future__ import print_function
+import re
+import requests
+from lxml.html import parse
+from requests.exceptions import ConnectionError
+
+def is_root_link(link):
+    pattern = re.compile("^/$")
+    return pattern.match(link)
+
+def is_mailto_link(link):
+    pattern = re.compile("^mailto:.*")
+    return pattern.match(link)
+
+def is_internal_link(link):
+    pattern = re.compile("^/.*")
+    return pattern.match(link)
+
+def get_links(doc):
+    return filter(
+        lambda x: not (
+            is_root_link(x)
+            or is_mailto_link(x))
+        , map(lambda y: y.get("href")
+              , doc.cssselect("a")))
+
+def verify_link(link):
+    try:
+        result = requests.get(link, timeout=20)
+        if result.status_code == 200:
+            print(link+" ==> OK")
+        else:
+            print("ERROR: link `"+link+"` failed with status "
+                  , result.status_code)
+    except ConnectionError as ex:
+        print("ERROR: ", link, ex)
+
+def check_page(host, start_url):
+    print("")
+    print("Checking links in page `"+start_url+"`")
+    doc = parse(start_url).getroot()
+    links = get_links(doc)
+    internal_links = filter(is_internal_link, links)
+    external_links = filter(lambda x: not is_internal_link(x), links)
+    external_links.append("http://somenon-existentsite.brr")
+    for link in internal_links:
+        verify_link(host+link)
+
+    for link in external_links:
+        verify_link(link)
+
+def check_links(args_obj, parser):
+    print("")
+    print("Checking links")
+    host = args_obj.host
+
+    # Check the home page
+    check_page(host, host)
+
+    # Check traits page
+    check_page(
+        host,
+        host+"/show_trait?trait_id=1435395_s_at&dataset=HC_M2_0606_P")
diff --git a/test/requests/main_web_functionality.py b/test/requests/main_web_functionality.py
new file mode 100644
index 00000000..7b89b833
--- /dev/null
+++ b/test/requests/main_web_functionality.py
@@ -0,0 +1,40 @@
+from __future__ import print_function
+import re
+import requests
+from lxml.html import parse
+from link_checker import check_page
+from requests.exceptions import ConnectionError
+
+def check_home(url):
+    doc = parse(url).getroot()
+    search_button = doc.cssselect("#btsearch")
+    assert(search_button[0].value == "Search")
+    print("OK")
+
+def check_search_page(host):
+    data = dict(
+        species="mouse"
+        , group="BXD"
+        , type="Hippocampus mRNA"
+        , dataset="HC_M2_0606_P"
+        , search_terms_or=""
+        , search_terms_and="MEAN=(15 16) LRS=(23 46)")
+    result = requests.get(host+"/search", params=data)
+    found = result.text.find("/show_trait?trait_id=1435395_s_at&dataset=HC_M2_0606_P")
+    assert(found >= 0)
+    print("OK")
+    check_traits_page(host, "/show_trait?trait_id=1435395_s_at&dataset=HC_M2_0606_P")
+
+def check_traits_page(host, traits_url):
+    doc = parse(host+traits_url).getroot()
+    traits_form = doc.forms[1]
+    assert(traits_form.fields["corr_dataset"] == "HC_M2_0606_P")
+    print("OK")
+    check_page(host, host+traits_url)
+
+def check_main_web_functionality(args_obj, parser):
+    print("")
+    print("Checking main web functionality...")
+    host = args_obj.host
+    check_home(host)
+    check_search_page(host)
diff --git a/test/requests/mapping_tests.py b/test/requests/mapping_tests.py
new file mode 100644
index 00000000..fd20df11
--- /dev/null
+++ b/test/requests/mapping_tests.py
@@ -0,0 +1,43 @@
+from __future__ import print_function
+import re
+import json
+import requests
+from lxml.html import fromstring
+
+def get_data(list_item):
+    try:
+        value = list_item[1]
+    except:
+        value = None
+    #print("list_item:", list_item, "==>", value)
+    return value
+
+def load_data_from_file():
+    filename = "../test/data/input/mapping/1435395_s_at_HC_M2_0606_P.json"
+    file_handle = open(filename, "r")
+    file_data = json.loads(file_handle.read().encode("utf-8"))
+    return file_data
+
+def check_pylmm_tool_selection(host, data):
+    data["method"] = "pylmm"
+    page = requests.post(host+"/marker_regression", data=data)
+    doc = fromstring(page.text)
+    form = doc.forms[1]
+    assert form.fields["dataset"] == "HC_M2_0606_P"
+    assert form.fields["value:BXD1"] == "15.034" # Check value in the file
+
+def check_R_qtl_tool_selection(host, data):
+    pass
+
+def check_CIM_tool_selection(host, data):
+    pass
+
+def check_mapping(args_obj, parser):
+    print("")
+    print("Checking mapping")
+
+    host = args_obj.host
+    data = load_data_from_file()
+    check_pylmm_tool_selection(host, data)
+    check_R_qtl_tool_selection(host, data)
+    check_CIM_tool_selection(host, data)
diff --git a/test/requests/parametrized_test.py b/test/requests/parametrized_test.py
new file mode 100644
index 00000000..abf98fce
--- /dev/null
+++ b/test/requests/parametrized_test.py
@@ -0,0 +1,27 @@
+import logging
+import unittest
+from elasticsearch import Elasticsearch, TransportError
+
+class ParametrizedTest(unittest.TestCase):
+
+    def __init__(self, methodName='runTest', gn2_url="http://localhost:5003", es_url="localhost:9200"):
+        super(ParametrizedTest, self).__init__(methodName=methodName)
+        self.gn2_url = gn2_url
+        self.es_url = es_url
+
+    def setUp(self):
+        self.es = Elasticsearch([self.es_url])
+        self.es_cleanup = []
+
+        es_logger = logging.getLogger("elasticsearch")
+        es_logger.addHandler(
+            logging.FileHandler("/tmp/es_TestRegistrationInfo.log"))
+        es_trace_logger = logging.getLogger("elasticsearch.trace")
+        es_trace_logger.addHandler(
+            logging.FileHandler("/tmp/es_TestRegistrationTrace.log"))
+
+    def tearDown(self):
+        self.es.delete_by_query(
+            index="users"
+            , doc_type="local"
+            , body={"query":{"match":{"email_address":"test@user.com"}}})
diff --git a/test/requests/run-integration-tests.py b/test/requests/run-integration-tests.py
new file mode 100644
index 00000000..5e816549
--- /dev/null
+++ b/test/requests/run-integration-tests.py
@@ -0,0 +1,34 @@
+import sys
+from test_login_local import TestLoginLocal
+from test_login_orcid import TestLoginOrcid
+from test_login_github import TestLoginGithub
+from test_registration import TestRegistration
+from unittest import TestSuite, TextTestRunner, TestLoader
+
+test_cases = [
+    TestRegistration
+    , TestLoginLocal
+    , TestLoginGithub
+    , TestLoginOrcid
+]
+
+def suite(gn2_url, es_url):
+    the_suite = TestSuite()
+    for case in test_cases:
+        the_suite.addTests(initTest(case, gn2_url, es_url))
+    return the_suite
+
+def initTest(klass, gn2_url, es_url):
+    loader = TestLoader()
+    methodNames = loader.getTestCaseNames(klass)
+    return [klass(mname, gn2_url, es_url) for mname in methodNames]
+
+def main(gn2_url, es_url):
+    runner = TextTestRunner()
+    runner.run(suite(gn2_url, es_url))
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise Exception("Required arguments missing:\n\tTry running `run-integration-test.py <gn2-url> <es-url>`")
+    else:
+        main(sys.argv[1], sys.argv[2])
diff --git a/test/requests/test-website.py b/test/requests/test-website.py
new file mode 100755
index 00000000..2bef6eb1
--- /dev/null
+++ b/test/requests/test-website.py
@@ -0,0 +1,71 @@
+# Run with something like
+#
+#   env GN2_PROFILE=/home/wrk/opt/gn-latest ./bin/genenetwork2 ./etc/default_settings.py -c ../test/requests/test-website.py http://localhost:5003
+#
+# Mostly to pick up the Guix GN2_PROFILE and python modules
+from __future__ import print_function
+import argparse
+from link_checker import check_links
+from mapping_tests import check_mapping
+from main_web_functionality import check_main_web_functionality
+
+print("Mechanical Rob firing up...")
+
+def run_all(args_obj, parser):
+    print("")
+    print("Running all tests.")
+    check_main_web_functionality(args_obj, parser)
+    check_links(args_obj, parser)
+    check_mapping(args_obj, parser)
+    # TODO: Add other functions as they are created.
+
+def print_help(args_obj, parser):
+    print(parser.format_help())
+
+def dummy(args_obj, parser):
+    print("Not implemented yet.")
+
+
+desc = """
+This is Mechanical-Rob - an automated web server tester for
+                         Genenetwork.org
+"""
+parser = argparse.ArgumentParser(description=desc)
+
+parser.add_argument("-d", "--database", metavar="DB", type=str
+                    , default="db_webqtl_s"
+                    , help="Use database (default db_webqtl_s)")
+
+parser.add_argument("host", metavar="HOST", type=str
+                    , default="http://localhost:5003"
+                    , help="The url to the web server")
+
+parser.add_argument("-a", "--all", dest="accumulate", action="store_const"
+                    , const=run_all, default=print_help
+                    , help="Runs all tests.")
+
+parser.add_argument("-l", "--link-checker", dest="accumulate"
+                    , action='store_const', const=check_links, default=print_help
+                    , help="Checks for dead links.")
+
+parser.add_argument("-f", "--main-functionality", dest="accumulate"
+                    , action='store_const', const=check_main_web_functionality
+                    , default=print_help
+                    , help="Checks for main web functionality.")
+
+parser.add_argument("-m", "--mapping", dest="accumulate"
+                    , action="store_const", const=check_mapping, default=print_help
+                    , help="Checks for mapping.")
+
+# parser.add_argument("-n", "--navigation", dest="accumulate"
+#                     , action="store_const", const=check_navigation, default=print_help
+#                     , help="Checks for navigation.")
+
+# parser.add_argument("-s", "--skip-broken", dest="accumulate"
+#                     , action="store_const", const=dummy, default=print_help
+#                     , help="Skip tests that are known to be broken.")
+
+args = parser.parse_args()
+# print("The arguments object: ", args)
+
+args.accumulate(args, parser)
diff --git a/test/requests/test_login_github.py b/test/requests/test_login_github.py
new file mode 100644
index 00000000..1bf4f695
--- /dev/null
+++ b/test/requests/test_login_github.py
@@ -0,0 +1,47 @@
+import uuid
+import requests
+from time import sleep
+from wqflask import app
+from parameterized import parameterized
+from parametrized_test import ParametrizedTest
+
+login_link_text = '<a id="login_in" href="/n/login">Sign in</a>'
+logout_link_text = '<a id="login_out" title="Signed in as ." href="/n/logout">Sign out</a>'
+uid = str(uuid.uuid4())
+
+class TestLoginGithub(ParametrizedTest):
+
+    def setUp(self):
+        super(TestLoginGithub, self).setUp()
+        data = {
+            "user_id": uid
+            , "name": "A. T. Est User"
+            , "github_id": 693024
+            , "user_url": "https://fake-github.com/atestuser"
+            , "login_type": "github"
+            , "organization": ""
+            , "active": 1
+            , "confirmed": 1
+        }
+        self.es.create(index="users", doc_type="local", body=data, id=uid)
+        sleep(1)
+
+    def tearDown(self):
+        super(TestLoginGithub, self).tearDown()
+        self.es.delete(index="users", doc_type="local", id=uid)
+
+    def testLoginUrl(self):
+        login_button_text = '<a href="https://github.com/login/oauth/authorize?client_id=' + app.config.get("GITHUB_CLIENT_ID") + '&amp;client_secret=' + app.config.get("GITHUB_CLIENT_SECRET") + '" title="Login with GitHub" class="btn btn-info btn-group">Login with Github</a>'
+        result = requests.get(self.gn2_url+"/n/login")
+        index = result.content.find(login_button_text)
+        self.assertTrue(index >= 0, "Should have found `Login with Github` button")
+
+    @parameterized.expand([
+        ("1234", login_link_text, "Login should have failed with non-existing user")
+        , (uid, logout_link_text, "Login should have been successful with existing user")
+        ])
+    def testLogin(self, test_uid, expected, message):
+        url = self.gn2_url+"/n/login?type=github&uid="+test_uid
+        result = requests.get(url)
+        index = result.content.find(expected)
+        self.assertTrue(index >= 0, message)
diff --git a/test/requests/test_login_local.py b/test/requests/test_login_local.py
new file mode 100644
index 00000000..808649ca
--- /dev/null
+++ b/test/requests/test_login_local.py
@@ -0,0 +1,60 @@
+import requests
+from wqflask import user_manager
+from parameterized import parameterized
+from parametrized_test import ParametrizedTest
+
+login_link_text = '<a id="login_in" href="/n/login">Sign in</a>'
+logout_link_text = '<a id="login_out" title="Signed in as ." href="/n/logout">Sign out</a>'
+
+class TestLoginLocal(ParametrizedTest):
+
+    def setUp(self):
+        super(TestLoginLocal, self).setUp()
+        self.login_url = self.gn2_url +"/n/login"
+        data = {
+            "es_connection": self.es,
+            "email_address": "test@user.com",
+            "full_name": "Test User",
+            "organization": "Test Organisation",
+            "password": "test_password",
+            "password_confirm": "test_password"
+        }
+        user_manager.basic_info = lambda : { "basic_info": "basic" }
+        user_manager.RegisterUser(data)
+
+    
+    @parameterized.expand([
+        (
+            {
+                "email_address": "non@existent.email",
+                "password": "doesitmatter?"
+            }, login_link_text, "Login should have failed with the wrong user details."),
+        (
+            {
+                "email_address": "test@user.com",
+                "password": "test_password"
+            }, logout_link_text, "Login should have been successful with correct user details and neither import_collections nor remember_me set"),
+        (
+            {
+                "email_address": "test@user.com",
+                "password": "test_password",
+                "import_collections": "y"
+            }, logout_link_text, "Login should have been successful with correct user details and only import_collections set"),
+        (
+            {
+                "email_address": "test@user.com",
+                "password": "test_password",
+                "remember_me": "y"
+            }, logout_link_text, "Login should have been successful with correct user details and only remember_me set"),
+        (
+            {
+                "email_address": "test@user.com",
+                "password": "test_password",
+                "remember_me": "y",
+                "import_collections": "y"
+            }, logout_link_text, "Login should have been successful with correct user details, and both remember_me, and import_collections set")
+    ])
+    def testLogin(self, data, expected, message):
+        result = requests.post(self.login_url, data=data)
+        index = result.content.find(expected)
+        self.assertTrue(index >= 0, message)
diff --git a/test/requests/test_login_orcid.py b/test/requests/test_login_orcid.py
new file mode 100644
index 00000000..ea15642e
--- /dev/null
+++ b/test/requests/test_login_orcid.py
@@ -0,0 +1,47 @@
+import uuid
+import requests
+from time import sleep
+from wqflask import app
+from parameterized import parameterized
+from parametrized_test import ParametrizedTest
+
+login_link_text = '<a id="login_in" href="/n/login">Sign in</a>'
+logout_link_text = '<a id="login_out" title="Signed in as ." href="/n/logout">Sign out</a>'
+uid = str(uuid.uuid4())
+
+class TestLoginOrcid(ParametrizedTest):
+
+    def setUp(self):
+        super(TestLoginOrcid, self).setUp()
+        data = {
+            "user_id": uid
+            , "name": "A. T. Est User"
+            , "orcid": 345872
+            , "user_url": "https://fake-orcid.org/atestuser"
+            , "login_type": "orcid"
+            , "organization": ""
+            , "active": 1
+            , "confirmed": 1
+        }
+        self.es.create(index="users", doc_type="local", body=data, id=uid)
+        sleep(1)
+
+    def tearDown(self):
+        super(TestLoginOrcid, self).tearDown()
+        self.es.delete(index="users", doc_type="local", id=uid)
+
+    def testLoginUrl(self):
+        login_button_text = 'a href="https://sandbox.orcid.org/oauth/authorize?response_type=code&amp;scope=/authenticate&amp;show_login=true&amp;client_id=' + app.config.get("ORCID_CLIENT_ID") + '&amp;client_secret=' + app.config.get("ORCID_CLIENT_SECRET") + '" title="Login with ORCID" class="btn btn-info btn-group">Login with ORCID</a>'
+        result = requests.get(self.gn2_url+"/n/login")
+        index = result.content.find(login_button_text)
+        self.assertTrue(index >= 0, "Should have found `Login with ORCID` button")
+
+    @parameterized.expand([
+        ("1234", login_link_text, "Login should have failed with non-existing user")
+        , (uid, logout_link_text, "Login should have been successful with existing user")
+        ])
+    def testLogin(self, test_uid, expected, message):
+        url = self.gn2_url+"/n/login?type=orcid&uid="+test_uid
+        result = requests.get(url)
+        index = result.content.find(expected)
+        self.assertTrue(index >= 0, message)
diff --git a/test/requests/test_registration.py b/test/requests/test_registration.py
new file mode 100644
index 00000000..0047e8a6
--- /dev/null
+++ b/test/requests/test_registration.py
@@ -0,0 +1,41 @@
+import sys
+import requests
+from parametrized_test import ParametrizedTest
+
+class TestRegistration(ParametrizedTest):
+
+    def tearDown(self):
+        for item in self.es_cleanup:
+            self.es.delete(index="users", doc_type="local", id=item["_id"])
+
+    def testRegistrationPage(self):
+        if self.es.ping():
+            data = {
+                "email_address": "test@user.com",
+                "full_name": "Test User",
+                "organization": "Test Organisation",
+                "password": "test_password",
+                "password_confirm": "test_password"
+            }
+            requests.post(self.gn2_url+"/n/register", data)
+            response = self.es.search(
+                index="users"
+                , doc_type="local"
+                , body={
+                    "query": {"match": {"email_address": "test@user.com"}}})
+            self.assertEqual(len(response["hits"]["hits"]), 1)
+        else:
+            self.skipTest("The elasticsearch server is down")
+
+def main(gn2, es):
+    import unittest
+    suite = unittest.TestSuite()
+    suite.addTest(TestRegistration(methodName="testRegistrationPage", gn2_url=gn2, es_url=es))
+    runner = unittest.TextTestRunner()
+    runner.run(suite)
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        raise Exception("Required arguments missing")
+    else:
+        main(sys.argv[1], sys.argv[2])
diff --git a/test/unittest/test_registration.py b/test/unittest/test_registration.py
new file mode 100644
index 00000000..98d0cdff
--- /dev/null
+++ b/test/unittest/test_registration.py
@@ -0,0 +1,27 @@
+# Run test with something like
+#
+# env GN2_PROFILE=~/opt/gn-latest GENENETWORK_FILES=$HOME/gn2_data ./bin/genenetwork2 ./etc/default_settings.py -c ../test/unittest/test_registration.py
+#
+
+import unittest
+import mock.es_double as es
+from wqflask.user_manager import RegisterUser
+
+class TestRegisterUser(unittest.TestCase):
+    def setUp(self):
+        self.es = es.ESDouble()
+
+    def testRegisterUserWithCorrectData(self):
+        data = {
+            "email_address": "user@example.com"
+            , "full_name": "A.N. Other"
+            , "organization": "Some Organisation"
+            , "password": "testing"
+            , "password_confirm": "testing"
+            , "es_connection": self.es
+        }
+        result = RegisterUser(data)
+        self.assertEqual(len(result.errors), 0, "Errors were not expected")
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/wqflask/flask_security/__init__.py b/wqflask/flask_security/__init__.py
deleted file mode 100644
index 81e6c89e..00000000
--- a/wqflask/flask_security/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security
-    ~~~~~~~~~~~~~~~~~~
-
-    Flask-Security is a Flask extension that aims to add quick and simple
-    security via Flask-Login, Flask-Principal, Flask-WTF, and passlib.
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-__version__ = '1.6.0'
-
-from .core import Security, RoleMixin, UserMixin, AnonymousUser, current_user
-from .datastore import SQLAlchemyUserDatastore, MongoEngineUserDatastore, PeeweeUserDatastore
-from .decorators import auth_token_required, http_auth_required, \
-     login_required, roles_accepted, roles_required
-from .forms import ForgotPasswordForm, LoginForm, RegisterForm, \
-     ResetPasswordForm, PasswordlessLoginForm, ConfirmRegisterForm
-from .signals import confirm_instructions_sent, password_reset, \
-     reset_password_instructions_sent, user_confirmed, user_registered
-from .utils import login_user, logout_user, url_for_security
-
-print "Using our own flask.ext.security"
\ No newline at end of file
diff --git a/wqflask/flask_security/changeable.py b/wqflask/flask_security/changeable.py
deleted file mode 100644
index 4447b655..00000000
--- a/wqflask/flask_security/changeable.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.changeable
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security recoverable module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :author: Eskil Heyn Olsen
-    :license: MIT, see LICENSE for more details.
-"""
-
-from flask import current_app as app, request
-from werkzeug.local import LocalProxy
-
-from .signals import password_changed
-from .utils import send_mail, encrypt_password, url_for_security, \
-    config_value
-
-
-# Convenient references
-_security = LocalProxy(lambda: app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-
-def send_password_changed_notice(user):
-    """Sends the password changed notice email for the specified user.
-
-    :param user: The user to send the notice to
-    """
-    send_mail(config_value('EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE'), user.email,
-              'change_notice', user=user)
-
-
-def change_user_password(user, password):
-    """Change the specified user's password
-
-    :param user: The user to change_password
-    :param password: The unencrypted new password
-    """
-    user.password = encrypt_password(password)
-    _datastore.put(user)
-    send_password_changed_notice(user)
-    password_changed.send(user, app=app._get_current_object())
diff --git a/wqflask/flask_security/confirmable.py b/wqflask/flask_security/confirmable.py
deleted file mode 100644
index a7caf6cd..00000000
--- a/wqflask/flask_security/confirmable.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.confirmable
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security confirmable module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from datetime import datetime
-
-from flask import current_app as app, request
-from werkzeug.local import LocalProxy
-
-from .utils import send_mail, md5, url_for_security, get_token_status,\
-     config_value
-from .signals import user_confirmed, confirm_instructions_sent
-
-
-# Convenient references
-_security = LocalProxy(lambda: app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-
-def generate_confirmation_link(user):
-    token = generate_confirmation_token(user)
-    url = url_for_security('confirm_email', token=token)
-    return request.url_root[:-1] + url, token
-
-
-def send_confirmation_instructions(user):
-    """Sends the confirmation instructions email for the specified user.
-
-    :param user: The user to send the instructions to
-    :param token: The confirmation token
-    """
-
-    confirmation_link, token = generate_confirmation_link(user)
-
-    send_mail(config_value('EMAIL_SUBJECT_CONFIRM'), user.email,
-              'confirmation_instructions', user=user,
-              confirmation_link=confirmation_link)
-
-    confirm_instructions_sent.send(user, app=app._get_current_object())
-    return token
-
-
-def generate_confirmation_token(user):
-    """Generates a unique confirmation token for the specified user.
-
-    :param user: The user to work with
-    """
-    data = [str(user.id), md5(user.email)]
-    return _security.confirm_serializer.dumps(data)
-
-
-def requires_confirmation(user):
-    """Returns `True` if the user requires confirmation."""
-    return _security.confirmable and user.confirmed_at == None
-
-
-def confirm_email_token_status(token):
-    """Returns the expired status, invalid status, and user of a confirmation
-    token. For example::
-
-        expired, invalid, user = confirm_email_token_status('...')
-
-    :param token: The confirmation token
-    """
-    return get_token_status(token, 'confirm', 'CONFIRM_EMAIL')
-
-
-def confirm_user(user):
-    """Confirms the specified user
-
-    :param user: The user to confirm
-    """
-    user.confirmed_at = datetime.utcnow()
-    _datastore.put(user)
-    user_confirmed.send(user, app=app._get_current_object())
diff --git a/wqflask/flask_security/core.py b/wqflask/flask_security/core.py
deleted file mode 100644
index 0f3a231f..00000000
--- a/wqflask/flask_security/core.py
+++ /dev/null
@@ -1,382 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.core
-    ~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security core module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from flask import current_app
-from flask.ext.login import AnonymousUser as AnonymousUserBase, \
-     UserMixin as BaseUserMixin, LoginManager, current_user
-from flask.ext.principal import Principal, RoleNeed, UserNeed, Identity, \
-     identity_loaded
-from itsdangerous import URLSafeTimedSerializer
-from passlib.context import CryptContext
-from werkzeug.datastructures import ImmutableList
-from werkzeug.local import LocalProxy
-
-from .utils import config_value as cv, get_config, md5, url_for_security
-from .views import create_blueprint
-from .forms import LoginForm, ConfirmRegisterForm, RegisterForm, \
-     ForgotPasswordForm, ChangePasswordForm, ResetPasswordForm, \
-     SendConfirmationForm, PasswordlessLoginForm
-
-# Convenient references
-_security = LocalProxy(lambda: current_app.extensions['security'])
-
-
-#: Default Flask-Security configuration
-_default_config = {
-    'BLUEPRINT_NAME': 'security',
-    'URL_PREFIX': None,
-    'SUBDOMAIN': None,
-    'FLASH_MESSAGES': True,
-    'PASSWORD_HASH': 'plaintext',
-    'PASSWORD_SALT': None,
-    'LOGIN_URL': '/login',
-    'LOGOUT_URL': '/logout',
-    'REGISTER_URL': '/register',
-    'RESET_URL': '/reset',
-    'CHANGE_URL': '/change',
-    'CONFIRM_URL': '/confirm',
-    'POST_LOGIN_VIEW': '/',
-    'POST_LOGOUT_VIEW': '/',
-    'CONFIRM_ERROR_VIEW': None,
-    'POST_REGISTER_VIEW': None,
-    'POST_CONFIRM_VIEW': None,
-    'POST_RESET_VIEW': None,
-    'POST_CHANGE_VIEW': None,
-    'UNAUTHORIZED_VIEW': None,
-    'FORGOT_PASSWORD_TEMPLATE': 'security/forgot_password.html',
-    'LOGIN_USER_TEMPLATE': 'security/login_user.html',
-    'REGISTER_USER_TEMPLATE': 'security/register_user.html',
-    'RESET_PASSWORD_TEMPLATE': 'security/reset_password.html',
-    'SEND_CONFIRMATION_TEMPLATE': 'security/send_confirmation.html',
-    'SEND_LOGIN_TEMPLATE': 'security/send_login.html',
-    'CONFIRMABLE': False,
-    'REGISTERABLE': False,
-    'RECOVERABLE': False,
-    'TRACKABLE': False,
-    'PASSWORDLESS': False,
-    'CHANGEABLE': False,
-    'SEND_REGISTER_EMAIL': True,
-    'LOGIN_WITHIN': '1 days',
-    'CONFIRM_EMAIL_WITHIN': '5 days',
-    'RESET_PASSWORD_WITHIN': '5 days',
-    'LOGIN_WITHOUT_CONFIRMATION': False,
-    'EMAIL_SENDER': 'no-reply@localhost',
-    'TOKEN_AUTHENTICATION_KEY': 'auth_token',
-    'TOKEN_AUTHENTICATION_HEADER': 'Authentication-Token',
-    'CONFIRM_SALT': 'confirm-salt',
-    'RESET_SALT': 'reset-salt',
-    'LOGIN_SALT': 'login-salt',
-    'CHANGE_SALT': 'change-salt',
-    'REMEMBER_SALT': 'remember-salt',
-    'DEFAULT_HTTP_AUTH_REALM': 'Login Required',
-    'EMAIL_SUBJECT_REGISTER': 'Welcome',
-    'EMAIL_SUBJECT_CONFIRM': 'Please confirm your email',
-    'EMAIL_SUBJECT_PASSWORDLESS': 'Login instructions',
-    'EMAIL_SUBJECT_PASSWORD_NOTICE': 'Your password has been reset',
-    'EMAIL_SUBJECT_PASSWORD_CHANGE_NOTICE': 'Your password has been changed',
-    'EMAIL_SUBJECT_PASSWORD_RESET': 'Password reset instructions'
-}
-
-#: Default Flask-Security messages
-_default_messages = {
-    'UNAUTHORIZED': ('You do not have permission to view this resource.', 'error'),
-    'CONFIRM_REGISTRATION': ('Thank you. Confirmation instructions have been sent to %(email)s.', 'success'),
-    'EMAIL_CONFIRMED': ('Thank you. Your email has been confirmed.', 'success'),
-    'ALREADY_CONFIRMED': ('Your email has already been confirmed.', 'info'),
-    'INVALID_CONFIRMATION_TOKEN': ('Invalid confirmation token.', 'error'),
-    'EMAIL_ALREADY_ASSOCIATED': ('%(email)s is already associated with an account.', 'error'),
-    'PASSWORD_MISMATCH': ('Password does not match', 'error'),
-    'RETYPE_PASSWORD_MISMATCH': ('Passwords do not match', 'error'),
-    'INVALID_REDIRECT': ('Redirections outside the domain are forbidden', 'error'),
-    'PASSWORD_RESET_REQUEST': ('Instructions to reset your password have been sent to %(email)s.', 'info'),
-    'PASSWORD_RESET_EXPIRED': ('You did not reset your password within %(within)s. New instructions have been sent to %(email)s.', 'error'),
-    'INVALID_RESET_PASSWORD_TOKEN': ('Invalid reset password token.', 'error'),
-    'CONFIRMATION_REQUIRED': ('Email requires confirmation.', 'error'),
-    'CONFIRMATION_REQUEST': ('Confirmation instructions have been sent to %(email)s.', 'info'),
-    'CONFIRMATION_EXPIRED': ('You did not confirm your email within %(within)s. New instructions to confirm your email have been sent to %(email)s.', 'error'),
-    'LOGIN_EXPIRED': ('You did not login within %(within)s. New instructions to login have been sent to %(email)s.', 'error'),
-    'LOGIN_EMAIL_SENT': ('Instructions to login have been sent to %(email)s.', 'success'),
-    'INVALID_LOGIN_TOKEN': ('Invalid login token.', 'error'),
-    'DISABLED_ACCOUNT': ('Account is disabled.', 'error'),
-    'EMAIL_NOT_PROVIDED': ('Email not provided', 'error'),
-    'INVALID_EMAIL_ADDRESS': ('Invalid email address', 'error'),
-    'PASSWORD_NOT_PROVIDED': ('Password not provided', 'error'),
-    'USER_DOES_NOT_EXIST': ('Specified user does not exist', 'error'),
-    'INVALID_PASSWORD': ('Invalid password', 'error'),
-    'PASSWORDLESS_LOGIN_SUCCESSFUL': ('You have successfuly logged in.', 'success'),
-    'PASSWORD_RESET': ('You successfully reset your password and you have been logged in automatically.', 'success'),
-    'PASSWORD_CHANGE': ('You successfully changed your password.', 'success'),
-    'LOGIN': ('Please log in to access this page.', 'info'),
-    'REFRESH': ('Please reauthenticate to access this page.', 'info'),
-}
-
-_allowed_password_hash_schemes = [
-    'bcrypt',
-    'des_crypt',
-    'pbkdf2_sha256',
-    'pbkdf2_sha512',
-    'sha256_crypt',
-    'sha512_crypt',
-    # And always last one...
-    'plaintext'
-]
-
-_default_forms = {
-    'login_form': LoginForm,
-    'confirm_register_form': ConfirmRegisterForm,
-    'register_form': RegisterForm,
-    'forgot_password_form': ForgotPasswordForm,
-    'reset_password_form': ResetPasswordForm,
-    'change_password_form': ChangePasswordForm,
-    'send_confirmation_form': SendConfirmationForm,
-    'passwordless_login_form': PasswordlessLoginForm,
-}
-
-
-def _user_loader(user_id):
-    return _security.datastore.find_user(id=user_id)
-
-
-def _token_loader(token):
-    try:
-        data = _security.remember_token_serializer.loads(token)
-        user = _security.datastore.find_user(id=data[0])
-        if user and md5(user.password) == data[1]:
-            return user
-    except:
-        pass
-
-    return None
-
-
-def _identity_loader():
-    if not isinstance(current_user._get_current_object(), AnonymousUser):
-        identity = Identity(current_user.id)
-        return identity
-
-
-def _on_identity_loaded(sender, identity):
-    if hasattr(current_user, 'id'):
-        identity.provides.add(UserNeed(current_user.id))
-
-    for role in current_user.roles:
-        identity.provides.add(RoleNeed(role.name))
-
-    identity.user = current_user
-
-
-def _get_login_manager(app):
-    lm = LoginManager()
-    lm.anonymous_user = AnonymousUser
-    lm.login_view = '%s.login' % cv('BLUEPRINT_NAME', app=app)
-    lm.user_loader(_user_loader)
-    lm.token_loader(_token_loader)
-    lm.login_message, lm.login_message_category = cv('MSG_LOGIN', app=app)
-    lm.needs_refresh_message, lm.needs_refresh_message_category = cv('MSG_REFRESH', app=app)
-    lm.init_app(app)
-    return lm
-
-
-def _get_principal(app):
-    p = Principal(app, use_sessions=False)
-    p.identity_loader(_identity_loader)
-    return p
-
-
-def _get_pwd_context(app):
-    pw_hash = cv('PASSWORD_HASH', app=app)
-    if pw_hash not in _allowed_password_hash_schemes:
-        allowed = ', '.join(_allowed_password_hash_schemes[:-1]) + ' and ' + _allowed_password_hash_schemes[-1]
-        raise ValueError("Invalid hash scheme %r. Allowed values are %s" % (pw_hash, allowed))
-    return CryptContext(schemes=_allowed_password_hash_schemes, default=pw_hash)
-
-
-def _get_serializer(app, name):
-    secret_key = app.config.get('SECRET_KEY')
-    salt = app.config.get('SECURITY_%s_SALT' % name.upper())
-    return URLSafeTimedSerializer(secret_key=secret_key, salt=salt)
-
-
-def _get_state(app, datastore, **kwargs):
-    for key, value in get_config(app).items():
-        print "in _get_state [{}]: {}".format(key, value)
-        kwargs[key.lower()] = value
-
-    kwargs.update(dict(
-        app=app,
-        datastore=datastore,
-        login_manager=_get_login_manager(app),
-        principal=_get_principal(app),
-        pwd_context=_get_pwd_context(app),
-        remember_token_serializer=_get_serializer(app, 'remember'),
-        login_serializer=_get_serializer(app, 'login'),
-        reset_serializer=_get_serializer(app, 'reset'),
-        confirm_serializer=_get_serializer(app, 'confirm'),
-        _context_processors={},
-        _send_mail_task=None
-    ))
-
-    for key, value in _default_forms.items():
-        if key not in kwargs or not kwargs[key]:
-            kwargs[key] = value
-
-    return _SecurityState(**kwargs)
-
-
-def _context_processor():
-    return dict(url_for_security=url_for_security, security=_security)
-
-
-class RoleMixin(object):
-    """Mixin for `Role` model definitions"""
-    def __eq__(self, other):
-        return (self.name == other or \
-                self.name == getattr(other, 'name', None))
-
-    def __ne__(self, other):
-        return (self.name != other and
-                self.name != getattr(other, 'name', None))
-
-
-class UserMixin(BaseUserMixin):
-    """Mixin for `User` model definitions"""
-
-    def is_active(self):
-        """Returns `True` if the user is active."""
-        return self.active
-
-    def get_auth_token(self):
-        """Returns the user's authentication token."""
-        data = [str(self.id), md5(self.password)]
-        return _security.remember_token_serializer.dumps(data)
-
-    def has_role(self, role):
-        """Returns `True` if the user identifies with the specified role.
-
-        :param role: A role name or `Role` instance"""
-        return role in self.roles
-
-
-class AnonymousUser(AnonymousUserBase):
-    """AnonymousUser definition"""
-
-    def __init__(self):
-        super(AnonymousUser, self).__init__()
-        self.roles = ImmutableList()
-
-    def has_role(self, *args):
-        """Returns `False`"""
-        return False
-
-
-class _SecurityState(object):
-
-    def __init__(self, **kwargs):
-        for key, value in kwargs.items():
-            setattr(self, key.lower(), value)
-
-    def _add_ctx_processor(self, endpoint, fn):
-        group = self._context_processors.setdefault(endpoint, [])
-        fn not in group and group.append(fn)
-
-    def _run_ctx_processor(self, endpoint):
-        rv, fns = {}, []
-        for g in [None, endpoint]:
-            for fn in self._context_processors.setdefault(g, []):
-                rv.update(fn())
-        return rv
-
-    def context_processor(self, fn):
-        self._add_ctx_processor(None, fn)
-
-    def forgot_password_context_processor(self, fn):
-        self._add_ctx_processor('forgot_password', fn)
-
-    def login_context_processor(self, fn):
-        self._add_ctx_processor('login', fn)
-
-    def register_context_processor(self, fn):
-        self._add_ctx_processor('register', fn)
-
-    def reset_password_context_processor(self, fn):
-        self._add_ctx_processor('reset_password', fn)
-
-    def change_password_context_processor(self, fn):
-        self._add_ctx_processor('change_password', fn)
-
-    def send_confirmation_context_processor(self, fn):
-        self._add_ctx_processor('send_confirmation', fn)
-
-    def send_login_context_processor(self, fn):
-        self._add_ctx_processor('send_login', fn)
-
-    def mail_context_processor(self, fn):
-        self._add_ctx_processor('mail', fn)
-
-    def send_mail_task(self, fn):
-        self._send_mail_task = fn
-
-
-class Security(object):
-    """The :class:`Security` class initializes the Flask-Security extension.
-
-    :param app: The application.
-    :param datastore: An instance of a user datastore.
-    """
-    def __init__(self, app=None, datastore=None, **kwargs):
-        self.app = app
-        self.datastore = datastore
-
-        if app is not None and datastore is not None:
-            self._state = self.init_app(app, datastore, **kwargs)
-
-    def init_app(self, app, datastore=None, register_blueprint=True,
-        login_form=None, confirm_register_form=None,
-        register_form=None, forgot_password_form=None,
-        reset_password_form=None, change_password_form=None,
-        send_confirmation_form=None, passwordless_login_form=None):
-        """Initializes the Flask-Security extension for the specified
-        application and datastore implentation.
-
-        :param app: The application.
-        :param datastore: An instance of a user datastore.
-        :param register_blueprint: to register the Security blueprint or not.
-        """
-        datastore = datastore or self.datastore
-
-        for key, value in _default_config.items():
-            app.config.setdefault('SECURITY_' + key, value)
-
-        for key, value in _default_messages.items():
-            app.config.setdefault('SECURITY_MSG_' + key, value)
-
-        identity_loaded.connect_via(app)(_on_identity_loaded)
-
-        state = _get_state(app, datastore,
-                           login_form=login_form,
-                           confirm_register_form=confirm_register_form,
-                           register_form=register_form,
-                           forgot_password_form=forgot_password_form,
-                           reset_password_form=reset_password_form,
-                           change_password_form=change_password_form,
-                           send_confirmation_form=send_confirmation_form,
-                           passwordless_login_form=passwordless_login_form)
-
-        if register_blueprint:
-            app.register_blueprint(create_blueprint(state, __name__))
-            app.context_processor(_context_processor)
-
-        app.extensions['security'] = state
-
-        return state
-
-    def __getattr__(self, name):
-        return getattr(self._state, name, None)
diff --git a/wqflask/flask_security/datastore.py b/wqflask/flask_security/datastore.py
deleted file mode 100644
index 634399d9..00000000
--- a/wqflask/flask_security/datastore.py
+++ /dev/null
@@ -1,261 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.datastore
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    This module contains an user datastore classes.
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-class Datastore(object):
-    def __init__(self, db):
-        self.db = db
-
-    def commit(self):
-        pass
-
-    def put(self, model):
-        raise NotImplementedError
-
-    def delete(self, model):
-        raise NotImplementedError
-
-
-class SQLAlchemyDatastore(Datastore):
-    def commit(self):
-        self.db.session.commit()
-
-    def put(self, model):
-        self.db.session.add(model)
-        return model
-
-    def delete(self, model):
-        self.db.session.delete(model)
-
-
-class MongoEngineDatastore(Datastore):
-    def put(self, model):
-        model.save()
-        return model
-
-    def delete(self, model):
-        model.delete()
-
-
-class PeeweeDatastore(Datastore):
-    def put(self, model):
-        model.save()
-        return model
-
-    def delete(self, model):
-        model.delete_instance()
-
-
-class UserDatastore(object):
-    """Abstracted user datastore.
-
-    :param user_model: A user model class definition
-    :param role_model: A role model class definition
-    """
-
-    def __init__(self, user_model, role_model):
-        self.user_model = user_model
-        self.role_model = role_model
-
-    def _prepare_role_modify_args(self, user, role):
-        if isinstance(user, basestring):
-            user = self.find_user(email=user)
-        if isinstance(role, basestring):
-            role = self.find_role(role)
-        return user, role
-
-    def _prepare_create_user_args(self, **kwargs):
-        kwargs.setdefault('active', True)
-        roles = kwargs.get('roles', [])
-        for i, role in enumerate(roles):
-            rn = role.name if isinstance(role, self.role_model) else role
-            # see if the role exists
-            roles[i] = self.find_role(rn)
-        kwargs['roles'] = roles
-        return kwargs
-
-    def find_user(self, *args, **kwargs):
-        """Returns a user matching the provided parameters."""
-        raise NotImplementedError
-
-    def find_role(self, *args, **kwargs):
-        """Returns a role matching the provided name."""
-        raise NotImplementedError
-
-    def add_role_to_user(self, user, role):
-        """Adds a role tp a user
-
-        :param user: The user to manipulate
-        :param role: The role to add to the user
-        """
-        rv = False
-        user, role = self._prepare_role_modify_args(user, role)
-        if role not in user.roles:
-            rv = True
-            user.roles.append(role)
-        return rv
-
-    def remove_role_from_user(self, user, role):
-        """Removes a role from a user
-
-        :param user: The user to manipulate
-        :param role: The role to remove from the user
-        """
-        rv = False
-        user, role = self._prepare_role_modify_args(user, role)
-        if role in user.roles:
-            rv = True
-            user.roles.remove(role)
-        return rv
-
-    def toggle_active(self, user):
-        """Toggles a user's active status. Always returns True."""
-        user.active = not user.active
-        return True
-
-    def deactivate_user(self, user):
-        """Deactivates a specified user. Returns `True` if a change was made.
-
-        :param user: The user to deactivate
-        """
-        if user.active:
-            user.active = False
-            return True
-        return False
-
-    def activate_user(self, user):
-        """Activates a specified user. Returns `True` if a change was made.
-
-        :param user: The user to activate
-        """
-        if not user.active:
-            user.active = True
-            return True
-        return False
-
-    def create_role(self, **kwargs):
-        """Creates and returns a new role from the given parameters."""
-
-        role = self.role_model(**kwargs)
-        return self.put(role)
-
-    def find_or_create_role(self, name, **kwargs):
-        """Returns a role matching the given name or creates it with any
-        additionally provided parameters
-        """
-        kwargs["name"] = name
-        return self.find_role(name) or self.create_role(**kwargs)
-
-    def create_user(self, **kwargs):
-        """Creates and returns a new user from the given parameters."""
-
-        user = self.user_model(**self._prepare_create_user_args(**kwargs))
-        print "in abstraced create_user, user is:", user
-        return self.put(user)
-
-    def delete_user(self, user):
-        """Delete the specified user
-
-        :param user: The user to delete
-        """
-        self.delete(user)
-
-
-class SQLAlchemyUserDatastore(SQLAlchemyDatastore, UserDatastore):
-    """A SQLAlchemy datastore implementation for Flask-Security that assumes the
-    use of the Flask-SQLAlchemy extension.
-    """
-    def __init__(self, db, user_model, role_model):
-        SQLAlchemyDatastore.__init__(self, db)
-        UserDatastore.__init__(self, user_model, role_model)
-
-    def find_user(self, **kwargs):
-        return self.user_model.query.filter_by(**kwargs).first()
-
-    def find_role(self, role):
-        return self.role_model.query.filter_by(name=role).first()
-
-
-class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
-    """A MongoEngine datastore implementation for Flask-Security that assumes
-    the use of the Flask-MongoEngine extension.
-    """
-    def __init__(self, db, user_model, role_model):
-        MongoEngineDatastore.__init__(self, db)
-        UserDatastore.__init__(self, user_model, role_model)
-
-    def find_user(self, **kwargs):
-        return self.user_model.objects(**kwargs).first()
-
-    def find_role(self, role):
-        return self.role_model.objects(name=role).first()
-
-
-class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
-    """A PeeweeD datastore implementation for Flask-Security that assumes
-    the use of the Flask-Peewee extension.
-
-    :param user_model: A user model class definition
-    :param role_model: A role model class definition
-    :param role_link: A model implementing the many-to-many user-role relation
-    """
-    def __init__(self, db, user_model, role_model, role_link):
-        PeeweeDatastore.__init__(self, db)
-        UserDatastore.__init__(self, user_model, role_model)
-        self.UserRole = role_link
-
-    def find_user(self, **kwargs):
-        try:
-            return self.user_model.filter(**kwargs).get()
-        except self.user_model.DoesNotExist:
-            return None
-
-    def find_role(self, role):
-        try:
-            return self.role_model.filter(name=role).get()
-        except self.role_model.DoesNotExist:
-            return None
-
-    def create_user(self, **kwargs):
-        """Creates and returns a new user from the given parameters."""
-        roles = kwargs.pop('roles', [])
-        user = self.user_model(**self._prepare_create_user_args(**kwargs))
-        user = self.put(user)
-        for role in roles:
-            self.add_role_to_user(user, role)
-        return user
-
-
-    def add_role_to_user(self, user, role):
-        """Adds a role tp a user
-
-        :param user: The user to manipulate
-        :param role: The role to add to the user
-        """
-        user, role = self._prepare_role_modify_args(user, role)
-        if self.UserRole.select().where(self.UserRole.user==user, self.UserRole.role==role).count():
-            return False
-        else:
-            self.UserRole.create(user=user, role=role)
-            return True
-
-    def remove_role_from_user(self, user, role):
-        """Removes a role from a user
-
-        :param user: The user to manipulate
-        :param role: The role to remove from the user
-        """
-        user, role = self._prepare_role_modify_args(user, role)
-        if self.UserRole.select().where(self.UserRole.user==user, self.UserRole.role==role).count():
-            self.UserRole.delete().where(self.UserRole.user==user, self.UserRole.role==role)
-            return True
-        else:
-            return False
-
diff --git a/wqflask/flask_security/decorators.py b/wqflask/flask_security/decorators.py
deleted file mode 100644
index 0ea1105c..00000000
--- a/wqflask/flask_security/decorators.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.decorators
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security decorators module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from functools import wraps
-
-from flask import current_app, Response, request, redirect, _request_ctx_stack
-from flask.ext.login import current_user, login_required
-from flask.ext.principal import RoleNeed, Permission, Identity, identity_changed
-from werkzeug.local import LocalProxy
-
-from . import utils
-
-
-# Convenient references
-_security = LocalProxy(lambda: current_app.extensions['security'])
-
-
-_default_unauthorized_html = """
-    <h1>Unauthorized</h1>
-    <p>The server could not verify that you are authorized to access the URL
-    requested. You either supplied the wrong credentials (e.g. a bad password),
-    or your browser doesn't understand how to supply the credentials required.</p>
-    """
-
-
-def _get_unauthorized_response(text=None, headers=None):
-    text = text or _default_unauthorized_html
-    headers = headers or {}
-    return Response(text, 401, headers)
-
-
-def _get_unauthorized_view():
-    cv = utils.get_url(utils.config_value('UNAUTHORIZED_VIEW'))
-    utils.do_flash(*utils.get_message('UNAUTHORIZED'))
-    return redirect(cv or request.referrer or '/')
-
-
-def _check_token():
-    header_key = _security.token_authentication_header
-    args_key = _security.token_authentication_key
-    header_token = request.headers.get(header_key, None)
-    token = request.args.get(args_key, header_token)
-    if request.json:
-        token = request.json.get(args_key, token)
-    serializer = _security.remember_token_serializer
-
-    try:
-        data = serializer.loads(token)
-    except:
-        return False
-
-    user = _security.datastore.find_user(id=data[0])
-
-    if utils.md5(user.password) == data[1]:
-        app = current_app._get_current_object()
-        _request_ctx_stack.top.user = user
-        identity_changed.send(app, identity=Identity(user.id))
-        return True
-
-
-def _check_http_auth():
-    auth = request.authorization or dict(username=None, password=None)
-    user = _security.datastore.find_user(email=auth.username)
-
-    if user and utils.verify_and_update_password(auth.password, user):
-        _security.datastore.commit()
-        app = current_app._get_current_object()
-        _request_ctx_stack.top.user = user
-        identity_changed.send(app, identity=Identity(user.id))
-        return True
-
-    return False
-
-
-def http_auth_required(realm):
-    """Decorator that protects endpoints using Basic HTTP authentication.
-    The username should be set to the user's email address.
-
-    :param realm: optional realm name"""
-
-    def decorator(fn):
-        @wraps(fn)
-        def wrapper(*args, **kwargs):
-            if _check_http_auth():
-                return fn(*args, **kwargs)
-            r = _security.default_http_auth_realm if callable(realm) else realm
-            h = {'WWW-Authenticate': 'Basic realm="%s"' % r}
-            return _get_unauthorized_response(headers=h)
-        return wrapper
-
-    if callable(realm):
-        return decorator(realm)
-    return decorator
-
-
-def auth_token_required(fn):
-    """Decorator that protects endpoints using token authentication. The token
-    should be added to the request by the client by using a query string
-    variable with a name equal to the configuration value of
-    `SECURITY_TOKEN_AUTHENTICATION_KEY` or in a request header named that of
-    the configuration value of `SECURITY_TOKEN_AUTHENTICATION_HEADER`
-    """
-
-    @wraps(fn)
-    def decorated(*args, **kwargs):
-        if _check_token():
-            return fn(*args, **kwargs)
-        return _get_unauthorized_response()
-    return decorated
-
-
-def auth_required(*auth_methods):
-    """
-    Decorator that protects enpoints through multiple mechanisms
-    Example::
-
-        @app.route('/dashboard')
-        @auth_required('token', 'session')
-        def dashboard():
-            return 'Dashboard'
-
-    :param auth_methods: Specified mechanisms.
-    """
-    login_mechanisms = {
-        'token': lambda: _check_token(),
-        'basic': lambda: _check_http_auth(),
-        'session': lambda: current_user.is_authenticated()
-    }
-
-    def wrapper(fn):
-        @wraps(fn)
-        def decorated_view(*args, **kwargs):
-            mechanisms = [login_mechanisms.get(method) for method in auth_methods]
-            for mechanism in mechanisms:
-                if mechanism and mechanism():
-                    return fn(*args, **kwargs)
-            return _get_unauthorized_response()
-        return decorated_view
-    return wrapper
-
-
-def roles_required(*roles):
-    """Decorator which specifies that a user must have all the specified roles.
-    Example::
-
-        @app.route('/dashboard')
-        @roles_required('admin', 'editor')
-        def dashboard():
-            return 'Dashboard'
-
-    The current user must have both the `admin` role and `editor` role in order
-    to view the page.
-
-    :param args: The required roles.
-    """
-    def wrapper(fn):
-        @wraps(fn)
-        def decorated_view(*args, **kwargs):
-            perms = [Permission(RoleNeed(role)) for role in roles]
-            for perm in perms:
-                if not perm.can():
-                    return _get_unauthorized_view()
-            return fn(*args, **kwargs)
-        return decorated_view
-    return wrapper
-
-
-def roles_accepted(*roles):
-    """Decorator which specifies that a user must have at least one of the
-    specified roles. Example::
-
-        @app.route('/create_post')
-        @roles_accepted('editor', 'author')
-        def create_post():
-            return 'Create Post'
-
-    The current user must have either the `editor` role or `author` role in
-    order to view the page.
-
-    :param args: The possible roles.
-    """
-    def wrapper(fn):
-        @wraps(fn)
-        def decorated_view(*args, **kwargs):
-            perm = Permission(*[RoleNeed(role) for role in roles])
-            if perm.can():
-                return fn(*args, **kwargs)
-            return _get_unauthorized_view()
-        return decorated_view
-    return wrapper
-
-
-def anonymous_user_required(f):
-    @wraps(f)
-    def wrapper(*args, **kwargs):
-        if current_user.is_authenticated():
-            return redirect(utils.get_url(_security.post_login_view))
-        return f(*args, **kwargs)
-    return wrapper
diff --git a/wqflask/flask_security/forms.py b/wqflask/flask_security/forms.py
deleted file mode 100644
index 54677e77..00000000
--- a/wqflask/flask_security/forms.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.forms
-    ~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security forms module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-import inspect
-import urlparse
-
-import flask_wtf as wtf
-
-from flask import request, current_app
-from flask_wtf import Form as BaseForm, TextField, PasswordField, \
-     SubmitField, HiddenField, BooleanField, ValidationError, Field
-from flask_login import current_user
-from werkzeug.local import LocalProxy
-
-from .confirmable import requires_confirmation
-from .utils import verify_and_update_password, get_message
-
-# Convenient reference
-_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
-
-_default_field_labels = {
-    'email': 'Email Address',
-    'password': 'Password',
-    'remember_me': 'Remember Me',
-    'login': 'Login',
-    'retype_password': 'Retype Password',
-    'register': 'Register',
-    'send_confirmation': 'Resend Confirmation Instructions',
-    'recover_password': 'Recover Password',
-    'reset_password': 'Reset Password',
-    'retype_password': 'Retype Password',
-    'new_password': 'New Password',
-    'change_password': 'Change Password',
-    'send_login_link': 'Send Login Link'
-}
-
-
-class ValidatorMixin(object):
-    def __call__(self, form, field):
-        if self.message and self.message.isupper():
-            self.message = get_message(self.message)[0]
-        return super(ValidatorMixin, self).__call__(form, field)
-
-
-class EqualTo(ValidatorMixin, wtf.EqualTo):
-    pass
-
-
-class Required(ValidatorMixin, wtf.Required):
-    pass
-
-
-class Email(ValidatorMixin, wtf.Email):
-    pass
-
-
-class Length(ValidatorMixin, wtf.Length):
-    pass
-
-
-email_required = Required(message='EMAIL_NOT_PROVIDED')
-email_validator = Email(message='INVALID_EMAIL_ADDRESS')
-password_required = Required(message='PASSWORD_NOT_PROVIDED')
-
-
-def get_form_field_label(key):
-    return _default_field_labels.get(key, '')
-
-
-def unique_user_email(form, field):
-    if _datastore.find_user(email=field.data) is not None:
-        msg = get_message('EMAIL_ALREADY_ASSOCIATED', email=field.data)[0]
-        raise ValidationError(msg)
-
-
-def valid_user_email(form, field):
-    form.user = _datastore.find_user(email=field.data)
-    if form.user is None:
-        raise ValidationError(get_message('USER_DOES_NOT_EXIST')[0])
-
-
-class Form(BaseForm):
-    def __init__(self, *args, **kwargs):
-        #print "importing tracer"
-        #from wqflask import tracer
-        #tracer.turn_on()
-        #print "imported tracer"
-        print "in Form, args:", args
-        print "in Form, kwargs:", kwargs
-        if current_app.testing:
-            self.TIME_LIMIT = None
-        super(Form, self).__init__(*args, **kwargs)
-
-
-class EmailFormMixin():
-    email = TextField(get_form_field_label('email'),
-        validators=[email_required,
-                    email_validator])
-
-
-class UserEmailFormMixin():
-    user = None
-    email = TextField(get_form_field_label('email'),
-        validators=[email_required,
-                    email_validator,
-                    valid_user_email])
-
-
-class UniqueEmailFormMixin():
-    email = TextField(get_form_field_label('email'),
-        validators=[email_required,
-                    email_validator,
-                    unique_user_email])
-
-
-class PasswordFormMixin():
-    password = PasswordField(get_form_field_label('password'),
-        validators=[password_required])
-
-
-class NewPasswordFormMixin():
-    password = PasswordField(get_form_field_label('password'),
-        validators=[password_required,
-                    Length(min=6, max=128)])
-
-
-class PasswordConfirmFormMixin():
-    password_confirm = PasswordField(
-        get_form_field_label('retype_password'),
-        validators=[EqualTo('password', message='RETYPE_PASSWORD_MISMATCH')])
-
-
-class NextFormMixin():
-    next = HiddenField()
-
-    def validate_next(self, field):
-        url_next = urlparse.urlsplit(field.data)
-        url_base = urlparse.urlsplit(request.host_url)
-        if url_next.netloc and url_next.netloc != url_base.netloc:
-            field.data = ''
-            raise ValidationError(get_message('INVALID_REDIRECT')[0])
-
-
-class RegisterFormMixin():
-    submit = SubmitField(get_form_field_label('register'))
-
-    def to_dict(form):
-        def is_field_and_user_attr(member):
-            print "in ifaua:", member
-            return isinstance(member, Field) and \
-                hasattr(_datastore.user_model, member.name)
-
-        print("green:", vars(form))
-        fields = inspect.getmembers(form, is_field_and_user_attr)
-        print("fields:" ,vars(form))
-        return dict((key, value.data) for key, value in fields)
-
-
-class SendConfirmationForm(Form, UserEmailFormMixin):
-    """The default forgot password form"""
-
-    submit = SubmitField(get_form_field_label('send_confirmation'))
-
-    def __init__(self, *args, **kwargs):
-        super(SendConfirmationForm, self).__init__(*args, **kwargs)
-        if request.method == 'GET':
-            self.email.data = request.args.get('email', None)
-
-    def validate(self):
-        if not super(SendConfirmationForm, self).validate():
-            return False
-        if self.user.confirmed_at is not None:
-            self.email.errors.append(get_message('ALREADY_CONFIRMED')[0])
-            return False
-        return True
-
-
-class ForgotPasswordForm(Form, UserEmailFormMixin):
-    """The default forgot password form"""
-
-    submit = SubmitField(get_form_field_label('recover_password'))
-
-
-class PasswordlessLoginForm(Form, UserEmailFormMixin):
-    """The passwordless login form"""
-
-    submit = SubmitField(get_form_field_label('send_login_link'))
-
-    def __init__(self, *args, **kwargs):
-        super(PasswordlessLoginForm, self).__init__(*args, **kwargs)
-
-    def validate(self):
-        if not super(PasswordlessLoginForm, self).validate():
-            return False
-        if not self.user.is_active():
-            self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
-            return False
-        return True
-
-
-class LoginForm(Form, NextFormMixin):
-    """The default login form"""
-
-    email = TextField(get_form_field_label('email'))
-    password = PasswordField(get_form_field_label('password'))
-    remember = BooleanField(get_form_field_label('remember_me'))
-    submit = SubmitField(get_form_field_label('login'))
-
-    def __init__(self, *args, **kwargs):
-        super(LoginForm, self).__init__(*args, **kwargs)
-
-    def validate(self):
-        if not super(LoginForm, self).validate():
-            return False
-
-        if self.email.data.strip() == '':
-            self.email.errors.append(get_message('EMAIL_NOT_PROVIDED')[0])
-            return False
-
-        if self.password.data.strip() == '':
-            self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0])
-            return False
-
-        self.user = _datastore.find_user(email=self.email.data)
-
-        if self.user is None:
-            self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
-            return False
-        if not verify_and_update_password(self.password.data, self.user):
-            self.password.errors.append(get_message('INVALID_PASSWORD')[0])
-            return False
-        if requires_confirmation(self.user):
-            self.email.errors.append(get_message('CONFIRMATION_REQUIRED')[0])
-            return False
-        if not self.user.is_active():
-            self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
-            return False
-        return True
-
-
-class ConfirmRegisterForm(Form, RegisterFormMixin,
-                          UniqueEmailFormMixin, NewPasswordFormMixin):
-    pass
-
-
-class RegisterForm(ConfirmRegisterForm, PasswordConfirmFormMixin):
-    pass
-
-
-class ResetPasswordForm(Form, NewPasswordFormMixin, PasswordConfirmFormMixin):
-    """The default reset password form"""
-
-    submit = SubmitField(get_form_field_label('reset_password'))
-
-
-class ChangePasswordForm(Form, PasswordFormMixin):
-    """The default change password form"""
-
-    new_password = PasswordField(get_form_field_label('new_password'),
-        validators=[password_required,
-                    Length(min=6, max=128)])
-
-    new_password_confirm = PasswordField(get_form_field_label('retype_password'),
-        validators=[EqualTo('new_password', message='RETYPE_PASSWORD_MISMATCH')])
-
-    submit = SubmitField(get_form_field_label('change_password'))
-
-    def validate(self):
-        if not super(ChangePasswordForm, self).validate():
-            return False
-
-        if self.password.data.strip() == '':
-            self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0])
-            return False
-        if not verify_and_update_password(self.password.data, current_user):
-            self.password.errors.append(get_message('INVALID_PASSWORD')[0])
-            return False
-        return True
diff --git a/wqflask/flask_security/passwordless.py b/wqflask/flask_security/passwordless.py
deleted file mode 100644
index b0accb2c..00000000
--- a/wqflask/flask_security/passwordless.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.passwordless
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security passwordless module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from flask import request, current_app as app
-from werkzeug.local import LocalProxy
-
-from .signals import login_instructions_sent
-from .utils import send_mail, url_for_security, get_token_status, \
-     config_value
-
-
-# Convenient references
-_security = LocalProxy(lambda: app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-
-def send_login_instructions(user):
-    """Sends the login instructions email for the specified user.
-
-    :param user: The user to send the instructions to
-    :param token: The login token
-    """
-    token = generate_login_token(user)
-    url = url_for_security('token_login', token=token)
-    login_link = request.url_root[:-1] + url
-
-    send_mail(config_value('EMAIL_SUBJECT_PASSWORDLESS'), user.email,
-              'login_instructions', user=user, login_link=login_link)
-
-    login_instructions_sent.send(dict(user=user, login_token=token),
-                                 app=app._get_current_object())
-
-
-def generate_login_token(user):
-    """Generates a unique login token for the specified user.
-
-    :param user: The user the token belongs to
-    """
-    return _security.login_serializer.dumps([str(user.id)])
-
-
-def login_token_status(token):
-    """Returns the expired status, invalid status, and user of a login token.
-    For example::
-
-        expired, invalid, user = login_token_status('...')
-
-    :param token: The login token
-    """
-    return get_token_status(token, 'login', 'LOGIN')
diff --git a/wqflask/flask_security/recoverable.py b/wqflask/flask_security/recoverable.py
deleted file mode 100644
index 6aafc111..00000000
--- a/wqflask/flask_security/recoverable.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.recoverable
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security recoverable module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from flask import current_app as app, request
-from werkzeug.local import LocalProxy
-
-from .signals import password_reset, reset_password_instructions_sent
-from .utils import send_mail, md5, encrypt_password, url_for_security, \
-     get_token_status, config_value
-
-
-# Convenient references
-_security = LocalProxy(lambda: app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-
-def send_reset_password_instructions(user):
-    """Sends the reset password instructions email for the specified user.
-
-    :param user: The user to send the instructions to
-    """
-    token = generate_reset_password_token(user)
-    url = url_for_security('reset_password', token=token)
-    reset_link = request.url_root[:-1] + url
-
-    send_mail(config_value('EMAIL_SUBJECT_PASSWORD_RESET'), user.email,
-              'reset_instructions',
-              user=user, reset_link=reset_link)
-
-    reset_password_instructions_sent.send(dict(user=user, token=token),
-                                          app=app._get_current_object())
-
-
-def send_password_reset_notice(user):
-    """Sends the password reset notice email for the specified user.
-
-    :param user: The user to send the notice to
-    """
-    send_mail(config_value('EMAIL_SUBJECT_PASSWORD_NOTICE'), user.email,
-              'reset_notice', user=user)
-
-
-def generate_reset_password_token(user):
-    """Generates a unique reset password token for the specified user.
-
-    :param user: The user to work with
-    """
-    data = [str(user.id), md5(user.password)]
-    return _security.reset_serializer.dumps(data)
-
-
-def reset_password_token_status(token):
-    """Returns the expired status, invalid status, and user of a password reset
-    token. For example::
-
-        expired, invalid, user = reset_password_token_status('...')
-
-    :param token: The password reset token
-    """
-    return get_token_status(token, 'reset', 'RESET_PASSWORD')
-
-def update_password(user, password):
-    """Update the specified user's password
-
-    :param user: The user to update_password
-    :param password: The unencrypted new password
-    """
-    user.password = encrypt_password(password)
-    _datastore.put(user)
-    send_password_reset_notice(user)
-    password_reset.send(user, app=app._get_current_object())
diff --git a/wqflask/flask_security/registerable.py b/wqflask/flask_security/registerable.py
deleted file mode 100644
index 4606c7c6..00000000
--- a/wqflask/flask_security/registerable.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.registerable
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security registerable module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from flask import current_app as app
-from werkzeug.local import LocalProxy
-
-from .confirmable import generate_confirmation_link
-from .signals import user_registered
-from .utils import do_flash, get_message, send_mail, encrypt_password, \
-     config_value
-
-# Convenient references
-_security = LocalProxy(lambda: app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-
-def register_user(**kwargs):
-    print "in register_user kwargs:", kwargs
-    confirmation_link, token = None, None
-    kwargs['password'] = encrypt_password(kwargs['password'])
-    user = _datastore.create_user(**kwargs)
-    _datastore.commit()
-
-    if _security.confirmable:
-        confirmation_link, token = generate_confirmation_link(user)
-        do_flash(*get_message('CONFIRM_REGISTRATION', email=user.email))
-
-    user_registered.send(dict(user=user, confirm_token=token),
-                         app=app._get_current_object())
-
-    if config_value('SEND_REGISTER_EMAIL'):
-        send_mail(config_value('EMAIL_SUBJECT_REGISTER'), user.email, 'welcome',
-                user=user, confirmation_link=confirmation_link)
-
-    return user
diff --git a/wqflask/flask_security/script.py b/wqflask/flask_security/script.py
deleted file mode 100644
index 9c9a2469..00000000
--- a/wqflask/flask_security/script.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.script
-    ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security script module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-try:
-    import simplejson as json
-except ImportError:
-    import json
-
-import re
-
-from flask import current_app
-from flask.ext.script import Command, Option
-from werkzeug.local import LocalProxy
-
-from .utils import encrypt_password
-
-
-_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
-
-
-def pprint(obj):
-    print json.dumps(obj, sort_keys=True, indent=4)
-
-
-def commit(fn):
-    def wrapper(*args, **kwargs):
-        fn(*args, **kwargs)
-        _datastore.commit()
-    return wrapper
-
-
-class CreateUserCommand(Command):
-    """Create a user"""
-
-    option_list = (
-        Option('-e', '--email',    dest='email',    default=None),
-        Option('-p', '--password', dest='password', default=None),
-        Option('-a', '--active',   dest='active',   default=''),
-    )
-
-    @commit
-    def run(self, **kwargs):
-        # sanitize active input
-        ai = re.sub(r'\s', '', str(kwargs['active']))
-        kwargs['active'] = ai.lower() in ['', 'y', 'yes', '1', 'active']
-
-        from flask_security.forms import ConfirmRegisterForm
-        from werkzeug.datastructures import MultiDict
-
-        form = ConfirmRegisterForm(MultiDict(kwargs), csrf_enabled=False)
-
-        if form.validate():
-            kwargs['password'] = encrypt_password(kwargs['password'])
-            _datastore.create_user(**kwargs)
-            print 'User created successfully.'
-            kwargs['password'] = '****'
-            pprint(kwargs)
-        else:
-            print 'Error creating user'
-            pprint(form.errors)
-
-
-class CreateRoleCommand(Command):
-    """Create a role"""
-
-    option_list = (
-        Option('-n', '--name', dest='name', default=None),
-        Option('-d', '--desc', dest='description', default=None),
-    )
-
-    @commit
-    def run(self, **kwargs):
-        _datastore.create_role(**kwargs)
-        print 'Role "%(name)s" created successfully.' % kwargs
-
-
-class _RoleCommand(Command):
-    option_list = (
-        Option('-u', '--user', dest='user_identifier'),
-        Option('-r', '--role', dest='role_name'),
-    )
-
-
-class AddRoleCommand(_RoleCommand):
-    """Add a role to a user"""
-
-    @commit
-    def run(self, user_identifier, role_name):
-        _datastore.add_role_to_user(user_identifier, role_name)
-        print "Role '%s' added to user '%s' successfully" % (role_name, user_identifier)
-
-
-class RemoveRoleCommand(_RoleCommand):
-    """Add a role to a user"""
-
-    @commit
-    def run(self, user_identifier, role_name):
-        _datastore.remove_role_from_user(user_identifier, role_name)
-        print "Role '%s' removed from user '%s' successfully" % (role_name, user_identifier)
-
-
-class _ToggleActiveCommand(Command):
-    option_list = (
-        Option('-u', '--user', dest='user_identifier'),
-    )
-
-
-class DeactivateUserCommand(_ToggleActiveCommand):
-    """Deactive a user"""
-
-    @commit
-    def run(self, user_identifier):
-        _datastore.deactivate_user(user_identifier)
-        print "User '%s' has been deactivated" % user_identifier
-
-
-class ActivateUserCommand(_ToggleActiveCommand):
-    """Deactive a user"""
-
-    @commit
-    def run(self, user_identifier):
-        _datastore.activate_user(user_identifier)
-        print "User '%s' has been activated" % user_identifier
diff --git a/wqflask/flask_security/signals.py b/wqflask/flask_security/signals.py
deleted file mode 100644
index e1c29548..00000000
--- a/wqflask/flask_security/signals.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.signals
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security signals module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-import blinker
-
-
-signals = blinker.Namespace()
-
-user_registered = signals.signal("user-registered")
-
-user_confirmed = signals.signal("user-confirmed")
-
-confirm_instructions_sent = signals.signal("confirm-instructions-sent")
-
-login_instructions_sent = signals.signal("login-instructions-sent")
-
-password_reset = signals.signal("password-reset")
-
-password_changed = signals.signal("password-changed")
-
-reset_password_instructions_sent = signals.signal("password-reset-instructions-sent")
diff --git a/wqflask/flask_security/templates/.DS_Store b/wqflask/flask_security/templates/.DS_Store
deleted file mode 100644
index b72f1d98..00000000
--- a/wqflask/flask_security/templates/.DS_Store
+++ /dev/null
Binary files differdiff --git a/wqflask/flask_security/templates/security/.DS_Store b/wqflask/flask_security/templates/security/.DS_Store
deleted file mode 100644
index 5008ddfc..00000000
--- a/wqflask/flask_security/templates/security/.DS_Store
+++ /dev/null
Binary files differdiff --git a/wqflask/flask_security/templates/security/_macros.html b/wqflask/flask_security/templates/security/_macros.html
deleted file mode 100644
index 8575f3db..00000000
--- a/wqflask/flask_security/templates/security/_macros.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% macro render_field_with_errors(field) %}
-  <p>
-    {{ field.label }} {{ field(**kwargs)|safe }}
-    {% if field.errors %}
-      <ul>
-      {% for error in field.errors %}
-        <li>{{ error }}</li>
-      {% endfor %}
-      </ul>
-    {% endif %}
-  </p>
-{% endmacro %}
-
-{% macro render_field(field) %}
-  <p>{{ field(**kwargs)|safe }}</p>
-{% endmacro %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/_menu.html b/wqflask/flask_security/templates/security/_menu.html
deleted file mode 100644
index 5291f809..00000000
--- a/wqflask/flask_security/templates/security/_menu.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% if security.registerable or security.recoverable or security.confirmabled %}
-<h2>Menu</h2>
-<ul>
-  <li><a href="{{ url_for_security('login') }}">Login</a></li>
-  {% if security.registerable %}
-  <li><a href="{{ url_for_security('register') }}">Register</a><br/></li>
-  {% endif %}
-  {% if security.recoverable %}
-  <li><a href="{{ url_for_security('forgot_password') }}">Forgot password</a><br/></li>
-  {% endif %}
-  {% if security.confirmable %}
-  <li><a href="{{ url_for_security('send_confirmation') }}">Confirm account</a></li>
-  {% endif %}
-</ul>
-{% endif %}
diff --git a/wqflask/flask_security/templates/security/_messages.html b/wqflask/flask_security/templates/security/_messages.html
deleted file mode 100644
index 179d0636..00000000
--- a/wqflask/flask_security/templates/security/_messages.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{%- with messages = get_flashed_messages(with_categories=true) -%}
-  {% if messages %}
-    <ul class="flashes">
-    {% for category, message in messages %}
-      <li class="{{ category }}">{{ message }}</li>
-    {% endfor %}
-    </ul>
-  {% endif %}
-{%- endwith %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/change_password.html b/wqflask/flask_security/templates/security/change_password.html
deleted file mode 100644
index 8ee3eb73..00000000
--- a/wqflask/flask_security/templates/security/change_password.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Change password</h1>
-<form action="{{ url_for_security('change_password') }}" method="POST" name="change_password_form">
-  {{ change_password_form.hidden_tag() }}
-  {{ render_field_with_errors(change_password_form.password) }}
-  {{ render_field_with_errors(change_password_form.new_password) }}
-  {{ render_field_with_errors(change_password_form.new_password_confirm) }}
-  {{ render_field(change_password_form.submit) }}
-</form>
-
diff --git a/wqflask/flask_security/templates/security/email/change_notice.html b/wqflask/flask_security/templates/security/email/change_notice.html
deleted file mode 100644
index d1224cf5..00000000
--- a/wqflask/flask_security/templates/security/email/change_notice.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<p>Your password has been changed.</p>
-{% if security.recoverable %}
-<p>If you did not change your password, <a href="{{ url_for_security('forgot_password', _external=True) }}">click here to reset it</a>.</p>
-{% endif %}
diff --git a/wqflask/flask_security/templates/security/email/change_notice.txt b/wqflask/flask_security/templates/security/email/change_notice.txt
deleted file mode 100644
index e74bd80d..00000000
--- a/wqflask/flask_security/templates/security/email/change_notice.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Your password has been changed
-{% if security.recoverable %}
-If you did not change your password, click the link below to reset it.
-{{ url_for_security('forgot_password', _external=True) }}
-{% endif %}
diff --git a/wqflask/flask_security/templates/security/email/confirmation_instructions.html b/wqflask/flask_security/templates/security/email/confirmation_instructions.html
deleted file mode 100644
index 5082a9a8..00000000
--- a/wqflask/flask_security/templates/security/email/confirmation_instructions.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<p>Please confirm your email through the link below:</p>
-
-<p><a href="{{ confirmation_link }}">Confirm my account</a></p>
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/confirmation_instructions.txt b/wqflask/flask_security/templates/security/email/confirmation_instructions.txt
deleted file mode 100644
index fb435b55..00000000
--- a/wqflask/flask_security/templates/security/email/confirmation_instructions.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Please confirm your email through the link below:
-
-{{ confirmation_link }}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/login_instructions.html b/wqflask/flask_security/templates/security/email/login_instructions.html
deleted file mode 100644
index 45a7cb57..00000000
--- a/wqflask/flask_security/templates/security/email/login_instructions.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<p>Welcome {{ user.email }}!</p>
-
-<p>You can log into your through the link below:</p>
-
-<p><a href="{{ login_link }}">Login now</a></p>
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/login_instructions.txt b/wqflask/flask_security/templates/security/email/login_instructions.txt
deleted file mode 100644
index 1364ed65..00000000
--- a/wqflask/flask_security/templates/security/email/login_instructions.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Welcome {{ user.email }}!
-
-You can log into your through the link below:
-
-{{ login_link }}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/reset_instructions.html b/wqflask/flask_security/templates/security/email/reset_instructions.html
deleted file mode 100644
index fd0b48d8..00000000
--- a/wqflask/flask_security/templates/security/email/reset_instructions.html
+++ /dev/null
@@ -1 +0,0 @@
-<p><a href="{{ reset_link }}">Click here to reset your password</a></p>
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/reset_instructions.txt b/wqflask/flask_security/templates/security/email/reset_instructions.txt
deleted file mode 100644
index 91ac288e..00000000
--- a/wqflask/flask_security/templates/security/email/reset_instructions.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Click the link below to reset your password:
-
-{{ reset_link }}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/reset_notice.html b/wqflask/flask_security/templates/security/email/reset_notice.html
deleted file mode 100644
index 536e2961..00000000
--- a/wqflask/flask_security/templates/security/email/reset_notice.html
+++ /dev/null
@@ -1 +0,0 @@
-<p>Your password has been reset</p>
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/reset_notice.txt b/wqflask/flask_security/templates/security/email/reset_notice.txt
deleted file mode 100644
index a3fa0b4b..00000000
--- a/wqflask/flask_security/templates/security/email/reset_notice.txt
+++ /dev/null
@@ -1 +0,0 @@
-Your password has been reset
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/welcome.html b/wqflask/flask_security/templates/security/email/welcome.html
deleted file mode 100644
index 55eaed61..00000000
--- a/wqflask/flask_security/templates/security/email/welcome.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<p>Welcome {{ user.email }}!</p>
-
-{% if security.confirmable %}
-<p>You can confirm your email through the link below:</p>
-
-<p><a href="{{ confirmation_link }}">Confirm my account</a></p>
-{% endif %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/email/welcome.txt b/wqflask/flask_security/templates/security/email/welcome.txt
deleted file mode 100644
index fb6ee5b5..00000000
--- a/wqflask/flask_security/templates/security/email/welcome.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Welcome {{ user.email }}!
-
-{% if security.confirmable %}
-You can confirm your email through the link below:
-
-{{ confirmation_link }}
-{% endif %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/forgot_password.html b/wqflask/flask_security/templates/security/forgot_password.html
deleted file mode 100644
index 90fcaf66..00000000
--- a/wqflask/flask_security/templates/security/forgot_password.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Send password reset instructions</h1>
-<form action="{{ url_for_security('forgot_password') }}" method="POST" name="forgot_password_form">
-  {{ forgot_password_form.hidden_tag() }}
-  {{ render_field_with_errors(forgot_password_form.email) }} 
-  {{ render_field(forgot_password_form.submit) }}
-</form>
-{% include "security/_menu.html" %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/login_user.html b/wqflask/flask_security/templates/security/login_user.html
deleted file mode 100644
index d781ce08..00000000
--- a/wqflask/flask_security/templates/security/login_user.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Login</h1>
-<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
-  {{ login_user_form.hidden_tag() }}
-  {{ render_field_with_errors(login_user_form.email) }}
-  {{ render_field_with_errors(login_user_form.password) }}
-  {{ render_field_with_errors(login_user_form.remember) }}
-  {{ render_field(login_user_form.next) }}
-  {{ render_field(login_user_form.submit) }}
-</form>
-{% include "security/_menu.html" %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/register_user.html b/wqflask/flask_security/templates/security/register_user.html
deleted file mode 100644
index 87cf9b1d..00000000
--- a/wqflask/flask_security/templates/security/register_user.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Register</h1>
-<form action="{{ url_for_security('register') }}" method="POST" name="register_user_form">
-  {{ register_user_form.hidden_tag() }}
-  {{ render_field_with_errors(register_user_form.email) }}
-  {{ render_field_with_errors(register_user_form.password) }}
-  {% if register_user_form.password_confirm %}
-    {{ render_field_with_errors(register_user_form.password_confirm) }}
-  {% endif %}
-  {{ render_field(register_user_form.submit) }}
-</form>
-{% include "security/_menu.html" %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/reset_password.html b/wqflask/flask_security/templates/security/reset_password.html
deleted file mode 100644
index e6fc3f58..00000000
--- a/wqflask/flask_security/templates/security/reset_password.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Reset password</h1>
-<form action="{{ url_for_security('reset_password', token=reset_password_token) }}" method="POST" name="reset_password_form">
-  {{ reset_password_form.hidden_tag() }}
-  {{ render_field_with_errors(reset_password_form.password) }}
-  {{ render_field_with_errors(reset_password_form.password_confirm) }}
-  {{ render_field(reset_password_form.submit) }}
-</form>
-{% include "security/_menu.html" %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/send_confirmation.html b/wqflask/flask_security/templates/security/send_confirmation.html
deleted file mode 100644
index 3e828407..00000000
--- a/wqflask/flask_security/templates/security/send_confirmation.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Resend confirmation instructions</h1>
-<form action="{{ url_for_security('send_confirmation') }}" method="POST" name="send_confirmation_form">
-  {{ send_confirmation_form.hidden_tag() }}
-  {{ render_field_with_errors(send_confirmation_form.email) }}
-  {{ render_field(send_confirmation_form.submit) }}
-</form>
-{% include "security/_menu.html" %}
\ No newline at end of file
diff --git a/wqflask/flask_security/templates/security/send_login.html b/wqflask/flask_security/templates/security/send_login.html
deleted file mode 100644
index 15611c57..00000000
--- a/wqflask/flask_security/templates/security/send_login.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% from "security/_macros.html" import render_field_with_errors, render_field %}
-{% include "security/_messages.html" %}
-<h1>Login</h1>
-<form action="{{ url_for_security('login') }}" method="POST" name="send_login_form">
-  {{ send_login_form.hidden_tag() }}
-  {{ render_field_with_errors(send_login_form.email) }}
-  {{ render_field(send_login_form.submit) }}
-</form>
-{% include "security/_menu.html" %}
\ No newline at end of file
diff --git a/wqflask/flask_security/utils.py b/wqflask/flask_security/utils.py
deleted file mode 100644
index 7397ab4f..00000000
--- a/wqflask/flask_security/utils.py
+++ /dev/null
@@ -1,379 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.utils
-    ~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security utils module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-import base64
-import blinker
-import functools
-import hashlib
-import hmac
-from contextlib import contextmanager
-from datetime import datetime, timedelta
-
-from flask import url_for, flash, current_app, request, session, render_template
-from flask.ext.login import login_user as _login_user, \
-     logout_user as _logout_user
-from flask.ext.mail import Message
-from flask.ext.principal import Identity, AnonymousIdentity, identity_changed
-from itsdangerous import BadSignature, SignatureExpired
-from werkzeug.local import LocalProxy
-
-from .signals import user_registered, user_confirmed, \
-    confirm_instructions_sent, login_instructions_sent, \
-    password_reset, password_changed, reset_password_instructions_sent
-
-# Convenient references
-_security = LocalProxy(lambda: current_app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-_pwd_context = LocalProxy(lambda: _security.pwd_context)
-
-
-def login_user(user, remember=True):
-    """Performs the login and sends the appropriate signal."""
-
-    if not _login_user(user, remember):
-        return False
-
-    if _security.trackable:
-        old_current_login, new_current_login = user.current_login_at, datetime.utcnow()
-        remote_addr = request.remote_addr or 'untrackable'
-        old_current_ip, new_current_ip = user.current_login_ip, remote_addr
-
-        user.last_login_at = old_current_login or new_current_login
-        user.current_login_at = new_current_login
-        user.last_login_ip = old_current_ip or new_current_ip
-        user.current_login_ip = new_current_ip
-        user.login_count = user.login_count + 1 if user.login_count else 1
-
-        _datastore.put(user)
-
-    identity_changed.send(current_app._get_current_object(),
-                          identity=Identity(user.id))
-    return True
-
-
-def logout_user():
-    for key in ('identity.name', 'identity.auth_type'):
-        session.pop(key, None)
-    identity_changed.send(current_app._get_current_object(),
-                          identity=AnonymousIdentity())
-    _logout_user()
-
-
-def get_hmac(password):
-    if _security.password_hash == 'plaintext':
-        return password
-
-    if _security.password_salt is None:
-        raise RuntimeError('The configuration value `SECURITY_PASSWORD_SALT` '
-            'must not be None when the value of `SECURITY_PASSWORD_HASH` is '
-            'set to "%s"' % _security.password_hash)
-
-    h = hmac.new(_security.password_salt, password.encode('utf-8'), hashlib.sha512)
-    return base64.b64encode(h.digest())
-
-
-def verify_password(password, password_hash):
-    return _pwd_context.verify(get_hmac(password), password_hash)
-
-
-def verify_and_update_password(password, user):
-    verified, new_password = _pwd_context.verify_and_update(get_hmac(password), user.password)
-    if verified and new_password:
-        user.password = new_password
-        _datastore.put(user)
-    return verified
-
-
-def encrypt_password(password):
-    return _pwd_context.encrypt(get_hmac(password))
-
-
-def md5(data):
-    return hashlib.md5(data).hexdigest()
-
-
-def do_flash(message, category=None):
-    """Flash a message depending on if the `FLASH_MESSAGES` configuration
-    value is set.
-
-    :param message: The flash message
-    :param category: The flash message category
-    """
-    if config_value('FLASH_MESSAGES'):
-        flash(message, category)
-
-
-def get_url(endpoint_or_url):
-    """Returns a URL if a valid endpoint is found. Otherwise, returns the
-    provided value.
-
-    :param endpoint_or_url: The endpoint name or URL to default to
-    """
-    try:
-        return url_for(endpoint_or_url)
-    except:
-        return endpoint_or_url
-
-
-def get_security_endpoint_name(endpoint):
-    return '%s.%s' % (_security.blueprint_name, endpoint)
-
-
-def url_for_security(endpoint, **values):
-    """Return a URL for the security blueprint
-
-    :param endpoint: the endpoint of the URL (name of the function)
-    :param values: the variable arguments of the URL rule
-    :param _external: if set to `True`, an absolute URL is generated. Server
-      address can be changed via `SERVER_NAME` configuration variable which
-      defaults to `localhost`.
-    :param _anchor: if provided this is added as anchor to the URL.
-    :param _method: if provided this explicitly specifies an HTTP method.
-    """
-    endpoint = get_security_endpoint_name(endpoint)
-    return url_for(endpoint, **values)
-
-
-def get_post_login_redirect():
-    """Returns the URL to redirect to after a user logs in successfully."""
-    return (get_url(request.args.get('next')) or
-            get_url(request.form.get('next')) or
-            find_redirect('SECURITY_POST_LOGIN_VIEW'))
-
-
-def find_redirect(key):
-    """Returns the URL to redirect to after a user logs in successfully.
-
-    :param key: The session or application configuration key to search for
-    """
-    rv = (get_url(session.pop(key.lower(), None)) or
-          get_url(current_app.config[key.upper()] or None) or '/')
-    return rv
-
-
-def get_config(app):
-    """Conveniently get the security configuration for the specified
-    application without the annoying 'SECURITY_' prefix.
-
-    :param app: The application to inspect
-    """
-    items = app.config.items()
-    prefix = 'SECURITY_'
-
-    def strip_prefix(tup):
-        return (tup[0].replace('SECURITY_', ''), tup[1])
-
-    return dict([strip_prefix(i) for i in items if i[0].startswith(prefix)])
-
-
-def get_message(key, **kwargs):
-    rv = config_value('MSG_' + key)
-    return rv[0] % kwargs, rv[1]
-
-
-def config_value(key, app=None, default=None):
-    """Get a Flask-Security configuration value.
-
-    :param key: The configuration key without the prefix `SECURITY_`
-    :param app: An optional specific application to inspect. Defaults to Flask's
-                `current_app`
-    :param default: An optional default value if the value is not set
-    """
-    app = app or current_app
-    return get_config(app).get(key.upper(), default)
-
-
-def get_max_age(key, app=None):
-    now = datetime.utcnow()
-    expires = now + get_within_delta(key + '_WITHIN', app)
-    td = (expires - now)
-    return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 1e6) / 1e6
-
-
-def get_within_delta(key, app=None):
-    """Get a timedelta object from the application configuration following
-    the internal convention of::
-
-        <Amount of Units> <Type of Units>
-
-    Examples of valid config values::
-
-        5 days
-        10 minutes
-
-    :param key: The config value key without the 'SECURITY_' prefix
-    :param app: Optional application to inspect. Defaults to Flask's
-                `current_app`
-    """
-    txt = config_value(key, app=app)
-    values = txt.split()
-    return timedelta(**{values[1]: int(values[0])})
-
-
-def send_mail(subject, recipient, template, **context):
-    """Send an email via the Flask-Mail extension.
-
-    :param subject: Email subject
-    :param recipient: Email recipient
-    :param template: The name of the email template
-    :param context: The context to render the template with
-    """
-
-    context.setdefault('security', _security)
-    context.update(_security._run_ctx_processor('mail'))
-
-    msg = Message(subject,
-                  sender=_security.email_sender,
-                  recipients=[recipient])
-
-    ctx = ('security/email', template)
-    msg.body = render_template('%s/%s.txt' % ctx, **context)
-    msg.html = render_template('%s/%s.html' % ctx, **context)
-
-    if _security._send_mail_task:
-        _security._send_mail_task(msg)
-        return
-
-    mail = current_app.extensions.get('mail')
-    mail.send(msg)
-
-
-def get_token_status(token, serializer, max_age=None):
-    serializer = getattr(_security, serializer + '_serializer')
-    max_age = get_max_age(max_age)
-    user, data = None, None
-    expired, invalid = False, False
-
-    try:
-        data = serializer.loads(token, max_age=max_age)
-    except SignatureExpired:
-        d, data = serializer.loads_unsafe(token)
-        expired = True
-    except BadSignature:
-        invalid = True
-
-    if data:
-        user = _datastore.find_user(id=data[0])
-
-    expired = expired and (user is not None)
-    return expired, invalid, user
-
-
-@contextmanager
-def capture_passwordless_login_requests():
-    login_requests = []
-
-    def _on(data, app):
-        login_requests.append(data)
-
-    login_instructions_sent.connect(_on)
-
-    try:
-        yield login_requests
-    finally:
-        login_instructions_sent.disconnect(_on)
-
-
-@contextmanager
-def capture_registrations():
-    """Testing utility for capturing registrations.
-
-    :param confirmation_sent_at: An optional datetime object to set the
-                                 user's `confirmation_sent_at` to
-    """
-    registrations = []
-
-    def _on(data, app):
-        registrations.append(data)
-
-    user_registered.connect(_on)
-
-    try:
-        yield registrations
-    finally:
-        user_registered.disconnect(_on)
-
-
-@contextmanager
-def capture_reset_password_requests(reset_password_sent_at=None):
-    """Testing utility for capturing password reset requests.
-
-    :param reset_password_sent_at: An optional datetime object to set the
-                                   user's `reset_password_sent_at` to
-    """
-    reset_requests = []
-
-    def _on(request, app):
-        reset_requests.append(request)
-
-    reset_password_instructions_sent.connect(_on)
-
-    try:
-        yield reset_requests
-    finally:
-        reset_password_instructions_sent.disconnect(_on)
-
-
-class CaptureSignals(object):
-    """Testing utility for capturing blinker signals.
-
-    Context manager which mocks out selected signals and registers which are `sent` on and what
-    arguments were sent. Instantiate with a list of blinker `NamedSignals` to patch. Each signal
-    has it's `send` mocked out.
-    """
-    def __init__(self, signals):
-        """Patch all given signals and make them available as attributes.
-
-        :param signals: list of signals
-        """
-        self._records = {}
-        self._receivers = {}
-        for signal in signals:
-            self._records[signal] = []
-            self._receivers[signal] = functools.partial(self._record, signal)
-
-    def __getitem__(self, signal):
-        """All captured signals are available via `ctxt[signal]`.
-        """
-        if isinstance(signal, blinker.base.NamedSignal):
-            return self._records[signal]
-        else:
-            super(CaptureSignals, self).__setitem__(signal)
-
-    def _record(self, signal, *args, **kwargs):
-        self._records[signal].append((args, kwargs))
-
-    def __enter__(self):
-        for signal, receiver in self._receivers.iteritems():
-            signal.connect(receiver)
-        return self
-
-    def __exit__(self, type, value, traceback):
-        for signal, receiver in self._receivers.iteritems():
-            signal.disconnect(receiver)
-
-    def signals_sent(self):
-        """Return a set of the signals sent.
-        :rtype: list of blinker `NamedSignals`.
-        """
-        return set([signal for signal, _ in self._records.iteritems() if self._records[signal]])
-
-
-def capture_signals():
-    """Factory method that creates a `CaptureSignals` with all the flask_security signals."""
-    return CaptureSignals([user_registered, user_confirmed,
-                           confirm_instructions_sent, login_instructions_sent,
-                           password_reset, password_changed,
-                           reset_password_instructions_sent])
-
-
diff --git a/wqflask/flask_security/views.py b/wqflask/flask_security/views.py
deleted file mode 100644
index 1b8488d8..00000000
--- a/wqflask/flask_security/views.py
+++ /dev/null
@@ -1,359 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flask.ext.security.views
-    ~~~~~~~~~~~~~~~~~~~~~~~~
-
-    Flask-Security views module
-
-    :copyright: (c) 2012 by Matt Wright.
-    :license: MIT, see LICENSE for more details.
-"""
-
-from flask import current_app, redirect, request, render_template, jsonify, \
-     after_this_request, Blueprint
-from flask_login import current_user
-from werkzeug.datastructures import MultiDict
-from werkzeug.local import LocalProxy
-
-from .confirmable import send_confirmation_instructions, \
-     confirm_user, confirm_email_token_status
-from .decorators import login_required, anonymous_user_required
-from .passwordless import send_login_instructions, \
-     login_token_status
-from .recoverable import reset_password_token_status, \
-     send_reset_password_instructions, update_password
-from .changeable import change_user_password
-from .registerable import register_user
-from .utils import get_url, get_post_login_redirect, do_flash, \
-     get_message, login_user, logout_user, url_for_security as url_for, \
-     config_value
-
-
-# Convenient references
-_security = LocalProxy(lambda: current_app.extensions['security'])
-
-_datastore = LocalProxy(lambda: _security.datastore)
-
-
-def _render_json(form, include_auth_token=False):
-    has_errors = len(form.errors) > 0
-
-    if has_errors:
-        code = 400
-        response = dict(errors=form.errors)
-    else:
-        code = 200
-        response = dict(user=dict(id=str(form.user.id)))
-        if include_auth_token:
-            token = form.user.get_auth_token()
-            response['user']['authentication_token'] = token
-
-    return jsonify(dict(meta=dict(code=code), response=response))
-
-
-def _commit(response=None):
-    _datastore.commit()
-    return response
-
-
-def _ctx(endpoint):
-    return _security._run_ctx_processor(endpoint)
-
-
-@anonymous_user_required
-def login():
-    """View function for login view"""
-
-    form_class = _security.login_form
-
-    if request.json:
-        form = form_class(MultiDict(request.json))
-    else:
-        form = form_class()
-
-    if form.validate_on_submit():
-        login_user(form.user, remember=form.remember.data)
-        after_this_request(_commit)
-
-        if not request.json:
-            return redirect(get_post_login_redirect())
-
-    form.next.data = get_url(request.args.get('next')) \
-                     or get_url(request.form.get('next')) or ''
-
-    if request.json:
-        return _render_json(form, True)
-
-    return render_template(config_value('LOGIN_USER_TEMPLATE'),
-                           login_user_form=form,
-                           **_ctx('login'))
-
-
-@login_required
-def logout():
-    """View function which handles a logout request."""
-
-    logout_user()
-
-    return redirect(request.args.get('next', None) or
-                    get_url(_security.post_logout_view))
-
-
-def register():
-    """View function which handles a registration request."""
-
-    if _security.confirmable or request.json:
-        form_class = _security.confirm_register_form
-    else:
-        form_class = _security.register_form
-
-    if request.json:
-        form_data = MultiDict(request.json)
-    else:
-        form_data = request.form
-
-    form = form_class(form_data)
-
-    if form.validate_on_submit():
-        user = register_user(**form.to_dict())
-        form.user = user
-
-        if not _security.confirmable or _security.login_without_confirmation:
-            after_this_request(_commit)
-            login_user(user)
-
-        if not request.json:
-            post_register_url = get_url(_security.post_register_view)
-            post_login_url = get_url(_security.post_login_view)
-            return redirect(post_register_url or post_login_url)
-
-    if request.json:
-        return _render_json(form)
-
-    return render_template(config_value('REGISTER_USER_TEMPLATE'),
-                           register_user_form=form,
-                           **_ctx('register'))
-
-
-def send_login():
-    """View function that sends login instructions for passwordless login"""
-
-    form_class = _security.passwordless_login_form
-
-    if request.json:
-        form = form_class(MultiDict(request.json))
-    else:
-        form = form_class()
-
-    if form.validate_on_submit():
-        send_login_instructions(form.user)
-        if request.json is None:
-            do_flash(*get_message('LOGIN_EMAIL_SENT', email=form.user.email))
-
-    if request.json:
-        return _render_json(form)
-
-    return render_template(config_value('SEND_LOGIN_TEMPLATE'),
-                           send_login_form=form,
-                           **_ctx('send_login'))
-
-
-@anonymous_user_required
-def token_login(token):
-    """View function that handles passwordless login via a token"""
-
-    expired, invalid, user = login_token_status(token)
-
-    if invalid:
-        do_flash(*get_message('INVALID_LOGIN_TOKEN'))
-    if expired:
-        send_login_instructions(user)
-        do_flash(*get_message('LOGIN_EXPIRED', email=user.email,
-                              within=_security.login_within))
-    if invalid or expired:
-        return redirect(url_for('login'))
-
-    login_user(user, True)
-    after_this_request(_commit)
-    do_flash(*get_message('PASSWORDLESS_LOGIN_SUCCESSFUL'))
-
-    return redirect(get_post_login_redirect())
-
-
-def send_confirmation():
-    """View function which sends confirmation instructions."""
-
-    form_class = _security.send_confirmation_form
-
-    if request.json:
-        form = form_class(MultiDict(request.json))
-    else:
-        form = form_class()
-
-    if form.validate_on_submit():
-        send_confirmation_instructions(form.user)
-        if request.json is None:
-            do_flash(*get_message('CONFIRMATION_REQUEST', email=form.user.email))
-
-    if request.json:
-        return _render_json(form)
-
-    return render_template(config_value('SEND_CONFIRMATION_TEMPLATE'),
-                           send_confirmation_form=form,
-                           **_ctx('send_confirmation'))
-
-
-@anonymous_user_required
-def confirm_email(token):
-    """View function which handles a email confirmation request."""
-
-    expired, invalid, user = confirm_email_token_status(token)
-
-    if not user or invalid:
-        invalid = True
-        do_flash(*get_message('INVALID_CONFIRMATION_TOKEN'))
-    if expired:
-        send_confirmation_instructions(user)
-        do_flash(*get_message('CONFIRMATION_EXPIRED', email=user.email,
-                              within=_security.confirm_email_within))
-    if invalid or expired:
-        return redirect(get_url(_security.confirm_error_view) or
-                        url_for('send_confirmation'))
-
-    confirm_user(user)
-    login_user(user, True)
-    after_this_request(_commit)
-    do_flash(*get_message('EMAIL_CONFIRMED'))
-
-    return redirect(get_url(_security.post_confirm_view) or
-                    get_url(_security.post_login_view))
-
-
-def forgot_password():
-    """View function that handles a forgotten password request."""
-
-    form_class = _security.forgot_password_form
-
-    if request.json:
-        form = form_class(MultiDict(request.json))
-    else:
-        form = form_class()
-
-    if form.validate_on_submit():
-        send_reset_password_instructions(form.user)
-        if request.json is None:
-            do_flash(*get_message('PASSWORD_RESET_REQUEST', email=form.user.email))
-
-    if request.json:
-        return _render_json(form)
-
-    return render_template(config_value('FORGOT_PASSWORD_TEMPLATE'),
-                           forgot_password_form=form,
-                           **_ctx('forgot_password'))
-
-
-@anonymous_user_required
-def reset_password(token):
-    """View function that handles a reset password request."""
-
-    expired, invalid, user = reset_password_token_status(token)
-
-    if invalid:
-        do_flash(*get_message('INVALID_RESET_PASSWORD_TOKEN'))
-    if expired:
-        do_flash(*get_message('PASSWORD_RESET_EXPIRED', email=user.email,
-                              within=_security.reset_password_within))
-    if invalid or expired:
-        return redirect(url_for('forgot_password'))
-
-    form = _security.reset_password_form()
-
-    if form.validate_on_submit():
-        after_this_request(_commit)
-        update_password(user, form.password.data)
-        do_flash(*get_message('PASSWORD_RESET'))
-        login_user(user, True)
-        return redirect(get_url(_security.post_reset_view) or
-                        get_url(_security.post_login_view))
-
-    return render_template(config_value('RESET_PASSWORD_TEMPLATE'),
-                           reset_password_form=form,
-                           reset_password_token=token,
-                           **_ctx('reset_password'))
-
-
-@login_required
-def change_password():
-    """View function which handles a change password request."""
-
-    form_class = _security.change_password_form
-
-    if request.json:
-        form = form_class(MultiDict(request.json))
-    else:
-        form = form_class()
-
-    if form.validate_on_submit():
-        after_this_request(_commit)
-        change_user_password(current_user, form.new_password.data)
-        if request.json is None:
-            do_flash(*get_message('PASSWORD_CHANGE'))
-            return redirect(get_url(_security.post_change_view) or
-                            get_url(_security.post_login_view))
-
-    if request.json:
-        return _render_json(form)
-
-    return render_template('security/change_password.html',
-                           change_password_form=form,
-                           **_ctx('change_password'))
-
-
-def create_blueprint(state, import_name):
-    """Creates the security extension blueprint"""
-
-    bp = Blueprint(state.blueprint_name, import_name,
-                   url_prefix=state.url_prefix,
-                   subdomain=state.subdomain,
-                   template_folder='templates')
-
-    bp.route(state.logout_url, endpoint='logout')(logout)
-
-    if state.passwordless:
-        bp.route(state.login_url,
-                 methods=['GET', 'POST'],
-                 endpoint='login')(send_login)
-        bp.route(state.login_url + '/<token>',
-                 endpoint='token_login')(token_login)
-    else:
-        bp.route(state.login_url,
-                 methods=['GET', 'POST'],
-                 endpoint='login')(login)
-
-    if state.registerable:
-        bp.route(state.register_url,
-                 methods=['GET', 'POST'],
-                 endpoint='register')(register)
-
-    if state.recoverable:
-        bp.route(state.reset_url,
-                 methods=['GET', 'POST'],
-                 endpoint='forgot_password')(forgot_password)
-        bp.route(state.reset_url + '/<token>',
-                 methods=['GET', 'POST'],
-                 endpoint='reset_password')(reset_password)
-
-    if state.changeable:
-        bp.route(state.change_url,
-                 methods=['GET', 'POST'],
-                 endpoint='change_password')(change_password)
-
-    if state.confirmable:
-        bp.route(state.confirm_url,
-                 methods=['GET', 'POST'],
-                 endpoint='send_confirmation')(send_confirmation)
-        bp.route(state.confirm_url + '/<token>',
-                 methods=['GET', 'POST'],
-                 endpoint='confirm_email')(confirm_email)
-
-    return bp
diff --git a/wqflask/mock/__init__.py b/wqflask/mock/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/wqflask/mock/__init__.py
diff --git a/wqflask/mock/es_double.py b/wqflask/mock/es_double.py
new file mode 100644
index 00000000..6ef8a1b9
--- /dev/null
+++ b/wqflask/mock/es_double.py
@@ -0,0 +1,15 @@
+class ESDouble(object):
+    def __init__(self):
+        self.items = {}
+
+    def ping(self):
+        return true
+
+    def create(self, index, doc_type, body, id):
+        self.items["index"] = {doc_type: {"id": id, "_source": data}}
+
+    def search(self, index, doc_type, body):
+        return {
+            "hits": {
+                "hits": self.items[index][doc_type][body]
+            }}
diff --git a/wqflask/run_gunicorn.py b/wqflask/run_gunicorn.py
index 14a2d689..adffdca3 100644
--- a/wqflask/run_gunicorn.py
+++ b/wqflask/run_gunicorn.py
@@ -7,9 +7,12 @@
 # from flask import Flask
 # application = Flask(__name__)
 
-print "Starting up Gunicorn process"
+print "===> Starting up Gunicorn process"
 
 from wqflask import app
+from utility.startup_config import app_config
+
+app_config()
 
 @app.route("/gunicorn")
 def hello():
diff --git a/wqflask/runserver.py b/wqflask/runserver.py
index a0c76e51..5f41d04d 100644
--- a/wqflask/runserver.py
+++ b/wqflask/runserver.py
@@ -21,22 +21,9 @@ GREEN = '\033[92m'
 BOLD  = '\033[1m'
 ENDC  = '\033[0m'
 
-import os
-app.config['SECRET_KEY'] = os.urandom(24)
+from utility.startup_config import app_config
 
-from utility.tools import WEBSERVER_MODE,get_setting_int,get_setting,get_setting_bool
-
-port = get_setting_int("SERVER_PORT")
-
-print("GN2 API server URL is ["+BLUE+get_setting("GN_SERVER_URL")+ENDC+"]")
-
-if get_setting_bool("USE_GN_SERVER"):
-    import requests
-    page = requests.get(get_setting("GN_SERVER_URL"))
-    if page.status_code != 200:
-        raise Exception("API server not found!")
-
-print("GN2 is running. Visit %s[http://localhost:%s/%s](%s)" % (BLUE,str(port),ENDC,get_setting("WEBSERVER_URL")))
+app_config()
 
 werkzeug_logger = logging.getLogger('werkzeug')
 
diff --git a/wqflask/utility/elasticsearch_tools.py b/wqflask/utility/elasticsearch_tools.py
new file mode 100644
index 00000000..734379f7
--- /dev/null
+++ b/wqflask/utility/elasticsearch_tools.py
@@ -0,0 +1,59 @@
+from elasticsearch import Elasticsearch, TransportError
+import logging
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+from utility.tools import ELASTICSEARCH_HOST, ELASTICSEARCH_PORT
+
+def test_elasticsearch_connection():
+    es = Elasticsearch(['http://'+ELASTICSEARCH_HOST+":"+ELASTICSEARCH_PORT+'/'], verify_certs=True)
+    if not es.ping():
+        logger.warning("Elasticsearch is DOWN")
+
+def get_elasticsearch_connection():
+    logger.info("get_elasticsearch_connection")
+    es = None
+    try:
+        assert(ELASTICSEARCH_HOST)
+        assert(ELASTICSEARCH_PORT)
+        logger.info("ES HOST",ELASTICSEARCH_HOST)
+
+        es = Elasticsearch([{
+            "host": ELASTICSEARCH_HOST
+            , "port": ELASTICSEARCH_PORT
+        }]) if (ELASTICSEARCH_HOST and ELASTICSEARCH_PORT) else None
+
+        es_logger = logging.getLogger("elasticsearch")
+        es_logger.setLevel(logging.INFO)
+        es_logger.addHandler(logging.NullHandler())
+    except:
+        es = None
+
+    return es
+
+def get_user_by_unique_column(es, column_name, column_value, index="users", doc_type="local"):
+    return get_item_by_unique_column(es, column_name, column_value, index=index, doc_type=doc_type)
+
+def save_user(es, user, user_id):
+    es_save_data(es, "users", "local", user, user_id)
+
+def get_item_by_unique_column(es, column_name, column_value, index, doc_type):
+    item_details = None
+    try:
+        response = es.search(
+            index = index
+            , doc_type = doc_type
+            , body = {
+                "query": { "match": { column_name: column_value } }
+            })
+        if len(response["hits"]["hits"]) > 0:
+            item_details = response["hits"]["hits"][0]["_source"]
+    except TransportError as te:
+        pass
+    return item_details
+
+def es_save_data(es, index, doc_type, data_item, data_id,):
+    from time import sleep
+    es.create(index, doc_type, body=data_item, id=data_id)
+    sleep(1) # Delay 1 second to allow indexing
diff --git a/wqflask/utility/startup_config.py b/wqflask/utility/startup_config.py
new file mode 100644
index 00000000..5a62cc50
--- /dev/null
+++ b/wqflask/utility/startup_config.py
@@ -0,0 +1,39 @@
+
+from wqflask import app
+from utility.tools import WEBSERVER_MODE, show_settings, get_setting_int, get_setting, get_setting_bool
+
+import utility.logger
+logger = utility.logger.getLogger(__name__ )
+
+BLUE  = '\033[94m'
+GREEN = '\033[92m'
+BOLD  = '\033[1m'
+ENDC  = '\033[0m'
+
+def app_config():
+    app.config['SESSION_TYPE'] = 'filesystem'
+    if not app.config.get('SECRET_KEY'):
+        import os
+        app.config['SECRET_KEY'] = str(os.urandom(24))
+
+    mode = WEBSERVER_MODE
+    if mode == "DEV" or mode == "DEBUG":
+        app.config['TEMPLATES_AUTO_RELOAD'] = True
+        # if mode == "DEBUG":
+        #     app.config['EXPLAIN_TEMPLATE_LOADING'] = True <--- use overriding app param instead
+    print("==========================================")
+    show_settings()
+
+    port = get_setting_int("SERVER_PORT")
+
+    if get_setting_bool("USE_GN_SERVER"):
+        print("GN2 API server URL is ["+BLUE+get_setting("GN_SERVER_URL")+ENDC+"]")
+        import requests
+        page = requests.get(get_setting("GN_SERVER_URL"))
+        if page.status_code != 200:
+            raise Exception("API server not found!")
+
+    import utility.elasticsearch_tools as es
+    es.test_elasticsearch_connection()
+
+    print("GN2 is running. Visit %s[http://localhost:%s/%s](%s)" % (BLUE,str(port),ENDC,get_setting("WEBSERVER_URL")))
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index d3113302..59bb49d8 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -54,7 +54,7 @@ def get_setting(command_id,guess=None):
     # print("Looking for "+command_id+"\n")
     command = value(os.environ.get(command_id))
     if command is None or command == "":
-        command = OVERRIDES.get(command_id)
+        command = OVERRIDES.get(command_id) # currently not in use
         if command is None:
             # ---- Check whether setting exists in app
             command = value(app.config.get(command_id))
@@ -220,7 +220,7 @@ def show_settings():
 
     logger.info(OVERRIDES)
     logger.info(BLUE+"Mr. Mojo Risin 2"+ENDC)
-    print "runserver.py: ****** Webserver configuration ******"
+    print "runserver.py: ****** Webserver configuration - k,v pairs from app.config ******"
     keylist = app.config.keys()
     keylist.sort()
     for k in keylist:
@@ -251,6 +251,31 @@ assert_dir(JS_GUIX_PATH)
 JS_GN_PATH         = get_setting('JS_GN_PATH')
 # assert_dir(JS_GN_PATH)
 
+GITHUB_CLIENT_ID = get_setting('GITHUB_CLIENT_ID')
+GITHUB_CLIENT_SECRET = get_setting('GITHUB_CLIENT_SECRET')
+GITHUB_AUTH_URL = None
+if GITHUB_CLIENT_ID != 'UNKNOWN' and GITHUB_CLIENT_SECRET:
+    GITHUB_AUTH_URL = "https://github.com/login/oauth/authorize?client_id=" + \
+                      GITHUB_CLIENT_ID+"&client_secret="+GITHUB_CLIENT_SECRET
+    GITHUB_API_URL = get_setting('GITHUB_API_URL')
+
+ORCID_CLIENT_ID = get_setting('ORCID_CLIENT_ID')
+ORCID_CLIENT_SECRET = get_setting('ORCID_CLIENT_SECRET')
+ORCID_AUTH_URL = None
+if ORCID_CLIENT_ID != 'UNKNOWN' and ORCID_CLIENT_SECRET:
+    ORCID_AUTH_URL = "https://sandbox.orcid.org/oauth/authorize?response_type=code&scope=/authenticate&show_login=true&client_id=" + \
+                      ORCID_CLIENT_ID+"&client_secret="+ORCID_CLIENT_SECRET
+    ORCID_TOKEN_URL = get_setting('ORCID_TOKEN_URL')
+
+ELASTICSEARCH_HOST = get_setting('ELASTICSEARCH_HOST')
+ELASTICSEARCH_PORT = get_setting('ELASTICSEARCH_PORT')
+import utility.elasticsearch_tools as es
+es.test_elasticsearch_connection()
+
+SMTP_CONNECT = get_setting('SMTP_CONNECT')
+SMTP_USERNAME = get_setting('SMTP_USERNAME')
+SMTP_PASSWORD = get_setting('SMTP_PASSWORD')
+
 PYLMM_COMMAND      = app_set("PYLMM_COMMAND",pylmm_command())
 GEMMA_COMMAND      = app_set("GEMMA_COMMAND",gemma_command())
 assert(GEMMA_COMMAND is not None)
@@ -265,18 +290,5 @@ assert_dir(JS_TWITTER_POST_FETCHER_PATH)
 
 from six import string_types
 
-if os.environ.get('WQFLASK_OVERRIDES'):
-    jsonfn = get_setting('WQFLASK_OVERRIDES')
-    logger.info("WQFLASK_OVERRIDES: %s" % jsonfn)
-    with open(jsonfn) as data_file:
-        overrides = json.load(data_file)
-        for k in overrides:
-            cmd = overrides[k]
-            if isinstance(cmd, string_types):
-                OVERRIDES[k] = eval(cmd)
-            else:
-                OVERRIDES[k] = cmd
-            logger.debug(OVERRIDES)
-
 # assert_file(PHEWAS_FILES+"/auwerx/PheWAS_pval_EMMA_norm.RData")
 assert_file(JS_TWITTER_POST_FETCHER_PATH+"/js/twitterFetcher_min.js")
diff --git a/wqflask/utility/type_checking.py b/wqflask/utility/type_checking.py
new file mode 100644
index 00000000..220e5f62
--- /dev/null
+++ b/wqflask/utility/type_checking.py
@@ -0,0 +1,42 @@
+# Type checking functions
+
+def is_float(value):
+    try:
+        float(value)
+        return True
+    except:
+        return False
+
+def is_int(value):
+    try:
+        int(value)
+        return True
+    except:
+        return False
+
+def is_str(value):
+    if value is None:
+        return False
+    try:
+        str(value)
+        return True
+    except:
+        return False
+
+def get_float(vars,name,default=None):
+    if name in vars:
+        if is_float(vars[name]):
+            return float(vars[name])
+    return None
+
+def get_int(vars,name,default=None):
+    if name in vars:
+        if is_int(vars[name]):
+            return float(vars[name])
+    return default
+
+def get_string(vars,name,default=None):
+    if name in vars:
+        if not vars[name] is None:
+            return str(vars[name])
+    return default
diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py
index 2188ce17..bc8e9900 100644
--- a/wqflask/wqflask/__init__.py
+++ b/wqflask/wqflask/__init__.py
@@ -13,8 +13,8 @@ logging.basicConfig(level=logging.INFO)
 app = Flask(__name__)
 
 app.config.from_object('cfg.default_settings')   # Get the defaults from cfg.default_settings
-app.config.from_envvar('WQFLASK_SETTINGS')       # See http://flask.pocoo.org/docs/config/#configuring-from-files
-# Note we also use WQFLASK_OVERRIDES
+app.config.from_envvar('GN2_SETTINGS')       # See http://flask.pocoo.org/docs/config/#configuring-from-files
+# Note no longer use the badly named WQFLASK_OVERRIDES (nyi)
 
 app.jinja_env.globals.update(
     undefined = jinja2.StrictUndefined,
diff --git a/wqflask/wqflask/correlation/show_corr_results.py b/wqflask/wqflask/correlation/show_corr_results.py
index 12f76b7d..73072423 100644
--- a/wqflask/wqflask/correlation/show_corr_results.py
+++ b/wqflask/wqflask/correlation/show_corr_results.py
@@ -52,6 +52,7 @@ import utility.webqtlUtil #this is for parallel computing only.
 from wqflask.correlation import correlation_functions
 from utility.benchmark import Bench
 import utility.webqtlUtil
+from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
 from wqflask import user_manager
 
 from MySQLdb import escape_string as escape
@@ -77,47 +78,6 @@ def print_mem(stage=""):
     mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
     #print("{}: {}".format(stage, mem/1024))
 
-def is_float(value):
-    try:
-        float(value)
-        return True
-    except:
-        return False
-
-def is_int(value):
-    try:
-        int(value)
-        return True
-    except:
-        return False
-
-def is_str(value):
-    if value is None:
-        return False
-    try:
-        str(value)
-        return True
-    except:
-        return False
-
-def get_float(vars,name,default=None):
-    if name in vars:
-        if is_float(vars[name]):
-            return float(vars[name])
-    return None
-
-def get_int(vars,name,default=None):
-    if name in vars:
-        if is_int(vars[name]):
-            return float(vars[name])
-    return default
-
-def get_string(vars,name,default=None):
-    if name in vars:
-        if not vars[name] is None:
-            return str(vars[name])
-    return default
-
 class AuthException(Exception):
     pass
 
diff --git a/wqflask/wqflask/gsearch.py b/wqflask/wqflask/gsearch.py
index e33e04e1..fe1e17d2 100644
--- a/wqflask/wqflask/gsearch.py
+++ b/wqflask/wqflask/gsearch.py
@@ -5,6 +5,7 @@ from base.data_set import create_dataset
 from base.trait import GeneralTrait
 from db import webqtlDatabaseFunction
 
+from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
 from utility.benchmark import Bench
 
 from utility.logger import getLogger
@@ -13,8 +14,13 @@ logger = getLogger(__name__)
 class GSearch(object):
 
     def __init__(self, kw):
+        assert('type' in kw)
+        assert('terms' in kw)
+
         self.type = kw['type']
         self.terms = kw['terms']
+        assert(is_str(self.type))
+
         if self.type == "gene":
             sql = """
                 SELECT
diff --git a/wqflask/wqflask/marker_regression/marker_regression.py b/wqflask/wqflask/marker_regression/marker_regression.py
index db4f8f46..3ec61e55 100644
--- a/wqflask/wqflask/marker_regression/marker_regression.py
+++ b/wqflask/wqflask/marker_regression/marker_regression.py
@@ -114,7 +114,7 @@ class MarkerRegression(object):
         self.num_perm = 0
         self.perm_output = []
         self.bootstrap_results = []
-        self.covariates = start_vars['covariates']
+        self.covariates = start_vars['covariates'] if "covariates" in start_vars else None
 
         #ZS: This is passed to GN1 code for single chr mapping
         self.selected_chr = -1
diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py
index 59e100d8..53c96591 100644
--- a/wqflask/wqflask/search_results.py
+++ b/wqflask/wqflask/search_results.py
@@ -10,6 +10,7 @@ import time
 import math
 import datetime
 import collections
+import re
 
 from pprint import pformat as pf
 
@@ -25,6 +26,7 @@ from db import webqtlDatabaseFunction
 from flask import render_template
 
 from utility import formatting
+from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
 
 from utility.logger import getLogger
 logger = getLogger(__name__ )
@@ -64,14 +66,26 @@ views.py).
         else:
             self.and_or = "and"
             self.search_terms = kw['search_terms_and']
-        self.search_term_exists = True
+        search = self.search_terms
+        # check for dodgy search terms
+        rx = re.compile(r'.*\W(href|http|sql|select|update)\W.*',re.IGNORECASE)
+        if rx.match(search):
+            logger.info("Regex failed search")
+            self.search_term_exists = False
+            return
+        else:
+            self.search_term_exists = True
+
         self.results = []
-        if kw['type'] == "Phenotypes":     # split datatype on type field
+        type = kw.get('type')
+        if type == "Phenotypes":     # split datatype on type field
             dataset_type = "Publish"
-        elif kw['type'] == "Genotypes":
+        elif type == "Genotypes":
             dataset_type = "Geno"
         else:
             dataset_type = "ProbeSet"      # ProbeSet is default
+
+        assert(is_str(kw.get('dataset')))
         self.dataset = create_dataset(kw['dataset'], dataset_type)
         logger.debug("search_terms:", self.search_terms)
         self.search()
@@ -145,6 +159,7 @@ statement and executes
                         else:
                             combined_where_clause += "OR"
                 else:
+                    logger.debug("Search failed 1")
                     self.search_term_exists = False
             if self.search_term_exists:
                 combined_where_clause = "(" + combined_where_clause + ")"
@@ -155,6 +170,7 @@ statement and executes
         else:
             logger.debug("len(search_terms)<=1")
             if self.search_terms == []:
+                logger.debug("Search failed 2")
                 self.search_term_exists = False
             else:
                 for a_search in self.search_terms:
@@ -162,6 +178,7 @@ statement and executes
                     if the_search != None:
                         self.results.extend(the_search.run())
                     else:
+                        logger.debug("Search failed 3")
                         self.search_term_exists = False
 
         if self.search_term_exists:
diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html
index b9f49a61..949760b6 100644
--- a/wqflask/wqflask/templates/new_security/login_user.html
+++ b/wqflask/wqflask/templates/new_security/login_user.html
@@ -15,15 +15,39 @@
 
             <h4>Don't have an account?</h4>
 
-
-            <a href="/n/register" class="btn btn-primary modalize">Create a new account</a>
-
-
-            <hr />
+            {% if es_server: %}
+	    <a href="/n/register" class="btn btn-primary modalize">Create a new account</a>
+	    {% else: %}
+	    <div class="alert alert-warning">
+	      <p>You cannot create an account at this moment.<br />
+		Please try again later.</p>
+	    </div>
+	    {% endif %}
+
+	    <hr />
+	    <h4>Login with external services</h4>
+
+	    {% if external_login: %}
+	    <div>
+	      {% if external_login["github"]: %}
+	      <a href="{{external_login['github']}}" title="Login with GitHub" class="btn btn-info btn-group">Login with Github</a>
+	      {% endif %}
+
+	      {% if external_login["orcid"]: %}
+	      <a href="{{external_login['orcid']}}" title="Login with ORCID" class="btn btn-info btn-group">Login with ORCID</a>
+	      {% endif %}
+	    </div>
+	    {% else: %}
+	    <div class="alert alert-warning">
+	      <p>You cannot login with external services at this time.<br />
+		Please try again later.</p>
+	    </div>
+	    {% endif %}
+	    <hr />
 
             <h4>Already have an account? Sign in here.</h4>
 
-
+	    {% if es_server: %}
             <form class="form-horizontal" action="/n/login" method="POST" name="login_user_form" id="loginUserForm">
                  <fieldset>
                     <div class="form-group">
@@ -61,6 +85,19 @@
               </fieldset>
 
             </form>
+	    {% else: %}
+	    <div class="alert alert-warning">
+	      <p>You cannot login at this moment using your GeneNetwork account (the authentication service is down).<br />
+		Please try again later.</p>
+	    </div>
+	    {% endif %}
+            {% if not es_server and not external_login: %}
+            <hr>
+	    <div class="alert alert-warning">
+               Note: it is safe to use GeneNetwork without a login. Login is only required for keeping track of
+            collections and getting access to some types of restricted data.
+            </div>
+            {% endif %}
         </div>
     </div>
 
diff --git a/wqflask/wqflask/user_manager.py b/wqflask/wqflask/user_manager.py
index f7fcd2d0..c55649f3 100644
--- a/wqflask/wqflask/user_manager.py
+++ b/wqflask/wqflask/user_manager.py
@@ -54,6 +54,9 @@ logger = getLogger(__name__)
 
 from base.data_set import create_datasets_list
 
+import requests
+from utility.elasticsearch_tools import get_elasticsearch_connection, get_user_by_unique_column, save_user
+
 THREE_DAYS = 60 * 60 * 24 * 3
 #THREE_DAYS = 45
 
@@ -119,7 +122,8 @@ class AnonUser(object):
             return collections
 
     def import_traits_to_user(self):
-        collections_list = json.loads(Redis.get(self.key))
+        result = Redis.get(self.key)
+        collections_list = json.loads(result if result else "[]")
         for collection in collections_list:
             uc = model.UserCollection()
             uc.name = collection['name']
@@ -268,16 +272,24 @@ class RegisterUser(object):
         self.thank_you_mode = False
         self.errors = []
         self.user = Bunch()
+        es = kw.get('es_connection', None)
+
+        if not es:
+            self.errors.append("Missing connection object")
 
-        self.user.email_address = kw.get('email_address', '').strip()
+        self.user.email_address = kw.get('email_address', '').encode("utf-8").strip()
         if not (5 <= len(self.user.email_address) <= 50):
             self.errors.append('Email Address needs to be between 5 and 50 characters.')
+        else:
+            email_exists = get_user_by_unique_column(es, "email_address", self.user.email_address)
+            if email_exists:
+                self.errors.append('User already exists with that email')
 
-        self.user.full_name = kw.get('full_name', '').strip()
+        self.user.full_name = kw.get('full_name', '').encode("utf-8").strip()
         if not (5 <= len(self.user.full_name) <= 50):
             self.errors.append('Full Name needs to be between 5 and 50 characters.')
 
-        self.user.organization = kw.get('organization', '').strip()
+        self.user.organization = kw.get('organization', '').encode("utf-8").strip()
         if self.user.organization and not (5 <= len(self.user.organization) <= 50):
             self.errors.append('Organization needs to be empty or between 5 and 50 characters.')
 
@@ -294,28 +306,11 @@ class RegisterUser(object):
         logger.debug("No errors!")
 
         set_password(password, self.user)
+        self.user.user_id = str(uuid.uuid4())
+        self.user.confirmed = 1
 
         self.user.registration_info = json.dumps(basic_info(), sort_keys=True)
-
-        self.new_user = model.User(**self.user.__dict__)
-        db_session.add(self.new_user)
-
-        try:
-            db_session.commit()
-        except sqlalchemy.exc.IntegrityError:
-            # This exception is thrown if the email address is already in the database
-            # To do: Perhaps put a link to sign in using an existing account here
-            self.errors.append("An account with this email address already exists. "
-                               "Click the button above to sign in using an existing account.")
-            return
-
-        logger.debug("Adding verification email to queue")
-        #self.send_email_verification()
-        VerificationEmail(self.new_user)
-        logger.debug("Added verification email to queue")
-
-        self.thank_you_mode = True
-
+        save_user(es, self.user.__dict__, self.user.user_id)
 
 def set_password(password, user):
     pwfields = Bunch()
@@ -361,7 +356,7 @@ class VerificationEmail(object):
         verification_code = str(uuid.uuid4())
         key = self.key_prefix + ":" + verification_code
 
-        data = json.dumps(dict(id=user.id,
+        data = json.dumps(dict(id=user.user_id,
                                timestamp=timestamp())
                           )
 
@@ -378,23 +373,33 @@ class ForgotPasswordEmail(VerificationEmail):
     template_name = "email/forgot_password.txt"
     key_prefix = "forgot_password_code"
     subject = "GeneNetwork password reset"
+    fromaddr = "no-reply@genenetwork.org"
 
-    def __init__(self, user):
+    def __init__(self, toaddr):
+        from email.MIMEMultipart import MIMEMultipart
+        from email.MIMEText import MIMEText
         verification_code = str(uuid.uuid4())
         key = self.key_prefix + ":" + verification_code
 
-        data = json.dumps(dict(id=user.id,
-                               timestamp=timestamp())
-                          )
+        data = {
+            "verification_code": verification_code,
+            "email_address": toaddr,
+            "timestamp": timestamp()
+        }
+        es_save_data(es, self.key_prefix, "local", data, verification_code)
 
-        Redis.set(key, data)
-        #two_days = 60 * 60 * 24 * 2
-        Redis.expire(key, THREE_DAYS)
-        to = user.email_address
         subject = self.subject
-        body = render_template(self.template_name,
-                               verification_code = verification_code)
-        send_email(to, subject, body)
+        body = render_template(
+            self.template_name,
+            verification_code = verification_code)
+
+        msg = MIMEMultipart()
+        msg["To"] = toaddr
+        msg["Subject"] = self.subject
+        msg["From"] = self.fromaddr
+        msg.attach(MIMEText(body, "plain"))
+
+        send_email(toaddr, msg.as_string())
 
 
 class Password(object):
@@ -429,38 +434,61 @@ def verify_email():
     response.set_cookie(UserSession.cookie_name, session_id_signed)
     return response
 
-@app.route("/n/password_reset")
+@app.route("/n/password_reset", methods=['GET'])
 def password_reset():
     logger.debug("in password_reset request.url is:", request.url)
 
     # We do this mainly just to assert that it's in proper form for displaying next page
     # Really not necessary but doesn't hurt
-    user_encode = DecodeUser(ForgotPasswordEmail.key_prefix).reencode_standalone()
-
-    return render_template("new_security/password_reset.html", user_encode=user_encode)
+    # user_encode = DecodeUser(ForgotPasswordEmail.key_prefix).reencode_standalone()
+    verification_code = request.args.get('code')
+    hmac = request.args.get('hm')
+    if verification_code:
+        code_details = get_item_by_unique_column(
+            es
+            , "verification_code"
+            , verification_code
+            , ForgotPasswordEmail.key_prefix
+            , "local")
+        if code_details:
+            user_details = get_user_by_unique_column(
+                es
+                , "email_address"
+                , code_details["email_address"])
+            if user_details:
+                return render_template(
+                    "new_security/password_reset.html", user_encode=user_details["user_id"])
+            else:
+                flash("Invalid code: User no longer exists!", "error")
+        else:
+            flash("Invalid code: Password reset code does not exist or might have expired!", "error")
+        return redirect(url_for("login"))#render_template("new_security/login_user.html", error=error)
 
 @app.route("/n/password_reset_step2", methods=('POST',))
 def password_reset_step2():
+    from utility.elasticsearch_tools import es
     logger.debug("in password_reset request.url is:", request.url)
 
     errors = []
+    user_id = request.form['user_encode']
 
-    user_encode = request.form['user_encode']
-    verification_code, separator, hmac = user_encode.partition(':')
-
-    hmac_verified = actual_hmac_creation(verification_code)
     logger.debug("locals are:", locals())
 
 
-    assert hmac == hmac_verified, "Someone has been naughty"
-
-    user = DecodeUser.actual_get_user(ForgotPasswordEmail.key_prefix, verification_code)
-    logger.debug("user is:", user)
-
+    user = Bunch()
     password = request.form['password']
-
     set_password(password, user)
-    db_session.commit()
+
+    es = get_elasticsearch_connection()
+    es.update(
+        index = "users"
+        , doc_type = "local"
+        , id = user_id
+        , body = {
+            "doc": {
+                "password": user.__dict__.get("password")
+            }
+        })
 
     flash("Password changed successfully. You can now sign in.", "alert-info")
     response = make_response(redirect(url_for('login')))
@@ -492,8 +520,85 @@ class DecodeUser(object):
 @app.route("/n/login", methods=('GET', 'POST'))
 def login():
     lu = LoginUser()
-    return lu.standard_login()
+    login_type = request.args.get("type")
+    if login_type:
+        uid = request.args.get("uid")
+        return lu.oauth2_login(login_type, uid)
+    else:
+        return lu.standard_login()
+
+@app.route("/n/login/github_oauth2", methods=('GET', 'POST'))
+def github_oauth2():
+    from utility.tools import GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
+    code = request.args.get("code")
+    data = {
+        "client_id": GITHUB_CLIENT_ID,
+        "client_secret": GITHUB_CLIENT_SECRET,
+        "code": code
+    }
+    result = requests.post("https://github.com/login/oauth/access_token", json=data)
+    result_dict = {arr[0]:arr[1] for arr in [tok.split("=") for tok in [token.encode("utf-8") for token in result.text.split("&")]]}
+
+    github_user = get_github_user_details(result_dict["access_token"])
+    es = get_elasticsearch_connection()
+    user_details = get_user_by_unique_column(es, "github_id", github_user["id"])
+    if user_details == None:
+        user_details = {
+            "user_id": str(uuid.uuid4())
+            , "name": github_user["name"].encode("utf-8")
+            , "github_id": github_user["id"]
+            , "user_url": github_user["html_url"].encode("utf-8")
+            , "login_type": "github"
+            , "organization": ""
+            , "active": 1
+            , "confirmed": 1
+        }
+        save_user(es, user_details, user_details["user_id"])
+    url = "/n/login?type=github&uid="+user_details["user_id"]
+    return redirect(url)
+
+@app.route("/n/login/orcid_oauth2", methods=('GET', 'POST'))
+def orcid_oauth2():
+    from uuid import uuid4
+    from utility.tools import ORCID_CLIENT_ID, ORCID_CLIENT_SECRET, ORCID_TOKEN_URL, ORCID_AUTH_URL
+    code = request.args.get("code")
+    error = request.args.get("error")
+    url = "/n/login"
+    if code:
+        data = {
+            "client_id": ORCID_CLIENT_ID
+            , "client_secret": ORCID_CLIENT_SECRET
+            , "grant_type": "authorization_code"
+            , "code": code
+        }
+        result = requests.post(ORCID_TOKEN_URL, data=data)
+        result_dict = json.loads(result.text.encode("utf-8"))
+
+        es = get_elasticsearch_connection()
+        user_details = get_user_by_unique_column(es, "orcid", result_dict["orcid"])
+        if user_details == None:
+            user_details = {
+                "user_id": str(uuid4())
+                , "name": result_dict["name"]
+                , "orcid": result_dict["orcid"]
+                , "user_url": "%s/%s" % (
+                    "/".join(ORCID_AUTH_URL.split("/")[:-2]),
+                    result_dict["orcid"])
+                , "login_type": "orcid"
+                , "organization": ""
+                , "active": 1
+                , "confirmed": 1
+            }
+            save_user(es, user_details, user_details["user_id"])
+        url = "/n/login?type=orcid&uid="+user_details["user_id"]
+    else:
+        flash("There was an error getting code from ORCID")
+    return redirect(url)
 
+def get_github_user_details(access_token):
+    from utility.tools import GITHUB_API_URL
+    result = requests.get(GITHUB_API_URL, params={"access_token":access_token})
+    return result.json()
 
 class LoginUser(object):
     remember_time = 60 * 60 * 24 * 30 # One month in seconds
@@ -501,27 +606,55 @@ class LoginUser(object):
     def __init__(self):
         self.remember_me = False
 
+    def oauth2_login(self, login_type, user_id):
+        """Login via an OAuth2 provider"""
+        es = get_elasticsearch_connection()
+        user_details = get_user_by_unique_column(es, "user_id", user_id)
+        if user_details:
+            user = model.User()
+            user.id = user_details["user_id"]
+            user.full_name = user_details["name"]
+            user.login_type = user_details["login_type"]
+            return self.actual_login(user)
+        else:
+            flash("Error logging in via OAuth2")
+            return make_response(redirect(url_for('login')))
+
     def standard_login(self):
         """Login through the normal form"""
         params = request.form if request.form else request.args
         logger.debug("in login params are:", params)
+        es = get_elasticsearch_connection()
         if not params:
-            return render_template("new_security/login_user.html")
+            from utility.tools import GITHUB_AUTH_URL, GITHUB_CLIENT_ID, ORCID_AUTH_URL, ORCID_CLIENT_ID
+            external_login = {}
+            if GITHUB_AUTH_URL and GITHUB_CLIENT_ID != 'UNKNOWN':
+                external_login["github"] = GITHUB_AUTH_URL
+            if ORCID_AUTH_URL and ORCID_CLIENT_ID != 'UNKNOWN':
+                external_login["orcid"] = ORCID_AUTH_URL
+            assert(es is not None)
+            return render_template(
+                "new_security/login_user.html"
+                , external_login=external_login
+                , es_server=es.ping())
         else:
-            try:
-                user = model.User.query.filter_by(email_address=params['email_address']).one()
-            except sqlalchemy.orm.exc.NoResultFound:
-                logger.debug("No account exists for that email address")
-                valid = False
-                user = None
-            else:
+            user_details = get_user_by_unique_column(es, "email_address", params["email_address"])
+            user = None
+            valid = None
+            if user_details:
+                user = model.User();
+                for key in user_details:
+                    user.__dict__[key] = user_details[key]
+                valid = False;
+
                 submitted_password = params['password']
                 pwfields = Struct(json.loads(user.password))
-                encrypted = Password(submitted_password,
-                                              pwfields.salt,
-                                              pwfields.iterations,
-                                              pwfields.keylength,
-                                              pwfields.hashfunc)
+                encrypted = Password(
+                    submitted_password,
+                    pwfields.salt,
+                    pwfields.iterations,
+                    pwfields.keylength,
+                    pwfields.hashfunc)
                 logger.debug("\n\nComparing:\n{}\n{}\n".format(encrypted.password, pwfields.password))
                 valid = pbkdf2.safe_str_cmp(encrypted.password, pwfields.password)
                 logger.debug("valid is:", valid)
@@ -549,7 +682,7 @@ class LoginUser(object):
         else:
             if user:
                 self.unsuccessful_login(user)
-            flash("Invalid email-address or password. Please try again.", "alert-error")
+            flash("Invalid email-address or password. Please try again.", "alert-danger")
             response = make_response(redirect(url_for('login')))
 
             return response
@@ -558,7 +691,6 @@ class LoginUser(object):
         """The meat of the logging in process"""
         session_id_signed = self.successful_login(user, assumed_by)
         flash("Thank you for logging in {}.".format(user.full_name), "alert-success")
-        print("IMPORT1:", import_collections)
         response = make_response(redirect(url_for('index_page', import_collections=import_collections)))
         if self.remember_me:
             max_age = self.remember_time
@@ -589,8 +721,6 @@ class LoginUser(object):
         else:
             expire_time = THREE_DAYS
         Redis.expire(key, expire_time)
-        db_session.add(login_rec)
-        db_session.commit()
         return session_id_signed
 
     def unsuccessful_login(self, user):
@@ -618,13 +748,16 @@ def forgot_password():
 def forgot_password_submit():
     params = request.form
     email_address = params['email_address']
-    try:
-        user = model.User.query.filter_by(email_address=email_address).one()
-    except orm.exc.NoResultFound:
-        flash("Couldn't find a user associated with the email address {}. Sorry.".format(
-            email_address))
-        return redirect(url_for("login"))
-    ForgotPasswordEmail(user)
+    user_details = get_user_by_unique_column(es, "email_address", email_address)
+    if user_details:
+        ForgotPasswordEmail(user_details["email_address"])
+    # try:
+    #     user = model.User.query.filter_by(email_address=email_address).one()
+    # except orm.exc.NoResultFound:
+    #     flash("Couldn't find a user associated with the email address {}. Sorry.".format(
+    #         email_address))
+    #     return redirect(url_for("login"))
+    # ForgotPasswordEmail(user)
     return render_template("new_security/forgot_password_step2.html",
                             subject=ForgotPasswordEmail.subject)
 
@@ -691,16 +824,18 @@ def register():
 
 
     params = request.form if request.form else request.args
+    params = params.to_dict(flat=True)
+    es = get_elasticsearch_connection()
+    params["es_connection"] = es
 
     if params:
         logger.debug("Attempting to register the user...")
         result = RegisterUser(params)
         errors = result.errors
 
-        if result.thank_you_mode:
-            assert not errors, "Errors while in thank you mode? That seems wrong..."
-            return render_template("new_security/registered.html",
-                                   subject=VerificationEmail.subject)
+        if len(errors) == 0:
+            flash("Registration successful. You may login with your new account", "alert-info")
+            return redirect(url_for("login"))
 
     return render_template("new_security/register_user.html", values=params, errors=errors)
 
@@ -771,13 +906,21 @@ app.jinja_env.globals.update(url_for_hmac=url_for_hmac,
 
 #######################################################################################
 
-def send_email(to, subject, body):
-    msg = json.dumps(dict(From="no-reply@genenetwork.org",
-                     To=to,
-                     Subject=subject,
-                     Body=body))
-    Redis.rpush("mail_queue", msg)
-
+# def send_email(to, subject, body):
+#     msg = json.dumps(dict(From="no-reply@genenetwork.org",
+#                      To=to,
+#                      Subject=subject,
+#                      Body=body))
+#     Redis.rpush("mail_queue", msg)
+
+def send_email(toaddr, msg, fromaddr="no-reply@genenetwork.org"):
+    from smtplib import SMTP
+    from utility.tools import SMTP_CONNECT, SMTP_USERNAME, SMTP_PASSWORD
+    server = SMTP(SMTP_CONNECT)
+    server.starttls()
+    server.login(SMTP_USERNAME, SMTP_PASSWORD)
+    server.sendmail(fromaddr, toaddr, msg)
+    server.quit()
 
 
 
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 998e5302..a65924d8 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -123,7 +123,7 @@ def handle_bad_request(e):
 @app.route("/")
 def index_page():
     logger.info("Sending index_page")
-    logger.debug(request.url)
+    logger.info(request.url)
     params = request.args
     if 'import_collections' in params:
         import_collections = params['import_collections']
@@ -141,7 +141,7 @@ def index_page():
 def tmp_page(img_path):
     logger.info("In tmp_page")
     logger.info("img_path:", img_path)
-    logger.debug(request.url)
+    logger.info(request.url)
     initial_start_vars = request.form
     logger.info("initial_start_vars:", initial_start_vars)
     imgfile = open(GENERATED_IMAGE_DIR + img_path, 'rb')
@@ -174,7 +174,7 @@ def twitter(filename):
 @app.route("/search", methods=('GET',))
 def search_page():
     logger.info("in search_page")
-    logger.debug(request.url)
+    logger.info(request.url)
     if 'info_database' in request.args:
         logger.info("Going to sharing_info_page")
         template_vars = sharing_info_page()
@@ -199,21 +199,22 @@ def search_page():
         logger.info("request.args is", request.args)
         the_search = search_results.SearchResultPage(request.args)
         result = the_search.__dict__
+        valid_search = result['search_term_exists']
 
         logger.debugf("result", result)
 
-        if USE_REDIS:
+        if USE_REDIS and valid_search:
             Redis.set(key, pickle.dumps(result, pickle.HIGHEST_PROTOCOL))
             Redis.expire(key, 60*60)
 
-        if result['search_term_exists']:
+        if valid_search:
             return render_template("search_result_page.html", **result)
         else:
             return render_template("search_error.html")
 
 @app.route("/gsearch", methods=('GET',))
 def gsearchact():
-    logger.debug(request.url)
+    logger.info(request.url)
     result = gsearch.GSearch(request.args).__dict__
     type = request.args['type']
     if type == "gene":
@@ -224,7 +225,7 @@ def gsearchact():
 @app.route("/gsearch_updating", methods=('POST',))
 def gsearch_updating():
     logger.info("REQUEST ARGS:", request.values)
-    logger.debug(request.url)
+    logger.info(request.url)
     result = update_search_results.GSearch(request.args).__dict__
     return result['results']
     # type = request.args['type']
@@ -235,31 +236,31 @@ def gsearch_updating():
 
 @app.route("/docedit")
 def docedit():
-    logger.debug(request.url)
+    logger.info(request.url)
     doc = docs.Docs(request.args['entry'])
     return render_template("docedit.html", **doc.__dict__)
 
 @app.route('/generated/<filename>')
 def generated_file(filename):
-    logger.debug(request.url)
+    logger.info(request.url)
     return send_from_directory(GENERATED_IMAGE_DIR,filename)
 
 @app.route("/help")
 def help():
-    logger.debug(request.url)
+    logger.info(request.url)
     doc = docs.Docs("help")
     return render_template("docs.html", **doc.__dict__)
 
 @app.route("/wgcna_setup", methods=('POST',))
 def wcgna_setup():
     logger.info("In wgcna, request.form is:", request.form)             # We are going to get additional user input for the analysis
-    logger.debug(request.url)
+    logger.info(request.url)
     return render_template("wgcna_setup.html", **request.form)          # Display them using the template
 
 @app.route("/wgcna_results", methods=('POST',))
 def wcgna_results():
     logger.info("In wgcna, request.form is:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     wgcna = wgcna_analysis.WGCNA()                                # Start R, load the package and pointers and create the analysis
     wgcnaA = wgcna.run_analysis(request.form)                     # Start the analysis, a wgcnaA object should be a separate long running thread
     result = wgcna.process_results(wgcnaA)                        # After the analysis is finished store the result
@@ -268,13 +269,13 @@ def wcgna_results():
 @app.route("/ctl_setup", methods=('POST',))
 def ctl_setup():
     logger.info("In ctl, request.form is:", request.form)             # We are going to get additional user input for the analysis
-    logger.debug(request.url)
+    logger.info(request.url)
     return render_template("ctl_setup.html", **request.form)          # Display them using the template
 
 @app.route("/ctl_results", methods=('POST',))
 def ctl_results():
     logger.info("In ctl, request.form is:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     ctl = ctl_analysis.CTL()                                  # Start R, load the package and pointers and create the analysis
     ctlA = ctl.run_analysis(request.form)                     # Start the analysis, a ctlA object should be a separate long running thread
     result = ctl.process_results(ctlA)                        # After the analysis is finished store the result
@@ -313,13 +314,13 @@ def environments():
 
 @app.route("/submit_trait")
 def submit_trait_form():
-    logger.debug(request.url)
+    logger.info(request.url)
     species_and_groups = get_species_groups()
     return render_template("submit_trait.html", **{'species_and_groups' : species_and_groups, 'gn_server_url' : GN_SERVER_URL, 'version' : GN_VERSION})
 
 @app.route("/create_temp_trait", methods=('POST',))
 def create_temp_trait():
-    logger.debug(request.url)
+    logger.info(request.url)
     print("REQUEST.FORM:", request.form)
     #template_vars = submit_trait.SubmitTrait(request.form)
 
@@ -332,7 +333,7 @@ def export_trait_excel():
     """Excel file consisting of the sample data from the trait data and analysis page"""
     logger.info("In export_trait_excel")
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     sample_data = export_trait_data.export_sample_table(request.form)
 
     logger.info("sample_data - type: %s -- size: %s" % (type(sample_data), len(sample_data)))
@@ -358,7 +359,7 @@ def export_trait_csv():
     """CSV file consisting of the sample data from the trait data and analysis page"""
     logger.info("In export_trait_csv")
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     sample_data = export_trait_data.export_sample_table(request.form)
 
     logger.info("sample_data - type: %s -- size: %s" % (type(sample_data), len(sample_data)))
@@ -379,7 +380,7 @@ def export_traits_csv():
     """CSV file consisting of the traits from the search result page"""
     logger.info("In export_traits_csv")
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     csv_data = export_traits.export_search_results_csv(request.form)
 
     return Response(csv_data,
@@ -389,7 +390,7 @@ def export_traits_csv():
 @app.route('/export_perm_data', methods=('POST',))
 def export_perm_data():
     """CSV file consisting of the permutation data for the mapping results"""
-    logger.debug(request.url)
+    logger.info(request.url)
     num_perm = float(request.form['num_perm'])
     perm_data = json.loads(request.form['perm_results'])
 
@@ -412,7 +413,7 @@ def export_perm_data():
 
 @app.route("/show_temp_trait", methods=('POST',))
 def show_temp_trait_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = show_trait.ShowTrait(request.form)
     #logger.info("js_data before dump:", template_vars.js_data)
     template_vars.js_data = json.dumps(template_vars.js_data,
@@ -427,7 +428,7 @@ def show_temp_trait_page():
 
 @app.route("/show_trait")
 def show_trait_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = show_trait.ShowTrait(request.args)
     #logger.info("js_data before dump:", template_vars.js_data)
     template_vars.js_data = json.dumps(template_vars.js_data,
@@ -443,7 +444,7 @@ def show_trait_page():
 @app.route("/heatmap", methods=('POST',))
 def heatmap_page():
     logger.info("In heatmap, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
 
     start_vars = request.form
     temp_uuid = uuid.uuid4()
@@ -493,7 +494,7 @@ def mapping_results_container_page():
 
 @app.route("/loading", methods=('POST',))
 def loading_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     initial_start_vars = request.form
     logger.debug("Marker regression called with initial_start_vars:", initial_start_vars.items())
     #temp_uuid = initial_start_vars['temp_uuid']
@@ -552,7 +553,7 @@ def loading_page():
 def marker_regression_page():
     initial_start_vars = request.form
     logger.debug("Marker regression called with initial_start_vars:", initial_start_vars.items())
-    logger.debug(request.url)
+    logger.info(request.url)
     temp_uuid = initial_start_vars['temp_uuid']
     wanted = (
         'trait_id',
@@ -678,7 +679,7 @@ def marker_regression_page():
 @app.route("/export_mapping_results", methods = ('POST',))
 def export_mapping_results():
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     file_path = request.form.get("results_path")
     results_csv = open(file_path, "r").read()
     response = Response(results_csv,
@@ -691,7 +692,7 @@ def export_mapping_results():
 @app.route("/export", methods = ('POST',))
 def export():
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     svg_xml = request.form.get("data", "Invalid data")
     filename = request.form.get("filename", "manhattan_plot_snp")
     response = Response(svg_xml, mimetype="image/svg+xml")
@@ -702,7 +703,7 @@ def export():
 def export_pdf():
     import cairosvg
     logger.info("request.form:", request.form)
-    logger.debug(request.url)
+    logger.info(request.url)
     svg_xml = request.form.get("data", "Invalid data")
     logger.info("svg_xml:", svg_xml)
     filename = request.form.get("filename", "interval_map_pdf")
@@ -715,7 +716,7 @@ def export_pdf():
 @app.route("/network_graph", methods=('POST',))
 def network_graph_page():
     logger.info("In network_graph, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
     start_vars = request.form
     traits = [trait.strip() for trait in start_vars['trait_list'].split(',')]
     if traits[0] != "":
@@ -731,7 +732,7 @@ def network_graph_page():
 @app.route("/corr_compute", methods=('POST',))
 def corr_compute_page():
     logger.info("In corr_compute, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
     #fd = webqtlFormData.webqtlFormData(request.form)
     template_vars = show_corr_results.CorrelationResults(request.form)
     return render_template("correlation_page.html", **template_vars.__dict__)
@@ -739,7 +740,7 @@ def corr_compute_page():
 @app.route("/corr_matrix", methods=('POST',))
 def corr_matrix_page():
     logger.info("In corr_matrix, request.form is:", pf(request.form))
-    logger.debug(request.url)
+    logger.info(request.url)
 
     start_vars = request.form
     traits = [trait.strip() for trait in start_vars['trait_list'].split(',')]
@@ -755,7 +756,7 @@ def corr_matrix_page():
 
 @app.route("/corr_scatter_plot")
 def corr_scatter_plot_page():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = corr_scatter_plot.CorrScatterPlot(request.args)
     template_vars.js_data = json.dumps(template_vars.js_data,
                                        default=json_default_handler,
@@ -764,7 +765,7 @@ def corr_scatter_plot_page():
 
 @app.route("/submit_bnw", methods=('POST',))
 def submit_bnw():
-    logger.debug(request.url)
+    logger.info(request.url)
     template_vars = get_bnw_input(request.form)
     return render_template("empty_collection.html", **{'tool':'Correlation Matrix'})
 
@@ -772,7 +773,7 @@ def submit_bnw():
 def sharing_info_page():
     """Info page displayed when the user clicks the "Info" button next to the dataset selection"""
     logger.info("In sharing_info_page")
-    logger.debug(request.url)
+    logger.info(request.url)
     fd = webqtlFormData.webqtlFormData(request.args)
     template_vars = SharingInfoPage.SharingInfoPage(fd)
     return template_vars
@@ -780,7 +781,7 @@ def sharing_info_page():
 # Take this out or secure it before putting into production
 @app.route("/get_temp_data")
 def get_temp_data():
-    logger.debug(request.url)
+    logger.info(request.url)
     temp_uuid = request.args['key']
     return flask.jsonify(temp_data.TempData(temp_uuid).get_all())