diff options
21 files changed, 1391 insertions, 502 deletions
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py index 0f8f937c..ec8c40a0 100644 --- a/wqflask/base/trait.py +++ b/wqflask/base/trait.py @@ -516,7 +516,7 @@ def retrieve_trait_info(trait, dataset, get_qtl_info=False): # If the dataset is confidential and the user has access to confidential # phenotype traits, then display the pre-publication description instead # of the post-publication description - if trait.confidential: + if not trait.pubmed_id: trait.abbreviation = trait.pre_publication_abbreviation trait.description_display = trait.pre_publication_description else: diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py new file mode 100644 index 00000000..5b621264 --- /dev/null +++ b/wqflask/tests/unit/wqflask/marker_regression/test_gemma_mapping.py @@ -0,0 +1,205 @@ +# test for wqflask/marker_regression/gemma_mapping.py +import unittest +import random +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 + + +class AttributeSetter: + def __init__(self, obj): + for key, val in obj.items(): + setattr(self, key, val) + + +class MockGroup(AttributeSetter): + def get_samplelist(self): + return None + + +class TestGemmaMapping(unittest.TestCase): + + @mock.patch("wqflask.marker_regression.gemma_mapping.parse_loco_output") + def test_run_gemma_firstrun_set_false(self, mock_parse_loco): + """add tests for gemma function where first run is set to false""" + dataset = AttributeSetter( + {"group": AttributeSetter({"genofile": "genofile.geno"})}) + + output_file = "file1" + mock_parse_loco.return_value = [] + this_trait = AttributeSetter({"name": "t1"}) + + result = run_gemma(this_trait=this_trait, this_dataset=dataset, samples=[], vals=[ + ], covariates="", use_loco=True, first_run=False, output_files=output_file) + + expected_results = ([], "file1") + self.assertEqual(expected_results, result) + + @mock.patch("wqflask.marker_regression.gemma_mapping.webqtlConfig.GENERATED_IMAGE_DIR", "/home/user/img") + @mock.patch("wqflask.marker_regression.gemma_mapping.GEMMAOPTS", "-debug") + @mock.patch("wqflask.marker_regression.gemma_mapping.GEMMA_WRAPPER_COMMAND", "ghc") + @mock.patch("wqflask.marker_regression.gemma_mapping.TEMPDIR", "/home/user/data/") + @mock.patch("wqflask.marker_regression.gemma_mapping.parse_loco_output") + @mock.patch("wqflask.marker_regression.gemma_mapping.logger") + @mock.patch("wqflask.marker_regression.gemma_mapping.flat_files") + @mock.patch("wqflask.marker_regression.gemma_mapping.gen_covariates_file") + @mock.patch("wqflask.marker_regression.run_mapping.random.choice") + @mock.patch("wqflask.marker_regression.gemma_mapping.os") + @mock.patch("wqflask.marker_regression.gemma_mapping.gen_pheno_txt_file") + def test_run_gemma_firstrun_set_true(self, mock_gen_pheno_txt, mock_os, mock_choice, mock_gen_covar, mock_flat_files, mock_logger, mock_parse_loco): + """add tests for run_gemma where first run is set to true""" + chromosomes = [] + for i in range(1, 5): + chromosomes.append(AttributeSetter({"name": f"CH{i}"})) + chromo = AttributeSetter({"chromosomes": chromosomes}) + dataset_group = MockGroup( + {"name": "GP1", "genofile": "file_geno"}) + dataset = AttributeSetter({"group": dataset_group, "name": "dataset1_name", + "species": AttributeSetter({"chromosomes": chromo})}) + trait = AttributeSetter({"name": "trait1"}) + samples = [] + mock_gen_pheno_txt.return_value = None + mock_os.path.isfile.return_value = True + mock_gen_covar.return_value = None + mock_choice.return_value = "R" + mock_flat_files.return_value = "/home/genotype/bimbam" + 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) + mock_gen_pheno_txt.assert_called_once() + mock_parse_loco.assert_called_once_with(dataset, "GP1_GWA_RRRRRR") + mock_os.path.isfile.assert_called_once_with( + ('/home/user/imgfile_output.assoc.txt')) + self.assertEqual(mock_logger.debug.call_count, 2) + self.assertEqual(mock_flat_files.call_count, 4) + self.assertEqual(results, ([], "GP1_GWA_RRRRRR")) + + @mock.patch("wqflask.marker_regression.gemma_mapping.TEMPDIR", "/home/user/data") + def test_gen_pheno_txt_file(self): + """add tests for generating pheno txt file""" + with mock.patch("builtins.open", mock.mock_open())as mock_open: + gen_pheno_txt_file(this_dataset={}, genofile_name="", vals=[ + "x", "w", "q", "we", "R"], trait_filename="fitr.re") + mock_open.assert_called_once_with( + '/home/user/data/gn2/fitr.re.txt', 'w') + filehandler = mock_open() + values = ["x", "w", "q", "we", "R"] + write_calls = [mock.call('NA\n'), mock.call('w\n'), mock.call( + 'q\n'), mock.call('we\n'), mock.call('R\n')] + + filehandler.write.assert_has_calls(write_calls) + + @mock.patch("wqflask.marker_regression.gemma_mapping.flat_files") + @mock.patch("wqflask.marker_regression.gemma_mapping.create_trait") + @mock.patch("wqflask.marker_regression.gemma_mapping.create_dataset") + def test_gen_covariates_file(self, create_dataset, create_trait, flat_files): + """add tests for generating covariates files""" + covariates = "X1:X2,Y1:Y2,M1:M3,V1:V2" + samplelist = ["X1", "X2", "X3", "X4"] + create_dataset_side_effect = [] + create_trait_side_effect = [] + + for i in range(4): + create_dataset_side_effect.append(AttributeSetter({"name": f'name_{i}'})) + create_trait_side_effect.append( + AttributeSetter({"data": [f'data_{i}']})) + + create_dataset.side_effect = create_trait_side_effect + create_trait.side_effect = create_trait_side_effect + + group = MockGroup({"name": "group_X", "samplelist": samplelist}) + this_dataset = AttributeSetter({"group": group}) + flat_files.return_value = "Home/Genenetwork" + + with mock.patch("builtins.open", mock.mock_open())as mock_open: + gen_covariates_file(this_dataset=this_dataset, covariates=covariates, + samples=["x1", "x2", "X3"]) + + create_dataset.assert_has_calls( + [mock.call('X2'), mock.call('Y2'), mock.call('M3'), mock.call('V2')]) + mock_calls = [] + trait_names = ["X1", "Y1", "M1", "V1"] + + for i, trait in enumerate(create_trait_side_effect): + mock_calls.append( + mock.call(dataset=trait, name=trait_names[i], cellid=None)) + + create_trait.assert_has_calls(mock_calls) + + flat_files.assert_called_once_with('mapping') + mock_open.assert_called_once_with( + 'Home/Genenetwork/group_X_covariates.txt', 'w') + filehandler = mock_open() + filehandler.write.assert_has_calls([mock.call( + '-9\t'), mock.call('-9\t'), mock.call('-9\t'), mock.call('-9\t'), mock.call('\n')]) + + @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") + def test_parse_loco_outputfile_found(self, mock_json, mock_os): + """add tests for parse loco output file found""" + mock_json.load.return_value = { + "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""" + 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) + 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}] + + self.assertEqual(expected_results, results) + + @mock.patch("wqflask.marker_regression.gemma_mapping.TEMPDIR", "/home/tmp") + @mock.patch("wqflask.marker_regression.gemma_mapping.os") + def test_parse_loco_outputfile_not_found(self, mock_os): + """add tests for parse loco output where output file not found""" + + mock_os.path.isfile.return_value = False + file_to_write = """{"files":["file_1","file_2"]}""" + + with mock.patch("builtins.open", mock.mock_open(read_data=file_to_write)) as mock_open: + results = parse_loco_output( + this_dataset={}, gwa_output_filename=".xw/") + self.assertEqual(results, []) diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_plink_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_plink_mapping.py new file mode 100644 index 00000000..5eec93f1 --- /dev/null +++ b/wqflask/tests/unit/wqflask/marker_regression/test_plink_mapping.py @@ -0,0 +1,85 @@ +# test for wqflask/marker_regression/plink_mapping.py +import unittest +from unittest import mock +from wqflask.marker_regression.plink_mapping import build_line_list +from wqflask.marker_regression.plink_mapping import get_samples_from_ped_file +from wqflask.marker_regression.plink_mapping import flat_files +from wqflask.marker_regression.plink_mapping import gen_pheno_txt_file_plink +from wqflask.marker_regression.plink_mapping import parse_plink_output + + +class AttributeSetter: + def __init__(self, obj): + for key, val in obj.items(): + setattr(self, key, val) +class TestPlinkMapping(unittest.TestCase): + + + def test_build_line_list(self): + """test for building line list""" + line_1 = "this is line one test" + irregular_line = " this is an, irregular line " + exp_line1 = ["this", "is", "line", "one", "test"] + + results = build_line_list(irregular_line) + self.assertEqual(exp_line1, build_line_list(line_1)) + self.assertEqual([], build_line_list()) + self.assertEqual(["this", "is", "an,", "irregular", "line"], results) + + @mock.patch("wqflask.marker_regression.plink_mapping.flat_files") + def test_get_samples_from_ped_file(self, mock_flat_files): + """test for getting samples from ped file""" + dataset = AttributeSetter({"group": AttributeSetter({"name": "n_1"})}) + file_sample = """Expected_1\tline test +Expected_2\there + Expected_3\tthree""" + mock_flat_files.return_value = "/home/user/" + with mock.patch("builtins.open", mock.mock_open(read_data=file_sample)) as mock_open: + results = get_samples_from_ped_file(dataset) + mock_flat_files.assert_called_once_with("mapping") + mock_open.assert_called_once_with("/home/user/n_1.ped", "r") + self.assertEqual( + ["Expected_1", "Expected_2", "Expected_3"], results) + + @mock.patch("wqflask.marker_regression.plink_mapping.TMPDIR", "/home/user/data/") + @mock.patch("wqflask.marker_regression.plink_mapping.get_samples_from_ped_file") + def test_gen_pheno_txt_file_plink(self, mock_samples): + """test for getting gen_pheno txt file""" + mock_samples.return_value = ["Expected_1", "Expected_2", "Expected_3"] + + trait = AttributeSetter({"name": "TX"}) + dataset = AttributeSetter({"group": AttributeSetter({"name": "n_1"})}) + vals = ["value=K1", "value=K2", "value=K3"] + with mock.patch("builtins.open", mock.mock_open()) as mock_open: + results = gen_pheno_txt_file_plink(this_trait=trait, dataset=dataset, + vals=vals, pheno_filename="ph_file") + mock_open.assert_called_once_with( + "/home/user/data/ph_file.txt", "wb") + filehandler = mock_open() + calls_expected = [mock.call('FID\tIID\tTX\n'), + mock.call('Expected_1\tExpected_1\tK1\nExpected_2\tExpected_2\tK2\nExpected_3\tExpected_3\tK3\n')] + + filehandler.write.assert_has_calls(calls_expected) + + filehandler.close.assert_called_once() + + @mock.patch("wqflask.marker_regression.plink_mapping.TMPDIR", "/home/user/data/") + @mock.patch("wqflask.marker_regression.plink_mapping.build_line_list") + def test_parse_plink_output(self, mock_line_list): + """test for parsing plink output""" + chromosomes = [0, 34, 110, 89, 123, 23, 2] + species = AttributeSetter( + {"name": "S1", "chromosomes": AttributeSetter({"chromosomes": chromosomes})}) + + fake_file = """0 AACCAT T98.6 0.89\n2 AATA B45 0.3\n121 ACG B56.4 NA""" + + mock_line_list.side_effect = [["0", "AACCAT", "T98.6", "0.89"], [ + "2", "AATA", "B45", "0.3"], ["121", "ACG", "B56.4", "NA"]] + with mock.patch("builtins.open", mock.mock_open(read_data=fake_file)) as mock_open: + parse_results = parse_plink_output( + output_filename="P1_file", species=species) + mock_open.assert_called_once_with( + "/home/user/data/P1_file.qassoc", "rb") + expected = (2, {'AACCAT': 0.89, 'AATA': 0.3}) + + self.assertEqual(parse_results, expected) diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_qtlreaper_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_qtlreaper_mapping.py new file mode 100644 index 00000000..b47f877a --- /dev/null +++ b/wqflask/tests/unit/wqflask/marker_regression/test_qtlreaper_mapping.py @@ -0,0 +1,21 @@ +import unittest +from unittest import mock +from wqflask.marker_regression.qtlreaper_mapping import gen_pheno_txt_file + +#issues some methods in genofile object are not defined +#modify samples should equal to vals +class TestQtlReaperMapping(unittest.TestCase): + @mock.patch("wqflask.marker_regression.qtlreaper_mapping.TEMPDIR", "/home/user/data") + def test_gen_pheno_txt_file(self): + vals=["V1","x","V4","V3","x"] + samples=["S1","S2","S3","S4","S5"] + trait_filename="trait_file" + with mock.patch("builtins.open", mock.mock_open())as mock_open: + gen_pheno_txt_file(samples=samples,vals=vals,trait_filename=trait_filename) + mock_open.assert_called_once_with("/home/user/data/gn2/trait_file.txt","w") + filehandler=mock_open() + write_calls= [mock.call('Trait\t'),mock.call('S1\tS3\tS4\n'),mock.call('T1\t'),mock.call('V1\tV4\tV3')] + + filehandler.write.assert_has_calls(write_calls) + + diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_rqtl_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_rqtl_mapping.py new file mode 100644 index 00000000..c585f1df --- /dev/null +++ b/wqflask/tests/unit/wqflask/marker_regression/test_rqtl_mapping.py @@ -0,0 +1,48 @@ +import unittest +from unittest import mock +from wqflask import app +from wqflask.marker_regression.rqtl_mapping import get_trait_data_type +from wqflask.marker_regression.rqtl_mapping import sanitize_rqtl_phenotype +from wqflask.marker_regression.rqtl_mapping import sanitize_rqtl_names + +class TestRqtlMapping(unittest.TestCase): + + def setUp(self): + self.app_context=app.app_context() + self.app_context.push() + + def tearDown(self): + self.app_context.pop() + + + @mock.patch("wqflask.marker_regression.rqtl_mapping.g") + @mock.patch("wqflask.marker_regression.rqtl_mapping.logger") + def test_get_trait_data(self,mock_logger,mock_db): + """test for getting trait data_type return True""" + query_value="""SELECT value FROM TraitMetadata WHERE type='trait_data_type'""" + mock_db.db.execute.return_value.fetchone.return_value=["""{"type":"trait_data_type","name":"T1","traid_id":"fer434f"}"""] + results=get_trait_data_type("traid_id") + mock_db.db.execute.assert_called_with(query_value) + self.assertEqual(results,"fer434f") + + def test_sanitize_rqtl_phenotype(self): + """test for sanitizing rqtl phenotype""" + vals=['f',"x","r","x","x"] + results=sanitize_rqtl_phenotype(vals) + expected_phenotype_string='c(f,NA,r,NA,NA)' + + self.assertEqual(results,expected_phenotype_string) + + def test_sanitize_rqtl_names(self): + """test for sanitzing rqtl names""" + vals=['f',"x","r","x","x"] + expected_sanitized_name="c('f',NA,'r',NA,NA)" + results=sanitize_rqtl_names(vals) + self.assertEqual(expected_sanitized_name,results) + + + + + + + diff --git a/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py b/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py new file mode 100644 index 00000000..4129cc0c --- /dev/null +++ b/wqflask/tests/unit/wqflask/marker_regression/test_run_mapping.py @@ -0,0 +1,284 @@ +import unittest +import datetime +from unittest import mock + +from wqflask.marker_regression.run_mapping import get_genofile_samplelist +from wqflask.marker_regression.run_mapping import geno_db_exists +from wqflask.marker_regression.run_mapping import write_input_for_browser +from wqflask.marker_regression.run_mapping import export_mapping_results +from wqflask.marker_regression.run_mapping import trim_markers_for_figure +from wqflask.marker_regression.run_mapping import get_perm_strata +from wqflask.marker_regression.run_mapping import get_chr_lengths + + +class AttributeSetter: + def __init__(self, obj): + for k, v in obj.items(): + setattr(self, k, v) + + +class MockGroup(AttributeSetter): + + def get_genofiles(self): + return [{"location": "~/genofiles/g1_file", "sample_list": ["S1", "S2", "S3", "S4"]}] + + +class TestRunMapping(unittest.TestCase): + def setUp(self): + + self.group = MockGroup( + {"genofile": "~/genofiles/g1_file", "name": "GP1_", "species": "Human"}) + chromosomes = { + "3": AttributeSetter({ + "name": "C1", + "length": "0.04" + }), + "4": AttributeSetter({ + "name": "C2", + "length": "0.03" + }), + "5": AttributeSetter({ + "name": "C4", + "length": "0.01" + }) + } + self.dataset = AttributeSetter( + {"fullname": "dataser_1", "group": self.group, "type": "ProbeSet"}) + + self.chromosomes = AttributeSetter({"chromosomes": chromosomes}) + self.trait = AttributeSetter( + {"symbol": "IGFI", "chr": "X1", "mb": 123313}) + + def tearDown(self): + self.dataset = AttributeSetter( + {"group": {"location": "~/genofiles/g1_file"}}) + + def test_get_genofile_samplelist(self): + + results_1 = get_genofile_samplelist(self.dataset) + self.assertEqual(results_1, ["S1", "S2", "S3", "S4"]) + self.group.genofile = "~/genofiles/g2_file" + result_2 = get_genofile_samplelist(self.dataset) + self.assertEqual(result_2, []) + + @mock.patch("wqflask.marker_regression.run_mapping.data_set") + def test_if_geno_db_exists(self, mock_data_set): + mock_data_set.create_dataset.side_effect = [ + AttributeSetter({}), Exception()] + results_no_error = geno_db_exists(self.dataset) + results_with_error = geno_db_exists(self.dataset) + + self.assertEqual(mock_data_set.create_dataset.call_count, 2) + self.assertEqual(results_with_error, "False") + self.assertEqual(results_no_error, "True") + + def test_trim_markers_for_figure(self): + + markers = [{ + "name": "MK1", + "chr": "C1", + "cM": "1", + "Mb": "12000", + "genotypes": [], + "dominance":"TT", + "additive":"VA", + "lod_score":0.5 + }, + { + "name": "MK2", + "chr": "C2", + "cM": "15", + "Mb": "10000", + "genotypes": [], + "lod_score":0.7 + }, + { + "name": "MK1", + "chr": "C3", + "cM": "45", + "Mb": "1", + "genotypes": [], + "dominance":"Tt", + "additive":"VE", + "lod_score":1 + }] + + marker_2 = [{ + "name": "MK1", + "chr": "C1", + "cM": "1", + "Mb": "12000", + "genotypes": [], + "dominance":"TT", + "additive":"VA", + "p_wald":4.6 + }] + results = trim_markers_for_figure(markers) + result_2 = trim_markers_for_figure(marker_2) + expected = [ + { + "name": "MK1", + "chr": "C1", + "cM": "1", + "Mb": "12000", + "genotypes": [], + "dominance":"TT", + "additive":"VA", + "lod_score":0.5 + }, + { + "name": "MK1", + "chr": "C3", + "cM": "45", + "Mb": "1", + "genotypes": [], + "dominance":"Tt", + "additive":"VE", + "lod_score":1 + } + + ] + self.assertEqual(results, expected) + self.assertEqual(result_2, marker_2) + + def test_export_mapping_results(self): + """test for exporting mapping results""" + datetime_mock = mock.Mock(wraps=datetime.datetime) + datetime_mock.now.return_value = datetime.datetime( + 2019, 9, 1, 10, 12, 12) + + markers = [{ + "name": "MK1", + "chr": "C1", + "cM": "1", + "Mb": "12000", + "genotypes": [], + "dominance":"TT", + "additive":"VA", + "lod_score":3 + }, + { + "name": "MK2", + "chr": "C2", + "cM": "15", + "Mb": "10000", + "genotypes": [], + "lod_score":7 + }, + { + "name": "MK1", + "chr": "C3", + "cM": "45", + "Mb": "1", + "genotypes": [], + "dominance":"Tt", + "additive":"VE", + "lod_score":7 + }] + + with mock.patch("builtins.open", mock.mock_open()) as mock_open: + + with mock.patch("wqflask.marker_regression.run_mapping.datetime.datetime", new=datetime_mock): + export_mapping_results(dataset=self.dataset, trait=self.trait, markers=markers, + results_path="~/results", mapping_scale="physic", score_type="-log(p)") + + write_calls = [ + mock.call('Time/Date: 09/01/19 / 10:12:12\n'), + mock.call('Population: Human GP1_\n'), mock.call( + 'Data Set: dataser_1\n'), + mock.call('Gene Symbol: IGFI\n'), mock.call( + 'Location: X1 @ 123313 Mb\n'), + mock.call('\n'), mock.call('Name,Chr,'), + mock.call('Mb,-log(p)'), mock.call('Cm,-log(p)'), + mock.call(',Additive'), mock.call(',Dominance'), + mock.call('\n'), mock.call('MK1,C1,'), + mock.call('12000,'), mock.call('1,'), + mock.call('3'), mock.call(',VA'), + mock.call(',TT'), mock.call('\n'), + mock.call('MK2,C2,'), mock.call('10000,'), + mock.call('15,'), mock.call('7'), + mock.call('\n'), mock.call('MK1,C3,'), + mock.call('1,'), mock.call('45,'), + mock.call('7'), mock.call(',VE'), + mock.call(',Tt') + + ] + mock_open.assert_called_once_with("~/results", "w+") + filehandler = mock_open() + filehandler.write.assert_has_calls(write_calls) + + @mock.patch("wqflask.marker_regression.run_mapping.random.choice") + def test_write_input_for_browser(self, mock_choice): + """test for writing input for browser""" + mock_choice.side_effect = ["F", "i", "l", "e", "s", "x"] + with mock.patch("builtins.open", mock.mock_open()) as mock_open: + expected = ['GP1__Filesx_GWAS', 'GP1__Filesx_ANNOT'] + + results = write_input_for_browser( + this_dataset=self.dataset, gwas_results={}, annotations={}) + self.assertEqual(results, expected) + + def test_get_perm_strata(self): + categorical_vars = ["C1", "C2", "W1"] + used_samples = ["S1", "S2"] + sample_list = AttributeSetter({"sample_attribute_values": { + "S1": { + "C1": "c1_value", + "C2": "c2_value", + "W1": "w1_value" + + }, + "S2": { + "W1": "w2_value", + "W2": "w2_value" + + }, + "S3": { + + "C1": "c1_value", + "C2": "c2_value" + + }, + + }}) + + results = get_perm_strata(this_trait={}, sample_list=sample_list, + categorical_vars=categorical_vars, used_samples=used_samples) + self.assertEqual(results, [2, 1]) + + def test_get_chr_length(self): + """test for getting chromosome length""" + chromosomes = AttributeSetter({"chromosomes": self.chromosomes}) + dataset = AttributeSetter({"species": chromosomes}) + results = get_chr_lengths( + mapping_scale="physic", mapping_method="reaper", dataset=dataset, qtl_results=[]) + chr_lengths = [] + for key, chromo in self.chromosomes.chromosomes.items(): + chr_lengths.append({"chr": chromo.name, "size": chromo.length}) + + self.assertEqual(chr_lengths, results) + + qtl_results = [{ + "chr": "16", + "cM": "0.2" + }, + { + "chr": "12", + "cM": "0.5" + }, + { + "chr": "18", + "cM": "0.1" + }, + { + "chr": "22", + "cM": "0.4" + }, + ] + + result_with_other_mapping_scale = get_chr_lengths( + mapping_scale="other", mapping_method="reaper", dataset=dataset, qtl_results=qtl_results) + expected_value = [{'chr': '1', 'size': '0'}, { + 'chr': '16', 'size': '500000.0'}, {'chr': '18', 'size': '400000.0'}] + + self.assertEqual(result_with_other_mapping_scale, expected_value) diff --git a/wqflask/wqflask/correlation/show_corr_results.py b/wqflask/wqflask/correlation/show_corr_results.py index 4c2b64ba..51aa1622 100644 --- a/wqflask/wqflask/correlation/show_corr_results.py +++ b/wqflask/wqflask/correlation/show_corr_results.py @@ -484,75 +484,79 @@ def generate_corr_json(corr_results, this_trait, dataset, target_dataset, for_ap if trait.view == False: continue results_dict = {} - if not for_api: - results_dict['checkbox'] = "<INPUT TYPE='checkbox' NAME='searchResult' class='checkbox trait_checkbox' style='padding-right: 0px;' VALUE='" + hmac.hmac_creation('{}:{}'.format(trait.name, trait.dataset.name)) + "'>" - results_dict['index'] = i + 1 - results_dict['trait_id'] = "<a href='/show_trait?trait_id="+str(trait.name)+"&dataset="+str(dataset.name)+"'>"+str(trait.name)+"</a>" - else: - results_dict['trait_id'] = trait.name + results_dict['index'] = i + 1 + results_dict['trait_id'] = trait.name + results_dict['dataset'] = trait.dataset.name + results_dict['hmac'] = hmac.data_hmac('{}:{}'.format(trait.name, trait.dataset.name)) if target_dataset.type == "ProbeSet": results_dict['symbol'] = trait.symbol - results_dict['description'] = trait.description_display + results_dict['description'] = "N/A" results_dict['location'] = trait.location_repr - results_dict['mean'] = float(trait.mean) + results_dict['mean'] = "N/A" + results_dict['lrs_score'] = "N/A" + results_dict['additive'] = "N/A" + if bool(trait.description_display): + results_dict['description'] = trait.description_display + if bool(trait.mean): + results_dict['mean'] = float(trait.mean) if trait.LRS_score_repr != "N/A": - results_dict['lrs_score'] = "%.1f" % float(trait.LRS_score_repr) - else: - results_dict['lrs_score'] = "N/A" + results_dict['lrs_score'] = f"{float(trait.LRS_score_repr):.1f}" results_dict['lrs_location'] = trait.LRS_location_repr - if trait.additive != "": - results_dict['additive'] = "%0.3f" % float(trait.additive) - else: - results_dict['additive'] = "N/A" - if for_api: - results_dict['sample_r'] = "%0.3f" % float(trait.sample_r) - else: - results_dict['sample_r'] = "<a target='_blank' href='corr_scatter_plot?dataset_1=" + str(dataset.name) + "&dataset_2=" + str(trait.dataset.name) + "&trait_1=" + str(this_trait.name) + "&trait_2=" + str(trait.name) + "'>" + "%0.3f" % float(trait.sample_r) + "</a>" + if bool(trait.additive): + results_dict['additive'] = f"{float(trait.additive):.3f}" + results_dict['sample_r'] = f"{float(trait.sample_r):.3f}" results_dict['num_overlap'] = trait.num_overlap - results_dict['sample_p'] = "%0.3e" % float(trait.sample_p) - if trait.lit_corr == "" or trait.lit_corr == 0: - results_dict['lit_corr'] = "--" - else: - results_dict['lit_corr'] = "%0.3f" % float(trait.lit_corr) - if trait.tissue_corr == "" or trait.tissue_corr == 0: - results_dict['tissue_corr'] = "--" - else: - results_dict['tissue_corr'] = "%0.3f" % float(trait.tissue_corr) + results_dict['sample_p'] = f"{float(trait.sample_p):.3e}" + results_dict['lit_corr'] = "--" + results_dict['tissue_corr'] = "--" + results_dict['tissue_pvalue'] = "--" + if bool(trait.lit_corr): + results_dict['lit_corr'] = f"{float(trait.lit_corr):.3f}" + if bool(trait.tissue_corr): + results_dict['tissue_corr'] = f"{float(trait.tissue_corr):.3f}" + results_dict['tissue_pvalue'] = f"{float(trait.tissue_pvalue):.3e}" elif target_dataset.type == "Publish": - results_dict['description'] = trait.description_display - results_dict['authors'] = trait.authors - if trait.pubmed_id: + results_dict['abbreviation_display'] = "N/A" + results_dict['description'] = "N/A" + results_dict['authors_display'] = "N/A" + results_dict['additive'] = "N/A" + if for_api: + results_dict['pubmed_id'] = "N/A" + results_dict['year'] = "N/A" + else: + results_dict['pubmed_link'] = "N/A" + results_dict['pubmed_text'] = "N/A" + + if bool(trait.abbreviation): + results_dict['abbreviation_display'] = trait.abbreviation + if bool(trait.description_display): + results_dict['description'] = trait.description_display + if bool(trait.authors): + authors_list = trait.authors.split(',') + if len(authors_list) > 6: + results_dict['authors_display'] = ", ".join(authors_list[:6]) + ", et al." + else: + results_dict['authors_display'] = trait.authors + if bool(trait.pubmed_id): if for_api: results_dict['pubmed_id'] = trait.pubmed_id results_dict['year'] = trait.pubmed_text else: - results_dict['pubmed'] = "<a href='" + trait.pubmed_link + "'> " + trait.pubmed_text + "</a>" - else: - if for_api: - results_dict['pubmed_id'] = "N/A" - results_dict['year'] = "N/A" - else: - results_dict['pubmed'] = "N/A" + results_dict['pubmed_link'] = trait.pubmed_link + results_dict['pubmed_text'] = trait.pubmed_text + results_dict['lrs_score'] = trait.LRS_score_repr results_dict['lrs_location'] = trait.LRS_location_repr - if trait.additive != "": - results_dict['additive'] = "%0.3f" % float(trait.additive) - else: - results_dict['additive'] = "N/A" - if for_api: - results_dict['sample_r'] = "%0.3f" % trait.sample_r - else: - results_dict['sample_r'] = "<a target='_blank' href='corr_scatter_plot?dataset_1=" + str(dataset.name) + "&dataset_2=" + str(trait.dataset.name) + "&trait_1=" + str(this_trait.name) + "&trait_2=" + str(trait.name) + "'>" + "%0.3f" % trait.sample_r + "</a>" + if bool(trait.additive): + results_dict['additive'] = f"{float(trait.additive):.3f}" + results_dict['sample_r'] = f"{float(trait.sample_r):.3f}" results_dict['num_overlap'] = trait.num_overlap - results_dict['sample_p'] = "%0.3e" % float(trait.sample_p) + results_dict['sample_p'] = f"{float(trait.sample_p):.3e}" else: - results_dict['lrs_location'] = trait.LRS_location_repr - if for_api: - results_dict['sample_r'] = "%0.3f" % trait.sample_r - else: - results_dict['sample_r'] = "<a target='_blank' href='corr_scatter_plot?dataset_1=" + str(dataset.name) + "&dataset_2=" + str(trait.dataset.name) + "&trait_1=" + str(this_trait.name) + "&trait_2=" + str(trait.name) + "'>" + "%0.3f" % float(trait.sample_r) + "</a>" + results_dict['location'] = trait.location_repr + results_dict['sample_r'] = f"{float(trait.sample_r):.3f}" results_dict['num_overlap'] = trait.num_overlap - results_dict['sample_p'] = "%0.3e" % float(trait.sample_p) + results_dict['sample_p'] = f"{float(trait.sample_p):.3e}" results_list.append(results_dict) diff --git a/wqflask/wqflask/export_traits.py b/wqflask/wqflask/export_traits.py index 3a886537..6fb760e0 100644 --- a/wqflask/wqflask/export_traits.py +++ b/wqflask/wqflask/export_traits.py @@ -44,7 +44,7 @@ def export_search_results_csv(targs): if targs['filter_term'] != "None": metadata.append(["Search Filter Terms: " + targs['filter_term']]) metadata.append(["Exported Row Number: " + str(len(table_rows))]) - metadata.append(["Funding for The GeneNetwork: NIAAA (U01AA13499, U24AA13513), NIDA, NIMH, and NIAAA (P20-DA21131), NCI MMHCC (U01CA105417), and NCRR (U01NR 105417)"]) + metadata.append(["Funding for The GeneNetwork: NIGMS (R01 GM123489, 2017-2021), NIDA (P30 DA044223, 2017-2022), NIA (R01AG043930, 2013-2018), NIAAA (U01 AA016662, U01 AA013499, U24 AA013513, U01 AA014425, 2006-2017), NIDA/NIMH/NIAAA (P20-DA 21131, 2001-2012), NCI MMHCC (U01CA105417), NCRR/BIRN (U24 RR021760)"]) metadata.append([]) trait_list = [] @@ -54,7 +54,7 @@ def export_search_results_csv(targs): trait_ob = retrieve_trait_info(trait_ob, trait_ob.dataset, get_qtl_info=True) trait_list.append(trait_ob) - table_headers = ['Species', 'Group', 'Dataset', 'Record ID', 'Symbol', 'Description', 'ProbeTarget', 'PubMed_ID', 'Chr', 'Mb', 'Alias', 'Gene_ID', 'Homologene_ID', 'UniGene_ID', 'Strand_Probe', 'Probe_set_specificity', 'Probe_set_BLAT_score', 'Probe_set_BLAT_Mb_start', 'Probe_set_BLAT_Mb_end', 'QTL_Chr', 'QTL_Mb', 'Locus_at_Peak', 'Max_LRS', 'P_value_of_MAX', 'Mean_Expression'] + table_headers = ['Index', 'URL', 'Species', 'Group', 'Dataset', 'Record ID', 'Symbol', 'Description', 'ProbeTarget', 'PubMed_ID', 'Chr', 'Mb', 'Alias', 'Gene_ID', 'Homologene_ID', 'UniGene_ID', 'Strand_Probe', 'Probe_set_specificity', 'Probe_set_BLAT_score', 'Probe_set_BLAT_Mb_start', 'Probe_set_BLAT_Mb_end', 'QTL_Chr', 'QTL_Mb', 'Locus_at_Peak', 'Max_LRS', 'P_value_of_MAX', 'Mean_Expression'] traits_by_group = sort_traits_by_group(trait_list) @@ -77,7 +77,7 @@ def export_search_results_csv(targs): csv_rows.append(full_headers) - for trait in group_traits: + for i, trait in enumerate(group_traits): if getattr(trait, "symbol", None): trait_symbol = getattr(trait, "symbol") elif getattr(trait, "abbreviation", None): @@ -85,6 +85,8 @@ def export_search_results_csv(targs): else: trait_symbol = "N/A" row_contents = [ + i + 1, + "https://genenetwork.org/show_trait?trait_id=" + str(trait.name) + "&dataset=" + str(trait.dataset.name), trait.dataset.group.species, trait.dataset.group.name, trait.dataset.name, diff --git a/wqflask/wqflask/marker_regression/gemma_mapping.py b/wqflask/wqflask/marker_regression/gemma_mapping.py index 68a8d5ba..02f91a32 100644 --- a/wqflask/wqflask/marker_regression/gemma_mapping.py +++ b/wqflask/wqflask/marker_regression/gemma_mapping.py @@ -31,16 +31,11 @@ def run_gemma(this_trait, this_dataset, samples, vals, covariates, use_loco, maf gwa_output_filename = this_dataset.group.name + "_GWA_" + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) this_chromosomes = this_dataset.species.chromosomes.chromosomes - chr_list_string = "" - for i in range(len(this_chromosomes)): - if i < (len(this_chromosomes) - 1): - chr_list_string += this_chromosomes[i+1].name + "," - else: - chr_list_string += this_chromosomes[i+1].name + this_chromosomes_name=[chromosome.name for chromosome in this_chromosomes] + chr_list_string=",".join(this_chromosomes_name) if covariates != "": gen_covariates_file(this_dataset, covariates, samples) - if use_loco == "True": generate_k_command = GEMMA_WRAPPER_COMMAND + ' --json --loco ' + chr_list_string + ' -- ' + GEMMAOPTS + ' -g %s/%s_geno.txt -p %s/gn2/%s.txt -a %s/%s_snps.txt -gk > %s/gn2/%s.json' % (flat_files('genotype/bimbam'), genofile_name, diff --git a/wqflask/wqflask/marker_regression/plink_mapping.py b/wqflask/wqflask/marker_regression/plink_mapping.py index fd91b6ca..5d675c38 100644 --- a/wqflask/wqflask/marker_regression/plink_mapping.py +++ b/wqflask/wqflask/marker_regression/plink_mapping.py @@ -9,11 +9,11 @@ import utility.logger logger = utility.logger.getLogger(__name__ ) def run_plink(this_trait, dataset, species, vals, maf): - plink_output_filename = webqtlUtil.genRandStr("%s_%s_"%(dataset.group.name, this_trait.name)) + plink_output_filename = webqtlUtil.genRandStr(f"{dataset.group.name}_{this_trait.name}_") gen_pheno_txt_file(dataset, vals) - plink_command = PLINK_COMMAND + ' --noweb --bfile %s/%s --no-pheno --no-fid --no-parents --no-sex --maf %s --out %s%s --assoc ' % ( - flat_files('mapping'), dataset.group.name, maf, TMPDIR, plink_output_filename) + + plink_command = f"{PLINK_COMMAND} --noweb --bfile {flat_files('mapping')}/{dataset.group.name} --no-pheno --no-fid --no-parents --no-sex --maf {maf} --out { TMPDIR}{plink_output_filename} --assoc " logger.debug("plink_command:", plink_command) os.system(plink_command) @@ -29,12 +29,12 @@ def gen_pheno_txt_file(this_dataset, vals): """Generates phenotype file for GEMMA/PLINK""" current_file_data = [] - with open("{}/{}.fam".format(flat_files('mapping'), this_dataset.group.name), "r") as outfile: + with open(f"{flat_files('mapping')}/{this_dataset.group.name}.fam", "r") as outfile: for i, line in enumerate(outfile): split_line = line.split() current_file_data.append(split_line) - with open("{}/{}.fam".format(flat_files('mapping'), this_dataset.group.name), "w") as outfile: + with open(f"{flat_files('mapping')}/{this_dataset.group.name}.fam","w") as outfile: for i, line in enumerate(current_file_data): if vals[i] == "x": this_val = -9 @@ -44,8 +44,8 @@ def gen_pheno_txt_file(this_dataset, vals): def gen_pheno_txt_file_plink(this_trait, dataset, vals, pheno_filename = ''): ped_sample_list = get_samples_from_ped_file(dataset) - output_file = open("%s%s.txt" % (TMPDIR, pheno_filename), "wb") - header = 'FID\tIID\t%s\n' % this_trait.name + output_file = open(f"{TMPDIR}{pheno_filename}.txt", "wb") + header = f"FID\tIID\t{this_trait.name}\n" output_file.write(header) new_value_list = [] @@ -65,7 +65,7 @@ def gen_pheno_txt_file_plink(this_trait, dataset, vals, pheno_filename = ''): for i, sample in enumerate(ped_sample_list): j = i+1 value = new_value_list[i] - new_line += '%s\t%s\t%s\n'%(sample, sample, value) + new_line += f"{sample}\t{sample}\t{value}\n" if j%1000 == 0: output_file.write(newLine) @@ -78,7 +78,7 @@ def gen_pheno_txt_file_plink(this_trait, dataset, vals, pheno_filename = ''): # get strain name from ped file in order def get_samples_from_ped_file(dataset): - ped_file= open("{}{}.ped".format(flat_files('mapping'), dataset.group.name), "r") + ped_file= open(f"{flat_files('mapping')}{dataset.group.name}.ped","r") line = ped_file.readline() sample_list=[] @@ -98,7 +98,7 @@ def parse_plink_output(output_filename, species): threshold_p_value = 1 - result_fp = open("%s%s.qassoc"% (TMPDIR, output_filename), "rb") + result_fp = open(f"{TMPDIR}{output_filename}.qassoc","rb") line = result_fp.readline() @@ -154,7 +154,7 @@ def parse_plink_output(output_filename, species): # function: convert line from str to list; # output: lineList list ####################################################### -def build_line_list(line=None): +def build_line_list(line=""): line_list = line.strip().split(' ')# irregular number of whitespaces between columns line_list = [item for item in line_list if item !=''] line_list = [item.strip() for item in line_list] diff --git a/wqflask/wqflask/marker_regression/qtlreaper_mapping.py b/wqflask/wqflask/marker_regression/qtlreaper_mapping.py index 78b1f7b0..505ae295 100644 --- a/wqflask/wqflask/marker_regression/qtlreaper_mapping.py +++ b/wqflask/wqflask/marker_regression/qtlreaper_mapping.py @@ -17,22 +17,29 @@ def run_reaper(this_trait, this_dataset, samples, vals, json_data, num_perm, boo else: genofile_name = this_dataset.group.name - trait_filename = str(this_trait.name) + "_" + str(this_dataset.name) + "_pheno" + trait_filename =f"{str(this_trait.name)}_{str(this_dataset.name)}_pheno" gen_pheno_txt_file(samples, vals, trait_filename) - output_filename = this_dataset.group.name + "_GWA_" + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) + output_filename = (f"{this_dataset.group.name}_GWA_"+ + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) + ) bootstrap_filename = None permu_filename = None opt_list = [] if boot_check and num_bootstrap > 0: - bootstrap_filename = this_dataset.group.name + "_BOOTSTRAP_" + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) + bootstrap_filename = (f"{this_dataset.group.name}_BOOTSTRAP_" + + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) + ) opt_list.append("-b") - opt_list.append("--n_bootstrap " + str(num_bootstrap)) - opt_list.append("--bootstrap_output " + webqtlConfig.GENERATED_IMAGE_DIR + bootstrap_filename + ".txt") + opt_list.append(f"--n_bootstrap{str(num_bootstrap)}") + opt_list.append(f"--bootstrap_output{webqtlConfig.GENERATED_IMAGE_DIR}{bootstrap_filename}.txt") if num_perm > 0: - permu_filename = this_dataset.group.name + "_PERM_" + ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) + permu_filename =("{this_dataset.group.name}_PERM_" + + ''.join(random.choice(string.ascii_uppercase + + string.digits) for _ in range(6)) + ) opt_list.append("-n " + str(num_perm)) opt_list.append("--permu_output " + webqtlConfig.GENERATED_IMAGE_DIR + permu_filename + ".txt") if control_marker != "" and do_control == "true": @@ -40,13 +47,15 @@ def run_reaper(this_trait, this_dataset, samples, vals, json_data, num_perm, boo if manhattan_plot != True: opt_list.append("--interval 1") - reaper_command = REAPER_COMMAND + ' --geno {0}/{1}.geno --traits {2}/gn2/{3}.txt {4} -o {5}{6}.txt'.format(flat_files('genotype'), - genofile_name, - TEMPDIR, - trait_filename, - " ".join(opt_list), - webqtlConfig.GENERATED_IMAGE_DIR, - output_filename) + reaper_command = (REAPER_COMMAND + + ' --geno {0}/{1}.geno --traits {2}/gn2/{3}.txt {4} -o {5}{6}.txt'.format(flat_files('genotype'), + + genofile_name, + TEMPDIR, + trait_filename, + " ".join(opt_list), + webqtlConfig.GENERATED_IMAGE_DIR, + output_filename)) logger.debug("reaper_command:" + reaper_command) os.system(reaper_command) @@ -61,12 +70,13 @@ def run_reaper(this_trait, this_dataset, samples, vals, json_data, num_perm, boo suggestive = permu_vals[int(num_perm*0.37-1)] significant = permu_vals[int(num_perm*0.95-1)] - return marker_obs, permu_vals, suggestive, significant, bootstrap_vals, [output_filename, permu_filename, bootstrap_filename] + return (marker_obs, permu_vals, suggestive, significant, bootstrap_vals, + [output_filename, permu_filename, bootstrap_filename]) def gen_pheno_txt_file(samples, vals, trait_filename): """Generates phenotype file for GEMMA""" - with open("{}/gn2/{}.txt".format(TEMPDIR, trait_filename), "w") as outfile: + with open(f"{TEMPDIR}/gn2/{trait_filename}.txt","w") as outfile: outfile.write("Trait\t") filtered_sample_list = [] @@ -90,7 +100,7 @@ def parse_reaper_output(gwa_filename, permu_filename, bootstrap_filename): only_cm = False only_mb = False - with open("{}{}.txt".format(webqtlConfig.GENERATED_IMAGE_DIR, gwa_filename)) as output_file: + with open(f"{webqtlConfig.GENERATED_IMAGE_DIR}{gwa_filename}.txt") as output_file: for line in output_file: if line.startswith("ID\t"): if len(line.split("\t")) < 8: @@ -137,13 +147,13 @@ def parse_reaper_output(gwa_filename, permu_filename, bootstrap_filename): permu_vals = [] if permu_filename: - with open("{}{}.txt".format(webqtlConfig.GENERATED_IMAGE_DIR, permu_filename)) as permu_file: + with open(f"{webqtlConfig.GENERATED_IMAGE_DIR}{permu_filename}.txt") as permu_file: for line in permu_file: permu_vals.append(float(line)) bootstrap_vals = [] if bootstrap_filename: - with open("{}{}.txt".format(webqtlConfig.GENERATED_IMAGE_DIR, bootstrap_filename)) as bootstrap_file: + with open(f"{webqtlConfig.GENERATED_IMAGE_DIR}{bootstrap_filename}.txt") as bootstrap_file: for line in bootstrap_file: bootstrap_vals.append(int(line)) diff --git a/wqflask/wqflask/static/new/css/show_trait.css b/wqflask/wqflask/static/new/css/show_trait.css index 5e1a279b..39c6ba53 100644 --- a/wqflask/wqflask/static/new/css/show_trait.css +++ b/wqflask/wqflask/static/new/css/show_trait.css @@ -145,11 +145,11 @@ input.corr-location { display: inline; } -div.block-by-index-div { +div.block-div { margin-bottom: 10px; } -div.block-by-attribute-div { +div.block-div-2 { margin-top:10px; margin-bottom:10px; } diff --git a/wqflask/wqflask/static/new/css/trait_list.css b/wqflask/wqflask/static/new/css/trait_list.css index 691dcb12..b83655da 100644 --- a/wqflask/wqflask/static/new/css/trait_list.css +++ b/wqflask/wqflask/static/new/css/trait_list.css @@ -1,12 +1,20 @@ -div.tool-button-container {
- min-width: 950px;
-}
-
-div.collection-table-options {
- min-width: 1100px;
-}
-
-div.show-hide-container {
- margin-bottom: 5px;
- margin-top: 10px;
-}
\ No newline at end of file +div.tool-button-container { + min-width: 950px; +} + +div.collection-table-options { + min-width: 1100px; +} + +div.show-hide-container { + margin-bottom: 5px; + margin-top: 10px; +} + +button.active { + background: #e5e5e5; + -webkit-box-shadow: inset 0px 0px 5px #c1c1c1; + -moz-box-shadow: inset 0px 0px 5px #c1c1c1; + box-shadow: inset 0px 0px 5px #c1c1c1; + outline: none; + }
\ No newline at end of file diff --git a/wqflask/wqflask/static/new/javascript/lodheatmap.js b/wqflask/wqflask/static/new/javascript/lodheatmap.js index 965a1d53..b82c95ad 100644 --- a/wqflask/wqflask/static/new/javascript/lodheatmap.js +++ b/wqflask/wqflask/static/new/javascript/lodheatmap.js @@ -44,7 +44,9 @@ lodheatmap = function() { _ref = data.chrnames; for (_i = 0, _len = _ref.length; _i < _len; _i++) { chr = _ref[_i]; - xLR[chr[0]] = getLeftRight(data.posByChr[chr[0]]); + if (data.posByChr[chr[0]].length > 0){ + xLR[chr[0]] = getLeftRight(data.posByChr[chr[0]]); + } } zmin = 0; zmax = 0; @@ -144,7 +146,7 @@ lodheatmap = function() { }).attr("stroke", "none").attr("stroke-width", "1").on("mouseover.paneltip", function(d) { yaxis.select("text#yaxis" + d.lodindex).attr("opacity", 1); d3.select(this).attr("stroke", "black"); - return celltip.show(d); + return celltip.show(d, this); }).on("mouseout.paneltip", function(d) { yaxis.select("text#yaxis" + d.lodindex).attr("opacity", 0); d3.select(this).attr("stroke", "none"); diff --git a/wqflask/wqflask/static/new/javascript/panelutil.js b/wqflask/wqflask/static/new/javascript/panelutil.js index 3c715c81..ea55a7cf 100644 --- a/wqflask/wqflask/static/new/javascript/panelutil.js +++ b/wqflask/wqflask/static/new/javascript/panelutil.js @@ -159,7 +159,6 @@ chrscales = function(data, width, chrGap, leftMargin, pad4heatmap, mappingScale) if (mappingScale == "morgan") { max_pos = d3.max(data.posByChr[chr[0]]) - console.log("max_pos:", max_pos) data.xscale[chr[0]] = d3.scale.linear().domain([chrStart[i], max_pos]).range([data.chrStart[i], data.chrEnd[i]]); } else { diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 86660126..9ffef4f8 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -1,42 +1,87 @@ +change_buttons = function() { + var button, buttons, item, num_checked, text, _i, _j, _k, _l, _len, _len2, _len3, _len4, _results, _results2; + buttons = ["#add", "#remove"]; + + num_checked = 0 + 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){ + num_checked += 1 + } + } + + if (num_checked === 0) { + for (_i = 0, _len = buttons.length; _i < _len; _i++) { + button = buttons[_i]; + $(button).prop("disabled", true); + } + } else { + for (_j = 0, _len2 = buttons.length; _j < _len2; _j++) { + button = buttons[_j]; + $(button).prop("disabled", false); + } + } +}; + $(function() { - var add, change_buttons, checked_traits, deselect_all, invert, remove, removed_traits, select_all; + var add, checked_traits, deselect_all, invert, remove, removed_traits, select_all; checked_traits = null; select_all = function() { - console.log("selected_all"); - $(".trait_checkbox").each(function() { - $(this).prop('checked', true); - if (!$(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').addClass('selected') - } - }); + table_api = $('#trait_table').DataTable(); + + check_cells = table_api.column(0).nodes().to$(); + for (let i = 0; i < check_cells.length; i++) { + check_cells[i].childNodes[0].checked = true; + } + + check_rows = table_api.rows().nodes(); + for (let i =0; i < check_rows.length; i++) { + check_rows[i].classList.add("selected"); + } + + change_buttons(); }; deselect_all = function() { - $(".trait_checkbox").each(function() { - $(this).prop('checked', false); - if ($(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').removeClass('selected') - } - }); + table_api = $('#trait_table').DataTable(); + + check_cells = table_api.column(0).nodes().to$(); + for (let i = 0; i < check_cells.length; i++) { + check_cells[i].childNodes[0].checked = false; + } + + check_rows = table_api.rows().nodes(); + for (let i =0; i < check_rows.length; i++) { + check_rows[i].classList.remove("selected") + } + + change_buttons(); }; invert = function() { - $(".trait_checkbox").each(function() { - if ($(this).prop('checked') == true) { - $(this).prop('checked', false) - } - else { - $(this).prop('checked', true) - } - - if ($(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').removeClass('selected') - } - else { - $(this).closest('tr').addClass('selected') - } - }); + 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){ + check_cells[i].childNodes[0].checked = false; + } else { + check_cells[i].childNodes[0].checked = true; + } + } + + check_rows = table_api.rows().nodes(); + for (let i =0; i < check_rows.length; i++) { + if (check_rows[i].classList.contains("selected")){ + check_rows[i].classList.remove("selected") + } else { + check_rows[i].classList.add("selected") + } + } + + change_buttons(); }; $('#searchbox').keyup(function(){ @@ -77,22 +122,6 @@ $(function() { change_buttons(); }); - $('.trait_checkbox:checkbox').change(function() { - change_buttons() - - if ($(this).is(":checked")) { - if (!$(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').addClass('selected') - } - } - else { - if ($(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').removeClass('selected') - } - } - - }); - add_to_collection = function() { var traits; traits = $("#trait_table input:checked").map(function() { @@ -117,25 +146,8 @@ $(function() { }; removed_traits = function() { - console.log('in removed_traits with checked_traits:', checked_traits); return checked_traits.closest("tr").fadeOut(); }; - change_buttons = function() { - var button, buttons, item, num_checked, text, _i, _j, _k, _l, _len, _len2, _len3, _len4, _results, _results2; - buttons = ["#add", "#remove"]; - num_checked = $('.trait_checkbox:checked').length; - if (num_checked === 0) { - for (_i = 0, _len = buttons.length; _i < _len; _i++) { - button = buttons[_i]; - $(button).prop("disabled", true); - } - } else { - for (_j = 0, _len2 = buttons.length; _j < _len2; _j++) { - button = buttons[_j]; - $(button).prop("disabled", false); - } - } - }; submit_bnw = function() { trait_data = submit_traits_to_export_or_bnw("trait_table", "submit_bnw") @@ -157,18 +169,23 @@ $(function() { }); table_dict['headers'] = headers; - rows = []; - trait_table.find('tbody tr').each(function (i, tr) { - if (trait_table.find('input[name="searchResult"]:checked').length > 0) { - if ($(this).find('input[name="searchResult"]').is(':checked')){ - rows.push($(this).find('input[name="searchResult"]:checked').val()) - } - } - else { - rows.push($(this).find('input[name="searchResult"]').val()) + selected_rows = []; + all_rows = []; //ZS: If no rows are checked, export all + table_api = $('#' + table_name).DataTable(); + check_cells = table_api.column(0).nodes().to$(); + for (let i = 0; i < check_cells.length; i++) { + this_node = check_cells[i].childNodes[0]; + all_rows.push(this_node.value) + if (this_node.checked){ + selected_rows.push(this_node.value) } - }); - table_dict['rows'] = rows; + } + + if (selected_rows.length > 0){ + table_dict['rows'] = selected_rows; + } else { + table_dict['rows'] = all_rows; + } json_table_dict = JSON.stringify(table_dict); $('input[name=export_data]').val(json_table_dict); @@ -253,8 +270,6 @@ $(function() { $("#add").click(add_to_collection); $("#submit_bnw").click(submit_bnw); $("#export_traits").click(export_traits); - $('.trait_checkbox, .btn').click(change_buttons); - let naturalAsc = $.fn.dataTableExt.oSort["natural-ci-asc"] let naturalDesc = $.fn.dataTableExt.oSort["natural-ci-desc"] diff --git a/wqflask/wqflask/static/new/javascript/show_trait.js b/wqflask/wqflask/static/new/javascript/show_trait.js index a34811f8..87c35984 100644 --- a/wqflask/wqflask/static/new/javascript/show_trait.js +++ b/wqflask/wqflask/static/new/javascript/show_trait.js @@ -568,14 +568,29 @@ create_value_dropdown = function(value) { populate_sample_attributes_values_dropdown = function() { var attribute_info, key, sample_attributes, selected_attribute, value, _i, _len, _ref, _ref1, _results; $('#attribute_values').empty(); - sample_attributes = {}; - attr_keys = Object.keys(js_data.attributes).sort(); - for (i=0; i < attr_keys.length; i++) { - attribute_info = js_data.attributes[attr_keys[i]]; - sample_attributes[attribute_info.name] = attribute_info.distinct_values; - } - selected_attribute = $('#exclude_menu').val().replace("_", " "); - _ref1 = sample_attributes[selected_attribute]; + sample_attributes = []; + + var attributes_as_list = Object.keys(js_data.attributes).map(function(key) { + return [key, js_data.attributes[key].name]; + }); + + attributes_as_list.sort(function(first, second) { + if (second[1] > first[1]){ + return -1 + } + if (first[1] > second[1]){ + return 1 + } + return 0 + }); + + for (i=0; i < attributes_as_list.length; i++) { + attribute_info = js_data.attributes[attributes_as_list[i][0]] + sample_attributes.push(attribute_info.distinct_values); + } + + selected_attribute = $('#exclude_column').val() + _ref1 = sample_attributes[selected_attribute - 1]; _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { value = _ref1[_i]; @@ -590,25 +605,37 @@ if (Object.keys(js_data.attributes).length){ populate_sample_attributes_values_dropdown(); } -$('#exclude_menu').change(populate_sample_attributes_values_dropdown); +$('#exclude_column').change(populate_sample_attributes_values_dropdown); block_by_attribute_value = function() { var attribute_name, cell_class, exclude_by_value; - attribute_name = $('#exclude_menu').val(); + + let exclude_group = $('#exclude_by_attr_group').val(); + let exclude_column = $('#exclude_column').val(); + + if (exclude_group === "other") { + var table_api = $('#samples_other').DataTable(); + } else { + var table_api = $('#samples_primary').DataTable(); + } + exclude_by_value = $('#attribute_values').val(); - cell_class = ".column_name-" + attribute_name; - return $(cell_class).each((function(_this) { - return function(index, element) { - var row; - if ($.trim($(element).text()) === exclude_by_value) { - row = $(element).parent('tr'); - return $(row).find(".trait-value-input").val("x"); - } - }; - })(this)); + + let val_nodes = table_api.column(3).nodes().to$(); + let exclude_val_nodes = table_api.column(attribute_start_pos + parseInt(exclude_column)).nodes().to$(); + + for (i = 0; i < exclude_val_nodes.length; i++) { + let this_col_value = exclude_val_nodes[i].childNodes[0].data; + let this_val_node = val_nodes[i].childNodes[0]; + + if (this_col_value == exclude_by_value){ + this_val_node.value = "x"; + } + } }; -$('#exclude_group').click(block_by_attribute_value); +$('#exclude_by_attr').click(block_by_attribute_value); + block_by_index = function() { - var end_index, error, index, index_list, index_set, index_string, start_index, _i, _j, _k, _len, _len1, _ref, _results; + var end_index, error, index, index_list, index_set, index_string, start_index, _i, _j, _k, _len, _len1, _ref; index_string = $('#remove_samples_field').val(); index_list = []; _ref = index_string.split(","); @@ -630,7 +657,7 @@ block_by_index = function() { index_list.push(index); } } - _results = []; + let block_group = $('#block_group').val(); if (block_group === "other") { table_api = $('#samples_other').DataTable(); @@ -645,6 +672,65 @@ block_by_index = function() { } }; +filter_by_value = function() { + let filter_logic = $('#filter_logic').val(); + let filter_column = $('#filter_column').val(); + let filter_value = $('#filter_value').val(); + let block_group = $('#filter_group').val(); + + if (block_group === "other") { + var table_api = $('#samples_other').DataTable(); + } else { + var table_api = $('#samples_primary').DataTable(); + } + + let val_nodes = table_api.column(3).nodes().to$(); + if (filter_column == "value"){ + var filter_val_nodes = table_api.column(3).nodes().to$(); + } + else if (filter_column == "stderr"){ + var filter_val_nodes = table_api.column(5).nodes().to$(); + } + else if (!isNaN(filter_column)){ + var filter_val_nodes = table_api.column(attribute_start_pos + parseInt(filter_column)).nodes().to$(); + } + else { + return false + } + + for (i = 0; i < filter_val_nodes.length; i++) { + if (filter_column == "value" || filter_column == "stderr"){ + var this_col_value = filter_val_nodes[i].childNodes[0].value; + } else { + var this_col_value = filter_val_nodes[i].childNodes[0].data; + } + let this_val_node = val_nodes[i].childNodes[0]; + + if(!isNaN(this_col_value) && !isNaN(filter_value)) { + if (filter_logic == "greater_than"){ + if (parseFloat(this_col_value) <= parseFloat(filter_value)){ + this_val_node.value = "x"; + } + } + else if (filter_logic == "less_than"){ + if (parseFloat(this_col_value) >= parseFloat(filter_value)){ + this_val_node.value = "x"; + } + } + else if (filter_logic == "greater_or_equal"){ + if (parseFloat(this_col_value) < parseFloat(filter_value)){ + this_val_node.value = "x"; + } + } + else if (filter_logic == "less_or_equal"){ + if (parseFloat(this_col_value) > parseFloat(filter_value)){ + this_val_node.value = "x"; + } + } + } + } +}; + hide_no_value = function() { return $('.value_se').each((function(_this) { return function(_index, element) { @@ -1528,6 +1614,12 @@ $('#block_by_index').click(function(){ block_by_index(); edit_data_change(); }); + +$('#filter_by_value').click(function(){ + filter_by_value(); + edit_data_change(); +}) + $('#exclude_group').click(edit_data_change); $('#block_outliers').click(edit_data_change); $('#reset').click(edit_data_change); diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index e37f8104..ccec495b 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -73,7 +73,7 @@ <form id="export_form" method="POST" action="/export_traits_csv"> <button class="btn btn-default" id="select_all" type="button"><span class="glyphicon glyphicon-ok"></span> Select All</button> <button class="btn btn-default" id="invert" type="button"><span class="glyphicon glyphicon-ok"></span> Invert</button> - <button class="btn btn-success" id="add" disabled="disabled" type="button"><i class="icon-plus-sign"></i> Copy</button> + <button class="btn btn-success" id="add" type="button" disabled><i class="icon-plus-sign"></i> Copy</button> <input type="hidden" name="database_name" id="database_name" value="None"> <input type="hidden" name="export_data" id="export_data" value=""> <input type="hidden" name="file_name" id="file_name" value="collection_table"> @@ -81,7 +81,7 @@ <input type="text" id="searchbox" class="form-control" style="width: 200px; display: inline; padding-bottom: 9px;" placeholder="Search Table For ..."> <input type="text" id="select_top" class="form-control" style="width: 200px; display: inline; padding-bottom: 9px;" placeholder="Select Top ..."> <button class="btn btn-default" id="deselect_all" type="button"><span class="glyphicon glyphicon-remove"></span> Deselect</button> - <button id="remove" class="btn btn-danger" data-url="/collections/remove" disabled="disabled" type="button"><i class="icon-minus-sign"></i> Delete Rows</button> + <button id="remove" class="btn btn-danger" data-url="/collections/remove" type="button" disabled><i class="icon-minus-sign"></i> Delete Rows</button> <button id="delete" class="btn btn-danger submit_special" data-url="/collections/delete" title="Delete this collection" > Delete Collection</button> </form> </div> @@ -109,10 +109,7 @@ <tbody> {% for this_trait in trait_obs %} <TR id="trait:{{ this_trait.name }}:{{ this_trait.dataset.name }}"> - <TD align="center" style="padding: 0px;"> - <INPUT TYPE="checkbox" NAME="searchResult" class="checkbox trait_checkbox" - VALUE="{{ data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name)) }}"> - </TD> + <TD align="center" style="padding: 0px;"><INPUT TYPE="checkbox" NAME="searchResult" class="checkbox trait_checkbox" VALUE="{{ data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name)) }}"></TD> <TD data-export="{{ loop.index }}" align="right">{{ loop.index }}</TD> <TD title="{{ this_trait.dataset.fullname }}" data-export="{{ this_trait.dataset.fullname }}">{{ this_trait.dataset.fullname }}</TD> <TD data-export="{{ this_trait.name }}"> @@ -178,15 +175,21 @@ <script language="javascript" type="text/javascript"> $(document).ready( function () { - - $('#trait_table tr').click(function(event) { - if (event.target.type !== 'checkbox') { - $(':checkbox', this).trigger('click'); - } - }); - - console.time("Creating table"); $('#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") + } + change_buttons() + }); + }, "columns": [ { "orderDataType": "dom-checkbox", @@ -220,7 +223,7 @@ "paging": false, "orderClasses": true } ); - console.timeEnd("Creating table"); + submit_special = function(url) { $("#collection_form").attr("action", url); diff --git a/wqflask/wqflask/templates/correlation_page.html b/wqflask/wqflask/templates/correlation_page.html index bc0b592c..aa74abf5 100644 --- a/wqflask/wqflask/templates/correlation_page.html +++ b/wqflask/wqflask/templates/correlation_page.html @@ -2,9 +2,11 @@ {% block title %}Correlation Results{% endblock %} {% block css %} <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" /> - <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" /> <link rel="stylesheet" type="text/css" href="{{ url_for('js', filename='DataTablesExtensions/buttonsBootstrap/css/buttons.bootstrap.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/trait_list.css" /> + <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" /> {% endblock %} {% block content %} <div class="container" style="min-width: 1250px;"> @@ -73,6 +75,7 @@ <div> <form id="export_form" method="POST" action="/export_traits_csv"> <button class="btn btn-default" id="select_all" type="button"><span class="glyphicon glyphicon-ok"></span> Select All</button> + <button class="btn btn-default" id="invert" type="button"><span class="glyphicon glyphicon-adjust"></span> Invert</button> <button class="btn btn-success" id="add" type="button" disabled><span class="glyphicon glyphicon-plus-sign"></span> Add</button> <input type="hidden" name="database_name" id="database_name" value="None"> <input type="hidden" name="export_data" id="export_data" value=""> @@ -112,10 +115,38 @@ </div> {% endif %} </div> - <div style="margin-bottom: 5px;"> - <b>Show/Hide Columns:</b> + <div class="show-hide-container"> + <b>Show/Hide Columns:</b> + <br> + <button class="toggle-vis" data-column="1">Index</button> + <button class="toggle-vis" data-column="2">Record</button> + {% if target_dataset.type == 'ProbeSet' %} + <button class="toggle-vis" data-column="3">Symbol</button> + <button class="toggle-vis" data-column="4">Description</button> + <button class="toggle-vis" data-column="5">Location</button> + <button class="toggle-vis" data-column="6">Mean</button> + <button class="toggle-vis" data-column="7">High P</button> + <button class="toggle-vis" data-column="8">Peak Location</button> + <button class="toggle-vis" data-column="9">Effect Size</button> + {% 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">High P</button> + <button class="toggle-vis" data-column="11">Peak Location</button> + <button class="toggle-vis" data-column="12">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> + <button class="toggle-vis" data-column="5">N</button> + <button class="toggle-vis" data-column="6">Sample p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})</button> + {% endif %} </div> - <div style="width: 100%; min-width: {% if target_dataset.type == "ProbeSet" %}1700px{% elif target_dataset.type == "Publish" %}1600px{% else %}600px{% endif %};"> + <div style="width: 100%; {% if target_dataset.type == "ProbeSet" %}min-width: 1700px;{% elif target_dataset.type == "Publish" %}min-width: 1600px;{% else %}width: 650px;{% endif %}"> <table id="trait_table" class="table-hover table-striped cell-border" style="float: left;"> <thead> <tr> @@ -127,77 +158,7 @@ </thead> <tbody> - {% for trait in correlation_results %} - <tr> - <td><INPUT TYPE="checkbox" NAME="searchResult" class="checkbox trait_checkbox" style="padding-right: 0px;" VALUE="{{ data_hmac('{}:{}'.format(trait.name, trait.dataset.name)) }}"></td> - <td data-export="{{ loop.index }}" style="padding-left: 8px; padding-right: 0px; padding-top: 4px; align: right;">{{ loop.index }}</td> - <td data-export="{{ trait.name }}"> - <a href="{{ url_for('show_trait_page', - trait_id = trait.name, - dataset = trait.dataset.name - )}}"> - {{ trait.name }} - </a> - </td> - {% if target_dataset.type == 'ProbeSet' %} - <td data-export="{{ trait.symbol }}">{{ trait.symbol }}</td> - <td data-export="{{ trait.description_display }}">{{ trait.description_display }}</TD> - <td data-export="{{ trait.location_repr }}" style="white-space: nowrap;">{{ trait.location_repr }}</td> - <td data-export="{{ '%0.3f' % trait.mean|float }}" align="right">{{ '%0.3f' % trait.mean|float }}</td> - <td data-export="{{ '%0.3f'|format(trait.sample_r) }}"" align="right"><a target="_blank" href="corr_scatter_plot?dataset_1={% if dataset.name == 'Temp' %}Temp_{{ dataset.group.name }}{% else %}{{ dataset.name }}{% endif %}&dataset_2={% if trait.dataset.name == 'Temp' %}Temp_{{ trait.dataset.group.name }}{% else %}{{ trait.dataset.name }}{% endif %}&trait_1={{ this_trait.name }}&trait_2={{ trait.name }}">{{ '%0.3f'|format(trait.sample_r) }}</a></td> - <td data-export="{{ trait.num_overlap }}" align="right">{{ trait.num_overlap }}</td> - <td data-export="{{ '%0.3e'|format(trait.sample_p) }}" align="right">{{ '%0.3e'|format(trait.sample_p) }}</td> - {% if trait.lit_corr == "" or trait.lit_corr == 0.000 %} - <td data-export="--" align="right">--</td> - {% else %} - <td data-export="{{ '%0.3f'|format(trait.lit_corr) }}" align="right">{{ '%0.3f'|format(trait.lit_corr) }}</td> - {% endif %} - {% if trait.tissue_corr == "" or trait.tissue_corr == 0.000 %} - <td data-export="--" align="right">--</td> - <td data-export="--" align="right">--</td> - {% else %} - <td data-export="{{ '%0.3f'|format(trait.tissue_corr) }}" align="right">{{ '%0.3f'|format(trait.tissue_corr) }}</td> - <td data-export="{{ '%0.3e'|format(trait.tissue_pvalue) }}" align="right">{{ '%0.3e'|format(trait.tissue_pvalue) }}</td> - {% endif %} - <td data-export={% if trait.LRS_score_repr != "N/A" %}"{{ '%0.1f' % trait.LRS_score_repr|float }}"{% else %}"N/A"{% endif %} align="right">{% if trait.LRS_score_repr != "N/A" %}{{ '%0.1f' % trait.LRS_score_repr|float }}{% else %}N/A{% endif %}</td> - <td data-export="{{ trait.LRS_location_repr }}" align="right">{{ trait.LRS_location_repr }}</td> - <td data-export={% if trait.additive != "" %}"{{ '%0.3f' % trait.additive|float }}"{% else %}"N/A"{% endif %} align="right">{% if trait.additive != "" %}{{ '%0.3f' % trait.additive|float }}{% else %}N/A{% endif %}</td> - {% elif target_dataset.type == "Publish" %} - {% if trait.abbreviation %} - <td title="{{ trait.abbreviation }}" data-export="{{ trait.abbreviation }}">{% if trait.abbreviation|length > 20 %}{{ trait.abbreviation[:20] }}...{% else %}{{ trait.abbreviation }}{% endif %}</td> - {% else %} - <td data-export="N/A">N/A</td> - {% endif %} - <td data-export="{{ trait.description_display }}">{% if trait.description_display|length > 70 %}{{ trait.description_display[:70] }}...{% else %}{{ trait.description_display }}{% endif %}</td> - {% if trait.authors %} - {% set authors_list = trait.authors.split(',') %} - <td data-export="{{ trait.authors }}">{% if authors_list|length > 6 %}{{ authors_list[:6]|join(', ') }}, et al.{% else %}{{ trait.authors }}{% endif %}</td> - {% else %} - <td data-export="N/A">N/A</td> - {% endif %} - <td data-export="{{ trait.pubmed_text }}"> - {% if trait.pubmed_text != "N/A" %} - <a href="{{ trait.pubmed_link }}"> - {{ trait.pubmed_text }} - </a> - {% else %} - {{ trait.pubmed_text }} - {% endif %} - </td> - <td data-export="{{ '%0.3f'|format(trait.sample_r) }}"" align="right"><a target="_blank" href="corr_scatter_plot?dataset_1={% if dataset.name == 'Temp' %}Temp_{{ dataset.group.name }}{% else %}{{ dataset.name }}{% endif %}&dataset_2={{ trait.dataset.name }}&trait_1={{ this_trait.name }}&trait_2={{ trait.name }}">{{ '%0.3f'|format(trait.sample_r) }}</a></td> - <td data-export="{{ trait.num_overlap }}" align="right">{{ trait.num_overlap }}</td> - <td data-export="{{ '%0.3e'|format(trait.sample_p) }}" align="right">{{ '%0.3e'|format(trait.sample_p) }}</td> - <td data-export="{{ trait.LRS_score_repr }}" align="right">{{ trait.LRS_score_repr }}</td> - <td data-export="{{ trait.LRS_location_repr }}" align="right">{{ trait.LRS_location_repr }}</td> - <td data-export={% if trait.additive != "" %}"{{ '%0.3f' % trait.additive|float }}"{% else %}"N/A"{% endif %} align="right">{% if trait.additive != "" %}{{ '%0.3f' % trait.additive|float }}{% else %}N/A{% endif %}</td> - {% elif target_dataset.type == "Geno" %} - <td data-export="{{ trait.location_repr }}" align="right">{{ trait.location_repr }}</TD> - <td data-export="{{ '%0.3f'|format(trait.sample_r) }}"" align="right"><a target="_blank" href="corr_scatter_plot?dataset_1={% if dataset.name == 'Temp' %}Temp_{{ dataset.group.name }}{% else %}{{ dataset.name }}{% endif %}&dataset_2={{ trait.dataset.name }}&trait_1={{ this_trait.name }}&trait_2={{ trait.name }}">{{ '%0.3f'|format(trait.sample_r) }}</a></td> - <td data-export="{{ trait.num_overlap }}" align="right">{{ trait.num_overlap }}</td> - <td data-export="{{ '%0.3e'|format(trait.sample_p) }}" align="right">{{ '%0.3e'|format(trait.sample_p) }}</td> - {% endif %} - </tr> - {% endfor %} + <td colspan="100%" align="center"><br><b><font size="15">Loading...</font></b><br></td> </tbody> </table> </div> @@ -215,6 +176,8 @@ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/buttons/js/dataTables.buttons.min.js') }}"></script> <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/buttons/js/buttons.colVis.min.js') }}"></script> <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/plugins/sorting/natural.js') }}"></script> + <script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js"></script> + <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/scroller/2.0.2/js/dataTables.scroller.min.js"></script> <script type="text/javascript" charset="utf-8"> var table_json = {{ json_results | safe }} @@ -294,51 +257,7 @@ {% endif %} $(document).ready( function () { - - $('#trait_table tr').click(function(event) { - if (event.target.type !== 'checkbox') { - $(':checkbox', this).trigger('click'); - } - }); - - function change_buttons() { - buttons = ["#add", "#remove"]; - num_checked = $('.trait_checkbox:checked').length; - if (num_checked === 0) { - for (_i = 0, _len = buttons.length; _i < _len; _i++) { - button = buttons[_i]; - $(button).prop("disabled", true); - } - } else { - for (_j = 0, _len2 = buttons.length; _j < _len2; _j++) { - button = buttons[_j]; - $(button).prop("disabled", false); - } - } - if ($(this).is(":checked")) { - if (!$(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').addClass('selected') - } - } - else { - if ($(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').removeClass('selected') - } - } - } - - console.time("Creating table"); - - {% if target_dataset.type == "ProbeSet" %} table_conf = { - "drawCallback": function( settings ) { - $('#trait_table tr').click(function(event) { - if (event.target.type !== 'checkbox') { - $(':checkbox', this).trigger('click'); - } - }); - $('.trait_checkbox:checkbox').on("change", change_buttons); - }, buttons: [ { extend: 'columnsToggle', @@ -352,135 +271,306 @@ postfixButtons: [ 'colvisRestore' ] } ], + '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") + } + }); + $('.trait_checkbox:checkbox').on("change", change_buttons); + }, + "data": table_json, + "columns": [ + { + 'data': null, + 'width': "25px", + 'orderDataType': "dom-checkbox", + 'orderSequence': [ "desc", "asc"], + 'render': function(data, type, row, meta) { + return '<input type="checkbox" name="searchResult" class="checkbox trait_checkbox" value="' + data.hmac + '">' + } + }, + { + 'title': "Index", + 'type': "natural", + 'width': "30px", + 'data': "index" + }, + { + 'title': "Record", + 'type': "natural-minus-na", + 'data': null, + 'width': "60px", + 'render': function(data, type, row, meta) { + return '<a target="_blank" href="/show_trait?trait_id=' + data.trait_id + '&dataset=' + data.dataset + '">' + data.trait_id + '</a>' + } + }{% if target_dataset.type == 'ProbeSet' %}, + { + 'title': "Symbol", + 'type': "natural", + 'width': "120px", + 'data': "symbol" + }, + { + 'title': "Description", + 'type': "natural", + 'data': null, + 'render': function(data, type, row, meta) { + try { + return decodeURIComponent(escape(data.description)) + } catch(err){ + return escape(data.description) + } + } + }, + { + 'title': "Location", + 'type': "natural-minus-na", + 'width': "125px", + 'data': "location" + }, + { + 'title': "Mean", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "mean", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Sample {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "sample_r", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "N", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "num_overlap", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Sample p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})", + 'type': "natural-minus-na", + 'width': "65px", + 'data': "sample_p", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Lit {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "lit_corr", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Tissue {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "tissue_corr", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Tissue p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "tissue_pvalue", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "High P<a href=\"http://gn1.genenetwork.org/glossary.html#LRS\" target=\"_blank\" style=\"color: white;\"> <i class=\"fa fa-info-circle\" aria-hidden=\"true\"></i></a>", + 'type': "natural-minus-na", + 'data': "lrs_score", + 'width': "60px", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Peak Location", + 'type': "natural-minus-na", + 'width': "125px", + 'data': "lrs_location" + }, + { + 'title': "Effect Size<a href=\"http://gn1.genenetwork.org/glossary.html#A\" target=\"_blank\" style=\"color: white;\"> <i class=\"fa fa-info-circle\" aria-hidden=\"true\"></i></a>", + 'type': "natural-minus-na", + 'data': "additive", + 'width': "85px", + 'orderSequence': [ "desc", "asc"] + }{% elif target_dataset.type == 'Publish' %}, + { + 'title': "Abbreviation", + 'type': "natural", + 'width': "200px", + 'data': null, + 'render': function(data, type, row, meta) { + try { + return decodeURIComponent(escape(data.abbreviation_display)) + } catch(err){ + return data.abbreviation_display + } + } + }, + { + 'title': "Description", + 'type': "natural", + 'data': null, + 'render': function(data, type, row, meta) { + try { + return decodeURIComponent(escape(data.description)) + } catch(err){ + return data.description + } + } + }, + { + 'title': "Authors", + 'type': "natural", + 'width': "400px", + 'data': null, + 'render': function(data, type, row, meta) { + try { + return decodeURIComponent(escape(data.authors_display)) + } catch(err){ + return data.authors_display + } + } + }, + { + 'title': "Year", + 'type': "natural-minus-na", + 'data': null, + 'width': "80px", + 'render': function(data, type, row, meta) { + if (data.pubmed_id != "N/A"){ + return '<a href="' + data.pubmed_link + '">' + data.pubmed_text + '</a>' + } else { + return data.pubmed_text + } + }, + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Sample {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "sample_r", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "N", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "num_overlap", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Sample p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})", + 'type': "natural-minus-na", + 'width': "65px", + 'data': "sample_p", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "High P<a href=\"http://gn1.genenetwork.org/glossary.html#LRS\" target=\"_blank\" style=\"color: white;\"> <i class=\"fa fa-info-circle\" aria-hidden=\"true\"></i></a>", + 'type': "natural-minus-na", + 'data': "lrs_score", + 'width': "60px", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Peak Location", + 'type': "natural-minus-na", + 'width': "125px", + 'data': "lrs_location" + }, + { + 'title': "Effect Size<a href=\"http://gn1.genenetwork.org/glossary.html#A\" target=\"_blank\" style=\"color: white;\"> <i class=\"fa fa-info-circle\" aria-hidden=\"true\"></i></a>", + 'type': "natural-minus-na", + 'data': "additive", + 'width': "85px", + 'orderSequence': [ "desc", "asc"] + }{% elif target_dataset.type == 'Geno' %}, + { + 'title': "Location", + 'type': "natural-minus-na", + 'width': "120px", + 'data': "location" + }, + { + 'title': "Sample {% if corr_method == 'pearson' %}r{% else %}rho{% endif %}", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "sample_r", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "N", + 'type': "natural-minus-na", + 'width': "40px", + 'data': "num_overlap", + 'orderSequence': [ "desc", "asc"] + }, + { + 'title': "Sample p({% if corr_method == 'pearson' %}r{% else %}rho{% endif %})", + 'type': "natural-minus-na", + 'width': "65px", + 'data': "sample_p", + 'orderSequence': [ "desc", "asc"] + }{% endif %} + ], "columnDefs": [ { "targets": 0, "orderable": false } ], - "columns": [ - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural", "width": "15%" }, - { "type": "natural" }, - { "type": "natural" }, - { "orderDataType": "dom-innertext", 'orderSequence': [ "desc", "asc"] }, - { "type": "natural" }, - { "type": "scientific" }, - { "type": "natural-minus-na", 'orderSequence': [ "desc", "asc"] }, - { "type": "natural-minus-na", 'orderSequence': [ "desc", "asc"] }, - { "type": "scientific" }, - { "type": "natural-minus-na" }, - { "type": "natural-minus-na" }, - { "type": "natural-minus-na" } - ], - "createdRow": function ( row, data, index ) { - $('td', row).eq(4).attr('title', $('td', row).eq(4).text()); - if ($('td', row).eq(4).text().length > 40) { - $('td', row).eq(4).text($('td', row).eq(4).text().substring(0, 40)); - $('td', row).eq(4).text($('td', row).eq(4).text() + '...') - } - }, + {% if target_dataset.type == 'Geno' %} + "order": [[6, "asc" ]], + {% else %} "order": [[9, "asc" ]], - "sDom": "Btir", - "iDisplayLength": -1, - "autoWidth": false, - "deferRender": true, + {% endif %} + "sDom": "itir", + "autoWidth": true, "bSortClasses": false, - "paging": false, - "orderClasses": true + "scrollY": "100vh", + "scroller": true, + "scrollCollapse": true } - {% elif target_dataset.type == "Publish" %} - table_conf = { - "drawCallback": function( settings ) { - $('#trait_table tr').click(function(event) { - if (event.target.type !== 'checkbox') { - $(':checkbox', this).trigger('click'); - } - }); - $('.trait_checkbox:checkbox').on("change", change_buttons); - }, - "buttons": [ - { - extend: 'columnsToggle', - columns: function( idx, data, node ) { - if (idx != 0) { - return true; - } else { - return false; - } - }, - postfixButtons: [ 'colvisRestore' ] - } - ], - "columnDefs": [ - { "targets": 0, "orderable": false } - ], - "columns": [ - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural", "width": "20%" }, - { "type": "natural", "width": "12%" }, - { "type": "natural-minus-na" }, - { "orderDataType": "dom-innertext", 'orderSequence': [ "desc", "asc"] }, - { "type": "natural" }, - { "type": "scientific" }, - { "type": "natural-minus-na" }, - { "type": "natural-minus-na" }, - { "type": "natural-minus-na" } - ], - "order": [[9, "asc" ]], - "sDom": "Btir", - "iDisplayLength": -1, - "autoWidth": false, - "deferRender": true, - "bSortClasses": false, - "paging": false, - "orderClasses": true, - } - {% elif target_dataset.type == "Geno" %} - table_conf = { - "paging": false, - buttons: [ - { - extend: 'columnsToggle', - columns: function( idx, data, node ) { - if (idx != 0) { - return true; - } else { - return false; - } - }, - postfixButtons: [ 'colvisRestore' ] - } - ], - "columnDefs": [ - { "targets": 0, "orderable": false } - ], - "columns": [ - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural" }, - { "type": "natural" }, - { "orderDataType": "dom-innertext", 'orderSequence': [ "desc", "asc"] }, - { "type": "natural-minus-na" }, - { "type": "scientific" } - ], - "order": [[6, "asc" ]], - "sDom": "Btir", - "autoWidth": false, - "bDeferRender": true, - "scrollY": "800px", - "scrollCollapse": false - } - {% endif %} + trait_table = $('#trait_table').DataTable(table_conf); + + trait_table.on( 'order.dt search.dt draw.dt', function () { + trait_table.column(1, {search:'applied', order:'applied'}).nodes().each( function (cell, i) { + cell.innerHTML = i+1; + } ); + } ).draw(); + + $('.toggle-vis').on('click', function (e) { + e.preventDefault(); - the_table = $('#trait_table').DataTable(table_conf); + // Get the column API object + var column = trait_table.column( $(this).attr('data-column') ); - console.timeEnd("Creating table"); + // Toggle the visibility + column.visible( ! column.visible() ); + + if (column.visible()){ + $(this).removeClass("active"); + } else { + $(this).addClass("active"); + } + } ); + + $('#redraw').on('click', function (e) { + e.preventDefault(); + trait_table.columns().visible( true ); + $('.toggle-vis.active').removeClass('active'); + }); submit_special = function(url) { $("#correlation_form").attr("action", url); @@ -498,7 +588,7 @@ $("#select_traits").click(function() { console.log("redrawing") - the_table.draw(); + trait_table.draw(); }); }); </script> diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 9a28a78e..87c97c50 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -5,6 +5,7 @@ <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/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" /> <link rel="stylesheet" type="text/css" href="static/new/css/trait_list.css" /> {% endblock %} @@ -128,6 +129,7 @@ {% endif %} <input type="hidden" name="export_data" id="export_data" value=""> <button class="btn btn-default" id="select_all" type="button"><span class="glyphicon glyphicon-ok"></span> Select</button> + <button class="btn btn-default" id="invert" type="button"><span class="glyphicon glyphicon-adjust"></span> Invert</button> <button class="btn btn-success" id="add" type="button" disabled><span class="glyphicon glyphicon-plus-sign"></span> Add</button> <button class="btn btn-default" id="export_traits">Download <span class="glyphicon glyphicon-download"></span></button> <input type="text" id="searchbox" class="form-control" style="width: 200px; display: inline;" placeholder="Search This Table For ..."> @@ -202,42 +204,21 @@ } }); - function change_buttons() { - buttons = ["#add", "#remove"]; - num_checked = $('.trait_checkbox:checked').length; - if (num_checked === 0) { - for (_i = 0, _len = buttons.length; _i < _len; _i++) { - button = buttons[_i]; - $(button).prop("disabled", true); - } - } else { - for (_j = 0, _len2 = buttons.length; _j < _len2; _j++) { - button = buttons[_j]; - $(button).prop("disabled", false); - } - } - //}); - if ($(this).is(":checked")) { - if (!$(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').addClass('selected') - } - } - else { - if ($(this).closest('tr').hasClass('selected')) { - $(this).closest('tr').removeClass('selected') - } - } - } - //ZS: Need to make sort by symbol, also need to make sure blank symbol fields at the bottom and symbols starting with numbers below letters trait_table = $('#trait_table').DataTable( { 'drawCallback': function( settings ) { - $('#trait_table tr').click(function(event) { - if (event.target.type !== 'checkbox' && event.target.tagName.toLowerCase() !== 'a') { - $(':checkbox', this).trigger('click'); - } - }); - $('.trait_checkbox:checkbox').on("change", change_buttons); + $('#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") + } + change_buttons() + }); }, 'createdRow': function ( row, data, index ) { $('td', row).eq(0).attr("style", "text-align: center; padding: 0px 10px 2px 10px;"); diff --git a/wqflask/wqflask/templates/show_trait_transform_and_filter.html b/wqflask/wqflask/templates/show_trait_transform_and_filter.html index 0418d972..d7eac378 100644 --- a/wqflask/wqflask/templates/show_trait_transform_and_filter.html +++ b/wqflask/wqflask/templates/show_trait_transform_and_filter.html @@ -4,7 +4,7 @@ <strong>Reset</strong> option as needed. </p> - <div id="blockMenuSpan" class="input-append block-by-index-div"> + <div id="blockMenuSpan" class="input-append block-div"> <label for="remove_samples_field">Block samples by index:</label> <input type="text" id="remove_samples_field" placeholder="Example: 3, 5-10, 12"> <select id="block_group" size="1"> @@ -21,19 +21,64 @@ Please check that your input is formatted correctly, e.g. <strong>3, 5-10, 12</strong> </div> {% if sample_groups[0].attributes %} - <div class="input-append block-by-attribute-div"> - <label for="exclude_menu">Block samples by group:</label> - <select id="exclude_menu" size=1> + <div class="input-append block-div-2"> + <label for="exclude_column">Block samples by group:</label> + <select id="exclude_column" size=1> {% for attribute in sample_groups[0].attributes %} - <option value="{{ sample_groups[0].attributes[attribute].name.replace(' ', '_') }}"> - {{ sample_groups[0].attributes[attribute].name }}</option> + {% if sample_groups[0].attributes[attribute].distinct_values|length <= 10 %} + <option value="{{ loop.index }}"> + {{ sample_groups[0].attributes[attribute].name }} + </option> + {% endif %} {% endfor %} </select> <select id="attribute_values" size=1> </select> - <input type="button" id="exclude_group" class="btn" value="Block"> + <select id="exclude_by_attr_group" size="1"> + <option value="primary"> + {{ sample_group_types['samples_primary'] }} + </option> + <option value="other"> + {{ sample_group_types['samples_other'] }} + </option> + </select> + <input type="button" id="exclude_by_attr" class="btn btn-danger" value="Block"> </div> {% endif %} + <div id="filterMenuSpan" class="input-append block-div-2"> + <label for="filter_samples_field">Filter samples by {% if not sample_groups[0].attributes %}value{% endif %} </label> + {% if sample_groups[0].attributes %} + <select id="filter_column"> + <option value="value">Value</option> + {% if js_data.se_exists %} + <option value="stderr">SE</option> + {% endif %} + {% for attribute in sample_groups[0].attributes %} + + <option value="{{ loop.index }}"> + {{ sample_groups[0].attributes[attribute].name }} + </option> + + {% endfor %} + </select> + {% endif %} + <select id="filter_logic" size="1"> + <option value="greater_than">></option> + <option value="less_than"><</option> + <option value="greater_or_equal">≥</option> + <option value="less_or_equal">≤</option> + </select> + <input type="text" id="filter_value" placeholder="Example: 3, 10, 15"> + <select id="filter_group" size="1"> + <option value="primary"> + {{ sample_group_types['samples_primary'] }} + </option> + <option value="other"> + {{ sample_group_types['samples_other'] }} + </option> + </select> + <input type="button" id="filter_by_value" class="btn btn-danger" value="Filter"> + </div> <div> <input type="button" id="hide_no_value" class="btn btn-default" value="Hide No Value"> <input type="button" id="block_outliers" class="btn btn-default" value="Block Outliers"> |