From e48ec8826ec48bd4ba53f681796a235737ab0d29 Mon Sep 17 00:00:00 2001 From: Zachary Sloan Date: Fri, 30 Jan 2015 17:21:19 +0000 Subject: Fixed several bugs Added legend to bar chart color by trait function Added scatterplot matrix figure Fixed database timeout problem --- wqflask/base/data_set.py | 4 +- wqflask/cfg/default_settings.py | 2 + wqflask/wqflask/search_results.py | 5 - wqflask/wqflask/static/new/css/show_trait.css | 8 +- .../wqflask/static/new/javascript/bar_chart.coffee | 34 +- wqflask/wqflask/static/new/javascript/bar_chart.js | 22 +- .../javascript/get_traits_from_collection.coffee | 44 +- .../new/javascript/get_traits_from_collection.js | 50 +- .../static/new/javascript/scatter-matrix.js | 549 +++++++++++++++++++++ .../new/javascript/show_trait_mapping_tools.coffee | 119 +---- .../new/javascript/show_trait_mapping_tools.js | 13 +- .../static/packages/bootstrap/css/bootstrap.css | 71 +-- wqflask/wqflask/templates/collections/view.html | 15 +- wqflask/wqflask/templates/index_page.html | 10 +- wqflask/wqflask/templates/interval_mapping.html | 234 +++++---- .../wqflask/templates/new_security/login_user.html | 23 +- wqflask/wqflask/templates/search_result_page.html | 24 +- wqflask/wqflask/templates/show_trait.html | 20 +- .../templates/show_trait_mapping_tools.html | 55 ++- .../templates/show_trait_statistics_new.html | 22 +- 20 files changed, 979 insertions(+), 345 deletions(-) create mode 100644 wqflask/wqflask/static/new/javascript/scatter-matrix.js (limited to 'wqflask') diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py index 15a8c7cc..8965e1d1 100755 --- a/wqflask/base/data_set.py +++ b/wqflask/base/data_set.py @@ -640,7 +640,7 @@ class PhenotypeDataSet(DataSet): 'Year', 'Max LRS', 'Max LRS Location', - 'Add. Effect'] + 'Add. Effect ?'] self.type = 'Publish' @@ -901,7 +901,7 @@ class MrnaAssayDataSet(DataSet): 'Mean Expr', 'Max LRS', 'Max LRS Location', - 'Add. Effect'] + 'Add. Effect ?'] # Todo: Obsolete or rename this field self.type = 'ProbeSet' diff --git a/wqflask/cfg/default_settings.py b/wqflask/cfg/default_settings.py index 8fca3d77..8b5d1313 100755 --- a/wqflask/cfg/default_settings.py +++ b/wqflask/cfg/default_settings.py @@ -13,3 +13,5 @@ SECURITY_RECOVERABLE = True SECURITY_EMAIL_SENDER = "no-reply@genenetwork.org" SECURITY_POST_LOGIN_VIEW = "/thank_you" + +SQLALCHEMY_POOL_RECYCLE = 3600 \ No newline at end of file diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py index 5444d1bc..9d2d0b88 100755 --- a/wqflask/wqflask/search_results.py +++ b/wqflask/wqflask/search_results.py @@ -233,11 +233,6 @@ class SearchResultPage(object): print("search_type is:", pf(search_type)) - # This is throwing an error when a_search['key'] is None, so I changed above - #search_type = string.upper(a_search['key']) - #if not search_type: - # search_type = self.dataset.type - search_ob = do_search.DoSearch.get_search(search_type) search_class = getattr(do_search, search_ob) print("search_class is: ", pf(search_class)) diff --git a/wqflask/wqflask/static/new/css/show_trait.css b/wqflask/wqflask/static/new/css/show_trait.css index 9dcbfce9..9fc82a85 100644 --- a/wqflask/wqflask/static/new/css/show_trait.css +++ b/wqflask/wqflask/static/new/css/show_trait.css @@ -1,3 +1,7 @@ -tr .outlier { - background-color: #ffff99; +tr .outlier { + background-color: #ffff99; +} + +#bar_chart_container { + overflow-x:scroll; } \ No newline at end of file diff --git a/wqflask/wqflask/static/new/javascript/bar_chart.coffee b/wqflask/wqflask/static/new/javascript/bar_chart.coffee index a48718de..c83de64e 100755 --- a/wqflask/wqflask/static/new/javascript/bar_chart.coffee +++ b/wqflask/wqflask/static/new/javascript/bar_chart.coffee @@ -28,7 +28,7 @@ class Bar_Chart @plot_height -= @y_buffer @create_scales() @create_graph() - + d3.select("#color_attribute").on("change", => @attribute = $("#color_attribute").val() console.log("attr_color_dict:", @attr_color_dict) @@ -59,6 +59,7 @@ class Bar_Chart else return @attr_color_dict[@attribute][d[2][@attribute]] ) + @draw_legend() @add_legend(@attribute, @distinct_attr_vals[@attribute]) ) @@ -181,6 +182,8 @@ class Bar_Chart @attr_color_dict = {} console.log("vals:", vals) for own key, distinct_vals of vals + @min_val = d3.min(distinct_vals) + @max_val = d3.max(distinct_vals) this_color_dict = {} if distinct_vals.length < 10 color = d3.scale.category10() @@ -197,8 +200,8 @@ class Bar_Chart return true ) color_range = d3.scale.linear() - .domain([d3.min(distinct_vals), - d3.max(distinct_vals)]) + .domain([min_val, + max_val]) .range([0,255]) for value, i in distinct_vals console.log("color_range(value):", parseInt(color_range(value))) @@ -206,12 +209,30 @@ class Bar_Chart #this_color_dict[value] = d3.rgb("lightblue").darker(color_range(parseInt(value))) #this_color_dict[value] = "rgb(0, 0, " + color_range(parseInt(value)) + ")" @attr_color_dict[key] = this_color_dict + + + + draw_legend: () -> + $('#legend-left').html(@min_val) + $('#legend-right').html(@max_val) + svg_html = ' \ + \ + \ + \ + \ + \ + \ + ' + console.log("svg_html:", svg_html) + $('#legend-colors').html(svg_html) get_trait_color_dict: (samples, vals) -> @trait_color_dict = {} console.log("vals:", vals) for own key, distinct_vals of vals this_color_dict = {} + @min_val = d3.min(distinct_vals) + @max_val = d3.max(distinct_vals) if distinct_vals.length < 10 color = d3.scale.category10() for value, i in distinct_vals @@ -434,8 +455,8 @@ class Bar_Chart .select("title") .text((d) => return d[1] - ) - + ) + @draw_legend() else @svg.selectAll(".bar") .data(@sorted_samples()) @@ -448,7 +469,8 @@ class Bar_Chart .select("title") .text((d) => return d[1] - ) + ) + @draw_legend() trim_values: (trait_sample_data) -> trimmed_samples = {} diff --git a/wqflask/wqflask/static/new/javascript/bar_chart.js b/wqflask/wqflask/static/new/javascript/bar_chart.js index 07761ba7..f5ed544b 100755 --- a/wqflask/wqflask/static/new/javascript/bar_chart.js +++ b/wqflask/wqflask/static/new/javascript/bar_chart.js @@ -66,6 +66,7 @@ Bar_Chart = (function() { } }); } + _this.draw_legend(); return _this.add_legend(_this.attribute, _this.distinct_attr_vals[_this.attribute]); }; })(this)); @@ -150,6 +151,8 @@ Bar_Chart = (function() { for (key in vals) { if (!__hasProp.call(vals, key)) continue; distinct_vals = vals[key]; + this.min_val = d3.min(distinct_vals); + this.max_val = d3.max(distinct_vals); this_color_dict = {}; if (distinct_vals.length < 10) { color = d3.scale.category10(); @@ -168,7 +171,7 @@ Bar_Chart = (function() { } }; })(this))) { - color_range = d3.scale.linear().domain([d3.min(distinct_vals), d3.max(distinct_vals)]).range([0, 255]); + color_range = d3.scale.linear().domain([min_val, max_val]).range([0, 255]); for (i = _j = 0, _len1 = distinct_vals.length; _j < _len1; i = ++_j) { value = distinct_vals[i]; console.log("color_range(value):", parseInt(color_range(value))); @@ -181,6 +184,15 @@ Bar_Chart = (function() { return _results; }; + Bar_Chart.prototype.draw_legend = function() { + var svg_html; + $('#legend-left').html(this.min_val); + $('#legend-right').html(this.max_val); + svg_html = ' '; + console.log("svg_html:", svg_html); + return $('#legend-colors').html(svg_html); + }; + Bar_Chart.prototype.get_trait_color_dict = function(samples, vals) { var color, color_range, distinct_vals, i, key, sample, this_color_dict, value, _i, _j, _len, _len1, _results; this.trait_color_dict = {}; @@ -189,6 +201,8 @@ Bar_Chart = (function() { if (!__hasProp.call(vals, key)) continue; distinct_vals = vals[key]; this_color_dict = {}; + this.min_val = d3.min(distinct_vals); + this.max_val = d3.max(distinct_vals); if (distinct_vals.length < 10) { color = d3.scale.category10(); for (i = _i = 0, _len = distinct_vals.length; _i < _len; i = ++_i) { @@ -426,7 +440,7 @@ Bar_Chart = (function() { console.log("TRAIT_COLOR_DICT:", this.trait_color_dict); console.log("SAMPLES:", this.samples); if (this.sort_by = "value") { - return this.svg.selectAll(".bar").data(this.samples).transition().duration(1000).style("fill", (function(_this) { + this.svg.selectAll(".bar").data(this.samples).transition().duration(1000).style("fill", (function(_this) { return function(d) { console.log("this color:", _this.trait_color_dict[d[0]]); return _this.trait_color_dict[d[0]]; @@ -436,8 +450,9 @@ Bar_Chart = (function() { return d[1]; }; })(this)); + return this.draw_legend(); } else { - return this.svg.selectAll(".bar").data(this.sorted_samples()).transition().duration(1000).style("fill", (function(_this) { + this.svg.selectAll(".bar").data(this.sorted_samples()).transition().duration(1000).style("fill", (function(_this) { return function(d) { console.log("this color:", _this.trait_color_dict[d[0]]); return _this.trait_color_dict[d[0]]; @@ -447,6 +462,7 @@ Bar_Chart = (function() { return d[1]; }; })(this)); + return this.draw_legend(); } }; diff --git a/wqflask/wqflask/static/new/javascript/get_traits_from_collection.coffee b/wqflask/wqflask/static/new/javascript/get_traits_from_collection.coffee index ff7c041c..07be824f 100755 --- a/wqflask/wqflask/static/new/javascript/get_traits_from_collection.coffee +++ b/wqflask/wqflask/static/new/javascript/get_traits_from_collection.coffee @@ -57,10 +57,52 @@ submit_click = () -> this_trait_vals.push(null) all_vals.push(this_trait_vals) - create_scatterplots(trait_names, samples, all_vals) + trait_vals_csv = create_trait_data_csv(selected_traits) + scatter_matrix = new ScatterMatrix(trait_vals_csv) + scatter_matrix.render() + + + #create_scatterplots(trait_names, samples, all_vals) $.colorbox.close() +create_trait_data_csv = (selected_traits) -> + trait_names = [] + trait_names.push($('input[name=trait_id]').val()) + samples = $('input[name=allsamples]').val().split(" ") + all_vals = [] + this_trait_vals = get_this_trait_vals(samples) + all_vals.push(this_trait_vals) + + for trait in Object.keys(selected_traits) + trait_names.push(trait) + + this_trait_vals = [] + for sample in samples + if sample in Object.keys(selected_traits[trait]) + this_trait_vals.push(parseFloat(selected_traits[trait][sample])) + else + this_trait_vals.push(null) + all_vals.push(this_trait_vals) + + console.log("all_vals:", all_vals) + + trait_vals_csv = trait_names.join(",") + trait_vals_csv += "\n" + + for sample, index in samples + if all_vals[0][index] == null + continue + sample_vals = [] + for trait in all_vals + sample_vals.push(trait[index]) + trait_vals_csv += sample_vals.join(",") + trait_vals_csv += "\n" + + #console.log("trait_vals_csv:", trait_vals_csv) + + return trait_vals_csv + trait_click = () -> console.log("Clicking on:", $(this)) trait = $(this).parent().find('.trait').text() diff --git a/wqflask/wqflask/static/new/javascript/get_traits_from_collection.js b/wqflask/wqflask/static/new/javascript/get_traits_from_collection.js index af3f5135..a73eafe4 100755 --- a/wqflask/wqflask/static/new/javascript/get_traits_from_collection.js +++ b/wqflask/wqflask/static/new/javascript/get_traits_from_collection.js @@ -1,5 +1,5 @@ // Generated by CoffeeScript 1.8.0 -var add_trait_data, assemble_into_json, back_to_collections, collection_click, collection_list, color_by_trait, get_this_trait_vals, get_trait_data, process_traits, selected_traits, submit_click, this_trait_data, trait_click, +var add_trait_data, assemble_into_json, back_to_collections, collection_click, collection_list, color_by_trait, create_trait_data_csv, get_this_trait_vals, get_trait_data, process_traits, selected_traits, submit_click, this_trait_data, trait_click, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; console.log("before get_traits_from_collection"); @@ -25,7 +25,7 @@ collection_click = function() { }; submit_click = function() { - var all_vals, sample, samples, this_trait_vals, trait, trait_names, traits, _i, _j, _len, _len1, _ref; + var all_vals, sample, samples, scatter_matrix, this_trait_vals, trait, trait_names, trait_vals_csv, traits, _i, _j, _len, _len1, _ref; selected_traits = {}; traits = []; $('#collections_holder').find('input[type=checkbox]:checked').each(function() { @@ -63,10 +63,54 @@ submit_click = function() { } all_vals.push(this_trait_vals); } - create_scatterplots(trait_names, samples, all_vals); + trait_vals_csv = create_trait_data_csv(selected_traits); + scatter_matrix = new ScatterMatrix(trait_vals_csv); + scatter_matrix.render(); return $.colorbox.close(); }; +create_trait_data_csv = function(selected_traits) { + var all_vals, index, sample, sample_vals, samples, this_trait_vals, trait, trait_names, trait_vals_csv, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref; + trait_names = []; + trait_names.push($('input[name=trait_id]').val()); + samples = $('input[name=allsamples]').val().split(" "); + all_vals = []; + this_trait_vals = get_this_trait_vals(samples); + all_vals.push(this_trait_vals); + _ref = Object.keys(selected_traits); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + trait = _ref[_i]; + trait_names.push(trait); + this_trait_vals = []; + for (_j = 0, _len1 = samples.length; _j < _len1; _j++) { + sample = samples[_j]; + if (__indexOf.call(Object.keys(selected_traits[trait]), sample) >= 0) { + this_trait_vals.push(parseFloat(selected_traits[trait][sample])); + } else { + this_trait_vals.push(null); + } + } + all_vals.push(this_trait_vals); + } + console.log("all_vals:", all_vals); + trait_vals_csv = trait_names.join(","); + trait_vals_csv += "\n"; + for (index = _k = 0, _len2 = samples.length; _k < _len2; index = ++_k) { + sample = samples[index]; + if (all_vals[0][index] === null) { + continue; + } + sample_vals = []; + for (_l = 0, _len3 = all_vals.length; _l < _len3; _l++) { + trait = all_vals[_l]; + sample_vals.push(trait[index]); + } + trait_vals_csv += sample_vals.join(","); + trait_vals_csv += "\n"; + } + return trait_vals_csv; +}; + trait_click = function() { var dataset, this_trait_url, trait; console.log("Clicking on:", $(this)); diff --git a/wqflask/wqflask/static/new/javascript/scatter-matrix.js b/wqflask/wqflask/static/new/javascript/scatter-matrix.js new file mode 100644 index 00000000..38fd276e --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/scatter-matrix.js @@ -0,0 +1,549 @@ +// Heavily influenced by Mike Bostock's Scatter Matrix example +// http://mbostock.github.io/d3/talk/20111116/iris-splom.html +// + +/* +ScatterMatrix = function(url) { + this.__url = url; + this.__data = undefined; + this.__cell_size = 140; +}; +*/ + +ScatterMatrix = function(csv_string) { + this.__csv_string = csv_string; + this.__data = undefined; + this.__cell_size = 140; +}; + +ScatterMatrix.prototype.cellSize = function(n) { + this.__cell_size = n; + return this; +}; + +/* +ScatterMatrix.prototype.onData = function(cb) { + if (this.__data) { cb(); return; } + var self = this; + d3.csv(self.__url, function(data) { + self.__data = data; + cb(); + }); +}; +*/ + +ScatterMatrix.prototype.onData = function(cb) { + if (this.__data) { cb(); return; } + var self = this; + console.log("self.csv_string:", self.__csv_string) + + data = d3.csv.parse(self.__csv_string); + self.__data = data; + cb(); + +/* + d3.csv.parseRows(self.__csv_string, function(data) { + self.__data = data; + cb(); + }); +*/ + +}; + +ScatterMatrix.prototype.render = function () { + var self = this; + + var container = d3.select('#scatterplot_container').append('div') + .attr('class', 'scatter-matrix-container'); + var control = container.append('div') + .attr('class', 'scatter-matrix-control'); + var svg = container.append('div') + .attr('class', 'scatter-matrix-svg') + .html('Loading data...'); + + this.onData(function() { + var data = self.__data; + + // Fetch data and get all string variables + var string_variables = [undefined]; + var numeric_variables = []; + var numeric_variable_values = {}; + + for (k in data[0]) { + if (isNaN(+data[0][k])) { string_variables.push(k); } + else { numeric_variables.push(k); numeric_variable_values[k] = []; } + } + + console.log("data:", data) + + data.forEach(function(d) { + for (var j in numeric_variables) { + var k = numeric_variables[j]; + var value = d[k]; + if (numeric_variable_values[k].indexOf(value) < 0) { + numeric_variable_values[k].push(value); + } + } + }); + + var size_control = control.append('div').attr('class', 'scatter-matrix-size-control'); + var color_control = control.append('div').attr('class', 'scatter-matrix-color-control'); + var filter_control = control.append('div').attr('class', 'scatter-matrix-filter-control'); + var variable_control = control.append('div').attr('class', 'scatter-matrix-variable-control'); + var drill_control = control.append('div').attr('class', 'scatter-matrix-drill-control'); + + // shared control states + var to_include = []; + var color_variable = undefined; + var selected_colors = undefined; + for (var j in numeric_variables) { + var v = numeric_variables[j]; + to_include.push(v); + } + var drill_variables = []; + + function set_filter(variable) { + filter_control.selectAll('*').remove(); + if (variable) { + // Get unique values for this variable + var values = []; + data.forEach(function(d) { + var v = d[variable]; + if (values.indexOf(v) < 0) { values.push(v); } + }); + + selected_colors = []; + for (var j in values) { + var v = values[j]; + selected_colors.push(v); + } + + var filter_li = + filter_control + .append('p').text('Filter by '+variable+': ') + .append('ul') + .selectAll('li') + .data(values) + .enter().append('li'); + + filter_li.append('input') + .attr('type', 'checkbox') + .attr('checked', 'checked') + .on('click', function(d, i) { + var new_selected_colors = []; + for (var j in selected_colors) { + var v = selected_colors[j]; + if (v !== d || this.checked) { new_selected_colors.push(v); } + } + if (this.checked) { new_selected_colors.push(d); } + selected_colors = new_selected_colors; + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + }); + filter_li.append('label') + .html(function(d) { return d; }); + } + } + + size_a = size_control.append('p').text('Change cell size: '); + size_a.append('a') + .attr('href', '#') + .html('-') + .on('click', function() { + self.__cell_size *= 0.75; + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + }); + size_a.append('span').html(' '); + size_a.append('a') + .attr('href', '#') + .html('+') + .on('click', function() { + self.__cell_size *= 1.25; + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + }); + + color_control.append('p').text('Select a variable to color:') + color_control + .append('ul') + .selectAll('li') + .data(string_variables) + .enter().append('li') + .append('a') + .attr('href', '#') + .text(function(d) { return d ? d : 'None'; }) + .on('click', function(d, i) { + color_variable = d; + selected_colors = undefined; + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + set_filter(d); + }); + + var variable_li = + variable_control + .append('p').text('Include variables: ') + .append('ul') + .selectAll('li') + .data(numeric_variables) + .enter().append('li'); + + variable_li.append('input') + .attr('type', 'checkbox') + .attr('checked', 'checked') + .on('click', function(d, i) { + var new_to_include = []; + for (var j in to_include) { + var v = to_include[j]; + if (v !== d || this.checked) { new_to_include.push(v); } + } + if (this.checked) { new_to_include.push(d); } + to_include = new_to_include; + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + }); + variable_li.append('label') + .html(function(d) { return d; }); + + drill_li = + drill_control + .append('p').text('Drill and Expand: ') + .append('ul') + .selectAll('li') + .data(numeric_variables) + .enter().append('li'); + + drill_li.append('input') + .attr('type', 'checkbox') + .on('click', function(d, i) { + var new_drill_variables = []; + for (var j in drill_variables) { + var v = drill_variables[j]; + if (v !== d || this.checked) { new_drill_variables.push(v); } + } + if (this.checked) { new_drill_variables.push(d); } + drill_variables = new_drill_variables; + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + }); + drill_li.append('label') + .html(function(d) { return d+' ('+numeric_variable_values[d].length+')'; }); + + self.__draw(self.__cell_size, svg, color_variable, selected_colors, to_include, drill_variables); + }); +}; + +ScatterMatrix.prototype.__draw = + function(cell_size, container_el, color_variable, selected_colors, to_include, drill_variables) { + var self = this; + this.onData(function() { + var data = self.__data; + + if (color_variable && selected_colors) { + data = []; + self.__data.forEach(function(d) { + if (selected_colors.indexOf(d[color_variable]) >= 0) { data.push(d); } + }); + } + + container_el.selectAll('*').remove(); + + // If no data, don't do anything + if (data.length == 0) { return; } + + // Parse headers from first row of data + var numeric_variables = []; + for (k in data[0]) { + if (!isNaN(+data[0][k]) && to_include.indexOf(k) >= 0) { numeric_variables.push(k); } + } + numeric_variables.sort(); + + // Get values of the string variable + var colors = []; + if (color_variable) { + // Using self.__data, instead of data, so our css classes are consistent when + // we filter by value. + self.__data.forEach(function(d) { + var s = d[color_variable]; + if (colors.indexOf(s) < 0) { colors.push(s); } + }); + } + + function color_class(d) { + var c = d; + if (color_variable && d[color_variable]) { c = d[color_variable]; } + return colors.length > 0 ? 'color-'+colors.indexOf(c) : 'color-2'; + } + + // Size parameters + var size = cell_size, padding = 10, + axis_width = 20, axis_height = 15, legend_width = 200, label_height = 15; + + // Get x and y scales for each numeric variable + var x = {}, y = {}; + numeric_variables.forEach(function(trait) { + // Coerce values to numbers. + data.forEach(function(d) { d[trait] = +d[trait]; }); + + var value = function(d) { return d[trait]; }, + domain = [d3.min(data, value), d3.max(data, value)], + range_x = [padding / 2, size - padding / 2], + range_y = [padding / 2, size - padding / 2]; + + x[trait] = d3.scale.linear().domain(domain).range(range_x); + y[trait] = d3.scale.linear().domain(domain).range(range_y.reverse()); + }); + + // When drilling, user select one or more variables. The first drilled + // variable becomes the x-axis variable for all columns, and each column + // contains only data points that match specific values for each of the + // drilled variables other than the first. + + var drill_values = []; + var drill_degrees = [] + drill_variables.forEach(function(variable) { + // Skip first one, since that's just the x axis + if (drill_values.length == 0) { + drill_values.push([]); + drill_degrees.push(1); + } + else { + var values = []; + data.forEach(function(d) { + var v = d[variable]; + if (v !== undefined && values.indexOf(v) < 0) { values.push(v); } + }); + values.sort(); + drill_values.push(values); + drill_degrees.push(values.length); + } + }); + var total_columns = 1; + drill_degrees.forEach(function(d) { total_columns *= d; }); + + // Pick out stuff to draw on horizontal and vertical dimensions + + if (drill_variables.length > 0) { + // First drill is now the x-axis variable for all columns + x_variables = []; + for (var i=0; i 0) { + // Don't draw any of the "drilled" variables in vertical dimension + y_variables = []; + numeric_variables.forEach(function(variable) { + if (drill_variables.indexOf(variable) < 0) { y_variables.push(variable); } + }); + } + else { + y_variables = numeric_variables.slice(0); + } + + var filter_descriptions = 0; + if (drill_variables.length > 1) { + filter_descriptions = drill_variables.length-1; + } + + // Axes + var x_axis = d3.svg.axis(); + var y_axis = d3.svg.axis(); + var intf = d3.format('d'); + var fltf = d3.format('.f'); + var scif = d3.format('e'); + + x_axis.ticks(5) + .tickSize(size * y_variables.length) + .tickFormat(function(d) { + if (Math.abs(+d) > 10000 || (Math.abs(d) < 0.001 && Math.abs(d) != 0)) { return scif(d); } + if (parseInt(d) == +d) { return intf(d); } + return fltf(d); + }); + + y_axis.ticks(5) + .tickSize(size * x_variables.length) + .tickFormat(function(d) { + if (Math.abs(+d) > 10000 || (Math.abs(d) < 0.001 && Math.abs(d) != 0)) { return scif(d); } + if (parseInt(d) == +d) { return intf(d); } + return fltf(d); + }); + + // Brush - for highlighting regions of data + var brush = d3.svg.brush() + .on("brushstart", brushstart) + .on("brush", brush) + .on("brushend", brushend); + + // Root panel + var svg = container_el.append("svg:svg") + .attr("width", label_height + size * x_variables.length + axis_width + padding + legend_width) + .attr("height", size * y_variables.length + axis_height + label_height + label_height*filter_descriptions) + .append("svg:g") + .attr("transform", "translate("+label_height+",0)"); + + // Push legend to the side + var legend = svg.selectAll("g.legend") + .data(colors) + .enter().append("svg:g") + .attr("class", "legend") + .attr("transform", function(d, i) { + return "translate(" + (label_height + size * x_variables.length + padding) + "," + (i*20+10) + ")"; + }); + + legend.append("svg:circle") + .attr("class", function(d, i) { return color_class(d); }) + .attr("r", 3); + + legend.append("svg:text") + .attr("x", 12) + .attr("dy", ".31em") + .text(function(d) { return d; }); + + // Draw X-axis + svg.selectAll("g.x.axis") + .data(x_variables) + .enter().append("svg:g") + .attr("class", "x axis") + .attr("transform", function(d, i) { return "translate(" + i * size + ",0)"; }) + .each(function(d) { d3.select(this).call(x_axis.scale(x[d]).orient("bottom")); }); + + // Draw Y-axis + svg.selectAll("g.y.axis") + .data(y_variables) + .enter().append("svg:g") + .attr("class", "y axis") + .attr("transform", function(d, i) { return "translate(0," + i * size + ")"; }) + .each(function(d) { d3.select(this).call(y_axis.scale(y[d]).orient("right")); }); + + // Draw scatter plot + var cell = svg.selectAll("g.cell") + .data(cross(x_variables, y_variables)) + .enter().append("svg:g") + .attr("class", "cell") + .attr("transform", function(d) { return "translate(" + d.i * size + "," + d.j * size + ")"; }) + .each(plot); + + // Add titles for y variables + cell.filter(function(d) { return d.i == 0; }).append("svg:text") + .attr("x", padding-size) + .attr("y", -label_height) + .attr("dy", ".71em") + .attr("transform", function(d) { return "rotate(-90)"; }) + .text(function(d) { return d.y; }); + + function plot(p) { + // console.log(p); + + var data_to_draw = data; + + // If drilling, compute what values of the drill variables correspond to + // this column. + // + var filter = {}; + if (drill_variables.length > 1) { + var column = p.i; + + var cap = 1; + for (var i=drill_variables.length-1; i > 0; i--) { + var var_name = drill_variables[i]; + var var_value = undefined; + + if (i == drill_variables.length-1) { + // for the last drill variable, we index by % + var_value = drill_values[i][column % drill_degrees[i]]; + } + else { + // otherwise divide by capacity of subsequent variables to get value array index + var_value = drill_values[i][parseInt(column/cap)]; + } + + filter[var_name] = var_value; + cap *= drill_degrees[i]; + } + + data_to_draw = []; + data.forEach(function(d) { + var pass = true; + for (k in filter) { if (d[k] != filter[k]) { pass = false; break; } } + if (pass === true) { data_to_draw.push(d); } + }); + } + + var cell = d3.select(this); + + // Frame + cell.append("svg:rect") + .attr("class", "frame") + .attr("x", padding / 2) + .attr("y", padding / 2) + .attr("width", size - padding) + .attr("height", size - padding); + + // Scatter plot dots + cell.selectAll("circle") + .data(data_to_draw) + .enter().append("svg:circle") + .attr("class", function(d) { return color_class(d); }) + .attr("cx", function(d) { return x[p.x](d[p.x]); }) + .attr("cy", function(d) { return y[p.y](d[p.y]); }) + .attr("r", 5); + + // Add titles for x variables and drill variable values + if (p.j == y_variables.length-1) { + cell.append("svg:text") + .attr("x", padding) + .attr("y", size+axis_height) + .attr("dy", ".71em") + .text(function(d) { return d.x; }); + + if (drill_variables.length > 1) { + var i = 0; + for (k in filter) { + i += 1; + cell.append("svg:text") + .attr("x", padding) + .attr("y", size+axis_height+label_height*i) + .attr("dy", ".71em") + .text(function(d) { return filter[k]+': '+k; }); + } + } + } + + // Brush + cell.call(brush.x(x[p.x]).y(y[p.y])); + } + + // Clear the previously-active brush, if any + function brushstart(p) { + if (brush.data !== p) { + cell.call(brush.clear()); + brush.x(x[p.x]).y(y[p.y]).data = p; + } + } + + // Highlight selected circles + function brush(p) { + var e = brush.extent(); + svg.selectAll(".cell circle").attr("class", function(d) { + return e[0][0] <= d[p.x] && d[p.x] <= e[1][0] + && e[0][1] <= d[p.y] && d[p.y] <= e[1][1] + ? color_class(d) : null; + }); + } + + // If brush is empty, select all circles + function brushend() { + if (brush.empty()) svg.selectAll(".cell circle").attr("class", function(d) { + return color_class(d); + }); + } + + function cross(a, b) { + var c = [], n = a.length, m = b.length, i, j; + for (i = -1; ++i < n;) for (j = -1; ++j < m;) c.push({x: a[i], i: i, y: b[j], j: j}); + return c; + } + }); + +}; + diff --git a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee index 8b73b85d..fdec0ee4 100755 --- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee +++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.coffee @@ -67,15 +67,24 @@ do_ajax_post = (url, form_data) -> success: (data) => clearInterval(this.my_timer) $('#progress_bar_container').modal('hide') - $("body").html(data) + open_mapping_results(data) + #$("body").html(data) ) console.log("settingInterval") this.my_timer = setInterval(get_progress, 1000) return false +open_mapping_results = (data) -> + $.colorbox( + html: data + href: "#mapping_results_holder" + height: "80%" + width: "80%" + ) + showalert = (message,alerttype) -> - $('#alert_placeholder').append('
×'+message+'
') + $('#alert_placeholder').append('
×'+message+'
') $("#interval_mapping_compute").click(() => @@ -95,32 +104,6 @@ $("#interval_mapping_compute").click(() => console.log("form_data is:", form_data) do_ajax_post(url, form_data) - - #remove_outliers = confirm("Remove outliers?") - #if use_outliers == true - # block_outliers() - # do_ajax_post(url, form_data) - #else - # do_ajax_post(url, form_data) - #$.ajax( - # type: "POST" - # url: url - # data: form_data - # error: (xhr, ajaxOptions, thrownError) => - # alert("Sorry, an error occurred") - # console.log(xhr) - # clearInterval(this.my_timer) - # $('#progress_bar_container').modal('hide') - # $("body").html("We got an error.") - # success: (data) => - # clearInterval(this.my_timer) - # $('#progress_bar_container').modal('hide') - # $("body").html(data) - #) - #console.log("settingInterval") - # - #this.my_timer = setInterval(get_progress, 1000) - #return false ) $('#suggestive').hide() @@ -146,26 +129,6 @@ $("#pylmm_compute").click(() => #if use_outliers == true # block_outliers() do_ajax_post(url, form_data) - - #$.ajax( - # type: "POST" - # url: url - # data: form_data - # error: (xhr, ajaxOptions, thrownError) => - # alert("Sorry, an error occurred") - # console.log(xhr) - # clearInterval(this.my_timer) - # $('#progress_bar_container').modal('hide') - # $("body").html("We got an error.") - # success: (data) => - # clearInterval(this.my_timer) - # $('#progress_bar_container').modal('hide') - # $("body").html(data) - #) - #console.log("settingInterval") - # - #this.my_timer = setInterval(get_progress, 1000) - #return false ) @@ -184,26 +147,6 @@ $("#rqtl_geno_compute").click(() => #if use_outliers == true # block_outliers() do_ajax_post(url, form_data) - - #$.ajax( - # type: "POST" - # url: url - # data: form_data - # error: (xhr, ajaxOptions, thrownError) => - # alert("Sorry, an error occurred") - # console.log(xhr) - # clearInterval(this.my_timer) - # $('#progress_bar_container').modal('hide') - # $("body").html("We got an error.") - # success: (data) => - # clearInterval(this.my_timer) - # $('#progress_bar_container').modal('hide') - # $("body").html(data) - #) - #console.log("settingInterval") - # - #this.my_timer = setInterval(get_progress, 1000) - #return false ) @@ -218,26 +161,6 @@ $("#plink_compute").click(() => console.log("form_data is:", form_data) do_ajax_post(url, form_data) - - #$.ajax( - # type: "POST" - # url: url - # data: form_data - # error: (xhr, ajaxOptions, thrownError) => - # alert("Sorry, an error occurred") - # console.log(xhr) - # clearInterval(this.my_timer) - # $('#static_progress_bar_container').modal('hide') - # $("body").html("We got an error.") - # success: (data) => - # clearInterval(this.my_timer) - # $('#static_progress_bar_container').modal('hide') - # $("body").html(data) - #) - #console.log("settingInterval") - # - #this.my_timer = setInterval(get_progress, 1000) - #return false ) $("#gemma_compute").click(() => @@ -251,26 +174,6 @@ $("#gemma_compute").click(() => console.log("form_data is:", form_data) do_ajax_post(url, form_data) - - #$.ajax( - # type: "POST" - # url: url - # data: form_data - # error: (xhr, ajaxOptions, thrownError) => - # alert("Sorry, an error occurred") - # console.log(xhr) - # clearInterval(this.my_timer) - # $('#static_progress_bar_container').modal('hide') - # $("body").html("We got an error.") - # success: (data) => - # clearInterval(this.my_timer) - # $('#static_progress_bar_container').modal('hide') - # $("body").html(data) - #) - #console.log("settingInterval") - # - #this.my_timer = setInterval(get_progress, 1000) - #return false ) #$(".submit_special").click(submit_special) diff --git a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js index 92d3183a..c8988cdc 100755 --- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js +++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js @@ -1,5 +1,5 @@ // Generated by CoffeeScript 1.8.0 -var block_outliers, composite_mapping_fields, do_ajax_post, get_progress, mapping_method_fields, showalert, submit_special, toggle_enable_disable, update_time_remaining; +var block_outliers, composite_mapping_fields, do_ajax_post, get_progress, mapping_method_fields, open_mapping_results, showalert, submit_special, toggle_enable_disable, update_time_remaining; submit_special = function() { var url; @@ -86,7 +86,7 @@ do_ajax_post = function(url, form_data) { return function(data) { clearInterval(_this.my_timer); $('#progress_bar_container').modal('hide'); - return $("body").html(data); + return open_mapping_results(data); }; })(this) }); @@ -95,6 +95,15 @@ do_ajax_post = function(url, form_data) { return false; }; +open_mapping_results = function(data) { + return $.colorbox({ + html: data, + href: "#mapping_results_holder", + height: "80%", + width: "80%" + }); +}; + showalert = function(message, alerttype) { return $('#alert_placeholder').append('
�' + message + '
'); }; diff --git a/wqflask/wqflask/static/packages/bootstrap/css/bootstrap.css b/wqflask/wqflask/static/packages/bootstrap/css/bootstrap.css index 3dc73a7a..8326e83f 100755 --- a/wqflask/wqflask/static/packages/bootstrap/css/bootstrap.css +++ b/wqflask/wqflask/static/packages/bootstrap/css/bootstrap.css @@ -1414,6 +1414,7 @@ pre code { margin-right: auto; margin-left: auto; } + .row { margin-right: -15px; margin-left: -15px; @@ -2763,13 +2764,13 @@ select[multiple].form-group-lg .form-control { margin-right: -15px; margin-left: -15px; } -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } + +.form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; } + .form-horizontal .has-feedback .form-control-feedback { right: 15px; } @@ -3760,7 +3761,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { top: auto; left: auto; } -@media (min-width: 768px) { + .nav-tabs.nav-justified > li { display: table-cell; width: 1%; @@ -3768,7 +3769,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-tabs.nav-justified > li > a { margin-bottom: 0; } -} + .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 4px; @@ -3778,7 +3779,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #ddd; } -@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; @@ -3788,7 +3789,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #fff; } -} + .nav-pills > li { float: left; } @@ -3825,7 +3826,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { top: auto; left: auto; } -@media (min-width: 768px) { + .nav-justified > li { display: table-cell; width: 1%; @@ -3833,7 +3834,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-justified > li > a { margin-bottom: 0; } -} + .nav-tabs-justified { border-bottom: 0; } @@ -3846,7 +3847,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-tabs-justified > .active > a:focus { border: 1px solid #ddd; } -@media (min-width: 768px) { + .nav-tabs-justified > li > a { border-bottom: 1px solid #ddd; border-radius: 4px 4px 0 0; @@ -3856,7 +3857,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-tabs-justified > .active > a:focus { border-bottom-color: #fff; } -} + .tab-content > .tab-pane { display: none; visibility: hidden; @@ -4079,7 +4080,7 @@ float: right!important; background-image: none; } } -@media (min-width: 768px) { + .navbar-nav { float: left; margin: 0; @@ -4091,7 +4092,7 @@ float: right!important; padding-top: 15px; padding-bottom: 15px; } -} + .navbar-form { padding: 10px 15px; margin-top: 8px; @@ -4200,25 +4201,25 @@ float: right!important; margin-top: 15px; margin-bottom: 15px; } -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } + +.navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; } -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } + + +.navbar-left { + float: left !important; +} +.navbar-right { + float: right !important; + margin-right: -15px; } +.navbar-right ~ .navbar-right { + margin-right: 0; +} + .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; @@ -6061,6 +6062,7 @@ button.close { .carousel-caption .btn { text-shadow: none; } +/* @media screen and (min-width: 768px) { .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right, @@ -6088,6 +6090,7 @@ button.close { bottom: 20px; } } +*/ .clearfix:before, .clearfix:after, .dl-horizontal dd:before, @@ -6195,6 +6198,7 @@ button.close { .visible-lg-inline-block { display: none !important; } +/* @media (max-width: 767px) { .visible-xs { display: block !important; @@ -6353,6 +6357,7 @@ button.close { display: table-cell !important; } } +*/ .visible-print-block { display: none !important; } diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 3d777866..fc1edf2a 100755 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -69,9 +69,9 @@ Description Location Mean - Max LRS + Max LRS ? Max LRS Location - Additive Effect + Additive Effect ? @@ -119,6 +119,13 @@ - - - - - - - - - - - - - - - - - +{% block css %} + + + + + + +{% endblock %} +{% block content %} + + +
+
+

+ Whole Genome Mapping +

+
+ + + +
+
+ + + +
+
+
+
+ +
+
+
+

+ Results +

+
+ + + + + + + + + + + + + {% for marker in qtl_results %} + + + + + + + + + {% endfor %} + +
IndexLRS ScoreChrMbLocusAdditive Effect
{{ loop.index }}{{ marker.lrs_value|float }}{{ marker.chr|int }}{{ marker.Mb|float }}{{ marker.name }}{{ marker.additive|float }}
+ +
+ + + +{% endblock %} + +{% block js %} + + + + + + + + + + + + + + + + + + {% endblock %} \ No newline at end of file diff --git a/wqflask/wqflask/templates/new_security/login_user.html b/wqflask/wqflask/templates/new_security/login_user.html index ecf8278c..8ad0c79e 100755 --- a/wqflask/wqflask/templates/new_security/login_user.html +++ b/wqflask/wqflask/templates/new_security/login_user.html @@ -3,6 +3,9 @@ {% block content %}
+ + {{ flash_me() }} + @@ -24,15 +27,15 @@
- -
+ +
- -
+ +

Forgot your password?
@@ -41,17 +44,15 @@
- -
- + +
+ Remember me
- -
+ +
diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 055ab979..731f6fbd 100755 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -49,7 +49,13 @@ {% for header in header_fields %} + {% if header == 'Max LRS' %} + {{header}} ? + {% elif header == 'Additive Effect' %} + {{header}} ? + {% else %} {{header}} + {% endif %} {% endfor %} @@ -140,17 +146,11 @@ $(document).ready( function () { - /*num_columns = $('#trait_table').find('tr:first th').length; - - nul_cols = [] - for (i=0; i tbody > tr').each(function() { - if ($(this).find('td:eq(i)').html()){ - continue; - } - }); - nul_cols.push(i) - }*/ + $('#trait_table tr').click(function(event) { + if (event.target.type !== 'checkbox') { + $(':checkbox', this).trigger('click'); + } + }); console.time("Creating table"); {% if dataset.type == 'ProbeSet' %} @@ -166,7 +166,7 @@ "sWidth": "15%" }, { "sType": "cust-txt" }, { "sType": "natural", - "sWidth": "10%" }, + "sWidth": "12%" }, { "sType": "natural", "sWidth": "15%" }, { "sType": "cust-txt" } diff --git a/wqflask/wqflask/templates/show_trait.html b/wqflask/wqflask/templates/show_trait.html index 5d0437df..a1723ef8 100755 --- a/wqflask/wqflask/templates/show_trait.html +++ b/wqflask/wqflask/templates/show_trait.html @@ -6,6 +6,7 @@ + @@ -40,7 +41,7 @@ @@ -54,7 +55,7 @@ @@ -68,7 +69,7 @@ @@ -82,7 +83,7 @@ @@ -122,6 +123,7 @@ + @@ -162,6 +164,16 @@ }; $(document).ready( function () { + + $('.panel-heading').find('a').click(function () { + if ($(this).hasClass('collapsed')){ + $(this).find('.glyphicon-chevron-down').removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up'); + } + else { + $(this).find('.glyphicon-chevron-up').removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down'); + } + }); + console.time("Creating table"); {% if sample_groups[0].se_exists() %} diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html index 84ddfd53..6f5fe237 100755 --- a/wqflask/wqflask/templates/show_trait_mapping_tools.html +++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html @@ -1,5 +1,7 @@
+
+
@@ -212,10 +208,23 @@
-
-
{% endif %}
+
+
+
+
Interval Mapping
+
Interval mapping is a process in which the statistical significance of a hypothetical QTL is evaluated at regular points across a chromosome, even in the absence of explicit genotype data at those points.
+
pyLMM
+
pyLMM is a fast and lightweight linear mixed-model (LMM) solver for use in genome-wide association studies (GWAS).
+
R/qtl
+
R/qtl is an extensible, interactive environment for mapping quantitative trait loci (QTL) in experimental crosses.
+
+
+
+
\ No newline at end of file diff --git a/wqflask/wqflask/templates/show_trait_statistics_new.html b/wqflask/wqflask/templates/show_trait_statistics_new.html index 11f5ba68..9ce60c0b 100755 --- a/wqflask/wqflask/templates/show_trait_statistics_new.html +++ b/wqflask/wqflask/templates/show_trait_statistics_new.html @@ -78,10 +78,28 @@ Color by Trait
-
+
+
+
+ + + + + +
+
+
-
-- cgit v1.2.3