if a variable is displayed, show trait properties section, otherwise do not.
[sgn.git] / mason / search / stocks.mas
blobbcf95d822b585fc554fd073538d8908df680b158
2 <%args>
4   $sp_person_autocomplete_uri => '/ajax/people/autocomplete'
5   $trait_autocomplete_uri     => '/ajax/stock/trait_autocomplete'
6   $onto_autocomplete_uri      => '/ajax/cvterm/autocomplete'
7   $trait_db_name              => 'SP'
9   $organisms       => undef
10   $stock_types     => undef
11   $breeding_programs => undef
12   $editable_stock_props => undef
13 </%args>
15 <& /util/import_javascript.mas, classes => [ 'jquery', 'jquery.dataTables', 'CXGN.Effects', 'CXGN.List' ] &>
17 <div class="container-fluid">
19 <& /page/page_title.mas, title=>'Search Accessions and Plots' &>
22 <%perl>
24 use CXGN::Page::FormattingHelpers qw / conditional_like_input_html simple_selectbox_html/;
26 my $accession_cvterm_id;
28 for (my $i=0; $i<= scalar(@$stock_types); $i++) {
29      if ( $stock_types->[$i][1] eq "accession" ) {
30         $accession_cvterm_id = $stock_types->[$i][0];
31         last();
32      }
35 my $stock_type_select = simple_selectbox_html(
36   choices   => $stock_types,
37   id        => "stock_type_select",
38   selected => $accession_cvterm_id
41 my $organism_select = simple_selectbox_html(
42   choices  =>  $organisms   ,
43   id       => "organism_select",
46 my $breeding_programs_select = simple_selectbox_html(
47     choices => $breeding_programs,
48     id      => "breeding_program" ,
51 </%perl>
53 <&| /page/info_section.mas, title => 'Search', collapsible=>1, collapsed=>0 &>
55   <div id="stock_search_form" class="well well-sm">
57     <&| /page/info_section.mas, title => 'Name Search', collapsible=>1, collapsed=>0 &>
59     <div class="row">
60       <div class="col-sm-11">
61         <div class="form-group form-group-sm">
62           <label class="col-sm-3 control-label">Stock Name or Description: </label>
63           <div class="col-sm-9" >
64             <div class="form-group form-group-sm">
65               <div class="input-group">
66                 <span class="input-group-btn" width="20%">
67                   <select class="form-control" id="any_name_matchtype" name="any_name_matchtype">
68                     <option title="starts_with" value="starts_with" selected="selected">starts with</option>
69                     <option title="contains" value="contains">contains</option>
70                     <option title="ends with" value="ends_with">ends with</option>
71                     <option title="exactly" value="exactly">exactly</option>
72                   </select>
73                 </span>
74                   <span class="input-group-btn">
75                   <input class="form-control" name="any_name" id="any_name" value="" size="30" type="text" placeholder="Type search here..."/>
76                 </span>
77               </div>
78             </div>
79             <div class="form-group form-group-sm">
80               <label class="col-sm-3 control-label">Stock Type: </label>
81               <div class="col-sm-9" >
82                 <% $stock_type_select %>
83               </div>
84             </div>
85           </div>
86         </div>
87       </div>
88     </div>
89     </&>
91     <&| /page/info_section.mas, title => 'Advanced Search', collapsible=>1, collapsed=>1, id=>'advanced_search_panel' &>
93     <&| /page/info_section.mas, title => 'Properties', collapsible=>1, collapsed=>1 &>
95     <div class="row">
96       <div class="col-sm-6">
97         <div class="form-group form-group-sm">
98           <label class="col-sm-3 control-label">Organism: </label>
99           <div class="col-sm-9" >
100             <% $organism_select %>
101           </div>
102         </div>
103       </div>
104     </div>
105     <div class="row">
106       <div class="col-sm-6">
107         <div class="form-group form-group-sm">
108           <label class="col-sm-4 control-label">Organization: </label>
109           <div class="col-sm-8" >
110             <input type="text" class="form-control" id="organization" placeholder="Type to Autocomplete"/>
111           </div>
112         </div>
113       </div>
114     </div>
116     <hr>
117     <div class="row">
118       <div class="col-sm-11">
119         <div class="form-group form-group-sm">
120           <label class="col-sm-3 control-label">Search By Another Property: </label>
121           <div class="col-sm-9" >
122             <div class="form-group">
123               <div class="input-group">
124                 <span class="input-group-btn">
125                   <select class="form-control" id="editable_stockprop_search_term" name="editable_stockprop_search_term">
126 %  foreach my $stockprop (@$editable_stock_props){
127                     <option title="<% $stockprop %>" value="<% $stockprop %>" ><% $stockprop %></option>
128 %  }
129                   </select>
130                 </span>
131                   <span class="input-group-btn">
132                     <button class="btn btn-primary btn-sm" id="editable_stockprop_search_add">Add</button>
133                 </span>
134               </div>
135             </div>
136           </div>
137         </div>
138       </div>
139     </div>
141     <div id="editable_stockprop_search_div">
142     </div>
144     </&>
145     <&| /page/info_section.mas, title => 'Usage', collapsible=>1, collapsed=>1 &>
147     <div class="row">
148       <div class="col-sm-6">
149         <div class="form-group form-group-sm">
150           <label class="col-sm-3 control-label">Project Name: </label>
151           <div class="col-sm-9" >
152             <input type="text" class="form-control" id="project" placeholder="Type to Autocomplete"/>
153           </div>
154         </div>
155       </div>
156       <div class="col-sm-6">
157         <div class="form-group form-group-sm">
158           <label class="col-sm-3 control-label">Project Location: </label>
159           <div class="col-sm-9" >
160             <input type="text" class="form-control" id="location" placeholder="Type to Autocomplete"/>
161           </div>
162         </div>
163       </div>
164     </div>
165     <div class="row">
166       <div class="col-sm-6">
167         <div class="form-group form-group-sm">
168           <label class="col-sm-3 control-label">Project Year: </label>
169           <div class="col-sm-9" >
170             <input type="text" class="form-control" id="year" placeholder="Type to Autocomplete"/>
171           </div>
172         </div>
173       </div>
174       <div class="col-sm-6">
175         <div class="form-group form-group-sm">
176           <label class="col-sm-3 control-label">Breeding Program: </label>
177           <div class="col-sm-9" >
178             <% $breeding_programs_select %>
179           </div>
180         </div>
181       </div>
182     </div>
184     </&>
185     <&| /page/info_section.mas, title => 'Phenotypes', collapsible=>1, collapsed=>1 &>
187     <div class="row">
188         <div class="col-sm-6">
189           <div class="form-group form-group-sm">
190             <label class="col-sm-3 control-label">Trait: </label>
191             <div class="col-sm-9" >
192               <input type="text" class="form-control" id="trait" placeholder="Type to Autocomplete"/>
193             </div>
194           </div>
195         </div>
196       <div class="col-sm-6">
197         <div class="form-group form-group-sm">
198           <label class="col-sm-3 control-label">Minimum Trait Value: </label>
199           <div class="col-sm-9" >
200             <input type="text" class="form-control" id="minimum_trait_value" />
201           </div>
202         </div>
203       </div>
204       </div>
205       <div class="row">
206       <div class="col-sm-6">
207         <div class="form-group form-group-sm">
208           <label class="col-sm-3 control-label">Maximum Trait Value: </label>
209           <div class="col-sm-9" >
210             <input type="text" class="form-control" id="maximum_trait_value" />
211           </div>
212         </div>
213       </div>
214     </div>
215     </&>
217     <&| /page/info_section.mas, title => 'Search Accessions Using Genotypes', collapsible=>1, collapsed=>1 &>
218         <div class="row">
219             <a href="/search/stocks_using_genotypes"><p>Go to Search Accessions Using Genotypes Page.</p></a>
220         </div>
221     </&>
223     </&> <!-- closes advanced search -->
225     <center>
226     <button class="btn btn-primary" id="submit_stock_search" />Search</button>
227     </center>
228     <br/><br/>
229   </div>
230 </&>
232 <br />
234 <&| /page/info_section.mas, title => 'Search Results', collapsible=>1, collapsed=>0 &>
236 <div class="well well-sm">
238     <div class="row">
239       <div class="col-sm-11">
240         <div class="form-group form-group-sm">
241           <label class="col-sm-4 control-label">View Another Property: </label>
242           <div class="col-sm-8" >
243             <div class="form-group">
244               <div class="input-group">
245                 <span class="input-group-btn">
246                   <select class="form-control" id="editable_stockprop_view_column_term" name="editable_stockprop_view_column_term">
247 %  foreach my $stockprop (sort { "\L$a" cmp "\L$b" } @$editable_stock_props){
248                     <option title="<% $stockprop %>" value="<% $stockprop %>" ><% $stockprop %></option>
249 %  }
250                   </select>
251                 </span>
252                   <span class="input-group-btn">
253                     <button class="btn btn-primary btn-sm" id="editable_stockprop_view_column_add">Add</button>
254                 </span>
255               </div>
256             </div>
257           </div>
258         </div>
259       </div>
260     </div>
262     <div class="panel panel-default">
263         <div class="panel-body">
264             <div id="stock_search_results_div" style="overflow:scroll" >
265                 <table id="stock_search_results" width="100%" class="table table-hover table-striped">
266                 <thead>
267                   <tr>
268                     <th>Stock Name</th>
269                     <th>Stock Type</th>
270                     <th>Organism</th>
271                     <th>Synonyms</th>
272                     <th>Organization</th>
273                 </tr>
274                 </thead>
276                 </table>
277             </div>
278         </div>
279     </div>
281     <div class="panel panel-default">
282         <div class="panel-body">
283             <&| /page/info_section.mas, title => 'Copy Results to a List', collapsible=>1, collapsed=>0, subtitle=>'<i>Copy the stock names currently showing in the search results table to a new or exisiting list</i>'&>
284             <br>
285             <div style="text-align:right" id="results_to_list_menu"></div>
286             <div id="search_result_names" style="display: none;"></div>
287             </&>
288         </div>
289     </div>
291 </div>
293 </&>
295 <& /page/detail_page_2_col_section.mas, info_section_title => "<h4 style='display:inline'>Graphical Filtering on Phenotype Performance</h4>", info_section_subtitle => 'Graphically filter an accessions average performance for combinations of traits', icon_class => "glyphicon glyphicon-equalizer", info_section_id => "stocks_search_graphical_filtering" &>
298 </div>
300 <script>
302 var stock_table;
304 jQuery(document).ready(function () {
306     window.addEventListener('load', () => {
307       // Reset the advanced search form parameters on load.
308       // Very similar to later functionality but that one is for advanced search and on load trigger only.
309       var input_list = document.getElementById('advanced_search_panel_content').getElementsByTagName('input');
310       for (let input_elem of input_list) {
311         input_elem.value = '';
312       }
314       var select_list = document.getElementById('advanced_search_panel_content').getElementsByTagName('select');
315       for (let select_elem of select_list) {
316         select_elem.selectedIndex = 0;
317       }
319       var type_select = document.getElementById('stock_type_select');
320       type_select.selectedIndex = [...type_select.options].findIndex(elem => elem.text.trim().toLowerCase() == 'accession') ?? 1;
321     });
324     const textFields = document.querySelectorAll('input[type="text"]');
325     // Loop through the text fields and set their values to an empty string
326       textFields.forEach(textField => {
327         textField.value = '';
328      });
330      // Get all the dropdown select elements in the form
331      // Reset all but 'stock_type_select' dropdown to zero value
332      // Leave 'stock_type_select' with the default "selected" value from the mason file.
333      const selectFields = document.querySelectorAll('select');
334      selectFields.forEach(selectField => {
335         if (selectField.id != 'stock_type_select') {
336           selectField.selectedIndex = 0;
337         }
338      });
340      jQuery("#person").autocomplete({
341         source: '<% $sp_person_autocomplete_uri %>'
342      });
343      jQuery("#trait").autocomplete({
344         source: '<% $trait_autocomplete_uri %>'
345      });
346      jQuery("#onto").autocomplete({
347         source: '<% $onto_autocomplete_uri %>' + "?db_name=" + '<% $trait_db_name %>'
348      });
349      jQuery("#project").autocomplete({
350         source: '/ajax/stock/project_autocomplete',
351      });
352      jQuery("#any_name").autocomplete({
353         source: '/ajax/stock/stock_autocomplete?stock_type_id='+jQuery('#stock_type_select option:selected').val(),
354      });
355      jQuery('#stock_type_select').change(function(){
356          jQuery("#any_name").autocomplete({
357             source: '/ajax/stock/stock_autocomplete?stock_type_id='+jQuery('#stock_type_select option:selected').val(),
358          });
359      });
360      jQuery("#location").autocomplete({
361         source: '/ajax/stock/geolocation_autocomplete',
362      });
363      jQuery("#year").autocomplete({
364         source: '/ajax/stock/project_year_autocomplete',
365      });
366      jQuery("#organization").autocomplete({
367         source: '/ajax/stock/stockproperty_autocomplete?property=organization',
368      });
370     // Update changed organization
371     jQuery(document).on("change", "#organization", function() {
372       editable_stockprops_search["organization"] = {
373         "matchtype": "contains",
374         "value": jQuery(this).val()
375       }
376     });
378     var editable_stockprops_search = {};
379     var params = {};
380     var matchtype = {};
381     jQuery('#editable_stockprop_search_add').click( function(){
382         // console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
383         // console.log("Value is "+jQuery('#editable_stockprop_search_term').val());
384         editable_stockprops_search[jQuery('#editable_stockprop_search_term').val()] = {"matchtype": "contains"};
385         console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
386         _draw_editable_stockprops_search_fields();
387     });
389     jQuery(document).on('click', 'button[name="editable_stockprop_search_remove"]', function(){
390         delete editable_stockprops_search[jQuery(this).data('property')];
391         _draw_editable_stockprops_search_fields();
392     });
394     jQuery(document).on("keyup", 'input[name="editable_stockprop_search_inputs"]', function(){
395         //Store typed value in object, in case of redraw inputs (input deletion or addition) the value will be still there. Also for search submission
396         editable_stockprops_search[jQuery(this).data('property')]["value"] = jQuery(this).val();
397         console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
398     });
400     jQuery(document).on("change", 'select[name="editable_stockprop_matchtype"]', function(){
402     // jQuery(document).on("change", 'input[name="editable_stockprop_matchtype"]', function(){
403         //Store typed value in object, in case of redraw inputs (input deletion or addition) the value will be still there. Also for search submission
404         console.log("prop is "+jQuery(this).data('property'));
405         editable_stockprops_search[jQuery(this).data('property')]["matchtype"] = jQuery(this).val();
406         console.log("editable stockprops search is "+JSON.stringify(editable_stockprops_search));
407     });
409     function _draw_editable_stockprops_search_fields(){
410         jQuery('#editable_stockprop_search_div').html('');
411         for (var property in editable_stockprops_search) {
412             if (editable_stockprops_search.hasOwnProperty(property)) {
413                 jQuery('#editable_stockprop_search_div').append('<div class="form-group form-group-sm"><label class="col-sm-3 control-label">'+property+': </label><div class="col-sm-9" ><div class="form-group"><div class="input-group"><span class="input-group-btn" width="20%"><select class="form-control" id="editable_stockprop_matchtype" name="editable_stockprop_matchtype" data-property="'+property+'"><option title="contains" value="contains" selected="selected">contains</option><option title="starts with" value="starts_with">starts with</option><option title="ends with" value="ends_with">ends with</option><option title="exactly" value="exactly">exactly</option><option title="one of" value="one of">one of (separate multiple possibilities with commas)</option></select></span><span class="input-group-btn"><input type="text" class="form-control" name="editable_stockprop_search_inputs" id="'+property+'_input_id" data-property="'+property+'" placeholder="Type to Autocomplete"/></span><span class="input-group-btn"><button name="editable_stockprop_search_remove" data-property="'+property+'" class="btn"><span class="glyphicon glyphicon-remove"></span></button></span></div></div></div></div>');
415                 jQuery('#editable_stockprop_search_div').find("#"+property+"_input_id").autocomplete({
416                    source: '/ajax/stock/stockproperty_autocomplete?property='+property,
417                 });
418             }
419         }
420     }
422     //For adding stockprop columns to search result
423     var stockprop_extra_columns_view = { 'organization':1 };
424     var stockprop_extra_columns_view_array = [ 'organization' ];
425     jQuery('#editable_stockprop_view_column_add').click(function(){
426         var selected_property = jQuery('#editable_stockprop_view_column_term').val();
427         if (!(selected_property in stockprop_extra_columns_view)){
428             stockprop_extra_columns_view[selected_property] = 1;
429             stockprop_extra_columns_view_array.push(selected_property);
431             var table_header_html = '<table id="stock_search_results" class="table table-hover table-striped"><thead><tr><th>Stock Name</th><th>Stock Type</th><th>Organism</th><th>Synonyms</th>';
432             for (var i=0; i<stockprop_extra_columns_view_array.length; i++){
433                 table_header_html = table_header_html + '<th>' + stockprop_extra_columns_view_array[i] + '</th>';
434             }
435             table_header_html = table_header_html + '</tr></thead></table>';
436             jQuery('#stock_search_results_div').html(table_header_html);
437         }
438         stock_table.destroy();
439         _load_stock_search_results(jQuery('#stock_type_select option:selected').text(), editable_stockprops_search, stockprop_extra_columns_view, stockprop_extra_columns_view_array);
440     });
442     parseArgs(editable_stockprops_search);
444     jQuery('#stock_search_results').on( 'draw.dt', function () {
445       var name_links = stock_table.column(0).data();
446       var names = [];
448       for (var i = 0; i < name_links.length; i++) { //extract text from anchor tags
449         names.push(name_links[i].match(/<a [^>]+>([^<]+)<\/a>/)[1]+'\n');
450       }
452       jQuery('#search_result_names').html(names);
453       addToListMenu('results_to_list_menu', 'search_result_names', {
454         listType: jQuery('#stock_type_select option:selected').text()+'s' || 'null'
455       });
457     });
459    jQuery('#submit_stock_search').click( function() {
460         //stock_table.search("stock_search_results").draw();
461         _load_stock_search_results(jQuery('#stock_type_select option:selected').text(), editable_stockprops_search, stockprop_extra_columns_view, stockprop_extra_columns_view_array);
462    });
464    jQuery('#stock_search_form').keypress( function( e ) {
465            var code = e.keyCode || e.which;
466            if( code == 13 ) {
467                 jQuery('#submit_stock_search').click();
468            }
469     } );
472     // For graphical filtering, using same search params
473     var run = false;
474     jQuery('#stock_search_graphical_filter_onswitch').on("click", function(){
475         var stock_type = jQuery('#stock_type_select option:selected').text();
476         if (stock_type == 'accession'){
477             jQuery.ajax ({
478                 url : '/ajax/search/stocks',
479                 data : {
480                     minimal_info : 1,
481                     any_name : jQuery('#any_name').val(),
482                     any_name_matchtype : jQuery('#any_name_matchtype').val(),
483                     stock_type : jQuery('#stock_type_select option:selected').text(),
484                     organism : jQuery('#organism_select').val(),
485                     person : jQuery('#person').val(),
486                     trait : jQuery('#trait').val(),
487                     minimum_trait_value : jQuery('#minimum_trait_value').val(),
488                     maximum_trait_value : jQuery('#maximum_trait_value').val(),
489                     project : jQuery('#project').val(),
490                     location : jQuery('#location').val(),
491                     year : jQuery('#year').val(),
492                     breeding_program : jQuery('#breeding_program').val(),
493                     editable_stockprop_values : JSON.stringify(editable_stockprops_search)
494                 },
495                 beforeSend: function() {
496                     jQuery("#working_modal").modal("show");
497                     jQuery('#stock_search_graphical_filter_div').html("[LOADING...]");
498                 },
499                 success: function(response){
500                     //console.log(response);
501                     jQuery("#working_modal").modal("hide");
502                     var stock_ids = [];
503                     for (var i=0; i<response.data.length; i++){
504                         stock_ids.push(response.data[i][0]);
505                     }
507                     if (run) {
508                         jQuery("#stock_search_graphical_filter_results_div").DataTable().destroy();
509                         jQuery("#stock_search_graphical_filter_results_div").html("");
510                     }
511                     run = true;
512                     params = {"germplasmDbIds" : stock_ids, "observationLevel" : 'plot'};
513                     loadBrAPIData("<% $c->config->{main_production_site_url} %>",params,function(response){
514                         useBrAPIData(response,true);
515                     });
517                 },
518                 error: function(response){
519                     jQuery("#working_modal").modal("hide");
520                     alert("Error retrieving accessions in graphical filtering.");
521                 }
522             });
523         } else {
524             jQuery('#stock_search_graphical_filter_div').html("Graphical Filtering only configured for accessions. Currently searching across "+stock_type+". Change your search to accesions to use graphical filtering.");
525         }
526     });
531 function parseArgs(editable_stockprops_search) {
532   const params = new URLSearchParams(window.location.search);
533   if ( params.has('any_name') ) {
534     jQuery('#any_name').val(params.get('any_name'));
535     jQuery('#stock_type_select').val(0);
536   }
537   if ( params.has('stock_type') ) {
538     var type = params.get('stock_type');
539     var value =  jQuery("#stock_type_select option[title="+ type +"]").val();
540     jQuery('#stock_type_select').val(value);
542   }
543   if ( params.has('prop') & params.has('value') ) {
544     editable_stockprops_search[params.get('prop')] = {"matchtype": "exactly"};
545     editable_stockprops_search[params.get('prop')]["value"] = params.get('value');
546   }
549 function _load_stock_search_results(stock_type, editable_stockprops_search, stockprop_extra_columns_view, stockprop_extra_columns_view_array){
550     console.log(stock_type);
551     console.log("Making AJAX request with editable stockprop "+JSON.stringify(editable_stockprops_search));
552     stock_table = jQuery('#stock_search_results').DataTable({
553         'destroy' : true,
554         'searching' : false,
555         'ordering'  : false,
556         'processing': true,
557         'serverSide': true,
558         'lengthMenu': [10,20,50,100,1000,5000],
559         'ajax': { 'url':  '/ajax/search/stocks',
560             'data': function(d) {
561               d.any_name  = jQuery('#any_name').val();
562               d.any_name_matchtype = jQuery('#any_name_matchtype').val();
563               d.stock_type   = stock_type;
564               d.organism     = jQuery('#organism_select').val();
565               d.person       = jQuery('#person').val();
566               d.trait        = jQuery('#trait').val();
567               d.minimum_trait_value        = jQuery('#minimum_trait_value').val();
568               d.maximum_trait_value        = jQuery('#maximum_trait_value').val();
569               d.project      = jQuery('#project').val();
570               d.location     = jQuery('#location').val();
571               d.year         = jQuery('#year').val();
572               d.breeding_program = jQuery('#breeding_program').val();
573               d.editable_stockprop_values = JSON.stringify(editable_stockprops_search);
574               d.extra_stockprop_columns_view = JSON.stringify(stockprop_extra_columns_view);
575               d.stockprop_extra_columns_view_array = JSON.stringify(stockprop_extra_columns_view_array);
576             }
577         }
578     });
581 </script>