aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzsloan2021-10-12 18:25:08 +0000
committerzsloan2021-10-12 18:25:08 +0000
commit0fa75b3d31a96a109d06306a1b41956ec06eae08 (patch)
tree328fa1587a7229f1acf184ec8fbbc7b2438f2d2e
parent6c9cfe7bd0bdedce2ea13cdc873e998a27295e20 (diff)
parenta212ad123f902b6a9c74bcac1d98bc274cebbdda (diff)
downloadgenenetwork2-0fa75b3d31a96a109d06306a1b41956ec06eae08.tar.gz
Merge branch 'testing' of github.com:genenetwork/genenetwork2 into feature/add_rqtl_pairscan
-rw-r--r--.github/workflows/main.yml16
-rw-r--r--doc/docker-container.org7
-rw-r--r--scripts/authentication/group.py153
-rw-r--r--scripts/authentication/resource.py104
-rw-r--r--scripts/convert_dol_genotypes.py74
-rw-r--r--wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py10
-rw-r--r--wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py43
-rw-r--r--wqflask/utility/redis_tools.py20
-rw-r--r--wqflask/wqflask/correlation/correlation_gn3_api.py5
-rw-r--r--wqflask/wqflask/decorators.py28
-rw-r--r--wqflask/wqflask/marker_regression/display_mapping_results.py81
-rw-r--r--wqflask/wqflask/marker_regression/qtlreaper_mapping.py95
-rw-r--r--wqflask/wqflask/marker_regression/rqtl_mapping.py7
-rw-r--r--wqflask/wqflask/marker_regression/run_mapping.py88
-rw-r--r--wqflask/wqflask/resource_manager.py2
-rw-r--r--wqflask/wqflask/show_trait/show_trait.py10
-rw-r--r--wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js2
-rw-r--r--wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js17
-rw-r--r--wqflask/wqflask/templates/admin/group_manager.html2
-rw-r--r--wqflask/wqflask/templates/base.html1
-rw-r--r--wqflask/wqflask/templates/collections/view.html2
-rw-r--r--wqflask/wqflask/templates/loading.html25
-rw-r--r--wqflask/wqflask/templates/mapping_results.html12
-rw-r--r--wqflask/wqflask/templates/new_security/_scripts.html1
-rw-r--r--wqflask/wqflask/templates/new_security/forgot_password.html1
-rw-r--r--wqflask/wqflask/templates/new_security/forgot_password_step2.html1
-rw-r--r--wqflask/wqflask/templates/new_security/login_user.html26
-rw-r--r--wqflask/wqflask/templates/new_security/password_reset.html1
-rw-r--r--wqflask/wqflask/templates/new_security/register_user.html1
-rw-r--r--wqflask/wqflask/templates/new_security/registered.html1
-rw-r--r--wqflask/wqflask/templates/new_security/thank_you.html1
-rw-r--r--wqflask/wqflask/templates/new_security/verification_still_needed.html1
-rw-r--r--wqflask/wqflask/templates/search_result_page.html8
-rw-r--r--wqflask/wqflask/templates/show_trait.html2
-rw-r--r--wqflask/wqflask/templates/show_trait_details.html3
-rwxr-xr-xwqflask/wqflask/templates/show_trait_mapping_tools.html24
-rw-r--r--wqflask/wqflask/views.py53
37 files changed, 581 insertions, 347 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index f279a7e5..8e2c7966 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -55,11 +55,11 @@ jobs:
GENENETWORK_FILES=/genotype_files/ bin/genenetwork2 \
etc/default_settings.py -c -m unittest discover -v
- - name: Test for Broken Links
- run: |
- env GN2_PROFILE=/gn2-profile \
- TMPDIR=/tmp\
- WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG \
- GENENETWORK_FILES=/genotype_files/ bin/genenetwork2 \
- etc/default_settings.py -c \
- $PWD/test/requests/links_scraper/genelinks.py
+ # - name: Test for Broken Links
+ # run: |
+ # env GN2_PROFILE=/gn2-profile \
+ # TMPDIR=/tmp\
+ # WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG \
+ # GENENETWORK_FILES=/genotype_files/ bin/genenetwork2 \
+ # etc/default_settings.py -c \
+ # $PWD/test/requests/links_scraper/genelinks.py
diff --git a/doc/docker-container.org b/doc/docker-container.org
index ef0d71fc..79b8272f 100644
--- a/doc/docker-container.org
+++ b/doc/docker-container.org
@@ -28,13 +28,6 @@ which will be added to a base mariaDB image.
First create the gn2 tar archive by running:
#+begin_src sh
-# For the Python 2 version:
-env GUIX_PACKAGE_PATH="/home/bonface/projects/guix-bioinformatics:/home/bonface/projects/guix-past/modules" \
- ./pre-inst-env guix pack --no-grafts\
- -S /gn2-profile=/ \
- screen python2-genenetwork2
-
-# For the Python 3 version:
env GUIX_PACKAGE_PATH="/home/bonface/projects/guix-bioinformatics:/home/bonface/projects/guix-past/modules" \
./pre-inst-env guix pack --no-grafts\
-S /gn2-profile=/ \
diff --git a/scripts/authentication/group.py b/scripts/authentication/group.py
new file mode 100644
index 00000000..c8c2caad
--- /dev/null
+++ b/scripts/authentication/group.py
@@ -0,0 +1,153 @@
+"""A script for adding users to a specific group.
+
+Example:
+
+Assuming there are no groups and 'test@bonfacemunyoki.com' does not
+exist in Redis:
+
+.. code-block:: bash
+ python group.py -g "editors" -m "test@bonfacemunyoki.com"
+
+results in::
+
+ Successfully created the group: 'editors'
+ Data: '{"admins": [], "members": []}'
+
+If 'me@bonfacemunyoki.com' exists in 'users' in Redis and we run:
+
+.. code-block:: bash
+ python group.py -g "editors" -m "me@bonfacemunyoki.com"
+
+now results in::
+
+ No new group was created.
+ Updated Data: {'admins': [], 'members': ['me@bonfacemunyoki.com']}
+
+"""
+
+import argparse
+import datetime
+import redis
+import json
+import uuid
+
+from typing import Dict, Optional, Set
+
+
+def create_group_data(users: Dict, target_group: str,
+ members: Optional[str] = None,
+ admins: Optional[str] = None) -> Dict:
+ """Return a dictionary that contains the following keys: "key",
+ "field", and "value" that can be used in a redis hash as follows:
+ HSET key field value
+
+ The "field" return value is a unique-id that is used to
+ distinguish the groups.
+
+ Parameters:
+
+ - `users`: a list of users for example:
+
+ {'8ad942fe-490d-453e-bd37-56f252e41603':
+ '{"email_address": "me@test.com",
+ "full_name": "John Doe",
+ "organization": "Genenetwork",
+ "password": {"algorithm": "pbkdf2",
+ "hashfunc": "sha256",
+ "salt": "gJrd1HnPSSCmzB5veMPaVk2ozzDlS1Z7Ggcyl1+pciA=",
+ "iterations": 100000, "keylength": 32,
+ "created_timestamp": "2021-09-22T11:32:44.971912",
+ "password": "edcdaa60e84526c6"},
+ "user_id": "8ad942fe", "confirmed": 1,
+ "registration_info": {
+ "timestamp": "2021-09-22T11:32:45.028833",
+ "ip_address": "127.0.0.1",
+ "user_agent": "Mozilla/5.0"}}'}
+
+ - `target_group`: the group name that will be stored inside the
+ "groups" hash in Redis.
+
+ - `members`: a comma-separated list of values that contain members
+ of the `target_group` e.g. "me@test1.com, me@test2.com,
+ me@test3.com"
+
+ - `admins`: a comma-separated list of values that contain
+ administrators of the `target_group` e.g. "me@test1.com,
+ me@test2.com, me@test3.com"
+
+ """
+ # Emails
+ _members: Set = set("".join(members.split()).split(",")
+ if members else [])
+ _admins: Set = set("".join(admins.split()).split(",")
+ if admins else [])
+
+ # Unique IDs
+ member_ids: Set = set()
+ admin_ids: Set = set()
+
+ for user_id, user_details in users.items():
+ _details = json.loads(user_details)
+ if _details.get("email_address") in _members:
+ member_ids.add(user_id)
+ if _details.get("email_address") in _admins:
+ admin_ids.add(user_id)
+
+ timestamp: str = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+ return {"key": "groups",
+ "field": str(uuid.uuid4()),
+ "value": json.dumps({
+ "name": target_group,
+ "admins": list(admin_ids),
+ "members": list(member_ids),
+ "changed_timestamp": timestamp,
+ })}
+
+
+if __name__ == "__main__":
+ # Initialising the parser CLI arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-g", "--group-name",
+ help="This is the name of the GROUP mask")
+ parser.add_argument("-m", "--members",
+ help="Members of the GROUP mask")
+ parser.add_argument("-a", "--admins",
+ help="Admins of the GROUP mask")
+ args = parser.parse_args()
+
+ if not args.group_name:
+ exit("\nExiting. Please specify a group name to use!\n")
+
+ members = args.members if args.members else None
+ admins = args.admins if args.admins else None
+
+ REDIS_CONN = redis.Redis(decode_responses=True)
+ USERS = REDIS_CONN.hgetall("users")
+
+ if not any([members, admins]):
+ exit("\nExiting. Please provide a value for "
+ "MEMBERS(-m) or ADMINS(-a)!\n")
+
+ data = create_group_data(
+ users=USERS,
+ target_group=args.group_name,
+ members=members,
+ admins=admins)
+
+ if not REDIS_CONN.hget("groups", data.get("field")):
+ updated_data = json.loads(data["value"])
+ timestamp = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+ updated_data["created_timestamp"] = timestamp
+ data["value"] = json.dumps(updated_data)
+
+ created_p = REDIS_CONN.hset(data.get("key", ""),
+ data.get("field", ""),
+ data.get("value", ""))
+
+ groups = json.loads(REDIS_CONN.hget("groups",
+ data.get("field"))) # type: ignore
+ if created_p:
+ exit(f"\nSuccessfully created the group: '{args.group_name}'\n"
+ f"`HGETALL groups {args.group_name}`: {groups}\n")
+ exit("\nNo new group was created.\n"
+ f"`HGETALL groups {args.group_name}`: {groups}\n")
diff --git a/scripts/authentication/resource.py b/scripts/authentication/resource.py
new file mode 100644
index 00000000..4996f34c
--- /dev/null
+++ b/scripts/authentication/resource.py
@@ -0,0 +1,104 @@
+"""A script that:
+
+- Optionally restores data from a json file.
+
+- By default, without any args provided, adds the group: 'editors' to
+every resource. 'editors' should have the right to edit both metadata
+and data.
+
+- Optionally creates a back-up every time you edit a resource.
+
+
+To restore a back-up:
+
+.. code-block:: python
+ python resource.py --restore <PATH/TO/RESOURCE/BACK-UP/FILE>
+
+To add editors to every resource without creating a back-up:
+
+.. code-block:: python
+ python resource.py
+
+To add editors to every resource while creating a back-up before any
+destructive edits:
+
+.. code-block:: python
+ python resource.py --enable-backup
+
+"""
+import argparse
+import json
+import redis
+import os
+
+from datetime import datetime
+
+
+def recover_hash(name: str, file_path: str, set_function) -> bool:
+ """Recover back-ups using the `set_function`
+
+ Parameters:
+
+ - `name`: Redis hash where `file_path` will be restored
+
+ - `file_path`: File path where redis hash is sourced from
+
+ - `set_function`: Function used to do the Redis backup for
+ example: HSET
+
+ """
+ try:
+ with open(file_path, "r") as f:
+ resources = json.load(f)
+ for resource_id, resource in resources.items():
+ set_function(name=name,
+ key=resource_id,
+ value=resource)
+ return True
+ except Exception as e:
+ print(e)
+ return False
+
+
+if __name__ == "__main__":
+ # Initialising the parser CLI arguments
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--group-id",
+ help="Add the group id to all resources")
+ parser.add_argument("--restore",
+ help="Restore from a given backup")
+ parser.add_argument("--enable-backup", action="store_true",
+ help="Create a back up before edits")
+ args = parser.parse_args()
+
+ if not args.group_id:
+ exit("Please specify the group-id!\n")
+ if args.restore:
+ if recover_hash(name="resources",
+ file_path=args.back_up,
+ set_function=redis.Redis(decode_responses=True).hset):
+ exit(f"\n Done restoring {args.back_up}!\n")
+ else:
+ exit(f"\n There was an error restoring {args.back_up}!\n")
+
+ REDIS_CONN = redis.Redis(decode_responses=True)
+ RESOURCES = REDIS_CONN.hgetall("resources")
+ BACKUP_DIR = os.path.join(os.getenv("HOME"), "redis")
+ if args.enable_backup:
+ FILENAME = ("resources-"
+ f"{datetime.now().strftime('%Y-%m-%d-%I:%M:%S-%p')}"
+ ".json")
+ if not os.path.exists(BACKUP_DIR):
+ os.mkdir(BACKUP_DIR)
+ with open(os.path.join(BACKUP_DIR, FILENAME), "w") as f:
+ json.dump(RESOURCES, f, indent=4)
+ print(f"\nDone backing upto {FILENAME}")
+
+ for resource_id, resource in RESOURCES.items():
+ _resource = json.loads(resource) # str -> dict conversion
+ _resource["group_masks"] = {args.group_id: {"metadata": "edit",
+ "data": "edit"}}
+ REDIS_CONN.hset("resources",
+ resource_id,
+ json.dumps(_resource))
+ exit("Done updating `resources`\n")
diff --git a/scripts/convert_dol_genotypes.py b/scripts/convert_dol_genotypes.py
new file mode 100644
index 00000000..81b3bd6d
--- /dev/null
+++ b/scripts/convert_dol_genotypes.py
@@ -0,0 +1,74 @@
+# This is just to convert the Rqtl2 format genotype files for DOL into a .geno file
+# Everything is hard-coded since I doubt this will be re-used and I just wanted to generate the file quickly
+
+import os
+
+geno_dir = "/home/zas1024/gn2-zach/DO_genotypes/"
+markers_file = "/home/zas1024/gn2-zach/DO_genotypes/SNP_Map.txt"
+gn_geno_path = "/home/zas1024/gn2-zach/DO_genotypes/DOL.geno"
+
+# Iterate through the SNP_Map.txt file to get marker positions
+marker_data = {}
+with open(markers_file, "r") as markers_fh:
+ for i, line in enumerate(markers_fh):
+ if i == 0:
+ continue
+ else:
+ line_items = line.split("\t")
+ this_marker = {}
+ this_marker['chr'] = line_items[2] if line_items[2] != "0" else "M"
+ this_marker['pos'] = f'{float(line_items[3])/1000000:.6f}'
+ marker_data[line_items[1]] = this_marker
+
+# Iterate through R/qtl2 format genotype files and pull out the samplelist and genotypes for each marker
+sample_names = []
+for filename in os.listdir(geno_dir):
+ if "gm4qtl2_geno" in filename:
+ with open(geno_dir + "/" + filename, "r") as rqtl_geno_fh:
+ for i, line in enumerate(rqtl_geno_fh):
+ line_items = line.split(",")
+ if i < 3:
+ continue
+ elif not len(sample_names) and i == 3:
+ sample_names = [item.replace("TLB", "TB") for item in line_items[1:]]
+ elif i > 3:
+ marker_data[line_items[0]]['genotypes'] = ["X" if item.strip() == "-" else item.strip() for item in line_items[1:]]
+
+# Generate list of marker obs to iterate through when writing to .geno file
+marker_list = []
+for key, value in marker_data.items():
+ if 'genotypes' in value:
+ this_marker = {
+ 'chr': value['chr'],
+ 'locus': key,
+ 'pos': value['pos'],
+ 'genotypes': value['genotypes']
+ }
+ marker_list.append(this_marker)
+
+def sort_func(e):
+ """For ensuring that X/Y chromosomes/mitochondria are sorted to the end correctly"""
+ try:
+ return float((e['chr']))*1000 + float(e['pos'])
+ except:
+ if e['chr'] == "X":
+ return 20000 + float(e['pos'])
+ elif e['chr'] == "Y":
+ return 21000 + float(e['pos'])
+ elif e['chr'] == "M":
+ return 22000 + float(e['pos'])
+
+# Sort markers by chromosome
+marker_list.sort(key=sort_func)
+
+# Write lines to .geno file
+with open(gn_geno_path, "w") as gn_geno_fh:
+ gn_geno_fh.write("\t".join((["Chr", "Locus", "cM", "Mb"] + sample_names)))
+ for marker in marker_list:
+ row_contents = [
+ marker['chr'],
+ marker['locus'],
+ marker['pos'],
+ marker['pos']
+ ] + marker['genotypes']
+ gn_geno_fh.write("\t".join(row_contents) + "\n")
diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py
index 255bfd7f..1ec18199 100644
--- a/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py
+++ b/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py
@@ -81,10 +81,12 @@ class TestGemmaMapping(unittest.TestCase):
def test_gen_pheno_txt_file(self):
"""add tests for generating pheno txt file"""
with mock.patch("builtins.open", mock.mock_open())as mock_open:
- gen_pheno_txt_file(this_dataset={}, genofile_name="", vals=[
- "x", "w", "q", "we", "R"], trait_filename="fitr.re")
+ gen_pheno_txt_file(
+ this_dataset=AttributeSetter({"name": "A"}),
+ genofile_name="", vals=[
+ "x", "w", "q", "we", "R"])
mock_open.assert_called_once_with(
- '/home/user/data/gn2/fitr.re.txt', 'w')
+ '/home/user/data/gn2/PHENO_KiAEKlCvM6iGTM9Kh_TAlQ.txt', 'w')
filehandler = mock_open()
values = ["x", "w", "q", "we", "R"]
write_calls = [mock.call('NA\n'), mock.call('w\n'), mock.call(
@@ -132,7 +134,7 @@ class TestGemmaMapping(unittest.TestCase):
flat_files.assert_called_once_with('mapping')
mock_open.assert_called_once_with(
- 'Home/Genenetwork/group_X_covariates.txt', 'w')
+ 'Home/Genenetwork/COVAR_anFZ_LfZYV0Ulywo+7tRCw.txt', 'w')
filehandler = mock_open()
filehandler.write.assert_has_calls([mock.call(
'-9\t'), mock.call('-9\t'), mock.call('-9\t'), mock.call('-9\t'), mock.call('\n')])
diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py
index c220a072..868b0b0b 100644
--- a/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py
+++ b/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py
@@ -43,11 +43,11 @@ class TestRunMapping(unittest.TestCase):
})
}
self.dataset = AttributeSetter(
- {"fullname": "dataser_1", "group": self.group, "type": "ProbeSet"})
+ {"fullname": "dataset_1", "group": self.group, "type": "ProbeSet"})
self.chromosomes = AttributeSetter({"chromosomes": chromosomes})
self.trait = AttributeSetter(
- {"symbol": "IGFI", "chr": "X1", "mb": 123313})
+ {"symbol": "IGFI", "chr": "X1", "mb": 123313, "display_name": "Test Name"})
def tearDown(self):
self.dataset = AttributeSetter(
@@ -180,34 +180,36 @@ class TestRunMapping(unittest.TestCase):
with mock.patch("wqflask.marker_regression.run_mapping.datetime.datetime", new=datetime_mock):
export_mapping_results(dataset=self.dataset, trait=self.trait, markers=markers,
- results_path="~/results", mapping_scale="physic", score_type="-log(p)",
- transform="qnorm", covariates="Dataset1:Trait1,Dataset2:Trait2", n_samples="100")
+ results_path="~/results", mapping_method="gemma", mapping_scale="physic",
+ score_type="-logP", transform="qnorm",
+ covariates="Dataset1:Trait1,Dataset2:Trait2",
+ n_samples="100", vals_hash="")
write_calls = [
mock.call('Time/Date: 09/01/19 / 10:12:12\n'),
mock.call('Population: Human GP1_\n'), mock.call(
- 'Data Set: dataser_1\n'),
- mock.call('N Samples: 100\n'), mock.call(
- 'Transform - Quantile Normalized\n'),
+ 'Data Set: dataset_1\n'),
+ mock.call('Trait: Test Name\n'),
+ mock.call('Trait Hash: \n'),
+ mock.call('N Samples: 100\n'),
+ mock.call('Mapping Tool: gemma\n'),
+ mock.call('Transform - Quantile Normalized\n'),
mock.call('Gene Symbol: IGFI\n'), mock.call(
'Location: X1 @ 123313 Mb\n'),
mock.call('Cofactors (dataset - trait):\n'),
mock.call('Trait1 - Dataset1\n'),
mock.call('Trait2 - Dataset2\n'),
mock.call('\n'), mock.call('Name,Chr,'),
- mock.call('Mb,-log(p)'), mock.call('Cm,-log(p)'),
+ mock.call('Mb,-logP'),
mock.call(',Additive'), mock.call(',Dominance'),
mock.call('\n'), mock.call('MK1,C1,'),
- mock.call('12000,'), mock.call('1,'),
- mock.call('3'), mock.call(',VA'),
- mock.call(',TT'), mock.call('\n'),
- mock.call('MK2,C2,'), mock.call('10000,'),
- mock.call('15,'), mock.call('7'),
+ mock.call('12000,'), mock.call('3'),
+ mock.call(',VA'), mock.call(',TT'),
+ mock.call('\n'), mock.call('MK2,C2,'),
+ mock.call('10000,'), mock.call('7'),
mock.call('\n'), mock.call('MK1,C3,'),
- mock.call('1,'), mock.call('45,'),
- mock.call('7'), mock.call(',VE'),
- mock.call(',Tt')
-
+ mock.call('1,'), mock.call('7'),
+ mock.call(',VE'), mock.call(',Tt')
]
mock_open.assert_called_once_with("~/results", "w+")
filehandler = mock_open()
@@ -232,25 +234,20 @@ class TestRunMapping(unittest.TestCase):
"c1": "c1_value",
"c2": "c2_value",
"w1": "w1_value"
-
},
"S2": {
"w1": "w2_value",
"w2": "w2_value"
-
},
"S3": {
"c1": "c1_value",
"c2": "c2_value"
-
},
-
}})
-
results = get_perm_strata(this_trait={}, sample_list=sample_list,
categorical_vars=categorical_vars, used_samples=used_samples)
- self.assertEqual(results, [2, 1])
+ self.assertEqual(results, [1, 1])
def test_get_chr_length(self):
"""test for getting chromosome length"""
diff --git a/wqflask/utility/redis_tools.py b/wqflask/utility/redis_tools.py
index ff125bd2..de9dde46 100644
--- a/wqflask/utility/redis_tools.py
+++ b/wqflask/utility/redis_tools.py
@@ -127,22 +127,20 @@ def check_verification_code(code):
def get_user_groups(user_id):
- # ZS: Get the groups where a user is an admin or a member and
+ # Get the groups where a user is an admin or a member and
# return lists corresponding to those two sets of groups
- admin_group_ids = [] # ZS: Group IDs where user is an admin
- user_group_ids = [] # ZS: Group IDs where user is a regular user
+ admin_group_ids = [] # Group IDs where user is an admin
+ user_group_ids = [] # Group IDs where user is a regular user
groups_list = Redis.hgetall("groups")
- for key in groups_list:
+ for group_id, group_details in groups_list.items():
try:
- group_ob = json.loads(groups_list[key])
- group_admins = set([this_admin.encode(
- 'utf-8') if this_admin else None for this_admin in group_ob['admins']])
- group_members = set([this_member.encode(
- 'utf-8') if this_member else None for this_member in group_ob['members']])
+ _details = json.loads(group_details)
+ group_admins = set([this_admin if this_admin else None for this_admin in _details['admins']])
+ group_members = set([this_member if this_member else None for this_member in _details['members']])
if user_id in group_admins:
- admin_group_ids.append(group_ob['id'])
+ admin_group_ids.append(group_id)
elif user_id in group_members:
- user_group_ids.append(group_ob['id'])
+ user_group_ids.append(group_id)
else:
continue
except:
diff --git a/wqflask/wqflask/correlation/correlation_gn3_api.py b/wqflask/wqflask/correlation/correlation_gn3_api.py
index d0d4bcba..a18bceaf 100644
--- a/wqflask/wqflask/correlation/correlation_gn3_api.py
+++ b/wqflask/wqflask/correlation/correlation_gn3_api.py
@@ -150,10 +150,7 @@ def fetch_sample_data(start_vars, this_trait, this_dataset, target_dataset):
sample_data = process_samples(
start_vars, this_dataset.group.all_samples_ordered())
- if target_dataset.type == "ProbeSet":
- target_dataset.get_probeset_data(list(sample_data.keys()))
- else:
- target_dataset.get_trait_data(list(sample_data.keys()))
+ target_dataset.get_trait_data(list(sample_data.keys()))
this_trait = retrieve_sample_data(this_trait, this_dataset)
this_trait_data = {
"trait_sample_data": sample_data,
diff --git a/wqflask/wqflask/decorators.py b/wqflask/wqflask/decorators.py
index f0978fd3..54aa6795 100644
--- a/wqflask/wqflask/decorators.py
+++ b/wqflask/wqflask/decorators.py
@@ -1,14 +1,36 @@
"""This module contains gn2 decorators"""
from flask import g
+from typing import Dict
from functools import wraps
+from utility.hmac import hmac_creation
+import json
+import requests
-def admin_login_required(f):
+
+def edit_access_required(f):
"""Use this for endpoints where admins are required"""
@wraps(f)
def wrap(*args, **kwargs):
- if g.user_session.record.get(b"user_email_address") not in [
- b"labwilliams@gmail.com"]:
+ resource_id: str = ""
+ if kwargs.get("inbredset_id"): # data type: dataset-publish
+ resource_id = hmac_creation("dataset-publish:"
+ f"{kwargs.get('inbredset_id')}:"
+ f"{kwargs.get('name')}")
+ if kwargs.get("dataset_name"): # data type: dataset-probe
+ resource_id = hmac_creation("dataset-probeset:"
+ f"{kwargs.get('dataset_name')}")
+ response: Dict = {}
+ try:
+ _user_id = g.user_session.record.get(b"user_id",
+ "").decode("utf-8")
+ response = json.loads(
+ requests.get("http://localhost:8080/"
+ "available?resource="
+ f"{resource_id}&user={_user_id}").content)
+ except:
+ response = {}
+ if "edit" not in response.get("data", []):
return "You need to be admin", 401
return f(*args, **kwargs)
return wrap
diff --git a/wqflask/wqflask/marker_regression/display_mapping_results.py b/wqflask/wqflask/marker_regression/display_mapping_results.py
index 3986c441..6254b9b9 100644
--- a/wqflask/wqflask/marker_regression/display_mapping_results.py
+++ b/wqflask/wqflask/marker_regression/display_mapping_results.py
@@ -24,6 +24,7 @@
#
# Last updated by Zach 12/14/2010
+import datetime
import string
from math import *
from PIL import Image
@@ -271,6 +272,7 @@ class DisplayMappingResults:
# Needing for form submission when doing single chr
# mapping or remapping after changing options
self.sample_vals = start_vars['sample_vals']
+ self.vals_hash= start_vars['vals_hash']
self.sample_vals_dict = json.loads(self.sample_vals)
self.transform = start_vars['transform']
@@ -355,8 +357,7 @@ class DisplayMappingResults:
if 'use_loco' in list(start_vars.keys()) and self.mapping_method == "gemma":
self.use_loco = start_vars['use_loco']
- if 'reaper_version' in list(start_vars.keys()) and self.mapping_method == "reaper":
- self.reaper_version = start_vars['reaper_version']
+ if self.mapping_method == "reaper":
if 'output_files' in start_vars:
self.output_files = ",".join(
[(the_file if the_file is not None else "") for the_file in start_vars['output_files']])
@@ -651,7 +652,7 @@ class DisplayMappingResults:
btminfo.append(
'Mapping using genotype data as a trait will result in infinity LRS at one locus. In order to display the result properly, all LRSs higher than 100 are capped at 100.')
- def plotIntMapping(self, canvas, offset=(80, 120, 90, 100), zoom=1, startMb=None, endMb=None, showLocusForm=""):
+ def plotIntMapping(self, canvas, offset=(80, 120, 110, 100), zoom=1, startMb=None, endMb=None, showLocusForm=""):
im_drawer = ImageDraw.Draw(canvas)
# calculating margins
xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
@@ -661,7 +662,7 @@ class DisplayMappingResults:
if self.legendChecked:
yTopOffset += 10
if self.covariates != "" and self.controlLocus and self.doControl != "false":
- yTopOffset += 20
+ yTopOffset += 25
if len(self.transform) > 0:
yTopOffset += 5
else:
@@ -1195,43 +1196,47 @@ class DisplayMappingResults:
dataset_label = "%s - %s" % (self.dataset.group.name,
self.dataset.fullname)
- string1 = 'Dataset: %s' % (dataset_label)
+
+ self.current_datetime = datetime.datetime.now().strftime("%b %d %Y %H:%M:%S")
+ string1 = 'UTC Timestamp: %s' % (self.current_datetime)
+ string2 = 'Dataset: %s' % (dataset_label)
+ string3 = 'Trait Hash: %s' % (self.vals_hash)
if self.genofile_string == "":
- string2 = 'Genotype File: %s.geno' % self.dataset.group.name
+ string4 = 'Genotype File: %s.geno' % self.dataset.group.name
else:
- string2 = 'Genotype File: %s' % self.genofile_string
+ string4 = 'Genotype File: %s' % self.genofile_string.split(":")[1]
- string4 = ''
+ string6 = ''
if self.mapping_method == "gemma" or self.mapping_method == "gemma_bimbam":
if self.use_loco == "True":
- string3 = 'Using GEMMA mapping method with LOCO and '
+ string5 = 'Using GEMMA mapping method with LOCO and '
else:
- string3 = 'Using GEMMA mapping method with '
+ string5 = 'Using GEMMA mapping method with '
if self.covariates != "":
- string3 += 'the cofactors below:'
+ string5 += 'the cofactors below:'
cofactor_names = ", ".join(
[covar.split(":")[0] for covar in self.covariates.split(",")])
- string4 = cofactor_names
+ string6 = cofactor_names
else:
- string3 += 'no cofactors'
+ string5 += 'no cofactors'
elif self.mapping_method == "rqtl_plink" or self.mapping_method == "rqtl_geno":
- string3 = 'Using R/qtl mapping method with '
+ string5 = 'Using R/qtl mapping method with '
if self.covariates != "":
- string3 += 'the cofactors below:'
+ string5 += 'the cofactors below:'
cofactor_names = ", ".join(
[covar.split(":")[0] for covar in self.covariates.split(",")])
- string4 = cofactor_names
+ string6 = cofactor_names
elif self.controlLocus and self.doControl != "false":
- string3 += '%s as control' % self.controlLocus
+ string5 += '%s as control' % self.controlLocus
else:
- string3 += 'no cofactors'
+ string5 += 'no cofactors'
else:
- string3 = 'Using Haldane mapping function with '
+ string5 = 'Using Haldane mapping function with '
if self.controlLocus and self.doControl != "false":
- string3 += '%s as control' % self.controlLocus
+ string5 += '%s as control' % self.controlLocus
else:
- string3 += 'no control for other QTLs'
+ string5 += 'no control for other QTLs'
y_constant = 10
if self.this_trait.name:
@@ -1243,24 +1248,26 @@ class DisplayMappingResults:
if self.this_trait.symbol:
identification += "Trait: %s - %s" % (
- self.this_trait.name, self.this_trait.symbol)
+ self.this_trait.display_name, self.this_trait.symbol)
elif self.dataset.type == "Publish":
if self.this_trait.post_publication_abbreviation:
identification += "Trait: %s - %s" % (
- self.this_trait.name, self.this_trait.post_publication_abbreviation)
+ self.this_trait.display_name, self.this_trait.post_publication_abbreviation)
elif self.this_trait.pre_publication_abbreviation:
identification += "Trait: %s - %s" % (
- self.this_trait.name, self.this_trait.pre_publication_abbreviation)
+ self.this_trait.display_name, self.this_trait.pre_publication_abbreviation)
else:
- identification += "Trait: %s" % (self.this_trait.name)
+ identification += "Trait: %s" % (self.this_trait.display_name)
else:
- identification += "Trait: %s" % (self.this_trait.name)
+ identification += "Trait: %s" % (self.this_trait.display_name)
identification += " with %s samples" % (self.n_samples)
d = 4 + max(
im_drawer.textsize(identification, font=labelFont)[0],
im_drawer.textsize(string1, font=labelFont)[0],
- im_drawer.textsize(string2, font=labelFont)[0])
+ im_drawer.textsize(string2, font=labelFont)[0],
+ im_drawer.textsize(string3, font=labelFont)[0],
+ im_drawer.textsize(string4, font=labelFont)[0])
im_drawer.text(
text=identification,
xy=(xLeftOffset, y_constant * fontZoom), font=labelFont,
@@ -1269,7 +1276,9 @@ class DisplayMappingResults:
else:
d = 4 + max(
im_drawer.textsize(string1, font=labelFont)[0],
- im_drawer.textsize(string2, font=labelFont)[0])
+ im_drawer.textsize(string2, font=labelFont)[0],
+ im_drawer.textsize(string3, font=labelFont)[0],
+ im_drawer.textsize(string4, font=labelFont)[0])
if len(self.transform) > 0:
transform_text = "Transform - "
@@ -1296,14 +1305,22 @@ class DisplayMappingResults:
text=string2, xy=(xLeftOffset, y_constant * fontZoom),
font=labelFont, fill=labelColor)
y_constant += 15
- if string3 != '':
+ im_drawer.text(
+ text=string3, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ im_drawer.text(
+ text=string4, xy=(xLeftOffset, y_constant * fontZoom),
+ font=labelFont, fill=labelColor)
+ y_constant += 15
+ if string4 != '':
im_drawer.text(
- text=string3, xy=(xLeftOffset, y_constant * fontZoom),
+ text=string5, xy=(xLeftOffset, y_constant * fontZoom),
font=labelFont, fill=labelColor)
y_constant += 15
- if string4 != '':
+ if string5 != '':
im_drawer.text(
- text=string4, xy=(xLeftOffset, y_constant * fontZoom),
+ text=string6, xy=(xLeftOffset, y_constant * fontZoom),
font=labelFont, fill=labelColor)
def drawGeneBand(self, canvas, gifmap, plotXScale, offset=(40, 120, 80, 10), zoom=1, startMb=None, endMb=None):
diff --git a/wqflask/wqflask/marker_regression/qtlreaper_mapping.py b/wqflask/wqflask/marker_regression/qtlreaper_mapping.py
index 4d6715ba..801674e1 100644
--- a/wqflask/wqflask/marker_regression/qtlreaper_mapping.py
+++ b/wqflask/wqflask/marker_regression/qtlreaper_mapping.py
@@ -178,101 +178,6 @@ def parse_reaper_output(gwa_filename, permu_filename, bootstrap_filename):
return marker_obs, permu_vals, bootstrap_vals
-def run_original_reaper(this_trait, dataset, samples_before, trait_vals, json_data, num_perm, bootCheck, num_bootstrap, do_control, control_marker, manhattan_plot):
- genotype = dataset.group.read_genotype_file(use_reaper=True)
-
- if manhattan_plot != True:
- genotype = genotype.addinterval()
-
- trimmed_samples = []
- trimmed_values = []
- for i in range(0, len(samples_before)):
- try:
- trimmed_values.append(float(trait_vals[i]))
- trimmed_samples.append(str(samples_before[i]))
- except:
- pass
-
- perm_output = []
- bootstrap_results = []
-
- if num_perm < 100:
- suggestive = 0
- significant = 0
- else:
- perm_output = genotype.permutation(
- strains=trimmed_samples, trait=trimmed_values, nperm=num_perm)
- suggestive = perm_output[int(num_perm * 0.37 - 1)]
- significant = perm_output[int(num_perm * 0.95 - 1)]
- # highly_significant = perm_output[int(num_perm*0.99-1)] #ZS: Currently not used, but leaving it here just in case
-
- json_data['suggestive'] = suggestive
- json_data['significant'] = significant
-
- if control_marker != "" and do_control == "true":
- reaper_results = genotype.regression(strains=trimmed_samples,
- trait=trimmed_values,
- control=str(control_marker))
- if bootCheck:
- control_geno = []
- control_geno2 = []
- _FIND = 0
- for _chr in genotype:
- for _locus in _chr:
- if _locus.name == control_marker:
- control_geno2 = _locus.genotype
- _FIND = 1
- break
- if _FIND:
- break
- if control_geno2:
- _prgy = list(genotype.prgy)
- for _strain in trimmed_samples:
- _idx = _prgy.index(_strain)
- control_geno.append(control_geno2[_idx])
-
- bootstrap_results = genotype.bootstrap(strains=trimmed_samples,
- trait=trimmed_values,
- control=control_geno,
- nboot=num_bootstrap)
- else:
- reaper_results = genotype.regression(strains=trimmed_samples,
- trait=trimmed_values)
-
- if bootCheck:
- bootstrap_results = genotype.bootstrap(strains=trimmed_samples,
- trait=trimmed_values,
- nboot=num_bootstrap)
-
- json_data['chr'] = []
- json_data['pos'] = []
- json_data['lod.hk'] = []
- json_data['markernames'] = []
- # if self.additive:
- # self.json_data['additive'] = []
-
- # Need to convert the QTL objects that qtl reaper returns into a json serializable dictionary
- qtl_results = []
- for qtl in reaper_results:
- reaper_locus = qtl.locus
- # ZS: Convert chr to int
- converted_chr = reaper_locus.chr
- if reaper_locus.chr != "X" and reaper_locus.chr != "X/Y":
- converted_chr = int(reaper_locus.chr)
- json_data['chr'].append(converted_chr)
- json_data['pos'].append(reaper_locus.Mb)
- json_data['lod.hk'].append(qtl.lrs)
- json_data['markernames'].append(reaper_locus.name)
- # if self.additive:
- # self.json_data['additive'].append(qtl.additive)
- locus = {"name": reaper_locus.name, "chr": reaper_locus.chr,
- "cM": reaper_locus.cM, "Mb": reaper_locus.Mb}
- qtl = {"lrs_value": qtl.lrs, "chr": converted_chr, "Mb": reaper_locus.Mb,
- "cM": reaper_locus.cM, "name": reaper_locus.name, "additive": qtl.additive, "dominance": qtl.dominance}
- qtl_results.append(qtl)
- return qtl_results, json_data, perm_output, suggestive, significant, bootstrap_results
-
-
def natural_sort(marker_list):
"""
Function to naturally sort numbers + strings, adopted from user Mark Byers here: https://stackoverflow.com/questions/4836710/does-python-have-a-built-in-function-for-string-natural-sort
diff --git a/wqflask/wqflask/marker_regression/rqtl_mapping.py b/wqflask/wqflask/marker_regression/rqtl_mapping.py
index 6e816b47..63e8c334 100644
--- a/wqflask/wqflask/marker_regression/rqtl_mapping.py
+++ b/wqflask/wqflask/marker_regression/rqtl_mapping.py
@@ -42,7 +42,7 @@ def run_rqtl(trait_name, vals, samples, dataset, pair_scan, mapping_scale, model
post_data["pairscan"] = True
if do_control == "true" and control_marker:
- post_data["control_marker"] = control_marker
+ post_data["control"] = control_marker
if not manhattan_plot:
post_data["interval"] = True
@@ -64,6 +64,7 @@ def get_hash_of_textio(the_file: TextIO) -> str:
the_file.seek(0)
hash_of_file = hashlib.md5(the_file.read().encode()).hexdigest()
+ hash_of_file = hash_of_file.replace("/", "_") # Replace / with _ to prevent issue with filenames being translated to directories
return hash_of_file
@@ -92,7 +93,7 @@ def write_phenotype_file(trait_name: str,
for i, sample in enumerate(samples):
this_row = [sample]
if vals[i] != "x":
- this_row.append(vals[i])
+ this_row.append(str(round(float(vals[i]), 3)))
else:
this_row.append("NA")
for cofactor in cofactor_data:
@@ -129,7 +130,7 @@ def cofactors_to_dict(cofactors: str, dataset_ob, samples) -> Dict:
sample_data = trait_ob.data
for index, sample in enumerate(samples):
if sample in sample_data:
- sample_value = sample_data[sample].value
+ sample_value = str(round(float(sample_data[sample].value), 3))
cofactor_dict[cofactor_name].append(sample_value)
else:
cofactor_dict[cofactor_name].append("NA")
diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py
index 6e47ecb8..640cf9cd 100644
--- a/wqflask/wqflask/marker_regression/run_mapping.py
+++ b/wqflask/wqflask/marker_regression/run_mapping.py
@@ -75,6 +75,7 @@ class RunMapping:
self.vals = []
self.samples = []
self.sample_vals = start_vars['sample_vals']
+ self.vals_hash = start_vars['vals_hash']
sample_val_dict = json.loads(self.sample_vals)
samples = sample_val_dict.keys()
if (len(genofile_samplelist) != 0):
@@ -103,9 +104,7 @@ class RunMapping:
if "results_path" in start_vars:
self.mapping_results_path = start_vars['results_path']
else:
- mapping_results_filename = self.dataset.group.name + "_" + \
- ''.join(random.choice(string.ascii_uppercase + string.digits)
- for _ in range(6))
+ mapping_results_filename = "_".join([self.dataset.group.name, self.mapping_method, self.vals_hash]).replace("/", "_")
self.mapping_results_path = "{}{}.csv".format(
webqtlConfig.GENERATED_IMAGE_DIR, mapping_results_filename)
@@ -272,47 +271,32 @@ class RunMapping:
self.bootCheck = False
self.num_bootstrap = 0
- self.reaper_version = start_vars['reaper_version']
-
self.control_marker = start_vars['control_marker']
self.do_control = start_vars['do_control']
logger.info("Running qtlreaper")
- if self.reaper_version == "new":
- self.first_run = True
- self.output_files = None
- # ZS: check if first run so existing result files can be used if it isn't (for example zooming on a chromosome, etc)
- if 'first_run' in start_vars:
- self.first_run = False
- if 'output_files' in start_vars:
- self.output_files = start_vars['output_files'].split(
- ",")
-
- results, self.perm_output, self.suggestive, self.significant, self.bootstrap_results, self.output_files = qtlreaper_mapping.run_reaper(self.this_trait,
- self.dataset,
- self.samples,
- self.vals,
- self.json_data,
- self.num_perm,
- self.bootCheck,
- self.num_bootstrap,
- self.do_control,
- self.control_marker,
- self.manhattan_plot,
- self.first_run,
- self.output_files)
- else:
- results, self.json_data, self.perm_output, self.suggestive, self.significant, self.bootstrap_results = qtlreaper_mapping.run_original_reaper(self.this_trait,
- self.dataset,
- self.samples,
- self.vals,
- self.json_data,
- self.num_perm,
- self.bootCheck,
- self.num_bootstrap,
- self.do_control,
- self.control_marker,
- self.manhattan_plot)
+ self.first_run = True
+ self.output_files = None
+ # ZS: check if first run so existing result files can be used if it isn't (for example zooming on a chromosome, etc)
+ if 'first_run' in start_vars:
+ self.first_run = False
+ if 'output_files' in start_vars:
+ self.output_files = start_vars['output_files'].split(
+ ",")
+
+ results, self.perm_output, self.suggestive, self.significant, self.bootstrap_results, self.output_files = qtlreaper_mapping.run_reaper(self.this_trait,
+ self.dataset,
+ self.samples,
+ self.vals,
+ self.json_data,
+ self.num_perm,
+ self.bootCheck,
+ self.num_bootstrap,
+ self.do_control,
+ self.control_marker,
+ self.manhattan_plot,
+ self.first_run,
+ self.output_files)
elif self.mapping_method == "plink":
self.score_type = "-logP"
self.manhattan_plot = True
@@ -397,7 +381,8 @@ class RunMapping:
with Bench("Exporting Results"):
export_mapping_results(self.dataset, self.this_trait, self.qtl_results, self.mapping_results_path,
- self.mapping_scale, self.score_type, self.transform, self.covariates, self.n_samples)
+ self.mapping_method, self.mapping_scale, self.score_type,
+ self.transform, self.covariates, self.n_samples, self.vals_hash)
with Bench("Trimming Markers for Figure"):
if len(self.qtl_results) > 30000:
@@ -515,14 +500,21 @@ class RunMapping:
return trimmed_genotype_data
-def export_mapping_results(dataset, trait, markers, results_path, mapping_scale, score_type, transform, covariates, n_samples):
+def export_mapping_results(dataset, trait, markers, results_path, mapping_method, mapping_scale, score_type, transform, covariates, n_samples, vals_hash):
+ if mapping_scale == "physic":
+ scale_string = "Mb"
+ else:
+ scale_string = "cM"
with open(results_path, "w+") as output_file:
output_file.write(
"Time/Date: " + datetime.datetime.now().strftime("%x / %X") + "\n")
output_file.write(
"Population: " + dataset.group.species.title() + " " + dataset.group.name + "\n")
output_file.write("Data Set: " + dataset.fullname + "\n")
+ output_file.write("Trait: " + trait.display_name + "\n")
+ output_file.write("Trait Hash: " + vals_hash + "\n")
output_file.write("N Samples: " + str(n_samples) + "\n")
+ output_file.write("Mapping Tool: " + str(mapping_method) + "\n")
if len(transform) > 0:
transform_text = "Transform - "
if transform == "qnorm":
@@ -552,10 +544,7 @@ def export_mapping_results(dataset, trait, markers, results_path, mapping_scale,
output_file.write("Name,Chr,")
if score_type.lower() == "-logP":
score_type = "-logP"
- if 'Mb' in markers[0]:
- output_file.write("Mb," + score_type)
- if 'cM' in markers[0]:
- output_file.write("Cm," + score_type)
+ output_file.write(scale_string + "," + score_type)
if "additive" in list(markers[0].keys()):
output_file.write(",Additive")
if "dominance" in list(markers[0].keys()):
@@ -563,11 +552,8 @@ def export_mapping_results(dataset, trait, markers, results_path, mapping_scale,
output_file.write("\n")
for i, marker in enumerate(markers):
output_file.write(marker['name'] + "," + str(marker['chr']) + ",")
- if 'Mb' in marker:
- output_file.write(str(marker['Mb']) + ",")
- if 'cM' in marker:
- output_file.write(str(marker['cM']) + ",")
- if "lod_score" in marker.keys():
+ output_file.write(str(marker[scale_string]) + ",")
+ if score_type == "-logP":
output_file.write(str(marker['lod_score']))
else:
output_file.write(str(marker['lrs_value']))
diff --git a/wqflask/wqflask/resource_manager.py b/wqflask/wqflask/resource_manager.py
index b28c1b04..c54dd0b3 100644
--- a/wqflask/wqflask/resource_manager.py
+++ b/wqflask/wqflask/resource_manager.py
@@ -8,8 +8,6 @@ from wqflask import app
from utility.authentication_tools import check_owner_or_admin
from utility.redis_tools import get_resource_info, get_group_info, get_groups_like_unique_column, get_user_id, get_user_by_unique_column, get_users_like_unique_column, add_access_mask, add_resource, change_resource_owner
-from utility.logger import getLogger
-logger = getLogger(__name__)
@app.route("/resources/manage", methods=('GET', 'POST'))
diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py
index c947a3b4..52d7d308 100644
--- a/wqflask/wqflask/show_trait/show_trait.py
+++ b/wqflask/wqflask/show_trait/show_trait.py
@@ -178,11 +178,11 @@ class ShowTrait:
self.sample_group_types['samples_primary'] = self.dataset.group.name
sample_lists = [group.sample_list for group in self.sample_groups]
- categorical_var_list = []
+ self.categorical_var_list = []
self.numerical_var_list = []
if not self.temp_trait:
# ZS: Only using first samplelist, since I think mapping only uses those samples
- categorical_var_list = get_categorical_variables(
+ self.categorical_var_list = get_categorical_variables(
self.this_trait, self.sample_groups[0])
self.numerical_var_list = get_numerical_variables(
self.this_trait, self.sample_groups[0])
@@ -290,8 +290,8 @@ class ShowTrait:
hddn['study_samplelists'] = json.dumps(study_samplelist_json)
hddn['num_perm'] = 0
hddn['categorical_vars'] = ""
- if categorical_var_list:
- hddn['categorical_vars'] = ",".join(categorical_var_list)
+ if self.categorical_var_list:
+ hddn['categorical_vars'] = ",".join(self.categorical_var_list)
hddn['manhattan_plot'] = ""
hddn['control_marker'] = ""
if not self.temp_trait:
@@ -326,7 +326,7 @@ class ShowTrait:
has_num_cases=self.has_num_cases,
attributes=self.sample_groups[0].attributes,
categorical_attr_exists=self.categorical_attr_exists,
- categorical_vars=",".join(categorical_var_list),
+ categorical_vars=",".join(self.categorical_var_list),
num_values=self.num_values,
qnorm_values=self.qnorm_vals,
zscore_values=self.z_scores,
diff --git a/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js b/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js
index 4de1b0ac..0a060cdc 100644
--- a/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js
+++ b/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js
@@ -130,6 +130,7 @@ var primary_table = $('#samples_primary').DataTable( {
$(row).addClass("value_se");
if (data.outlier) {
$(row).addClass("outlier");
+ $(row).attr("style", "background-color: orange;");
}
$('td', row).eq(1).addClass("column_name-Index")
$('td', row).eq(2).addClass("column_name-Sample")
@@ -189,6 +190,7 @@ if (js_data.sample_lists.length > 1){
$(row).addClass("value_se");
if (data.outlier) {
$(row).addClass("outlier");
+ $(row).attr("style", "background-color: orange;");
}
$('td', row).eq(1).addClass("column_name-Index")
$('td', row).eq(2).addClass("column_name-Sample")
diff --git a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
index 4f994eae..d3b44309 100644
--- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
+++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
@@ -140,12 +140,17 @@ $('input[name=display_all]').change((function(_this) {
};
})(this));
-//ZS: This is a list of inputs to be passed to the loading page, since not all inputs on the trait page are relevant to mapping
-var mapping_input_list = ['temp_uuid', 'trait_id', 'dataset', 'tool_used', 'form_url', 'method', 'transform', 'trimmed_markers', 'selected_chr', 'chromosomes', 'mapping_scale', 'sample_vals',
- 'score_type', 'suggestive', 'significant', 'num_perm', 'permCheck', 'perm_output', 'perm_strata', 'categorical_vars', 'num_bootstrap', 'bootCheck', 'bootstrap_results',
- 'LRSCheck', 'covariates', 'maf', 'use_loco', 'manhattan_plot', 'control_marker', 'do_control', 'genofile',
- 'pair_scan', 'startMb', 'endMb', 'graphWidth', 'lrsMax', 'additiveCheck', 'showSNP', 'showGenes', 'viewLegend', 'haplotypeAnalystCheck',
- 'mapmethod_rqtl', 'mapmodel_rqtl', 'temp_trait', 'group', 'species', 'reaper_version', 'primary_samples']
+// This is a list of inputs to be passed to the loading page, since not all inputs on the trait page are relevant to mapping
+var mapping_input_list = ['temp_uuid', 'trait_id', 'dataset', 'tool_used', 'form_url', 'method',
+ 'transform', 'trimmed_markers', 'selected_chr', 'chromosomes', 'mapping_scale',
+ 'sample_vals', 'vals_hash', 'score_type', 'suggestive', 'significant',
+ 'num_perm', 'permCheck', 'perm_output', 'perm_strata', 'categorical_vars',
+ 'num_bootstrap', 'bootCheck', 'bootstrap_results', 'LRSCheck', 'covariates',
+ 'maf', 'use_loco', 'manhattan_plot', 'control_marker', 'do_control',
+ 'genofile', 'pair_scan', 'startMb', 'endMb', 'graphWidth', 'lrsMax',
+ 'additiveCheck', 'showSNP', 'showGenes', 'viewLegend', 'haplotypeAnalystCheck',
+ 'mapmethod_rqtl', 'mapmodel_rqtl', 'temp_trait', 'group', 'species',
+ 'reaper_version', 'primary_samples']
$(".rqtl-geno-tab, #rqtl_geno_compute").on("click", (function(_this) {
return function() {
diff --git a/wqflask/wqflask/templates/admin/group_manager.html b/wqflask/wqflask/templates/admin/group_manager.html
index c0b99e75..692a7abc 100644
--- a/wqflask/wqflask/templates/admin/group_manager.html
+++ b/wqflask/wqflask/templates/admin/group_manager.html
@@ -81,7 +81,7 @@
<tr>
<td><input type="checkbox" name="read" value="{{ group.id }}"></td>
<td>{{ loop.index }}</td>
- <td>{{ group.name }}</td>
+ <td><a href="/groups/view?id={{ group.id }}">{{ group.name }}</a></td>
<td>{{ group.admins|length + group.members|length }}</td>
<td>{{ group.created_timestamp }}</td>
<td>{{ group.changed_timestamp }}</td>
diff --git a/wqflask/wqflask/templates/base.html b/wqflask/wqflask/templates/base.html
index 049ebe6d..14e6bc88 100644
--- a/wqflask/wqflask/templates/base.html
+++ b/wqflask/wqflask/templates/base.html
@@ -87,6 +87,7 @@
<li><a href="https://systems-genetics.org/">Systems Genetics PheWAS</a></li>
<li><a href="http://ucscbrowser.genenetwork.org/">Genome Browser</a></li>
<li><a href="http://power.genenetwork.org">BXD Power Calculator</a></li>
+ <li><a href="http://notebook.genenetwork.org/">Jupyter Notebook Launcher</a></li>
<li><a href="http://datafiles.genenetwork.org">Interplanetary File System</a></li>
</ul>
</li>
diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html
index 9ec98ab1..a3090bcf 100644
--- a/wqflask/wqflask/templates/collections/view.html
+++ b/wqflask/wqflask/templates/collections/view.html
@@ -49,7 +49,7 @@
<input type="text" id="select_top" class="form-control" style="width: 200px; display: inline; padding-bottom: 9px;" placeholder="Select Top ...">
<button class="btn btn-default" id="deselect_all" type="button"><span class="glyphicon glyphicon-remove"></span> Deselect</button>
<button id="remove" class="btn btn-danger" data-url="/collections/remove" type="button" disabled><i class="icon-minus-sign"></i> Delete Rows</button>
- <button id="delete" class="btn btn-danger submit_special" data-url="/collections/delete" title="Delete this collection" > Delete Collection</button>
+ <button id="delete" class="btn btn-danger submit_special" data-url="/collections/delete" type="button" title="Delete this collection" > Delete Collection</button>
</form>
</div>
<div style="margin-top: 10px; margin-bottom: 5px;">
diff --git a/wqflask/wqflask/templates/loading.html b/wqflask/wqflask/templates/loading.html
index 1edde31e..ccf810b0 100644
--- a/wqflask/wqflask/templates/loading.html
+++ b/wqflask/wqflask/templates/loading.html
@@ -12,6 +12,8 @@
{% if start_vars.tool_used == "Mapping" %}
<h1>Computing the Maps</h1>
<br>
+ <b>Time Elapsed:</b> <span class="timer"></span>
+ <br>
<b>Trait Metadata</b>
<br>
species = <b><i>{{ start_vars.species[0] | upper }}{{ start_vars.species[1:] }}</i></b>
@@ -101,9 +103,6 @@
<script src="{{ url_for('js', filename='jquery/jquery.min.js') }}" type="text/javascript"></script>
<script src="{{ url_for('js', filename='bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
<script type="text/javascript">
-$("#loading_form").attr("action", "{{ start_vars.form_url }}");
-setTimeout(function(){ $("#loading_form").submit()}, 350);
-
$('#show_full_diff').click(function() {
if ($('#diff_table_container').is(':visible')){
$('#diff_table_container').hide();
@@ -111,4 +110,24 @@ $('#show_full_diff').click(function() {
$('#diff_table_container').show();
}
})
+
+var start = new Date;
+
+setInterval(function() {
+ minutes = Math.floor((new Date - start) / 1000 / 60)
+ seconds = Math.round(((new Date - start) / 1000) % 60)
+ if (seconds < 10 && minutes >= 1){
+ seconds_text = "0" + seconds.toString()
+ } else {
+ seconds_text = seconds.toString()
+ }
+ if (minutes < 1) {
+ $('.timer').text(seconds_text + " seconds");
+ } else {
+ $('.timer').text(minutes.toString() + ":" + seconds_text);
+ }
+}, 100);
+
+$("#loading_form").attr("action", "{{ start_vars.form_url }}");
+setTimeout(function(){ $("#loading_form").submit()}, 350);
</script>
diff --git a/wqflask/wqflask/templates/mapping_results.html b/wqflask/wqflask/templates/mapping_results.html
index 81eb1ba1..f2d11e89 100644
--- a/wqflask/wqflask/templates/mapping_results.html
+++ b/wqflask/wqflask/templates/mapping_results.html
@@ -34,6 +34,7 @@
<input type="hidden" name="results_path" value="{{ mapping_results_path }}">
<input type="hidden" name="method" value="{{ mapping_method }}">
<input type="hidden" name="sample_vals" value="{{ sample_vals }}">
+ <input type="hidden" name="vals_hash" value="{{ vals_hash }}">
<input type="hidden" name="n_samples" value="{{ n_samples }}">
<input type="hidden" name="maf" value="{{ maf }}">
<input type="hidden" name="use_loco" value="{{ use_loco }}">
@@ -67,15 +68,16 @@
<h2>Map Viewer: Whole Genome</h2><br>
<b>Population:</b> {{ dataset.group.species|capitalize }} {{ dataset.group.name }}<br>
<b>Database:</b> {{ dataset.fullname }}<br>
- {% if dataset.type == "ProbeSet" %}<b>Trait ID:</b>{% else %}<b>Record ID:</b>{% endif %} <a href="/show_trait?trait_id={{ this_trait.name }}&dataset={{ dataset.name }}">{{ this_trait.name }}</a><br>
+ {% if dataset.type == "ProbeSet" %}<b>Trait ID:</b>{% else %}<b>Record ID:</b>{% endif %} <a href="/show_trait?trait_id={{ this_trait.name }}&dataset={{ dataset.name }}">{{ this_trait.display_name }}</a><br>
+ <b>Trait Hash: </b> {{ vals_hash }}<br>
{% if dataset.type == "ProbeSet" %}
<b>Gene Symbol:</b> <i>{{ this_trait.symbol }}</i><br>
<b>Location:</b> Chr {{ this_trait.chr }} @ {{ this_trait.mb }} Mb<br>
{% endif %}
- {% if genofile_string is defined %}
- <b>Genotypes:</b> {{ genofile_string.split(":")[1] }}
+ {% if genofile_string != "" %}
+ <b>Genotypes:</b> {{ genofile_string.split(":")[1] }}<br>
{% endif %}
- <br>
+ <b>Current Date/Time:</b> {{ current_datetime }}<br>
<br>
<a class="export_mapping_results" href="#" target="_blank" >Download Full Results</a>
</div>
@@ -529,7 +531,7 @@
});
{% endif %}
- {% if mapping_method != "gemma" and mapping_method != "plink" %}
+ {% if mapping_method != "gemma" and mapping_method != "plink" and nperm > 0 and permChecked == "ON" %}
$('#download_perm').click(function(){
perm_info_dict = {
perm_data: js_data.perm_results,
diff --git a/wqflask/wqflask/templates/new_security/_scripts.html b/wqflask/wqflask/templates/new_security/_scripts.html
deleted file mode 100644
index 5fefe305..00000000
--- a/wqflask/wqflask/templates/new_security/_scripts.html
+++ /dev/null
@@ -1 +0,0 @@
-<!--<script type="text/javascript" src="/static/new/javascript/login.js"></script>-->
diff --git a/wqflask/wqflask/templates/new_security/forgot_password.html b/wqflask/wqflask/templates/new_security/forgot_password.html
index e5c42a45..60a221da 100644
--- a/wqflask/wqflask/templates/new_security/forgot_password.html
+++ b/wqflask/wqflask/templates/new_security/forgot_password.html
@@ -48,6 +48,5 @@
{% endblock %}
{% block js %}
- {% include "new_security/_scripts.html" %}
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/forgot_password_step2.html b/wqflask/wqflask/templates/new_security/forgot_password_step2.html
index b4bf41c7..1835fd4c 100644
--- a/wqflask/wqflask/templates/new_security/forgot_password_step2.html
+++ b/wqflask/wqflask/templates/new_security/forgot_password_step2.html
@@ -20,7 +20,6 @@
{% endblock %}
{% block js %}
- {% include "new_security/_scripts.html" %}
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='zxcvbn/zxcvbn.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html
index 095036f0..88eab6bc 100644
--- a/wqflask/wqflask/templates/new_security/login_user.html
+++ b/wqflask/wqflask/templates/new_security/login_user.html
@@ -114,31 +114,5 @@ label.error,div.error{
{% endblock %}
{% block js %}
- <!-- Disable plugin, see https://github.com/genenetwork/genenetwork2/issues/310
-
- <script type="text/javascript" src="/static/new/packages/ValidationPlugin/dist/jquery.validate.min.js"></script>
- <script>
- $(document).ready(function () {
- $("#loginUserForm").validate({
- onkeyup: false,
- onsubmit: true,
- onfocusout: function(element) { $(element).valid(); },
- rules: {
- email_address: {
- required: true,
- email: true
- },
- password: {
- required: true
- }
- }
- });
- });
-
- </script>
-
- -->
-
- {% include "new_security/_scripts.html" %}
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/password_reset.html b/wqflask/wqflask/templates/new_security/password_reset.html
index 684c12b1..e21f075c 100644
--- a/wqflask/wqflask/templates/new_security/password_reset.html
+++ b/wqflask/wqflask/templates/new_security/password_reset.html
@@ -73,7 +73,6 @@
{% block js %}
- {% include "new_security/_scripts.html" %}
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='zxcvbn/zxcvbn.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/register_user.html b/wqflask/wqflask/templates/new_security/register_user.html
index 3ae4488b..c2895517 100644
--- a/wqflask/wqflask/templates/new_security/register_user.html
+++ b/wqflask/wqflask/templates/new_security/register_user.html
@@ -100,7 +100,6 @@
{% block js %}
- {% include "new_security/_scripts.html" %}
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='zxcvbn/zxcvbn.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/registered.html b/wqflask/wqflask/templates/new_security/registered.html
index f2f58ec1..29889a97 100644
--- a/wqflask/wqflask/templates/new_security/registered.html
+++ b/wqflask/wqflask/templates/new_security/registered.html
@@ -19,7 +19,6 @@
{% block js %}
- {% include "new_security/_scripts.html" %}
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='zxcvbn/zxcvbn.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/thank_you.html b/wqflask/wqflask/templates/new_security/thank_you.html
index 0ff7ee8d..d4f5e574 100644
--- a/wqflask/wqflask/templates/new_security/thank_you.html
+++ b/wqflask/wqflask/templates/new_security/thank_you.html
@@ -18,7 +18,6 @@
{% endblock %}
{% block js %}
- {% include "new_security/_scripts.html" %}
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='zxcvbn/zxcvbn.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/new_security/verification_still_needed.html b/wqflask/wqflask/templates/new_security/verification_still_needed.html
index dc0f9e68..1f91fd8d 100644
--- a/wqflask/wqflask/templates/new_security/verification_still_needed.html
+++ b/wqflask/wqflask/templates/new_security/verification_still_needed.html
@@ -21,7 +21,6 @@
{% endblock %}
{% block js %}
- {% include "new_security/_scripts.html" %}
<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='zxcvbn/zxcvbn.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/password_strength.js"></script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html
index 7ec335d5..c499aa8f 100644
--- a/wqflask/wqflask/templates/search_result_page.html
+++ b/wqflask/wqflask/templates/search_result_page.html
@@ -53,6 +53,7 @@
A total of {{ results|count }} records were found.
</p>
+ {% if results|count > 0 %}
{% if go_term is not none %}
<p><b>The associated genes include:</b><br><br>{% for word in search_terms %}{{ word.search_term[0] }}{% endfor %}</p>
{% endif %}
@@ -133,8 +134,11 @@
</div>
</div>
{% endif %}
+ {% else %}
+ <br>
+ <button type="button" onclick="window.location.href='/'">Return To Index Page</button>
+ {% endif %}
</div>
-
<div id="myModal"></div>
<!-- End of body -->
@@ -171,6 +175,7 @@
return params;
};
+ {% if results|count > 0 %}
//ZS: Need to make sort by symbol, also need to make sure blank symbol fields at the bottom and symbols starting with numbers below letters
trait_table = $('#trait_table').DataTable( {
'drawCallback': function( settings ) {
@@ -412,6 +417,7 @@
var table = $('#trait_table').DataTable();
table.colReorder.reset()
});
+ {% endif %}
submit_special = function(url) {
$("#trait_submission_form").attr("action", url);
diff --git a/wqflask/wqflask/templates/show_trait.html b/wqflask/wqflask/templates/show_trait.html
index 3dbf5f57..f3fa1332 100644
--- a/wqflask/wqflask/templates/show_trait.html
+++ b/wqflask/wqflask/templates/show_trait.html
@@ -254,8 +254,6 @@
} );
{% endif %}
- $('#samples_primary, #samples_other').find("tr.outlier").css('background-color', 'orange')
-
$('.edit_sample_checkbox:checkbox').change(function() {
if ($(this).is(":checked")) {
if (!$(this).closest('tr').hasClass('selected')) {
diff --git a/wqflask/wqflask/templates/show_trait_details.html b/wqflask/wqflask/templates/show_trait_details.html
index 53e16aa0..2a21dd24 100644
--- a/wqflask/wqflask/templates/show_trait_details.html
+++ b/wqflask/wqflask/templates/show_trait_details.html
@@ -242,6 +242,9 @@
{% if this_trait.dataset.type == 'ProbeSet' %}
<button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource" onclick="window.open('/trait/edit/probeset-name/{{ this_trait.name }}', '_blank')">Edit</button>
{% endif %}
+ {% if admin_status == "owner" or admin_status == "edit-admins" or admin_status == "edit-access" %}
+ <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource" onclick="window.open('./resources/manage?resource_id={{ resource_id }}', '_blank')">Edit Privileges</button>
+ {% endif %}
{% endif %}
</div>
</div>
diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html
index 7a785b91..fbb26ede 100755
--- a/wqflask/wqflask/templates/show_trait_mapping_tools.html
+++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html
@@ -99,15 +99,6 @@
<div class="tab-pane" id="interval_mapping">
<div class="form-horizontal section-form-div">
<div class="mapping_method_fields form-group">
- <label for="reaper_version" class="col-xs-3 control-label">Version<sup><a href="https://github.com/chfi/rust-qtlreaper" target="_blank" title="'New' is the new qtlreaper implementation written in RUST by Christian Fischer. 'Original' corresponds to the original version written in C.">?</a></sup></label>
- <div class="col-xs-3 controls">
- <select name="reaper_version" class="form-control reaper-ver-select">
- <option value="new">New</option>
- <option value="original">Original</option>
- </select>
- </div>
- </div>
- <div class="mapping_method_fields form-group">
<label for="chr_select" class="col-xs-3 control-label">Chromosome</label>
<div class="col-xs-2 controls">
<select id="chr_reaper" class="form-control chr-select">
@@ -266,21 +257,6 @@
</div>
{% endif %}
<div class="mapping_method_fields form-group">
- <label for="control_for" class="col-xs-3 control-label">Control&nbsp;for</label>
- <div class="col-xs-6 controls">
- <input name="control_rqtl_geno" value="{% if dataset.type == 'ProbeSet' and this_trait.locus_chr != '' %}{{ nearest_marker }}{% endif %}" type="text" class="form-control cofactor-input" />
- <label class="radio-inline">
- <input type="radio" name="do_control_rqtl" value="true">
- Yes
- </label>
- <label class="radio-inline">
- <input type="radio" name="do_control_rqtl" value="false" checked="">
- No
- </label>
- </div>
- </div>
-
- <div class="mapping_method_fields form-group">
<label for="mapmodel_rqtl_geno" class="col-xs-3 control-label">Model</label>
<div class="col-xs-4 controls">
<select id="mapmodel_rqtl_geno" name="mapmodel_rqtl_geno" class="form-control">
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 000d71d9..6936ce78 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -85,7 +85,7 @@ from wqflask.export_traits import export_search_results_csv
from wqflask.gsearch import GSearch
from wqflask.update_search_results import GSearch as UpdateGSearch
from wqflask.docs import Docs, update_text
-from wqflask.decorators import admin_login_required
+from wqflask.decorators import edit_access_required
from wqflask.db_info import InfoPage
from utility import temp_data
@@ -160,28 +160,37 @@ def shutdown_session(exception=None):
@app.errorhandler(Exception)
-def handle_bad_request(e):
+def handle_generic_exceptions(e):
+ import werkzeug
err_msg = str(e)
- logger.error(err_msg)
- logger.error(request.url)
- # get the stack trace and send it to the logger
- exc_type, exc_value, exc_traceback = sys.exc_info()
- logger.error(traceback.format_exc())
now = datetime.datetime.utcnow()
time_str = now.strftime('%l:%M%p UTC %b %d, %Y')
- formatted_lines = [request.url
- + " (" + time_str + ")"] + traceback.format_exc().splitlines()
-
+ # get the stack trace and send it to the logger
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ formatted_lines = {f"{request.url} ({time_str}) "
+ f" {traceback.format_exc().splitlines()}"}
+
+ _message_templates = {
+ werkzeug.exceptions.NotFound: ("404: Not Found: "
+ f"{time_str}: {request.url}"),
+ werkzeug.exceptions.BadRequest: ("400: Bad Request: "
+ f"{time_str}: {request.url}"),
+ werkzeug.exceptions.RequestTimeout: ("408: Request Timeout: "
+ f"{time_str}: {request.url}")}
+ # Default to the lengthy stack trace!
+ logger.error(_message_templates.get(exc_type,
+ formatted_lines))
# Handle random animations
# Use a cookie to have one animation on refresh
animation = request.cookies.get(err_msg[:32])
if not animation:
- list = [fn for fn in os.listdir(
- "./wqflask/static/gif/error") if fn.endswith(".gif")]
- animation = random.choice(list)
+ animation = random.choice([fn for fn in os.listdir(
+ "./wqflask/static/gif/error") if fn.endswith(".gif")])
resp = make_response(render_template("error.html", message=err_msg,
- stack=formatted_lines, error_image=animation, version=GN_VERSION))
+ stack=formatted_lines,
+ error_image=animation,
+ version=GN_VERSION))
# logger.error("Set cookie %s with %s" % (err_msg, animation))
resp.set_cookie(err_msg[:32], animation)
@@ -411,7 +420,7 @@ def submit_trait_form():
@app.route("/trait/<name>/edit/inbredset-id/<inbredset_id>")
-@admin_login_required
+@edit_access_required
def edit_phenotype(name, inbredset_id):
conn = MySQLdb.Connect(db=current_app.config.get("DB_NAME"),
user=current_app.config.get("DB_USER"),
@@ -468,7 +477,7 @@ def edit_phenotype(name, inbredset_id):
@app.route("/trait/edit/probeset-name/<dataset_name>")
-@admin_login_required
+@edit_access_required
def edit_probeset(dataset_name):
conn = MySQLdb.Connect(db=current_app.config.get("DB_NAME"),
user=current_app.config.get("DB_USER"),
@@ -511,7 +520,7 @@ def edit_probeset(dataset_name):
@app.route("/trait/update", methods=["POST"])
-@admin_login_required
+@edit_access_required
def update_phenotype():
conn = MySQLdb.Connect(db=current_app.config.get("DB_NAME"),
user=current_app.config.get("DB_USER"),
@@ -633,11 +642,11 @@ def update_phenotype():
json_data=json.dumps(diff_data)))
flash(f"Diff-data: \n{diff_data}\nhas been uploaded", "success")
return redirect(f"/trait/{data_.get('dataset-name')}"
- f"/edit/phenotype-id/{data_.get('phenotype-id')}")
+ f"/edit/inbredset-id/{data_.get('inbred-set-id')}")
@app.route("/probeset/update", methods=["POST"])
-@admin_login_required
+@edit_access_required
def update_probeset():
conn = MySQLdb.Connect(db=current_app.config.get("DB_NAME"),
user=current_app.config.get("DB_USER"),
@@ -1051,6 +1060,7 @@ def mapping_results_page():
'samples',
'vals',
'sample_vals',
+ 'vals_hash',
'first_run',
'output_files',
'geno_db_exists',
@@ -1096,7 +1106,6 @@ def mapping_results_page():
'mapmethod_rqtl',
'mapmodel_rqtl',
'temp_trait',
- 'reaper_version',
'n_samples',
'transform'
)
@@ -1161,7 +1170,7 @@ def export_mapping_results():
results_csv = open(file_path, "r").read()
response = Response(results_csv,
mimetype='text/csv',
- headers={"Content-Disposition": "attachment;filename=mapping_results.csv"})
+ headers={"Content-Disposition": "attachment;filename=" + os.path.basename(file_path)})
return response
@@ -1364,7 +1373,7 @@ def get_sample_data_as_csv(trait_name: int, phenotype_id: int):
@app.route("/admin/data-sample/diffs/")
-@admin_login_required
+@edit_access_required
def display_diffs_admin():
TMPDIR = current_app.config.get("TMPDIR")
DIFF_DIR = f"{TMPDIR}/sample-data/diffs"