diff options
33 files changed, 1009 insertions, 266 deletions
diff --git a/test/requests/link_checker.py b/test/requests/link_checker.py index bc3b5861..f162ec84 100644 --- a/test/requests/link_checker.py +++ b/test/requests/link_checker.py @@ -103,9 +103,9 @@ def check_packaged_js_files(args_obj, parser): "/js/DataTablesExtensions/colResize/dataTables.colResize.js", "/js/DataTablesExtensions/colReorder/js/dataTables.colReorder.js", "/js/DataTablesExtensions/buttons/js/buttons.colVis.min.js", - "/js/DataTablesExtensions/scroller/js/scroller.dataTables.min.js", + "/js/DataTablesExtensions/scroller/js/dataTables.scroller.min.js", "/js/DataTables/js/jquery.dataTables.js", - "/css/DataTablesExtensions/scroller/css/scroller.dataTables.min.css", + "/js/DataTablesExtensions/scrollerStyle/css/scroller.dataTables.min.css", # Datatables plugins: "/js/DataTablesExtensions/plugins/sorting/natural.js", "/js/DataTablesExtensions/plugins/sorting/scientific.js", 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 eab6afe6..fe2569b8 100644 --- a/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py +++ b/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py @@ -144,21 +144,26 @@ class TestGemmaMapping(unittest.TestCase): "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/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 6175dc7e..fb4dc4f4 100644 --- a/wqflask/wqflask/correlation/show_corr_results.py +++ b/wqflask/wqflask/correlation/show_corr_results.py @@ -86,8 +86,6 @@ class CorrelationResults(object): else: helper_functions.get_species_dataset_trait(self, start_vars) - #self.dataset.group.read_genotype_file() - corr_samples_group = start_vars['corr_samples_group'] self.sample_data = {} @@ -462,13 +460,13 @@ class CorrelationResults(object): if not excluded_samples: excluded_samples = () + sample_val_dict = json.loads(start_vars['sample_vals']) for sample in sample_names: if sample not in excluded_samples: - # print("Looking for",sample,"in",start_vars) - value = start_vars.get('value:' + sample) - if value: - if not value.strip().lower() == 'x': - self.sample_data[str(sample)] = float(value) + value = sample_val_dict[sample] + if not value.strip().lower() == 'x': + self.sample_data[str(sample)] = float(value) + def do_bicor(this_trait_vals, target_trait_vals): r_library = ro.r["library"] # Map the library function diff --git a/wqflask/wqflask/marker_regression/display_mapping_results.py b/wqflask/wqflask/marker_regression/display_mapping_results.py index 08c2d750..9f1b050d 100644 --- a/wqflask/wqflask/marker_regression/display_mapping_results.py +++ b/wqflask/wqflask/marker_regression/display_mapping_results.py @@ -270,8 +270,9 @@ class DisplayMappingResults(object): # Needing for form submission when doing single chr # mapping or remapping after changing options - self.samples = start_vars['samples'] - self.vals = start_vars['vals'] + self.sample_vals = start_vars['sample_vals'] + self.sample_vals_dict = json.loads(self.sample_vals) + self.transform = start_vars['transform'] self.mapping_method = start_vars['mapping_method'] self.mapping_results_path = start_vars['mapping_results_path'] @@ -492,11 +493,10 @@ class DisplayMappingResults(object): ## count the amount of individuals to be plotted, and increase self.graphHeight if self.haplotypeAnalystChecked and self.selectedChr > -1: thisTrait = self.this_trait - _strains, _vals, _vars, _aliases = thisTrait.export_informative() smd=[] - for ii, _val in enumerate(self.vals): - if _val != "x": - temp = GeneralObject(name=self.samples[ii], value=float(_val)) + for sample in self.sample_vals_dict.keys(): + if self.sample_vals_dict[sample] != "x": + temp = GeneralObject(name=sample, value=float(self.sample_vals_dict[sample])) smd.append(temp) else: continue @@ -1464,12 +1464,11 @@ class DisplayMappingResults(object): yPaddingTop = yTopOffset thisTrait = self.this_trait - _strains, _vals, _vars, _aliases = thisTrait.export_informative() smd=[] - for ii, _val in enumerate(self.vals): - if _val != "x": - temp = GeneralObject(name=self.samples[ii], value=float(_val)) + for sample in self.sample_vals_dict.keys(): + if self.sample_vals_dict[sample] != "x": + temp = GeneralObject(name=sample, value=float(self.sample_vals_dict[sample])) smd.append(temp) else: continue diff --git a/wqflask/wqflask/marker_regression/rqtl_mapping.py b/wqflask/wqflask/marker_regression/rqtl_mapping.py index 0a5758af..4117a0e5 100644 --- a/wqflask/wqflask/marker_regression/rqtl_mapping.py +++ b/wqflask/wqflask/marker_regression/rqtl_mapping.py @@ -94,9 +94,6 @@ def run_rqtl_geno(vals, samples, dataset, mapping_scale, method, model, permChec ro.r('all_covars <- cbind(marker_covars, trait_covars)') else: ro.r('all_covars <- marker_covars') - #logger.info("Saving"); - #ro.r('save.image(file = "/home/dannya/gn2-danny/cross.RData")') - #logger.info("Saving Done"); covars = ro.r['all_covars'] #DEBUG to save the session object to file if pair_scan: @@ -159,7 +156,7 @@ def generate_cross_from_geno(dataset, scale_units): # TODO: Need to figur toskip = which(unlist(lapply(header, function(x){ length(grep("Chr\t", x)) })) == 1)-1 # Major hack to skip the geno headers type <- getGenoCode(header, 'type') if(type == '4-way'){ - genocodes <- c('1','2','3','4') + genocodes <- NULL } else { genocodes <- c(getGenoCode(header, 'mat'), getGenoCode(header, 'het'), getGenoCode(header, 'pat')) # Get the genotype codes } @@ -174,7 +171,7 @@ def generate_cross_from_geno(dataset, scale_units): # TODO: Need to figur require(qtl) if(type == '4-way'){ cat('Loading in as 4-WAY\n') - cross = read.cross(file=out, 'csvr', genotypes=genocodes, crosstype="4way", convertXdata=FALSE) # Load the created cross file using R/qtl read.cross + cross = read.cross(file=out, 'csvr', genotypes=NULL, crosstype="4way") # Load the created cross file using R/qtl read.cross }else if(type == 'f2'){ cat('Loading in as F2\n') cross = read.cross(file=out, 'csvr', genotypes=genocodes, crosstype="f2") # Load the created cross file using R/qtl read.cross @@ -332,8 +329,6 @@ def add_cofactors(cross, this_dataset, covariates, samples): covar_name_string += '"' + col_name + '", ' else: covar_name_string += '"' + col_name + '"' - - logger.info("covar_name_string:" + covar_name_string) else: col_name = "covar_" + str(i) cross = add_phenotype(cross, covar_as_string, col_name) @@ -343,7 +338,6 @@ def add_cofactors(cross, this_dataset, covariates, samples): covar_name_string += '"' + col_name + '"' covar_name_string += ")" - logger.info("covar_name_string:" + covar_name_string); covars_ob = pull_var("trait_covars", cross, covar_name_string) return cross, covars_ob diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py index 891fcc66..a1f87b61 100644 --- a/wqflask/wqflask/marker_regression/run_mapping.py +++ b/wqflask/wqflask/marker_regression/run_mapping.py @@ -71,56 +71,22 @@ class RunMapping(object): all_samples_ordered = self.dataset.group.all_samples_ordered() self.vals = [] - if 'samples' in start_vars: - self.samples = start_vars['samples'].split(",") - if (len(genofile_samplelist) != 0): - for sample in genofile_samplelist: - if sample in self.samples: - value = start_vars.get('value:' + sample) - if value: - self.vals.append(value) - else: - self.vals.append("x") - else: - for sample in self.samples: - value = start_vars.get('value:' + sample) - if value: - self.vals.append(value) + self.samples = [] + self.sample_vals = start_vars['sample_vals'] + sample_val_dict = json.loads(self.sample_vals) + samples = sample_val_dict.keys() + if (len(genofile_samplelist) != 0): + for sample in genofile_samplelist: + self.samples.append(sample) + if sample in samples: + self.vals.append(sample_val_dict[sample]) + else: + self.vals.append("x") else: - self.samples = [] - if (len(genofile_samplelist) != 0): - for sample in genofile_samplelist: - if sample in self.dataset.group.samplelist: - in_trait_data = False - for item in self.this_trait.data: - if self.this_trait.data[item].name == sample: - value = start_vars['value:' + self.this_trait.data[item].name] - self.samples.append(self.this_trait.data[item].name) - self.vals.append(value) - in_trait_data = True - break - if not in_trait_data: - value = start_vars.get('value:' + sample) - if value: - self.samples.append(sample) - self.vals.append(value) - else: - self.vals.append("x") - else: - for sample in self.dataset.group.samplelist: # sample is actually the name of an individual - in_trait_data = False - for item in self.this_trait.data: - if self.this_trait.data[item].name == sample: - value = start_vars['value:' + self.this_trait.data[item].name] - self.samples.append(self.this_trait.data[item].name) - self.vals.append(value) - in_trait_data = True - break - if not in_trait_data: - value = start_vars.get('value:' + sample) - if value: - self.samples.append(sample) - self.vals.append(value) + for sample in self.dataset.group.samplelist: + if sample in samples: + self.vals.append(sample_val_dict[sample]) + self.samples.append(sample) if 'n_samples' in start_vars: self.n_samples = start_vars['n_samples'] 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 83d4f193..2ec9d6d5 100644 --- a/wqflask/wqflask/static/new/javascript/show_trait.js +++ b/wqflask/wqflask/static/new/javascript/show_trait.js @@ -94,25 +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)); -$(".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() { @@ -225,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); @@ -273,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 @@ -314,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) @@ -345,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) @@ -419,6 +409,25 @@ process_id = function() { } return processed; }; + +fetch_sample_values = function() { + // This is meant to fetch all sample values using DataTables API, since they can't all be submitted with the form when using Scroller (and this should also be faster) + sample_val_dict = {}; + + table = 'samples_primary'; + if ($('#' + table).length){ + table_api = $('#' + table).DataTable(); + val_nodes = table_api.column(3).nodes().to$(); + for (_j = 0; _j < val_nodes.length; _j++){ + sample_name = val_nodes[_j].childNodes[0].name.split(":")[1] + sample_val = val_nodes[_j].childNodes[0].value + sample_val_dict[sample_name] = sample_val + } + } + + return sample_val_dict; +} + edit_data_change = function() { var already_seen, checkbox, name, real_dict, real_value, real_variance, row, rows, sample_sets, table, tables, _i, _j, _len, _len1; already_seen = {}; @@ -448,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, @@ -541,7 +550,7 @@ $('select[name=corr_dataset]').change(on_dataset_change); $('select[name=location_type]').change(on_dataset_change); submit_special = function(url) { - get_table_contents_for_form_submit("trait_data_form"); + $("input[name=sample_vals]").val(JSON.stringify(fetch_sample_values())) $("#trait_data_form").attr("action", url); $("#trait_data_form").submit(); }; @@ -565,7 +574,7 @@ get_table_contents_for_form_submit = function(form_id) { }); } -var corr_input_list = ['corr_type', 'primary_samples', 'trait_id', 'dataset', 'group', 'tool_used', 'form_url', 'corr_sample_method', 'corr_samples_group', 'corr_dataset', 'min_expr', +var corr_input_list = ['sample_vals', 'corr_type', 'primary_samples', 'trait_id', 'dataset', 'group', 'tool_used', 'form_url', 'corr_sample_method', 'corr_samples_group', 'corr_dataset', 'min_expr', 'corr_return_results', 'location_type', 'loc_chr', 'min_loc_mb', 'max_loc_mb', 'p_range_lower', 'p_range_upper'] $(".corr_compute").on("click", (function(_this) { @@ -818,6 +827,7 @@ reset_samples_table = function() { }; $('.reset').click(function() { reset_samples_table(); + $('input[name="transform"]').val(""); edit_data_change(); }); @@ -1307,7 +1317,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/show_trait_mapping_tools.js b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js index 34582f21..3ae52975 100644 --- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js +++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js @@ -141,7 +141,7 @@ $('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', +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', 'control_marker_db', 'do_control', 'genofile', 'pair_scan', 'startMb', 'endMb', 'graphWidth', 'lrsMax', 'additiveCheck', 'showSNP', 'showGenes', 'viewLegend', 'haplotypeAnalystCheck', 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/mapping_results.html b/wqflask/wqflask/templates/mapping_results.html index 1a76ef7a..c2256e47 100644 --- a/wqflask/wqflask/templates/mapping_results.html +++ b/wqflask/wqflask/templates/mapping_results.html @@ -31,10 +31,7 @@ {% endif %} <input type="hidden" name="results_path" value="{{ mapping_results_path }}"> <input type="hidden" name="method" value="{{ mapping_method }}"> - <input type="hidden" name="samples" value="{{ samples|join(",") }}"> - {% for sample in samples %} - <input type="hidden" name="value:{{ sample }}" value="{{ vals[loop.index - 1] }}"> - {% endfor %} + <input type="hidden" name="sample_vals" value="{{ sample_vals }}"> <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 }}"> @@ -248,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;"> @@ -375,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%" }, @@ -450,7 +461,7 @@ }); - var mapping_input_list = ['temp_uuid', 'trait_id', 'dataset', 'tool_used', 'form_url', 'method', 'transform', 'trimmed_markers', 'selected_chr', 'chromosomes', 'mapping_scale', + 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', 'color_scheme', 'manhattan_single_color', 'control_marker', 'control_marker_db', 'do_control', 'genofile', 'pair_scan', 'startMb', 'endMb', 'graphWidth', 'lrsMax', 'additiveCheck', 'showSNP', 'showGenes', 'viewLegend', 'haplotypeAnalystCheck', 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.html b/wqflask/wqflask/templates/show_trait.html index c881eb76..77fc9342 100644 --- a/wqflask/wqflask/templates/show_trait.html +++ b/wqflask/wqflask/templates/show_trait.html @@ -35,6 +35,7 @@ <input type="hidden" name="genofile" value=""> <input type="hidden" name="covariates" value=""> <input type="hidden" name="transform" value=""> + <input type="hidden" name="sample_vals" value=""> <div class="container showtrait-main-div"> <div class="panel-group" id="accordion"> 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/views.py b/wqflask/wqflask/views.py index 25563e86..e4834d53 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) @@ -621,12 +650,13 @@ def loading_page(): wanted = initial_start_vars['wanted_inputs'].split(",") start_vars = {} for key, value in list(initial_start_vars.items()): - if key in wanted or key.startswith(('value:')): + if key in wanted: start_vars[key] = value if 'n_samples' in start_vars: n_samples = int(start_vars['n_samples']) else: + sample_vals_dict = json.loads(start_vars['sample_vals']) if 'group' in start_vars: dataset = create_dataset(start_vars['dataset'], group_name = start_vars['group']) else: @@ -642,9 +672,9 @@ def loading_page(): samples = genofile_samples for sample in samples: - value = start_vars.get('value:' + sample) - if value != "x": - n_samples += 1 + if sample in sample_vals_dict: + if sample_vals_dict[sample] != "x": + n_samples += 1 start_vars['n_samples'] = n_samples start_vars['wanted_inputs'] = initial_start_vars['wanted_inputs'] @@ -660,7 +690,6 @@ def loading_page(): @app.route("/run_mapping", methods=('POST',)) def mapping_results_page(): initial_start_vars = request.form - #logger.debug("Mapping called with initial_start_vars:", initial_start_vars.items()) logger.info(request.url) temp_uuid = initial_start_vars['temp_uuid'] wanted = ( @@ -670,6 +699,7 @@ def mapping_results_page(): 'species', 'samples', 'vals', + 'sample_vals', 'first_run', 'output_files', 'geno_db_exists', @@ -723,13 +753,11 @@ def mapping_results_page(): ) start_vars = {} for key, value in list(initial_start_vars.items()): - if key in wanted or key.startswith(('value:')): + if key in wanted: start_vars[key] = value - #logger.debug("Mapping called with start_vars:", start_vars) version = "v3" key = "mapping_results:{}:".format(version) + json.dumps(start_vars, sort_keys=True) - #logger.info("key is:", pf(key)) with Bench("Loading cache"): result = None # Just for testing #result = Redis.get(key) @@ -751,10 +779,9 @@ def mapping_results_page(): rendered_template = render_template("mapping_error.html") return rendered_template except: - rendered_template = render_template("mapping_error.html") - return rendered_template + rendered_template = render_template("mapping_error.html") + return rendered_template - #if template_vars.mapping_method != "gemma" and template_vars.mapping_method != "plink": template_vars.js_data = json.dumps(template_vars.js_data, default=json_default_handler, indent=" ") @@ -775,10 +802,6 @@ def mapping_results_page(): rendered_template = render_template("pair_scan_results.html", **result) else: gn1_template_vars = display_mapping_results.DisplayMappingResults(result).__dict__ - #pickled_result = pickle.dumps(result, pickle.HIGHEST_PROTOCOL) - #logger.info("pickled result length:", len(pickled_result)) - #Redis.set(key, pickled_result) - #Redis.expire(key, 1*60) with Bench("Rendering template"): #if (gn1_template_vars['mapping_method'] == "gemma") or (gn1_template_vars['mapping_method'] == "plink"): @@ -896,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 |