diff options
Diffstat (limited to 'wqflask')
33 files changed, 1083 insertions, 312 deletions
diff --git a/wqflask/tests/unit/utility/test_type_checking.py b/wqflask/tests/unit/utility/test_type_checking.py new file mode 100644 index 00000000..48d110c7 --- /dev/null +++ b/wqflask/tests/unit/utility/test_type_checking.py @@ -0,0 +1,54 @@ +import unittest +from utility.type_checking import is_float +from utility.type_checking import is_int +from utility.type_checking import is_str +from utility.type_checking import get_float +from utility.type_checking import get_int +from utility.type_checking import get_string + + +class TestTypeChecking(unittest.TestCase): + def test_is_float(self): + floats = [2, 1.2, '3.1'] + not_floats = ["String", None, [], ()] + for flt in floats: + results = is_float(flt) + self.assertTrue(results) + for nflt in not_floats: + results = is_float(nflt) + self.assertFalse(results) + + def test_is_int(self): + int_values = [1, 1.1] + not_int_values = ["string", None, [], "1.1"] + for int_val in int_values: + results = is_int(int_val) + self.assertTrue(results) + for not_int in not_int_values: + results = is_int(not_int) + self.assertFalse(results) + + def test_is_str(self): + string_values = [1, False, [], {}, "string_value"] + falsey_values = [None] + for string_val in string_values: + results = is_str(string_val) + self.assertTrue(results) + for non_string in falsey_values: + results = is_str(non_string) + self.assertFalse(results) + + def test_get_float(self): + vars_object = {"min_value": "12"} + results = get_float(vars_object, "min_value") + self.assertEqual(results, 12.0) + + def test_get_int(self): + vars_object = {"lx_value": "1"} + results = get_int(vars_object, "lx_value") + self.assertEqual(results, 1) + + def test_get_string(self): + string_object = {"mx_value": 1} + results = get_string(string_object, "mx_value") + self.assertEqual(results, "1")
\ No newline at end of file diff --git a/wqflask/tests/unit/wqflask/api/test_correlation.py b/wqflask/tests/unit/wqflask/api/test_correlation.py new file mode 100644 index 00000000..d0264b87 --- /dev/null +++ b/wqflask/tests/unit/wqflask/api/test_correlation.py @@ -0,0 +1,153 @@ +import unittest +from unittest import mock +from wqflask import app +from collections import OrderedDict +from wqflask.api.correlation import init_corr_params +from wqflask.api.correlation import convert_to_mouse_gene_id +from wqflask.api.correlation import do_literature_correlation_for_all_traits +from wqflask.api.correlation import get_sample_r_and_p_values +from wqflask.api.correlation import calculate_results + + +class AttributeSetter: + def __init__(self, obj): + for k, v in obj.items(): + setattr(self, k, v) + + +class MockDataset(AttributeSetter): + def get_trait_data(self): + return None + + def retrieve_genes(self, id=None): + return { + "TT-1": "GH-1", + "TT-2": "GH-2", + "TT-3": "GH-3" + + } + + +class TestCorrelations(unittest.TestCase): + def setUp(self): + self.app_context = app.app_context() + self.app_context.push() + + def tearDown(self): + self.app_context.pop() + + def test_init_corr_params(self): + start_vars = { + "return_count": "3", + "type": "T1", + "method": "spearman" + } + + corr_params_results = init_corr_params(start_vars=start_vars) + expected_results = { + "return_count": 3, + "type": "T1", + "method": "spearman" + } + + self.assertEqual(corr_params_results, expected_results) + + @mock.patch("wqflask.api.correlation.g") + def test_convert_to_mouse_gene_id(self, mock_db): + + results = convert_to_mouse_gene_id(species="Other", gene_id="") + self.assertEqual(results, None) + + rat_species_results = convert_to_mouse_gene_id( + species="rat", gene_id="GH1") + + mock_db.db.execute.return_value.fetchone.side_effect = [ + AttributeSetter({"mouse": "MG-1"}), AttributeSetter({"mouse": "MG-2"})] + + self.assertEqual(convert_to_mouse_gene_id( + species="mouse", gene_id="MG-4"), "MG-4") + self.assertEqual(convert_to_mouse_gene_id( + species="rat", gene_id="R1"), "MG-1") + self.assertEqual(convert_to_mouse_gene_id( + species="human", gene_id="H1"), "MG-2") + + @mock.patch("wqflask.api.correlation.g") + @mock.patch("wqflask.api.correlation.convert_to_mouse_gene_id") + def test_do_literature_correlation_for_all_traits(self, mock_convert_to_mouse_geneid, mock_db): + mock_convert_to_mouse_geneid.side_effect = [ + "MG-1", "MG-2;", "MG-3", "MG-4"] + + trait_geneid_dict = { + "TT-1": "GH-1", + "TT-2": "GH-2", + "TT-3": "GH-3" + + } + mock_db.db.execute.return_value.fetchone.side_effect = [AttributeSetter( + {"value": "V1"}), AttributeSetter({"value": "V2"}), AttributeSetter({"value": "V3"})] + + this_trait = AttributeSetter({"geneid": "GH-1"}) + + target_dataset = AttributeSetter( + {"group": AttributeSetter({"species": "rat"})}) + results = do_literature_correlation_for_all_traits( + this_trait=this_trait, target_dataset=target_dataset, trait_geneid_dict=trait_geneid_dict, corr_params={}) + + expected_results = {'TT-1': ['GH-1', 0], + 'TT-2': ['GH-2', 'V1'], 'TT-3': ['GH-3', 'V2']} + self.assertEqual(results, expected_results) + + @mock.patch("wqflask.api.correlation.corr_result_helpers.normalize_values") + def test_get_sample_r_and_p_values(self, mock_normalize): + + group = AttributeSetter( + {"samplelist": ["S1", "S2", "S3", "S4", "S5", "S6", "S7"]}) + target_dataset = AttributeSetter({"group": group}) + + target_vals = [3.4, 6.2, 4.1, 3.4, 1.2, 5.6] + trait_data = {"S1": AttributeSetter({"value": 2.3}), "S2": AttributeSetter({"value": 1.1}), + "S3": AttributeSetter( + {"value": 6.3}), "S4": AttributeSetter({"value": 3.6}), "S5": AttributeSetter({"value": 4.1}), + "S6": AttributeSetter({"value": 5.0})} + this_trait = AttributeSetter({"data": trait_data}) + mock_normalize.return_value = ([2.3, 1.1, 6.3, 3.6, 4.1, 5.0], + [3.4, 6.2, 4.1, 3.4, 1.2, 5.6], 6) + mock_normalize.side_effect = [([2.3, 1.1, 6.3, 3.6, 4.1, 5.0], + [3.4, 6.2, 4.1, 3.4, 1.2, 5.6], 6), + ([2.3, 1.1, 6.3, 3.6, 4.1, 5.0], + [3.4, 6.2, 4.1, 3.4, 1.2, 5.6], 6), + ([2.3, 1.1, 1.4], [3.4, 6.2, 4.1], 3)] + + results_pearsonr = get_sample_r_and_p_values(this_trait=this_trait, this_dataset={ + }, target_vals=target_vals, target_dataset=target_dataset, type="pearson") + results_spearmanr = get_sample_r_and_p_values(this_trait=this_trait, this_dataset={ + }, target_vals=target_vals, target_dataset=target_dataset, type="spearman") + results_num_overlap = get_sample_r_and_p_values(this_trait=this_trait, this_dataset={ + }, target_vals=target_vals, target_dataset=target_dataset, type="pearson") + expected_pearsonr = [-0.21618688834430866, 0.680771605997119, 6] + expected_spearmanr = [-0.11595420713048969, 0.826848213385815, 6] + for i, val in enumerate(expected_pearsonr): + self.assertAlmostEqual(val, results_pearsonr[i],4) + for i, val in enumerate(expected_spearmanr): + self.assertAlmostEqual(val, results_spearmanr[i],4) + self.assertEqual(results_num_overlap, None) + + @mock.patch("wqflask.api.correlation.do_literature_correlation_for_all_traits") + def test_calculate_results(self, literature_correlation): + + literature_correlation.return_value = { + 'TT-1': ['GH-1', 0], 'TT-2': ['GH-2', 3], 'TT-3': ['GH-3', 1]} + + this_dataset = MockDataset( + {"group": AttributeSetter({"species": "rat"})}) + target_dataset = MockDataset( + {"group": AttributeSetter({"species": "rat"})}) + this_trait = AttributeSetter({"geneid": "GH-1"}) + corr_params = {"type": "literature"} + sorted_results = calculate_results( + this_trait=this_trait, this_dataset=this_dataset, target_dataset=target_dataset, corr_params=corr_params) + expected_results = {'TT-2': ['GH-2', 3], + 'TT-3': ['GH-3', 1], 'TT-1': ['GH-1', 0]} + + self.assertTrue(isinstance(sorted_results, OrderedDict)) + self.assertEqual(dict(sorted_results), expected_results) diff --git a/wqflask/tests/unit/wqflask/api/test_mapping.py b/wqflask/tests/unit/wqflask/api/test_mapping.py new file mode 100644 index 00000000..b094294a --- /dev/null +++ b/wqflask/tests/unit/wqflask/api/test_mapping.py @@ -0,0 +1,108 @@ +import unittest +from unittest import mock +from wqflask.api.mapping import initialize_parameters +from wqflask.api.mapping import do_mapping_for_api + + +class AttributeSetter: + def __init__(self, obj): + for key, value in obj.items(): + setattr(self, key, value) + + +class MockGroup(AttributeSetter): + def get_marker(self): + self.markers = [] + + +class TestMapping(unittest.TestCase): + + def test_initialize_parameters(self): + expected_results = { + "format": "json", + "limit_to": False, + "mapping_method": "gemma", + "maf": 0.01, + "use_loco": True, + "num_perm": 0, + "perm_check": False + } + + results = initialize_parameters( + start_vars={}, dataset={}, this_trait={}) + self.assertEqual(results, expected_results) + + start_vars = { + "format": "F1", + "limit_to": "1", + "mapping_method": "rqtl", + "control_marker": True, + "pair_scan": "true", + "interval_mapping": "true", + "use_loco": "true", + "num_perm": "14" + + } + + results_2 = initialize_parameters( + start_vars=start_vars, dataset={}, this_trait={}) + expected_results = { + "format": "F1", + "limit_to": 1, + "mapping_method": "gemma", + "maf": 0.01, + "use_loco": True, + "num_perm": 14, + "perm_check": "ON" + } + + self.assertEqual(results_2, expected_results) + + @mock.patch("wqflask.api.mapping.rqtl_mapping.run_rqtl_geno") + @mock.patch("wqflask.api.mapping.gemma_mapping.run_gemma") + @mock.patch("wqflask.api.mapping.initialize_parameters") + @mock.patch("wqflask.api.mapping.retrieve_sample_data") + @mock.patch("wqflask.api.mapping.create_trait") + @mock.patch("wqflask.api.mapping.data_set.create_dataset") + def test_do_mapping_for_api(self, mock_create_dataset, mock_trait, mock_retrieve_sample, mock_param, run_gemma, run_rqtl_geno): + start_vars = { + "db": "Temp", + "trait_id": "dewf3232rff2", + "format": "F1", + "mapping_method": "gemma", + "use_loco": True + + } + sampleList = ["S1", "S2", "S3", "S4"] + samplelist = ["S1", "S2", "S4"] + dataset = AttributeSetter({"group": samplelist}) + this_trait = AttributeSetter({}) + trait_data = AttributeSetter({ + "data": { + "item1": AttributeSetter({"name": "S1", "value": "S1_value"}), + "item2": AttributeSetter({"name": "S2", "value": "S2_value"}), + "item3": AttributeSetter({"name": "S3", "value": "S3_value"}), + + } + }) + trait = AttributeSetter({ + "data": trait_data + }) + + dataset.return_value = dataset + mock_trait.return_value = this_trait + + mock_retrieve_sample.return_value = trait + mock_param.return_value = { + "format": "F1", + "limit_to": False, + "mapping_method": "gemma", + "maf": 0.01, + "use_loco": "True", + "num_perm": 14, + "perm_check": "ON" + } + + run_gemma.return_value = ["results"] + results = do_mapping_for_api(start_vars=start_vars) + self.assertEqual(results, ("results", None)) diff --git a/wqflask/tests/unit/wqflask/correlation/__init__.py b/wqflask/tests/unit/wqflask/correlation/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/wqflask/tests/unit/wqflask/correlation/__init__.py diff --git a/wqflask/tests/unit/wqflask/correlation/test_correlation_functions.py b/wqflask/tests/unit/wqflask/correlation/test_correlation_functions.py new file mode 100644 index 00000000..44d2e0fc --- /dev/null +++ b/wqflask/tests/unit/wqflask/correlation/test_correlation_functions.py @@ -0,0 +1,20 @@ +import unittest +from unittest import mock +from wqflask.correlation.correlation_functions import get_trait_symbol_and_tissue_values +from wqflask.correlation.correlation_functions import cal_zero_order_corr_for_tiss + + +class TestCorrelationFunctions(unittest.TestCase): + + @mock.patch("wqflask.correlation.correlation_functions.MrnaAssayTissueData") + def test_get_trait_symbol_and_tissue_values(self, mock_class): + """test for getting trait symbol and tissue_values""" + mock_class_instance = mock_class.return_value + mock_class_instance.gene_symbols = ["k1", "k2", "k3"] + mock_class_instance.get_symbol_values_pairs.return_value = { + "k1": ["v1", "v2", "v3"], "k2": ["v2", "v3"], "k3": ["k3"]} + results = get_trait_symbol_and_tissue_values( + symbol_list=["k1", "k2", "k3"]) + mock_class.assert_called_with(gene_symbols=['k1', 'k2', 'k3']) + self.assertEqual({"k1": ["v1", "v2", "v3"], "k2": [ + "v2", "v3"], "k3": ["k3"]}, results) diff --git a/wqflask/tests/unit/wqflask/correlation/test_show_corr_results.py b/wqflask/tests/unit/wqflask/correlation/test_show_corr_results.py new file mode 100644 index 00000000..33601990 --- /dev/null +++ b/wqflask/tests/unit/wqflask/correlation/test_show_corr_results.py @@ -0,0 +1,98 @@ +import unittest +from unittest import mock +from wqflask.correlation.show_corr_results import get_header_fields +from wqflask.correlation.show_corr_results import generate_corr_json + + +class AttributeSetter: + def __init__(self, trait_obj): + for key, value in trait_obj.items(): + setattr(self, key, value) + + +class TestShowCorrResults(unittest.TestCase): + def test_get_header_fields(self): + expected = [ + ['Index', + 'Record', + 'Symbol', + 'Description', + 'Location', + 'Mean', + 'Sample rho', + 'N', + 'Sample p(rho)', + 'Lit rho', + 'Tissue rho', + 'Tissue p(rho)', + 'Max LRS', + 'Max LRS Location', + 'Additive Effect'], + + ['Index', + 'ID', + 'Location', + 'Sample r', + 'N', + 'Sample p(r)'] + + ] + result1 = get_header_fields("ProbeSet", "spearman") + result2 = get_header_fields("Other", "Other") + self.assertEqual(result1, expected[0]) + self.assertEqual(result2, expected[1]) + + @mock.patch("wqflask.correlation.show_corr_results.hmac.data_hmac") + def test_generate_corr_json(self, mock_data_hmac): + mock_data_hmac.return_value = "hajsdiau" + + dataset = AttributeSetter({"name": "the_name"}) + this_trait = AttributeSetter( + {"name": "trait_test", "dataset": dataset}) + target_dataset = AttributeSetter({"type": "Publish"}) + corr_trait_1 = AttributeSetter({ + "name": "trait_1", + "dataset": AttributeSetter({"name": "dataset_1"}), + "view": True, + "abbreviation": "T1", + "description_display": "Trait I description", + "authors": "JM J,JYEW", + "pubmed_id": "34n4nn31hn43", + "pubmed_text": "2016", + "pubmed_link": "https://www.load", + "lod_score": "", + "mean": "", + "LRS_location_repr": "BXBS", + "additive": "", + "sample_r": 10.5, + "num_overlap": 2, + "sample_p": 5 + + + + + }) + corr_results = [corr_trait_1] + + dataset_type_other = { + "location": "cx-3-4", + "sample_4": 12.32, + "num_overlap": 3, + "sample_p": 10.34 + } + + expected_results = '[{"index": 1, "trait_id": "trait_1", "dataset": "dataset_1", "hmac": "hajsdiau", "abbreviation_display": "T1", "description": "Trait I description", "mean": "N/A", "authors_display": "JM J,JYEW", "additive": "N/A", "pubmed_id": "34n4nn31hn43", "year": "2016", "lod_score": "N/A", "lrs_location": "BXBS", "sample_r": "10.500", "num_overlap": 2, "sample_p": "5.000e+00"}]' + + results1 = generate_corr_json(corr_results=corr_results, this_trait=this_trait, + dataset=dataset, target_dataset=target_dataset, for_api=True) + self.assertEqual(expected_results, results1) + + def test_generate_corr_json_view_false(self): + trait = AttributeSetter({"view": False}) + corr_results = [trait] + this_trait = AttributeSetter({"name": "trait_test"}) + dataset = AttributeSetter({"name": "the_name"}) + + results_where_view_is_false = generate_corr_json( + corr_results=corr_results, this_trait=this_trait, dataset={}, target_dataset={}, for_api=False) + self.assertEqual(results_where_view_is_false, "[]") 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 b8c13ab4..fe2569b8 100644 --- a/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py +++ b/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py @@ -5,7 +5,6 @@ from unittest import mock from wqflask.marker_regression.gemma_mapping import run_gemma from wqflask.marker_regression.gemma_mapping import gen_pheno_txt_file from wqflask.marker_regression.gemma_mapping import gen_covariates_file -from wqflask.marker_regression.gemma_mapping import parse_gemma_output from wqflask.marker_regression.gemma_mapping import parse_loco_output @@ -69,11 +68,9 @@ class TestGemmaMapping(unittest.TestCase): mock_parse_loco.return_value = [] results = run_gemma(this_trait=trait, this_dataset=dataset, samples=[ ], vals=[], covariates="", use_loco=True) - system_calls = [mock.call('ghc --json -- -debug -g /home/genotype/bimbam/file_geno.txt -p /home/user/data//gn2/trait1_dataset1_name_pheno.txt -a /home/genotype/bimbam/file_snps.txt -gk > /home/user/data//gn2/GP1_K_RRRRRR.json'), - mock.call('ghc --json --input /home/user/data//gn2/GP1_K_RRRRRR.json -- -debug -a /home/genotype/bimbam/file_snps.txt -lmm 2 -g /home/genotype/bimbam/file_geno.txt -p /home/user/data//gn2/trait1_dataset1_name_pheno.txt > /home/user/data//gn2/GP1_GWA_RRRRRR.json')] - mock_os.system.assert_has_calls(system_calls) + self.assertEqual(mock_os.system.call_count,2) mock_gen_pheno_txt.assert_called_once() - mock_parse_loco.assert_called_once_with(dataset, "GP1_GWA_RRRRRR") + mock_parse_loco.assert_called_once_with(dataset, "GP1_GWA_RRRRRR",True) mock_os.path.isfile.assert_called_once_with( ('/home/user/imgfile_output.assoc.txt')) self.assertEqual(mock_flat_files.call_count, 4) @@ -138,31 +135,6 @@ class TestGemmaMapping(unittest.TestCase): filehandler.write.assert_has_calls([mock.call( '-9\t'), mock.call('-9\t'), mock.call('-9\t'), mock.call('-9\t'), mock.call('\n')]) - @mock.patch("wqflask.marker_regression.gemma_mapping.webqtlConfig.GENERATED_IMAGE_DIR", "/home/user/img/") - def test_parse_gemma_output(self): - """add test for generating gemma output with obj returned""" - file = """X/Y\t gn2\t21\tQ\tE\tA\tP\tMMB\tCDE\t0.5 -X/Y\tgn2\t21322\tQ\tE\tA\tP\tMMB\tCDE\t0.5 -chr\tgn1\t12312\tQ\tE\tA\tP\tMMB\tCDE\t0.7 -X\tgn7\t2324424\tQ\tE\tA\tP\tMMB\tCDE\t0.4 -125\tgn9\t433575\tQ\tE\tA\tP\tMMB\tCDE\t0.67 -""" - with mock.patch("builtins.open", mock.mock_open(read_data=file)) as mock_open: - results = parse_gemma_output(genofile_name="gema_file") - expected = [{'name': ' gn2', 'chr': 'X/Y', 'Mb': 2.1e-05, 'p_value': 0.5, 'lod_score': 0.3010299956639812}, {'name': 'gn2', 'chr': 'X/Y', 'Mb': 0.021322, 'p_value': 0.5, 'lod_score': 0.3010299956639812}, - {'name': 'gn7', 'chr': 'X', 'Mb': 2.324424, 'p_value': 0.4, 'lod_score': 0.3979400086720376}, {'name': 'gn9', 'chr': 125, 'Mb': 0.433575, 'p_value': 0.67, 'lod_score': 0.17392519729917352}] - mock_open.assert_called_once_with( - "/home/user/img/gema_file_output.assoc.txt") - self.assertEqual(results, expected) - - @mock.patch("wqflask.marker_regression.gemma_mapping.webqtlConfig.GENERATED_IMAGE_DIR", "/home/user/img") - def test_parse_gemma_output_with_empty_return(self): - """add tests for parse gemma output where nothing returned""" - output_file_results = """chr\t today""" - with mock.patch("builtins.open", mock.mock_open(read_data=output_file_results)) as mock_open: - results = parse_gemma_output(genofile_name="gema_file") - self.assertEqual(results, []) - @mock.patch("wqflask.marker_regression.gemma_mapping.TEMPDIR", "/home/tmp") @mock.patch("wqflask.marker_regression.gemma_mapping.os") @mock.patch("wqflask.marker_regression.gemma_mapping.json") @@ -172,21 +144,26 @@ X\tgn7\t2324424\tQ\tE\tA\tP\tMMB\tCDE\t0.4 "files": [["file_name", "user", "~/file1"], ["file_name", "user", "~/file2"]] } - return_file_1 = """X/Y\t L1\t21\tQ\tE\tA\tP\tMMB\tCDE\t0.5 -X/Y\tL2\t21322\tQ\tE\tA\tP\tMMB\tCDE\t0.5 -chr\tL3\t12312\tQ\tE\tA\tP\tMMB\tCDE\t0.7""" - return_file_2 = """chr\tother\t21322\tQ\tE\tA\tP\tMMB\tCDE\t0.5""" + return_file="""X/Y\tM1\t28.457155\tQ\tE\tA\tMMB\t23.3\tW\t0.9\t0.85\t +chr4\tM2\t12\tQ\tE\tMMB\tR\t24\tW\t0.87\t0.5 +Y\tM4\t12\tQ\tE\tMMB\tR\t11.6\tW\t0.21\t0.7 +X\tM5\t12\tQ\tE\tMMB\tR\t21.1\tW\t0.65\t0.6""" + + return_file_2 = """chr\tother\t21322\tQ\tE\tA\tP\tMMB\tCDE\t0.5\t0.4""" mock_os.path.isfile.return_value = True file_to_write = """{"files":["file_1","file_2"]}""" with mock.patch("builtins.open") as mock_open: handles = (mock.mock_open(read_data="gwas").return_value, mock.mock_open( - read_data=return_file_1).return_value, mock.mock_open(read_data=return_file_2).return_value) + read_data=return_file).return_value, mock.mock_open(read_data=return_file_2).return_value) mock_open.side_effect = handles results = parse_loco_output( this_dataset={}, gwa_output_filename=".xw/") - expected_results = [{'name': ' L1', 'chr': 'X/Y', 'Mb': 2.1e-05, 'p_value': 0.5, 'lod_score': 0.3010299956639812}, { - 'name': 'L2', 'chr': 'X/Y', 'Mb': 0.021322, 'p_value': 0.5, 'lod_score': 0.3010299956639812}] + expected_results= [ + {'name': 'M1', 'chr': 'X/Y', 'Mb': 2.8457155e-05, 'p_value': 0.85, 'additive': 23.3, 'lod_score': 0.07058107428570727}, + {'name': 'M2', 'chr': 4, 'Mb': 1.2e-05, 'p_value': 0.5, 'additive': 24.0, 'lod_score': 0.3010299956639812}, + {'name': 'M4', 'chr': 'Y', 'Mb': 1.2e-05, 'p_value': 0.7, 'additive': 11.6, 'lod_score': 0.1549019599857432}, + {'name': 'M5', 'chr': 'X', 'Mb': 1.2e-05, 'p_value': 0.6, 'additive': 21.1, 'lod_score': 0.22184874961635637}] self.assertEqual(expected_results, results) diff --git a/wqflask/tests/unit/wqflask/snp_browser/__init__.py b/wqflask/tests/unit/wqflask/snp_browser/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/wqflask/tests/unit/wqflask/snp_browser/__init__.py diff --git a/wqflask/tests/unit/wqflask/snp_browser/test_snp_browser.py b/wqflask/tests/unit/wqflask/snp_browser/test_snp_browser.py new file mode 100644 index 00000000..ce3e7b83 --- /dev/null +++ b/wqflask/tests/unit/wqflask/snp_browser/test_snp_browser.py @@ -0,0 +1,105 @@ +import unittest +from unittest import mock +from wqflask import app +from wqflask.snp_browser.snp_browser import get_gene_id +from wqflask.snp_browser.snp_browser import get_gene_id_name_dict +from wqflask.snp_browser.snp_browser import check_if_in_gene +from wqflask.snp_browser.snp_browser import get_browser_sample_lists +from wqflask.snp_browser.snp_browser import get_header_list + + +class TestSnpBrowser(unittest.TestCase): + def setUp(self): + self.app_context = app.app_context() + self.app_context.push() + + def tearDown(self): + self.app_context.pop() + + def test_get_header_list(self): + empty_columns = {"snp_source": "false", "conservation_score": "true", "gene_name": "false", + "transcript": "false", "exon": "false", "domain_2": "true", "function": "false", "function_details": "true"} + strains = {"mouse": ["S1", "S2", "S3", "S4", "S5"], "rat": []} + expected_results = ([['Index', 'SNP ID', 'Chr', 'Mb', 'Alleles', 'ConScore', + 'Domain 1', 'Domain 2', 'Details'], + ['S1', 'S2', 'S3', 'S4', 'S5']], 5, + ['index', 'snp_name', 'chr', 'mb_formatted', 'alleles', + 'conservation_score', 'domain_1', 'domain_2', + 'function_details', 'S1', 'S2', 'S3', 'S4', 'S5']) + + results_with_snp = get_header_list( + variant_type="SNP", strains=strains, species="Mouse", empty_columns=empty_columns) + results_with_indel = get_header_list( + variant_type="InDel", strains=strains, species="rat", empty_columns=[]) + expected_results_with_indel = ( + ['Index', 'ID', 'Type', 'InDel Chr', 'Mb Start', + 'Mb End', 'Strand', 'Size', 'Sequence', 'Source'], 0, + ['index', 'indel_name', 'indel_type', 'indel_chr', 'indel_mb_s', + 'indel_mb_e', 'indel_strand', 'indel_size', 'indel_sequence', 'source_name']) + + self.assertEqual(expected_results, results_with_snp) + self.assertEqual(expected_results_with_indel, results_with_indel) + + @mock.patch("wqflask.snp_browser.snp_browser.g") + def test_get_gene_id(self, mock_db): + mock_db.db.execute.return_value.fetchone.return_value = "517d729f-aa13-4413-a885-40a3f7ff768a" + db_query_value = """ + SELECT + geneId + FROM + GeneList + WHERE + SpeciesId = c9c0f59e-1259-4cba-91e6-831ef1a99c83 AND geneSymbol = 'INSR' + """ + results = get_gene_id( + species_id="c9c0f59e-1259-4cba-91e6-831ef1a99c83", gene_name="INSR") + mock_db.db.execute.assert_called_once_with(db_query_value) + self.assertEqual(results, "517d729f-aa13-4413-a885-40a3f7ff768a") + + @mock.patch("wqflask.snp_browser.snp_browser.g") + def test_gene_id_name_dict(self, mock_db): + no_gene_names = [] + self.assertEqual("", get_gene_id_name_dict( + species_id="fregb343bui43g4", gene_name_list=no_gene_names)) + gene_name_list = ["GH1", "GH2", "GH3"] + mock_db.db.execute.return_value.fetchall.side_effect = [[], [("fsdf43-fseferger-f22", "GH1"), ("1sdf43-fsewferger-f22", "GH2"), + ("fwdj43-fstferger-f22", "GH3")]] + no_results = get_gene_id_name_dict( + species_id="ret3-32rf32", gene_name_list=gene_name_list) + results_found = get_gene_id_name_dict( + species_id="ret3-32rf32", gene_name_list=gene_name_list) + expected_found = {'GH1': 'fsdf43-fseferger-f22', + 'GH2': '1sdf43-fsewferger-f22', 'GH3': 'fwdj43-fstferger-f22'} + db_query_value = """ + SELECT + geneId, geneSymbol + FROM + GeneList + WHERE + SpeciesId = ret3-32rf32 AND geneSymbol in ('GH1','GH2','GH3') + """ + mock_db.db.execute.assert_called_with(db_query_value) + self.assertEqual(results_found, expected_found) + self.assertEqual(no_results, {}) + + @mock.patch("wqflask.snp_browser.snp_browser.g") + def test_check_if_in_gene(self, mock_db): + mock_db.db.execute.return_value.fetchone.side_effect = [ + ("fsdf-232sdf-sdf", "GHA"), ""] + results_found = check_if_in_gene( + species_id="517d729f-aa13-4413-a885-40a3f7ff768a", chr="CH1", mb=12.09) + db_query_value = """SELECT geneId, geneSymbol + FROM GeneList + WHERE SpeciesId = 517d729f-aa13-4413-a885-40a3f7ff768a AND chromosome = 'CH1' AND + (txStart < 12.09 AND txEnd > 12.09); """ + gene_not_found = check_if_in_gene( + species_id="517d729f-aa13-4413-a885-40a3f7ff768a", chr="CH1", mb=12.09) + mock_db.db.execute.assert_called_with(db_query_value) + self.assertEqual(gene_not_found, "") + + @mock.patch("wqflask.snp_browser.snp_browser.g") + def test_get_browser_sample_lists(self, mock_db): + mock_db.db.execute.return_value.fetchall.return_value = [] + + results = get_browser_sample_lists(species_id="12") + self.assertEqual(results, {'mouse': [], 'rat': []}) diff --git a/wqflask/tests/unit/wqflask/test_server_side.py b/wqflask/tests/unit/wqflask/test_server_side.py new file mode 100644 index 00000000..4f91d8ca --- /dev/null +++ b/wqflask/tests/unit/wqflask/test_server_side.py @@ -0,0 +1,31 @@ +import unittest + +from wqflask.server_side import ServerSideTable + + +class TestServerSideTableTests(unittest.TestCase): + """ + Test the ServerSideTable class + + test table: + first, second, third + 'd', 4, 'zz' + 'b', 2, 'aa' + 'c', 1, 'ss' + """ + + def test_get_page(self): + rows_count = 3 + table_rows = [ + {'first': 'd', 'second': 4, 'third': 'zz'}, + {'first': 'b', 'second': 2, 'third': 'aa'}, + {'first': 'c', 'second': 1, 'third': 'ss'}, + ] + headers = ['first', 'second', 'third'] + request_args = {'sEcho': '1', 'iSortCol_0': '1', 'iSortingCols': '1', 'sSortDir_0': 'asc', 'iDisplayStart': '0', 'iDisplayLength': '3'} + + test_page = ServerSideTable(rows_count, table_rows, headers, request_args).get_page() + self.assertEqual(test_page['sEcho'], '1') + self.assertEqual(test_page['iTotalRecords'], 'nan') + self.assertEqual(test_page['iTotalDisplayRecords'], '3') + self.assertEqual(test_page['data'], [{'first': 'b', 'second': 2, 'third': 'aa'}, {'first': 'c', 'second': 1, 'third': 'ss'}, {'first': 'd', 'second': 4, 'third': 'zz'}]) diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py index 68ef0f04..65df59c3 100644 --- a/wqflask/utility/tools.py +++ b/wqflask/utility/tools.py @@ -267,7 +267,7 @@ 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://orcid.org/oauth/authorize?response_type=code&scope=/authenticate&show_login=true&client_id=" + \ - ORCID_CLIENT_ID+"&client_secret="+ORCID_CLIENT_SECRET + ORCID_CLIENT_ID+"&client_secret="+ORCID_CLIENT_SECRET + "&redirect_uri=" + GN2_BRANCH_URL + "n/login/orcid_oauth2" ORCID_TOKEN_URL = get_setting('ORCID_TOKEN_URL') ELASTICSEARCH_HOST = get_setting('ELASTICSEARCH_HOST') diff --git a/wqflask/utility/type_checking.py b/wqflask/utility/type_checking.py index f15b17e2..6b029317 100644 --- a/wqflask/utility/type_checking.py +++ b/wqflask/utility/type_checking.py @@ -23,20 +23,20 @@ def is_str(value): except: return False -def get_float(vars,name,default=None): - if name in vars: - if is_float(vars[name]): - return float(vars[name]) +def get_float(vars_obj,name,default=None): + if name in vars_obj: + if is_float(vars_obj[name]): + return float(vars_obj[name]) return default -def get_int(vars,name,default=None): - if name in vars: - if is_int(vars[name]): - return float(vars[name]) +def get_int(vars_obj,name,default=None): + if name in vars_obj: + if is_int(vars_obj[name]): + return float(vars_obj[name]) return default -def get_string(vars,name,default=None): - if name in vars: - if not vars[name] is None: - return str(vars[name]) +def get_string(vars_obj,name,default=None): + if name in vars_obj: + if not vars_obj[name] is None: + return str(vars_obj[name]) return default diff --git a/wqflask/wqflask/correlation/correlation_functions.py b/wqflask/wqflask/correlation/correlation_functions.py index b883e361..fd7691d4 100644 --- a/wqflask/wqflask/correlation/correlation_functions.py +++ b/wqflask/wqflask/correlation/correlation_functions.py @@ -71,34 +71,6 @@ def cal_zero_order_corr_for_tiss (primaryValue=[], targetValue=[], method='pears return corr_result -########################################################################### -#Input: cursor, symbolList (list), dataIdDict(Dict) -#output: symbolValuepairDict (dictionary):one dictionary of Symbol and Value Pair, -# key is symbol, value is one list of expression values of one probeSet; -#function: get one dictionary whose key is gene symbol and value is tissue expression data (list type). -#Attention! All keys are lower case! -########################################################################### -def get_symbol_value_pairs(tissue_data): - id_list = [tissue_data[symbol.lower()].data_id for item in tissue_data] - - symbol_value_pairs = {} - value_list=[] - - query = """SELECT value, id - FROM TissueProbeSetData - WHERE Id IN {}""".format(create_in_clause(id_list)) - - try : - results = g.db.execute(query).fetchall() - for result in results: - value_list.append(result.value) - symbol_value_pairs[symbol] = value_list - except: - symbol_value_pairs[symbol] = None - - return symbol_value_pairs - - ######################################################################################################## #input: cursor, symbolList (list), dataIdDict(Dict): key is symbol #output: SymbolValuePairDict(dictionary):one dictionary of Symbol and Value Pair. diff --git a/wqflask/wqflask/correlation/show_corr_results.py b/wqflask/wqflask/correlation/show_corr_results.py index be983c87..a36b947c 100644 --- a/wqflask/wqflask/correlation/show_corr_results.py +++ b/wqflask/wqflask/correlation/show_corr_results.py @@ -71,7 +71,6 @@ class CorrelationResults(object): assert('corr_sample_method' in start_vars) assert('corr_samples_group' in start_vars) assert('corr_dataset' in start_vars) - #assert('min_expr' in start_vars) assert('corr_return_results' in start_vars) if 'loc_chr' in start_vars: assert('min_loc_mb' in start_vars) @@ -195,15 +194,15 @@ class CorrelationResults(object): if (float(self.correlation_data[trait][0]) >= self.p_range_lower and float(self.correlation_data[trait][0]) <= self.p_range_upper): - if self.target_dataset.type == "ProbeSet" or self.target_dataset.type == "Geno": - + if (self.target_dataset.type == "ProbeSet" or self.target_dataset.type == "Publish") and bool(trait_object.mean): if (self.min_expr != None) and (float(trait_object.mean) < self.min_expr): continue - elif range_chr_as_int != None and (chr_as_int != range_chr_as_int): + if self.target_dataset.type == "ProbeSet" or self.target_dataset.type == "Geno": + if range_chr_as_int != None and (chr_as_int != range_chr_as_int): continue - elif (self.min_location_mb != None) and (float(trait_object.mb) < float(self.min_location_mb)): + if (self.min_location_mb != None) and (float(trait_object.mb) < float(self.min_location_mb)): continue - elif (self.max_location_mb != None) and (float(trait_object.mb) > float(self.max_location_mb)): + if (self.max_location_mb != None) and (float(trait_object.mb) > float(self.max_location_mb)): continue (trait_object.sample_r, @@ -517,6 +516,7 @@ def generate_corr_json(corr_results, this_trait, dataset, target_dataset, for_ap elif target_dataset.type == "Publish": results_dict['abbreviation_display'] = "N/A" results_dict['description'] = "N/A" + results_dict['mean'] = "N/A" results_dict['authors_display'] = "N/A" results_dict['additive'] = "N/A" if for_api: @@ -530,6 +530,8 @@ def generate_corr_json(corr_results, this_trait, dataset, target_dataset, for_ap results_dict['abbreviation_display'] = trait.abbreviation if bool(trait.description_display): results_dict['description'] = trait.description_display + if bool(trait.mean): + results_dict['mean'] = f"{float(trait.mean):.3f}" if bool(trait.authors): authors_list = trait.authors.split(',') if len(authors_list) > 6: @@ -603,6 +605,7 @@ def get_header_fields(data_type, corr_method): 'Record', 'Abbreviation', 'Description', + 'Mean', 'Authors', 'Year', 'Sample rho', @@ -616,6 +619,7 @@ def get_header_fields(data_type, corr_method): 'Record', 'Abbreviation', 'Description', + 'Mean', 'Authors', 'Year', 'Sample r', diff --git a/wqflask/wqflask/marker_regression/gemma_mapping.py b/wqflask/wqflask/marker_regression/gemma_mapping.py index 630a3afa..ab3a7278 100644 --- a/wqflask/wqflask/marker_regression/gemma_mapping.py +++ b/wqflask/wqflask/marker_regression/gemma_mapping.py @@ -54,7 +54,7 @@ def run_gemma(this_trait, this_dataset, samples, vals, covariates, use_loco, maf TEMPDIR, trait_filename) if covariates != "": - gemma_command += ' -c %s/%s_covariates.txt -a %s/%s_snps.txt -lmm 2 -maf %s > %s/gn2/%s.json' % (flat_files('mapping'), + gemma_command += ' -c %s/%s_covariates.txt -a %s/%s_snps.txt -lmm 9 -maf %s > %s/gn2/%s.json' % (flat_files('mapping'), this_dataset.group.name, flat_files('genotype/bimbam'), genofile_name, @@ -62,7 +62,7 @@ def run_gemma(this_trait, this_dataset, samples, vals, covariates, use_loco, maf TEMPDIR, gwa_output_filename) else: - gemma_command += ' -a %s/%s_snps.txt -lmm 2 -maf %s > %s/gn2/%s.json' % (flat_files('genotype/bimbam'), + gemma_command += ' -a %s/%s_snps.txt -lmm 9 -maf %s > %s/gn2/%s.json' % (flat_files('genotype/bimbam'), genofile_name, maf, TEMPDIR, @@ -184,11 +184,8 @@ def parse_loco_output(this_dataset, gwa_output_filename, loco="True"): else: marker['chr'] = line.split("\t")[0] marker['Mb'] = float(line.split("\t")[2]) / 1000000 - if loco == "True": - marker['p_value'] = float(line.split("\t")[9]) - else: - marker['p_value'] = float(line.split("\t")[10]) - marker['additive'] = float(line.split("\t")[7]) + marker['p_value'] = float(line.split("\t")[10]) + marker['additive'] = float(line.split("\t")[7]) if math.isnan(marker['p_value']) or (marker['p_value'] <= 0): marker['lod_score'] = 0 #marker['lrs_value'] = 0 diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py index 0d0894a4..c4ea2921 100644 --- a/wqflask/wqflask/search_results.py +++ b/wqflask/wqflask/search_results.py @@ -28,9 +28,9 @@ class SearchResultPage(object): #maxReturn = 3000 def __init__(self, kw): - """This class gets invoked after hitting submit on the main menu (in -views.py). - + """ + This class gets invoked after hitting submit on the main menu (in + views.py). """ ########################################### @@ -86,7 +86,6 @@ views.py). else: self.gen_search_result() - def gen_search_result(self): """ Get the info displayed in the search result table from the set of results computed in @@ -161,7 +160,14 @@ views.py). trait_dict[key] = trait_dict[key].decode('utf-8') trait_list.append(trait_dict) - self.trait_list = json.dumps(trait_list) + self.trait_list = trait_list + + if this_trait.dataset.type == "ProbeSet": + self.header_data_names = ['index', 'display_name', 'symbol', 'description', 'location', 'mean', 'lrs_score', 'lrs_location', 'additive'] + elif this_trait.dataset.type == "Publish": + self.header_data_names = ['index', 'display_name', 'description', 'mean', 'authors', 'pubmed_text', 'lrs_score', 'lrs_location', 'additive'] + elif this_trait.dataset.type == "Geno": + self.header_data_names = ['index', 'display_name', 'location'] def search(self): """ diff --git a/wqflask/wqflask/server_side.py b/wqflask/wqflask/server_side.py new file mode 100644 index 00000000..5f764767 --- /dev/null +++ b/wqflask/wqflask/server_side.py @@ -0,0 +1,93 @@ +# handles server side table processing + + + +class ServerSideTable(object): + """ + This class is used to do server-side processing + on the DataTables table such as paginating, sorting, + filtering(not implemented) etc. This takes the load off + the client-side and reduces the size of data interchanged. + + Usage: + ServerSideTable(table_data, request_values) + where, + `table_data` must have data members + `rows_count` as number of rows in the table, + `table_rows` as data rows of the table, + `header_data_names` as headers names of the table. + + `request_values` must have request arguments values + including the DataTables server-side processing arguments. + + Have a look at snp_browser_table() function in + wqflask/wqflask/views.py for reference use. + """ + + def __init__(self, rows_count, table_rows, header_data_names, request_values): + self.request_values = request_values + self.sEcho = self.request_values['sEcho'] + + self.rows_count = rows_count + self.table_rows = table_rows + self.header_data_names = header_data_names + + self.sort_rows() + self.paginate_rows() + + def sort_rows(self): + """ + Sorts the rows taking in to account the column (or columns) that the + user has selected. + """ + def is_reverse(str_direction): + """ Maps the 'desc' and 'asc' words to True or False. """ + return True if str_direction == 'desc' else False + + if (self.request_values['iSortCol_0'] != "") and (int(self.request_values['iSortingCols']) > 0): + for i in range(0, int(self.request_values['iSortingCols'])): + column_number = int(self.request_values['iSortCol_' + str(i)]) + column_name = self.header_data_names[column_number - 1] + sort_direction = self.request_values['sSortDir_' + str(i)] + self.table_rows = sorted(self.table_rows, + key=lambda x: x[column_name], + reverse=is_reverse(sort_direction)) + + def paginate_rows(self): + """ + Selects a subset of the filtered and sorted data based on if the table + has pagination, the current page and the size of each page. + """ + def requires_pagination(): + """ Check if the table is going to be paginated """ + if self.request_values['iDisplayStart'] != "": + if int(self.request_values['iDisplayLength']) != -1: + return True + return False + + if not requires_pagination(): + return + + start = int(self.request_values['iDisplayStart']) + length = int(self.request_values['iDisplayLength']) + + # if search returns only one page + if len(self.table_rows) <= length: + # display only one page + self.table_rows = self.table_rows[start:] + else: + limit = -len(self.table_rows) + start + length + if limit < 0: + # display pagination + self.table_rows = self.table_rows[start:limit] + else: + # display last page of pagination + self.table_rows = self.table_rows[start:] + + def get_page(self): + output = {} + output['sEcho'] = str(self.sEcho) + output['iTotalRecords'] = str(float('Nan')) + output['iTotalDisplayRecords'] = str(self.rows_count) + output['data'] = self.table_rows + return output diff --git a/wqflask/wqflask/snp_browser/snp_browser.py b/wqflask/wqflask/snp_browser/snp_browser.py index 6c3fcf53..a52399a2 100644 --- a/wqflask/wqflask/snp_browser/snp_browser.py +++ b/wqflask/wqflask/snp_browser/snp_browser.py @@ -4,7 +4,7 @@ import string from PIL import (Image) from utility.logger import getLogger -logger = getLogger(__name__ ) +logger = getLogger(__name__) from base import species from base import webqtlConfig @@ -14,21 +14,21 @@ class SnpBrowser(object): def __init__(self, start_vars): self.strain_lists = get_browser_sample_lists() self.initialize_parameters(start_vars) - self.limit_number = 10000 if self.first_run == "false": self.filtered_results = self.get_browser_results() + self.table_rows = self.get_table_rows() + self.rows_count = len(self.table_rows) - if len(self.filtered_results) <= self.limit_number: - self.table_rows = self.get_table_rows() - else: - self.empty_columns = None + del self.filtered_results + + if 'sEcho' not in start_vars: self.table_rows = [] if self.limit_strains == "true": - self.header_fields, self.empty_field_count = get_header_list(variant_type = self.variant_type, strains = self.chosen_strains, empty_columns = self.empty_columns) + self.header_fields, self.empty_field_count, self.header_data_names = get_header_list(variant_type = self.variant_type, strains = self.chosen_strains, empty_columns = self.empty_columns) else: - self.header_fields, self.empty_field_count = get_header_list(variant_type = self.variant_type, strains = self.strain_lists, species = self.species_name, empty_columns = self.empty_columns) + self.header_fields, self.empty_field_count, self.header_data_names = get_header_list(variant_type = self.variant_type, strains = self.strain_lists, species = self.species_name, empty_columns = self.empty_columns) def initialize_parameters(self, start_vars): if 'first_run' in start_vars: @@ -368,19 +368,19 @@ class SnpBrowser(object): #ZS: list of booleans representing which columns are entirely empty, so they aren't displayed on the page; only including ones that are sometimes empty (since there's always a location, etc) self.empty_columns = { - "snp_source": "false", - "conservation_score": "false", - "gene_name": "false", - "transcript": "false", - "exon": "false", - "domain_2": "false", - "function": "false", - "function_details": "false" - } + "snp_source": "false", + "conservation_score": "false", + "gene_name": "false", + "transcript": "false", + "exon": "false", + "domain_2": "false", + "function": "false", + "function_details": "false" + } the_rows = [] for i, result in enumerate(self.filtered_results): - this_row = [] + this_row = {} if self.variant_type == "SNP": snp_name, rs, chr, mb, alleles, gene, transcript, exon, domain, function, function_details, snp_source, conservation_score, snp_id = result[:14] allele_value_list = result[14:] @@ -520,13 +520,10 @@ class SnpBrowser(object): "source_name": str(source_name) } #this_row = [indel_name, indel_chr, indel_mb_s, indel_mb_e, indel_strand, indel_type, indel_size, indel_sequence, source_name] - else: - this_row = {} the_rows.append(this_row) return the_rows - def include_record(self, domain, function, snp_source, conservation_score): """ Decide whether to add this record """ @@ -674,9 +671,13 @@ def get_header_list(variant_type, strains, species = None, empty_columns = None) empty_field_count = 0 #ZS: This is an awkward way of letting the javascript know the index where the allele value columns start; there's probably a better way of doing this header_fields = [] + header_data_names = [] if variant_type == "SNP": header_fields.append(['Index', 'SNP ID', 'Chr', 'Mb', 'Alleles', 'Source', 'ConScore', 'Gene', 'Transcript', 'Exon', 'Domain 1', 'Domain 2', 'Function', 'Details']) + header_data_names = ['index', 'snp_name', 'chr', 'mb_formatted', 'alleles', 'snp_source', 'conservation_score', 'gene_name', 'transcript', 'exon', 'domain_1', 'domain_2', 'function', 'function_details'] + header_fields.append(strain_list) + header_data_names += strain_list if empty_columns != None: if empty_columns['snp_source'] == "false": @@ -703,11 +704,16 @@ def get_header_list(variant_type, strains, species = None, empty_columns = None) if empty_columns['function_details'] == "false": empty_field_count += 1 header_fields[0].remove('Details') + + for col in empty_columns.keys(): + if empty_columns[col] == "false": + header_data_names.remove(col) elif variant_type == "InDel": header_fields = ['Index', 'ID', 'Type', 'InDel Chr', 'Mb Start', 'Mb End', 'Strand', 'Size', 'Sequence', 'Source'] + header_data_names = ['index', 'indel_name', 'indel_type', 'indel_chr', 'indel_mb_s', 'indel_mb_e', 'indel_strand', 'indel_size', 'indel_sequence', 'source_name'] - return header_fields, empty_field_count + return header_fields, empty_field_count, header_data_names def get_effect_details_by_category(effect_name = None, effect_value = None): gene_list = [] @@ -868,8 +874,6 @@ def get_gene_id_name_dict(species_id, gene_name_list): if len(results) > 0: for item in results: gene_id_name_dict[item[1]] = item[0] - else: - pass return gene_id_name_dict @@ -883,7 +887,7 @@ def check_if_in_gene(species_id, chr, mb): query = """SELECT geneId,geneSymbol FROM GeneList WHERE chromosome = '{0}' AND - (txStart < {1} AND txEnd > {1}); """.format(species_id, chr, mb) + (txStart < {1} AND txEnd > {1}); """.format(chr, mb) result = g.db.execute(query).fetchone() diff --git a/wqflask/wqflask/static/new/css/snp_browser.css b/wqflask/wqflask/static/new/css/snp_browser.css index 30fe9a59..a7942d2a 100644 --- a/wqflask/wqflask/static/new/css/snp_browser.css +++ b/wqflask/wqflask/static/new/css/snp_browser.css @@ -6,6 +6,10 @@ table.dataTable thead th { vertical-align: bottom; } +table.dataTable tbody tr.selected { + background-color: #ffee99; +} + table.dataTable thead .sorting, table.dataTable thead .sorting_asc, table.dataTable thead .sorting_desc, @@ -18,7 +22,7 @@ table.dataTable thead .sorting_desc_disabled { table.dataTable thead th{ border-right: 1px solid white; color: white; - background-color: royalblue; + background-color: #369; } table.dataTable tbody td { diff --git a/wqflask/wqflask/static/new/javascript/draw_probability_plot.js b/wqflask/wqflask/static/new/javascript/draw_probability_plot.js index 3d756303..1b944d4f 100644 --- a/wqflask/wqflask/static/new/javascript/draw_probability_plot.js +++ b/wqflask/wqflask/static/new/javascript/draw_probability_plot.js @@ -118,7 +118,6 @@ }; }; data = [make_data('samples_primary'), make_data('samples_other')]; - console.log("THE DATA IS:", data); d3.select("#prob_plot_container svg").datum(data).call(chart); if (js_data.trait_symbol != null) { $("#prob_plot_title").html("<h3>" + js_data.trait_symbol + ": " + js_data.trait_id + "</h3>"); diff --git a/wqflask/wqflask/static/new/javascript/init_genome_browser.js b/wqflask/wqflask/static/new/javascript/init_genome_browser.js index 2552fb04..508f5bf2 100644 --- a/wqflask/wqflask/static/new/javascript/init_genome_browser.js +++ b/wqflask/wqflask/static/new/javascript/init_genome_browser.js @@ -1,5 +1,3 @@ -console.log("THE FILES:", js_data.browser_files) - snps_filename = "/browser_input?filename=" + js_data.browser_files[0] annot_filename = "/browser_input?filename=" + js_data.browser_files[1] diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 9ffef4f8..05dcfda5 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -1,4 +1,4 @@ -change_buttons = function() { +change_buttons = function(check_node = 0) { var button, buttons, item, num_checked, text, _i, _j, _k, _l, _len, _len2, _len3, _len4, _results, _results2; buttons = ["#add", "#remove"]; @@ -6,7 +6,7 @@ change_buttons = function() { table_api = $('#trait_table').DataTable(); check_cells = table_api.column(0).nodes().to$(); for (let i = 0; i < check_cells.length; i++) { - if (check_cells[i].childNodes[0].checked){ + if (check_cells[i].childNodes[check_node].checked){ num_checked += 1 } } diff --git a/wqflask/wqflask/static/new/javascript/show_trait.js b/wqflask/wqflask/static/new/javascript/show_trait.js index b71a9dd8..b8ffa8e8 100644 --- a/wqflask/wqflask/static/new/javascript/show_trait.js +++ b/wqflask/wqflask/static/new/javascript/show_trait.js @@ -94,33 +94,7 @@ add = function() { $('#add_to_collection').click(add); sample_lists = js_data.sample_lists; sample_group_types = js_data.sample_group_types; -d3.select("#select_compare_trait").on("click", (function(_this) { - return function() { - $('.scatter-matrix-container').remove(); - return open_trait_selection(); - }; -})(this)); -d3.select("#select_covariates").on("click", (function(_this) { - return function() { - return open_covariate_selection(); - }; -})(this)); -$("#remove_covariates").click(function () { - $("input[name=covariates]").val("") - $(".selected-covariates").val("") -}); -$(".select_covariates").click(function () { - open_covariate_selection(); -}); -$(".remove_covariates").click(function () { - $("input[name=covariates]").val("") - $(".selected-covariates").val("") -}); -d3.select("#clear_compare_trait").on("click", (function(_this) { - return function() { - return $('.scatter-matrix-container').remove(); - }; -})(this)); + open_trait_selection = function() { return $('#collections_holder').load('/collections/list?color_by_trait #collections_list', (function(_this) { return function() { @@ -233,6 +207,8 @@ update_histogram = function() { if ($('input[name="transform"]').val() != ""){ root.histogram_layout['xaxis']['title'] = "<b>" + js_data.unit_type + " (" + $('input[name="transform"]').val() + ")</b>" + } else { + root.histogram_layout['xaxis']['title'] = "<b>" + js_data.unit_type + "</b>" } Plotly.newPlot('histogram', root.histogram_data, root.histogram_layout, root.modebar_options); @@ -281,6 +257,8 @@ update_bar_chart = function() { if ($('input[name="transform"]').val() != ""){ root.bar_layout['yaxis']['title'] = "<b>" + js_data.unit_type + " (" + $('input[name="transform"]').val() + ")</b>" + } else { + root.bar_layout['yaxis']['title'] = "<b>" + js_data.unit_type + "</b>" } root.bar_data[0]['y'] = trait_vals @@ -322,6 +300,8 @@ update_box_plot = function() { if ($('input[name="transform"]').val() != ""){ root.box_layout['yaxis']['title'] = "<b>" + js_data.unit_type + " (" + $('input[name="transform"]').val() + ")</b>" + } else { + root.box_layout['yaxis']['title'] = "<b>" + js_data.unit_type + "</b>" } Plotly.newPlot('box_plot', root.box_data, root.box_layout, root.modebar_options) @@ -353,6 +333,8 @@ update_violin_plot = function() { if ($('input[name="transform"]').val() != ""){ root.violin_layout['yaxis']['title'] = "<b>" + js_data.unit_type + " (" + $('input[name="transform"]').val() + ")</b>" + } else { + root.violin_layout['yaxis']['title'] = "<b>" + js_data.unit_type + "</b>" } Plotly.newPlot('violin_plot', root.violin_data, root.violin_layout, root.modebar_options) @@ -475,15 +457,15 @@ edit_data_change = function() { if (is_number(sample_val) && sample_val !== "") { sample_val = parseFloat(sample_val); sample_sets[table].add_value(sample_val); - if (typeof var_nodes !== 'undefined'){ - sample_var = null; - } else { + try { sample_var = var_nodes[_j].childNodes[0].value if (is_number(sample_var)) { sample_var = parseFloat(sample_var) } else { sample_var = null; } + } catch { + sample_var = null; } sample_dict = { value: sample_val, @@ -498,6 +480,7 @@ edit_data_change = function() { } } } + } update_stat_values(sample_sets); @@ -543,6 +526,24 @@ on_corr_method_change = function() { }; $('select[name=corr_type]').change(on_corr_method_change); +on_dataset_change = function() { + let dataset_type = $('select[name=corr_dataset] option:selected').data('type'); + + if (dataset_type == "mrna_assay"){ + $('#min_expr_filter').show(); + $('#location_filter').show(); + } + else if (dataset_type == "pheno"){ + $('#min_expr_filter').show(); + $('#location_filter').hide(); + } + else { + $('#min_expr_filter').hide(); + $('#location_filter').show(); + } +} +$('select[name=corr_dataset]').change(on_dataset_change); + submit_special = function(url) { $("input[name=sample_vals]").val(JSON.stringify(fetch_sample_values())) $("#trait_data_form").attr("action", url); @@ -650,6 +651,8 @@ block_by_attribute_value = function() { this_val_node.value = "x"; } } + + edit_data_change(); }; $('#exclude_by_attr').click(block_by_attribute_value); @@ -819,6 +822,7 @@ reset_samples_table = function() { }; $('.reset').click(function() { reset_samples_table(); + $('input[name="transform"]').val(""); edit_data_change(); }); @@ -1308,7 +1312,7 @@ if (js_data.num_values < 256) { margin: { l: left_margin, r: 30, - t: 30, + t: 100, b: bottom_margin } }; diff --git a/wqflask/wqflask/static/new/javascript/stats.js b/wqflask/wqflask/static/new/javascript/stats.js index 4df03412..6c443ab3 100644 --- a/wqflask/wqflask/static/new/javascript/stats.js +++ b/wqflask/wqflask/static/new/javascript/stats.js @@ -91,8 +91,6 @@ Stats = (function() { Stats.prototype.interquartile = function() { var iq, length, q1, q3; length = this.the_values.length; - console.log("in interquartile the_values are:", this.the_values); - console.log("length is:", length); if (js_data.dataset_type == "ProbeSet" && js_data.data_scale == "linear_positive") { q1 = Math.log2(this.the_values[Math.floor(length * .25)]); q3 = Math.log2(this.the_values[Math.floor(length * .75)]); diff --git a/wqflask/wqflask/templates/correlation_page.html b/wqflask/wqflask/templates/correlation_page.html index 8e2a23fd..6188c0e7 100644 --- a/wqflask/wqflask/templates/correlation_page.html +++ b/wqflask/wqflask/templates/correlation_page.html @@ -95,14 +95,15 @@ {% elif target_dataset.type == 'Publish' %} <button class="toggle-vis" data-column="3">Abbreviation</button> <button class="toggle-vis" data-column="4">Description</button> - <button class="toggle-vis" data-column="5">Authors</button> - <button class="toggle-vis" data-column="6">Year</button> - <button class="toggle-vis" data-column="7">Sample {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}</button> - <button class="toggle-vis" data-column="8">N</button> - <button class="toggle-vis" data-column="9">Sample p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})</button> - <button class="toggle-vis" data-column="10">Peak LOD</button> - <button class="toggle-vis" data-column="11">Peak Location</button> - <button class="toggle-vis" data-column="12">Effect Size</button> + <button class="toggle-vis" data-column="5">Mean</button> + <button class="toggle-vis" data-column="6">Authors</button> + <button class="toggle-vis" data-column="7">Year</button> + <button class="toggle-vis" data-column="8">Sample {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}</button> + <button class="toggle-vis" data-column="9">N</button> + <button class="toggle-vis" data-column="10">Sample p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})</button> + <button class="toggle-vis" data-column="11">Peak LOD</button> + <button class="toggle-vis" data-column="12">Peak Location</button> + <button class="toggle-vis" data-column="13">Effect Size</button> {% else %} <button class="toggle-vis" data-column="3">Location</button> <button class="toggle-vis" data-column="4">Sample {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}</button> @@ -399,6 +400,13 @@ } }, { + 'title': "Mean", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "mean", + 'orderSequence': [ "desc", "asc"] + }, + { 'title': "Authors", 'type': "natural", 'width': "400px", @@ -514,6 +522,8 @@ } ], {% if target_dataset.type == 'Geno' %} "order": [[6, "asc" ]], + {% elif target_dataset.type == 'Publish' %} + "order": [[10, "asc" ]], {% else %} "order": [[9, "asc" ]], {% endif %} diff --git a/wqflask/wqflask/templates/mapping_results.html b/wqflask/wqflask/templates/mapping_results.html index 2fb37832..c2256e47 100644 --- a/wqflask/wqflask/templates/mapping_results.html +++ b/wqflask/wqflask/templates/mapping_results.html @@ -245,7 +245,7 @@ <button class="btn btn-default" id="select_all"><span class="glyphicon glyphicon-ok"></span> Select All</button> <button class="btn btn-default" id="deselect_all"><span class="glyphicon glyphicon-remove"></span> Deselect All</button> <button class="btn btn-default" id="invert"><span class="glyphicon glyphicon-resize-vertical"></span> Invert</button> - <button class="btn btn-success" id="add" disabled><span class="glyphicon glyphicon-plus-sign"></span> Add</button> + {% if geno_db_exists == "True" %}<button class="btn btn-success" id="add" disabled><span class="glyphicon glyphicon-plus-sign"></span> Add</button>{% endif %} <br /> <br /> <div id="table_container" style="width:{% if 'additive' in trimmed_markers[0] %}600{% else %}550{% endif %}px;"> @@ -372,6 +372,20 @@ console.time("Creating table"); {% if selectedChr == -1 %} $('#trait_table').DataTable( { + "drawCallback": function( settings ) { + $('#trait_table tr').off().on("click", function(event) { + if (event.target.type !== 'checkbox' && event.target.tagName.toLowerCase() !== 'a') { + var obj =$(this).find('input'); + obj.prop('checked', !obj.is(':checked')); + } + if ($(this).hasClass("selected")){ + $(this).removeClass("selected") + } else { + $(this).addClass("selected") + } + {% if geno_db_exists == "True" %}change_buttons(check_node=1){% endif %} + }); + }, "columns": [ { "type": "natural", "width": "5%" }, { "type": "natural", "width": "8%" }, diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index f2334512..411a6628 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -3,7 +3,7 @@ {% block css %} <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" /> <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='fontawesome/css/font-awesome.min.css') }}" /> - <link rel="stylesheet" type="text/css" href="{{ url_for('js', filename='DataTablesExtensions/scroller/css/scroller.dataTables.min.css') }}"> + <link rel="stylesheet" type="text/css" href="{{ url_for('js', filename='DataTablesExtensions/scrollerStyle/css/scroller.dataTables.min.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('js', filename='DataTablesExtensions/buttonStyles/css/buttons.dataTables.min.css') }}"> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"> <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" /> @@ -160,6 +160,16 @@ <script type="text/javascript" charset="utf-8"> $(document).ready( function () { + + var getParams = function(url) { + let parser = document.createElement('a'); + parser.href = url; + let params = parser.search.substring(1); + if(params.length > 0) { + return ('?'+params); + } + return params; + }; $('#trait_table tr').click(function(event) { if (event.target.type !== 'checkbox') { @@ -225,7 +235,7 @@ 'data': null, 'width': "25px", 'orderDataType': "dom-checkbox", - 'orderSequence': [ "desc", "asc"], + 'orderable': false, 'render': function(data, type, row, meta) { return '<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + data.hmac + '">' } @@ -373,15 +383,33 @@ }{% endif %} ], "order": [[1, "asc" ]], - 'sDom': "itirp", + {% if dataset.type != 'Geno' %} + buttons: [ + { + extend: 'columnsToggle', + columns: function( idx, data, node ) { + if (idx != 0) { + return true; + } else { + return false; + } + }, + postfixButtons: [ 'colvisRestore' ] + } + ], + 'sDom': "Bpitirp", + {% else %} + 'sDom': "pitirp", + {% endif %} 'iDisplayLength': 500, 'deferRender': true, 'paging': true, 'orderClasses': true, 'processing': true, - 'language': { - 'loadingRecords': ' ', - 'processing': 'Loading...' + 'bServerSide': true, + 'sAjaxSource': '/search_table'+getParams(window.location.href), + 'infoCallback': function(settings, start, end, max, total, pre) { + return "Showing " + start + " to " + (start + this.api().data().length - 1) + " of " + total + " entries"; } } ); diff --git a/wqflask/wqflask/templates/show_trait_calculate_correlations.html b/wqflask/wqflask/templates/show_trait_calculate_correlations.html index ba72ff27..eaa0c308 100644 --- a/wqflask/wqflask/templates/show_trait_calculate_correlations.html +++ b/wqflask/wqflask/templates/show_trait_calculate_correlations.html @@ -2,86 +2,85 @@ <div class="col-xs-3 correlation-options"> <div class="form-horizontal section-form-div"> - <div class="form-group"> - <label for="corr_type" class="col-xs-2 control-label">Method</label> - <div class="col-xs-3 controls"> - <select name="corr_type" class="form-control"> - <option value="sample">Sample r</option> - <option value="lit">Literature r</option> - <option value="tissue">Tissue r</option> - </select> - </div> - </div> + <div class="form-group"> + <label for="corr_type" class="col-xs-2 control-label">Method</label> + <div class="col-xs-3 controls"> + <select name="corr_type" class="form-control"> + <option value="sample">Sample r</option> + <option value="lit">Literature r</option> + <option value="tissue">Tissue r</option> + </select> + </div> + </div> - <div class="form-group"> - <label for="corr_dataset" class="col-xs-2 control-label">Database</label> - <div class="col-xs-10 controls"> - <select name="corr_dataset" class="form-control"> - {% for tissue in corr_tools.dataset_menu %} - {% if tissue.tissue %} - <optgroup label="{{ tissue.tissue }} ------"> - {% endif %} - {% for dataset in tissue.datasets %} - <option value="{{ dataset[1] }}" - {% if corr_tools.dataset_menu_selected == dataset[1] %} - selected - {% endif %}> - {{ dataset[0] }} - </option> - {% endfor %} - {% if tissue.tissue %} - </optgroup> - {% endif %} - {% endfor %} - </select> - </div> - </div> + <div class="form-group"> + <label for="corr_dataset" class="col-xs-2 control-label">Database</label> + <div class="col-xs-10 controls"> + <select name="corr_dataset" class="form-control"> + {% for tissue in corr_tools.dataset_menu %} + {% if tissue.tissue %} + <optgroup label="{{ tissue.tissue }} ------"> + {% endif %} + {% for dataset in tissue.datasets %} + <option data-type="{% if tissue.tissue %}mrna_assay{% elif dataset[1][-4:] == 'Geno' %}geno{% else %}pheno{% endif %}" value="{{ dataset[1] }}" + {% if corr_tools.dataset_menu_selected == dataset[1] %} + selected + {% endif %}> + {{ dataset[0] }} + </option> + {% endfor %} + {% if tissue.tissue %} + </optgroup> + {% endif %} + {% endfor %} + </select> + </div> + </div> - <div class="form-group"> - <label for="corr_return_results" class="col-xs-2 control-label">Limit to</label> - <div class="col-xs-4 controls"> - <select name="corr_return_results" class="form-control"> - {% for return_result in corr_tools.return_results_menu %} - <option value="{{ return_result }}" - {% if corr_tools.return_results_menu_selected == return_result %} - selected - {% endif %}> - Top {{ return_result }} - </option> - {% endfor %} - </select> - </div> - </div> + <div class="form-group"> + <label for="corr_return_results" class="col-xs-2 control-label">Limit to</label> + <div class="col-xs-4 controls"> + <select name="corr_return_results" class="form-control"> + {% for return_result in corr_tools.return_results_menu %} + <option value="{{ return_result }}" + {% if corr_tools.return_results_menu_selected == return_result %} + selected + {% endif %}> + Top {{ return_result }} + </option> + {% endfor %} + </select> + </div> + </div> - <div class="form-group"> - <label for="corr_samples_group" class="col-xs-2 control-label">Samples</label> - <div class="col-xs-4 controls"> - <select name="corr_samples_group" class="form-control"> - {% for group, pretty_group in sample_group_types.items() %} - <option value="{{ group }}">{{ pretty_group }}</option> - {% endfor %} - </select> - </div> - </div> + <div class="form-group"> + <label for="corr_samples_group" class="col-xs-2 control-label">Samples</label> + <div class="col-xs-4 controls"> + <select name="corr_samples_group" class="form-control"> + {% for group, pretty_group in sample_group_types.items() %} + <option value="{{ group }}">{{ pretty_group }}</option> + {% endfor %} + </select> + </div> + </div> - <div id="corr_sample_method" class="form-group"> - <label for="corr_sample_method" class="col-xs-2 control-label">Type</label> - <div class="col-xs-4 controls"> - <select name="corr_sample_method" class="form-control"> - <option value="pearson">Pearson</option> - <option value="spearman">Spearman Rank</option> - <option value="bicor">Biweight Midcorrelation</option> - </select> - </div> - </div> - {% if dataset.type != "Publish" %} - <div class="form-group"> - <label class="col-xs-2 control-label">Min Expr</label> - <div class="col-xs-4 controls"> - <input name="min_expr" value="" type="text" class="form-control min-expr-field"> - </div> - </div> - <div class="form-group"> + <div id="corr_sample_method" class="form-group"> + <label for="corr_sample_method" class="col-xs-2 control-label">Type</label> + <div class="col-xs-4 controls"> + <select name="corr_sample_method" class="form-control"> + <option value="pearson">Pearson</option> + <option value="spearman">Spearman Rank</option> + <option value="bicor">Biweight Midcorrelation</option> + </select> + </div> + </div> + <div id="min_expr_filter" class="form-group" style="display: {% if dataset.type != 'Geno' %}block{% else %}none{% endif %};"> + <label class="col-xs-2 control-label">Min Expr</label> + <div class="col-xs-4 controls"> + <input name="min_expr" value="" type="text" class="form-control min-expr-field"> + </div> + </div> + <div id="location_filter" class="form-group" style="display: {% if dataset.type != 'Publish' %}block{% else %}none{% endif %};"> <label class="col-xs-2 control-label">Location</label> <div class="col-xs-6 controls"> <span> @@ -91,7 +90,6 @@ <br> </div> </div> - {% endif %} <div class="form-group"> <label class="col-xs-2 control-label">Range</label> <div class="col-xs-5 controls"> diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html index eca436c6..c42fe4aa 100755 --- a/wqflask/wqflask/templates/show_trait_mapping_tools.html +++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html @@ -74,8 +74,8 @@ No collections available. Please add traits to a collection to use them as covariates. {% else %} <div class="select-covar-div"> - <button type="button" id="select_covariates" class="btn btn-default select-covar-button">Select</button> - <button type="button" id="remove_covariates" class="btn btn-default select-covar-button">Remove</button> + <button type="button" class="btn btn-default select-covar-button select_covariates">Select</button> + <button type="button" class="btn btn-default select-covar-button remove_covariates">Remove</button> </div> <textarea rows="3" cols="50" readonly placeholder="No covariates selected" class="selected-covariates"></textarea> {% endif %} @@ -317,8 +317,8 @@ No collections available. Please add traits to a collection to use them as covariates. {% else %} <div class="select-covar-div"> - <button type="button" id="select_covariates" class="btn btn-default select-covar-button">Select</button> - <button type="button" id="remove_covariates" class="btn btn-default select-covar-button">Remove</button> + <button type="button" class="btn btn-default select-covar-button select_covariates">Select</button> + <button type="button" class="btn btn-default select-covar-button remove_covariates">Remove</button> </div> <textarea rows="3" cols="50" readonly placeholder="No covariates selected" class="selected-covariates"></textarea> {% endif %} diff --git a/wqflask/wqflask/templates/show_trait_statistics.html b/wqflask/wqflask/templates/show_trait_statistics.html index 4f347d4e..865959b1 100644 --- a/wqflask/wqflask/templates/show_trait_statistics.html +++ b/wqflask/wqflask/templates/show_trait_statistics.html @@ -21,9 +21,6 @@ <li> <a href="#violin_plot_tab" class="violin_plot_tab" data-toggle="tab">Violin Plot</a> </li> - <li> - <a href="#scatterplot_matrix" data-toggle="tab">Scatterplot Matrix</a> - </li> </ul> <div class="tab-content"> <div class="tab-pane active" id="stats_tab"> @@ -116,19 +113,6 @@ <div id="violin_plot"></div> </div> </div> - <div class="tab-pane" id="scatterplot_matrix"> - <div class="btn-group scatterplot-btn-div"> - <button type="button" class="btn btn-default" id="select_compare_trait"> - <i class="icon-th-large"></i> Select Traits - </button> - <button type="button" class="btn btn-default" id="clear_compare_trait"> - <i class="icon-trash"></i> Clear - </button> - </div> - <div id="scatterplot_container"> - <div id="comparison_scatterplot" class="qtlcharts"></div> - </div> - </div> </div> </div> <div id="collections_holder_wrapper" style="display:none;"> diff --git a/wqflask/wqflask/templates/snp_browser.html b/wqflask/wqflask/templates/snp_browser.html index a96b8e3b..b9aea570 100644 --- a/wqflask/wqflask/templates/snp_browser.html +++ b/wqflask/wqflask/templates/snp_browser.html @@ -15,7 +15,7 @@ <input type="hidden" name="first_run" value="{{ first_run }}"> <input type="hidden" name="chosen_strains_mouse" value="{{ chosen_strains_mouse|join(",") }}"> <input type="hidden" name="chosen_strains_rat" value="{{ chosen_strains_rat|join(",") }}"> - <div class="col-xs-4" style="padding-left: 0px;"> + <div class="col-xs-4" style="width: 260px; padding-left: 30px; padding-right: 0px;"> <div class="form-group row" style="margin-bottom: 5px;"> <label for="snp_or_indel" style="text-align: right;" class="col-xs-4 col-form-label"><b>Type:</b></label> <div class="col-xs-8"> @@ -74,7 +74,7 @@ </div> </div> </div> - <div class="col-xs-4" style="padding-left: 0px;"> + <div class="col-xs-4" style="width: 310px; padding-left: 0px; padding-right: 20px;"> <div class="form-group row" style="margin-bottom: 10px;"> <label for="strains" style="text-align: right;" class="col-xs-4 col-form-label"><b>Strains:</b></label> <div class="col-xs-8"> @@ -107,8 +107,14 @@ </div> </div> </div> + <div class="form-group row"> + <label class="col-xs-4 col-form-label"></label> + <div class="col-xs-8" style="margin-top: 65px;"> + <input class="btn btn-primary" type="button" name="export_csv" value="Export to CSV"> + </div> + </div> </div> - <div class="col-xs-4" style="padding-left: 20px;"> + <div class="col-xs-4" style="width: 310px; padding-left: 20px;"> <div class="form-group row" style="margin-bottom: 5px;"> <label for="domain" style="text-align: right;" class="col-xs-4 col-form-label"><b>Domain:</b></label> <div class="col-xs-8"> @@ -182,24 +188,21 @@ </div> <div style="margin-top: 20px;"> - {% if filtered_results is defined %} - {% if filtered_results|length > limit_number %} - There are more than 10000 results. Consider limiting your search to a smaller range. - {% else %} + {% if table_rows is defined %} <table class="dataTable cell-border nowrap" id="results_table" style="float: left;"> <thead> <tr> <th></th> {% if header_fields|length == 2 %} {% for header in header_fields[0] %} - <th data-export="{{ header }}">{{ header }}</th> + <th data-export="{{ header }}" name="{{ header }}">{{ header }}</th> {% endfor %} {% for strain in header_fields[1] %} - <th data-export="{{ strain }}" style="align: center; text-align: center; line-height: 15px;">{% for letter in strain %}<div style="transform: rotate(90deg);">{{ letter }}</div>{% endfor %}</th> + <th data-export="{{ strain }}" name="{{ strain }}" style="align: center; text-align: center; line-height: 12px;">{% for letter in strain|reverse %}<div style="transform: rotate(270deg);">{{ letter }}</div>{% endfor %}</th> {% endfor %} {% else %} {% for header in header_fields %} - <th data-export="{{ header }}">{{ header }}</th> + <th data-export="{{ header }}" name="{{ header }}">{{ header }}</th> {% endfor %} {% endif %} </tr> @@ -208,7 +211,6 @@ <td colspan="100%" align="center"><br><b><font size="15">Loading...</font></b><br></td> </tbody> </table> - {% endif %} {% endif %} </div> </div> @@ -222,12 +224,24 @@ <script language="javascript" type="text/javascript" src="/static/new/javascript/typeahead_rn6.json"></script> <script type='text/javascript'> - var json_rows = {{ table_rows|safe }}; var empty_columns = {{ empty_columns|safe }}; + + var remain_field_count = 15 - {{ empty_field_count|safe }}; + var total_field_count = 15 - {{ empty_field_count|safe }} + {{ allele_list|safe|length }}; </script> <script language="javascript"> + var getParams = function(url) { + let parser = document.createElement('a'); + parser.href = url; + let params = parser.search.substring(1); + if(params.length > 0) { + return ('?'+params); + } + return params; + }; + var substringMatcher = function(strs) { return function findMatches(q, cb) { var matches, substringRegex; @@ -260,19 +274,20 @@ source: substringMatcher(rat_genes) }); - {% if filtered_results is defined %} + {% if table_rows is defined %} $("#results_table").DataTable( { - 'data': json_rows, {% if variant_type == "SNP" %} 'columns': [ { 'data': null, + 'className': 'dt-body-center', 'orderable': false, 'render': function(data, type, row, meta) { - return '<input type="checkbox" name="trait_check">' + return '<input type="checkbox" class="checkbox" id="variant_checkbox" onchange="onVarinatCheck(this)" name="trait_check">' } }, { - 'data': 'index' + 'data': 'index', + 'className': 'dt-body-right' }, { 'data': null, 'render': function(data, type, row, meta) { @@ -283,27 +298,30 @@ } } }, { - 'data': 'chr' + 'data': 'chr', + 'className': 'dt-body-center' }, { - 'data': 'mb_formatted' + 'data': 'mb_formatted', + 'className': 'dt-body-right' }, { 'data': 'alleles' }, {% if empty_columns['snp_source'] == "true" %}{ 'data': null, 'render': function(data, type, row, meta) { if (data.snp_source == "Sanger/UCLA") { - return '<a href="' + data.source_urls[0] + '">Sanger</a><a href="' + data.source_urls[1] + '">UCLA</a>' + return '<a href="' + data.source_urls[0] + '">Sanger</a>, <a href="' + data.source_urls[1] + '">UCLA</a>' } else { return data.snp_source } } }, {% endif %} {% if empty_columns['conservation_score'] == "true" %}{ - 'data': 'conservation_score' + 'data': 'conservation_score', + 'className': 'dt-body-right' }, {% endif %} {% if empty_columns['gene_name'] == "true" %}{ 'data': null, 'render': function(data, type, row, meta) { if (data.gene_name != "") { - return '<i>' + data.gene_name + '</i><br><a href="' + data.gene_link + '">NCBI</a>' + return '<i>' + data.gene_name + '</i>, <a href="' + data.gene_link + '">NCBI</a>' } else { return data.gene_name } @@ -330,6 +348,7 @@ }, {% endif %} {% for item in allele_list %} { 'data': null, 'orderable': false, + 'className': 'dt-body-center', 'render': function(data, type, row, meta) { if (typeof data.allele_value_list[{{ loop.index - 1 }}][0] !== "undefined") { return data.allele_value_list[{{ loop.index - 1 }}][0] @@ -339,12 +358,9 @@ } }{% if loop.index < allele_list|length %},{% endif %}{% endfor %} ], - 'createdRow': function( row, data, dataIndex) { - $('td', row).eq(0).attr("style", "text-align: center; padding: 4px 10px 2px 10px;"); - $('td', row).eq(1).attr("align", "right"); - for (i = {{ 15 - empty_field_count }}; i < ({{ 15 - empty_field_count }} + {{ allele_list|length }}); i++) { + 'createdRow': function(row, data, dataIndex) { + for (i = remain_field_count; i < total_field_count; i++) { var this_allele = $('td', row).eq(i).text(); - $('td', row).eq(i).attr("style", "text-align: center; padding: 4px 10px 2px 10px;"); switch (this_allele) { case "A": $('td', row).eq(i).addClass('A_allele_color'); @@ -380,24 +396,29 @@ { 'data': null, 'render': function(data, type, row, meta) { - return '<input type="checkbox" name="trait_check">' + return '<input type="checkbox" class="checkbox" id="variant_checkbox" onchange="onVarinatCheck(this)" name="trait_check">' } }, { - 'data': 'index' + 'data': 'index', + 'className': 'dt-body-right' }, { 'data': 'indel_name' }, { 'data': 'indel_type' }, { - 'data': 'indel_chr' + 'data': 'indel_chr', + 'className': 'dt-body-center' }, { - 'data': 'indel_mb_s' + 'data': 'indel_mb_s', + 'className': 'dt-body-right' }, { - 'data': 'indel_mb_e' + 'data': 'indel_mb_e', + 'className': 'dt-body-right' }, { 'data': 'indel_strand' }, { - 'data': 'indel_size' + 'data': 'indel_size', + 'className': 'dt-body-right' }, { 'data': 'indel_sequence' }, { @@ -407,15 +428,28 @@ {% endif %} 'order': [[1, "asc" ]], 'sDom': "rtip", - 'iDisplayLength': 500, - 'processing': true, - 'language': { - 'loadingRecords': ' ', - 'processing': 'Loading...' + 'iDisplayLength': 100, + 'bServerSide': true, + 'sAjaxSource': '/snp_browser_table'+getParams(window.location.href), + 'infoCallback': function(settings, start, end, max, total, pre) { + return "Showing " + start + " to " + (start + this.api().data().length - 1) + " of " + total + " entries"; } }); {% endif %} + function onVarinatCheck(checkboxElem) { + if (checkboxElem.checked) { + if (!checkboxElem.parentElement.parentElement.classList.contains('selected')) { + checkboxElem.parentElement.parentElement.classList.add('selected') + } + } + else { + if (checkboxElem.parentElement.parentElement.classList.contains('selected')) { + checkboxElem.parentElement.parentElement.classList.remove('selected') + } + } + } + $("#species_select").change(function() { this_species = $(this).val(); $("#strain_select").empty() @@ -509,6 +543,40 @@ }); $("input[name=chosen_strains]").val(strain_list.join(",")); }); + + + + $("input[name=export_csv]").click(function() { + var csv = []; + var rows = document.querySelectorAll("table tr"); + + var headers = []; + var col_header = rows[0].querySelectorAll("th"); + for(let i = 1; i < col_header.length; i++) { + headers.push(col_header[i].getAttribute("name")); + } + csv.push(headers.join(",")); + + for (let i = 1; i < rows.length; i++) { + var row = [], cols = rows[i].querySelectorAll("td"); + var checkBox = rows[i].querySelector("input"); + + if(checkBox.checked == true) { + for (let j = 1; j < cols.length; j++) + row.push(cols[j].innerText); + + csv.push(row.join(",")); + } + } + + var csvFile = new Blob([csv.join("\n")], {type: "text/csv"}); + var downloadLink = document.createElement("a"); + downloadLink.download = "variant_data.csv"; + downloadLink.href = window.URL.createObjectURL(csvFile); + downloadLink.style.display = "none"; + document.body.appendChild(downloadLink); + downloadLink.click(); + }); </script> {% endblock %} diff --git a/wqflask/wqflask/user_login.py b/wqflask/wqflask/user_login.py index f25ebc32..bc608e84 100644 --- a/wqflask/wqflask/user_login.py +++ b/wqflask/wqflask/user_login.py @@ -25,7 +25,7 @@ from utility.logger import getLogger logger = getLogger(__name__) from smtplib import SMTP -from utility.tools import SMTP_CONNECT, SMTP_USERNAME, SMTP_PASSWORD, LOG_SQL_ALCHEMY +from utility.tools import SMTP_CONNECT, SMTP_USERNAME, SMTP_PASSWORD, LOG_SQL_ALCHEMY, GN2_BRANCH_URL THREE_DAYS = 60 * 60 * 24 * 3 @@ -239,7 +239,7 @@ def github_oauth2(): } 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("&")]]} + result_dict = {arr[0]:arr[1] for arr in [tok.split("=") for tok in result.text.split("&")]} github_user = get_github_user_details(result_dict["access_token"]) @@ -277,9 +277,11 @@ def orcid_oauth2(): data = { "client_id": ORCID_CLIENT_ID, "client_secret": ORCID_CLIENT_SECRET, - "grant_type": "authorization_code", + "grant_type": "authorization_code", + "redirect_uri": GN2_BRANCH_URL + "n/login/orcid_oauth2", "code": code } + result = requests.post(ORCID_TOKEN_URL, data=data) result_dict = json.loads(result.text.encode("utf-8")) diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index c136711e..2b3e2a37 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -26,6 +26,15 @@ import sqlalchemy from wqflask import app from flask import g, Response, request, make_response, render_template, send_from_directory, jsonify, redirect, url_for, send_file +from wqflask import group_manager +from wqflask import resource_manager +from wqflask import search_results +from wqflask import export_traits +from wqflask import gsearch +from wqflask import update_search_results +from wqflask import docs +from wqflask import news +from wqflask import server_side from wqflask.submit_bnw import get_bnw_input from base.data_set import create_dataset, DataSet # Used by YAML in marker_regression from wqflask.show_trait import show_trait @@ -220,6 +229,26 @@ def search_page(): else: return render_template("search_error.html") +@app.route("/search_table", methods=('GET',)) +def search_page_table(): + logger.info("in search_page table") + logger.info(request.url) + + logger.info("request.args is", request.args) + the_search = search_results.SearchResultPage(request.args) + + logger.info(type(the_search.trait_list)) + logger.info(the_search.trait_list) + + current_page = server_side.ServerSideTable( + len(the_search.trait_list), + the_search.trait_list, + the_search.header_data_names, + request.args, + ).get_page() + + return flask.jsonify(current_page) + @app.route("/gsearch", methods=('GET',)) def gsearchact(): logger.info(request.url) @@ -890,6 +919,19 @@ def db_info_page(): return render_template("info_page.html", **template_vars.__dict__) +@app.route("/snp_browser_table", methods=('GET',)) +def snp_browser_table(): + logger.info(request.url) + snp_table_data = snp_browser.SnpBrowser(request.args) + current_page = server_side.ServerSideTable( + snp_table_data.rows_count, + snp_table_data.table_rows, + snp_table_data.header_data_names, + request.args, + ).get_page() + + return flask.jsonify(current_page) + @app.route("/tutorial/WebQTLTour", methods=('GET',)) def tutorial_page(): #ZS: Currently just links to GN1 |