4 SGN::Controller::AJAX::Cross - a REST controller class to provide the
5 functions for adding crosses
9 Add a new cross or upload a file containing crosses to add
13 Jeremy Edwards <jde22@cornell.edu>
14 Lukas Mueller <lam87@cornell.edu>
18 package SGN
::Controller
::AJAX
::Cross
;
24 use File
::Basename qw
| basename dirname
|;
27 use File
::Spec
::Functions
;
29 use List
::MoreUtils qw
/any /;
30 use Bio
::GeneticRelationships
::Pedigree
;
31 use Bio
::GeneticRelationships
::Individual
;
33 use CXGN
::Pedigree
::AddCrosses
;
34 use CXGN
::Pedigree
::AddProgeny
;
35 use CXGN
::Pedigree
::AddCrossInfo
;
36 use CXGN
::Pedigree
::ParseUpload
;
38 BEGIN { extends
'Catalyst::Controller::REST' }
41 default => 'application/json',
43 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
46 sub upload_cross_file
: Path
('/ajax/cross/upload_crosses_file') : ActionClass
('REST') { }
48 sub upload_cross_file_POST
: Args
(0) {
50 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
51 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
52 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
53 my $dbh = $c->dbc->dbh;
54 my $program = $c->req->param('cross_upload_breeding_program');
55 my $location = $c->req->param('cross_upload_location');
56 my $upload = $c->req->upload('crosses_upload_file');
57 my $prefix = $c->req->param('upload_prefix');
58 my $suffix = $c->req->param('upload_suffix');
59 my $uploader = CXGN
::UploadFile
->new();
62 my $upload_original_name = $upload->filename();
63 my $upload_tempfile = $upload->tempname;
64 my $subdirectory = "cross_upload";
65 my $archived_filename_with_path;
72 my $time = DateTime
->now();
73 my $timestamp = $time->ymd()."_".$time->hms();
76 my $upload_file_type = "crosses excel";#get from form when more options are added
79 print STDERR
"User not logged in... not adding a crosses.\n";
80 $c->stash->{rest
} = {error
=> "You need to be logged in to add a cross." };
83 $user_id = $c->user()->get_object()->get_sp_person_id();
85 $owner_name = $c->user()->get_object()->get_username();
87 ## Store uploaded temporary file in archive
88 $archived_filename_with_path = $uploader->archive($c, $subdirectory, $upload_tempfile, $upload_original_name, $timestamp);
89 $md5 = $uploader->get_md5($archived_filename_with_path);
90 if (!$archived_filename_with_path) {
91 $c->stash->{rest
} = {error
=> "Could not save file $upload_original_name in archive",};
94 unlink $upload_tempfile;
96 $upload_metadata{'archived_file'} = $archived_filename_with_path;
97 $upload_metadata{'archived_file_type'}="cross upload file";
98 $upload_metadata{'user_id'}=$user_id;
99 $upload_metadata{'date'}="$timestamp";
101 #parse uploaded file with appropriate plugin
102 $parser = CXGN
::Pedigree
::ParseUpload
->new(chado_schema
=> $chado_schema, filename
=> $archived_filename_with_path);
103 $parser->load_plugin('CrossesExcelFormat');
104 $parsed_data = $parser->parse();
107 my $return_error = '';
109 if (! $parser->has_parse_errors() ){
110 $return_error = "Could not get parsing errors";
111 $c->stash->{rest
} = {error_string
=> $return_error,};
115 $parse_errors = $parser->get_parse_errors();
116 foreach my $error_string (@
{$parse_errors}){
117 $return_error=$return_error.$error_string."<br>";
121 $c->stash->{rest
} = {error_string
=> $return_error,};
125 my $cross_add = CXGN
::Pedigree
::AddCrosses
127 chado_schema
=> $chado_schema,
128 phenome_schema
=> $phenome_schema,
129 metadata_schema
=> $metadata_schema,
131 location
=> $location,
133 crosses
=> $parsed_data->{crosses
},
134 owner_name
=> $owner_name,
137 #validate the crosses
138 if (!$cross_add->validate_crosses()){
139 $c->stash->{rest
} = {error_string
=> "Error validating crosses",};
144 if (!$cross_add->add_crosses()){
145 $c->stash->{rest
} = {error_string
=> "Error adding crosses",};
150 foreach my $cross_name_key (keys %{$parsed_data->{progeny
}}){
151 my $progeny_number = $parsed_data->{progeny
}->{$cross_name_key};
152 my $progeny_increment = 1;
155 #create array of progeny names to add for this cross
156 while ($progeny_increment < $progeny_number + 1) {
157 $progeny_increment = sprintf "%03d", $progeny_increment;
158 my $stock_name = $cross_name_key.$prefix.$progeny_increment.$suffix;
159 push @progeny_names, $stock_name;
160 $progeny_increment++;
163 #add array of progeny to the cross
164 my $progeny_add = CXGN
::Pedigree
::AddProgeny
166 chado_schema
=> $chado_schema,
167 phenome_schema
=> $phenome_schema,
169 cross_name
=> $cross_name_key,
170 progeny_names
=> \
@progeny_names,
171 owner_name
=> $owner_name,
173 if (!$progeny_add->add_progeny()){
174 $c->stash->{rest
} = {error_string
=> "Error adding progeny",};
175 #should delete crosses and other progeny if add progeny fails?
180 #add the number of flowers to crosses
181 foreach my $cross_name_key (keys %{$parsed_data->{flowers
}}) {
182 my $number_of_flowers = $parsed_data->{flowers
}->{$cross_name_key};
183 my $cross_add_info = CXGN
::Pedigree
::AddCrossInfo
->new({ chado_schema
=> $chado_schema, cross_name
=> $cross_name_key} );
184 $cross_add_info->set_number_of_flowers($number_of_flowers);
185 $cross_add_info->add_info();
188 #add the number of seeds to crosses
189 foreach my $cross_name_key (keys %{$parsed_data->{seeds
}}) {
190 my $number_of_seeds = $parsed_data->{seeds
}->{$cross_name_key};
191 my $cross_add_info = CXGN
::Pedigree
::AddCrossInfo
->new({ chado_schema
=> $chado_schema, cross_name
=> $cross_name_key} );
192 $cross_add_info->set_number_of_seeds($number_of_seeds);
193 $cross_add_info->add_info();
196 $c->stash->{rest
} = {success
=> "1",};
200 sub add_cross
: Local
: ActionClass
('REST') { }
202 sub add_cross_POST
:Args
(0) {
204 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
205 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
206 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
207 my $dbh = $c->dbc->dbh;
208 my $cross_name = $c->req->param('cross_name');
209 my $cross_type = $c->req->param('cross_type');
210 my $program = $c->req->param('program');
211 my $location = $c->req->param('location');
212 my $maternal = $c->req->param('maternal_parent');
213 my $paternal = $c->req->param('paternal_parent');
214 my $prefix = $c->req->param('prefix');
215 my $suffix = $c->req->param('suffix');
216 my $progeny_number = $c->req->param('progeny_number');
217 my $number_of_flowers = $c->req->param('number_of_flowers');
218 my $number_of_seeds = $c->req->param('number_of_seeds');
219 my $visible_to_role = $c->req->param('visible_to_role');
223 my @array_of_pedigree_objects;
224 my $progeny_increment = 1;
225 my $paternal_parent_not_required;
226 my $number_of_flowers_cvterm;
227 my $number_of_seeds_cvterm;
230 if ($cross_type eq "open" || $cross_type eq "bulk_open") {
231 $paternal_parent_not_required = 1;
234 print STDERR
"Adding Cross... Maternal: $maternal Paternal: $paternal Cross Type: $cross_type\n";
237 print STDERR
"User not logged in... not adding a cross.\n";
238 $c->stash->{rest
} = {error
=> "You need to be logged in to add a cross." };
242 $owner_name = $c->user()->get_object()->get_username();
244 if (!any
{ $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles) ) {
245 print STDERR
"User does not have sufficient privileges.\n";
246 $c->stash->{rest
} = {error
=> "you have insufficient privileges to add a cross." };
250 #check that progeny number is an integer less than maximum allowed
251 my $maximum_progeny_number = 999; #higher numbers break cross name convention
252 if ($progeny_number) {
253 if ((! $progeny_number =~ m/^\d+$/) or ($progeny_number > $maximum_progeny_number) or ($progeny_number < 1)) {
254 $c->stash->{rest
} = {error
=> "progeny number exceeds the maximum of $maximum_progeny_number or is invalid." };
259 #check that maternal name is not blank
260 if ($maternal eq "") {
261 $c->stash->{rest
} = {error
=> "maternal parent name cannot be blank." };
265 #if required, check that paternal parent name is not blank;
266 if ($paternal eq "" && !$paternal_parent_not_required) {
267 $c->stash->{rest
} = {error
=> "paternal parent name cannot be blank." };
271 #check that parents exist in the database
272 if (! $chado_schema->resultset("Stock::Stock")->find({name
=>$maternal,})){
273 $c->stash->{rest
} = {error
=> "maternal parent does not exist." };
277 if (!$paternal_parent_not_required) {
278 if (! $chado_schema->resultset("Stock::Stock")->find({name
=>$paternal,})){
279 $c->stash->{rest
} = {error
=> "paternal parent does not exist." };
284 #check that cross name does not already exist
285 if ($chado_schema->resultset("Stock::Stock")->find({name
=>$cross_name})){
286 $c->stash->{rest
} = {error
=> "cross name already exists." };
290 #check that progeny do not already exist
291 if ($chado_schema->resultset("Stock::Stock")->find({name
=>$cross_name.$prefix.'001'.$suffix,})){
292 $c->stash->{rest
} = {error
=> "progeny already exist." };
296 #objects to store cross information
297 my $cross_to_add = Bio
::GeneticRelationships
::Pedigree
->new(name
=> $cross_name, cross_type
=> $cross_type);
298 my $female_individual = Bio
::GeneticRelationships
::Individual
->new(name
=> $maternal);
299 $cross_to_add->set_female_parent($female_individual);
301 if (!$paternal_parent_not_required){
302 my $male_individual = Bio
::GeneticRelationships
::Individual
->new(name
=> $paternal);
303 $cross_to_add->set_male_parent($male_individual);
307 $cross_to_add->set_cross_type($cross_type);
308 $cross_to_add->set_name($cross_name);
311 #create array of pedigree objects to add, in this case just one pedigree
312 @array_of_pedigree_objects = ($cross_to_add);
313 $cross_add = CXGN
::Pedigree
::AddCrosses
315 chado_schema
=> $chado_schema,
316 phenome_schema
=> $phenome_schema,
317 #metadata_schema => $metadata_schema,
319 location
=> $location,
321 crosses
=> \
@array_of_pedigree_objects,
322 owner_name
=> $owner_name,
327 $cross_add->add_crosses();
330 $c->stash->{rest
} = { error
=> "Error creating the cross: $@" };
335 #create progeny if specified
336 if ($progeny_number) {
338 #create array of progeny names to add for this cross
339 while ($progeny_increment < $progeny_number + 1) {
340 $progeny_increment = sprintf "%03d", $progeny_increment;
341 my $stock_name = $cross_name.$prefix.$progeny_increment.$suffix;
342 push @progeny_names, $stock_name;
343 $progeny_increment++;
346 #add array of progeny to the cross
347 $progeny_add = CXGN
::Pedigree
::AddProgeny
349 chado_schema
=> $chado_schema,
350 phenome_schema
=> $phenome_schema,
352 cross_name
=> $cross_name,
353 progeny_names
=> \
@progeny_names,
354 owner_name
=> $owner_name,
356 $progeny_add->add_progeny();
360 #add number of flowers as an experimentprop if specified
361 if ($number_of_flowers) {
362 my $cross_add_info = CXGN
::Pedigree
::AddCrossInfo
->new({ chado_schema
=> $chado_schema, cross_name
=> $cross_name} );
363 $cross_add_info->set_number_of_flowers($number_of_flowers);
364 $cross_add_info->add_info();
367 #add number of seeds as an experimentprop if specified
368 if ($number_of_seeds) {
369 my $cross_add_info = CXGN
::Pedigree
::AddCrossInfo
->new({ chado_schema
=> $chado_schema, cross_name
=> $cross_name} );
370 $cross_add_info->set_number_of_seeds($number_of_seeds);
371 $cross_add_info->add_info();
376 $c->stash->{rest
} = { error
=> "An error occurred: $@"};
380 $c->stash->{rest
} = { error
=> '', };
383 sub get_cross_relationships
:Path
('/cross/ajax/relationships') :Args
(1) {
386 my $cross_id = shift;
388 my $schema = $c->dbic_schema("Bio::Chado::Schema");
390 my $cross = $schema->resultset("Stock::Stock")->find( { stock_id
=> $cross_id });
392 if ($cross && $cross->type()->name() ne "cross") {
393 $c->stash->{rest
} = { error
=> 'This entry is not of type cross and cannot be displayed using this page.' };
397 my $crs = $schema->resultset("Stock::StockRelationship")->search( { object_id
=> $cross_id } );
399 my $maternal_parent = "";
400 my $paternal_parent = "";
403 foreach my $child ($crs->all()) {
404 if ($child->type->name() eq "female_parent") {
405 $maternal_parent = [ $child->subject->name, $child->subject->stock_id() ];
407 if ($child->type->name() eq "male_parent") {
408 $paternal_parent = [ $child->subject->name, $child->subject->stock_id() ];
410 if ($child->type->name() eq "member_of") {
411 push @progeny, [ $child->subject->name, $child->subject->stock_id() ];
415 $c->stash->{rest
} = { maternal_parent
=> $maternal_parent,
416 paternal_parent
=> $paternal_parent,
417 progeny
=> \
@progeny,
422 sub get_cross_properties
:Path
('/cross/ajax/properties') Args
(1) {
425 my $cross_id = shift;
427 my $schema = $c->dbic_schema("Bio::Chado::Schema");
429 my $rs = $schema->resultset("NaturalDiversity::NdExperimentprop")->search( { 'nd_experiment_stocks.stock_id' => $cross_id }, { join => { 'nd_experiment' => 'nd_experiment_stocks' }});
433 print STDERR
"PROPS LEN ".$rs->count()."\n";
435 while (my $prop = $rs->next()) {
436 push @
{$props->{$prop->type->name()}}, [ $prop->get_column('value'), $prop->get_column('nd_experimentprop_id') ];
439 print STDERR Dumper
($props);
440 $c->stash->{rest
} = { props
=> $props };
445 sub save_property_check
:Path
('/cross/property/check') Args
(1) {
448 my $cross_id = shift;
450 my $type = $c->req->param("type");
451 my $value = $c->req->param("value");
454 my $schema = $c->dbic_schema("Bio::Chado::Schema");
455 my $type_row = $schema->resultset('Cv::Cvterm')->find( { name
=> $type } );
458 $c->stash->{rest
} = { error
=> "The type '$type' does not exist in the database" };
462 my $type_id = $type_row->cvterm_id();
464 my %suggested_values = (
465 cross_type
=> { 'biparental'=>1, 'self'=>1, 'open pollinated'=>1, 'bulk'=>1, 'bulk selfed'=>1, 'bulk and open pollinated'=>1, 'doubled haplotype'=>1 },
466 number_of_flowers
=> '\d+',
467 number_of_seeds
=> '\d+',
468 date
=> '\d{4}\\/\d{2}\\/\d{2}',
474 my %example_values = (
475 date
=> '2014/03/29',
477 number_of_flowers
=> 23,
478 number_of_seeds
=> 42,
479 operator
=> 'Alfonso',
480 cross_name
=> 'nextgen_cross',
483 if (ref($suggested_values{$type})) {
484 if (!exists($suggested_values{$type}->{$value})) { # don't make this case insensitive!
485 $c->stash->{rest
} = { message
=> 'The provided value is not in the suggested list of terms. This could affect downstream data processing.' };
490 if ($value !~ m/^$suggested_values{$type}$/) {
491 $c->stash->{rest
} = { error
=> 'The provided value is not of the correct type. Format example: "'.$example_values{$type}.'"' };
495 $c->stash->{rest
} = { success
=> 1 };
498 sub cross_property_save
:Path
('/cross/property/save') Args
(1) {
503 $c->stash->{rest
} = { error
=> "You must be logged in add properties." };
506 if (!($c->user()->has_role('submitter') or $c->user()->has_role('curator'))) {
507 $c->stash->{rest
} = { error
=> "You do not have sufficient privileges to add properties." };
511 my $cross_id = $c->req->param("cross_id");
512 my $type = $c->req->param("type");
513 my $value = $c->req->param("value");
515 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
517 my $exp_id = $schema->resultset("NaturalDiversity::NdExperiment")->search( { 'nd_experiment_stocks.stock_id' => $cross_id }, { join => 'nd_experiment_stocks' })->first()->get_column('nd_experiment_id');
520 my $type_row = $schema->resultset("Cv::Cvterm")->find( { 'me.name' => $type, 'cv.name' => 'nd_experiment_property' }, { join => { 'cv'}});
522 $type_id = $type_row->cvterm_id();
525 $c->stash->{rest
} = { error
=> "The type $type does not exist in the database." };
529 my $rs = $schema->resultset("NaturalDiversity::NdExperimentprop")->search( { 'nd_experiment_stocks.stock_id' => $cross_id, 'me.type_id' => $type_id }, { join => { 'nd_experiment' => { 'nd_experiment_stocks' }}});
531 my $row = $rs->first();
533 $row = $schema->resultset("NaturalDiversity::NdExperimentprop")->create( { 'nd_experiment_stocks.stock_id' => $cross_id, 'me.type_id' => $type_id, 'me.value'=>$value, 'me.nd_experiment_id' => $exp_id }, { join => {'nd_experiment' => {'nd_experiment_stocks' }}});
538 $row->set_column( 'value' => $value );
542 $c->stash->{rest
} = { success
=> 1 };
546 sub add_more_progeny
:Path
('/cross/progeny/add') Args
(1) {
549 my $cross_id = shift;
552 $c->stash->{rest
} = { error
=> "You must be logged in add progeny." };
555 if (!($c->user()->has_role('submitter') or $c->user()->has_role('curator'))) {
556 $c->stash->{rest
} = { error
=> "You do not have sufficient privileges to add progeny." };
560 my $basename = $c->req->param("basename");
561 my $start_number = $c->req->param("start_number");
562 my $progeny_count = $c->req->param("progeny_count");
563 my $cross_name = $c->req->param("cross_name");
565 my @progeny_names = ();
566 foreach my $n (1..$progeny_count) {
567 push @progeny_names, $basename. (sprintf "%03d", $n + $start_number -1);
570 print STDERR Dumper
(\
@progeny_names);
572 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
573 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
574 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
575 my $dbh = $c->dbc->dbh;
577 my $owner_name = $c->user()->get_object()->get_username();
579 my $progeny_add = CXGN
::Pedigree
::AddProgeny
581 chado_schema
=> $chado_schema,
582 phenome_schema
=> $phenome_schema,
584 cross_name
=> $cross_name,
585 progeny_names
=> \
@progeny_names,
586 owner_name
=> $owner_name,
588 if (!$progeny_add->add_progeny()){
589 $c->stash->{rest
} = {error_string
=> "Error adding progeny. Please change the input parameters and try again.",};
590 #should delete crosses and other progeny if add progeny fails?
594 $c->stash->{rest
} = { success
=> 1};