fix test with new description field.
[sgn.git] / mason / breeders_toolbox / create_seedlots_from_trial_dialogs.mas
blobdfe350040a5e737b9c3f8fb0deae1c4ebba433ba
1 <%args>
2 $timestamp => localtime()
3 </%args>
5 <div class="modal falde" id="create_seedlots_trial_dialog" name="create_seedlots_trial_dialog" tabindex="-1" aria-labelledby="create_seedlots_trial_dialog_title">
6     <div class="modal-dialog modal-xl" role="document">
7         <div class="modal-content">
8             <div class="modal-header">
9                 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
10                 <h4 class="modal-title" id="create_seedlots_trial_dialog_title">Create Seedlots from a Trial</h4>
11             </div>
12             <div class="modal-body">
13                 <div class="container-fluid">
15                     <!-- CREATE SEEDLOTS WORKFLOW -->
16                     <&| /util/workflow.mas, id=> "create_seedlots_trial_workflow" &>
17                         
18                         
19                         <!-- STEP 1: Intro -->
20                         <&| /util/workflow.mas:step, title=> "Intro" &>
21                             <& /page/page_title.mas, title=>"Introduction" &>
22                             <p>
23                                 This workflow will guide you through the process of creating seedlots from the accessions 
24                                 in a field trial.  You can create one new seedlot for each plot or unique accession 
25                                 in the trial.  You can also set the initial contents (count or weight) of the seedlot 
26                                 as the value of a recorded trait from the trial.
27                             </p>
28                             <br/><br/>
29                             <center>
30                                 <button class="btn btn-primary" onclick="Workflow.complete(this);">Go to Next Step</button>
31                             </center>
32                         </&>
34                         <!-- STEP 2: Select Trial -->
35                         <&| /util/workflow.mas:step, title=> "Select Trial" &>
36                             <& /page/page_title.mas, title=>"Select a Field Trial" &>
37                             <p>
38                                 Enter the name of the field trial from which to create the seedlots:
39                             </p>
40                             <br /><br />
41                             <div class="form-group">
42                                 <label class="col-sm-3 control-label" style="text-align: right">Trial: </label>
43                                 <div class="col-sm-9" >
44                                     <input class="form-control" id="create_seedlots_trial_name" placeholder="Trial Name..." value="">
45                                 </div>
46                             </div>
47                             <br /><br /><br /><br />
48                             <div class="alert alert-danger" id="trial_error_message_container" style="display: none">
49                                 <p id="trial_error_message"></p>
50                             </div>
51                             <br/><br/>
52                             <center>
53                                 <button id="create_seedlots_trial_complete_select_trial" class="btn btn-primary">Go to Next Step</button>
54                             </center>
55                         </&>
57                         <!-- STEP 3: Select Seedlots -->
58                         <&| /util/workflow.mas:step, title=> "Select Seedlots" &>
59                             <& /page/page_title.mas, title=>"Select Seedlots" &>
61                             <p>
62                                 Seedlots can be generated from either each <strong>accession</strong> or <strong>plot</strong>
63                                 <span class='create_seedlots_trial_subplots_available'> or <strong>subplot</strong></span>
64                                 <span class='create_seedlots_trial_plants_available'> or <strong>plant</strong></span>
65                                 in this trial.
66                             </p>
67                             <ul>
68                                 <li>If you select <strong>accessions</strong>: <span id="create_seedlots_trial_accession_count"></span> seedlots will be created</li>
69                                 <li>If you select <strong>plots</strong>: <span id="create_seedlots_trial_plot_count"></span> seedlots will be created</li>
70                                 <li class='create_seedlots_trial_subplots_available_list'>If you select <strong>subplots</strong>: <span id="create_seedlots_trial_subplot_count"></span> seedlots will be created</li>
71                                 <li class='create_seedlots_trial_plants_available_list'>If you select <strong>plants</strong>: <span id="create_seedlots_trial_plant_count"></span> seedlots will be created</li>
72                             </ul>
74                             <br /><br />
76                             <div class="form-group">
77                                 <div class="col-sm-3"></div>
78                                 <div class="col-sm-6" >
79                                     <select id="create_seedlots_trial_stock_type" class="form-control">
80                                         <option value="accessions">Accessions</option>
81                                         <option value="plots">Plots</option>
82                                         <option class="create_seedlots_trial_subplots_available" value="subplots">Subplots</option>
83                                         <option class="create_seedlots_trial_plants_available" value="plants">Plants</option>
84                                     </select>
85                                 </div>
86                                 <div class="col-sm-3"></div>
87                             </div>
88                             
89                             <br/><br/><br /><br />
90                             <center>
91                                 <button id="create_seedlots_trial_complete_select_stock_type" class="btn btn-primary">Go to Next Step</button>
92                             </center>
93                         </&>
95                         <!-- STEP 4: Name Seedlots -->
96                         <&| /util/workflow.mas:step, title=> "Name Seedlots" &>
97                             <& /page/page_title.mas, title=>"Name Seedlots" &>
99                             <p>
100                                 Enter a template for naming each new seedlot.  The following variables (surrounded by curly braces) can be used in the template and the value of that variable will be replaced.
101                             </p>
102                             <ul>
103                                 <li><code>trial_name</code></li>
104                                 <li><code>accession_name</code></li>
105                                 <li class="plot_level_variable"><code>plot_name</code></li>
106                                 <li class="plot_level_variable"><code>plot_number</code></li>
107                                 <li class="plot_level_variable"><code>rep</code></li>
108                                 <li class="plot_level_variable"><code>block</code></li>
109                                 <li class="subplot_level_variable"><code>subplot_name</code></li>
110                                 <li class="subplot_level_variable"><code>subplot_index</code></li>
111                                 <li class="plant_level_variable"><code>plant_name</code></li>
112                                 <li class="plant_level_variable"><code>plant_index</code></li>
113                                 <li><code>index</code> - an incrementing index number, starting with 1</li>
114                             </ul>
116                             <br /><br />
118                             <div class="form-group">
119                                 <label class="col-sm-3 control-label" style="text-align: right">Name Template: </label>
120                                 <div class="col-sm-7" >
121                                     <input class="form-control" id="create_seedlots_trial_name_template" value="">
122                                 </div>
123                                 <div class="col-sm-2">
124                                     <button id="create_seedlots_trial_name_update" type="button" class="btn btn-primary btn-block" disabled>Update</button>
125                                 </div>
126                             </div>
128                             <br /><br /><br />
130                             <center>
131                                 <button id="create_seedlots_trial_complete_name_seedlots" class="btn btn-primary">Go to Next Step</button>
132                             </center>
134                             <br /><br />
136                             <h4>Seedlot Names</h4>
137                             <table id="create_seedlots_trial_name_table" class="table table-striped table-hover"></table>
138                         </&>
140                         <!-- STEP 5: Seedlot Contents -->
141                         <&| /util/workflow.mas:step, title=> "Set Contents" &>
142                             <& /page/page_title.mas, title=>"Set Seedlot Contents" &>
144                             <p>
145                                 Each seedlot needs to have its initial contents (either a count or a weight) set.  You 
146                                 can either set the same initial value for each seedlot, use the value from one of the 
147                                 traits recorded for this trial, or compute a new value from the recorded traits for this 
148                                 trial.  If you are creating a seedlot for each accession and select a trait or a computed 
149                                 value as their initial value, then the sum of the trait values for each of the accession's 
150                                 plots will be used.  The contents of each seedlot can be modified in the table below 
151                                 before its creation.
152                             </p>
154                             <br /><br />
156                             <div class="form-group">
157                                 <label class="col-sm-3 control-label" style="text-align: right">Contents Type: </label>
158                                 <div class="col-sm-9" >
159                                     <select class="form-control" id="create_seedlots_trial_contents_type">
160                                         <option value="amount">Amount / Count</option>
161                                         <option value="weight">Weight</option>
162                                     </select>
163                                 </div>
164                             </div>
166                             <br /><br />
168                             <div class="form-group">
169                                 <label class="col-sm-3 control-label" style="text-align: right">Contents Value: </label>
170                                 <div class="col-sm-9" >
171                                     <select class="form-control" id="create_seedlots_trial_contents_value">
172                                         <option value="constant">Constant Value</option>
173                                         <option value="trait">Trait Value</option>
174                                         <option value="computed">Computed Value from Traits</option>
175                                     </select>
176                                 </div>
177                             </div>
179                             <br /><br />
181                             <div class="form-group" id="create_seedlots_trial_contents_constant_form">
182                                 <label class="col-sm-3 control-label" style="text-align: right">Constant: </label>
183                                 <div class="col-sm-9" >
184                                     <input class="form-control" id="create_seedlots_trial_contents_constant" value="1">
185                                 </div>
186                             </div>
188                             <div class="form-group" id="create_seedlots_trial_contents_trait_form" style="display: none">
189                                 <label class="col-sm-3 control-label" style="text-align: right">Trait: </label>
190                                 <div class="col-sm-9" >
191                                     <select class="form-control" id="create_seedlots_trial_contents_trait"></select>
192                                 </div>
193                             </div>
195                             <div class="form-group" id="create_seedlots_trial_contents_computed_form" style="display: none">
196                                 <label class="col-sm-3 control-label" style="text-align: right">Trait Variables: </label>
197                                 <div class="col-sm-9">
198                                     <p>
199                                         Use the following variables as substitutions for the trait values to compute a new value 
200                                         for the initial seedlot contents.  If you are creating one seedlot <strong>per plot</strong>, then 
201                                         the single plot trait value will be used in the computation.  If you are creating one seedlot 
202                                         <strong>per accession</strong>, then the sum of the trait values for each plot of that accession 
203                                         will be used in the computation.
204                                     </p>
205                                     <table class="table table-striped">
206                                         <thead>
207                                             <tr>
208                                                 <th>Variable</th>
209                                                 <th>Trait</th>
210                                             </tr>
211                                         </thead>
212                                         <tbody id="create_seedlots_trial_contents_computed_table"></tbody>
213                                     </table>
214                                 </div>
216                                 <label class="col-sm-3 control-label" style="text-align: right">Contents Formula: </label>
217                                 <div class="col-sm-9">
218                                     <input class="form-control" id="create_seedlots_trial_contents_computed_formula" value="" placeholder="{trait_1}/10">
219                                     <p>
220                                         <strong>NOTE:</strong> The formula is evaluated using javascript, so any basic operators (+-*/) or 
221                                         javascript Math functions (Math.abs(x)) can be used.
222                                     </p>
223                                 </div>
225                                 <div class="col-sm-12" style="text-align: center; margin: 25px 0">
226                                     <button id="create_seedlots_trial_complete_computed_compute" class="btn btn-primary" disabled>Compute</button>
227                                 </div>
228                             </div>
230                             <br /><br />
232                             <center>
233                                 <button id="create_seedlots_trial_complete_set_contents" class="btn btn-primary">Go to Next Step</button>
234                             </center>
236                             <br /><br />
238                             <h4>Seedlot Contents</h4>
239                             <table id="create_seedlots_trial_contents_table" class="table table-striped table-hover"></table>
240                         </&>
242                         <!-- STEP 6: Seedlot Metadata -->
243                         <&| /util/workflow.mas:step, title=> "Set Metadata" &>
244                             <& /page/page_title.mas, title=>"Set Seedlot Metadata" &>
246                             <p>
247                                 The following metadata is required for each seedlot.  Fill out the form at the top 
248                                 apply the metadata to all of the seedlots.  Then, each seedlot can be manually 
249                                 modified in the table below.
250                             </p>
252                             <br /><br />
254                             <div class="form-group">
255                                 <label class="col-sm-3 control-label" style="text-align: right">Breeding Program: </label>
256                                 <div class="col-sm-9" >
257                                     <div id="create_seedlots_trial_breeding_program_div"></div>
258                                 </div>
259                             </div>
261                             <br /><br />
263                             <div class="form-group">
264                                 <label class="col-sm-3 control-label" style="text-align: right">Location: </label>
265                                 <div class="col-sm-9" >
266                                     <input class="form-control create_seedlots_trial_metadata_input" id="create_seedlots_trial_location" value="" placeholder="Required">
267                                 </div>
268                             </div>
270                             <br /><br />
272                             <div class="form-group">
273                                 <label class="col-sm-3 control-label" style="text-align: right">Box Name: </label>
274                                 <div class="col-sm-9" >
275                                     <input class="form-control create_seedlots_trial_metadata_input" id="create_seedlots_trial_box_name" value="" placeholder="Required">
276                                 </div>
277                             </div>
279                             <br /><br />
281                             <div class="form-group">
282                                 <label class="col-sm-3 control-label" style="text-align: right">Quality Issues: </label>
283                                 <div class="col-sm-9" >
284                                     <input class="form-control create_seedlots_trial_metadata_input" id="create_seedlots_trial_quality_issues" value="" placeholder="Optional, list quality issues here, or 'ok' for good quality seed">
285                                 </div>
286                             </div>
288                             <br /><br />
290                             <div class="form-group">
291                                 <label class="col-sm-3 control-label" style="text-align: right">Organization: </label>
292                                 <div class="col-sm-9" >
293                                     <input class="form-control create_seedlots_trial_metadata_input" id="create_seedlots_trial_organization" value="" placeholder="Optional">
294                                 </div>
295                             </div>
297                             <br /><br />
299                             <div class="form-group">
300                                 <label class="col-sm-3 control-label" style="text-align: right">Timestamp: </label>
301                                 <div class="col-sm-9" >
302                                     <input class="form-control create_seedlots_trial_metadata_input" id="create_seedlots_trial_timestamp" value="<% $timestamp %>" placeholder="<% $timestamp %>">
303                                 </div>
304                             </div>
306                             <br /><br />
308                             <div class="form-group">
309                                 <label class="col-sm-3 control-label" style="text-align: right">Description: </label>
310                                 <div class="col-sm-9" >
311                                     <input class="form-control create_seedlots_trial_metadata_input" id="create_seedlots_trial_description" value="" placeholder="Optional">
312                                 </div>
313                             </div>
315                             <br /><br /><br />
317                             <center>
318                                 <button id="create_seedlots_trial_update_metadata" class="btn btn-primary">Update</button>
319                             </center>
321                             <br />
323                             <center>
324                                 <button id="create_seedlots_trial_complete_set_metadata" class="btn btn-primary" disabled>Go to Next Step</button>
325                             </center>
327                             <br /><br />
329                             <h4>Seedlot Metadata</h4>
330                             <table id="create_seedlots_trial_metadata_table" class="table table-striped table-hover"></table>
331                         </&>
333                         <!-- STEP 7: Confirm -->
334                         <&| /util/workflow.mas:step, title=> "Confirm" &>
335                             <& /page/page_title.mas, title=>"Confirm Seedlots" &>
337                             <p>
338                                 Confirm the attributes for the new seedlots in the table below.  If something 
339                                 is incorrect, navigate to the previous step and correct the information.  Once 
340                                 all the information is correct, continue to the next step to create the seedlots.
341                             </p>
343                             <br /><br />
345                             <center>
346                                 <button id="create_seedlots_trial_complete_confirm_seedlots" class="btn btn-primary">
347                                     <span class="glyphicon glyphicon-ok"></span>&nbsp;Create Seedlots
348                                 </button>
349                             </center>
351                             <br /><br />
353                             <h4>Seedlots to Create</h4>
354                             <table id="create_seedlots_trial_confirm_table" class="table table-striped table-hover"></table>
355                         </&>
357                         <!-- STEP 8: Create -->
358                         <&| /util/workflow.mas:step, title=> "Create" &>
359                             <& /page/page_title.mas, title=>"Create Seedlots" &>
361                             <br /><br />
362                             
363                             <div id="create_seedlots_trial_uploading">
364                                 <p id="create_seedlots_trial_status"><strong>Creating Seedlots...</strong></p>
365                                 <div class="progress">
366                                     <div id="create_seedlots_trial_progress" class="progress-bar" role="progressbar" aria-valuenow="70"
367                                     aria-valuemin="0" aria-valuemax="100" style="width:70%">
368                                         <span class="sr-only">70% Complete</span>
369                                     </div>
370                                 </div>
371                             </div>
373                             <div id="create_seedlots_trial_results"></div>
375                             <br /><br />
377                             <h4>Seedlot Progress</h4>
378                             <table id="create_seedlots_trial_results_table" class="table table-striped table-hover"></table>
379                         </&>
381                     </&>
383                 </div>
384             </div>
385         </div>
386     </div>
387 </div>
390 <style>
392 </style>
395 <script type="text/javascript">
396     let TRIAL_ID;
397     let BREEDING_PROGRAM_ID;
398     let ACCESSIONS;
399     let PLOTS;
400     let SUBPLOTS;
401     let PLANTS;
402     let TRAITS;
403     let TRAIT_DATA;
404     let SEEDLOTS;
405     const UPLOAD_BATCH_SIZE = 5;
407     jQuery(document).ready(function() {
409         // Click / Change Listeners
410         jQuery('[name="create_seedlots_trial_button"]').click( function() {
411             jQuery('#create_seedlots_trial_dialog').modal('show');
412         });
413         jQuery("#create_seedlots_trial_complete_select_trial").click(completeSelectTrial);
414         jQuery("#create_seedlots_trial_complete_select_stock_type").click(completeSelectStockType);
415         jQuery("#create_seedlots_trial_name_template").keyup(function() {
416             jQuery("#create_seedlots_trial_name_update").attr("disabled", false);
417             jQuery("#create_seedlots_trial_complete_name_seedlots").attr("disabled", true);
418         });
419         jQuery("#create_seedlots_trial_name_update").click(updateSeedlotNames);
420         jQuery(document).on('keyup', '.create_seedlots_trial_name_seedlot', function() {
421             let el = jQuery(this);
422             let index = el.data("index");
423             let name = el.val();
424             SEEDLOTS[index].name = name;
425         });
426         jQuery("#create_seedlots_trial_complete_name_seedlots").click(completeNameSeedlots);
427         jQuery("#create_seedlots_trial_contents_value").change(function() {
428             let val = jQuery("#create_seedlots_trial_contents_value").val();
429             jQuery("#create_seedlots_trial_contents_constant_form").css("display", val === "constant" ? "block" : "none");
430             jQuery("#create_seedlots_trial_contents_trait_form").css("display", val === "trait" ? "block" : "none");
431             jQuery("#create_seedlots_trial_contents_computed_form").css("display", val === "computed" ? "block" : "none");
432             jQuery("#create_seedlots_trial_complete_set_contents").attr("disabled", val === "computed");
433         });
434         jQuery("#create_seedlots_trial_contents_value").change(updateSeedlotContents);
435         jQuery("#create_seedlots_trial_contents_constant").keyup(updateSeedlotContents);
436         jQuery("#create_seedlots_trial_contents_trait").change(function() {
437             let id = jQuery("#create_seedlots_trial_contents_trait").val();
438             let name = jQuery("#create_seedlots_trial_contents_trait option:selected").text();
439             if ( id && name ) {
440                 updateSeedlotContents();
441             }
442         });
443         jQuery("#create_seedlots_trial_contents_computed_formula").keyup(function() {
444             jQuery("#create_seedlots_trial_complete_computed_compute").attr("disabled", false);
445             jQuery("#create_seedlots_trial_complete_set_contents").attr("disabled", true);
446         });
447         jQuery("#create_seedlots_trial_complete_computed_compute").click(updateSeedlotContents);
448         jQuery(document).on('keyup', '.create_seedlots_trial_contents_seedlot', function() {
449             let el = jQuery(this);
450             let index = el.data("index");
451             let contents = el.val();
452             SEEDLOTS[index].contents = contents;
453         });
454         jQuery("#create_seedlots_trial_complete_set_contents").click(completeSeedlotContents);
455         jQuery("#create_seedlots_trial_update_metadata").click(function() { updateSeedlotMetadata() });
456         jQuery(document).on('change', "#create_seedlots_trial_breeding_program_id", function() {
457             jQuery("#create_seedlots_trial_update_metadata").attr("disabled", false);
458             jQuery("#create_seedlots_trial_complete_set_metadata").attr("disabled", true);
459         });
460         jQuery(".create_seedlots_trial_metadata_input").keyup(function() {
461             jQuery("#create_seedlots_trial_update_metadata").attr("disabled", false);
462             jQuery("#create_seedlots_trial_complete_set_metadata").attr("disabled", true);
463         });
464         jQuery(document).on('change', '.create_seedlots_trial_metadata_input_seedlot_breeding_program', function() {
465             let el = jQuery(this);
466             let sel = el.find("option:selected");
467             let index = el.data("index");
468             let id = el.val();
469             let name = sel.text();
470             SEEDLOTS[index].metadata.breeding_program = { id: id, name: name };
471         });
472         jQuery(document).on('keyup', '.create_seedlots_trial_metadata_input_seedlot', function() {
473             let el = jQuery(this);
474             let index = el.data("index");
475             let prop = el.data("prop");
476             let value = el.val();
477             SEEDLOTS[index].metadata[prop] = value;
478         });
479         jQuery("#create_seedlots_trial_complete_set_metadata").click(completeSeedlotMetadata);
480         jQuery("#create_seedlots_trial_complete_confirm_seedlots").click(completeSeedlotConfirmation);
482         // Autocomplete
483         jQuery("#create_seedlots_trial_name").autocomplete({
484             source: '/ajax/trials/trial_autocomplete'
485         });
486         jQuery("#create_seedlots_trial_location").autocomplete({
487             source: '/ajax/stock/geolocation_autocomplete',
488         });
490         // Breeding Program Select Box
491         get_select_box('breeding_programs', 'create_seedlots_trial_breeding_program_div', { 'name' : 'create_seedlots_trial_breeding_program_id', 'id' : 'create_seedlots_trial_breeding_program_id' });
493     });
497     // 
498     // WORKFLOW FUNCTIONS
499     //
501     /**
502      * Complete the Select Trial step
503      * - Get details of selected trial
504      */
505     function completeSelectTrial() {
506         let trial_error;
508         // Make sure trial name was entered
509         let trial_name = jQuery("#create_seedlots_trial_name").val();
510         if ( !trial_name || trial_name === '' ) {
511             alert("Enter a trial name to continue");
512             return;
513         }
515         // Display working modal and disable button
516         jQuery('#working_modal').modal("show");
517         jQuery("#create_seedlots_trial_complete_select_trial").attr("disabled", true);
518         jQuery("#trial_error_message_container").hide();
520         // Get Trial ID
521         getTrialID(trial_name, function(trial_id) {
522             if ( trial_id ) {
523                 TRIAL_ID = trial_id;
525                 // Get the trial details
526                 getTrialDetails(trial_id, function(error, breeding_program_id, accessions, plots, subplots, plants, traits) {
527                     if ( error ) {
528                         trial_error = error;
529                         _finish();
530                     }
531                     else if ( breeding_program_id && accessions && plots ) {
532                         BREEDING_PROGRAM_ID = breeding_program_id;
533                         ACCESSIONS = accessions;
534                         PLOTS = plots;
535                         SUBPLOTS = subplots;
536                         PLANTS = plants;
537                         TRAITS = traits ? traits : [];
538                         if ( traits ) {
539                             getTraitData(function() {
540                                 _finish(true);
541                             });
542                         }
543                         else {
544                             _finish(true);
545                         }
546                     }
547                     else {
548                         _finish();
549                     }
550                 });
552             }
553             else {
554                 _finish();
555             }
556         });
557         
559         /**
560          * Hide the working modal, renable the button, and continue (if complete)
561          */
562         function _finish(complete) {
563             jQuery('#working_modal').modal("hide");
564             if ( trial_error ) {
565                 jQuery("#trial_error_message").html(trial_error);
566                 jQuery("#trial_error_message_container").show();
567             }
568             else {
569                 jQuery("#trial_error_message_container").hide();
570             }
571             jQuery("#create_seedlots_trial_complete_select_trial").attr("disabled", false);
572             jQuery("#create_seedlots_trial_accession_count").html(ACCESSIONS ? ACCESSIONS.length : '');
573             jQuery("#create_seedlots_trial_plot_count").html(PLOTS ? PLOTS.length : '');
574             jQuery("#create_seedlots_trial_subplot_count").html(SUBPLOTS ? SUBPLOTS.length : '');
575             jQuery("#create_seedlots_trial_plant_count").html(PLANTS ? PLANTS.length : '');
576             jQuery(".create_seedlots_trial_subplots_available").css("display", SUBPLOTS && SUBPLOTS.length > 0 ? 'inline' : 'none');
577             jQuery(".create_seedlots_trial_plants_available").css("display", PLANTS && PLANTS.length > 0 ? 'inline' : 'none');
578             jQuery(".create_seedlots_trial_subplots_available_list").css("display", SUBPLOTS && SUBPLOTS.length > 0 ? 'list-item' : 'none');
579             jQuery(".create_seedlots_trial_plants_available_list").css("display", PLANTS && PLANTS.length > 0 ? 'list-item' : 'none');
580             if ( complete ) {
581                 Workflow.complete('#create_seedlots_trial_complete_select_trial');
582                 Workflow.focus("#create_seedlots_trial_workflow", 2);
583             }
584         }
585     }
587     /**
588      * Complete the stock type selection step
589      * - Setup the initial Seedlots
590      */
591     function completeSelectStockType() {
592         let stock_type = jQuery("#create_seedlots_trial_stock_type").val();
594         // Hide the plot-level variables, if stock type is not plots
595         jQuery(".plot_level_variable").css("display", ["plots", "subplots", "plants"].includes(stock_type) ? "list-item" : "none");
596         jQuery(".subplot_level_variable").css("display", stock_type === 'subplots' ? "list-item" : "none");
597         jQuery(".plant_level_variable").css("display", stock_type === 'plants' ? "list-item" : "none");
598         jQuery("#create_seedlots_trial_name_template").val(
599             stock_type === "plots" ? "{trial_name}-{accession_name}-{rep}" : 
600             stock_type === "subplots" ? "{trial_name}-{accession_name}-subplot{subplot_index}" :
601             stock_type === "plants" ? "{trial_name}-{accession_name}-plant{plant_index}" :
602             "{trial_name}-{accession_name}"
603         );
605         // Build the Seedlots
606         SEEDLOTS = [];
607         if ( stock_type === 'accessions' ) {
608             for ( let i = 0; i < ACCESSIONS.length; i++ ) {
609                 SEEDLOTS.push({
610                     accession: ACCESSIONS[i],
611                     name: ""
612                 });
613             }
614         }
615         else if ( stock_type === 'plots' ) {
616             for ( let i = 0; i < PLOTS.length; i++ ) {
617                 SEEDLOTS.push({
618                     plot: PLOTS[i].plot,
619                     accession: PLOTS[i].accession,
620                     name: ""
621                 });
622             }
623         }
624         else if ( stock_type === 'subplots' ) {
625             for ( let i = 0; i < SUBPLOTS.length; i++ ) {
626                 SEEDLOTS.push({
627                     plot: SUBPLOTS[i].plot,
628                     subplot: SUBPLOTS[i].subplot,
629                     accession: SUBPLOTS[i].accession,
630                     name: ""
631                 });
632             }
633         }
634         else if ( stock_type === 'plants' ) {
635             for ( let i = 0; i < PLANTS.length; i++ ) {
636                 SEEDLOTS.push({
637                     plot: PLANTS[i].plot,
638                     plant: PLANTS[i].plant,
639                     accession: PLANTS[i].accession,
640                     name: ""
641                 });
642             }
643         }
644         else {
645             alert("ERROR: unknown seedlot stock type!");
646         }
648         Workflow.complete('#create_seedlots_trial_complete_select_stock_type');
649         Workflow.focus("#create_seedlots_trial_workflow", 3);
650         updateSeedlotNames();
651     }
653     /**
654      * Complete the seedlot name step
655      * - Make sure names are unique
656      * - Setup trait information
657      */
658     function completeNameSeedlots() {
660         // Make sure Seedlot names are unique
661         let unique = true;
662         let names = [];
663         for ( let i = 0; i < SEEDLOTS.length; i++ ) {
664             if ( names.includes(SEEDLOTS[i].name) ) {
665                 alert("Seedlot names must be unique - '" + SEEDLOTS[i].name + "' is used more than once");
666                 return;
667             }
668             names.push(SEEDLOTS[i].name);
669         }
671         // Set trait values
672         let html = "<option value=''>Choose trait...</option>";
673         for ( let i = 0; i < TRAITS.length; i++ ) {
674             html += "<option value='" + TRAITS[i].id + "'>" + TRAITS[i].name + "</option>";
675         }
676         jQuery("#create_seedlots_trial_contents_trait").html(html);
678         Workflow.complete('#create_seedlots_trial_complete_name_seedlots');
679         Workflow.focus("#create_seedlots_trial_workflow", 4);
680         updateSeedlotContents();
682     }
684     /**
685      * Complete the seedlot contents step
686      */
687     function completeSeedlotContents() {
688         jQuery("#create_seedlots_trial_breeding_program_id").val(BREEDING_PROGRAM_ID);
689         Workflow.complete('#create_seedlots_trial_complete_set_contents');
690         Workflow.focus("#create_seedlots_trial_workflow", 5);
691         updateSeedlotMetadata(true);
692     }
694     /**
695      * Complete the seedlot metadata step
696      */
697     function completeSeedlotMetadata() {
698         Workflow.complete('#create_seedlots_trial_complete_set_metadata');
699         Workflow.focus("#create_seedlots_trial_workflow", 6);
700         updateSeedlotDetails();
701     }
703     /**
704      * Complete the seedlot confirmation step
705      * - Start uploading Seedlots to DB
706      */
707     function completeSeedlotConfirmation() {
708         Workflow.complete('#create_seedlots_trial_complete_confirm_seedlots');
709         Workflow.focus("#create_seedlots_trial_workflow", 7);
710         startSeedlotUpload(displayUploadResults);
711     }
715     //
716     // HELPER FUNCTIONS
717     // 
720     /**
721      * Set the name of each seedlot - using the template and replacing the values
722      * then update the table displaying the results
723      */
724     function updateSeedlotNames() {
725         let trial_name = jQuery("#create_seedlots_trial_name").val();
726         let stock_type = jQuery("#create_seedlots_trial_stock_type").val();
727         let name_template = jQuery("#create_seedlots_trial_name_template").val();
728         if ( name_template === '{plot_name}' ) {
729             return alert("The seedlot name cannot be the same as the plot name, please add a prefix or suffix to make the name unique");
730         }
732         let html = "<thead></tr>";
733         if ( stock_type === 'accessions' ) html += "<th>Trial Name</th>";
734         html += "<th>Accession Name</th>";
735         if ( ["plots", "subplots", "plants"].includes(stock_type) ) html += "<th>Plot Number</th>";
736         if ( ["plots", "subplots", "plants"].includes(stock_type)  ) html += "<th>Rep</th>";
737         if ( ["plots", "subplots", "plants"].includes(stock_type)  ) html += "<th>Block</th>";
738         if ( stock_type === 'subplots' ) html += "<th>Subplot Name</th>";
739         if ( stock_type === 'subplots' ) html += "<th>Subplot Index</th>";
740         if ( stock_type === 'plants' ) html += "<th>Plant Name</th>";
741         if ( stock_type === 'plants' ) html += "<th>Plant Index</th>";
742         html += "<th>Seedlot Name</th>";
743         html += "</tr></thead>";
745         for ( let i = 0; i < SEEDLOTS.length; i++ ) {
746             index = i+1;
747             let name = name_template
748                 .replace("{trial_name}", trial_name)
749                 .replace("{accession_name}", SEEDLOTS[i].accession.name)
750                 .replace("{index}", index);
751             if ( SEEDLOTS[i].plot ) {
752                 name = name
753                     .replace("{plot_name}", SEEDLOTS[i].plot.name)
754                     .replace("{plot_number}", SEEDLOTS[i].plot.number)
755                     .replace("{rep}", SEEDLOTS[i].plot.rep)
756                     .replace("{block}", SEEDLOTS[i].plot.block);
757             }
758             if ( SEEDLOTS[i].subplot ) {
759                 name = name
760                     .replace("{subplot_name}", SEEDLOTS[i].subplot.name)
761                     .replace("{subplot_index}", SEEDLOTS[i].subplot.index);
762             }
763             if ( SEEDLOTS[i].plant ) {
764                 name = name
765                     .replace("{plant_name}", SEEDLOTS[i].plant.name)
766                     .replace("{plant_index}", SEEDLOTS[i].plant.index);
767             }
768             SEEDLOTS[i].name = name;
770             html += "<tr>";
771             if ( stock_type === 'accessions' ) html += "<td>" + trial_name + "</td>";
772             html += "<td>" + SEEDLOTS[i].accession.name + "</td>";
773             if ( ["plots", "subplots", "plants"].includes(stock_type) ) html += "<td>" + SEEDLOTS[i].plot.number + "</td>";
774             if ( ["plots", "subplots", "plants"].includes(stock_type) ) html += "<td>" + SEEDLOTS[i].plot.rep + "</td>";
775             if ( ["plots", "subplots", "plants"].includes(stock_type) ) html += "<td>" + SEEDLOTS[i].plot.block + "</td>";
776             if ( stock_type === 'subplots' ) html += "<td>" + SEEDLOTS[i].subplot.name + "</td>";
777             if ( stock_type === 'subplots' ) html += "<td>" + SEEDLOTS[i].subplot.index + "</td>";
778             if ( stock_type === 'plants' ) html += "<td>" + SEEDLOTS[i].plant.name + "</td>";
779             if ( stock_type === 'plants' ) html += "<td>" + SEEDLOTS[i].plant.index + "</td>";
780             html += "<td><input class='form-control create_seedlots_trial_name_seedlot' data-index='" + i + "' value='" + name + "'></td>";
781             html += "</tr>";
782         }
784         jQuery("#create_seedlots_trial_name_table").html(html);
785         jQuery("#create_seedlots_trial_complete_name_seedlots").attr("disabled", false);
786         jQuery("#create_seedlots_trial_name_update").attr("disabled", true);
787     }
789     /**
790      * Calculate the seedlot contents and update the table displaying the results
791      */
792     function updateSeedlotContents() {
793         let stock_type = jQuery("#create_seedlots_trial_stock_type").val();
794         let contents_type = jQuery("#create_seedlots_trial_contents_type").val();
795         let value = jQuery("#create_seedlots_trial_contents_value").val();
796         let constant = jQuery("#create_seedlots_trial_contents_constant").val();
797         let trait_id = jQuery("#create_seedlots_trial_contents_trait").val();
798         let formula = jQuery("#create_seedlots_trial_contents_computed_formula").val();
800         let html = "<thead></tr>";
801         html += "<th>Seedlot Name</th>";
802         html += "<th>Contents</th>";
803         html += "</tr></thead>";
805         for ( let i = 0; i < SEEDLOTS.length; i++ ) {
806             let contents = 0;
807             
808             // Set contents value
809             if ( value === 'constant' ) {
810                 contents = constant;
811             }
812             else if ( value === 'trait' && TRAIT_DATA ) {
813                 contents = _getTraitData(i, trait_id);
814             }
815             else if ( value === 'computed' && TRAIT_DATA && formula && formula !== "" ) {
816                 let seedlot_formula = formula;
817                 for ( let j = 0; j < TRAITS.length; j++ ) {
818                     let variable = TRAITS[j].variable;
819                     let variable_trait_id = TRAITS[j].id;
820                     if ( formula.includes('{' + variable + '}') ) {
821                         let variable_value = _getTraitData(i, variable_trait_id);
822                         seedlot_formula = seedlot_formula.replaceAll('{' + variable + '}', variable_value);
823                     }
824                 }
825                 try {
826                     let eval_value = eval(seedlot_formula);
827                     if ( eval_value ) contents = eval_value;
828                 } 
829                 catch (e) {}
830             }
831         
832             html += "<tr>";
833             html += "<td>" + SEEDLOTS[i].name + "</td>";
834             html += "<td><input class='form-control create_seedlots_trial_contents_seedlot' data-index='" + i + "' value='" + contents + "'></td>";
835             html +=" </tr>";
837             SEEDLOTS[i].contents = contents;
838         }
840         jQuery("#create_seedlots_trial_contents_table").html(html);
841         jQuery("#create_seedlots_trial_complete_computed_compute").attr("disabled", true);
842         jQuery("#create_seedlots_trial_complete_set_contents").attr("disabled", false);
845         /**
846          * Get the value of the specified trait for the specified seedlot
847          * If 'stock_type' == 'accessions', then the sum of the plots is returned
848          */
849         function _getTraitData(seedlot_index, trait_id) {
850             let contents = 0;
851             for ( let j = 0; j < TRAIT_DATA.length; j++ ) {
852                 if ( TRAIT_DATA[j].trait_id + '' === trait_id + '' ) {
853                 
854                     // Sum plots of the same accession
855                     if ( stock_type === 'accessions' ) {
856                         if ( SEEDLOTS[seedlot_index].accession.id === TRAIT_DATA[j].accession_id ) {
857                             try {
858                                 contents = contents + parseFloat(TRAIT_DATA[j].trait_value);
859                             }
860                             catch (e) {}
861                         }
862                     }
864                     // Use the plot value
865                     else {
866                         if ( SEEDLOTS[seedlot_index].plot.id === TRAIT_DATA[j].plot_id ) {
867                             contents = TRAIT_DATA[j].trait_value;
868                         }
869                     }
870                     
871                 }
872             }
873             return contents;
874         }
875     }
877     /**
878      * Set the seedlot metadata and update the table displaying the individual metadata
879      */
880     function updateSeedlotMetadata(quiet) {
881         let p_id = jQuery("#create_seedlots_trial_breeding_program_id").val();
882         let p_name = jQuery("#create_seedlots_trial_breeding_program_id option:selected").text();
883         let l = jQuery("#create_seedlots_trial_location").val();
884         let b = jQuery("#create_seedlots_trial_box_name").val();
885         let q = jQuery("#create_seedlots_trial_quality_issues").val();
886         let o = jQuery("#create_seedlots_trial_organization").val();
887         let t = jQuery("#create_seedlots_trial_timestamp").val();
888         let d = jQuery("#create_seedlots_trial_description").val();
890         // Check for required fields
891         if ( !p_id || p_id === '' ) {
892             if (!quiet) alert("Breeding Program is required");
893             return;
894         }
895         if ( !l || l === '' ) {
896             if (!quiet) alert("Location is required");
897             return;
898         }
899         if ( !b || b === '' ) {
900             if (!quiet) alert("Box Name is required");
901             return;
902         }
904         // Build Table
905         let html = "<thead><tr>";
906         html += "<th>Seedlot Name</th>";
907         html += "<th>Metadata</th>";
908         html += "</tr></thead>";
910         for ( let i = 0; i < SEEDLOTS.length; i++ ) {
911             let p_select = jQuery("#create_seedlots_trial_breeding_program_id").html();
913             html += "<tr>";
914             html += "<td>" + SEEDLOTS[i].name + "</td>";
915             html += "<td>";
916             html += "<strong>Breeding Program:</strong><select class='form-control  create_seedlots_trial_metadata_input_seedlot_breeding_program' data-index='" + i + "'>" + p_select + "</select><br />";
917             html += "<strong>Location:</strong><input class='form-control create_seedlots_trial_metadata_input_seedlot' data-prop='location' data-index='" + i + "' value='" + l + "'><br />";
918             html += "<strong>Box Name:</strong><input class='form-control create_seedlots_trial_metadata_input_seedlot' data-prop='box_name' data-index='" + i + "' value='" + b + "'><br />";
919             html += "<strong>Quality Issues:</strong><input class='form-control create_seedlots_trial_metadata_input_seedlot' data-prop='quality_issues' data-index='" + i + "' value='" + q + "'><br />";
920             html += "<strong>Organization:</strong><input class='form-control create_seedlots_trial_metadata_input_seedlot' data-prop='organization' data-index='" + i + "' value='" + o + "'><br />";
921             html += "<strong>Timestamp:</strong><input class='form-control create_seedlots_trial_metadata_input_seedlot' data-prop='timestamp' data-index='" + i + "' value='" + t + "'><br />";
922             html += "<strong>Description:</strong><input class='form-control create_seedlots_trial_metadata_input_seedlot' data-prop='description' data-index='" + i + "' value='" + d + "'><br />";
923             html += "</td>";
924             html += "</tr>";
926             SEEDLOTS[i].metadata = {
927                 breeding_program: {
928                     id: p_id,
929                     name: p_name,
930                 },
931                 location: l,
932                 box_name: b,
933                 quality_issues: q,
934                 organization: o,
935                 timestamp: t,
936                 description: d
937             }
938         }
940         jQuery("#create_seedlots_trial_metadata_table").html(html);
941         jQuery(".create_seedlots_trial_metadata_input_seedlot_breeding_program").val(p_id);
942         jQuery("#create_seedlots_trial_update_metadata").attr("disabled", true);
943         jQuery("#create_seedlots_trial_complete_set_metadata").attr("disabled", false);
944     }
947     /**
948      * Build the table of Seedlot details for the user to confirm
949      */
950     function updateSeedlotDetails() {
951         let stock_type = jQuery("#create_seedlots_trial_stock_type").val();
952         let contents_type = jQuery("#create_seedlots_trial_contents_type").val();
954         let html = "<thead><tr>";
955         html += "<th>Seedlot Name</th>";
956         html += "<th>Breeding Program</th>";
957         html += "<th>Source Accession</th>";
958         if ( stock_type === 'plots' ) html += "<th>Source Plot</th>";
959         if ( stock_type === 'subplots' ) html += "<th>Source Subplot</th>";
960         if ( stock_type === 'plants' ) html += "<th>Source Plant</th>";
961         if ( contents_type === 'amount' ) html += "<th>Contents (Amount)</th>";
962         if ( contents_type === 'weight' ) html += "<th>Contents (Weight)</th>";
963         html += "<th>Location</th>";
964         html += "<th>Box Name</th>";
965         html += "<th>Quality Issues</th>";
966         html += "<th>Organization</th>";
967         html += "<th>Timestamp</th>";
968         html += "<th>Description</th>";
969         html += "</tr></thead>";
971         for ( let i = 0; i < SEEDLOTS.length; i++ ) {
972             html += "<tr>";
973             html += "<td>" + SEEDLOTS[i].name + "</td>";
974             html += "<td>" + SEEDLOTS[i].metadata.breeding_program.name + "</td>";
975             html += "<td>" + SEEDLOTS[i].accession.name + "</td>";
976             if ( stock_type === 'plots' ) html += "<td>" + SEEDLOTS[i].plot.name + "</td>";
977             if ( stock_type === 'subplots' ) html += "<td>" + SEEDLOTS[i].subplot.name + "</td>";
978             if ( stock_type === 'plants' ) html += "<td>" + SEEDLOTS[i].plant.name + "</td>";
979             html += "<td>" + SEEDLOTS[i].contents + "</td>";
980             html += "<td>" + SEEDLOTS[i].metadata.location + "</td>";
981             html += "<td>" + SEEDLOTS[i].metadata.box_name + "</td>";
982             html += "<td>" + SEEDLOTS[i].metadata.quality_issues + "</td>";
983             html += "<td>" + SEEDLOTS[i].metadata.organization + "</td>";
984             html += "<td>" + SEEDLOTS[i].metadata.timestamp + "</td>";
985             html += "<td>" + SEEDLOTS[i].metadata.description + "</td>";
986             html += "</tr>";
987         }
989         jQuery("#create_seedlots_trial_confirm_table").html(html);
990     }
993     /**
994      * Upload each of the Seedlots to the Database in batches of up to 
995      * UPLOAD_BATCH_SIZE seedlots per batch
996      * @param {Function} callback Callback function(errors, success_count)
997      */
998     function startSeedlotUpload(callback) {
999         let stock_type = jQuery("#create_seedlots_trial_stock_type").val();
1000         let contents_type = jQuery("#create_seedlots_trial_contents_type").val();
1001         jQuery("#create_seedlots_trial_status").html("<strong>Creating Seedlots...</strong>");
1002         jQuery("#create_seedlots_trial_progress").css('width', '0%').attr('aria-valuenow', 0);
1003         jQuery("#create_seedlots_trial_results").empty();
1005         // Create Results table
1006         let html = "<thead><tr>";
1007         html += "<th>Seedlot</th>";
1008         html += "<th>Progress</th>";
1009         html += "</tr></thead>";
1010         html += "<tbody id='create_seedlots_trial_results_table_body'></tbody>";
1011         for ( let i = 0; i < SEEDLOTS.length; i++ ) {
1012             let status_id = "create_seedlots_trial_results_row_" + i;
1013             html += "<tr><td>" + SEEDLOTS[i].name + "</td><td id='" + status_id + "'>Pending...</td>";
1014             SEEDLOTS[i].status_id = status_id;
1015         }
1016         jQuery("#create_seedlots_trial_results_table").html(html);
1018         // Information to return
1019         let upload_count = 0;                                           // Count of successfully created/uploaded seedlots
1020         let errors = [];                                                // Array of errors encountered during upload
1022         // Set counts of seedlots and batches
1023         let seedlot_count = 0;                                          // Count of completed seedlots
1024         let seedlot_total = SEEDLOTS.length;                            // Total number of seedlots
1025         let seedlot_batch_count;                                        // Count of completed seedlots, in current batch
1026         let seedlot_batch_total;                                        // Total number of seedlots, in current batch
1027         let batch_count = 0                                             // Count of completed batches
1028         let batch_total = Math.ceil(seedlot_total/UPLOAD_BATCH_SIZE);   // Total number of batches
1030         // Start the first batch
1031         _uploadBatch();
1033         /**
1034          * Upload a batch of Seedlots (max set with UPLOAD_BATCH_SIZE)
1035          * - when the seedlot is complete, call _finishSeedlot()
1036          */
1037         function _uploadBatch() {
1038             let seedlot_batch_start = batch_count*UPLOAD_BATCH_SIZE;
1039             let seedlot_batch_end = seedlot_batch_start+UPLOAD_BATCH_SIZE;
1040             if ( seedlot_batch_end > seedlot_total ) seedlot_batch_end = seedlot_total;
1041             seedlot_batch_count = 0;
1042             seedlot_batch_total = seedlot_batch_end - seedlot_batch_start;
1043             for ( let seedlot_index = seedlot_batch_start; seedlot_index < seedlot_batch_end; seedlot_index++ ) {
1044                 _uploadSeedlot(seedlot_index, function() {
1045                     let p = ((seedlot_count+1)/seedlot_total)*100;
1046                     jQuery("#create_seedlots_trial_progress").css('width', p+'%').attr('aria-valuenow', p);
1047                     _finishSeedlot();
1048                 });
1049             }
1050         }
1052         /**
1053          * Upload the specified seedlot
1054          */
1055         function _uploadSeedlot(seedlot_index, callback) {
1056             let seedlot = SEEDLOTS[seedlot_index];
1057             _setStatusInfo(seedlot.status_id, "Creating...");
1059             // Set seedlot request data
1060             let data = {
1061                 'seedlot_name': seedlot.name,
1062                 'seedlot_location': seedlot.metadata.location,
1063                 'seedlot_box_name': seedlot.metadata.box_name,
1064                 'seedlot_accession_uniquename': seedlot.accession.name,
1065                 'seedlot_organization': seedlot.metadata.organization,
1066                 'seedlot_timestamp': seedlot.metadata.timestamp,
1067                 'seedlot_description': seedlot.metadata.description,
1068                 'seedlot_breeding_program_id': seedlot.metadata.breeding_program.id,
1069                 'seedlot_quality': seedlot.metadata.quality_issues,
1070                 'no_refresh': 1
1071             };
1072             if ( stock_type === 'plots' ) data['seedlot_source'] = seedlot.plot.name;
1073             if ( stock_type === 'subplots' ) data['seedlot_source'] = seedlot.subplot.name;
1074             if ( stock_type === 'plants' ) data['seedlot_source'] = seedlot.plant.name;
1075             if ( contents_type === 'amount' ) data['seedlot_amount'] = seedlot.contents;
1076             if ( contents_type === 'weight' ) data['seedlot_weight'] = seedlot.contents;
1078             jQuery.ajax({
1079                 url: '/ajax/breeders/seedlot-create',
1080                 data: data,
1081                 success: function(response) {
1082                     jQuery('#working_modal').modal('hide');
1083                     if (response.success === 1) {
1084                         upload_count++;
1085                         _setStatusComplete(seedlot.status_id);
1086                     }
1087                     if (response.error) {
1088                         errors.push({
1089                             index: seedlot_index,
1090                             seedlot: seedlot,
1091                             error: response.error
1092                         });
1093                         _setStatusError(seedlot.status_id, response.error);
1094                     }
1095                 },
1096                 error: function(response){
1097                     let msg = "AJAX error";
1098                     errors.push({
1099                         index: seedlot_index,
1100                         seedlot: seedlot,
1101                         error: msg
1102                     });
1103                     _setStatusError(seedlot.status_id, msg);
1104                 },
1105                 complete: function() {
1106                     return callback();
1107                 }
1108             });
1109         }
1111         /**
1112          * Display an info message for the specified seedlot
1113          */
1114         function _setStatusInfo(id, info) {
1115             jQuery("#" + id).html(info);
1116         }
1118         /**
1119          * Display a complete message for the specified seedlot
1120          */
1121         function _setStatusComplete(id) {
1122             jQuery("#" + id).html("<strong><span class='glyphicon glyphicon-ok-sign text-success'></span></strong>&nbsp;Seedlot Created!");
1123         }
1125         /**
1126          * Display an error message for the specified seedlot
1127          */
1128         function _setStatusError(id, error) {
1129             jQuery("#" + id).html("<strong><span class='glyphicon glyphicon-exclamation-sign text-danger'></span>&nbsp;ERROR:</strong> " + error);
1130         }
1132         /**
1133          * Finished uploading a seedlot
1134          * - when all of the batch's seedlots have been uploaded, call _finishBatch()
1135          */
1136         function _finishSeedlot() {
1137             seedlot_count++;
1138             seedlot_batch_count++;
1139             if ( seedlot_batch_count >= seedlot_batch_total ) {
1140                 _finishBatch();
1141             }
1142         }
1144         /**
1145          * Finished uploading a batch of seedlots
1146          * - when all of the batches are complete, refresh the matviews
1147          * - if there are remaining batches, start the next batch
1148          */
1149         function _finishBatch() {
1150             batch_count++;
1151             if ( batch_count >= batch_total ) {
1152                 jQuery.ajax({
1153                     url: '/ajax/breeder/refresh?matviews=stockprop'
1154                 });
1155                 if ( callback ) return callback(errors, upload_count);
1156             }
1157             else {
1158                 _uploadBatch();
1159             }
1160         }
1161     }
1163     /**
1164      * Display the status results message
1165      * @param {Object[]} errors List of upload errors
1166      * @param {int} upload_count Count of successfully uploaded seedlots
1167      */
1168     function displayUploadResults(errors, upload_count) {
1169         jQuery("#create_seedlots_trial_status").html("<strong>Upload Complete</strong>");
1170         jQuery("#create_seedlots_trial_progress").css('width', '100%').attr('aria-valuenow', 100);
1172         let html = "<br /><br />";
1173         if ( !errors || errors.length === 0 ) {
1174             html += "<div class='alert-success' role='alert'>";
1175             html += "<strong><span class='glyphicon glyphicon-ok-sign'></span></strong>&nbsp;";
1176             html += upload_count + " seedlots successfully created!";
1177             html += "</div>";
1178         }
1179         if ( errors && errors.length > 0 ) {
1180             html += "<div class='alert-danger' role='alert'>"
1181             html += "<strong><span class='glyphicon glyphicon-exclamation-sign'></span>&nbsp; " + errors.length + " errors encountered!<br />";
1182             html += "See below for which seedlots failed and more details<br />";
1183             if ( upload_count > 0 ) html += "<br /><strong><span class='glyphicon glyphicon-ok-sign'></span>&nbsp; " + upload_count + " seedlots successfully created";
1184             html += "</div>";
1185         }
1187         jQuery("#create_seedlots_trial_results").html(html);
1188     }
1191     //
1192     // DB QUERY FUNCTIONS
1193     //
1195     /**
1196      * Query the database for the trial id (by name)
1197      * @param {string} trial_name Trial Name
1198      * @param {Function} callback Callback function(trial_id)
1199      */
1200     function getTrialID(trial_name, callback) {
1201         let trial_id;
1202         jQuery.ajax({
1203             type: "GET",
1204             url: '/ajax/breeders/trial_lookup',
1205             data: {
1206                 name: trial_name
1207             },
1208             success: function(response) {
1209                 if (response.error) {
1210                     alert(response.error);
1211                 }
1212                 else if ( response.trial_id ) {
1213                     trial_id = response.trial_id;
1214                 }
1215                 else {
1216                     alert('Could not lookup trial');
1217                 }
1218             },
1219             error: function(response){
1220                 alert('Could not lookup trial');
1221             },
1222             complete: function() {
1223                 return callback(trial_id);
1224             }
1225         });
1226     }
1228     /**
1229      * Get required metadata for the Trial:
1230      * - Error message
1231      * - Breeding Program ID
1232      * - Accessions
1233      * - Plots
1234      * - Subplots
1235      * - Plants
1236      * - Traits
1237      * @param {int} trial_id Trial ID
1238      * @param {function} callback Callback function(error, breeding_program_id, accessions, plots, subplots, plants, traits)
1239      */
1240     function getTrialDetails(trial_id, callback) {
1241         let count = 0;
1242         let total = 4;
1244         let breeding_program_id, accessions, plots, subplots, plants, traits;
1245         let error;
1247         _getBreedingProgram();
1248         _getAccessions();
1249         _getPlots();
1250         _getTraits();
1252         function _getBreedingProgram() {
1253             jQuery.ajax({
1254                 type: "GET",
1255                 url: '/breeders/programs_by_trial/' + trial_id,
1256                 success: function(response) {
1257                     if (response.error) {
1258                         alert(response.error);
1259                     }
1260                     else if ( response.projects ) {
1261                         breeding_program_id = response.projects[0][0];
1262                     }
1263                     else {
1264                         alert('Could not lookup trial breeding program');
1265                     }
1266                 },
1267                 error: function(response){
1268                     alert('Could not lookup trial breeding program');
1269                 },
1270                 complete: function() {
1271                     _finish();
1272                 }
1273             });
1274         }
1276         function _getAccessions() {
1277             jQuery.ajax({
1278                 type: "GET",
1279                 url: '/ajax/breeders/trial/' + trial_id + '/accessions',
1280                 success: function(response) {
1281                     if (response.error) {
1282                         alert(response.error);
1283                     }
1284                     else if ( response.accessions ) {
1285                         accessions = [];
1286                         for ( let i = 0; i < response.accessions[0].length; i++ ) {
1287                             if ( response.accessions[0][i].stock_type !== 'accession' ) {
1288                                 error = `This tool only supports trials with plots that contain accessions.  This trial has one or more plots that contain a ${response.accessions[0][i].stock_type}.`;
1289                             }
1290                             accessions.push({
1291                                 id: response.accessions[0][i].stock_id, 
1292                                 name: response.accessions[0][i].accession_name
1293                             });
1294                         }
1295                     }
1296                     else {
1297                         alert('Could not lookup trial accessions');
1298                     }
1299                 },
1300                 error: function(response){
1301                     alert('Could not lookup trial accessions');
1302                 },
1303                 complete: function() {
1304                     _finish();
1305                 }
1306             });
1307         }
1309         function _getPlots() {
1310             jQuery.ajax({
1311                 type: "GET",
1312                 url: '/ajax/breeders/trial/' + trial_id + '/layout',
1313                 success: function(response) {
1314                     if (response.error) {
1315                         alert(response.error);
1316                     }
1317                     else if ( response.design ) {
1318                         plots = [];
1319                         subplots = [];
1320                         plants = [];
1321                         for (const [plot, info] of Object.entries(response.design)) {
1322                             plots.push({
1323                                 plot: {
1324                                     id: info.plot_id,
1325                                     number: info.plot_number,
1326                                     name: info.plot_name,
1327                                     rep: info.rep_number,
1328                                     block: info.block_number
1329                                 },
1330                                 accession: {
1331                                     name: info.accession_name,
1332                                     id: info.accession_id
1333                                 }
1334                             });
1335                             if ( info.subplot_names && info.subplot_ids && info.subplot_index_numbers ) {
1336                                 for ( let i = 0; i < info.subplot_names.length; i++ ) {
1337                                     subplots.push({
1338                                         subplot: {
1339                                             name: info.subplot_names[i],
1340                                             id: info.subplot_ids[i],
1341                                             index: info.subplot_index_numbers[i]
1342                                         },
1343                                         plot: {
1344                                             id: info.plot_id,
1345                                             number: info.plot_number,
1346                                             name: info.plot_name,
1347                                             rep: info.rep_number,
1348                                             block: info.block_number
1349                                         },
1350                                         accession: {
1351                                             name: info.accession_name,
1352                                             id: info.accession_id
1353                                         }
1354                                     });
1355                                 }
1356                             }
1357                             if ( info.plant_names && info.plant_ids && info.plant_index_numbers ) {
1358                                 for ( let i = 0; i < info.plant_names.length; i++ ) {
1359                                     plants.push({
1360                                         plant: {
1361                                             name: info.plant_names[i],
1362                                             id: info.plant_ids[i],
1363                                             index: info.plant_index_numbers[i]
1364                                         },
1365                                         plot: {
1366                                             id: info.plot_id,
1367                                             number: info.plot_number,
1368                                             name: info.plot_name,
1369                                             rep: info.rep_number,
1370                                             block: info.block_number
1371                                         },
1372                                         accession: {
1373                                             name: info.accession_name,
1374                                             id: info.accession_id
1375                                         }
1376                                     });
1377                                 }
1378                             }
1379                         }
1380                     }
1381                     else {
1382                         alert('Could not lookup trial plots');
1383                     }
1384                 },
1385                 error: function(response){
1386                     alert('Could not lookup trial plots');
1387                 },
1388                 complete: function() {
1389                     _finish();
1390                 }
1391             });
1392         }
1394         function _getTraits() {
1395             jQuery.ajax({
1396                 type: "GET",
1397                 url: '/ajax/breeders/trial/' + trial_id + '/traits_assayed?stock_type=plot',
1398                 success: function(response) {
1399                     if (response.error) {
1400                         alert(response.error);
1401                     }
1402                     else if ( response.traits_assayed ) {
1403                         traits = [];
1404                         let html = "";
1405                         for ( let i = 0; i < response.traits_assayed[0].length; i++ ) {
1406                             let id = response.traits_assayed[0][i][0];
1407                             let name = response.traits_assayed[0][i][1];
1408                             let variable = "trait_" + parseInt(i+1);
1409                             traits.push({
1410                                 id: id,
1411                                 name: name,
1412                                 variable: variable
1413                             });
1414                             html += "<tr><td><code>{" + variable + "}</code></td>";
1415                             html += "<td>" + name + "</td></tr>";
1416                         }
1417                         jQuery("#create_seedlots_trial_contents_computed_table").html(html);
1418                     }
1419                     else {
1420                         alert('Could not lookup trial traits');
1421                     }
1422                 },
1423                 error: function(response){
1424                     alert('Could not lookup trial traits');
1425                 },
1426                 complete: function() {
1427                     _finish();
1428                 }
1429             });
1430         }
1431         
1432         function _finish() {
1433             count++;
1434             if ( count >= total ) {
1435                 return callback(error, breeding_program_id, accessions, plots, subplots, plants, traits);
1436             }
1437         }
1438     }
1440     /**
1441      * Set the plot-level trait data for all of the traits
1442      * @param {function} callback Callback function()
1443      */
1444     function getTraitData(callback) {
1445         TRAIT_DATA = [];
1446         
1447         let count = 0;
1448         let total = TRAITS.length;
1449         if ( total > 0 ) {
1450             for ( let i = 0; i < TRAITS.length; i++ ) {
1451                 _getTraitData(TRAITS[i].id, TRAITS[i].name);
1452             }
1453         }
1454         else {
1455             _finish();
1456         }
1458         function _getTraitData(trait_id, trait_name) {
1459             jQuery.ajax({
1460                 type: "GET",
1461                 url: '/ajax/breeders/trial/' + TRIAL_ID + '/trait_phenotypes/?trait=' + trait_id + '&display=plot',
1462                 success: function(response) {
1463                     if (response.error) {
1464                         alert(response.error);
1465                     }
1466                     else if ( response.status && response.status === 'success' && response.data ) {
1467                         _parseTraitData(response.data, trait_id, trait_name);
1468                     }
1469                     else {
1470                         alert('Could not get trait data');
1471                     }
1472                 },
1473                 error: function(response){
1474                     alert('Could not get trait data');
1475                 },
1476                 complete: function() {
1477                     _finish();
1478                 }
1479             });
1480         }
1482         function _parseTraitData(data, trait_id, trait_name) {
1483             let accession_id_col, plot_id_col, trait_col;
1484             let headers = data[0];
1485             for ( let i = 0; i < headers.length; i++ ) {
1486                 if ( headers[i] === 'germplasmDbId' ) accession_id_col = i;
1487                 if ( headers[i] === 'observationUnitDbId' ) plot_id_col = i;
1488                 if ( headers[i] === trait_name ) trait_col = i;
1489             }
1490             for ( let i = 1; i < data.length; i++ ) {
1491                 TRAIT_DATA.push({
1492                     accession_id: data[i][accession_id_col],
1493                     plot_id: data[i][plot_id_col],
1494                     trait_id: trait_id,
1495                     trait_value: data[i][trait_col]
1496                 });
1497             }
1498         }
1500         function _finish() {
1501             count++;
1502             if ( count >= total ) {
1503                 return callback();
1504             }
1505         }
1506     }
1507 </script>
1509 <style>
1510     .create_seedlots_trial_subplots_available, .create_seedlots_trial_plants_available {
1511         display: none;
1512     }
1513     .alert-success, .alert-danger {
1514         padding: 15px !important;
1515         margin-bottom: 20px !important;
1516         border: 1px solid transparent !important;
1517         border-radius: 4px !important;
1518     }
1519 </style>