create greenhouse with plots and plants. download layout works
[sgn.git] / lib / SGN / Controller / AJAX / Trial.pm
blobac1247098c82a18f8e10f6e0ab07588f79a09bf3
2 =head1 NAME
4 SGN::Controller::AJAX::Trial - a REST controller class to provide the
5 backend for adding trials and viewing trials
7 =head1 DESCRIPTION
9 Creating, viewing and deleting trials
11 =head1 AUTHOR
13 Jeremy Edwards <jde22@cornell.edu>
15 Deletion by Lukas
17 =cut
19 package SGN::Controller::AJAX::Trial;
21 use Moose;
22 use Try::Tiny;
23 use Scalar::Util qw(looks_like_number);
24 use DateTime;
25 use File::Basename qw | basename dirname|;
26 use File::Copy;
27 use File::Slurp;
28 use File::Spec::Functions;
29 use Digest::MD5;
30 use List::MoreUtils qw /any /;
31 use Data::Dumper;
32 use CXGN::Trial;
33 use CXGN::Trial::TrialDesign;
34 use CXGN::Trial::TrialCreate;
35 use JSON -support_by_pp;
36 use SGN::View::Trial qw/design_layout_view design_info_view/;
37 use CXGN::Location::LocationLookup;
38 use CXGN::Stock::StockLookup;
39 use CXGN::Trial::TrialLayout;
40 use CXGN::BreedersToolbox::Projects;
41 use CXGN::BreedersToolbox::Delete;
42 use CXGN::UploadFile;
43 use CXGN::Trial::ParseUpload;
44 use CXGN::List::Transform;
45 use SGN::Model::Cvterm;
46 use JSON;
48 BEGIN { extends 'Catalyst::Controller::REST' }
50 __PACKAGE__->config(
51 default => 'application/json',
52 stash_key => 'rest',
53 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
56 has 'schema' => (
57 is => 'rw',
58 isa => 'DBIx::Class::Schema',
59 lazy_build => 1,
62 sub get_trial_layout : Path('/ajax/trial/layout') : ActionClass('REST') { }
64 sub get_trial_layout_POST : Args(0) {
65 my ($self, $c) = @_;
66 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
67 my $project;
68 print STDERR "\n\ntrial layout controller\n";
69 my $trial_layout = CXGN::Trial::TrialLayout->new({schema => $schema, project => $project} );
71 #my $trial_id = $c->req->parm('trial_id');
72 # my $project = $schema->resultset('Project::Project')->find(
73 # {
74 # id => $trial_id,
75 # }
76 # );
80 sub generate_experimental_design : Path('/ajax/trial/generate_experimental_design') : ActionClass('REST') { }
82 sub generate_experimental_design_POST : Args(0) {
83 my ($self, $c) = @_;
84 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
85 my $trial_design = CXGN::Trial::TrialDesign->new();
86 my %design;
87 my %design_info;
88 my $error;
89 my $project_name = $c->req->param('project_name');
90 my $project_description = $c->req->param('project_description');
91 my $year = $c->req->param('year');
92 my @stock_names;
93 my $design_layout_view_html;
94 my $design_info_view_html;
95 if ($c->req->param('stock_list')) {
96 @stock_names = @{_parse_list_from_json($c->req->param('stock_list'))};
97 # my $data = $self->transform_stock_list($c, \@raw_stock_names);
98 # if (exists($data->{missing}) && ref($data->{missing}) && @{$data->{missing}} >0) {
99 # $c->stash->{rest} = { error => "Some stocks were not found. Please edit the list and try again." };
100 # return;
102 # if ($data->{transform} && @{$data->{transform}}>0) {
103 # @stock_names = @{$data->{transform}};
106 my @control_names;
107 if ($c->req->param('control_list')) {
108 @control_names = @{_parse_list_from_json($c->req->param('control_list'))};
111 my @control_names_crbd;
112 if ($c->req->param('control_list_crbd')) {
113 @control_names_crbd = @{_parse_list_from_json($c->req->param('control_list_crbd'))};
116 my $design_type = $c->req->param('design_type');
117 my $rep_count = $c->req->param('rep_count');
118 my $block_number = $c->req->param('block_number');
120 my $row_number = $c->req->param('row_number');
121 my $block_row_number=$c->req->param('row_number_per_block');
122 my $block_col_number=$c->req->param('col_number_per_block');
123 my $col_number =$c->req->param('col_number');
125 my $block_size = $c->req->param('block_size');
126 my $max_block_size = $c->req->param('max_block_size');
127 my $plot_prefix = $c->req->param('plot_prefix');
128 my $start_number = $c->req->param('start_number');
129 my $increment = $c->req->param('increment');
130 my $trial_location = $c->req->param('trial_location');
131 #my $trial_name = $c->req->param('project_name');
132 my $greenhouse_num_plants = $c->req->param('greenhouse_num_plants');
133 my $use_same_layout = $c->req->param('use_same_layout');
134 #my $trial_name = "Trial $trial_location $year"; #need to add something to make unique in case of multiple trials in location per year?
135 if ($design_type eq "RCBD" || $design_type eq "Alpha") {
136 if (@control_names_crbd) {
137 @stock_names = (@stock_names, @control_names_crbd);
141 my @locations;
142 my $trial_locations;
143 my $multi_location;
145 try {
146 $multi_location = decode_json($trial_location);
147 foreach my $loc (@$multi_location) {
148 push @locations, $loc;
152 catch {
153 push @locations, $trial_location;
156 my $location_number = scalar(@locations);
158 #print STDERR Dumper(@locations);
159 print STDERR join "\n",$design_type;
160 print STDERR "\n";
162 print STDERR join "\n",$block_number;
163 print STDERR "\n";
165 print STDERR join "\n",$row_number;
166 print STDERR "\n";
168 print STDERR join "\n",$use_same_layout;
169 print STDERR "\n";
171 print STDERR join "\n",$trial_location;
172 print STDERR "\n";
176 if (!$c->user()) {
177 $c->stash->{rest} = {error => "You need to be logged in to add a trial" };
178 return;
181 if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles) ) { #user must have privileges to add a trial
182 $c->stash->{rest} = {error => "You have insufficient privileges to add a trial." };
183 return;
186 my @design_array;
187 my @design_layout_view_html_array;
189 foreach $trial_locations (@locations) {
191 my $trial_name = $c->req->param('project_name');
192 my $geolocation_lookup = CXGN::Location::LocationLookup->new(schema => $schema);
193 #$geolocation_lookup->set_location_name($c->req->param('trial_location'));
194 $geolocation_lookup->set_location_name($trial_locations);
195 #print STDERR Dumper(\$geolocation_lookup);
196 if (!$geolocation_lookup->get_geolocation()){
197 $c->stash->{rest} = { error => "Trial location not found" };
198 return;
201 # my $trial_create = CXGN::Trial::TrialCreate->new(chado_schema => $schema);
202 # $trial_create->set_trial_year($c->req->param('year'));
203 # $trial_create->set_trial_location($c->req->param('trial_location'));
204 # if ($trial_create->trial_name_already_exists()) {
205 # $c->stash->{rest} = {error => "Trial name \"".$trial_create->get_trial_name()."\" already exists" };
206 # return;
209 if (scalar(@locations) > 1) {
210 $trial_name = $trial_name."_".$trial_locations;
213 $trial_design->set_trial_name($trial_name);
215 my $design_created = 0;
216 if ($use_same_layout) {
217 $design_created = 1;
220 if ($design_created) {
221 $trial_design->set_randomization_seed($design_created);
224 if (@stock_names) {
225 $trial_design->set_stock_list(\@stock_names);
226 $design_info{'number_of_stocks'} = scalar(@stock_names);
227 } else {
228 $c->stash->{rest} = {error => "No list of stocks supplied." };
229 return;
231 if (@control_names) {
232 $trial_design->set_control_list(\@control_names);
233 $design_info{'number_of_controls'} = scalar(@control_names);
235 if (@control_names_crbd) {
236 $trial_design->set_control_list_crbd(\@control_names_crbd);
237 $design_info{'number_of_controls_crbd'} = scalar(@control_names_crbd);
239 if ($start_number) {
240 $trial_design->set_plot_start_number($start_number);
241 } else {
242 $trial_design->set_plot_start_number(1);
244 if ($increment) {
245 $trial_design->set_plot_number_increment($increment);
246 } else {
247 $trial_design->set_plot_number_increment(1);
249 if ($plot_prefix) {
250 $trial_design->set_plot_name_prefix($plot_prefix);
252 if ($rep_count) {
253 $trial_design->set_number_of_reps($rep_count);
255 if ($block_number) {
256 $trial_design->set_number_of_blocks($block_number);
257 #$trial_design->set_number_of_blocks(8);
259 if($row_number){
260 $trial_design->set_number_of_rows($row_number);
261 #$trial_design->set_number_of_rows(9);
263 if($block_row_number){
264 $trial_design->set_block_row_numbers($block_row_number);
265 #$trial_design->set_number_of_rows(9);
267 if($block_col_number){
268 $trial_design->set_block_col_numbers($block_col_number);
269 #$trial_design->set_number_of_rows(9);
271 if($col_number){
272 $trial_design->set_number_of_cols($col_number);
273 #$trial_design->set_number_of_rows(9);
275 if ($block_size) {
276 $trial_design->set_block_size($block_size);
278 if ($max_block_size) {
279 $trial_design->set_maximum_block_size($max_block_size);
281 if ($greenhouse_num_plants) {
282 my $json = JSON->new();
283 $trial_design->set_greenhouse_num_plants($json->decode($greenhouse_num_plants));
285 if ($location_number) {
286 $design_info{'number_of_locations'} = $location_number;
288 if ($design_type) {
289 $trial_design->set_design_type($design_type);
290 $design_info{'design_type'} = $design_type;
291 } else {
292 $c->stash->{rest} = {error => "No design type supplied." };
293 return;
295 if (!$trial_design->has_design_type()) {
296 $c->stash->{rest} = {error => "Design type not supported." };
297 return;
300 try {
301 $trial_design->calculate_design();
302 } catch {
303 $c->stash->{rest} = {error => "Could not calculate design: $_"};
304 $error=1;
306 if ($error) {return;}
307 if ($trial_design->get_design()) {
308 %design = %{$trial_design->get_design()};
309 } else {
310 $c->stash->{rest} = {error => "Could not generate design" };
311 return;
313 $design_layout_view_html = design_layout_view(\%design, \%design_info, $design_type);
314 $design_info_view_html = design_info_view(\%design, \%design_info);
315 my $design_json = encode_json(\%design);
316 push @design_array, $design_json;
317 push @design_layout_view_html_array, $design_layout_view_html;
320 $c->stash->{rest} = {
321 success => "1",
322 design_layout_view_html => encode_json(\@design_layout_view_html_array),
323 #design_layout_view_html => $design_layout_view_html,
324 design_info_view_html => $design_info_view_html,
325 #design_json => $design_json,
326 design_json => encode_json(\@design_array),
333 sub save_experimental_design : Path('/ajax/trial/save_experimental_design') : ActionClass('REST') { }
335 sub save_experimental_design_POST : Args(0) {
336 my ($self, $c) = @_;
337 #my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
338 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
339 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
340 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
341 my $dbh = $c->dbc->dbh;
343 print STDERR "Saving trial... :-)\n";
345 #my $trial_create = new CXGN::Trial::TrialCreate(chado_schema => $schema);
346 if (!$c->user()) {
347 $c->stash->{rest} = {error => "You need to be logged in to add a trial" };
348 return;
350 if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles) ) {
351 $c->stash->{rest} = {error => "You have insufficient privileges to add a trial." };
352 return;
354 my $user_id = $c->user()->get_object()->get_sp_person_id();
356 my $user_name = $c->user()->get_object()->get_username();
358 print STDERR "\nUserName: $user_name\n\n";
359 my $error;
361 my $design = _parse_design_from_json($c->req->param('design_json'));
362 print STDERR Dumper $design;
364 my @locations;
365 my $trial_location;
366 my $multi_location;
367 my $trial_locations = $c->req->param('trial_location');
368 my $trial_name = $c->req->param('project_name');
369 my $breeding_program = $c->req->param('breeding_program_name');
370 my $schema = $c->dbic_schema("Bio::Chado::Schema");
371 my $breeding_program_id = $schema->resultset("Project::Project")->find({name=>$breeding_program})->project_id();
372 my $folder;
373 my $new_trial_id;
375 try {
376 $multi_location = decode_json($trial_locations);
377 foreach my $loc (@$multi_location) {
378 push @locations, $loc;
382 catch {
383 push @locations, $trial_locations;
385 my $folder_id;
386 my $parent_folder_id = 0;
387 if (scalar(@locations) > 1) {
389 my $existing = $schema->resultset("Project::Project")->find( { name => $trial_name });
391 if ($existing) {
392 $c->stash->{rest} = { error => "An folder or trial with that name already exists in the database. Please select another name." };
393 return;
396 $folder = CXGN::Trial::Folder->create(
398 bcs_schema => $schema,
399 parent_folder_id => $parent_folder_id,
400 name => $trial_name,
401 breeding_program_id => $breeding_program_id,
403 $folder_id = $folder->folder_id();
406 my $design_index = 0;
408 foreach $trial_location (@locations) {
409 my $trial_name = $c->req->param('project_name');
410 if (scalar(@locations) > 1) {
411 $trial_name = $trial_name."_".$trial_location;
414 my $trial_location_design = decode_json($design->[$design_index]);
415 print STDERR Dumper $trial_location_design;
417 my $greenhouse_num_plants = $c->req->param('greenhouse_num_plants');
418 my $json = JSON->new();
419 if ($greenhouse_num_plants) {
420 $greenhouse_num_plants = $json->decode($greenhouse_num_plants);
423 my $trial_create = CXGN::Trial::TrialCreate->new({
424 chado_schema => $chado_schema,
425 phenome_schema => $phenome_schema,
426 dbh => $dbh,
427 user_name => $user_name,
428 design => $trial_location_design,
429 program => $breeding_program,
430 trial_year => $c->req->param('year'),
431 trial_description => $c->req->param('project_description'),
432 trial_location => $trial_location,
433 #trial_location => $c->req->param('trial_location'),
434 trial_name => $trial_name,
435 #trial_name => $c->req->param('project_name'),
436 design_type => $c->req->param('design_type'),
437 greenhouse_num_plants => $greenhouse_num_plants,
440 #$trial_create->set_user($c->user()->id());
441 #$trial_create->set_trial_year($c->req->param('year'));
442 #$trial_create->set_trial_location($c->req->param('trial_location'));
443 #$trial_create->set_trial_description($c->req->param('project_description'));
444 #$trial_create->set_design_type($c->req->param('design_type'));
445 #$trial_create->set_breeding_program_id($c->req->param('breeding_program_name'));
446 #$trial_create->set_design(_parse_design_from_json($c->req->param('design_json')));
447 #$trial_create->set_stock_list(_parse_list_from_json($c->req->param('stock_list')));
448 # if ($c->req->param('control_list')) {
449 # $trial_create->set_control_list(_parse_list_from_json($c->req->param('control_list')));
451 if ($trial_create->trial_name_already_exists()) {
452 $c->stash->{rest} = {error => "Trial name \"".$trial_create->get_trial_name()."\" already exists" };
453 return;
456 try {
457 $trial_create->save_trial();
458 } catch {
459 $c->stash->{rest} = {error => "Error saving trial in the database $_"};
460 print STDERR "ERROR SAVING TRIAL!\n";
461 $error = 1;
464 $design_index++;
466 if ($folder_id) {
467 $new_trial_id = $schema->resultset("Project::Project")->find({name=>$trial_name})->project_id();
469 my $folder1 = CXGN::Trial::Folder->new(
471 bcs_schema => $chado_schema,
472 folder_id => $new_trial_id,
474 $folder1->associate_parent($folder_id);
477 if ($error) {return;}
478 print STDERR "Trial saved successfully\n";
479 $c->stash->{rest} = {success => "1",};
480 return;
484 sub verify_stock_list : Path('/ajax/trial/verify_stock_list') : ActionClass('REST') { }
486 sub verify_stock_list_POST : Args(0) {
487 my ($self, $c) = @_;
488 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
489 my @stock_names;
490 my $error;
491 my %errors;
492 my $error_alert;
493 if ($c->req->param('stock_list')) {
494 @stock_names = @{_parse_list_from_json($c->req->param('stock_list'))};
495 #my $data = $self->transform_stock_list($c, \@raw_stock_names);
496 #if (exists($data->{missing}) && ref($data->{missing}) && @{$data->{missing}} >0) {
497 # $c->stash->{rest} = { error => "Some stocks were not found. Please edit the list and try again." };
498 # return;
500 # if ($data->{transform} && @{$data->{transform}}>0) {
501 # @stock_names = @{$data->{transform}};
505 if (!@stock_names) {
506 $c->stash->{rest} = {error => "No stock names supplied"};
507 return;
511 foreach my $stock_name (@stock_names) {
513 my $stock;
514 my $number_of_stocks_found;
515 my $stock_lookup = CXGN::Stock::StockLookup->new(schema => $schema);
516 $stock_lookup->set_stock_name($stock_name);
517 $stock = $stock_lookup->get_stock();
518 $number_of_stocks_found = $stock_lookup->get_matching_stock_count();
519 if ($number_of_stocks_found > 1) {
520 $errors{$stock_name} = "Multiple stocks found matching $stock_name\n";
522 if (!$number_of_stocks_found) {
523 $errors{$stock_name} = "No stocks found matching $stock_name\n";
526 if (%errors) {
527 foreach my $key (keys %errors) {
528 $error_alert .= "Stock $key: ".$errors{$key}."\n";
530 $c->stash->{rest} = {error => $error_alert};
531 } else {
532 $c->stash->{rest} = {
533 success => "1",
538 sub _parse_list_from_json {
539 my $list_json = shift;
540 my $json = new JSON;
541 if ($list_json) {
542 my $decoded_list = $json->allow_nonref->utf8->relaxed->escape_slash->loose->allow_singlequote->allow_barekey->decode($list_json);
543 #my $decoded_list = decode_json($list_json);
544 my @array_of_list_items = @{$decoded_list};
545 return \@array_of_list_items;
547 else {
548 return;
552 sub _parse_design_from_json {
553 my $design_json = shift;
554 my $json = new JSON;
555 if ($design_json) {
556 my $decoded_json = $json->allow_nonref->utf8->relaxed->escape_slash->loose->allow_singlequote->allow_barekey->decode($design_json);
557 #my $decoded_json = decode_json($design_json);
558 #my %design = %{$decoded_json};
559 return $decoded_json;
561 else {
562 return;
566 ###################################################################################
568 sub upload_trial_file : Path('/ajax/trial/upload_trial_file') : ActionClass('REST') { }
570 sub upload_trial_file_POST : Args(0) {
571 my ($self, $c) = @_;
573 print STDERR "Check 1: ".localtime();
575 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
576 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
577 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
578 my $dbh = $c->dbc->dbh;
579 my $program = $c->req->param('trial_upload_breeding_program');
580 my $trial_location = $c->req->param('trial_upload_location');
581 my $trial_name = $c->req->param('trial_upload_name');
582 my $trial_year = $c->req->param('trial_upload_year');
583 my $trial_description = $c->req->param('trial_upload_description');
584 my $trial_design_method = $c->req->param('trial_upload_design_method');
585 my $upload = $c->req->upload('trial_uploaded_file');
586 my $uploader = CXGN::UploadFile->new();
587 my $parser;
588 my $parsed_data;
589 my $upload_original_name = $upload->filename();
590 my $upload_tempfile = $upload->tempname;
591 my $subdirectory = "trial_upload";
592 my $archived_filename_with_path;
593 my $md5;
594 my $validate_file;
595 my $parsed_file;
596 my $parse_errors;
597 my %parsed_data;
598 my %upload_metadata;
599 my $time = DateTime->now();
600 my $timestamp = $time->ymd()."_".$time->hms();
601 my $user_id;
602 my $user_name;
603 my $error;
605 print STDERR "Check 2: ".localtime();
607 if ($upload_original_name =~ /\s/ || $upload_original_name =~ /\// || $upload_original_name =~ /\\/ ) {
608 print STDERR "File name must not have spaces or slashes.\n";
609 $c->stash->{rest} = {error => "Uploaded file name must not contain spaces or slashes." };
610 return;
613 if (!$c->user()) {
614 print STDERR "User not logged in... not uploading a trial.\n";
615 $c->stash->{rest} = {error => "You need to be logged in to upload a trial." };
616 return;
618 if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles) ) {
619 $c->stash->{rest} = {error => "You have insufficient privileges to upload a trial." };
620 return;
623 $user_id = $c->user()->get_object()->get_sp_person_id();
625 $user_name = $c->user()->get_object()->get_username();
627 ## Store uploaded temporary file in archive
628 $archived_filename_with_path = $uploader->archive($c, $subdirectory, $upload_tempfile, $upload_original_name, $timestamp);
629 $md5 = $uploader->get_md5($archived_filename_with_path);
630 if (!$archived_filename_with_path) {
631 $c->stash->{rest} = {error => "Could not save file $upload_original_name in archive",};
632 return;
634 unlink $upload_tempfile;
636 print STDERR "Check 3: ".localtime();
638 $upload_metadata{'archived_file'} = $archived_filename_with_path;
639 $upload_metadata{'archived_file_type'}="trial upload file";
640 $upload_metadata{'user_id'}=$user_id;
641 $upload_metadata{'date'}="$timestamp";
643 #parse uploaded file with appropriate plugin
644 $parser = CXGN::Trial::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path);
645 $parser->load_plugin('TrialExcelFormat');
646 $parsed_data = $parser->parse();
650 if (!$parsed_data) {
651 my $return_error = '';
653 if (! $parser->has_parse_errors() ){
654 $return_error = "Could not get parsing errors";
655 $c->stash->{rest} = {error_string => $return_error,};
658 else {
659 $parse_errors = $parser->get_parse_errors();
660 #print STDERR Dumper $parse_errors;
662 foreach my $error_string (@{$parse_errors->{'error_messages'}}){
663 $return_error=$return_error.$error_string."<br>";
667 $c->stash->{rest} = {error_string => $return_error, missing_accessions => $parse_errors->{'missing_accessions'}};
668 return;
671 print STDERR "Check 4: ".localtime();
673 #print STDERR Dumper $parsed_data;
675 my $trial_create = CXGN::Trial::TrialCreate
676 ->new({
677 chado_schema => $chado_schema,
678 phenome_schema => $phenome_schema,
679 dbh => $dbh,
680 trial_year => $trial_year,
681 trial_description => $trial_description,
682 trial_location => $trial_location,
683 trial_name => $trial_name,
684 user_name => $user_name, #not implemented
685 design_type => $trial_design_method,
686 design => $parsed_data,
687 program => $program,
688 upload_trial_file => $upload,
691 try {
692 $trial_create->save_trial();
693 } catch {
694 $c->stash->{rest} = {error => "Error saving trial in the database $_"};
695 $error = 1;
698 print STDERR "Check 5: ".localtime();
700 if ($error) {return;}
701 $c->stash->{rest} = {success => "1",};
702 return;
709 ###################################################################################
710 ##remove this soon. using above instead
711 sub upload_trial_layout : Path('/trial/upload_trial_layout') : ActionClass('REST') { }
713 sub upload_trial_layout_POST : Args(0) {
714 my ($self, $c) = @_;
715 my @contents;
716 my $error = 0;
717 my $upload = $c->req->upload('trial_upload_file');
718 my $header_line;
719 my @header_contents;
720 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
721 if (!$c->user()) { #user must be logged in
722 $c->stash->{rest} = {error => "You need to be logged in to upload a file." };
723 return;
725 if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles) ) { #user must have privileges to add a trial
726 $c->stash->{rest} = {error => "You have insufficient privileges to upload a file." };
727 return;
729 if (!$upload) { #upload file required
730 $c->stash->{rest} = {error => "File upload failed: no file name received"};
731 return;
733 try { #get file contents
734 @contents = split /\n/, $upload->slurp;
735 } catch {
736 $c->stash->{rest} = {error => "File upload failed: $_"};
737 $error = 1;
739 if ($error) {return;}
740 if (@contents < 2) { #upload file must contain at least one line of data plus a header
741 $c->stash->{rest} = {error => "File upload failed: contains less than two lines"};
742 return;
744 $header_line = shift(@contents);
745 @header_contents = split /\t/, $header_line;
746 try { #verify header contents
747 _verify_trial_layout_header(\@header_contents);
748 } catch {
749 $c->stash->{rest} = {error => "File upload failed: $_"};
750 $error = 1;
752 if ($error) {return;}
754 #verify location
755 if (! $schema->resultset("NaturalDiversity::NdGeolocation")->find({description=>$c->req->param('add_project_location'),})){
756 $c->stash->{rest} = {error => "File upload failed: location not found"};
757 return;
760 try { #verify contents of file
761 _verify_trial_layout_contents($self, $c, \@contents);
762 } catch {
763 my %error_hash = %{$_};
764 #my $error_string = Dumper(%error_hash);
765 my $error_string = _formatted_string_from_error_hash(\%error_hash);
766 $c->stash->{rest} = {error => "File upload failed: missing or invalid content (see details that follow..)", error_string => "$error_string"};
767 $error = 1;
769 if ($error) {return;}
771 try { #add file contents to the database
772 _add_trial_layout_to_database($self,$c,\@contents);
773 } catch {
774 $c->stash->{rest} = {error => "File upload failed: $_"};
777 if ($error) {
778 return;
779 } else {
780 $c->stash->{rest} = {success => "1"};
784 sub _add_trial_layout_to_database {
785 my $self = shift;
786 my $c = shift;
787 my $contents_ref = shift;
788 my @contents = @{$contents_ref};
789 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
790 my $year = $c->req->param('add_project_year');
791 my $location = $c->req->param('add_project_location');
792 my $project_name = $c->req->param('add_project_name');
793 my $project_description = $c->req->param('add_project_description');
794 my $plot_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot', 'stock_type');
795 my $geolocation = $schema->resultset("NaturalDiversity::NdGeolocation")
796 ->find_or_create({
797 description => $location, #add this as an option
799 my $organism = $schema->resultset("Organism::Organism")
800 ->find_or_create({
801 genus => 'Manihot',
802 species => 'Manihot esculenta',
805 #this is wrong. Does not seem to be used in the database !!
806 my $plot_exp_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_experiment', 'experiment_type');
809 #create project
810 my $project = $schema->resultset('Project::Project')
811 ->find_or_create({
812 name => $project_name,
813 description => $location,
817 my $projectprop_year = $project->create_projectprops( { 'project year' => $year,}, {autocreate=>1});
818 my $organism_id = $organism->organism_id();
820 foreach my $content_line (@contents) {
821 my @line_contents = split /\t/, $content_line;
822 my $plot_name = $line_contents[0];
823 my $block_number = $line_contents[1];
824 my $rep_number = $line_contents[2];
825 my $stock_name = $line_contents[3];
826 my $stock;
827 my $stock_rs = $schema->resultset("Stock::Stock")
828 ->search({
829 -or => [
830 'lower(me.uniquename)' => { like => lc($stock_name) },
831 -and => [
832 'lower(type.name)' => { like => '%synonym%' },
833 'lower(stockprops.value)' => { like => lc($stock_name) },
838 join => { 'stockprops' => 'type'} ,
839 distinct => 1
842 if ($stock_rs->count >1 ) {
843 die ("multiple stocks found matching $stock_name");
844 } elsif ($stock_rs->count == 1) {
845 $stock = $stock_rs->first;
846 } else {
847 die ("no stocks found matching $stock_name");
849 my $unique_plot_name =
850 $project_name."_".$stock_name."_plot_".$plot_name."_block_".$block_number."_rep_".$rep_number."_".$year."_".$location;
851 my $plot = $schema->resultset("Stock::Stock")
852 ->find_or_create({
853 organism_id => $stock->organism_id(),
854 name => $unique_plot_name,
855 uniquename => $unique_plot_name,
856 type_id => $plot_cvterm->cvterm_id,
858 my $experiment = $schema->resultset('NaturalDiversity::NdExperiment')
859 ->create({
860 nd_geolocation_id => $geolocation->nd_geolocation_id(),
861 type_id => $plot_exp_cvterm->cvterm_id(),
863 #link to the project
864 $experiment
865 ->find_or_create_related('nd_experiment_projects',{
866 project_id => $project->project_id()
868 #link the experiment to the stock
869 $experiment
870 ->find_or_create_related('nd_experiment_stocks' ,{
871 stock_id => $plot->stock_id(),
872 type_id => $plot_exp_cvterm->cvterm_id(),
877 sub _verify_trial_layout_header {
878 my $header_content_ref = shift;
879 my @header_contents = @{$header_content_ref};
880 if ($header_contents[0] ne 'plot_name' ||
881 $header_contents[1] ne 'block_number' ||
882 $header_contents[2] ne 'rep_number' ||
883 $header_contents[3] ne 'stock_name') {
884 die ("Wrong column names in header\n");
886 if (@header_contents != 4) {
887 die ("Wrong number of columns in header\n");
889 return;
892 sub _verify_trial_layout_contents {
893 my $self = shift;
894 my $c = shift;
895 my $contents_ref = shift;
896 my @contents = @{$contents_ref};
897 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
898 my $year = $c->req->param('add_project_year');
899 my $location = $c->req->param('add_project_location');
900 my $project_name = $c->req->param('add_project_name');
901 my $line_number = 1;
902 my %error_hash;
903 my %plot_name_errors;
904 my %block_number_errors;
905 my %rep_number_errors;
906 my %stock_name_errors;
907 my %column_number_errors;
908 foreach my $content_line (@contents) {
909 my @line_contents = split /\t/, $content_line;
910 if (@line_contents != 4) {
911 my $column_count = scalar(@line_contents);
912 $column_number_errors{$line_number} = "Line $line_number: wrong number of columns, expected 4, found $column_count";
913 $line_number++;
914 next;
916 my $plot_name = $line_contents[0];
917 my $block_number = $line_contents[1];
918 my $rep_number = $line_contents[2];
919 my $stock_name = $line_contents[3];
920 if (!$stock_name) {
921 $stock_name_errors{$line_number} = "Line $line_number: stock name is missing";
922 } else {
923 #make sure stock name exists and returns a unique result
924 my $stock_rs = $schema->resultset("Stock::Stock")
925 ->search({
926 -or => [
927 'lower(me.uniquename)' => { like => lc($stock_name) },
928 -and => [
929 'lower(type.name)' => { like => '%synonym%' },
930 'lower(stockprops.value)' => { like => lc($stock_name) },
935 join => { 'stockprops' => 'type'} ,
936 distinct => 1
939 if ($stock_rs->count >1 ) {
940 my $error_string = "Line $line_number: multiple accessions found for stock name $stock_name (";
941 while ( my $st = $stock_rs->next) {
942 my $error_string .= $st->uniquename.",";
944 $stock_name_errors{$line_number} = $error_string;
945 } elsif ($stock_rs->count == 1) {
946 } else {
947 $stock_name_errors{$line_number} = "Line $line_number: stock name $stock_name not found";
951 if (!$plot_name) {
952 $plot_name_errors{$line_number} = "Line $line_number: plot name is missing";
953 } else {
954 my $unique_plot_name = $project_name."_".$stock_name."_plot_".$plot_name."_block_".$block_number."_rep_".$rep_number."_".$year."_".$location;
955 if ($schema->resultset("Stock::Stock")->find({uniquename=>$unique_plot_name,})) {
956 $plot_name_errors{$line_number} = "Line $line_number: plot name $unique_plot_name is not unique";
960 #check for valid block number
961 if (!$block_number) {
962 $block_number_errors{$line_number} = "Line $line_number: block number is missing";
963 } else {
964 if (!($block_number =~ /^\d+?$/)) {
965 $block_number_errors{$line_number} = "Line $line_number: block number $block_number is not an integer";
966 } elsif ($block_number < 1 || $block_number > 1000000) {
967 $block_number_errors{$line_number} = "Line $line_number: block number $block_number is out of range";
971 #check for valid rep number
972 if (!$rep_number) {
973 $rep_number_errors{$line_number} = "Line $line_number: rep number is missing";
974 } else {
975 if (!($rep_number =~ /^\d+?$/)) {
976 $rep_number_errors{$line_number} = "Line $line_number: rep number $rep_number is not an integer";
977 } elsif ($rep_number < 1 || $rep_number > 1000000) {
978 $rep_number_errors{$line_number} = "Line $line_number: rep number $block_number is out of range";
981 $line_number++;
984 if (%plot_name_errors) {$error_hash{'plot_name_errors'}=\%plot_name_errors;}
985 if (%block_number_errors) {$error_hash{'block_number_errors'}=\%block_number_errors;}
986 if (%rep_number_errors) {$error_hash{'rep_number_errors'}=\%rep_number_errors;}
987 if (%stock_name_errors) {$error_hash{'stock_name_errors'}=\%stock_name_errors;}
988 if (%column_number_errors) {$error_hash{'column_number_errors'}=\%column_number_errors;}
989 if (%error_hash) {
990 die (\%error_hash);
992 return;
995 sub _formatted_string_from_error_hash {
996 my $error_hash_ref = shift;
997 my %error_hash = %{$error_hash_ref};
998 my $error_string ;
999 if ($error_hash{column_number_errors}) {
1000 $error_string .= "<b>Column number errors</b><br><br>"._formatted_string_from_error_hash_by_type(\%{$error_hash{column_number_errors}})."<br><br>";
1002 if ($error_hash{stock_name_errors}) {
1003 $error_string .= "<b>Stock name errors</b><br><br>"._formatted_string_from_error_hash_by_type(\%{$error_hash{stock_name_errors}})."<br><br>";
1005 if ($error_hash{'plot_name_errors'}) {
1006 $error_string .= "<b>Plot name errors</b><br><br>"._formatted_string_from_error_hash_by_type(\%{$error_hash{'plot_name_errors'}})."<br><br>";
1008 if ($error_hash{'block_number_errors'}) {
1009 $error_string .= "<b>Block number errors</b><br><br>"._formatted_string_from_error_hash_by_type(\%{$error_hash{'block_number_errors'}})."<br><br>";
1011 if ($error_hash{'rep_number_errors'}) {
1012 $error_string .= "<b>Rep number errors</b><br><br>"._formatted_string_from_error_hash_by_type(\%{$error_hash{'rep_number_errors'}})."<br><br>";
1014 return $error_string;
1017 sub _formatted_string_from_error_hash_by_type {
1018 my $error_hash_ref = shift;
1019 my %error_hash = %{$error_hash_ref};
1020 my $error_string;
1021 foreach my $key (sort { $a <=> $b} keys %error_hash) {
1022 $error_string .= $error_hash{$key} . "<br>";
1024 return $error_string;
1028 ### The following was moved to TrialMetadata.
1029 # sub trial : Chained('/') PathPart('ajax/breeders/trial') CaptureArgs(1) {
1030 # my $self = shift;
1031 # my $c = shift;
1032 # my $trial_id = shift;
1034 # print STDERR "TRIAL ID: $trial_id\n";
1035 # $c->stash->{trial_id} = $trial_id;
1036 # $c->stash->{trial} = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema"), trial_id => $trial_id });
1038 # if (!$c->stash->{trial}) {
1039 # $c->stash->{rest} = { error => "The specified trial with id $trial_id does not exist" };
1040 # return;
1046 # =head2 delete_trial_by_file
1048 # Usage:
1049 # Desc:
1050 # Ret:
1051 # Args:
1052 # Side Effects:
1053 # Example:
1055 # =cut
1057 # sub delete_trial_by_file : Path('/breeders/trial/delete/file') Args(1) {
1058 # my $self = shift;
1059 # my $c = shift;
1061 # my $file_id = shift;
1063 # if (!$c->user()) {
1064 # $c->stash->{rest} = { error => 'You must be logged in to delete a trial' };
1065 # return;
1068 # if (! ($c->user->check_roles('curator') || $c->user->check_roles('submitter'))) {
1069 # $c->stash->{rest} = { error => 'You do not have sufficient privileges to delete a trial.' };
1072 # my $del = CXGN::BreedersToolbox::Delete->new(
1073 # bcs_schema => $c->dbic_schema("Bio::Chado::Schema"),
1074 # metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema"),
1075 # phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema"),
1076 # );
1078 # if ($del->delete_experiments_by_file($c->user->get_object()->get_sp_person_id(), $file_id)) {
1079 # $c->stash->{rest} = { success => 1 };
1081 # else {
1082 # $c->stash->{rest} = { error => "The trial information could not be removed from the database." };
1087 # =head2 delete_trial_by_trial_id
1089 # Usage:
1090 # Desc: Deletes plots associated with a phenotyping experiment
1091 # Ret:
1092 # Args:
1093 # Side Effects:
1094 # Example:
1096 # =cut
1098 # sub delete_trial_by_trial_id : Path('/breeders/trial/delete/id') Args(1) {
1099 # my $self = shift;
1100 # my $c = shift;
1102 # my $trial_id = shift;
1104 # print STDERR "DELETING trial $trial_id\n";
1106 # if (!$c->user()) {
1107 # $c->stash->{rest} = { error => 'You must be logged in to delete a trial' };
1108 # return;
1111 # my $user_id = $c->user->get_object()->get_sp_person_id();
1113 # my $schema = $c->dbic_schema("Bio::Chado::Schema");
1115 # my $breeding_program_rs = $schema->resultset("Cv::Cvterm")->search( { name => "breeding_program" });
1117 # my $breeding_program_id = $breeding_program_rs->first()->cvterm_id();
1119 # my $breeding_program_name = $breeding_program_rs->first()->name();
1121 # my $trial_organization_id = $schema->resultset("Project::Projectprop")->search(
1123 # project_id => $trial_id,
1124 # type_id=>$breeding_program_id
1125 # });
1127 # if (! ($c->user->check_roles('curator') || ( $c->user->check_roles('submitter') && $c->roles($breeding_program_name) ))) {
1128 # $c->stash->{rest} = { error => 'You do not have sufficient privileges to delete a trial.' };
1131 # # my $del = CXGN::BreedersToolbox::Delete->new(
1132 # # bcs_schema => $c->dbic_schema("Bio::Chado::Schema"),
1133 # # metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema"),
1134 # # phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema"),
1135 # # );
1137 # my $t = CXGN::Trial->new( { trial_id=> $trial_id, bcs_schema => $c->dbic_schema("Bio::Chado::Schema") });
1139 # my $hash = $t->delete_experiments($user_id, $trial_id);
1141 # $c->stash->{rest} = $hash;
1145 # =head2 delete_phenotype_data_by_trial_id
1147 # Usage:
1148 # Desc:
1149 # Ret:
1150 # Args:
1151 # Side Effects:
1152 # Example:
1154 # =cut
1156 # sub delete_phenotype_data_by_trial_id : Path('/breeders/trial/phenotype/delete/id') Args(1) {
1157 # my $self = shift;
1158 # my $c = shift;
1160 # my $trial_id = shift;
1162 # print STDERR "DELETING phenotypes of trial $trial_id\n";
1164 # if (!$c->user()) {
1165 # $c->stash->{rest} = { error => 'You must be logged in to delete a trial' };
1166 # return;
1169 # my $user_id = $c->user->get_object()->get_sp_person_id();
1171 # my $schema = $c->dbic_schema("Bio::Chado::Schema");
1173 # my $breeding_program_rs = $schema->resultset("Cv::Cvterm")->search( { name => "breeding_program" });
1175 # my $breeding_program_id = $breeding_program_rs->first()->cvterm_id();
1177 # my $breeding_program_name = $breeding_program_rs->first()->name();
1179 # my $trial_organization_id = $schema->resultset("Project::Projectprop")->search(
1181 # project_id => $trial_id,
1182 # type_id=>$breeding_program_id
1183 # });
1185 # if (! ($c->user->check_roles('curator') || ( $c->user->check_roles('submitter') && $c->roles($breeding_program_name) ))) {
1186 # $c->stash->{rest} = { error => 'You do not have sufficient privileges to delete a trial.' };
1189 # my $t = CXGN::Trial->new( { trial_id => $trial_id, bcs_schema => $c->dbic_schema("Bio::Chado::Schema") });
1191 # my $error = $t->delete_metadata($c->dbic_schema("CXGN::Metadata::Schema"), $c->dbic_schema("CXGN::Phenome::Schema"));
1193 # print STDERR "ERROR DELETING METADATA: $error\n";
1194 # my $error = $t->delete_phenotype_data($trial_id);
1196 # print STDERR "ERROR DELETING PHENOTYPES: $error\n";
1197 # if ($error) {
1198 # $c->stash->{rest} = { error => $error };
1200 # else {
1201 # $c->stash->{rest} = { success => "1" };
1205 # =head2 delete_trial_layout_by_trial_id
1207 # Usage:
1208 # Desc:
1209 # Ret:
1210 # Args:
1211 # Side Effects:
1212 # Example:
1214 # =cut
1216 # sub delete_trial_layout_by_trial_id : Path('/breeders/trial/layout/delete/id') Args(1) {
1217 # my $self = shift;
1218 # my $c = shift;
1220 # my $trial_id = shift;
1222 # print STDERR "DELETING trial layout $trial_id\n";
1224 # if (!$c->user()) {
1225 # $c->stash->{rest} = { error => 'You must be logged in to delete a trial layout' };
1226 # return;
1229 # my $user_id = $c->user->get_object()->get_sp_person_id();
1231 # my $schema = $c->dbic_schema("Bio::Chado::Schema");
1233 # my $breeding_program_rs = $schema->resultset("Cv::Cvterm")->search( { name => "breeding_program" });
1235 # my $breeding_program_id = $breeding_program_rs->first()->cvterm_id();
1237 # my $breeding_program_name = $breeding_program_rs->first()->name();
1239 # my $trial_organization_id = $schema->resultset("Project::Projectprop")->search(
1241 # project_id => $trial_id,
1242 # type_id=>$breeding_program_id
1243 # });
1245 # if (! ($c->user->check_roles('curator') || ( $c->user->check_roles('submitter') && $c->roles($breeding_program_name) ))) {
1246 # $c->stash->{rest} = { error => 'You do not have sufficient privileges to delete a trial.' };
1249 # #my $del = CXGN::BreedersToolbox::Delete->new(
1250 # # bcs_schema => $c->dbic_schema("Bio::Chado::Schema"),
1251 # # metadata_schema => $c->dbic_schema("CXGN::Metadata::Schema"),
1252 # # phenome_schema => $c->dbic_schema("CXGN::Phenome::Schema"),
1253 # # );
1255 # my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema"), trial_id => $trial_id });
1256 # #my $error = $del->delete_field_layout_by_trial($trial_id);
1258 # my $error = $t->delete_field_layout();
1259 # if ($error) {
1260 # $c->stash->{rest} = { error => $error };
1262 # $c->stash->{rest} = { success => 1 };
1268 # sub trial_description : Local() ActionClass('REST');
1270 # sub trial_description_GET : Chained('trial') PathPart('description') Args(0) {
1271 # my $self = shift;
1272 # my $c = shift;
1274 # my $trial = $c->stash->{trial};
1276 # print STDERR "TRIAL: ".$trial->get_description()."\n";
1278 # $c->stash->{rest} = { description => $trial->get_description() };
1282 # sub trial_description_POST : Chained('trial') PathPart('description') Args(1) {
1283 # my $self = shift;
1284 # my $c = shift;
1285 # my $description = shift;
1287 # if (!($c->user()->check_roles('curator') || $c->user()->check_roles('submitter'))) {
1288 # $c->stash->{rest} = { error => 'You do not have the required privileges to edit the trial type of this trial.' };
1289 # return;
1292 # my $trial_id = $c->stash->{trial_id};
1293 # my $trial = $c->stash->{trial};
1295 # my $p = CXGN::BreedersToolbox::Projects->new( { schema => $c->dbic_schema("Bio::Chado::Schema") });
1297 # my $breeding_program = $p->get_breeding_programs_by_trial($trial_id);
1299 # if (! ($c->user() && ($c->user->check_roles("curator") || $c->user->check_roles($breeding_program)))) {
1300 # $c->stash->{rest} = { error => "You need to be logged in with sufficient privileges to change the description of a trial." };
1301 # return;
1304 # $trial->set_description($description);
1306 # $c->stash->{rest} = { success => 1 };
1309 # # sub get_trial_type :Path('/ajax/breeders/trial/type') Args(1) {
1310 # # my $self = shift;
1311 # # my $c = shift;
1312 # # my $trial_id = shift;
1314 # # my $t = CXGN::Trial->new( { bcs_schema => $c->dbic_schema("Bio::Chado::Schema"), trial_id => $trial_id } );
1316 # # $c->stash->{rest} = { type => $t->get_project_type() };
1318 # # }
1321 # sub trial_location : Local() ActionClass('REST');
1323 # sub trial_location_GET : Chained('trial') PathPart('location') Args(0) {
1324 # my $self = shift;
1325 # my $c = shift;
1327 # my $t = $c->stash->{trial};
1329 # $c->stash->{rest} = { location => [ $t->get_location()->[0], $t->get_location()->[1] ] };
1333 # sub trial_location_POST : Chained('trial') PathPart('location') Args(1) {
1334 # my $self = shift;
1335 # my $c = shift;
1336 # my $location_id = shift;
1338 # if (!($c->user()->check_roles('curator') || $c->user()->check_roles('submitter'))) {
1339 # $c->stash->{rest} = { error => 'You do not have the required privileges to edit the trial type of this trial.' };
1340 # return;
1343 # print STDERR "trial location POST!\n";
1345 # #my $location_id = $c->req->param("location_id");
1347 # my $t = $c->stash->{trial};
1348 # my $trial_id = $c->stash->{trial_id};
1350 # # remove old location
1352 # $t->remove_location($t->get_location()->[0]);
1354 # # add new one
1356 # $t->set_location($location_id);
1358 # $c->stash->{rest} = { message => "Successfully stored location for trial $trial_id",
1359 # trial_id => $trial_id };
1363 # sub trial_year : Local() ActionClass('REST');
1365 # sub trial_year_GET : Chained('trial') PathPart('year') Args(0) {
1366 # my $self = shift;
1367 # my $c = shift;
1369 # my $t = $c->stash->{trial};
1371 # $c->stash->{rest} = { year => $t->get_year() };
1375 # sub trial_year_POST : Chained('trial') PathPart('year') Args(1) {
1376 # my $self = shift;
1377 # my $c = shift;
1378 # my $year = shift;
1380 # if (!($c->user()->check_roles('curator') || $c->user()->check_roles('submitter'))) {
1381 # $c->stash->{rest} = { error => 'You do not have the required privileges to edit the trial type of this trial.' };
1382 # return;
1385 # my $t = $c->stash->{trial};
1387 # $t->set_year($year);
1389 # $c->stash->{rest} = { message => "Year set successfully" };
1392 # sub trial_type : Local() ActionClass('REST');
1394 # sub trial_type_GET : Chained('trial') PathPart('type') Args(0) {
1395 # my $self = shift;
1396 # my $c = shift;
1398 # my $t = $c->stash->{trial};
1400 # my $type = $t->get_project_type();
1401 # $c->stash->{rest} = { type => $type };
1404 # sub trial_type_POST : Chained('trial') PathPart('type') Args(1) {
1405 # my $self = shift;
1406 # my $c = shift;
1407 # my $type = shift;
1409 # if (!($c->user()->check_roles('curator') || $c->user()->check_roles('submitter'))) {
1410 # $c->stash->{rest} = { error => 'You do not have the required privileges to edit the trial type of this trial.' };
1411 # return;
1414 # my $t = $c->stash->{trial};
1415 # my $trial_id = $c->stash->{trial_id};
1417 # # set the new trial type
1419 # $t->set_project_type($type);
1421 # $c->stash->{rest} = { success => 1 };