1 jQuery(document).ready(function() {
4 .on('show.bs.collapse', '.panel-collapse', function() {
5 var $span = jQuery(this).parents('.panel').find('.panel-heading span.clickable');
6 $span.find('i').removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
8 .on('hide.bs.collapse', '.panel-collapse', function() {
9 var $span = jQuery(this).parents('.panel').find('.panel-heading span.clickable');
10 $span.find('i').removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
13 jQuery('#pagetitle_h3').append(' <a id="selection_index_more_info" href="#"><span class="glyphicon glyphicon-info-sign"></span></a>');
15 jQuery('#select_trial_for_selection_index').change( // update selection index options when trial selection changes
18 jQuery('#selection_index').html("");
19 jQuery('#trait_table').html("");
20 jQuery('#weighted_values_div').html("");
21 jQuery('#raw_avgs_div').html("");
22 jQuery('#sin_formula_list_select').val("");
25 if (jQuery(this).val() == '') {
29 jQuery('#trait_table_label').html('Traits and coefficients for <a href="/breeders_toolbox/trial/'+jQuery(this).val()+'">'+jQuery('option:selected', this).text()+'</a>:')
31 var trial_id = jQuery(this).val();
32 var trial_name = jQuery("option:selected", this).text();
38 jQuery.ajax({ // get traits phenotyped in trial
39 url: '/ajax/breeder/search',
42 'categories': ['trials', 'traits'],
46 beforeSend: function() {
49 complete: function() {
52 success: function(response) {
53 var list = response.list || 0;
55 trait_html = '<option id="select_message" value="" title="No trait measurements found.">No trait measurements found for '+trial_name+'.</option>\n';
56 jQuery('#trait_list').html(trait_html);
60 for (i = 0; i < list.length; i++) {
61 trait_ids.push(list[i][0]);
65 jQuery.ajax({ // get trait synonyms
66 url: '/ajax/cvterm/get_synonyms',
70 'trait_ids': trait_ids
72 success: function(response) {
73 synonyms = response.synonyms;
74 //console.log("synonyms = " + JSON.stringify(synonyms));
75 trait_html = '<option id="select_message" value="" title="Select a trait">Select a trait</option>\n';
76 for (i = 0; i < list.length; i++) {
77 var trait_id = list[i][0];
78 var trait_name = list[i][1];
79 var parts = trait_name.split("|");
80 var synonym = synonyms[trait_id];
81 // synonym_fixed = synonym.replace(/"/g, "");
82 // var syn_parts = synonym_fixed.split(" ");
83 // synonym_fixed = syn_parts[0];
84 trait_html += '<option value="' + trait_id + '" data-synonym="' + synonym + '" data-list_name="' + trait_name + '" title="' + parts[0] + '">' + parts[0] + '</a>\n';
87 jQuery('#trait_list').html(trait_html);
90 error: function(response) {
91 alert("An error occurred while retrieving synonyms for traits with ids " + trait_ids);
95 error: function(response) {
96 alert("An error occurred while transforming the list " + list_id);
100 jQuery.ajax({ // get plots phenotyped in trial
101 url: '/ajax/breeder/search',
104 'categories': ['trials', 'plots'],
108 success: function(response) {
109 var plots = response.list || [];
110 //console.log("plots: " + JSON.stringify(plots));
111 var plot_ids = plots.map(function(val) {
114 //console.log("plot ids: " + JSON.stringify(plot_ids));
116 url: '/ajax/breeders/trial/' + data + '/controls_by_plot',
120 success: function(response) {
121 //console.log('controls:' + JSON.stringify(response));
122 var accessions = response.accessions;
124 if (response.accessions[0].length == 0) {
125 accession_html = '<option value="" title="Select a control">No controls found</a>\n';
127 accession_html = '<option value="" title="Select a control">Select a control</a>\n';
128 for (i = 0; i < response.accessions[0].length; i++) {
129 accession_html += '<option value="' + accessions[0][i].stock_id + '" title="' + response.accessions[0][i].stock_id + '">' + response.accessions[0][i].accession_name + '</a>\n';
132 jQuery('#control_list').html(accession_html);
133 jQuery('#trait_list').focus();
135 error: function(response) {
136 jQuery('#control_list').html('<option value="" title="Select a control">Error retrieving trial controls</a>');
140 error: function(response) {
141 jQuery('#control_list').html('<option value="" title="Select a control">Error retrieving trial design</a>');
146 jQuery('#trait_list').change( // add selected trait to trait table
148 var trait_id = jQuery('option:selected', this).val();
149 var coefficient_id = trait_id + '_coefficient';
150 var control_id = trait_id + '_control';
151 var trait_name = jQuery('option:selected', this).text();
152 var trait_synonym = jQuery('option:selected', this).data("synonym");
153 var list_name = jQuery('option:selected', this).data("list_name");
154 var control_html = jQuery('#control_list').html();
155 var trait_html = "<tr id='" + trait_id + "_row'><td><a href='/cvterm/" + trait_id + "/view' data-list_name='" + list_name + "' data-value='" + trait_id + "'>" + trait_name + "</a></td><td><p id='" + trait_id + "_synonym'>" + trait_synonym + "<p></td><td><input type='text' id='" + coefficient_id + "' class='form-control' placeholder='Default is 1'></input></td><td><select class='form-control' id='" + control_id + "'>" + control_html + "</select></td><td align='center'><a title='Remove' id='" + trait_id + "_remove' href='javascript:remove_trait(" + trait_id + ")'><span class='glyphicon glyphicon-remove'></span></a></td></tr>";
156 jQuery('#trait_table').append(trait_html);
157 jQuery('#select_message').text('Add another trait');
158 jQuery('#select_message').attr('selected', true);
160 jQuery('#' + coefficient_id).focus();
161 jQuery('#' + coefficient_id).change( //
163 if (isNaN(jQuery(this).val())) {
164 jQuery(this).val('');
165 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> Error.<br> Index coefficients must be a positive or negative number.</li></center>";
166 jQuery('#selection_index_error_dialog').modal("show");
170 jQuery('#trait_list').focus();
173 jQuery('#' + control_id).change(
177 //jQuery('#calculate_rankings').removeClass('disabled');
180 jQuery('#save_sin').click(function() {
181 if (jQuery('#trait_table').children().length < 1) {
182 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> Formula not saved.<br> At least one trait must be selected before saving a SIN formula.</li></center>";
183 jQuery('#selection_index_error_dialog').modal("show");
186 var lo = new CXGN.List();
187 var new_name = jQuery('#save_sin_name').val();
188 console.log("Saving SIN formula to list named " + new_name);
189 var selected_trait_rows = jQuery('#trait_table').children();
194 jQuery(selected_trait_rows).each(function(index, selected_trait_rows) {
195 var trait_id = jQuery('a', this).data("value");
196 traits.push(jQuery('a', this).data("list_name"));
197 coefficients.push(jQuery('#' + trait_id + '_coefficient').val() || 1); // default = 1
199 if (jQuery('#' + trait_id + '_control option:selected').val()) {
200 control_name = jQuery('#' + trait_id + '_control option:selected').text();
205 controls.push(control_name.trim());
208 var data = "traits:" + traits.join();
209 data += "\nnumbers:" + coefficients.join();
210 data += "\naccessions:" + controls.join();
211 console.log("Saving SIN formula to dataset: " + JSON.stringify(data));
213 list_id = lo.newList(new_name);
215 var elementsAdded = lo.addToList(list_id, data);
216 lo.setListType(list_id, 'dataset');
219 alert("Saved SIN formula with name " + new_name);
224 jQuery('#selection_index_more_info').click(function() {
225 jQuery('#selection_index_info_dialog').modal("show");
228 jQuery('#selection_index_error_close_button').click(function() {
229 document.getElementById('selection_index_error_message').innerHTML = "";
233 jQuery('#calculate_rankings').click(function() {
235 if (jQuery('#trait_table').children().length < 1) {
236 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> Error.<br> A trial and at least one trait must be selected before calculating rankings.</li></center>";
237 jQuery('#selection_index_error_dialog').modal("show");
240 jQuery('#raw_avgs_div').html("");
241 jQuery('#weighted_values_div').html("");
242 var trial_id = jQuery("#select_trial_for_selection_index option:selected").val();
243 var selected_trait_rows = jQuery('#trait_table').children();
246 weighted_column_names = [],
250 var trial_name = jQuery('#select_trial_for_selection_index option:selected').text();
254 weighted_column_names.push({
257 jQuery(selected_trait_rows).each(function(index, selected_trait_rows) {
258 var trait_id = jQuery('a', this).data("value");
259 trait_ids.push(trait_id);
260 var trait = jQuery('#' + trait_id + '_synonym').text()
261 if (trait == 'None') {
262 trait = jQuery('a', this).text();
264 var trait_term = trait; //= "mean "+trait;
265 var coefficient = jQuery('#' + trait_id + '_coefficient').val() || 1; // default = 1
268 coefficients.push(coefficient);
269 var control = jQuery('#' + trait_id + '_control option:selected').val() || '';
270 controls.push(control);
272 trait_term += " as a fraction of " + jQuery('#' + trait_id + '_control option:selected').text();
277 weighted_column_names.push({
278 title: coefficient + " * (" + trait_term + ")"
281 weighted_column_names.push({
286 var allow_missing = jQuery("#allow_missing").is(':checked');
288 console.log("trait_ids:" + trait_ids + "\ncoefficients:" + coefficients + "\ncontrols:" + controls);
290 jQuery.ajax({ // get raw averaged and weighted phenotypes from trial
291 url: '/ajax/breeder/search/avg_phenotypes',
294 'trial_id': trial_id,
295 'trait_ids': trait_ids,
296 'coefficients': coefficients,
297 'controls': controls,
298 'allow_missing': allow_missing
300 success: function(response) {
301 if (response.error) {
302 alert(response.error);
304 var raw_avgs = response.raw_avg_values || [];
305 var weighted_values = response.weighted_values || [];
306 var trial_name = jQuery('#select_trial_for_selection_index option:selected').text();
307 build_table(raw_avgs, column_names, trial_name, 'raw_avgs_div');
308 build_table(weighted_values, weighted_column_names, trial_name, 'weighted_values_div');
310 error: function(response) {
311 alert("An error occurred while retrieving average phenotypes");
318 function build_table(data, column_names, trial_name, target_div) {
320 var table_id = target_div.replace("div", "table");
321 var table_type = target_div.replace("_div", "");
322 var table_html = '<br><br><div class="table-responsive" style="margin-top: 10px;"><table id="' + table_id + '" class="table table-hover table-striped table-bordered" width="100%"><caption class="well well-sm" style="caption-side: bottom;margin-top: 10px;"><center> Table description: <i>' + table_type + ' for trial ' + trial_name + '.</i></center></caption></table></div>'
323 if (table_type == 'weighted_values') {
324 table_html += '<div class="col-sm-12 col-md-12 col-lg-12"><hr><label>Save top ranked accessions to a list: </label><br><br><div class="col-sm-3 col-md-3 col-lg-3" style="display: inline-block"><label>By number:</label> <select class="form-control" id="top_number"></select></div><div class="col-sm-3 col-md-3 col-lg-3" style="display: inline-block"><label>Or percent:</label> <select class="form-control" id="top_percent"></select></div><div class="col-sm-6 col-md-6 col-lg-6"><div style="text-align:right" id="ranking_to_list_menu"></div><div id="top_ranked_names" style="display: none;"></div></div><br><br><br><br><br></div>';
327 jQuery('#' + target_div).html(table_html);
329 var export_message = 'Accession rankings calculated using a selection index at ' + window.location.href;
330 var penultimate_column = column_names.length - 2;
332 jQuery('#' + table_id).DataTable({
336 extend: 'excelHtml5',
337 title: trial_name +'_rankings'
341 title: trial_name +'_rankings'
345 title: trial_name +'_rankings',
346 message: export_message
350 message: export_message
363 columns: column_names,
365 [penultimate_column, "desc"]
369 if (table_type == 'weighted_values') {
371 var table = $('#weighted_values_table').DataTable();
372 var name_links = table.column(0).data();
373 jQuery("#top_number").append('<option value="">Select a number</option>');
374 jQuery("#top_percent").append('<option value="">Select a percent</option>');
376 for (i = 1; i <= name_links.length; i++) {
377 jQuery("#top_number").append('<option value=' + i + '>' + i + '</option>');
379 for (i = 1; i <= 100; i++) {
380 jQuery("#top_percent").append('<option value=' + i + '>' + i + '%</option>');
383 jQuery('select[id^="top_"]').change( // save top # or % of accession to add to lists
385 var type = this.id.split("_").pop();
386 var number = jQuery('#top_' + type).val();
389 if (type == 'number') {
390 jQuery("#top_percent").val(''); // reset other select
391 for (var i = 0; i < number; i++) { //extract names from anchor tags
392 names.push(name_links[i].match(/<a [^>]+>([^<]+)<\/a>/)[1] + '\n');
394 //console.log("retrieved top "+number+" names: "+names);
395 } else if (type == 'percent') {
396 jQuery("#top_number").val(''); // reset other select
397 var adjusted_number = Math.round((number / 100) * name_links.length);
398 for (var i = 0; i < adjusted_number; i++) { //extract names from anchor tags
399 names.push(name_links[i].match(/<a [^>]+>([^<]+)<\/a>/)[1] + '\n');
401 //console.log("retrieved top "+number+" percent of names: "+names);
403 jQuery('#top_ranked_names').html(names);
404 addToListMenu('ranking_to_list_menu', 'top_ranked_names', {
405 listType: 'accessions',
412 function load_sin() { // update traits and selection index when a saved sin formula is picked
414 if (!jQuery("#select_trial_for_selection_index option:selected").val()) {
415 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> Error.<br> A trial must be selected before loading a SIN formula.</li></center>";
416 jQuery('#selection_index_error_dialog').modal("show");
420 //retrieve contents of list:
421 var sin_list_id = jQuery('#sin_formula_list_select').val();
426 var lo = new CXGN.List();
427 var list_data = lo.getListData(sin_list_id);
428 var sin_data = list_data.elements;
431 var coefficients = [];
434 for (i = 0; i < sin_data.length; i++) {
435 var array = sin_data[i];
437 var string = array.shift();
438 var parts = string.split(/:(.+)/);
439 var values = parts[1];
442 traits = values.split(",");
445 coefficients = values.split(",");
448 controls = values.split(",");
453 var ids = lo.transform(sin_list_id, 'dataset_2_dataset_ids');
455 var control_ids = [];
457 for (i = 0; i < ids.length; i++) {
459 var parts = data.split(/:(.+)/);
460 var values = parts[1];
463 trait_ids = values.split(",");
465 case 'accession_ids':
466 control_ids = values.split(",");
471 jQuery('#selection_index').html("");
472 jQuery('#trait_table').html("");
473 var omitted_traits = [];
474 var omitted_controls = [];
476 //add traits, coefficients, and controls to table
477 for (i = 0; i < trait_ids.length; i++) {
478 var trait_id = trait_ids[i];
479 var control_id = control_ids[i];
480 //console.log("building trait table with trait:" + trait_id + traits[i] + " and coefficient:" + coefficients[i] + " and control:" + control_id + controls[i]);
481 var coefficient_input_id = trait_id + '_coefficient';
482 var control_select_id = trait_id + '_control';
483 var trait_name = jQuery('#trait_list option[value=' + trait_id + ']').text();
485 omitted_traits.push("<a href='/cvterm/" + trait_id + "/view' data-value='" + trait_id + "'>" + traits[i] + "</a>");
488 var trait_synonym = jQuery('#trait_list option[value=' + trait_id + ']').data("synonym");
489 var control_html = jQuery('#control_list').html();
490 //console.log("control html"+control_html);
491 var trait_html = "<tr id='" + trait_id + "_row'><td><a href='/cvterm/" + trait_id + "/view' data-value='" + trait_id + "'>" + trait_name + "</a></td><td><p id='" + trait_id + "_synonym'>" + trait_synonym + "<p></td><td><input type='text' id='" + coefficient_input_id + "' class='form-control' placeholder='Default is 1'></input></td><td><select class='form-control' id='" + control_select_id + "'>" + control_html + "</select></td><td align='center'><a title='Remove' id='" + trait_id + "_remove' href='javascript:remove_trait(" + trait_id + ")'><span class='glyphicon glyphicon-remove'></span></a></td></tr>";
493 jQuery('#trait_table').append(trait_html);
494 jQuery('#' + coefficient_input_id).val(coefficients[i]);
495 if (jQuery('#' + control_select_id).find('option[value="' + control_id + '"]').length) {
496 jQuery('#' + control_select_id).val(control_id);
497 } else if (control_id) {
498 omitted_controls.push("<a href='/stock/" + control_id + "/view' data-value='" + control_id + "'>" + controls[i] + "</a>");
501 jQuery('#select_message').text('Add another trait');
502 jQuery('#select_message').attr('selected', true);
505 if (omitted_traits.length > 0 && omitted_controls.length > 0) {
506 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> The following parts of the saved SIN formula have been omitted because they were not found in this trial:</li></center><br><center><p>Traits: " + omitted_traits.join(", ") + "</p></center><br><center><p>Controls: " + omitted_controls.join(", ") + "</p></center>";
507 jQuery('#selection_index_error_dialog').modal("show");
509 else if (omitted_traits.length > 0) {
510 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> The following parts of the saved SIN formula have been omitted because they were not found in this trial:</li></center><br><center><p>Traits: " + omitted_traits.join(", ") + "</p></center>";
511 jQuery('#selection_index_error_dialog').modal("show");
513 else if (omitted_controls.length > 0) {
514 document.getElementById('selection_index_error_message').innerHTML = "<center><li class='list-group-item list-group-item-danger'> The following parts of the saved SIN formula have been omitted because they were not found in this trial:</li></center><br><center><p>Controls: " + omitted_controls.join(", ") + "</p></center>";
515 jQuery('#selection_index_error_dialog').modal("show");
520 function remove_trait(trait_id) {
521 jQuery('#' + trait_id + '_row').remove();
525 function update_formula() {
526 //console.log("updating formula....");
527 var selected_trait_rows = jQuery('#trait_table').children();
528 if (selected_trait_rows.length < 1) {
529 jQuery('#ranking_formula').html("<center><i>Select a trial, then pick traits and coefficients (or load a saved formula).</i></center>");
530 jQuery('#calculate_rankings').addClass('disabled');
531 jQuery('#save_sin').addClass('disabled');
534 var formula = "<center><b>SIN = </b></center>";
536 jQuery(selected_trait_rows).each(function(index, selected_trait_rows) {
537 var trait_id = jQuery('a', this).data("value");
538 var trait = jQuery('#' + trait_id + '_synonym').text()
539 if (trait == 'None') {
540 trait = jQuery('a', this).text();
542 var trait_term = trait; //= "mean "+trait;
543 var coefficient = jQuery('#' + trait_id + '_coefficient').val() || 1; // default = 1
544 if (jQuery('#' + trait_id + '_control option:selected').val()) { // if control selected for scaling
545 trait_term += " as a fraction of " + jQuery('#' + trait_id + '_control option:selected').text();
548 if (term_number == 0 || coefficient <= 0) {
549 formula += "<center>" + coefficient + " * ( " + trait_term + ")</center>";
551 formula += "<center>+ " + coefficient + " * ( " + trait_term + ")</center>";
555 jQuery('#ranking_formula').html(formula);
556 jQuery('#calculate_rankings').removeClass('disabled');
557 jQuery('#save_sin').removeClass('disabled');