Added eval; site now shows clean dataset missing message instead of server error...
[sgn.git] / lib / SGN / Controller / AJAX / Cross.pm
blobf8474a8112f5c3843b7c9e5d411f3cc9a67d74ce
2 =head1 NAME
4 SGN::Controller::AJAX::Cross - a REST controller class to provide the
5 functions for adding crosses
7 =head1 DESCRIPTION
9 Add a new cross or upload a file containing crosses to add
11 =head1 AUTHOR
13 Jeremy Edwards <jde22@cornell.edu>
14 Lukas Mueller <lam87@cornell.edu>
15 Titima Tantikanjana <tt15@cornell.edu>
17 =cut
19 package SGN::Controller::AJAX::Cross;
21 use Moose;
22 use Try::Tiny;
23 use DateTime;
24 use Time::HiRes qw(time);
25 use POSIX qw(strftime);
26 use Data::Dumper;
27 use File::Basename qw | basename dirname|;
28 use File::Copy;
29 use File::Slurp;
30 use File::Spec::Functions;
31 use Digest::MD5;
32 use List::MoreUtils qw /any /;
33 use List::MoreUtils 'none';
34 use Bio::GeneticRelationships::Pedigree;
35 use Bio::GeneticRelationships::Individual;
36 use CXGN::UploadFile;
37 use CXGN::Pedigree::AddCrossingtrial;
38 use CXGN::Pedigree::AddCrosses;
39 use CXGN::Pedigree::AddProgeny;
40 use CXGN::Pedigree::AddProgeniesExistingAccessions;
41 use CXGN::Pedigree::AddCrossInfo;
42 use CXGN::Pedigree::AddFamilyNames;
43 use CXGN::Pedigree::AddPopulations;
44 use CXGN::Pedigree::AddCrossTransaction;
45 use CXGN::Pedigree::ParseUpload;
46 use CXGN::Trial::Folder;
47 use CXGN::Trial::TrialLayout;
48 use CXGN::Stock::StockLookup;
49 use Carp;
50 use File::Path qw(make_path);
51 use File::Spec::Functions qw / catfile catdir/;
52 use CXGN::Cross;
53 use JSON;
54 use Tie::UrlEncoder; our(%urlencode);
55 use LWP::UserAgent;
56 use HTML::Entities;
57 use URI::Encode qw(uri_encode uri_decode);
58 use Sort::Key::Natural qw(natsort);
61 BEGIN { extends 'Catalyst::Controller::REST' }
63 __PACKAGE__->config(
64 default => 'application/json',
65 stash_key => 'rest',
66 map => { 'application/json' => 'JSON', 'text/html' => 'JSON' },
69 sub upload_cross_file : Path('/ajax/cross/upload_crosses_file') : ActionClass('REST') { }
71 sub upload_cross_file_POST : Args(0) {
72 my ($self, $c) = @_;
73 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
74 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
75 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
76 my $dbh = $c->dbc->dbh;
77 my $manage_page_crossing_experiment_id = $c->req->param('upload_crosses_crossing_experiment_id');
78 my $experiment_page_crossing_experiment_id = $c->req->param('experiment_id');
79 my $crossing_trial_id;
80 if ($manage_page_crossing_experiment_id) {
81 $crossing_trial_id = $manage_page_crossing_experiment_id;
82 } elsif ($experiment_page_crossing_experiment_id) {
83 $crossing_trial_id = $experiment_page_crossing_experiment_id;
85 my $crosses_simple_upload = $c->req->upload('xls_crosses_simple_file');
86 my $crosses_plots_upload = $c->req->upload('xls_crosses_plots_file');
87 my $crosses_plants_upload = $c->req->upload('xls_crosses_plants_file');
88 my $crosses_simplified_parents_upload = $c->req->upload('xls_crosses_simplified_parents_file');
90 my $upload;
91 my $upload_type;
92 if ($crosses_plots_upload) {
93 $upload = $crosses_plots_upload;
94 $upload_type = 'CrossesExcelFormat';
96 if ($crosses_plants_upload) {
97 $upload = $crosses_plants_upload;
98 $upload_type = 'CrossesExcelFormat';
101 if ($crosses_simple_upload) {
102 $upload = $crosses_simple_upload;
103 $upload_type = 'CrossesSimpleExcel';
106 if ($crosses_simplified_parents_upload) {
107 $upload = $crosses_simplified_parents_upload;
108 $upload_type = 'CrossesSimplifiedParentInfoExcel';
111 my $parser;
112 my $parsed_data;
113 my $upload_original_name = $upload->filename();
114 my $upload_tempfile = $upload->tempname;
115 my $subdirectory = "cross_upload";
116 my $archived_filename_with_path;
117 my $md5;
118 my $validate_file;
119 my $parsed_file;
120 my $parse_errors;
121 my %parsed_data;
122 my $time = DateTime->now();
123 my $timestamp = $time->ymd()."_".$time->hms();
124 my $user_role;
125 my $user_id;
126 my $user_name;
127 my $owner_name;
128 my $session_id = $c->req->param("sgn_session_id");
130 if ($session_id){
131 my $dbh = $c->dbc->dbh;
132 my @user_info = CXGN::Login->new($dbh)->query_from_cookie($session_id);
133 if (!$user_info[0]){
134 $c->stash->{rest} = {error=>'You must be logged in to upload crosses!'};
135 $c->detach();
137 $user_id = $user_info[0];
138 $user_role = $user_info[1];
139 my $p = CXGN::People::Person->new($dbh, $user_id);
140 $user_name = $p->get_username;
141 } else{
142 if (!$c->user){
143 $c->stash->{rest} = {error=>'You must be logged in to upload crosses!'};
144 $c->detach();
146 $user_id = $c->user()->get_object()->get_sp_person_id();
147 $user_name = $c->user()->get_object()->get_username();
148 $user_role = $c->user->get_object->get_user_type();
151 if (($user_role ne 'curator') && ($user_role ne 'submitter')) {
152 $c->stash->{rest} = {error=>'Only a submitter or a curator can upload crosses'};
153 $c->detach();
156 my $uploader = CXGN::UploadFile->new({
157 tempfile => $upload_tempfile,
158 subdirectory => $subdirectory,
159 archive_path => $c->config->{archive_path},
160 archive_filename => $upload_original_name,
161 timestamp => $timestamp,
162 user_id => $user_id,
163 user_role => $user_role
166 ## Store uploaded temporary file in arhive
167 $archived_filename_with_path = $uploader->archive();
168 $md5 = $uploader->get_md5($archived_filename_with_path);
169 if (!$archived_filename_with_path) {
170 $c->stash->{rest} = {error => "Could not save file $upload_original_name in archive",};
171 return;
173 unlink $upload_tempfile;
175 my $cross_additional_info_string = $c->config->{cross_additional_info};
176 my @additional_info = split ',', $cross_additional_info_string;
177 my $cross_additional_info = \@additional_info;
179 #parse uploaded file with appropriate plugin
180 $parser = CXGN::Pedigree::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path, cross_additional_info => $cross_additional_info);
181 $parser->load_plugin($upload_type);
182 $parsed_data = $parser->parse();
183 #print STDERR "Dumper of parsed data:\t" . Dumper($parsed_data) . "\n";
185 if (!$parsed_data){
186 my $return_error = '';
187 my $parse_errors;
188 if (!$parser->has_parse_errors() ){
189 $c->stash->{rest} = {error_string => "Could not get parsing errors"};
190 } else {
191 $parse_errors = $parser->get_parse_errors();
192 #print STDERR Dumper $parse_errors;
194 foreach my $error_string (@{$parse_errors->{'error_messages'}}){
195 $return_error .= $error_string."<br>";
198 $c->stash->{rest} = {error_string => $return_error, missing_accessions => $parse_errors->{'missing_accessions'}, missing_plots => $parse_errors->{'missing_plots'}, missing_accessions_or_crosses => $parse_errors->{'missing_accessions_or_crosses'}};
199 $c->detach();
202 my $md_row = $metadata_schema->resultset("MdMetadata")->create({create_person_id => $user_id});
203 $md_row->insert();
204 my $upload_file = CXGN::UploadFile->new();
205 my $md5 = $upload_file->get_md5($archived_filename_with_path);
206 my $md5checksum = $md5->hexdigest();
207 my $file_row = $metadata_schema->resultset("MdFiles")->create({
208 basename => basename($archived_filename_with_path),
209 dirname => dirname($archived_filename_with_path),
210 filetype => 'crosses',
211 md5checksum => $md5checksum,
212 metadata_id => $md_row->metadata_id(),
214 my $file_id = $file_row->file_id();
216 my $cross_add = CXGN::Pedigree::AddCrosses->new({
217 chado_schema => $chado_schema,
218 phenome_schema => $phenome_schema,
219 metadata_schema => $metadata_schema,
220 dbh => $dbh,
221 crossing_trial_id => $crossing_trial_id,
222 crosses => $parsed_data->{crosses},
223 user_id => $user_id,
224 file_id => $file_id
227 #validate the crosses
228 if (!$cross_add->validate_crosses()){
229 $c->stash->{rest} = {error_string => "Error validating crosses",};
230 return;
233 #add the crosses
234 if (!$cross_add->add_crosses()){
235 $c->stash->{rest} = {error_string => "Error adding crosses",};
236 return;
239 if ($parsed_data->{'additional_info'}) {
240 my %cross_additional_info = %{$parsed_data->{additional_info}};
241 foreach my $cross_name (keys %cross_additional_info) {
242 my %info_hash = %{$cross_additional_info{$cross_name}};
243 foreach my $info_type (keys %info_hash) {
244 my $value = $info_hash{$info_type};
245 my $cross_add_info = CXGN::Pedigree::AddCrossInfo->new({
246 chado_schema => $chado_schema,
247 cross_name => $cross_name,
248 key => $info_type,
249 value => $value,
250 data_type => 'cross_additional_info'
253 $cross_add_info->add_info();
255 if (!$cross_add_info->add_info()){
256 $c->stash->{rest} = {error_string => "Error saving info",};
257 return;
264 $c->stash->{rest} = {success => "1",};
268 sub add_cross : Local : ActionClass('REST') { }
270 sub add_cross_POST :Args(0) {
271 my ($self, $c) = @_;
272 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
273 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
274 my $cross_name = $c->req->param('cross_name');
275 my $cross_type = $c->req->param('cross_type');
276 my $crossing_trial_id = $c->req->param('crossing_trial_id');
277 my $female_plot_id = $c->req->param('female_plot');
278 my $male_plot_id = $c->req->param('male_plot');
279 my $cross_combination = $c->req->param('cross_combination');
280 $cross_name =~ s/^\s+|\s+$//g; #trim whitespace from front and end.
282 print STDERR "CROSS COMBINATION=".Dumper($cross_combination)."\n";
283 my $user_id;
284 if (!$c->user()) {
285 print STDERR "User not logged in... not adding a cross.\n";
286 $c->stash->{rest} = {error => "You need to be logged in to add a cross." };
287 return;
290 if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles) ) {
291 print STDERR "User does not have sufficient privileges.\n";
292 $c->stash->{rest} = {error => "you have insufficient privileges to add a cross." };
293 return;
294 } else {
295 $user_id = $c->user()->get_object()->get_sp_person_id();
298 if ($cross_type eq "polycross") {
299 print STDERR "Handling a polycross\n";
300 my @maternal_parents = split (',', $c->req->param('maternal_parents'));
301 print STDERR "Maternal parents array:" . @maternal_parents . "\n Maternal parents with ref:" . \@maternal_parents . "\n Maternal parents with dumper:". Dumper(@maternal_parents) . "\n";
302 my $paternal = $cross_name . '_population';
303 my $population_add = CXGN::Pedigree::AddPopulations->new({ schema => $chado_schema, phenome_schema => $phenome_schema, user_id => $user_id, name => $paternal, members => \@maternal_parents} );
304 $population_add->add_population();
305 $cross_type = 'polycross';
306 print STDERR "Scalar maternatal paretns:" . scalar @maternal_parents;
307 for (my $i = 0; $i < scalar @maternal_parents; $i++) {
308 my $maternal = $maternal_parents[$i];
309 my $polycross_name = $cross_name . '_' . $maternal;
310 print STDERR "First polycross to add is $polycross_name with amternal $maternal and paternal $paternal\n";
311 my $success = $self->add_individual_cross($c, $chado_schema, $polycross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal);
312 if (!$success) {
313 return;
315 print STDERR "polycross addition $polycross_name worked successfully\n";
318 elsif ($cross_type eq "reciprocal") {
319 $cross_type = 'biparental';
320 my @maternal_parents = split (',', $c->req->param('maternal_parents'));
321 for (my $i = 0; $i < scalar @maternal_parents; $i++) {
322 my $maternal = $maternal_parents[$i];
323 for (my $j = 0; $j < scalar @maternal_parents; $j++) {
324 my $paternal = $maternal_parents[$j];
325 if ($maternal eq $paternal) {
326 next;
328 my $reciprocal_cross_name = $cross_name . '_' . $maternal . 'x' . $paternal . '_reciprocalcross';
329 my $success = $self->add_individual_cross($c, $chado_schema, $reciprocal_cross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal);
330 if (!$success) {
331 return;
336 elsif ($cross_type eq "multicross") {
337 $cross_type = 'biparental';
338 my @maternal_parents = split (',', $c->req->param('maternal_parents'));
339 my @paternal_parents = split (',', $c->req->param('paternal_parents'));
340 for (my $i = 0; $i < scalar @maternal_parents; $i++) {
341 my $maternal = $maternal_parents[$i];
342 my $paternal = $paternal_parents[$i];
343 my $multicross_name = $cross_name . '_' . $maternal . 'x' . $paternal . '_multicross';
344 my $success = $self->add_individual_cross($c, $chado_schema, $multicross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal);
345 if (!$success) {
346 return;
350 else {
351 my $maternal = $c->req->param('maternal');
352 my $paternal = $c->req->param('paternal');
353 my $success = $self->add_individual_cross($c, $chado_schema, $cross_name, $cross_type, $crossing_trial_id, $female_plot_id, $male_plot_id, $maternal, $paternal, $cross_combination);
354 if (!$success) {
355 return;
358 $c->stash->{rest} = {success => "1",};
361 sub get_cross_relationships :Path('/cross/ajax/relationships') :Args(1) {
362 my $self = shift;
363 my $c = shift;
364 my $cross_id = shift;
366 my $schema = $c->dbic_schema("Bio::Chado::Schema");
368 my $cross = $schema->resultset("Stock::Stock")->find( { stock_id => $cross_id });
370 if ($cross && $cross->type()->name() ne "cross") {
371 $c->stash->{rest} = { error => 'This entry is not of type cross and cannot be displayed using this page.' };
372 return;
375 my $cross_obj = CXGN::Cross->new({schema=>$schema, cross_stock_id=>$cross_id});
376 my ($maternal_parent, $paternal_parent, $progeny) = $cross_obj->get_cross_relationships();
378 $c->stash->{rest} = {
379 maternal_parent => $maternal_parent,
380 paternal_parent => $paternal_parent,
381 progeny => $progeny,
386 sub get_membership :Path('/ajax/cross/membership') :Args(1) {
387 my $self = shift;
388 my $c = shift;
389 my $cross_id = shift;
391 my $schema = $c->dbic_schema("Bio::Chado::Schema");
393 my $cross = $schema->resultset("Stock::Stock")->find( { stock_id => $cross_id });
395 if ($cross && $cross->type()->name() ne "cross") {
396 $c->stash->{rest} = { error => 'This entry is not of type cross and cannot be displayed using this page.' };
397 return;
400 my $cross_obj = CXGN::Cross->new({schema=>$schema, cross_stock_id=>$cross_id});
401 my $result = $cross_obj->get_membership();
402 my @membership_info;
404 foreach my $r (@$result){
405 my ($crossing_experiment_id, $crossing_experiment_name, $description, $family_id, $family_name) =@$r;
406 push @membership_info, [qq{<a href="/breeders/trial/$crossing_experiment_id">$crossing_experiment_name</a>}, $description, qq{<a href = "/family/$family_id/">$family_name</a>}];
409 $c->stash->{rest} = { data => \@membership_info };
414 sub get_cross_parents :Path('/ajax/cross/accession_plot_plant_parents') Args(1) {
415 my $self = shift;
416 my $c = shift;
417 my $cross_id = shift;
419 my $schema = $c->dbic_schema("Bio::Chado::Schema");
420 my $cross = $schema->resultset("Stock::Stock")->find( { stock_id => $cross_id });
422 if ($cross && $cross->type()->name() ne "cross") {
423 $c->stash->{rest} = { error => 'This entry is not of type cross and cannot be displayed using this page.' };
424 return;
427 my $cross_obj = CXGN::Cross->new({schema=>$schema, cross_stock_id=>$cross_id});
428 my $result = $cross_obj->cross_parents();
429 my @cross_parent_info;
431 foreach my $r (@$result){
432 my ($female_accession_id, $female_accession_name, $female_plot_id, $female_plot_name, $female_plant_id, $female_plant_name, $male_accession_id, $male_accession_name, $male_plot_id, $male_plot_name, $male_plant_id, $male_plant_name, $cross_type, $cross_combination, $female_ploidy, $male_ploidy) = @$r;
433 push @cross_parent_info, [$cross_combination, $cross_type,
434 qq{<a href="/stock/$female_accession_id/view">$female_accession_name</a>},
435 $female_ploidy,
436 qq{<a href="/stock/$male_accession_id/view">$male_accession_name</a>},
437 $male_ploidy,
438 qq{<a href="/stock/$female_plot_id/view">$female_plot_name</a>},
439 qq{<a href="/stock/$male_plot_id/view">$male_plot_name</a>},
440 qq{<a href="/stock/$female_plant_id/view">$female_plant_name</a>},
441 qq{<a href="/stock/$male_plant_id/view">$male_plant_name</a>}];
444 $c->stash->{rest} = {data => \@cross_parent_info}
449 sub get_cross_properties :Path('/ajax/cross/properties') Args(1) {
450 my $self = shift;
451 my $c = shift;
452 my $cross_id = shift;
454 my $schema = $c->dbic_schema("Bio::Chado::Schema");
455 my $cross_info_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'crossing_metadata_json', 'stock_property')->cvterm_id();
456 my $cross_info = $schema->resultset("Stock::Stockprop")->find({stock_id => $cross_id, type_id => $cross_info_cvterm});
458 my $cross_json_string;
459 if($cross_info){
460 $cross_json_string = $cross_info->value();
463 my $cross_props_hash ={};
464 if($cross_json_string){
465 $cross_props_hash = decode_json $cross_json_string;
468 my $cross_properties = $c->config->{cross_properties};
469 my @column_order = split ',',$cross_properties;
470 my @props;
471 my @row;
472 foreach my $key (@column_order){
473 push @row, $cross_props_hash->{$key};
476 push @props,\@row;
477 $c->stash->{rest} = {data => \@props};
482 sub get_cross_tissue_culture_summary :Path('/ajax/cross/tissue_culture_summary') Args(1) {
483 my $self = shift;
484 my $c = shift;
485 my $cross_id = shift;
486 my $schema = $c->dbic_schema("Bio::Chado::Schema");
488 my $cross_samples_obj = CXGN::Cross->new({schema=>$schema, cross_stock_id=>$cross_id});
489 my $cross_sample_data = $cross_samples_obj->get_cross_tissue_culture_samples();
491 my $embryo_ids = $cross_sample_data->{'Embryo IDs'};
492 my $subculture_ids = $cross_sample_data->{'Subculture IDs'};
493 my $rooting_ids = $cross_sample_data->{'Rooting IDs'};
494 my $weaning1_ids = $cross_sample_data->{'Weaning1 IDs'};
495 my $weaning2_ids = $cross_sample_data->{'Weaning2 IDs'};
496 my $screenhouse_ids = $cross_sample_data->{'Screenhouse IDs'};
497 my $hardening_ids = $cross_sample_data->{'Hardening IDs'};
498 my $openfield_ids = $cross_sample_data->{'Openfield IDs'};
500 my @embryo_ids_array;
501 my @subculture_ids_array;
502 my @rooting_ids_array;
503 my @weaning1_ids_array;
504 my @weaning2_ids_array;
505 my @screenhouse_ids_array;
506 my @hardening_ids_array;
507 my @openfield_ids_array;
509 if (defined $embryo_ids) {
510 @embryo_ids_array = @$embryo_ids;
513 if (defined $subculture_ids) {
514 @subculture_ids_array = @$subculture_ids;
517 if (defined $rooting_ids) {
518 @rooting_ids_array = @$rooting_ids;
521 if (defined $weaning1_ids) {
522 @weaning1_ids_array = @$weaning1_ids;
525 if (defined $weaning2_ids) {
526 @weaning2_ids_array = @$weaning2_ids;
529 if (defined $screenhouse_ids) {
530 @screenhouse_ids_array = @$screenhouse_ids;
533 if (defined $hardening_ids) {
534 @hardening_ids_array = @$hardening_ids;
537 if (defined $openfield_ids) {
538 @openfield_ids_array = @$openfield_ids;
541 my @all_rows;
542 my @each_row;
543 my $checkmark = qq{<img src="/img/checkmark_green.jpg"/>};
544 my $x_mark = qq{<img src="/img/x_mark_red.jpg"/>};
545 my @sorted_embryo_ids = natsort @embryo_ids_array;
547 foreach my $embryo_id (@sorted_embryo_ids) {
549 if ($embryo_id) {
550 push @each_row, $embryo_id;
553 if ($embryo_id ~~ @subculture_ids_array) {
554 push @each_row, $checkmark;
555 } else {
556 push @each_row, $x_mark;
559 if ($embryo_id ~~ @rooting_ids_array) {
560 push @each_row, $checkmark;
561 } else {
562 push @each_row, $x_mark;
565 if ($embryo_id ~~ @weaning1_ids_array) {
566 push @each_row, $checkmark;
567 } else {
568 push @each_row, $x_mark;
571 if ($embryo_id ~~ @weaning2_ids_array) {
572 push @each_row, $checkmark;
573 } else {
574 push @each_row, $x_mark;
577 if ($embryo_id ~~ @screenhouse_ids_array) {
578 push @each_row, $checkmark;
579 } else {
580 push @each_row, $x_mark;
583 if ($embryo_id ~~ @hardening_ids_array) {
584 push @each_row, $checkmark;
585 } else {
586 push @each_row, $x_mark;
589 if ($embryo_id ~~ @openfield_ids_array) {
590 push @each_row, $checkmark;
591 } else {
592 push @each_row, $x_mark;
595 push @all_rows, [@each_row];
596 @each_row =();
598 # print STDERR "SORTED EMBRYO IDS =".Dumper(\@sorted_embryo_ids)."\n";
599 $c->stash->{rest} = { data => \@all_rows };
603 sub save_property_check :Path('/cross/property/check') Args(1) {
604 my $self = shift;
605 my $c = shift;
606 my $cross_id = shift;
608 my $type = $c->req->param("type");
609 my $value = $c->req->param("value");
612 my $schema = $c->dbic_schema("Bio::Chado::Schema");
614 if ($type =~ m/Number/ || $type =~ m/Days/) { $type = 'number';}
615 if ($type =~ m/Date/) { $type = 'date';}
617 my %suggested_values = (
618 # cross_name => '.*',
619 # cross_type => { 'biparental'=>1, 'self'=>1, 'open'=>1, 'bulk'=>1, 'bulk_self'=>1, 'bulk_open'=>1, 'doubled_haploid'=>1 },
620 number => '\d+',
621 date => '\d{4}\\/\d{2}\\/\d{2}',
624 my %example_values = (
625 date => '2014/03/29',
626 number => 20,
627 # cross_type => 'biparental',
628 # cross_name => 'nextgen_cross',
631 if (ref($suggested_values{$type})) {
632 if (!exists($suggested_values{$type}->{$value})) { # don't make this case insensitive!
633 $c->stash->{rest} = { message => 'The provided value is not in the suggested list of terms. This could affect downstream data processing.' };
634 return;
637 else {
638 if ($value !~ m/^$suggested_values{$type}$/) {
639 $c->stash->{rest} = { error => 'The provided value is not in a valid format. Format example: "'.$example_values{$type}.'"' };
640 return;
643 $c->stash->{rest} = { success => 1 };
648 sub cross_property_save :Path('/cross/property/save') Args(1) {
649 my $self = shift;
650 my $c = shift;
652 if (!$c->user()) {
653 $c->stash->{rest} = { error => "You must be logged in to add properties." };
654 return;
656 if (!($c->user()->has_role('submitter') or $c->user()->has_role('curator'))) {
657 $c->stash->{rest} = { error => "You do not have sufficient privileges to add properties." };
658 return;
661 my $cross_id = $c->req->param("cross_id");
662 my $type = $c->req->param("type");
663 my $value = $c->req->param("value");
664 my $data_type = $c->req->param("data_type");
665 # print STDERR "DATA TYPE =".Dumper($data_type)."\n";
666 # print STDERR "TYPE =".Dumper($type)."\n";
667 # print STDERR "VALUE =".Dumper($value)."\n";
669 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
670 my $cross_name = $schema->resultset("Stock::Stock")->find({stock_id => $cross_id})->uniquename();
672 my $cross_add_info = CXGN::Pedigree::AddCrossInfo->new({
673 chado_schema => $schema,
674 cross_name => $cross_name,
675 key => $type,
676 value => $value,
677 data_type => $data_type
679 $cross_add_info->add_info();
681 if (!$cross_add_info->add_info()){
682 $c->stash->{rest} = {error_string => "Error saving info",};
683 return;
686 $c->stash->{rest} = { success => 1};
689 sub add_more_progeny :Path('/cross/progeny/add') Args(1) {
690 my $self = shift;
691 my $c = shift;
692 my $cross_id = shift;
694 if (!$c->user()) {
695 $c->stash->{rest} = { error => "You must be logged in add progeny." };
696 return;
698 if (!($c->user()->has_role('submitter') or $c->user()->has_role('curator'))) {
699 $c->stash->{rest} = { error => "You do not have sufficient privileges to add progeny." };
700 return;
703 my $basename = $c->req->param("basename");
704 my $start_number = $c->req->param("start_number");
705 my $progeny_count = $c->req->param("progeny_count");
706 my $cross_name = $c->req->param("cross_name");
708 my @progeny_names = ();
709 foreach my $n (1..$progeny_count) {
710 push @progeny_names, $basename. (sprintf "%03d", $n + $start_number -1);
713 #print STDERR Dumper(\@progeny_names);
715 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
716 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
717 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
718 my $dbh = $c->dbc->dbh;
720 my $owner_name = $c->user()->get_object()->get_username();
722 my $progeny_add = CXGN::Pedigree::AddProgeny
723 ->new({
724 chado_schema => $chado_schema,
725 phenome_schema => $phenome_schema,
726 dbh => $dbh,
727 cross_name => $cross_name,
728 progeny_names => \@progeny_names,
729 owner_name => $owner_name,
731 if (!$progeny_add->add_progeny()){
732 $c->stash->{rest} = {error_string => "Error adding progeny. Please change the input parameters and try again.",};
733 #should delete crosses and other progeny if add progeny fails?
734 return;
737 $c->stash->{rest} = { success => 1};
742 #my $new_cross = CXGN::Cross->new({ schema=>schema });
743 #$new_cross->female_parent($fjfj);
744 #$new_cross->male_parent(kdkjf);
745 #$new_cross->location(kjlsdlkjdfskj);
746 #...type
747 #...cross_name
748 #...plots...
749 #$new_cross->store();
751 sub add_individual_cross {
752 my $self = shift;
753 my $c = shift;
754 my $chado_schema = shift;
755 my $cross_name = shift;
756 my $cross_type = shift;
757 my $crossing_trial_id = shift;
758 my $female_plot_id = shift;
759 my $female_plot;
760 my $male_plot_id = shift;
761 my $male_plot;
762 my $maternal = shift;
763 my $paternal = shift;
764 my $cross_combination = shift;
766 my $owner_name = $c->user()->get_object()->get_username();
767 my $user_id = $c->user()->get_object()->get_sp_person_id();
768 my @progeny_names;
769 my $progeny_increment = 1;
770 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
771 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
772 my $dbh = $c->dbc->dbh;
773 my $prefix = $c->req->param('prefix');
774 my $suffix = $c->req->param('suffix');
775 my $progeny_number = $c->req->param('progeny_number');
776 my $visible_to_role = $c->req->param('visible_to_role');
778 if ($female_plot_id){
779 my $female_plot_rs = $chado_schema->resultset("Stock::Stock")->find({stock_id => $female_plot_id});
780 $female_plot = $female_plot_rs->name();
783 if ($male_plot_id){
784 my $male_plot_rs = $chado_schema->resultset("Stock::Stock")->find({stock_id => $male_plot_id});
785 $male_plot = $male_plot_rs->name();
789 #check that progeny number is an integer less than maximum allowed
790 my $maximum_progeny_number = 999; #higher numbers break cross name convention
791 if ($progeny_number) {
792 if ((! $progeny_number =~ m/^\d+$/) or ($progeny_number > $maximum_progeny_number) or ($progeny_number < 1)) {
793 $c->stash->{rest} = {error => "progeny number exceeds the maximum of $maximum_progeny_number or is invalid." };
794 return 0;
798 #check that maternal name is not blank
799 if ($maternal eq "") {
800 $c->stash->{rest} = {error => "Female parent name cannot be blank." };
801 return 0;
804 #if required, check that paternal parent name is not blank;
805 if ($paternal eq "" && ($cross_type ne "open") && ($cross_type ne "bulk_open")) {
806 $c->stash->{rest} = {error => "Male parent name cannot be blank." };
807 return 0;
810 #check that parents exist in the database
811 if (! $chado_schema->resultset("Stock::Stock")->find({uniquename=>$maternal,})){
812 $c->stash->{rest} = {error => "Female parent does not exist." };
813 return 0;
816 if ($paternal) {
817 if (! $chado_schema->resultset("Stock::Stock")->find({uniquename=>$paternal,})){
818 $c->stash->{rest} = {error => "Male parent does not exist." };
819 return 0;
823 #check that cross name does not already exist
824 if ($chado_schema->resultset("Stock::Stock")->find({uniquename=>$cross_name})){
825 $c->stash->{rest} = {error => "Cross Unique ID already exists." };
826 return 0;
829 #check that progeny do not already exist
830 if ($chado_schema->resultset("Stock::Stock")->find({uniquename=>$cross_name.$prefix.'001'.$suffix,})){
831 $c->stash->{rest} = {error => "progeny already exist." };
832 return 0;
835 #objects to store cross information
836 my $cross_to_add = Bio::GeneticRelationships::Pedigree->new(name => $cross_name, cross_type => $cross_type, cross_combination => $cross_combination,);
837 my $female_individual = Bio::GeneticRelationships::Individual->new(name => $maternal);
838 $cross_to_add->set_female_parent($female_individual);
840 if ($paternal) {
841 my $male_individual = Bio::GeneticRelationships::Individual->new(name => $paternal);
842 $cross_to_add->set_male_parent($male_individual);
845 if ($female_plot) {
846 my $female_plot_individual = Bio::GeneticRelationships::Individual->new(name => $female_plot);
847 $cross_to_add->set_female_plot($female_plot_individual);
850 if ($male_plot) {
851 my $male_plot_individual = Bio::GeneticRelationships::Individual->new(name => $male_plot);
852 $cross_to_add->set_male_plot($male_plot_individual);
855 $cross_to_add->set_cross_type($cross_type);
856 $cross_to_add->set_name($cross_name);
857 $cross_to_add->set_cross_combination($cross_combination);
859 eval {
860 #create array of pedigree objects to add, in this case just one pedigree
861 my @array_of_pedigree_objects = ($cross_to_add);
862 my $cross_add = CXGN::Pedigree::AddCrosses
863 ->new({
864 chado_schema => $chado_schema,
865 phenome_schema => $phenome_schema,
866 dbh => $dbh,
867 crossing_trial_id => $crossing_trial_id,
868 crosses => \@array_of_pedigree_objects,
869 user_id => $user_id,
872 #add the crosses
873 $cross_add->add_crosses();
876 if ($@) {
877 $c->stash->{rest} = { error => "Error creating the cross: $@" };
878 return 0;
881 eval {
882 #create progeny if specified
883 if ($progeny_number) {
884 #create array of progeny names to add for this cross
885 while ($progeny_increment < $progeny_number + 1) {
886 $progeny_increment = sprintf "%03d", $progeny_increment;
887 my $stock_name = $cross_name.$prefix.$progeny_increment.$suffix;
888 push @progeny_names, $stock_name;
889 $progeny_increment++;
892 #add array of progeny to the cross
893 my $progeny_add = CXGN::Pedigree::AddProgeny
894 ->new({
895 chado_schema => $chado_schema,
896 phenome_schema => $phenome_schema,
897 dbh => $dbh,
898 cross_name => $cross_name,
899 progeny_names => \@progeny_names,
900 owner_name => $owner_name,
902 $progeny_add->add_progeny();
906 if ($@) {
907 $c->stash->{rest} = { error => "An error occurred: $@"};
908 return 0;
910 return 1;
915 sub add_crossingtrial : Path('/ajax/cross/add_crossingtrial') : ActionClass('REST') {}
917 sub add_crossingtrial_POST :Args(0){
918 my ($self, $c) = @_;
919 my $schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
920 my $dbh = $c->dbc->dbh;
921 my $crossingtrial_name = $c->req->param('crossingtrial_name');
922 my $breeding_program_id = $c->req->param('crossingtrial_program_id');
923 my $location = $c->req->param('crossingtrial_location');
924 my $year = $c->req->param('year');
925 my $project_description = $c->req->param('project_description');
927 if (!$c->user()){
928 print STDERR "User not logged in... not adding a crossing experiment.\n";
929 $c->stash->{rest} = {error => "You need to be logged in to add a crossing experiment."};
930 return;
933 if (!any { $_ eq "curator" || $_ eq "submitter" } ($c->user()->roles)){
934 print STDERR "User does not have sufficient privileges.\n";
935 $c->stash->{rest} = {error => "you have insufficient privileges to add a crossing experiment." };
936 return;
939 my $user_id = $c->user()->get_object()->get_sp_person_id();
941 my $geolocation_lookup = CXGN::Location::LocationLookup->new(schema =>$schema);
942 $geolocation_lookup->set_location_name($location);
943 if(!$geolocation_lookup->get_geolocation()){
944 $c->stash->{rest}={error => "Location not found"};
945 return;
948 my $error;
949 eval{
950 my $add_crossingtrial = CXGN::Pedigree::AddCrossingtrial->new({
951 chado_schema => $schema,
952 dbh => $dbh,
953 breeding_program_id => $breeding_program_id,
954 year => $year,
955 project_description => $project_description,
956 crossingtrial_name => $crossingtrial_name,
957 nd_geolocation_id => $geolocation_lookup->get_geolocation()->nd_geolocation_id(),
958 owner_id => $user_id
960 my $store_return = $add_crossingtrial->save_crossingtrial();
961 if ($store_return->{error}){
962 $error = $store_return->{error};
966 if ($@) {
967 $c->stash->{rest} = {error => $@};
968 return;
971 if ($error){
972 $c->stash->{rest} = {error => $error};
973 } else {
974 $c->stash->{rest} = {success => 1};
978 sub upload_progenies : Path('/ajax/cross/upload_progenies') : ActionClass('REST'){ }
980 sub upload_progenies_POST : Args(0) {
981 my $self = shift;
982 my $c = shift;
983 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
984 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
985 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
986 my $dbh = $c->dbc->dbh;
987 my $upload = $c->req->upload('progenies_new_upload_file');
988 my $upload_type = 'ProgeniesExcel';
989 my $parser;
990 my $parsed_data;
991 my $upload_original_name = $upload->filename();
992 my $upload_tempfile = $upload->tempname;
993 my $subdirectory = "cross_upload";
994 my $archived_filename_with_path;
995 my $md5;
996 my $validate_file;
997 my $parsed_file;
998 my $parse_errors;
999 my %parsed_data;
1000 my $time = DateTime->now();
1001 my $timestamp = $time->ymd()."_".$time->hms();
1002 my $user_role;
1003 my $user_id;
1004 my $user_name;
1005 my $owner_name;
1006 # my $upload_file_type = "crosses excel";#get from form when more options are added
1007 my $session_id = $c->req->param("sgn_session_id");
1009 if ($session_id){
1010 my $dbh = $c->dbc->dbh;
1011 my @user_info = CXGN::Login->new($dbh)->query_from_cookie($session_id);
1012 if (!$user_info[0]){
1013 $c->stash->{rest} = {error=>'You must be logged in to upload progenies!'};
1014 $c->detach();
1016 $user_id = $user_info[0];
1017 $user_role = $user_info[1];
1018 my $p = CXGN::People::Person->new($dbh, $user_id);
1019 $user_name = $p->get_username;
1020 } else{
1021 if (!$c->user){
1022 $c->stash->{rest} = {error=>'You must be logged in to upload progenies!'};
1023 $c->detach();
1025 $user_id = $c->user()->get_object()->get_sp_person_id();
1026 $user_name = $c->user()->get_object()->get_username();
1027 $user_role = $c->user->get_object->get_user_type();
1030 if (($user_role ne 'curator') && ($user_role ne 'submitter')) {
1031 $c->stash->{rest} = {error=>'Only a submitter or a curator can upload progenies'};
1032 $c->detach();
1035 my $uploader = CXGN::UploadFile->new({
1036 tempfile => $upload_tempfile,
1037 subdirectory => $subdirectory,
1038 archive_path => $c->config->{archive_path},
1039 archive_filename => $upload_original_name,
1040 timestamp => $timestamp,
1041 user_id => $user_id,
1042 user_role => $user_role
1045 ## Store uploaded temporary file in arhive
1046 $archived_filename_with_path = $uploader->archive();
1047 $md5 = $uploader->get_md5($archived_filename_with_path);
1048 if (!$archived_filename_with_path) {
1049 $c->stash->{rest} = {error => "Could not save file $upload_original_name in archive",};
1050 return;
1052 unlink $upload_tempfile;
1054 #parse uploaded file with appropriate plugin
1055 $parser = CXGN::Pedigree::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path);
1056 $parser->load_plugin($upload_type);
1057 $parsed_data = $parser->parse();
1058 #print STDERR "Dumper of parsed data:\t" . Dumper($parsed_data) . "\n";
1060 if (!$parsed_data){
1061 my $return_error = '';
1062 my $parse_errors;
1063 if (!$parser->has_parse_errors() ){
1064 $c->stash->{rest} = {error_string => "Could not get parsing errors"};
1065 } else {
1066 $parse_errors = $parser->get_parse_errors();
1067 #print STDERR Dumper $parse_errors;
1069 foreach my $error_string (@{$parse_errors->{'error_messages'}}){
1070 $return_error .= $error_string."<br>";
1073 $c->stash->{rest} = {error_string => $return_error};
1074 $c->detach();
1077 #add the progeny
1078 my %progeny_hash = %{$parsed_data};
1079 my @all_crosses = keys %progeny_hash;
1080 foreach my $cross_name_key (keys %progeny_hash){
1081 my $progenies_ref = $progeny_hash{$cross_name_key};
1082 my @progenies = @{$progenies_ref};
1083 my $progeny_add = CXGN::Pedigree::AddProgeny->new({
1084 chado_schema => $chado_schema,
1085 phenome_schema => $phenome_schema,
1086 dbh => $dbh,
1087 cross_name => $cross_name_key,
1088 progeny_names => \@progenies,
1089 owner_name => $user_name,
1091 if (!$progeny_add->add_progeny()){
1092 $c->stash->{rest} = {error_string => "Error adding progeny",};
1093 return;
1097 my $md_row = $metadata_schema->resultset("MdMetadata")->create({create_person_id => $user_id});
1098 $md_row->insert();
1099 my $upload_file = CXGN::UploadFile->new();
1100 my $md5 = $upload_file->get_md5($archived_filename_with_path);
1101 my $md5checksum = $md5->hexdigest();
1102 my $file_row = $metadata_schema->resultset("MdFiles")->create({
1103 basename => basename($archived_filename_with_path),
1104 dirname => dirname($archived_filename_with_path),
1105 filetype => 'cross_progenies',
1106 md5checksum => $md5checksum,
1107 metadata_id => $md_row->metadata_id(),
1110 my $file_id = $file_row->file_id();
1111 # print STDERR "FILE ID =".Dumper($file_id)."\n";
1112 foreach my $cross_name (@all_crosses) {
1113 my $cross_experiment_type = CXGN::Cross->new({schema => $chado_schema, cross_name => $cross_name});
1114 my $experiment_id = $cross_experiment_type->get_nd_experiment_id_with_type_cross_experiment();
1115 # print STDERR "ND EXPERIMENT ID =".Dumper($experiment_id)."\n";
1116 my $nd_experiment_file = $phenome_schema->resultset("NdExperimentMdFiles")->create({
1117 nd_experiment_id => $experiment_id,
1118 file_id => $file_id,
1122 $c->stash->{rest} = {success => "1",};
1126 sub validate_upload_existing_progenies : Path('/ajax/cross/validate_upload_existing_progenies') : ActionClass('REST'){ }
1128 sub validate_upload_existing_progenies_POST : Args(0) {
1129 my $self = shift;
1130 my $c = shift;
1131 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
1132 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
1133 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
1134 my $dbh = $c->dbc->dbh;
1135 my $upload = $c->req->upload('progenies_exist_upload_file');
1136 my $upload_type = 'ValidateExistingProgeniesExcel';
1137 my $parser;
1138 my $parsed_data;
1139 my $upload_original_name = $upload->filename();
1140 my $upload_tempfile = $upload->tempname;
1141 my $subdirectory = "cross_upload";
1142 my $archived_filename_with_path;
1143 my $md5;
1144 my $validate_file;
1145 my $parsed_file;
1146 my $parse_errors;
1147 my %parsed_data;
1148 my $time = DateTime->now();
1149 my $timestamp = $time->ymd()."_".$time->hms();
1150 my $user_role;
1151 my $user_id;
1152 my $user_name;
1153 my $owner_name;
1154 # my $upload_file_type = "crosses excel";#get from form when more options are added
1155 my $session_id = $c->req->param("sgn_session_id");
1156 if ($session_id){
1157 my $dbh = $c->dbc->dbh;
1158 my @user_info = CXGN::Login->new($dbh)->query_from_cookie($session_id);
1159 if (!$user_info[0]){
1160 $c->stash->{rest} = {error=>'You must be logged in to upload progenies!'};
1161 $c->detach();
1163 $user_id = $user_info[0];
1164 $user_role = $user_info[1];
1165 my $p = CXGN::People::Person->new($dbh, $user_id);
1166 $user_name = $p->get_username;
1167 } else {
1168 if (!$c->user){
1169 $c->stash->{rest} = {error=>'You must be logged in to upload progenies!'};
1170 $c->detach();
1172 $user_id = $c->user()->get_object()->get_sp_person_id();
1173 $user_name = $c->user()->get_object()->get_username();
1174 $user_role = $c->user->get_object->get_user_type();
1177 if (($user_role ne 'curator') && ($user_role ne 'submitter')) {
1178 $c->stash->{rest} = {error=>'Only a submitter or a curator can upload progenies'};
1179 $c->detach();
1182 my $uploader = CXGN::UploadFile->new({
1183 tempfile => $upload_tempfile,
1184 subdirectory => $subdirectory,
1185 archive_path => $c->config->{archive_path},
1186 archive_filename => $upload_original_name,
1187 timestamp => $timestamp,
1188 user_id => $user_id,
1189 user_role => $user_role
1192 ## Store uploaded temporary file in arhive
1193 $archived_filename_with_path = $uploader->archive();
1194 $md5 = $uploader->get_md5($archived_filename_with_path);
1195 if (!$archived_filename_with_path) {
1196 $c->stash->{rest} = {error => "Could not save file $upload_original_name in archive",};
1197 return;
1199 unlink $upload_tempfile;
1201 #parse uploaded file with appropriate plugin
1202 $parser = CXGN::Pedigree::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path);
1203 $parser->load_plugin($upload_type);
1204 $parsed_data = $parser->parse();
1205 #print STDERR "Dumper of parsed data:\t" . Dumper($parsed_data) . "\n";
1207 my $return_error = '';
1208 my $existing_pedigree = '';
1209 if (!$parser->has_parse_errors() ){
1210 $c->stash->{rest} = {error_string => "Could not get parsing errors"};
1211 } else {
1212 $parse_errors = $parser->get_parse_errors();
1213 #print STDERR Dumper $parse_errors;
1215 foreach my $error_string (@{$parse_errors->{'error_messages'}}){
1216 $return_error .= $error_string."<br>";
1219 foreach my $each_pedigree (@{$parse_errors->{'existing_pedigrees'}}){
1220 $existing_pedigree .= $each_pedigree."<br>";
1224 $c->stash->{rest} = {error_string => $return_error, existing_pedigrees => $existing_pedigree, archived_file_name => $archived_filename_with_path, user_id => $user_id};
1228 sub store_upload_existing_progenies : Path('/ajax/cross/store_upload_existing_progenies') Args(0) {
1229 my $self = shift;
1230 my $c = shift;
1231 my $archived_filename_with_path = $c->req->param('archived_file_name');
1232 my $user_id = $c->req->param('user_id');
1233 # print STDERR "ARCHIVED FILE NAME =".Dumper($archived_filename_with_path)."\n";
1234 # print STDERR "USER ID =".Dumper($user_id)."\n";
1235 my $overwrite_pedigrees = $c->req->param('overwrite_pedigrees') ne 'false' ? $c->req->param('overwrite_pedigrees') : 0;
1236 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
1237 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
1238 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
1239 my $dbh = $c->dbc->dbh;
1241 my $upload_type = 'StoreExistingProgeniesExcel';
1242 my @all_crosses;
1244 my $parser = CXGN::Pedigree::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path);
1245 $parser->load_plugin($upload_type);
1246 my $parsed_data = $parser->parse();
1247 if ($parsed_data){
1248 my %progeny_hash = %{$parsed_data};
1249 @all_crosses = keys %progeny_hash;
1250 foreach my $cross_name_key (keys %progeny_hash){
1251 my $progenies_ref = $progeny_hash{$cross_name_key};
1252 my @progenies = @{$progenies_ref};
1253 my $adding_progenies = CXGN::Pedigree::AddProgeniesExistingAccessions->new({
1254 chado_schema => $chado_schema,
1255 cross_name => $cross_name_key,
1256 progeny_names => \@progenies,
1259 my $return = $adding_progenies->add_progenies_existing_accessions($overwrite_pedigrees);
1260 my $error;
1261 if (!$return){
1262 $error = "The progenies were not stored";
1265 if ($return->{error}){
1266 $error = $return->{error};
1269 if ($error){
1270 $c->stash->{rest} = { error => $error };
1271 return;
1276 my $md_row = $metadata_schema->resultset("MdMetadata")->create({create_person_id => $user_id});
1277 $md_row->insert();
1278 my $upload_file = CXGN::UploadFile->new();
1279 my $md5 = $upload_file->get_md5($archived_filename_with_path);
1280 my $md5checksum = $md5->hexdigest();
1281 my $file_row = $metadata_schema->resultset("MdFiles")->create({
1282 basename => basename($archived_filename_with_path),
1283 dirname => dirname($archived_filename_with_path),
1284 filetype => 'cross_progenies',
1285 md5checksum => $md5checksum,
1286 metadata_id => $md_row->metadata_id(),
1289 my $file_id = $file_row->file_id();
1290 # print STDERR "FILE ID =".Dumper($file_id)."\n";
1291 foreach my $cross_name (@all_crosses) {
1292 my $cross_experiment_type = CXGN::Cross->new({schema => $chado_schema, cross_name => $cross_name});
1293 my $experiment_id = $cross_experiment_type->get_nd_experiment_id_with_type_cross_experiment();
1294 # print STDERR "ND EXPERIMENT ID =".Dumper($experiment_id)."\n";
1295 my $nd_experiment_file = $phenome_schema->resultset("NdExperimentMdFiles")->create({
1296 nd_experiment_id => $experiment_id,
1297 file_id => $file_id,
1301 $c->stash->{rest} = { success => 1 };
1304 sub upload_info : Path('/ajax/cross/upload_info') : ActionClass('REST'){ }
1306 sub upload_info_POST : Args(0) {
1307 my $self = shift;
1308 my $c = shift;
1309 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
1310 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
1311 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
1312 my $dbh = $c->dbc->dbh;
1313 my $cross_info_upload = $c->req->upload('crossinfo_upload_file');
1314 my $additional_info_upload = $c->req->upload('additional_info_upload_file');
1315 my $upload;
1316 my $upload_type;
1317 my $data_type;
1319 if ($cross_info_upload) {
1320 $upload = $cross_info_upload;
1321 $upload_type = 'CrossInfoExcel';
1322 $data_type = 'crossing_metadata_json';
1324 if ($additional_info_upload) {
1325 $upload = $additional_info_upload;
1326 $upload_type = 'AdditionalInfoExcel';
1327 $data_type = 'cross_additional_info';
1329 # print STDERR "INFO UPLOAD =".Dumper($cross_info_upload)."\n";
1330 # print STDERR "ADDITIONAL INFO UPLOAD =".Dumper($additional_info_upload)."\n";
1331 # print STDERR "DATA TYPE =".Dumper($data_type)."\n";
1333 my $parser;
1334 my $parsed_data;
1335 my $upload_original_name = $upload->filename();
1336 my $upload_tempfile = $upload->tempname;
1337 my $subdirectory = "cross_upload";
1338 my $archived_filename_with_path;
1339 my $md5;
1340 my $validate_file;
1341 my $parsed_file;
1342 my $parse_errors;
1343 my %parsed_data;
1344 my $time = DateTime->now();
1345 my $timestamp = $time->ymd()."_".$time->hms();
1346 my $user_role;
1347 my $user_id;
1348 my $user_name;
1349 my $session_id = $c->req->param("sgn_session_id");
1351 if ($session_id){
1352 my $dbh = $c->dbc->dbh;
1353 my @user_info = CXGN::Login->new($dbh)->query_from_cookie($session_id);
1354 if (!$user_info[0]){
1355 $c->stash->{rest} = {error=>'You must be logged in to upload cross info!'};
1356 $c->detach();
1358 $user_id = $user_info[0];
1359 $user_role = $user_info[1];
1360 my $p = CXGN::People::Person->new($dbh, $user_id);
1361 $user_name = $p->get_username;
1362 } else {
1363 if (!$c->user){
1364 $c->stash->{rest} = {error=>'You must be logged in to upload cross info!'};
1365 $c->detach();
1367 $user_id = $c->user()->get_object()->get_sp_person_id();
1368 $user_name = $c->user()->get_object()->get_username();
1369 $user_role = $c->user->get_object->get_user_type();
1372 if (($user_role ne 'curator') && ($user_role ne 'submitter')) {
1373 $c->stash->{rest} = {error=>'Only a submitter or a curator can upload cross info'};
1374 $c->detach();
1377 my $uploader = CXGN::UploadFile->new({
1378 tempfile => $upload_tempfile,
1379 subdirectory => $subdirectory,
1380 archive_path => $c->config->{archive_path},
1381 archive_filename => $upload_original_name,
1382 timestamp => $timestamp,
1383 user_id => $user_id,
1384 user_role => $user_role
1387 ## Store uploaded temporary file in arhive
1388 $archived_filename_with_path = $uploader->archive();
1389 $md5 = $uploader->get_md5($archived_filename_with_path);
1390 if (!$archived_filename_with_path) {
1391 $c->stash->{rest} = {error => "Could not save file $upload_original_name in archive",};
1392 return;
1394 unlink $upload_tempfile;
1396 my $cross_properties_string = $c->config->{cross_properties};
1397 my @properties = split ',', $cross_properties_string;
1398 my $cross_properties = \@properties;
1400 my $cross_additional_info_string = $c->config->{cross_additional_info};
1401 my @additional_info = split ',', $cross_additional_info_string;
1402 my $cross_additional_info = \@additional_info;
1404 #parse uploaded file with appropriate plugin
1405 $parser = CXGN::Pedigree::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path, cross_properties => $cross_properties, cross_additional_info => $cross_additional_info);
1406 $parser->load_plugin($upload_type);
1407 $parsed_data = $parser->parse();
1408 # print STDERR "Dumper of parsed data:\t" . Dumper($parsed_data) . "\n";
1410 if (!$parsed_data) {
1411 my $return_error = '';
1412 my $parse_errors;
1413 if (!$parser->has_parse_errors() ){
1414 $c->stash->{rest} = {error_string => "Could not get parsing errors"};
1415 } else {
1416 $parse_errors = $parser->get_parse_errors();
1417 #print STDERR Dumper $parse_errors;
1419 foreach my $error_string (@{$parse_errors->{'error_messages'}}){
1420 $return_error .= $error_string."<br>";
1423 $c->stash->{rest} = {error_string => $return_error, missing_crosses => $parse_errors->{'missing_crosses'} };
1424 $c->detach();
1427 my @all_crosses;
1428 if ($parsed_data) {
1429 my %cross_info = %{$parsed_data};
1430 @all_crosses = keys %cross_info;
1431 foreach my $cross_name (keys %cross_info) {
1432 my %info_hash = %{$cross_info{$cross_name}};
1433 foreach my $info_type (keys %info_hash) {
1434 my $value = $info_hash{$info_type};
1435 my $cross_add_info = CXGN::Pedigree::AddCrossInfo->new({
1436 chado_schema => $chado_schema,
1437 cross_name => $cross_name,
1438 key => $info_type,
1439 value => $value,
1440 data_type => $data_type
1443 $cross_add_info->add_info();
1445 if (!$cross_add_info->add_info()){
1446 $c->stash->{rest} = {error_string => "Error saving info",};
1447 return;
1453 # print STDERR "FILE =".Dumper($archived_filename_with_path)."\n";
1454 my $md_row = $metadata_schema->resultset("MdMetadata")->create({create_person_id => $user_id});
1455 $md_row->insert();
1456 my $upload_file = CXGN::UploadFile->new();
1457 my $md5 = $upload_file->get_md5($archived_filename_with_path);
1458 my $md5checksum = $md5->hexdigest();
1459 my $file_row = $metadata_schema->resultset("MdFiles")->create({
1460 basename => basename($archived_filename_with_path),
1461 dirname => dirname($archived_filename_with_path),
1462 filetype => 'cross_info',
1463 md5checksum => $md5checksum,
1464 metadata_id => $md_row->metadata_id(),
1467 my $file_id = $file_row->file_id();
1468 # print STDERR "FILE ID =".Dumper($file_id)."\n";
1469 foreach my $cross_name (@all_crosses) {
1470 my $cross_experiment_type = CXGN::Cross->new({schema => $chado_schema, cross_name => $cross_name});
1471 my $experiment_id = $cross_experiment_type->get_nd_experiment_id_with_type_cross_experiment();
1472 # print STDERR "ND EXPERIMENT ID =".Dumper($experiment_id)."\n";
1473 my $nd_experiment_file = $phenome_schema->resultset("NdExperimentMdFiles")->create({
1474 nd_experiment_id => $experiment_id,
1475 file_id => $file_id,
1479 $c->stash->{rest} = {success => "1",};
1483 sub upload_family_names : Path('/ajax/cross/upload_family_names') : ActionClass('REST'){ }
1485 sub upload_family_names_POST : Args(0) {
1486 my $self = shift;
1487 my $c = shift;
1488 my $chado_schema = $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado');
1489 my $metadata_schema = $c->dbic_schema("CXGN::Metadata::Schema");
1490 my $phenome_schema = $c->dbic_schema("CXGN::Phenome::Schema");
1491 my $dbh = $c->dbc->dbh;
1492 my $same_parents_upload = $c->req->upload('same_parents_file');
1493 my $reciprocal_parents_upload = $c->req->upload('reciprocal_parents_file');
1495 my $upload_original_name;
1496 my $upload_tempfile;
1497 my $family_type;
1499 if ($same_parents_upload) {
1500 $upload_original_name = $same_parents_upload->filename();
1501 $upload_tempfile = $same_parents_upload->tempname;
1502 $family_type = 'same_parents';
1505 if ($reciprocal_parents_upload) {
1506 $upload_original_name = $reciprocal_parents_upload->filename();
1507 $upload_tempfile = $reciprocal_parents_upload->tempname;
1508 $family_type = 'reciprocal_parents';
1511 my $parser;
1512 my $parsed_data;
1513 my $subdirectory = "cross_upload";
1514 my $archived_filename_with_path;
1515 my $md5;
1516 my $validate_file;
1517 my $parsed_file;
1518 my $parse_errors;
1519 my %parsed_data;
1520 my $time = DateTime->now();
1521 my $timestamp = $time->ymd()."_".$time->hms();
1522 my $user_role;
1523 my $user_id;
1524 my $user_name;
1525 my $owner_name;
1526 # my $upload_file_type = "crosses excel";#get from form when more options are added
1527 my $session_id = $c->req->param("sgn_session_id");
1529 if ($session_id){
1530 my $dbh = $c->dbc->dbh;
1531 my @user_info = CXGN::Login->new($dbh)->query_from_cookie($session_id);
1532 if (!$user_info[0]){
1533 $c->stash->{rest} = {error=>'You must be logged in to upload family names!'};
1534 $c->detach();
1536 $user_id = $user_info[0];
1537 $user_role = $user_info[1];
1538 my $p = CXGN::People::Person->new($dbh, $user_id);
1539 $user_name = $p->get_username;
1540 } else{
1541 if (!$c->user){
1542 $c->stash->{rest} = {error=>'You must be logged in to upload family names!'};
1543 $c->detach();
1545 $user_id = $c->user()->get_object()->get_sp_person_id();
1546 $user_name = $c->user()->get_object()->get_username();
1547 $user_role = $c->user->get_object->get_user_type();
1550 if (($user_role ne 'curator') && ($user_role ne 'submitter')) {
1551 $c->stash->{rest} = {error=>'Only a submitter or a curator can upload family names'};
1552 $c->detach();
1555 my $uploader = CXGN::UploadFile->new({
1556 tempfile => $upload_tempfile,
1557 subdirectory => $subdirectory,
1558 archive_path => $c->config->{archive_path},
1559 archive_filename => $upload_original_name,
1560 timestamp => $timestamp,
1561 user_id => $user_id,
1562 user_role => $user_role
1565 ## Store uploaded temporary file in arhive
1566 $archived_filename_with_path = $uploader->archive();
1567 $md5 = $uploader->get_md5($archived_filename_with_path);
1568 if (!$archived_filename_with_path) {
1569 $c->stash->{rest} = {error => "Could not save file $upload_original_name in archive",};
1570 return;
1572 unlink $upload_tempfile;
1574 #parse uploaded file with appropriate plugin
1575 $parser = CXGN::Pedigree::ParseUpload->new(chado_schema => $chado_schema, filename => $archived_filename_with_path);
1576 $parser->load_plugin('FamilyNameExcel');
1577 $parsed_data = $parser->parse();
1578 #print STDERR "Dumper of parsed data:\t" . Dumper($parsed_data) . "\n";
1580 if (!$parsed_data){
1581 my $return_error = '';
1582 my $parse_errors;
1583 if (!$parser->has_parse_errors() ){
1584 $c->stash->{rest} = {error_string => "Could not get parsing errors"};
1585 } else {
1586 $parse_errors = $parser->get_parse_errors();
1587 #print STDERR Dumper $parse_errors;
1589 foreach my $error_string (@{$parse_errors->{'error_messages'}}){
1590 $return_error .= $error_string."<br>";
1593 $c->stash->{rest} = {error_string => $return_error, missing_crosses => $parse_errors->{'missing_crosses'} };
1594 $c->detach();
1597 #add family name and associate with cross
1598 my @all_crosses;
1599 if ($parsed_data){
1600 my %family_name_hash = %{$parsed_data};
1601 @all_crosses = keys %family_name_hash;
1602 foreach my $cross_name(keys %family_name_hash){
1603 my $family_name = $family_name_hash{$cross_name};
1605 my $family_name_add = CXGN::Pedigree::AddFamilyNames->new({
1606 chado_schema => $chado_schema,
1607 phenome_schema => $phenome_schema,
1608 dbh => $dbh,
1609 cross_name => $cross_name,
1610 family_name => $family_name,
1611 owner_name => $user_name,
1612 family_type => $family_type
1615 my $return = $family_name_add->add_family_name();
1616 my $error;
1617 if (!$return){
1618 $error = "Error adding family name";
1620 if ($return->{error}){
1621 $error = $return->{error};
1623 if ($error){
1624 $c->stash->{rest} = {error_string => $error };
1625 $c->detach();
1630 # print STDERR "FILE =".Dumper($archived_filename_with_path)."\n";
1631 my $md_row = $metadata_schema->resultset("MdMetadata")->create({create_person_id => $user_id});
1632 $md_row->insert();
1633 my $upload_file = CXGN::UploadFile->new();
1634 my $md5 = $upload_file->get_md5($archived_filename_with_path);
1635 my $md5checksum = $md5->hexdigest();
1636 my $file_row = $metadata_schema->resultset("MdFiles")->create({
1637 basename => basename($archived_filename_with_path),
1638 dirname => dirname($archived_filename_with_path),
1639 filetype => 'families',
1640 md5checksum => $md5checksum,
1641 metadata_id => $md_row->metadata_id(),
1644 my $file_id = $file_row->file_id();
1645 # print STDERR "FILE ID =".Dumper($file_id)."\n";
1646 foreach my $cross_name (@all_crosses) {
1647 my $cross_experiment_type = CXGN::Cross->new({schema => $chado_schema, cross_name => $cross_name});
1648 my $experiment_id = $cross_experiment_type->get_nd_experiment_id_with_type_cross_experiment();
1649 # print STDERR "ND EXPERIMENT ID =".Dumper($experiment_id)."\n";
1650 my $nd_experiment_file = $phenome_schema->resultset("NdExperimentMdFiles")->create({
1651 nd_experiment_id => $experiment_id,
1652 file_id => $file_id,
1656 $c->stash->{rest} = {success => "1",};
1660 sub delete_cross : Path('/ajax/cross/delete') : ActionClass('REST'){ }
1662 sub delete_cross_POST : Args(0) {
1663 my $self = shift;
1664 my $c = shift;
1666 if (!$c->user()){
1667 $c->stash->{rest} = { error => "You must be logged in to delete crosses" };
1668 $c->detach();
1670 if (!$c->user()->check_roles("curator")) {
1671 $c->stash->{rest} = { error => "You do not have the correct role to delete crosses. Please contact us." };
1672 $c->detach();
1675 my $cross_stock_id = $c->req->param("cross_id");
1677 my $cross = CXGN::Cross->new( { schema => $c->dbic_schema('Bio::Chado::Schema', 'sgn_chado'), cross_stock_id => $cross_stock_id });
1679 if (!$cross->cross_stock_id()) {
1680 $c->stash->{rest} = { error => "No such cross exists. Cannot delete." };
1681 return;
1684 my $error = $cross->delete();
1686 print STDERR "ERROR = $error\n";
1688 if ($error) {
1689 $c->stash->{rest} = { error => "An error occurred attempting to delete a cross. ($@)" };
1690 return;
1693 $c->stash->{rest} = { success => 1 };
1697 sub get_cross_transactions :Path('/ajax/cross/transactions') Args(1) {
1698 my $self = shift;
1699 my $c = shift;
1700 my $cross_id = shift;
1702 my $schema = $c->dbic_schema("Bio::Chado::Schema");
1703 my $cross_transaction_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'cross_transaction_json', 'stock_property')->cvterm_id();
1704 my $cross_transactions = $schema->resultset("Stock::Stockprop")->find({stock_id => $cross_id, type_id => $cross_transaction_cvterm});
1706 my $cross_transaction_string;
1707 my %cross_transaction_hash;
1708 my @all_transactions;
1710 if($cross_transactions){
1711 $cross_transaction_string = $cross_transactions->value();
1712 my $cross_transaction_ref = decode_json $cross_transaction_string;
1713 %cross_transaction_hash = %{$cross_transaction_ref};
1714 foreach my $transaction_key (sort keys %cross_transaction_hash) {
1715 my $operator = $cross_transaction_hash{$transaction_key}{'Operator'};
1716 my $timestamp = $cross_transaction_hash{$transaction_key}{'Timestamp'};
1717 my $number_of_flowers = $cross_transaction_hash{$transaction_key}{'Number of Flowers'};
1718 my $number_of_fruits = $cross_transaction_hash{$transaction_key}{'Number of Fruits'};
1719 my $number_of_seeds = $cross_transaction_hash{$transaction_key}{'Number of Seeds'};
1720 push @all_transactions, [$transaction_key, $operator, $timestamp, $number_of_flowers, $number_of_fruits, $number_of_seeds];
1724 $c->stash->{rest} = {data => \@all_transactions};
1729 sub get_cross_additional_info :Path('/ajax/cross/additional_info') Args(1) {
1730 my $self = shift;
1731 my $c = shift;
1732 my $cross_id = shift;
1733 my $schema = $c->dbic_schema("Bio::Chado::Schema");
1735 my $cross_additional_info_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'cross_additional_info', 'stock_property')->cvterm_id();
1736 my $cross_additional_info_rs = $schema->resultset("Stock::Stockprop")->find({stock_id => $cross_id, type_id => $cross_additional_info_cvterm});
1738 my $cross_info_json_string;
1739 if($cross_additional_info_rs){
1740 $cross_info_json_string = $cross_additional_info_rs->value();
1743 my $cross_info_hash ={};
1744 if($cross_info_json_string){
1745 $cross_info_hash = decode_json $cross_info_json_string;
1748 my $cross_additional_info = $c->config->{cross_additional_info};
1749 my @column_order = split ',',$cross_additional_info;
1750 my @props;
1751 my @row;
1752 foreach my $key (@column_order){
1753 push @row, $cross_info_hash->{$key};
1756 push @props,\@row;
1757 $c->stash->{rest} = {data => \@props};