From 3dae9e9580ea535c22d862ca1c65f25378b69926 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 27 Feb 2019 16:43:33 -0600 Subject: Added loading page for correlation Added a better error page for mapping if no results are returned (which generally implied an error occurred during the running of GEMMA) Fixed some issues with tables, like them sorting incorrectly or showing the wrong number of digits --- wqflask/wqflask/marker_regression/run_mapping.py | 188 +++++++++++---------- wqflask/wqflask/show_trait/show_trait.py | 2 +- .../wqflask/static/new/javascript/show_trait.js | 4 +- wqflask/wqflask/templates/correlation_page.html | 29 +--- wqflask/wqflask/templates/loading_correlation.html | 24 +++ wqflask/wqflask/templates/mapping_error.html | 14 ++ wqflask/wqflask/templates/search_result_page.html | 4 +- wqflask/wqflask/templates/show_trait.html | 50 +++++- wqflask/wqflask/views.py | 95 +++++++---- 9 files changed, 252 insertions(+), 158 deletions(-) create mode 100644 wqflask/wqflask/templates/loading_correlation.html create mode 100644 wqflask/wqflask/templates/mapping_error.html diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py index 73d985b8..7ee3ac01 100644 --- a/wqflask/wqflask/marker_regression/run_mapping.py +++ b/wqflask/wqflask/marker_regression/run_mapping.py @@ -242,99 +242,103 @@ class RunMapping(object): else: logger.debug("RUNNING NOTHING") - if self.pair_scan == True: - self.qtl_results = [] - highest_chr = 1 #This is needed in order to convert the highest chr to X/Y - for marker in results: - if marker['chr1'] > 0 or marker['chr1'] == "X" or marker['chr1'] == "X/Y": - if marker['chr1'] > highest_chr or marker['chr1'] == "X" or marker['chr1'] == "X/Y": - highest_chr = marker['chr1'] - if 'lod_score' in marker.keys(): - self.qtl_results.append(marker) - - self.trimmed_markers = results - - for qtl in enumerate(self.qtl_results): - self.json_data['chr1'].append(str(qtl['chr1'])) - self.json_data['chr2'].append(str(qtl['chr2'])) - self.json_data['Mb'].append(qtl['Mb']) - self.json_data['markernames'].append(qtl['name']) - - self.js_data = dict( - json_data = self.json_data, - this_trait = self.this_trait.name, - data_set = self.dataset.name, - maf = self.maf, - manhattan_plot = self.manhattan_plot, - mapping_scale = self.mapping_scale, - qtl_results = self.qtl_results - ) - + self.no_results = False + if len(results) == 0: + self.no_results = True else: - self.qtl_results = [] - highest_chr = 1 #This is needed in order to convert the highest chr to X/Y - for marker in results: - if marker['chr'] > 0 or marker['chr'] == "X" or marker['chr'] == "X/Y": - if marker['chr'] > highest_chr or marker['chr'] == "X" or marker['chr'] == "X/Y": - highest_chr = marker['chr'] - if ('lod_score' in marker.keys()) or ('lrs_value' in marker.keys()): - self.qtl_results.append(marker) - - with Bench("Exporting Results"): - export_mapping_results(self.dataset, self.this_trait, self.qtl_results, self.mapping_results_path, self.mapping_scale, self.score_type) - - with Bench("Trimming Markers for Figure"): - if len(self.qtl_results) > 30000: - self.qtl_results = trim_markers_for_figure(self.qtl_results) - - with Bench("Trimming Markers for Table"): - self.trimmed_markers = trim_markers_for_table(results) - - if self.mapping_method != "gemma": - self.json_data['chr'] = [] - self.json_data['pos'] = [] - self.json_data['lod.hk'] = [] - self.json_data['markernames'] = [] - - self.json_data['suggestive'] = self.suggestive - self.json_data['significant'] = self.significant - - #Need to convert the QTL objects that qtl reaper returns into a json serializable dictionary - for index, qtl in enumerate(self.qtl_results): - #if index<40: - # logger.debug("lod score is:", qtl['lod_score']) - if qtl['chr'] == highest_chr and highest_chr != "X" and highest_chr != "X/Y": - #logger.debug("changing to X") - self.json_data['chr'].append("X") - else: - self.json_data['chr'].append(str(qtl['chr'])) - self.json_data['pos'].append(qtl['Mb']) - if 'lrs_value' in qtl.keys(): - self.json_data['lod.hk'].append(str(qtl['lrs_value'])) - else: - self.json_data['lod.hk'].append(str(qtl['lod_score'])) - self.json_data['markernames'].append(qtl['name']) - - #Get chromosome lengths for drawing the interval map plot - chromosome_mb_lengths = {} - self.json_data['chrnames'] = [] - for key in self.species.chromosomes.chromosomes.keys(): - self.json_data['chrnames'].append([self.species.chromosomes.chromosomes[key].name, self.species.chromosomes.chromosomes[key].mb_length]) - chromosome_mb_lengths[key] = self.species.chromosomes.chromosomes[key].mb_length - - self.js_data = dict( - result_score_type = self.score_type, - json_data = self.json_data, - this_trait = self.this_trait.name, - data_set = self.dataset.name, - maf = self.maf, - manhattan_plot = self.manhattan_plot, - mapping_scale = self.mapping_scale, - chromosomes = chromosome_mb_lengths, - qtl_results = self.qtl_results, - num_perm = self.num_perm, - perm_results = self.perm_output, - ) + if self.pair_scan == True: + self.qtl_results = [] + highest_chr = 1 #This is needed in order to convert the highest chr to X/Y + for marker in results: + if marker['chr1'] > 0 or marker['chr1'] == "X" or marker['chr1'] == "X/Y": + if marker['chr1'] > highest_chr or marker['chr1'] == "X" or marker['chr1'] == "X/Y": + highest_chr = marker['chr1'] + if 'lod_score' in marker.keys(): + self.qtl_results.append(marker) + + self.trimmed_markers = results + + for qtl in enumerate(self.qtl_results): + self.json_data['chr1'].append(str(qtl['chr1'])) + self.json_data['chr2'].append(str(qtl['chr2'])) + self.json_data['Mb'].append(qtl['Mb']) + self.json_data['markernames'].append(qtl['name']) + + self.js_data = dict( + json_data = self.json_data, + this_trait = self.this_trait.name, + data_set = self.dataset.name, + maf = self.maf, + manhattan_plot = self.manhattan_plot, + mapping_scale = self.mapping_scale, + qtl_results = self.qtl_results + ) + + else: + self.qtl_results = [] + highest_chr = 1 #This is needed in order to convert the highest chr to X/Y + for marker in results: + if marker['chr'] > 0 or marker['chr'] == "X" or marker['chr'] == "X/Y": + if marker['chr'] > highest_chr or marker['chr'] == "X" or marker['chr'] == "X/Y": + highest_chr = marker['chr'] + if ('lod_score' in marker.keys()) or ('lrs_value' in marker.keys()): + self.qtl_results.append(marker) + + with Bench("Exporting Results"): + export_mapping_results(self.dataset, self.this_trait, self.qtl_results, self.mapping_results_path, self.mapping_scale, self.score_type) + + with Bench("Trimming Markers for Figure"): + if len(self.qtl_results) > 30000: + self.qtl_results = trim_markers_for_figure(self.qtl_results) + + with Bench("Trimming Markers for Table"): + self.trimmed_markers = trim_markers_for_table(results) + + if self.mapping_method != "gemma": + self.json_data['chr'] = [] + self.json_data['pos'] = [] + self.json_data['lod.hk'] = [] + self.json_data['markernames'] = [] + + self.json_data['suggestive'] = self.suggestive + self.json_data['significant'] = self.significant + + #Need to convert the QTL objects that qtl reaper returns into a json serializable dictionary + for index, qtl in enumerate(self.qtl_results): + #if index<40: + # logger.debug("lod score is:", qtl['lod_score']) + if qtl['chr'] == highest_chr and highest_chr != "X" and highest_chr != "X/Y": + #logger.debug("changing to X") + self.json_data['chr'].append("X") + else: + self.json_data['chr'].append(str(qtl['chr'])) + self.json_data['pos'].append(qtl['Mb']) + if 'lrs_value' in qtl.keys(): + self.json_data['lod.hk'].append(str(qtl['lrs_value'])) + else: + self.json_data['lod.hk'].append(str(qtl['lod_score'])) + self.json_data['markernames'].append(qtl['name']) + + #Get chromosome lengths for drawing the interval map plot + chromosome_mb_lengths = {} + self.json_data['chrnames'] = [] + for key in self.species.chromosomes.chromosomes.keys(): + self.json_data['chrnames'].append([self.species.chromosomes.chromosomes[key].name, self.species.chromosomes.chromosomes[key].mb_length]) + chromosome_mb_lengths[key] = self.species.chromosomes.chromosomes[key].mb_length + + self.js_data = dict( + result_score_type = self.score_type, + json_data = self.json_data, + this_trait = self.this_trait.name, + data_set = self.dataset.name, + maf = self.maf, + manhattan_plot = self.manhattan_plot, + mapping_scale = self.mapping_scale, + chromosomes = chromosome_mb_lengths, + qtl_results = self.qtl_results, + num_perm = self.num_perm, + perm_results = self.perm_output, + ) def run_rqtl_plink(self): # os.chdir("") never do this inside a webserver!! diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py index 9b4470fe..4e4c7705 100644 --- a/wqflask/wqflask/show_trait/show_trait.py +++ b/wqflask/wqflask/show_trait/show_trait.py @@ -414,7 +414,7 @@ def get_table_widths(sample_groups, has_num_cases=False): trait_table_width += 70 if has_num_cases: trait_table_width += 30 - trait_table_width += len(sample_groups[0].attributes)*40 + trait_table_width += len(sample_groups[0].attributes)*70 trait_table_width = str(trait_table_width) + "px" diff --git a/wqflask/wqflask/static/new/javascript/show_trait.js b/wqflask/wqflask/static/new/javascript/show_trait.js index bcb67527..f278b840 100644 --- a/wqflask/wqflask/static/new/javascript/show_trait.js +++ b/wqflask/wqflask/static/new/javascript/show_trait.js @@ -447,14 +447,14 @@ submit_special = function(url) { submit_corr = function(){ var url; - url = "/corr_compute"; + url = "/corr_loading"; return submit_special(url); }; $(".corr_compute").on("click", (function(_this) { return function() { var url; - url = "/corr_compute"; + url = "/corr_loading"; return submit_special(url); }; })(this)); diff --git a/wqflask/wqflask/templates/correlation_page.html b/wqflask/wqflask/templates/correlation_page.html index 76aa8d2d..76513c82 100644 --- a/wqflask/wqflask/templates/correlation_page.html +++ b/wqflask/wqflask/templates/correlation_page.html @@ -229,22 +229,6 @@ + + \ No newline at end of file diff --git a/wqflask/wqflask/templates/mapping_error.html b/wqflask/wqflask/templates/mapping_error.html new file mode 100644 index 00000000..b73a2c31 --- /dev/null +++ b/wqflask/wqflask/templates/mapping_error.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% block title %}Error{% endblock %} +{% block content %} + + {{ header("An error occurred during mapping") }} + +
+

There is likely an issue with the genotype file associated with this group/RISet. Please contact Zach Sloan (zachary.a.sloan@gmail.com) or Arthur Centeno (acenteno@gmail.com) about the data set in question.

+
+ + + + +{% endblock %} diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 31a5b94e..5f529edb 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -224,7 +224,7 @@ } $('td', row).eq(4).attr('title', $('td', row).eq(4).text()); $('td', row).eq(4).attr('data-export', $('td', row).eq(4).text()); - $('td', row).slice(6,10).attr("align", "right"); + $('td', row).slice(5,10).attr("align", "right"); $('td', row).eq(5).attr('data-export', $('td', row).eq(5).text()); $('td', row).eq(6).attr('data-export', $('td', row).eq(6).text()); $('td', row).eq(7).attr('data-export', $('td', row).eq(7).text()); @@ -282,6 +282,7 @@ { 'title': "Location", 'type': "natural", + 'width': "140px", 'data': "location" }, { @@ -299,6 +300,7 @@ { 'title': "Max LRS Location", 'type': "natural", + 'width': "140px", 'data': "lrs_location" }, { diff --git a/wqflask/wqflask/templates/show_trait.html b/wqflask/wqflask/templates/show_trait.html index 9d2cb7ed..d7d85741 100644 --- a/wqflask/wqflask/templates/show_trait.html +++ b/wqflask/wqflask/templates/show_trait.html @@ -161,12 +161,42 @@ } ); }; + function getValue(x) { + if (x == 'x') { + return "x"; + } + else { + return parseFloat(x); + } + } + $.fn.dataTable.ext.order['dom-input'] = function (settings, col) { return this.api().column(col, { order: 'index' }).nodes().map(function (td, i) { return $('input', td).val(); }); } + $.fn.dataTableExt.oSort['cust-txt-asc'] = function (a, b) { + var x = getValue(a); + var y = getValue(b); + + if (x == 'x' || x == '') { + return 1; + } + else if (y == 'x' || y == '') { + return -1; + } + else { + return ((x < y) ? -1 : ((x > y) ? 1 : 0)); + } + }; + + $.fn.dataTableExt.oSort['cust-txt-desc'] = function (a, b) { + var x = getValue(a); + var y = getValue(b); + return ((x < y) ? 1 : ((x > y) ? -1 : 0)); + }; + $(document).ready( function () { $('.panel-heading').click(function () { if ($(this).hasClass('collapsed')){ @@ -257,12 +287,13 @@ { 'title': "Value", 'orderDataType': "dom-input", + 'type': "cust-txt", 'data': null, 'render': function(data, type, row, meta) { if (data.value == null) { return '' } else { - return '' + return '' } } }{% if sample_groups[0].se_exists() %}, @@ -277,20 +308,21 @@ { 'title': "SE", 'orderDataType': "dom-input", + 'type': "cust-txt", 'data': null, 'render': function(data, type, row, meta) { if (data.variance == null) { return '' } else { - return '' + return '' } } }{% endif %}{% if has_num_cases %}, { 'title': "N", - 'type': "natural", + 'orderDataType': "dom-input", + 'type': "cust-txt", 'data': null, - 'orderDataType': "cust-txt", 'render': function(data, type, row, meta) { if (data.num_cases == null) { return '' @@ -387,12 +419,13 @@ { 'title': "Value", 'orderDataType': "dom-input", + 'type': "cust-txt", 'data': null, 'render': function(data, type, row, meta) { if (data.value == null) { return '' } else { - return '' + return '' } } }{% if sample_groups[1].se_exists() %}, @@ -407,20 +440,21 @@ { 'title': "SE", 'orderDataType': "dom-input", + 'type': "cust-txt", 'data': null, 'render': function(data, type, row, meta) { if (data.variance == null) { return '' } else { - return '' + return '' } } }{% endif %}{% if has_num_cases %}, { 'title': "N", - 'type': "natural", + 'orderDataType': "dom-input", + 'type': "cust-txt", 'data': null, - 'orderDataType': "cust-txt", 'render': function(data, type, row, meta) { if (data.num_cases == null) { return '' diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index cd2e047b..0b0871b1 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -684,36 +684,39 @@ def mapping_results_page(): with Bench("Total time in RunMapping"): template_vars = run_mapping.RunMapping(start_vars, temp_uuid) - if template_vars.mapping_method != "gemma" and template_vars.mapping_method != "plink": - template_vars.js_data = json.dumps(template_vars.js_data, - default=json_default_handler, - indent=" ") - - result = template_vars.__dict__ - - if result['pair_scan']: - with Bench("Rendering template"): - img_path = result['pair_scan_filename'] - logger.info("img_path:", img_path) - initial_start_vars = request.form - logger.info("initial_start_vars:", initial_start_vars) - imgfile = open(TEMPDIR + img_path, 'rb') - imgdata = imgfile.read() - imgB64 = imgdata.encode("base64") - bytesarray = array.array('B', imgB64) - result['pair_scan_array'] = bytesarray - rendered_template = render_template("pair_scan_results.html", **result) + if template_vars.no_results: + rendered_template = render_template("mapping_error.html") else: - gn1_template_vars = display_mapping_results.DisplayMappingResults(result).__dict__ - #pickled_result = pickle.dumps(result, pickle.HIGHEST_PROTOCOL) - #logger.info("pickled result length:", len(pickled_result)) - #Redis.set(key, pickled_result) - #Redis.expire(key, 1*60) - - with Bench("Rendering template"): - if (gn1_template_vars['mapping_method'] == "gemma") or (gn1_template_vars['mapping_method'] == "plink"): - gn1_template_vars.pop('qtlresults', None) - rendered_template = render_template("mapping_results.html", **gn1_template_vars) + if template_vars.mapping_method != "gemma" and template_vars.mapping_method != "plink": + template_vars.js_data = json.dumps(template_vars.js_data, + default=json_default_handler, + indent=" ") + + result = template_vars.__dict__ + + if result['pair_scan']: + with Bench("Rendering template"): + img_path = result['pair_scan_filename'] + logger.info("img_path:", img_path) + initial_start_vars = request.form + logger.info("initial_start_vars:", initial_start_vars) + imgfile = open(TEMPDIR + img_path, 'rb') + imgdata = imgfile.read() + imgB64 = imgdata.encode("base64") + bytesarray = array.array('B', imgB64) + result['pair_scan_array'] = bytesarray + rendered_template = render_template("pair_scan_results.html", **result) + else: + gn1_template_vars = display_mapping_results.DisplayMappingResults(result).__dict__ + #pickled_result = pickle.dumps(result, pickle.HIGHEST_PROTOCOL) + #logger.info("pickled result length:", len(pickled_result)) + #Redis.set(key, pickled_result) + #Redis.expire(key, 1*60) + + with Bench("Rendering template"): + if (gn1_template_vars['mapping_method'] == "gemma") or (gn1_template_vars['mapping_method'] == "plink"): + gn1_template_vars.pop('qtlresults', None) + rendered_template = render_template("mapping_results.html", **gn1_template_vars) return rendered_template @@ -769,6 +772,40 @@ def network_graph_page(): else: return render_template("empty_collection.html", **{'tool':'Network Graph'}) +@app.route("/corr_loading", methods=('POST',)) +def corr_loading_page(): + logger.info(request.url) + initial_start_vars = request.form + logger.debug("Marker regression called with initial_start_vars:", initial_start_vars.items()) + #temp_uuid = initial_start_vars['temp_uuid'] + wanted = ( + 'corr_type', + 'trait_id', + 'dataset', + 'group', + 'corr_sample_method', + 'corr_samples_group', + 'corr_dataset', + 'min_expr', + 'corr_return_results', + 'loc_chr', + 'min_loc_mb', + 'max_loc_mb', + 'p_range_lower', + 'p_range_upper' + ) + start_vars_container = {} + start_vars = {} + for key, value in initial_start_vars.iteritems(): + if key in wanted or key.startswith(('value:')): + start_vars[key] = value + + start_vars_container['start_vars'] = start_vars + rendered_template = render_template("loading_correlation.html", **start_vars_container) + + return rendered_template + + @app.route("/corr_compute", methods=('POST',)) def corr_compute_page(): logger.info("In corr_compute, request.form is:", pf(request.form)) -- cgit v1.2.3