From 7aa792b0616fab1268269ad8c4675117c42544ad Mon Sep 17 00:00:00 2001 From: zsloan Date: Sun, 30 Oct 2022 17:10:28 +0000 Subject: [PATCH 1/5] Create button for adding traits to table --- wqflask/wqflask/templates/show_trait_edit_data.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wqflask/wqflask/templates/show_trait_edit_data.html b/wqflask/wqflask/templates/show_trait_edit_data.html index 31807f459..5b258de2e 100644 --- a/wqflask/wqflask/templates/show_trait_edit_data.html +++ b/wqflask/wqflask/templates/show_trait_edit_data.html @@ -1,6 +1,8 @@
{% for sample_type in sample_groups %}
+ +

{% if loop.index == 1 and (sample_groups[0].se_exists or has_num_cases or sample_groups[0].attributes|length > 0) %} Show/Hide Columns:
From e710c917aea735f2e56e28dd31aeabcd7fa5b1de Mon Sep 17 00:00:00 2001 From: zsloan Date: Sun, 30 Oct 2022 17:12:13 +0000 Subject: [PATCH 2/5] Destroy/empty table before creation This is necessary to clear new column headers, but I need to change it so that it only does it when adding new columns --- wqflask/wqflask/static/new/javascript/create_datatable.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wqflask/wqflask/static/new/javascript/create_datatable.js b/wqflask/wqflask/static/new/javascript/create_datatable.js index 84754da8d..ddf72e9c9 100644 --- a/wqflask/wqflask/static/new/javascript/create_datatable.js +++ b/wqflask/wqflask/static/new/javascript/create_datatable.js @@ -67,6 +67,9 @@ create_table = function(tableId="trait_table", tableData = [], columnDefs = [], recheckRows(theTable, checkedRows); } } else { + theTable = $('#' + tableId).DataTable(tableSettings); + theTable.destroy(); + $('#' + tableId).empty(); theTable = $('#' + tableId).DataTable(tableSettings); theTable.draw(); $('#' + tableId + '_container').css("width", String($('#' + tableId).width() + 17) + "px"); From 6651900639e9d02af999a4cefe425fd0415285e1 Mon Sep 17 00:00:00 2001 From: zsloan Date: Sun, 30 Oct 2022 17:15:36 +0000 Subject: [PATCH 3/5] Add JS for adding traits to table --- .../new/javascript/add_traits_to_table.js | 291 +++++++++++++++ .../initialize_show_trait_tables.js | 349 +++++++++--------- .../static/new/javascript/show_trait.js | 11 +- 3 files changed, 484 insertions(+), 167 deletions(-) create mode 100644 wqflask/wqflask/static/new/javascript/add_traits_to_table.js diff --git a/wqflask/wqflask/static/new/javascript/add_traits_to_table.js b/wqflask/wqflask/static/new/javascript/add_traits_to_table.js new file mode 100644 index 000000000..74e175fb6 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/add_traits_to_table.js @@ -0,0 +1,291 @@ +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, 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; }; + +this_trait_data = null; + +var selected_traits = {}; + +$('#collections_list').attr("style", "width: 100%;"); +$('#trait_table').dataTable( { + "drawCallback": function( settings ) { + $('#trait_table tr').click(function(event) { + if (event.target.type !== 'checkbox') { + $(':checkbox', this).trigger('click'); + } + }); + }, + "columns": [ + { "type": "natural", "width": "3%" }, + { "type": "natural", "width": "8%" }, + { "type": "natural", "width": "20%" }, + { "type": "natural", "width": "25%" }, + { "type": "natural", "width": "25%" }, + { "type": "natural", "width": "15%" } + ], + "columnDefs": [ { + "targets": 0, + "orderable": false + } ], + "order": [[1, "asc" ]], + "sDom": "RZtr", + "iDisplayLength": -1, + "autoWidth": true, + "bDeferRender": true, + "bSortClasses": false, + "paging": false, + "orderClasses": true +} ); + +if ( ! $.fn.DataTable.isDataTable( '#collection_table' ) ) { + $('#collection_table').dataTable( { + "createdRow": function ( row, data, index ) { + if ($('td', row).eq(2).text().length > 40) { + $('td', row).eq(2).text($('td', row).eq(2).text().substring(0, 40)); + $('td', row).eq(2).text($('td', row).eq(2).text() + '...') + } + if ($('td', row).eq(4).text().length > 50) { + $('td', row).eq(4).text($('td', row).eq(4).text().substring(0, 50)); + $('td', row).eq(4).text($('td', row).eq(4).text() + '...') + } + }, + "columnDefs": [ { + "targets": 0, + "orderable": false + } ], + "order": [[1, "asc" ]], + "sDom": "ZRtr", + "iDisplayLength": -1, + "autoWidth": true, + "bSortClasses": false, + "paging": false, + "orderClasses": true + } ); +} + +collection_click = function() { + var this_collection_url; + this_collection_url = $(this).find('.collection_name').prop("href"); + this_collection_url += "&json"; + collection_list = $("#collections_holder").html(); + return $.ajax({ + dataType: "json", + url: this_collection_url, + success: process_traits + }); +}; + +submit_click = function() { + var sample, samples, scatter_matrix, this_trait_vals, trait, trait_names, trait_vals_csv, traits, _i, _j, _len, _len1, _ref; + new_data = js_data['sample_lists']; + traits = []; + $('#collections_holder').find('input[type=checkbox]:checked').each(function() { + var this_dataset, this_trait, this_trait_url; + this_trait = $(this).parents('tr').find('.trait').text(); + this_dataset = $(this).parents('tr').find('.dataset').data("dataset"); + this_trait_url = "/trait/get_sample_data?trait=" + this_trait + "&dataset=" + this_dataset; + return $.ajax({ + dataType: "json", + url: this_trait_url, + async: false, + success: add_trait_data + }); + }); + + tableIds = ["samples_primary"] + console.log("WIDTH:", $('#primary_container').width() ) + $('#primary_container').width($('#primary_container').width() + 40*Object.keys(selected_traits).length) + if (js_data.sample_lists.length > 1) { + tableIds.push("samples_other") + $('#other_container').width($('#primary_container').width() + 40*selected_traits.length) + } + + for (var i = 0; i < tableIds.length; i++) { + for (var j = 0; j < js_data['sample_lists'][i].length; j++){ + for (const [key, value] of Object.entries(selected_traits)) { + if (js_data['sample_lists'][i][j].name in selected_traits[key]){ + new_data[i][j][key] = selected_traits[key][js_data['sample_lists'][i][j].name] + } else { + new_data[i][j][key] = "x" + } + } + } + } + + var other_columns = [] + i = 0; + for (const [key, _value] of Object.entries(selected_traits)) { + new_ob = { + 'title': key, + 'type': "natural", + 'searchable' : true, + 'width': "40px", + 'targets': $('#samples_primary thead th').length + i, + 'data': key + } + console.log("NEW OB:", new_ob) + other_columns.push(new_ob); + i++; + } + someotherthing = other_columns + console.log(someotherthing) + + console.log("CHECKING:", other_columns) + + initialize_show_trait_tables(new_data=new_data, new_columns=other_columns) + + return $.colorbox.close(); +}; + +trait_click = function() { + var dataset, this_trait_url, trait; + + trait = $(this).parent().find('.trait').text(); + dataset = $(this).parent().find('.dataset').text(); + this_trait_url = "/trait/get_sample_data?trait=" + trait + "&dataset=" + dataset; + $.ajax({ + dataType: "json", + url: this_trait_url, + success: get_trait_data + }); + return $.colorbox.close(); +}; + +trait_row_click = function() { + var dataset, this_trait_url, trait; + trait = $(this).find('.trait').text(); + dataset = $(this).find('.dataset').data("dataset"); + this_trait_url = "/trait/get_sample_data?trait=" + trait + "&dataset=" + dataset; + $.ajax({ + dataType: "json", + url: this_trait_url, + success: get_trait_data + }); + return $.colorbox.close(); +}; + +add_trait_data = function(trait_data) { + selected_traits[trait_data[0].name] = trait_data[1]; + return false; +}; + +get_trait_data = function(trait_data) { + var sample, samples, this_trait_vals, trait_sample_data, vals, _i, _len; + trait_sample_data = trait_data[1]; + if ( $('input[name=allsamples]').length ) { + samples = $('input[name=allsamples]').val().split(" "); + } else { + samples = js_data.indIDs + } + sample_vals = []; + vals = []; + for (_i = 0, _len = samples.length; _i < _len; _i++) { + sample = samples[_i]; + if (sample in trait_sample_data) { + sample_vals.push(sample + ":" + parseFloat(trait_sample_data[sample])) + vals.push(parseFloat(trait_sample_data[sample])) + } else { + sample_vals.push(null) + vals.push(null) + } + } + if ( $('input[name=allsamples]').length ) { + if ($('input[name=samples]').length < 1) { + $('#hidden_inputs').append(''); + } + $('#hidden_inputs').append(''); + this_trait_vals = get_this_trait_vals(samples); + return color_by_trait(trait_sample_data); + } else{ + sorted = vals.slice().sort(function(a,b){return a-b}) + ranks = vals.slice().map(function(v){ return sorted.indexOf(v)+1 }); + sample_ranks = [] + for (_i = 0; _i < samples.length; _i++){ + if (samples[_i] in trait_sample_data){ + sample_ranks.push(samples[_i] + ":" + ranks[_i]) + } else { + sample_ranks.push(null) + } + } + return false + } +}; + +get_this_trait_vals = function(samples) { + var sample, this_trait_vals, this_val, this_vals_json, _i, _len; + this_trait_vals = []; + for (_i = 0, _len = samples.length; _i < _len; _i++) { + sample = samples[_i]; + this_val = parseFloat($("input[name='value:" + sample + "']").val()); + if (!isNaN(this_val)) { + this_trait_vals.push(this_val); + } else { + this_trait_vals.push(null); + } + } + this_vals_json = '[' + this_trait_vals.toString() + ']'; + return this_trait_vals; +}; + +assemble_into_json = function(this_trait_vals) { + var json_data, json_ids, num_traits, samples; + num_traits = $('input[name=vals]').length; + samples = $('input[name=samples]').val(); + json_ids = samples; + json_data = '[' + this_trait_vals; + $('input[name=vals]').each((function(_this) { + return function(index, element) { + return json_data += ',' + $(element).val(); + }; + })(this)); + json_data += ']'; + return [json_ids, json_data]; +}; + +color_by_trait = function(trait_sample_data, textStatus, jqXHR) { + return root.bar_chart.color_by_trait(trait_sample_data); +}; + +process_traits = function(trait_data, textStatus, jqXHR) { + var the_html, trait, _i, _len; + the_html = ""; + the_html += " "; + the_html += ""; + the_html += ""; + the_html += ""; + for (_i = 0, _len = trait_data.length; _i < _len; _i++) { + trait = trait_data[_i]; + the_html += ""; + the_html += ""; + if ("abbreviation" in trait) { + the_html += ""; + } else if ("symbol" in trait) { + the_html += ""; + } else { + the_html += ""; + } + the_html += ""; + the_html += ""; + } + the_html += ""; + the_html += "
RecordData SetDescription
" + trait.name + "" + trait.name + "" + trait.name + "" + trait.dataset_name + "" + trait.description + "
"; + the_html += "" + $("#collections_holder").html(the_html); + return $('#collections_holder').colorbox.resize(); +}; + +back_to_collections = function() { + $("#collections_holder").html(collection_list); + $(document).on("click", ".collection_line", collection_click); + return $('#collections_holder').colorbox.resize(); +}; + +$(".collection_line").on("click", collection_click); +$("#submit_cofactors").on("click", submit_click); + +if ($('#scatterplot2').length){ + $(".trait_line").on("click", trait_row_click); +} else { + $(".trait").on("click", trait_click); +} +$("#back_to_collections").on("click", back_to_collections); diff --git a/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js b/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js index cfc4f39e6..4942a46e5 100644 --- a/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js +++ b/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js @@ -1,225 +1,244 @@ // This file initializes the tables for the show_trait page - -// This variable is just created to get the column position of the first case attribute (if case attributes exist), since it's needed to set the row classes in createdRow for the DataTable -var attributeStartPos = 3; -if (js_data.se_exists) { - attributeStartPos += 2; -} -if (js_data.has_num_cases === true) { - attributeStartPos += 1; -} - -buildColumns = function() { - let columnList = [ - { - 'data': null, - 'orderDataType': "dom-checkbox", - 'searchable' : false, - 'targets': 0, - 'width': "25px", - 'render': function() { - return '' - } - }, - { - 'title': "ID", - 'type': "natural", - 'searchable' : false, - 'targets': 1, - 'width': "35px", - 'data': "this_id" - }, - { - 'title': "Sample", - 'type': "natural", - 'data': null, - 'targets': 2, - 'width': "60px", - 'render': function(data) { - return '' + data.name + '' - } - }, - { - 'title': "
Value
", - 'orderDataType': "dom-input", - 'type': "cust-txt", - 'data': null, - 'targets': 3, - 'width': "60px", - 'render': function(data) { - if (data.value == null) { - return '' - } else { - return '' - } - } - } - ]; - - attrStart = 4 +initialize_show_trait_tables = function(new_data = [], new_columns = []) { + // This variable is just created to get the column position of the first case attribute (if case attributes exist), since it's needed to set the row classes in createdRow for the DataTable + var attributeStartPos = 3; if (js_data.se_exists) { - attrStart += 2 - columnList.push( + attributeStartPos += 2; + } + if (js_data.has_num_cases === true) { + attributeStartPos += 1; + } + + buildColumns = function(new_columns = []) { + let columnList = [ { - 'bSortable': false, - 'type': "natural", 'data': null, - 'targets': 4, + 'orderDataType': "dom-checkbox", 'searchable' : false, + 'targets': 0, 'width': "25px", 'render': function() { - return '±' + return '' } }, { - 'title': "
SE
", + 'title': "ID", + 'type': "natural", + 'searchable' : false, + 'targets': 1, + 'width': "35px", + 'data': "this_id" + }, + { + 'title': "Sample", + 'type': "natural", + 'data': null, + 'targets': 2, + 'width': "60px", + 'render': function(data) { + return '' + data.name + '' + } + }, + { + 'title': "
Value
", 'orderDataType': "dom-input", 'type': "cust-txt", 'data': null, - 'targets': 5, + 'targets': 3, 'width': "60px", 'render': function(data) { - if (data.variance == null) { - return '' + if (data.value == null) { + return '' } else { - return '' + return '' } } } - ); + ]; - if (js_data.has_num_cases === true) { - attrStart += 1 + attrStart = 4 + if (js_data.se_exists) { + attrStart += 2 columnList.push( { - 'title': "
N
", - 'orderDataType': "dom-input", - 'type': "cust-txt", + 'bSortable': false, + 'type': "natural", 'data': null, - 'targets': 6, - 'width': "60px", - 'render': function(data) { - if (data.num_cases == null || data.num_cases == undefined) { - return '' - } else { - return '' - } + 'targets': 4, + 'searchable' : false, + 'width': "25px", + 'render': function() { + return '±' } - } - ); - } - } - else { - if (js_data.has_num_cases === true) { - attrStart += 1 - columnList.push( + }, { - 'title': "
N
", + 'title': "
SE
", 'orderDataType': "dom-input", 'type': "cust-txt", 'data': null, - 'targets': 4, + 'targets': 5, 'width': "60px", 'render': function(data) { - if (data.num_cases == null || data.num_cases == undefined) { - return '' + if (data.variance == null) { + return '' } else { - return '' + return '' } } } ); + + if (js_data.has_num_cases === true) { + attrStart += 1 + columnList.push( + { + 'title': "
N
", + 'orderDataType': "dom-input", + 'type': "cust-txt", + 'data': null, + 'targets': 6, + 'width': "60px", + 'render': function(data) { + if (data.num_cases == null || data.num_cases == undefined) { + return '' + } else { + return '' + } + } + } + ); + } + } + else { + if (js_data.has_num_cases === true) { + attrStart += 1 + columnList.push( + { + 'title': "
N
", + 'orderDataType': "dom-input", + 'type': "cust-txt", + 'data': null, + 'targets': 4, + 'width': "60px", + 'render': function(data) { + if (data.num_cases == null || data.num_cases == undefined) { + return '' + } else { + return '' + } + } + } + ); + } } - } - attrKeys = Object.keys(js_data.attributes).sort((a, b) => (js_data.attributes[a].id > js_data.attributes[b].id) ? 1 : -1) - for (i = 0; i < attrKeys.length; i++){ - columnList.push( - { - 'title': "
" + js_data.attributes[attrKeys[i]].name + "
", - 'type': "natural", - 'data': null, - 'targets': attrStart + i, - 'render': function(data, type, row, meta) { - attr_name = Object.keys(data.extra_attributes).sort((a, b) => (parseInt(a) > parseInt(b)) ? 1 : -1)[meta.col - data.first_attr_col] + attrKeys = Object.keys(js_data.attributes).sort((a, b) => (js_data.attributes[a].id > js_data.attributes[b].id) ? 1 : -1) + for (i = 0; i < attrKeys.length; i++){ + columnList.push( + { + 'title': "
" + js_data.attributes[attrKeys[i]].name + "
", + 'type': "natural", + 'data': null, + 'targets': attrStart + i, + 'render': function(data, type, row, meta) { + attr_name = Object.keys(data.extra_attributes).sort((a, b) => (parseInt(a) > parseInt(b)) ? 1 : -1)[meta.col - data.first_attr_col] - if (attr_name != null && attr_name != undefined){ - if (Array.isArray(data.extra_attributes[attr_name])){ - return '' + data.extra_attributes[attr_name][0] + '' + if (attr_name != null && attr_name != undefined){ + if (Array.isArray(data.extra_attributes[attr_name])){ + return '' + data.extra_attributes[attr_name][0] + '' + } else { + return data.extra_attributes[attr_name] + } } else { - return data.extra_attributes[attr_name] + return "" } - } else { - return "" } } - } - ) + ) + } + return columnList.concat(new_columns) } - return columnList -} -columnDefs = buildColumns(); + columnDefs = buildColumns(new_columns); -tableIds = ["samples_primary"] -if (js_data.sample_lists.length > 1) { - tableIds.push("samples_other") -} + tableIds = ["samples_primary"] + if (js_data.sample_lists.length > 1) { + tableIds.push("samples_other") + } -for (var i = 0; i < tableIds.length; i++) { - tableId = tableIds[i] + for (var i = 0; i < tableIds.length; i++) { + tableId = tableIds[i] - if (tableId == "samples_primary"){ - tableType = "Primary" - } else { - tableType = "Other" - } + if (tableId == "samples_primary"){ + tableType = "Primary" + } else { + tableType = "Other" + } - tableSettings = { - "drawCallback": function( settings ) { - $('#' + tableId + ' 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") && event.target.tagName.toLowerCase() !== 'a'){ - $(this).removeClass("selected") - } else if (event.target.tagName.toLowerCase() !== 'a') { - $(this).addClass("selected") + tableSettings = { + "drawCallback": function( settings ) { + $('#' + tableId + ' 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") && event.target.tagName.toLowerCase() !== 'a'){ + $(this).removeClass("selected") + } else if (event.target.tagName.toLowerCase() !== 'a') { + $(this).addClass("selected") + } + }); + }, + 'createdRow': function ( row, data, index ) { + $(row).attr('id', tableType + "_" + data.this_id) + $(row).addClass("value_se"); + if (data.outlier) { + $(row).addClass("outlier"); + $(row).attr("style", "background-color: orange;"); } - }); - }, - 'createdRow': function ( row, data, index ) { - $(row).attr('id', tableType + "_" + data.this_id) - $(row).addClass("value_se"); - if (data.outlier) { - $(row).addClass("outlier"); - $(row).attr("style", "background-color: orange;"); - } - $('td', row).eq(1).addClass("column_name-Index") - $('td', row).eq(2).addClass("column_name-Sample") - $('td', row).eq(3).addClass("column_name-Value") - if (js_data.se_exists) { - $('td', row).eq(5).addClass("column_name-SE") - if (js_data.has_num_cases === true) { - $('td', row).eq(6).addClass("column_name-num_cases") + $('td', row).eq(1).addClass("column_name-Index") + $('td', row).eq(2).addClass("column_name-Sample") + $('td', row).eq(3).addClass("column_name-Value") + if (js_data.se_exists) { + $('td', row).eq(5).addClass("column_name-SE") + if (js_data.has_num_cases === true) { + $('td', row).eq(6).addClass("column_name-num_cases") + } else { + if (js_data.has_num_cases === true) { + $('td', row).eq(4).addClass("column_name-num_cases") + } + } } else { if (js_data.has_num_cases === true) { $('td', row).eq(4).addClass("column_name-num_cases") } } - } else { - if (js_data.has_num_cases === true) { - $('td', row).eq(4).addClass("column_name-num_cases") + + for (j=0; j < attrKeys.length; j++) { + $('td', row).eq(attributeStartPos + j + 1).addClass("column_name-" + js_data.attributes[attrKeys[j]].name) + $('td', row).eq(attributeStartPos + j + 1).attr("style", "text-align: " + js_data.attributes[attrKeys[j]].alignment + "; padding-top: 2px; padding-bottom: 0px;") } } - - for (j=0; j < attrKeys.length; j++) { - $('td', row).eq(attributeStartPos + j + 1).addClass("column_name-" + js_data.attributes[attrKeys[j]].name) - $('td', row).eq(attributeStartPos + j + 1).attr("style", "text-align: " + js_data.attributes[attrKeys[j]].alignment + "; padding-top: 2px; padding-bottom: 0px;") - } + } + + if (new_data.length > 0){ + // empty_tables(); + create_table(tableId, new_data[i], columnDefs, tableSettings); + } else { + create_table(tableId, js_data['sample_lists'][i], columnDefs, tableSettings); } } +} - create_table(tableId, js_data['sample_lists'][i], columnDefs, tableSettings); +empty_tables = function() { + $('#primary_container').empty(); + $('#primary_container').append( + "\ + \ + \ + \ +

Loading...
" + ); } + +initialize_show_trait_tables(); diff --git a/wqflask/wqflask/static/new/javascript/show_trait.js b/wqflask/wqflask/static/new/javascript/show_trait.js index 1562ac253..7023e3d15 100644 --- a/wqflask/wqflask/static/new/javascript/show_trait.js +++ b/wqflask/wqflask/static/new/javascript/show_trait.js @@ -99,6 +99,10 @@ $(".select_covariates").click(function () { openCovariateSelection(); }); +$(".add-traits-to-table").click(function () { + openTraitSelection(); +}); + $(".remove_covariates").click(function () { $(".selected-covariates option:selected").each(function() { this_val = $(this).val(); @@ -147,19 +151,22 @@ $(".remove_all_covariates").click(function() { }) openTraitSelection = function() { - return $('#collections_holder').load('/collections/list?color_by_trait #collections_list', (function(_this) { + return $('#collections_holder').load('/collections/list #collections_list', (function(_this) { return function() { $.colorbox({ inline: true, href: "#collections_holder", + width: "1000px", + height: "700px", onComplete: function(){ - $.getScript("/static/new/javascript/get_traits_from_collection.js"); + $.getScript("/static/new/javascript/add_traits_to_table.js"); } }); return $('a.collection_name').attr('onClick', 'return false'); }; })(this)); }; + openCovariateSelection = function() { return $('#collections_holder').load('/collections/list #collections_list', (function(_this) { return function() { From 8995c0c9891cea962eadbdd868ac9705862a479f Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 17 Nov 2022 19:09:13 +0000 Subject: [PATCH 4/5] Only empty table container on loading if data is loaded from JSON Otherwise the row data is effectively deleted. This was initially done because it appears to be the only way to add columns to an existing data table that uses Scroller. --- wqflask/wqflask/static/new/javascript/create_datatable.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/create_datatable.js b/wqflask/wqflask/static/new/javascript/create_datatable.js index ddf72e9c9..9b2ba79bf 100644 --- a/wqflask/wqflask/static/new/javascript/create_datatable.js +++ b/wqflask/wqflask/static/new/javascript/create_datatable.js @@ -68,9 +68,11 @@ create_table = function(tableId="trait_table", tableData = [], columnDefs = [], } } else { theTable = $('#' + tableId).DataTable(tableSettings); - theTable.destroy(); - $('#' + tableId).empty(); - theTable = $('#' + tableId).DataTable(tableSettings); + if (tableData.length > 0) { + theTable.destroy(); + $('#' + tableId).empty(); + theTable = $('#' + tableId).DataTable(tableSettings); + } theTable.draw(); $('#' + tableId + '_container').css("width", String($('#' + tableId).width() + 17) + "px"); } From 1db28340ee703051645365a50eb10c9c25b5aebb Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 17 Nov 2022 21:54:22 +0000 Subject: [PATCH 5/5] Make Transform/Filter options work with added trait columns This also changes the way columns are added; previously a list of new columns was passed to the table generation code, but now I just add them to the attribute data in js_data The main remaining feature to add is the ability to remove columns (which I'll probably do as just the ability to remove all columns; I'm not sure how to reasonable add the option to remove specific columns) --- .../new/javascript/add_traits_to_table.js | 591 +++++++++--------- .../initialize_show_trait_tables.js | 44 +- 2 files changed, 324 insertions(+), 311 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/add_traits_to_table.js b/wqflask/wqflask/static/new/javascript/add_traits_to_table.js index 74e175fb6..acb206bfc 100644 --- a/wqflask/wqflask/static/new/javascript/add_traits_to_table.js +++ b/wqflask/wqflask/static/new/javascript/add_traits_to_table.js @@ -1,291 +1,300 @@ -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, 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; }; - -this_trait_data = null; - -var selected_traits = {}; - -$('#collections_list').attr("style", "width: 100%;"); -$('#trait_table').dataTable( { - "drawCallback": function( settings ) { - $('#trait_table tr').click(function(event) { - if (event.target.type !== 'checkbox') { - $(':checkbox', this).trigger('click'); - } - }); - }, - "columns": [ - { "type": "natural", "width": "3%" }, - { "type": "natural", "width": "8%" }, - { "type": "natural", "width": "20%" }, - { "type": "natural", "width": "25%" }, - { "type": "natural", "width": "25%" }, - { "type": "natural", "width": "15%" } - ], - "columnDefs": [ { - "targets": 0, - "orderable": false - } ], - "order": [[1, "asc" ]], - "sDom": "RZtr", - "iDisplayLength": -1, - "autoWidth": true, - "bDeferRender": true, - "bSortClasses": false, - "paging": false, - "orderClasses": true -} ); - -if ( ! $.fn.DataTable.isDataTable( '#collection_table' ) ) { - $('#collection_table').dataTable( { - "createdRow": function ( row, data, index ) { - if ($('td', row).eq(2).text().length > 40) { - $('td', row).eq(2).text($('td', row).eq(2).text().substring(0, 40)); - $('td', row).eq(2).text($('td', row).eq(2).text() + '...') - } - if ($('td', row).eq(4).text().length > 50) { - $('td', row).eq(4).text($('td', row).eq(4).text().substring(0, 50)); - $('td', row).eq(4).text($('td', row).eq(4).text() + '...') - } - }, - "columnDefs": [ { - "targets": 0, - "orderable": false - } ], - "order": [[1, "asc" ]], - "sDom": "ZRtr", - "iDisplayLength": -1, - "autoWidth": true, - "bSortClasses": false, - "paging": false, - "orderClasses": true - } ); -} - -collection_click = function() { - var this_collection_url; - this_collection_url = $(this).find('.collection_name').prop("href"); - this_collection_url += "&json"; - collection_list = $("#collections_holder").html(); - return $.ajax({ - dataType: "json", - url: this_collection_url, - success: process_traits - }); -}; - -submit_click = function() { - var sample, samples, scatter_matrix, this_trait_vals, trait, trait_names, trait_vals_csv, traits, _i, _j, _len, _len1, _ref; - new_data = js_data['sample_lists']; - traits = []; - $('#collections_holder').find('input[type=checkbox]:checked').each(function() { - var this_dataset, this_trait, this_trait_url; - this_trait = $(this).parents('tr').find('.trait').text(); - this_dataset = $(this).parents('tr').find('.dataset').data("dataset"); - this_trait_url = "/trait/get_sample_data?trait=" + this_trait + "&dataset=" + this_dataset; - return $.ajax({ - dataType: "json", - url: this_trait_url, - async: false, - success: add_trait_data - }); - }); - - tableIds = ["samples_primary"] - console.log("WIDTH:", $('#primary_container').width() ) - $('#primary_container').width($('#primary_container').width() + 40*Object.keys(selected_traits).length) - if (js_data.sample_lists.length > 1) { - tableIds.push("samples_other") - $('#other_container').width($('#primary_container').width() + 40*selected_traits.length) - } - - for (var i = 0; i < tableIds.length; i++) { - for (var j = 0; j < js_data['sample_lists'][i].length; j++){ - for (const [key, value] of Object.entries(selected_traits)) { - if (js_data['sample_lists'][i][j].name in selected_traits[key]){ - new_data[i][j][key] = selected_traits[key][js_data['sample_lists'][i][j].name] - } else { - new_data[i][j][key] = "x" - } - } - } - } - - var other_columns = [] - i = 0; - for (const [key, _value] of Object.entries(selected_traits)) { - new_ob = { - 'title': key, - 'type': "natural", - 'searchable' : true, - 'width': "40px", - 'targets': $('#samples_primary thead th').length + i, - 'data': key - } - console.log("NEW OB:", new_ob) - other_columns.push(new_ob); - i++; - } - someotherthing = other_columns - console.log(someotherthing) - - console.log("CHECKING:", other_columns) - - initialize_show_trait_tables(new_data=new_data, new_columns=other_columns) - - return $.colorbox.close(); -}; - -trait_click = function() { - var dataset, this_trait_url, trait; - - trait = $(this).parent().find('.trait').text(); - dataset = $(this).parent().find('.dataset').text(); - this_trait_url = "/trait/get_sample_data?trait=" + trait + "&dataset=" + dataset; - $.ajax({ - dataType: "json", - url: this_trait_url, - success: get_trait_data - }); - return $.colorbox.close(); -}; - -trait_row_click = function() { - var dataset, this_trait_url, trait; - trait = $(this).find('.trait').text(); - dataset = $(this).find('.dataset').data("dataset"); - this_trait_url = "/trait/get_sample_data?trait=" + trait + "&dataset=" + dataset; - $.ajax({ - dataType: "json", - url: this_trait_url, - success: get_trait_data - }); - return $.colorbox.close(); -}; - -add_trait_data = function(trait_data) { - selected_traits[trait_data[0].name] = trait_data[1]; - return false; -}; - -get_trait_data = function(trait_data) { - var sample, samples, this_trait_vals, trait_sample_data, vals, _i, _len; - trait_sample_data = trait_data[1]; - if ( $('input[name=allsamples]').length ) { - samples = $('input[name=allsamples]').val().split(" "); - } else { - samples = js_data.indIDs - } - sample_vals = []; - vals = []; - for (_i = 0, _len = samples.length; _i < _len; _i++) { - sample = samples[_i]; - if (sample in trait_sample_data) { - sample_vals.push(sample + ":" + parseFloat(trait_sample_data[sample])) - vals.push(parseFloat(trait_sample_data[sample])) - } else { - sample_vals.push(null) - vals.push(null) - } - } - if ( $('input[name=allsamples]').length ) { - if ($('input[name=samples]').length < 1) { - $('#hidden_inputs').append(''); - } - $('#hidden_inputs').append(''); - this_trait_vals = get_this_trait_vals(samples); - return color_by_trait(trait_sample_data); - } else{ - sorted = vals.slice().sort(function(a,b){return a-b}) - ranks = vals.slice().map(function(v){ return sorted.indexOf(v)+1 }); - sample_ranks = [] - for (_i = 0; _i < samples.length; _i++){ - if (samples[_i] in trait_sample_data){ - sample_ranks.push(samples[_i] + ":" + ranks[_i]) - } else { - sample_ranks.push(null) - } - } - return false - } -}; - -get_this_trait_vals = function(samples) { - var sample, this_trait_vals, this_val, this_vals_json, _i, _len; - this_trait_vals = []; - for (_i = 0, _len = samples.length; _i < _len; _i++) { - sample = samples[_i]; - this_val = parseFloat($("input[name='value:" + sample + "']").val()); - if (!isNaN(this_val)) { - this_trait_vals.push(this_val); - } else { - this_trait_vals.push(null); - } - } - this_vals_json = '[' + this_trait_vals.toString() + ']'; - return this_trait_vals; -}; - -assemble_into_json = function(this_trait_vals) { - var json_data, json_ids, num_traits, samples; - num_traits = $('input[name=vals]').length; - samples = $('input[name=samples]').val(); - json_ids = samples; - json_data = '[' + this_trait_vals; - $('input[name=vals]').each((function(_this) { - return function(index, element) { - return json_data += ',' + $(element).val(); - }; - })(this)); - json_data += ']'; - return [json_ids, json_data]; -}; - -color_by_trait = function(trait_sample_data, textStatus, jqXHR) { - return root.bar_chart.color_by_trait(trait_sample_data); -}; - -process_traits = function(trait_data, textStatus, jqXHR) { - var the_html, trait, _i, _len; - the_html = ""; - the_html += " "; - the_html += ""; - the_html += ""; - the_html += ""; - for (_i = 0, _len = trait_data.length; _i < _len; _i++) { - trait = trait_data[_i]; - the_html += ""; - the_html += ""; - if ("abbreviation" in trait) { - the_html += ""; - } else if ("symbol" in trait) { - the_html += ""; - } else { - the_html += ""; - } - the_html += ""; - the_html += ""; - } - the_html += ""; - the_html += "
RecordData SetDescription
" + trait.name + "" + trait.name + "" + trait.name + "" + trait.dataset_name + "" + trait.description + "
"; - the_html += "" - $("#collections_holder").html(the_html); - return $('#collections_holder').colorbox.resize(); -}; - -back_to_collections = function() { - $("#collections_holder").html(collection_list); - $(document).on("click", ".collection_line", collection_click); - return $('#collections_holder').colorbox.resize(); -}; - -$(".collection_line").on("click", collection_click); -$("#submit_cofactors").on("click", submit_click); - -if ($('#scatterplot2').length){ - $(".trait_line").on("click", trait_row_click); -} else { - $(".trait").on("click", trait_click); -} -$("#back_to_collections").on("click", back_to_collections); +var add_trait_data, assemble_into_json, back_to_collections, collection_click, collection_list, create_trait_data_csv, get_this_trait_vals, get_trait_data, process_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; }; + +this_trait_data = null; + +var selected_traits = {}; + +var attributeStartPos = 3; +if (js_data.se_exists) { + attributeStartPos += 2; +} +if (js_data.has_num_cases === true) { + attributeStartPos += 1; +} + +$('#collections_list').attr("style", "width: 100%;"); +$('#trait_table').dataTable( { + "drawCallback": function( settings ) { + $('#trait_table tr').click(function(event) { + if (event.target.type !== 'checkbox') { + $(':checkbox', this).trigger('click'); + } + }); + }, + "columns": [ + { "type": "natural", "width": "3%" }, + { "type": "natural", "width": "8%" }, + { "type": "natural", "width": "20%" }, + { "type": "natural", "width": "25%" }, + { "type": "natural", "width": "25%" }, + { "type": "natural", "width": "15%" } + ], + "columnDefs": [ { + "targets": 0, + "orderable": false + } ], + "order": [[1, "asc" ]], + "sDom": "RZtr", + "iDisplayLength": -1, + "autoWidth": true, + "bDeferRender": true, + "bSortClasses": false, + "paging": false, + "orderClasses": true +} ); + +if ( ! $.fn.DataTable.isDataTable( '#collection_table' ) ) { + $('#collection_table').dataTable( { + "createdRow": function ( row, data, index ) { + if ($('td', row).eq(2).text().length > 40) { + $('td', row).eq(2).text($('td', row).eq(2).text().substring(0, 40)); + $('td', row).eq(2).text($('td', row).eq(2).text() + '...') + } + if ($('td', row).eq(4).text().length > 50) { + $('td', row).eq(4).text($('td', row).eq(4).text().substring(0, 50)); + $('td', row).eq(4).text($('td', row).eq(4).text() + '...') + } + }, + "columnDefs": [ { + "targets": 0, + "orderable": false + } ], + "order": [[1, "asc" ]], + "sDom": "ZRtr", + "iDisplayLength": -1, + "autoWidth": true, + "bSortClasses": false, + "paging": false, + "orderClasses": true + } ); +} + +collection_click = function() { + var this_collection_url; + this_collection_url = $(this).find('.collection_name').prop("href"); + this_collection_url += "&json"; + collection_list = $("#collections_holder").html(); + return $.ajax({ + dataType: "json", + url: this_collection_url, + success: process_traits + }); +}; + +submit_click = function() { + var sample, samples, scatter_matrix, this_trait_vals, trait, trait_names, trait_vals_csv, traits, _i, _j, _len, _len1, _ref; + new_data = js_data['sample_lists']; + traits = []; + $('#collections_holder').find('input[type=checkbox]:checked').each(function() { + var this_dataset, this_trait, this_trait_url; + this_trait = $(this).parents('tr').find('.trait').text(); + this_dataset = $(this).parents('tr').find('.dataset').data("dataset"); + this_trait_url = "/trait/get_sample_data?trait=" + this_trait + "&dataset=" + this_dataset; + return $.ajax({ + dataType: "json", + url: this_trait_url, + async: false, + success: add_trait_data + }); + }); + + tableIds = ["samples_primary"] + $('#primary_container').width($('#primary_container').width() + 40*Object.keys(selected_traits).length) + if (js_data.sample_lists.length > 1) { + tableIds.push("samples_other") + $('#other_container').width($('#primary_container').width() + 40*selected_traits.length) + } + + for (var i = 0; i < tableIds.length; i++) { + for (var j = 0; j < js_data['sample_lists'][i].length; j++){ + for (const [key, value] of Object.entries(selected_traits)) { + if (js_data['sample_lists'][i][j].name in selected_traits[key]){ + new_data[i][j][key] = selected_traits[key][js_data['sample_lists'][i][j].name] + } else { + new_data[i][j][key] = "x" + } + } + } + } + + attrIds = Object.keys(js_data.attributes).sort((a, b) => (parseInt(js_data.attributes[a].id) > parseInt(js_data.attributes[b].id)) ? 1 : -1) + maxAttrId = attrIds[attrIds.length - 1] + + i = 0; + for (const [key, _value] of Object.entries(selected_traits)) { + let distinctVals = [...new Set(Object.keys(selected_traits[key]).map((key2) => selected_traits[key][key2]))]; + + js_data.attributes[parseInt(maxAttrId) + i] = { + id: (parseInt(maxAttrId) + i).toString(), + name: key, + description: key, + distinct_values: distinctVals, + alignment: 'right', + data: key + } + + $('#filter_column').append(new Option(key, attributeStartPos + i)) + + if (distinctVals.length <= 10 && distinctVals.length > 1) { + $('#exclude_column').append(new Option(key, attrIds.length + i)) + } + + i++; + } + + initialize_show_trait_tables(new_data=new_data) + populateSampleAttributesValuesDropdown(); + + return $.colorbox.close(); +}; + +trait_click = function() { + var dataset, this_trait_url, trait; + + trait = $(this).parent().find('.trait').text(); + dataset = $(this).parent().find('.dataset').text(); + this_trait_url = "/trait/get_sample_data?trait=" + trait + "&dataset=" + dataset; + $.ajax({ + dataType: "json", + url: this_trait_url, + success: get_trait_data + }); + return $.colorbox.close(); +}; + +trait_row_click = function() { + var dataset, this_trait_url, trait; + trait = $(this).find('.trait').text(); + dataset = $(this).find('.dataset').data("dataset"); + this_trait_url = "/trait/get_sample_data?trait=" + trait + "&dataset=" + dataset; + $.ajax({ + dataType: "json", + url: this_trait_url, + success: get_trait_data + }); + return $.colorbox.close(); +}; + +add_trait_data = function(trait_data) { + selected_traits[trait_data[0].name] = trait_data[1]; + return false; +}; + +get_trait_data = function(trait_data) { + var sample, samples, this_trait_vals, trait_sample_data, vals, _i, _len; + trait_sample_data = trait_data[1]; + if ( $('input[name=allsamples]').length ) { + samples = $('input[name=allsamples]').val().split(" "); + } else { + samples = js_data.indIDs + } + sample_vals = []; + vals = []; + for (_i = 0, _len = samples.length; _i < _len; _i++) { + sample = samples[_i]; + if (sample in trait_sample_data) { + sample_vals.push(sample + ":" + parseFloat(trait_sample_data[sample])) + vals.push(parseFloat(trait_sample_data[sample])) + } else { + sample_vals.push(null) + vals.push(null) + } + } + if ( $('input[name=allsamples]').length ) { + if ($('input[name=samples]').length < 1) { + $('#hidden_inputs').append(''); + } + $('#hidden_inputs').append(''); + this_trait_vals = get_this_trait_vals(samples); + return color_by_trait(trait_sample_data); + } else{ + sorted = vals.slice().sort(function(a,b){return a-b}) + ranks = vals.slice().map(function(v){ return sorted.indexOf(v)+1 }); + sample_ranks = [] + for (_i = 0; _i < samples.length; _i++){ + if (samples[_i] in trait_sample_data){ + sample_ranks.push(samples[_i] + ":" + ranks[_i]) + } else { + sample_ranks.push(null) + } + } + return false + } +}; + +get_this_trait_vals = function(samples) { + var sample, this_trait_vals, this_val, this_vals_json, _i, _len; + this_trait_vals = []; + for (_i = 0, _len = samples.length; _i < _len; _i++) { + sample = samples[_i]; + this_val = parseFloat($("input[name='value:" + sample + "']").val()); + if (!isNaN(this_val)) { + this_trait_vals.push(this_val); + } else { + this_trait_vals.push(null); + } + } + this_vals_json = '[' + this_trait_vals.toString() + ']'; + return this_trait_vals; +}; + +assemble_into_json = function(this_trait_vals) { + var json_data, json_ids, num_traits, samples; + num_traits = $('input[name=vals]').length; + samples = $('input[name=samples]').val(); + json_ids = samples; + json_data = '[' + this_trait_vals; + $('input[name=vals]').each((function(_this) { + return function(index, element) { + return json_data += ',' + $(element).val(); + }; + })(this)); + json_data += ']'; + return [json_ids, json_data]; +}; + +process_traits = function(trait_data, textStatus, jqXHR) { + var the_html, trait, _i, _len; + the_html = ""; + the_html += " "; + the_html += ""; + the_html += ""; + the_html += ""; + for (_i = 0, _len = trait_data.length; _i < _len; _i++) { + trait = trait_data[_i]; + the_html += ""; + the_html += ""; + if ("abbreviation" in trait) { + the_html += ""; + } else if ("symbol" in trait) { + the_html += ""; + } else { + the_html += ""; + } + the_html += ""; + the_html += ""; + } + the_html += ""; + the_html += "
RecordData SetDescription
" + trait.name + "" + trait.name + "" + trait.name + "" + trait.dataset_name + "" + trait.description + "
"; + the_html += "" + $("#collections_holder").html(the_html); + return $('#collections_holder').colorbox.resize(); +}; + +back_to_collections = function() { + $("#collections_holder").html(collection_list); + $(document).on("click", ".collection_line", collection_click); + return $('#collections_holder').colorbox.resize(); +}; + +$(".collection_line").on("click", collection_click); +$("#submit_cofactors").on("click", submit_click); + +if ($('#scatterplot2').length){ + $(".trait_line").on("click", trait_row_click); +} else { + $(".trait").on("click", trait_click); +} +$("#back_to_collections").on("click", back_to_collections); diff --git a/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js b/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js index 4942a46e5..76696fb2f 100644 --- a/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js +++ b/wqflask/wqflask/static/new/javascript/initialize_show_trait_tables.js @@ -1,5 +1,5 @@ // This file initializes the tables for the show_trait page -initialize_show_trait_tables = function(new_data = [], new_columns = []) { +initialize_show_trait_tables = function(new_data = []) { // This variable is just created to get the column position of the first case attribute (if case attributes exist), since it's needed to set the row classes in createdRow for the DataTable var attributeStartPos = 3; if (js_data.se_exists) { @@ -9,7 +9,7 @@ initialize_show_trait_tables = function(new_data = [], new_columns = []) { attributeStartPos += 1; } - buildColumns = function(new_columns = []) { + buildColumns = function() { let columnList = [ { 'data': null, @@ -134,32 +134,36 @@ initialize_show_trait_tables = function(new_data = [], new_columns = []) { attrKeys = Object.keys(js_data.attributes).sort((a, b) => (js_data.attributes[a].id > js_data.attributes[b].id) ? 1 : -1) for (i = 0; i < attrKeys.length; i++){ - columnList.push( - { - 'title': "
" + js_data.attributes[attrKeys[i]].name + "
", - 'type': "natural", - 'data': null, - 'targets': attrStart + i, - 'render': function(data, type, row, meta) { - attr_name = Object.keys(data.extra_attributes).sort((a, b) => (parseInt(a) > parseInt(b)) ? 1 : -1)[meta.col - data.first_attr_col] + thisCol = { + 'data': null, + 'title': "
" + js_data.attributes[attrKeys[i]].name + "
", + 'type': "natural", + 'targets': attrStart + i, + } + if ('data' in js_data.attributes[attrKeys[i]]) { + thisCol['data'] = js_data.attributes[attrKeys[i]].data + } else { + thisCol['render'] = function(data, type, row, meta) { + attr_name = Object.keys(data.extra_attributes).sort((a, b) => (parseInt(a) > parseInt(b)) ? 1 : -1)[meta.col - data.first_attr_col] - if (attr_name != null && attr_name != undefined){ - if (Array.isArray(data.extra_attributes[attr_name])){ - return '' + data.extra_attributes[attr_name][0] + '' - } else { - return data.extra_attributes[attr_name] - } + if (attr_name != null && attr_name != undefined){ + if (Array.isArray(data.extra_attributes[attr_name])){ + return '' + data.extra_attributes[attr_name][0] + '' } else { - return "" + return data.extra_attributes[attr_name] } + } else { + return "" } } - ) + } + + columnList.push(thisCol) } - return columnList.concat(new_columns) + return columnList } - columnDefs = buildColumns(new_columns); + columnDefs = buildColumns(); tableIds = ["samples_primary"] if (js_data.sample_lists.length > 1) {