seedlot upload with accession synonyms. seedlot upload works to update existing seedlots
[sgn.git] / lib / SGN / Controller / BreedersToolbox.pm
blob3f23ee5b0efa532dc2a099f54743c86e5898c4f4
2 package SGN::Controller::BreedersToolbox;
4 use Moose;
6 use Data::Dumper;
7 use SGN::Controller::AJAX::List;
8 use CXGN::List::Transform;
9 use CXGN::BreedersToolbox::Projects;
10 use CXGN::BreedersToolbox::Accessions;
11 use SGN::Model::Cvterm;
12 use URI::FromHash 'uri';
13 use Spreadsheet::WriteExcel;
14 use Spreadsheet::Read;
15 use File::Slurp qw | read_file |;
16 use File::Temp;
17 use CXGN::Trial::TrialLayout;
18 use Try::Tiny;
19 use File::Basename qw | basename dirname|;
20 use File::Spec::Functions;
21 use CXGN::People::Roles;
22 use CXGN::Trial::TrialLayout;
25 BEGIN { extends 'Catalyst::Controller'; }
27 sub manage_breeding_programs : Path("/breeders/manage_programs") :Args(0) {
28 my $self = shift;
29 my $c = shift;
31 if (!$c->user()) {
33 # redirect to login page
34 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
35 return;
38 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
40 my $projects = CXGN::BreedersToolbox::Projects->new( { schema=> $schema } );
42 my $breeding_programs = $projects->get_breeding_programs();
44 $c->stash->{breeding_programs} = $breeding_programs;
45 $c->stash->{user} = $c->user();
47 $c->stash->{template} = '/breeders_toolbox/breeding_programs.mas';
52 sub manage_trials : Path("/breeders/trials") Args(0) {
53 my $self = shift;
54 my $c = shift;
56 if (!$c->user()) {
58 # redirect to login page
59 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
60 return;
63 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
65 my $projects = CXGN::BreedersToolbox::Projects->new( { schema=> $schema } );
67 my $breeding_programs = $projects->get_breeding_programs();
69 # use get_all_locations, as other calls for locations can be slow
71 $c->stash->{locations} = $projects->get_all_locations();
73 $c->stash->{breeding_programs} = $breeding_programs;
75 $c->stash->{template} = '/breeders_toolbox/manage_projects.mas';
78 sub manage_accessions : Path("/breeders/accessions") Args(0) {
79 my $self = shift;
80 my $c = shift;
81 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
82 my $list_id = $c->req->param('list_id') || ''; #If a list_id is given in the URL, then the add accessions process will automatically begin with that list.
84 if (!$c->user()) {
85 # redirect to login page
87 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
88 return;
91 my $ac = CXGN::BreedersToolbox::Accessions->new( { schema=>$schema });
93 my $accessions = $ac->get_all_accessions($c);
94 # my $populations = $ac->get_all_populations($c);
96 my @editable_stock_props = split ',', $c->config->{editable_stock_props};
97 my %editable_stock_props = map { $_=>1 } @editable_stock_props;
99 $c->stash->{accessions} = $accessions;
100 $c->stash->{list_id} = $list_id;
101 #$c->stash->{population_groups} = $populations;
102 $c->stash->{preferred_species} = $c->config->{preferred_species};
103 $c->stash->{editable_stock_props} = \%editable_stock_props;
104 $c->stash->{template} = '/breeders_toolbox/manage_accessions.mas';
107 sub manage_roles : Path("/breeders/manage_roles") Args(0) {
108 my $self = shift;
109 my $c = shift;
111 if (!$c->user()) {
112 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
113 return;
116 $c->stash->{is_curator} = $c->user->check_roles("curator");
118 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
119 my $person_roles = CXGN::People::Roles->new({ bcs_schema=>$schema });
120 my $breeding_programs = $person_roles->get_breeding_program_roles();
122 $c->stash->{roles} = $breeding_programs;
123 $c->stash->{template} = '/breeders_toolbox/manage_roles.mas';
127 sub manage_locations : Path("/breeders/locations") Args(0) {
128 my $self = shift;
129 my $c = shift;
131 if (!$c->user()) {
133 # redirect to login page
135 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
136 return;
139 $c->assets->include('/static/css/leaflet.css');
140 $c->assets->include('/static/css/leaflet.extra-markers.min.css');
141 $c->assets->include('/static/css/esri-leaflet-geocoder.css');
143 $c->stash->{user_id} = $c->user()->get_object()->get_sp_person_id();
145 $c->stash->{template} = '/breeders_toolbox/manage_locations.mas';
148 sub manage_nurseries : Path("/breeders/nurseries") Args(0) {
149 my $self = shift;
150 my $c = shift;
152 if (!$c->user()) {
154 # redirect to login page
156 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
157 return;
159 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
160 my $bp = CXGN::BreedersToolbox::Projects->new( { schema=>$schema });
161 my $breeding_programs = $bp->get_breeding_programs();
163 $c->stash->{user_id} = $c->user()->get_object()->get_sp_person_id();
165 $c->stash->{locations} = $bp->get_all_locations($c);
167 #$c->stash->{projects} = $self->get_projects($c);
169 $c->stash->{programs} = $breeding_programs;
171 $c->stash->{roles} = $c->user()->roles();
173 $c->stash->{nurseries} = $self->get_nurseries($c);
175 $c->stash->{template} = '/breeders_toolbox/manage_nurseries.mas';
179 sub manage_crosses : Path("/breeders/crosses") Args(0) {
180 my $self = shift;
181 my $c = shift;
183 if (!$c->user()) {
185 # redirect to login page
187 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
188 return;
190 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
191 my $bp = CXGN::BreedersToolbox::Projects->new( { schema=>$schema });
192 my $breeding_programs = $bp->get_breeding_programs();
194 my $crossingtrial = CXGN::BreedersToolbox::Projects->new( { schema=>$schema });
195 my $crossing_trials = $crossingtrial->get_crossing_trials();
197 $c->stash->{user_id} = $c->user()->get_object()->get_sp_person_id();
199 $c->stash->{locations} = $bp->get_all_locations($c);
201 $c->stash->{programs} = $breeding_programs;
203 $c->stash->{crossing_trials} = $crossing_trials;
205 $c->stash->{roles} = $c->user()->roles();
207 $c->stash->{cross_populations} = $self->get_crosses($c);
209 $c->stash->{template} = '/breeders_toolbox/manage_crosses.mas';
213 sub manage_phenotyping :Path("/breeders/phenotyping") Args(0) {
214 my $self =shift;
215 my $c = shift;
217 if (!$c->user()) {
218 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
219 return;
222 my $data = $self->get_phenotyping_data($c);
224 $c->stash->{phenotype_files} = $data->{phenotype_files};
225 $c->stash->{deleted_phenotype_files} = $data->{deleted_phenotype_files};
227 $c->stash->{template} = '/breeders_toolbox/manage_phenotyping.mas';
231 sub manage_upload :Path("/breeders/upload") Args(0) {
232 my $self =shift;
233 my $c = shift;
234 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
236 if (!$c->user()) {
237 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
238 return;
241 my $projects = CXGN::BreedersToolbox::Projects->new( { schema=> $schema } );
242 my $breeding_programs = $projects->get_breeding_programs();
243 $c->stash->{locations} = $projects->get_all_locations();
244 $c->stash->{breeding_programs} = $breeding_programs;
245 $c->stash->{timestamp} = localtime;
246 $c->stash->{preferred_species} = $c->config->{preferred_species};
247 $c->stash->{template} = '/breeders_toolbox/manage_upload.mas';
250 sub manage_plot_phenotyping :Path("/breeders/plot_phenotyping") Args(0) {
251 my $self =shift;
252 my $c = shift;
253 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
254 my $stock_id = $c->req->param('stock_id');
256 if (!$c->user()) {
257 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
258 return;
260 my $stock = $schema->resultset("Stock::Stock")->find( { stock_id=>$stock_id })->uniquename();
262 $c->stash->{plot_name} = $stock;
263 $c->stash->{stock_id} = $stock_id;
264 $c->stash->{template} = '/breeders_toolbox/manage_plot_phenotyping.mas';
268 sub manage_trial_phenotyping :Path("/breeders/trial_phenotyping") Args(0) {
269 my $self =shift;
270 my $c = shift;
271 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
272 my $trial_id = $c->req->param('trial_id');
274 if (!$c->user()) {
275 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
276 return;
278 my $project_name = $schema->resultset("Project::Project")->find( { project_id=>$trial_id })->name();
280 $c->stash->{trial_name} = $project_name;
281 $c->stash->{trial_id} = $trial_id;
282 $c->stash->{template} = '/breeders_toolbox/manage_trial_phenotyping.mas';
285 sub manage_odk_data_collection :Path("/breeders/odk") Args(0) {
286 my $self =shift;
287 my $c = shift;
288 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
290 if (!$c->user()) {
291 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
292 return;
294 $c->stash->{odk_crossing_data_service_name} = $c->config->{odk_crossing_data_service_name};
295 $c->stash->{odk_crossing_data_service_url} = $c->config->{odk_crossing_data_service_url};
296 $c->stash->{odk_crossing_data_form_name} = $c->config->{odk_crossing_data_form_name};
297 $c->stash->{odk_crossing_data_test_form_name} = $c->config->{odk_crossing_data_test_form_name};
298 $c->stash->{odk_phenotyping_data_service_name} = $c->config->{odk_phenotyping_data_service_name};
299 $c->stash->{odk_phenotyping_data_service_url} = $c->config->{odk_phenotyping_data_service_url};
300 $c->stash->{template} = '/breeders_toolbox/manage_odk_data_collection.mas';
303 sub manage_phenotyping_download : Path("/breeders/phenotyping/download") Args(1) {
304 my $self =shift;
305 my $c = shift;
306 my $file_id = shift;
308 my $metadata_schema = $c->dbic_schema('CXGN::Metadata::Schema');
309 my $file_row = $metadata_schema->resultset("MdFiles")->find({file_id => $file_id});
310 my $file_destination = catfile($file_row->dirname, $file_row->basename);
311 #print STDERR "\n\n\nfile name:".$file_row->basename."\n";
312 my $contents = read_file($file_destination);
313 my $file_name = $file_row->basename;
314 $c->res->content_type('Application/trt');
315 $c->res->header('Content-Disposition', qq[attachment; filename="$file_name"]);
316 $c->res->body($contents);
319 sub manage_phenotyping_view : Path("/breeders/phenotyping/view") Args(1) {
320 my $self =shift;
321 my $c = shift;
322 my $file_id = shift;
324 my $metadata_schema = $c->dbic_schema('CXGN::Metadata::Schema');
325 my $file_row = $metadata_schema->resultset("MdFiles")->find({file_id => $file_id});
326 my $file_destination = catfile($file_row->dirname, $file_row->basename);
327 #print STDERR "\n\n\nfile name:".$file_row->basename."\n";
328 my @contents = ReadData ($file_destination);
329 #print STDERR Dumper \@contents;
330 my $file_name = $file_row->basename;
331 $c->stash->{file_content} = \@contents;
332 $c->stash->{filename} = $file_name;
333 $c->stash->{template} = '/breeders_toolbox/view_file.mas';
336 sub make_cross_form :Path("/stock/cross/new") :Args(0) {
337 my ($self, $c) = @_;
338 $c->stash->{template} = '/breeders_toolbox/new_cross.mas';
339 if ($c->user()) {
340 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
341 # get projects
342 my @rows = $schema->resultset('Project::Project')->all();
343 my @projects = ();
344 foreach my $row (@rows) {
345 push @projects, [ $row->project_id, $row->name, $row->description ];
347 $c->stash->{project_list} = \@projects;
348 @rows = $schema->resultset('NaturalDiversity::NdGeolocation')->all();
349 my @locations = ();
350 foreach my $row (@rows) {
351 push @locations, [ $row->nd_geolocation_id,$row->description ];
353 $c->stash->{locations} = \@locations;
356 else {
357 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
358 return;
363 sub make_cross :Path("/stock/cross/generate") :Args(0) {
364 my ($self, $c) = @_;
365 $c->stash->{template} = '/breeders_toolbox/progeny_from_crosses.mas';
366 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
367 my $cross_name = $c->req->param('cross_name');
368 $c->stash->{cross_name} = $cross_name;
369 my $trial_id = $c->req->param('trial_id');
370 $c->stash->{trial_id} = $trial_id;
371 #my $location = $c->req->param('location_id');
372 my $maternal = $c->req->param('maternal');
373 my $paternal = $c->req->param('paternal');
374 my $prefix = $c->req->param('prefix');
375 my $suffix = $c->req->param('suffix');
376 my $progeny_number = $c->req->param('progeny_number');
377 my $visible_to_role = $c->req->param('visible_to_role');
379 if (! $c->user()) { # redirect
380 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
381 return;
385 #check that progeny number is an integer less than maximum allowed
386 my $maximum_progeny_number = 1000;
387 if ((! $progeny_number =~ m/^\d+$/) or ($progeny_number > $maximum_progeny_number)){
388 #redirect to error page?
389 return;
392 #check that parent names are not blank
393 if ($maternal eq "" or $paternal eq "") {
394 return;
397 #check that parents exist in the database
398 if (! $schema->resultset("Stock::Stock")->find({name=>$maternal,})){
399 return;
401 if (! $schema->resultset("Stock::Stock")->find({name=>$paternal,})){
402 return;
405 #check that cross name does not already exist
406 if ($schema->resultset("Stock::Stock")->find({name=>$cross_name})){
407 return;
410 #check that progeny do not already exist
411 if ($schema->resultset("Stock::Stock")->find({name=>$prefix.$cross_name.$suffix."-1",})){
412 return;
415 my $organism = $schema->resultset("Organism::Organism")->find_or_create(
417 genus => 'Manihot',
418 species => 'Manihot esculenta',
419 } );
420 my $organism_id = $organism->organism_id();
422 my $accession_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'accession', 'stock_type');
424 my $population_cvterm = $schema->resultset("Cv::Cvterm")->find(
425 { name => 'population',
429 my $female_parent_stock = $schema->resultset("Stock::Stock")->find(
430 { name => $maternal,
431 } );
433 my $male_parent_stock = $schema->resultset("Stock::Stock")->find(
434 { name => $paternal,
435 } );
437 my $population_stock = $schema->resultset("Stock::Stock")->find_or_create(
438 { organism_id => $organism_id,
439 name => $cross_name,
440 uniquename => $cross_name,
441 type_id => $population_cvterm->cvterm_id,
442 } );
443 my $female_parent = SGN::Model::Cvterm->get_cvterm_row($schema, 'female_parent', 'stock_relationship');
445 my $male_parent = SGN::Model::Cvterm->get_cvterm_row($schema, 'male_parent', 'stock_relationship');
448 my $population_members = SGN::Model::Cvterm->get_cvterm_row($schema, 'cross_relationship','stock_relationship');
451 my $visible_to_role_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'visible_to_role', 'local');
453 my $increment = 1;
454 while ($increment < $progeny_number + 1) {
455 $increment = sprintf "%03d", $increment;
456 my $stock_name = $prefix.$cross_name."_".$increment.$suffix;
457 my $accession_stock = $schema->resultset("Stock::Stock")->create(
458 { organism_id => $organism_id,
459 name => $stock_name,
460 uniquename => $stock_name,
461 type_id => $accession_cvterm->cvterm_id,
462 } );
463 $accession_stock->find_or_create_related('stock_relationship_objects', {
464 type_id => $female_parent->cvterm_id(),
465 object_id => $accession_stock->stock_id(),
466 subject_id => $female_parent_stock->stock_id(),
467 } );
468 $accession_stock->find_or_create_related('stock_relationship_objects', {
469 type_id => $male_parent->cvterm_id(),
470 object_id => $accession_stock->stock_id(),
471 subject_id => $male_parent_stock->stock_id(),
472 } );
473 $accession_stock->find_or_create_related('stock_relationship_objects', {
474 type_id => $population_members->cvterm_id(),
475 object_id => $accession_stock->stock_id(),
476 subject_id => $population_stock->stock_id(),
477 } );
478 if ($visible_to_role ne "") {
479 my $accession_stock_prop = $schema->resultset("Stock::Stockprop")->find_or_create(
480 { type_id =>$visible_to_role_cvterm->cvterm_id(),
481 value => $visible_to_role,
482 stock_id => $accession_stock->stock_id()
485 $increment++;
488 if ($@) {
492 sub selection_index : Path("/selection/index") :Args(0) {
493 my $self = shift;
494 my $c = shift;
496 if (!$c->user()) {
498 # redirect to login page
499 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
500 return;
503 # my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
505 # my $projects = CXGN::BreedersToolbox::Projects->new( { schema=> $schema } );
507 # my $breeding_programs = $projects->get_breeding_programs();
509 # $c->stash->{breeding_programs} = $breeding_programs;
510 $c->stash->{user} = $c->user();
512 $c->stash->{template} = '/breeders_toolbox/selection_index.mas';
518 sub breeder_home :Path("/breeders/home") Args(0) {
519 my ($self , $c) = @_;
522 if (!$c->user()) {
524 # redirect to login page
525 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
526 return;
529 # my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
530 # my $bp = CXGN::BreedersToolbox::Projects->new( { schema=>$schema });
531 # my $breeding_programs = $bp->get_breeding_programs();
533 # $c->stash->{programs} = $breeding_programs;
534 # $c->stash->{breeding_programs} = $breeding_programs;
536 # # my $locations_by_breeding_program;
537 # # foreach my $b (@$breeding_programs) {
538 # # $locations_by_breeding_program->{$b->[1]} = $bp->get_locations_by_breeding_program($b->[0]);
539 # # }
540 # # $locations_by_breeding_program->{'Other'} = $bp->get_locations_by_breeding_program();
542 # $c->stash->{locations_by_breeding_program} = ""; #$locations_by_breeding_program;
544 # # get roles
546 # my @roles = $c->user->roles();
547 # $c->stash->{roles}=\@roles;
549 # $c->stash->{cross_populations} = $self->get_crosses($c);
551 # $c->stash->{stockrelationships} = $self->get_stock_relationships($c);
553 # my $locations = $bp->get_locations($c);
555 # $c->stash->{locations} = $locations;
556 # # get uploaded phenotype files
559 # my $data = $self->get_phenotyping_data($c);
561 # $c->stash->{phenotype_files} = $data->{file_info};
562 # $c->stash->{deleted_phenotype_files} = $data->{deleted_file_info};
565 $c->stash->{template} = '/breeders_toolbox/home.mas';
569 sub breeder_search : Path('/breeders/search/') :Args(0) {
570 my ($self, $c) = @_;
571 $c->stash->{dataset_id} = $c->req->param('dataset_id');
572 $c->stash->{template} = '/breeders_toolbox/breeder_search_page.mas';
577 sub get_crosses : Private {
578 my $self = shift;
579 my $c = shift;
581 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
583 # get crosses
585 my $stock_type_cv = $schema->resultset("Cv::Cv")->find( {name=>'stock_type'});
586 my $cross_cvterm = $schema->resultset("Cv::Cvterm")->find(
587 { name => 'cross',
588 cv_id => $stock_type_cv->cv_id(),
590 my @cross_populations = ();
592 if ($cross_cvterm) {
594 my @cross_population_stocks = $schema->resultset("Stock::Stock")->search(
595 { type_id => $cross_cvterm->cvterm_id, is_obsolete => 'f'
596 } );
597 foreach my $cross_pop (@cross_population_stocks) {
598 push @cross_populations, [$cross_pop->name,$cross_pop->stock_id];
601 return \@cross_populations;
606 sub get_phenotyping_data : Private {
607 my $self = shift;
608 my $c = shift;
610 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
612 my $file_info = [];
613 my $deleted_file_info = [];
615 my $metadata_rs = $metadata_schema->resultset("MdMetadata")->search( { create_person_id => $c->user()->get_object->get_sp_person_id() }, { order_by => 'create_date' } );
617 print STDERR "RETRIEVED ".$metadata_rs->count()." METADATA ENTRIES...\n";
619 while (my $md_row = ($metadata_rs->next())) {
620 my $file_rs = $metadata_schema->resultset("MdFiles")->search( { metadata_id => $md_row->metadata_id(), filetype => {'!=' => 'document_browser'} } );
622 if (!$md_row->obsolete) {
623 while (my $file_row = $file_rs->next()) {
624 push @$file_info, { file_id => $file_row->file_id(),
625 basename => $file_row->basename,
626 dirname => $file_row->dirname,
627 file_type => $file_row->filetype,
628 md5checksum => $file_row->md5checksum,
629 create_date => $md_row->create_date,
633 else {
634 while (my $file_row = $file_rs->next()) {
635 push @$deleted_file_info, { file_id => $file_row->file_id(),
636 basename => $file_row->basename,
637 dirname => $file_row->dirname,
638 file_type => $file_row->filetype,
639 md5checksum => $file_row->md5checksum,
640 create_date => $md_row->create_date,
646 my $data = { phenotype_files => $file_info,
647 deleted_phenotype_files => $deleted_file_info,
649 return $data;
654 sub manage_genotyping : Path("/breeders/genotyping") Args(0) {
655 my $self = shift;
656 my $c = shift;
658 if (!$c->user()) {
659 # redirect to login page
660 $c->res->redirect( uri( path => '/solpeople/login.pl', query => { goto_url => $c->req->uri->path_query } ) );
661 return;
664 my $schema = $c->dbic_schema('Bio::Chado::Schema');
666 my $projects = CXGN::BreedersToolbox::Projects->new( { schema=> $schema } );
668 my $breeding_programs = $projects->get_breeding_programs();
670 my %genotyping_trials_by_breeding_project = ();
672 foreach my $bp (@$breeding_programs) {
673 $genotyping_trials_by_breeding_project{$bp->[1]}= $projects->get_genotyping_trials_by_breeding_program($bp->[0]);
676 $genotyping_trials_by_breeding_project{'Other'} = $projects->get_genotyping_trials_by_breeding_program();
678 $c->stash->{locations} = $projects->get_all_locations($c);
680 $c->stash->{genotyping_trials_by_breeding_project} = \%genotyping_trials_by_breeding_project;
682 $c->stash->{breeding_programs} = $breeding_programs;
684 $c->stash->{template} = '/breeders_toolbox/manage_genotyping.mas';