fix an intriguing issue raised by lint.
[sgn.git] / lib / CXGN / BrAPI / v2 / ObservationUnits.pm
blob893ab7e98e5f63f169c50821f43e58b1ec11a257
1 package CXGN::BrAPI::v2::ObservationUnits;
3 use Moose;
4 use Data::Dumper;
5 use SGN::Model::Cvterm;
6 use CXGN::Trial;
7 use CXGN::Trait;
8 use CXGN::Trial::TrialLayoutSearch;
9 use CXGN::BrAPI::Pagination;
10 use CXGN::BrAPI::JSONResponse;
11 use CXGN::BrAPI::v2::ExternalReferences;
12 use Try::Tiny;
13 use CXGN::List::Transform;
14 use Scalar::Util qw(looks_like_number);
15 use JSON;
18 extends 'CXGN::BrAPI::v2::Common';
20 sub search {
21 my $self = shift;
22 my $params = shift;
23 my $c = shift;
24 my $page_size = $self->page_size;
25 my $page = $self->page;
26 my $status = $self->status;
27 my @data_files;
28 my $result;
29 my $total_count = 0;
31 my ($data,$total_count, $page_size,$page,$status) = _search($self, $params, $c);
33 my %results = (data=>$data);
34 my $pagination = CXGN::BrAPI::Pagination->pagination_response($total_count,$page_size,$page);
35 return CXGN::BrAPI::JSONResponse->return_success(\%results, $pagination, \@data_files, $status, 'Observation Units search result constructed');
39 sub _search {
40 my $self = shift;
41 my $params = shift;
42 my $c = shift;
44 my $page_size = $self->page_size;
45 my $page = $self->page;
46 my $status = $self->status;
47 my $total_count = 0;
49 my $data_level = $params->{observationUnitLevelName} || ['all'];
50 my $years_arrayref = $params->{seasonDbId} || ($params->{seasonDbIds} || ());
51 my $location_ids_arrayref = $params->{locationDbId} || ($params->{locationDbIds} || ());
52 my $accession_ids_arrayref = $params->{germplasmDbId} || ($params->{germplasmDbIds} || ());
53 my $trait_list_arrayref = $params->{observationVariableName} || ($params->{observationVariableNames} || ());
54 my $trait_ids_arrayref = $params->{observationVariableDbId} || ($params->{observationVariableDbIds} || ());
55 my $program_ids_arrayref = $params->{programDbId} || ($params->{programDbIds} || ());
56 my $folder_ids_arrayref = $params->{trialDbId} || ($params->{trialDbIds} || ());
57 my $start_time = $params->{observationTimeStampRangeStart}->[0] || undef;
58 my $end_time = $params->{observationTimeStampRangeEnd}->[0] || undef;
59 my $observation_unit_db_id = $params->{observationUnitDbId} || ($params->{observationUnitDbIds} || ());
60 my $observation_unit_names_list = $params->{observationUnitName} || ($params->{observationUnitNames} || ());
61 my $include_observations = $params->{includeObservations} || "False";
62 $include_observations = ref($include_observations) eq 'ARRAY' ? ${$include_observations}[0] : $include_observations;
63 my $level_order_arrayref = $params->{observationUnitLevelOrder} || ($params->{observationUnitLevelOrders} || ());
64 my $level_code_arrayref = $params->{observationUnitLevelCode} || ($params->{observationUnitLevelCodes} || ());
65 my $levels_relation_arrayref = $params->{observationLevelRelationships} || ();
66 my $levels_arrayref = $params->{observationLevels} || ();
67 my $reference_ids_arrayref = $params->{externalReferenceId} || $params->{externalReferenceID} || ($params->{externalReferenceIds} || $params->{externalReferenceIDs} || ());
68 my $reference_sources_arrayref = $params->{externalReferenceSource} || ($params->{externalReferenceSources} || ());
70 my $study_ids_arrayref = $params->{studyDbId} || ($params->{studyDbIds} || ());
72 my $phenotype_additional_info_type_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'phenotype_additional_info', 'phenotype_property')->cvterm_id();
73 my $external_references_type_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'phenotype_external_references', 'phenotype_property')->cvterm_id();
74 my $plot_geo_json_type_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plot_geo_json', 'stock_property')->cvterm_id();
75 my $stock_additional_info_type_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'stock_additional_info', 'stock_property')->cvterm_id();
77 if ($levels_arrayref){
78 $data_level = ();
79 foreach ( @{$levels_arrayref} ){
80 push @$level_code_arrayref, $_->{levelCode} if ($_->{levelCode});
81 push @{$data_level}, $_->{levelName} if ($_->{levelName});
83 if (! $data_level) {
84 $data_level = ['all'];
87 my $main_production_site_url = $c->config->{main_production_site_url};
89 my $lt = CXGN::List::Transform->new();
91 if (!$trait_ids_arrayref && $trait_list_arrayref) {
92 $trait_ids_arrayref = $lt->transform($self->bcs_schema, "traits_2_trait_ids", $trait_list_arrayref)->{transform};
95 my $limit = $page_size;
96 my $offset = $page_size*$page;
97 print STDERR "ObservationUnits call Checkpoint 1: ".DateTime->now()."\n";
99 my $layout_search = CXGN::Trial::TrialLayoutSearch->new(
101 bcs_schema=>$self->bcs_schema,
102 data_level=>$data_level->[0],
103 trial_list=>$study_ids_arrayref,
104 location_list=>$location_ids_arrayref,
105 accession_list=>$accession_ids_arrayref,
106 folder_list=>$folder_ids_arrayref,
107 program_list=>$program_ids_arrayref,
108 observation_unit_id_list=>$observation_unit_db_id,
109 observation_unit_names_list=>$observation_unit_names_list,
110 experiment_type=>'field_layout',
111 include_observations=> lc($include_observations) eq 'true' ? 1 : 0,
112 xref_id_list=>$reference_ids_arrayref,
113 xref_source_list=>$reference_sources_arrayref,
114 order_by=> ($c && $c->config->{brapi_ou_order_plot_num}) ? 'NULLIF(regexp_replace(plot_number, \'\D\', \'\', \'g\'), \'\')::numeric' : undef,
115 limit=>$limit,
116 offset=>$offset,
119 my ($data,$observations_data) = $layout_search->search();
120 print STDERR "ObservationUnits call Checkpoint 2: ".DateTime->now()."\n";
121 #print STDERR Dumper $data;
122 my $start_index = $page*$page_size;
123 my $end_index = $page*$page_size + $page_size - 1;
125 my @data_window;
127 # Get the plot parents of the plants
128 my @plant_ids;
129 my %plant_parents;
130 foreach my $obs_unit (@$data){
131 if ($obs_unit->{obsunit_type_name} eq 'plant') {
132 push @plant_ids, $obs_unit->{obsunit_stock_id};
135 if (@plant_ids && scalar @plant_ids > 0) {
136 %plant_parents = $self->_get_plants_plot_parent(\@plant_ids);
138 print STDERR "ObservationUnits call Checkpoint 3: ".DateTime->now()."\n";
139 foreach my $obs_unit (@$data){
141 ## Formatting observations
142 my $brapi_observations = [];
144 if( lc $include_observations eq 'true' && $observations_data) {
145 my $observation_id = $obs_unit->{obsunit_stock_id};
146 $brapi_observations = %{$observations_data}{$observation_id} ? %{$observations_data}{$observation_id} : [];
149 ## Formatting treatments
150 my @brapi_treatments;
152 if ($c->config->{brapi_treatments_no_management_factor}) {
153 my $treatments = $obs_unit->{treatments};
154 while (my ($factor, $modality) = each %$treatments) {
155 my $modality = $modality ? $modality : undef;
156 push @brapi_treatments, {
157 factor => $factor,
158 modality => $modality,
163 ## Getting gps coordinates
164 my $sp_rs ='';
165 eval {
166 $sp_rs = $self->bcs_schema->resultset("Stock::Stockprop")->search({ type_id => $plot_geo_json_type_id, stock_id => $obs_unit->{obsunit_stock_id} });
168 my %geolocation_lookup;
169 while( my $r = $sp_rs->next()){
170 $geolocation_lookup{$r->stock_id} = $r->value;
172 my $geo_coordinates_string = $geolocation_lookup{$obs_unit->{obsunit_stock_id}} ?$geolocation_lookup{$obs_unit->{obsunit_stock_id}} : undef;
173 my $geo_coordinates;
175 if ($geo_coordinates_string){
176 $geo_coordinates = decode_json $geo_coordinates_string;
179 ## Getting additional info
180 my $additional_info;
182 my $rs = $self->bcs_schema->resultset("Stock::Stockprop")->search({ type_id => $stock_additional_info_type_id, stock_id => $obs_unit->{obsunit_stock_id} });
183 if ($rs->count() > 0){
184 my $additional_info_json = $rs->first()->value();
185 $additional_info = $additional_info_json ? decode_json($additional_info_json) : undef;
188 my %numbers;
190 my $entry_type = $obs_unit->{is_a_control} ? 'check' : 'test';
192 my $replicate = $obs_unit->{rep};
194 my $block = $obs_unit->{block};
196 my $plot;
198 my $plant;
200 my $family_stock_id;
202 my $family_name;
204 ## Following code lines add observationUnitParent to additionalInfo, useful for BI
205 if ($obs_unit->{obsunit_type_name} eq 'plant') {
206 $plant = $obs_unit->{plant_number};
208 $numbers{plant} = $plant;
210 if ($plant_parents{$obs_unit->{obsunit_stock_id}}) {
211 my $plot_object = $plant_parents{$obs_unit->{obsunit_stock_id}};
212 $plot = $plot_object->{plot_number};
214 $numbers{plot} = $plot;
216 $additional_info->{observationUnitParent} = $plot_object->{id};
218 } else {
219 $plot = $obs_unit->{plot_number};
220 $numbers{plot} = $plot;
223 ## Format position coordinates
224 my $level_name = $obs_unit->{obsunit_type_name};
226 print STDERR "LEVEL NAME: ".Dumper(\%numbers);
228 my $level_order = _order($level_name) + 0;
230 my $level_code = $numbers{$level_name}; ###### eval "\$$level_name" || "";
232 if ( $level_order_arrayref && ! grep { $_ eq $level_order } @{$level_order_arrayref} ) { next; }
233 if ( $level_code_arrayref && ! grep { $_ eq $level_code } @{$level_code_arrayref} ) { next; }
235 my @observationLevelRelationships;
236 if ($replicate) {
237 push @observationLevelRelationships, {
238 levelCode => $replicate,
239 levelName => "rep",
240 levelOrder => _order("rep"),
243 if ($block) {
244 push @observationLevelRelationships, {
245 levelCode => $block,
246 levelName => "block",
247 levelOrder => _order("block"),
250 if ($plot) {
251 push @observationLevelRelationships, {
252 levelCode => qq|$plot|,
253 levelName => "plot",
254 levelOrder => _order("plot"),
257 if ($plant) {
258 push @observationLevelRelationships, {
259 levelCode => $plant,
260 levelName => "plant",
261 levelOrder => _order("plant"),
265 my %observationUnitPosition = (
266 entryType => $entry_type,
267 geoCoordinates => $geo_coordinates,
268 positionCoordinateX => $obs_unit->{col_number} ? $obs_unit->{col_number} + 0 : undef,
269 positionCoordinateXType => 'GRID_COL',
270 positionCoordinateY => $obs_unit->{row_number} ? $obs_unit->{row_number} + 0 : undef,
271 positionCoordinateYType => 'GRID_ROW',
272 observationLevel => {
273 levelName => $level_name,
274 levelOrder => $level_order,
275 levelCode => $level_code,
277 observationLevelRelationships => \@observationLevelRelationships,
280 my $brapi_observationUnitPosition = decode_json(encode_json \%observationUnitPosition);
282 #Get external references
283 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
284 bcs_schema => $self->bcs_schema,
285 table_name => 'stock',
286 table_id_key => 'stock_id',
287 id => qq|$obs_unit->{obsunit_stock_id}|
289 my $external_references = $references->search();
290 my @formatted_external_references = %{$external_references} ? values %{$external_references} : [];
292 ## Get plot images
293 my @plot_image_ids;
294 eval {
295 my $image_id = CXGN::Stock->new({
296 schema => $self->bcs_schema,
297 stock_id => $obs_unit->{obsunit_stock_id},
299 @plot_image_ids = $image_id->get_image_ids();
301 my @ids;
302 foreach my $arrayimage (@plot_image_ids){
303 push @ids, $arrayimage->[0];
306 if ($obs_unit->{family_stock_id}) {
307 $additional_info->{familyDbId} = qq|$obs_unit->{family_stock_id}|;
308 $additional_info->{familyName} = $obs_unit->{family_uniquename};
311 push @data_window, {
312 externalReferences => @formatted_external_references,
313 additionalInfo => $additional_info,
314 germplasmDbId => $obs_unit->{germplasm_stock_id} ? qq|$obs_unit->{germplasm_stock_id}| : undef,
315 germplasmName => $obs_unit->{germplasm_uniquename} ? qq|$obs_unit->{germplasm_uniquename}| : undef,
316 crossDbId => $obs_unit->{cross_stock_id} ? qq|$obs_unit->{cross_stock_id}| : undef,
317 crossName => $obs_unit->{cross_uniquename} ? qq|$obs_unit->{cross_uniquename}| : undef,
318 locationDbId => qq|$obs_unit->{location_id}|,
319 locationName => $obs_unit->{location_name},
320 observationUnitDbId => qq|$obs_unit->{obsunit_stock_id}|,
321 observations => $brapi_observations,
322 observationUnitName => $obs_unit->{obsunit_uniquename},
323 observationUnitPosition => $brapi_observationUnitPosition,
324 observationUnitPUI => $main_production_site_url . "/stock/" . $obs_unit->{obsunit_stock_id} . "/view",
325 programName => $obs_unit->{breeding_program_name},
326 programDbId => qq|$obs_unit->{breeding_program_id}|,
327 seedLotDbId => $obs_unit->{seedlot_id} ? qq|$obs_unit->{seedlot_id}| : undef,
328 seedLotName => $obs_unit->{seedlot_name} ? qq|$obs_unit->{seedlot_name}| : undef,
329 studyDbId => qq|$obs_unit->{trial_id}|,
330 studyName => $obs_unit->{trial_name},
331 plotImageDbIds => \@ids,
332 treatments => \@brapi_treatments,
333 trialDbId => $obs_unit->{folder_id} ? qq|$obs_unit->{folder_id}| : qq|$obs_unit->{trial_id}|,
334 trialName => $obs_unit->{folder_name} ? $obs_unit->{folder_name} : $obs_unit->{trial_name},
336 $total_count = $obs_unit->{full_count};
339 print STDERR "ObservationUnits call Checkpoint 4: ".DateTime->now()."\n";
340 my $results = (data=>\@data_window);
342 return ($results,$total_count, $page_size,$page,$status);
345 sub _get_plants_plot_parent {
346 my $self = shift;
347 my $plant_id_array = shift;
348 my $schema = $self->bcs_schema;
350 my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant_of', 'stock_relationship')->cvterm_id();
351 my $plot_number_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot number', 'stock_property')->cvterm_id();
352 my $plant_ids_string = join ',', @{$plant_id_array};
353 my $select = "select stock.stock_id, stock_relationship.subject_id, stockprop.value from stock join stock_relationship on stock.stock_id = stock_relationship.object_id join stockprop on stock_relationship.subject_id = stockprop.stock_id where stockprop.type_id = $plot_number_cvterm_id and stock_relationship.type_id = $plant_cvterm_id and stock.stock_id in ($plant_ids_string);";
354 my $h = $schema->storage->dbh()->prepare($select);
355 $h->execute();
357 my %plant_hash;
358 while (my ($plant_id, $plot_id, $plot_number) = $h->fetchrow_array()) {
359 $plant_hash{$plant_id} = { id => $plot_id, plot_number => $plot_number };
362 return %plant_hash;
365 sub detail {
366 my $self = shift;
367 my $observation_unit_db_id = shift;
368 my $c = shift;
370 my $search_params = {
371 observationUnitDbIds => [ $observation_unit_db_id ],
372 includeObservations => 'true'
375 my @data_files;
376 my ($data,$total_count, $page_size,$page,$status) = _search($self, $search_params, $c);
377 my $results = $data->[0];
378 my $pagination = CXGN::BrAPI::Pagination->pagination_response($total_count,$page_size,$page);
379 return CXGN::BrAPI::JSONResponse->return_success($results, $pagination, \@data_files, $status, 'Observation Units search result constructed');
382 sub observationunits_update {
383 my $self = shift;
384 my $data = shift;
385 my $c = shift;
387 my $page_size = $self->page_size;
388 my $page = $self->page;
389 my $status = $self->status;
391 my $dbh = $self->bcs_schema()->storage()->dbh();
392 my $schema = $self->bcs_schema;
393 my $plot_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plot', 'stock_type')->cvterm_id();
394 my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plant', 'stock_type')->cvterm_id();
395 my $stock_geo_json_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_geo_json', 'stock_property');
396 my $plot_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot number', 'stock_property');
397 my $plant_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant number', 'stock_property');
398 my $block_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'block', 'stock_property');
399 my $is_a_control_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'is a control', 'stock_property');
400 my $rep_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'replicate', 'stock_property');
401 my $range_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'range', 'stock_property');
402 my $row_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'row_number', 'stock_property');
403 my $col_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'col_number', 'stock_property');
404 my $additional_info_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'stock_additional_info', 'stock_property');
406 foreach my $params (@$data) {
407 my $observation_unit_db_id = $params->{observationUnitDbId} ? $params->{observationUnitDbId} : undef;
408 my $data_level = $params->{observationUnitLevelName}->[0] || 'all';
409 my $years_arrayref = $params->{seasonDbId} ? $params->{seasonDbId} : undef;
410 my $location_ids_arrayref = $params->{locationDbId} ? $params->{locationDbId} : undef;
411 my $study_ids_arrayref = $params->{studyDbId} ? $params->{studyDbId} : undef;
412 my $accession_id = $params->{germplasmDbId} ? $params->{germplasmDbId} : undef;
413 my $accession_name = $params->{germplasmName} ? $params->{germplasmName}: undef;
414 my $trait_list_arrayref = $params->{observationVariableDbId} ? $params->{observationVariableDbId} : undef;
415 my $program_ids_arrayref = $params->{programDbId} ? $params->{programDbId} : undef;
416 my $folder_ids_arrayref = $params->{trialDbId} ? $params->{trialDbId} : undef;
417 my $observationUnit_name = $params->{observationUnitName} ? $params->{observationUnitName} : undef;
418 my $observationUnit_position_arrayref = $params->{observationUnitPosition} ? $params->{observationUnitPosition} : undef;
419 my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef;
420 my $seedlot_id = $params->{seedLotDbId} || ""; #not implemented yet
421 my $treatments = $params->{treatments} || ""; #not implemented yet
423 my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef;
424 my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef;
425 my $plot_geo_json = $params->{observationUnitPosition}->{geoCoordinates} ? $params->{observationUnitPosition}->{geoCoordinates} : undef;
426 my $level_relations = $params->{observationUnitPosition}->{observationLevelRelationships} ? $params->{observationUnitPosition}->{observationLevelRelationships} : undef;
427 my $level_name = $params->{observationUnitPosition}->{observationLevel}->{levelName} || undef;
428 my $level_number = $params->{observationUnitPosition}->{observationLevel}->{levelCode} ? $params->{observationUnitPosition}->{observationLevel}->{levelCode} : undef;
429 my $raw_additional_info = $params->{additionalInfo} || undef;
430 my $is_a_control = $raw_additional_info->{control} ? $raw_additional_info->{control} : undef;
432 my $entry_type = $params->{observationUnitPosition}->{entryType} ? $params->{observationUnitPosition}->{entryType} : undef;
433 my $is_a_control = $params->{additionalInfo}->{control} ? $params->{additionalInfo}->{control} : undef;
435 # BrAPI entryType overrides additionalinfo.control
436 if ($entry_type) {
437 $is_a_control = uc($entry_type) eq 'CHECK' ? 1 : 0;
440 my $range_number = $raw_additional_info->{range} ? $raw_additional_info->{range} : undef;
441 my %specific_keys = map { $_ => 1 } ("observationUnitParent","control","range");
442 my %additional_info;
443 my $block_number;
444 my $rep_number;
446 foreach (@$level_relations){
447 if($_->{levelName} eq 'block'){
448 $block_number = $_->{levelCode} ? $_->{levelCode} : undef;
450 if($_->{levelName} eq 'rep'){
451 $rep_number = $_->{levelCode} ? $_->{levelCode} : undef;
452 $_->{levelName} = 'rep';
455 if (defined $raw_additional_info) {
456 foreach my $key (keys %$raw_additional_info) {
457 if (!exists($specific_keys{$key})) {
458 $additional_info{$key} = $raw_additional_info->{$key};
463 #Check if observation_unit_db_id is plot or plant and not other stock type
464 my $stock = $self->bcs_schema->resultset('Stock::Stock')->find({stock_id=>$observation_unit_db_id});
465 my $stock_type = $stock->type_id;
467 if (( $stock_type ne $plot_cvterm_id && $stock_type ne $plant_cvterm_id ) || ($level_name ne 'plant' && $level_name ne 'plot')){
468 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf("Only 'plot' or 'plant' allowed for observation level and observationUnitDbId."), 400);
471 #Update: accession
472 # if (! defined $accession_id && ! defined $accession_name) {
473 # return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
475 # my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
476 # if ($germplasm_search_result->{error}) {
477 # return $germplasm_search_result->{error};
478 # } else {
479 # $accession_name = $germplasm_search_result->{name};
483 if(defined $accession_id){
484 # Speed can be improved here by adding a simple query
485 my $layout_accession_search = CXGN::Trial::TrialLayoutSearch->new(
487 bcs_schema=>$schema,
488 data_level=>'all',
489 observation_unit_id_list=>[$observation_unit_db_id],
490 # experiment_type=>'field_layout',
491 include_observations=>1,
494 my ($data_accession,$data_accession_observations) = $layout_accession_search->search();
495 my $old_accession;
496 my $old_accession_id;
498 foreach my $obs_unit (@$data_accession){
499 $old_accession = $obs_unit->{germplasm_uniquename};
500 $old_accession_id = $obs_unit->{germplasm_stock_id};
503 if($accession_id ne $old_accession_id){
504 if (! defined $accession_id && ! defined $accession_name) {
505 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
507 my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
508 if ($germplasm_search_result->{error}) {
509 return $germplasm_search_result->{error};
510 } else {
511 $accession_name = $germplasm_search_result->{name};
514 #update accession
515 my $plot_of_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_of', 'stock_relationship')->cvterm_id();
516 if ($old_accession && $accession_id && $old_accession_id ne $accession_id) {
517 my $replace_plot_accession_fieldmap = CXGN::Trial::FieldMap->new({
518 bcs_schema => $schema,
519 trial_id => $study_ids_arrayref,
520 # new_accession => $accession_name,
521 # old_accession => $old_accession,
522 # old_plot_id => $observation_unit_db_id,
523 # old_plot_name => $observationUnit_name,
524 experiment_type => 'field_layout'
527 my $return_error = $replace_plot_accession_fieldmap->update_fieldmap_precheck();
528 if ($return_error) {
529 print STDERR Dumper $return_error;
530 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Something went wrong. Accession cannot be replaced.'));
533 my $replace_return_error = $replace_plot_accession_fieldmap->replace_plot_accession_fieldMap($observation_unit_db_id, $old_accession_id, $plot_of_type_id);
534 if ($replace_return_error) {
535 print STDERR Dumper $replace_return_error;
536 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Something went wrong. Accession cannot be replaced.'));
542 #Update: geo coordinates
543 my $geo_coordinates = $observationUnit_position_arrayref->{geoCoordinates} || undef;
544 if($geo_coordinates) {
546 my $geno_json_string = encode_json $geo_coordinates;
548 #sub upload coordinates
549 my $upload_plot_gps_txn = sub {
551 my $plots_rs = $schema->resultset("Stock::Stock")->search({stock_id => {-in=>$observation_unit_db_id}});
553 while (my $plot=$plots_rs->next){
554 my $previous_plot_gps_rs = $schema->resultset("Stock::Stockprop")->search({stock_id=>$plot->stock_id, type_id=>$stock_geo_json_cvterm->cvterm_id});
555 $previous_plot_gps_rs->delete_all();
556 $plot->create_stockprops({$stock_geo_json_cvterm->name() => $geno_json_string});
560 eval {
561 $schema->txn_do($upload_plot_gps_txn);
563 if ($@) {
564 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('An error condition occurred, was not able to upload trial plot GPS coordinates. ($@)'));
568 #update stockprops
569 if ($level_number){
570 if ($level_name eq 'plot'){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$plot_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$level_number }, { key=>'stockprop_c1' }); }
571 if ($level_name eq 'plant'){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$plant_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$level_number }, { key=>'stockprop_c1' }); }
573 if ($block_number){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$block_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$block_number },{ key=>'stockprop_c1' }); }
574 if ($is_a_control){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$is_a_control_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$is_a_control },{ key=>'stockprop_c1' }); }
575 if ($rep_number){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$rep_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$rep_number },{ key=>'stockprop_c1' }); }
576 if ($range_number){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$range_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$range_number },{ key=>'stockprop_c1' }); }
577 if ($row_number){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$row_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$row_number },{ key=>'stockprop_c1' }); }
578 if ($col_number){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$col_number_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>$col_number },{ key=>'stockprop_c1' }); }
579 if (%additional_info){ $schema->resultset("Stock::Stockprop")->update_or_create({ type_id=>$additional_info_cvterm->cvterm_id, stock_id=>$observation_unit_db_id, rank=>0, value=>encode_json \%additional_info },{ key=>'stockprop_c1' }); }
581 #store/update external references
582 if ($observationUnit_x_ref){
583 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
584 bcs_schema => $self->bcs_schema,
585 table_name => 'stock',
586 table_id_key => 'stock_id',
587 external_references => $observationUnit_x_ref,
588 id => $observation_unit_db_id
590 my $reference_result = $references->store();
594 my @observation_unit_db_ids;
595 foreach my $params (@$data) { push @observation_unit_db_ids, $params->{observationUnitDbId}; }
597 my $search_params = {observationUnitDbIds => \@observation_unit_db_ids };
598 return $self->search($search_params, $c);
601 sub _get_existing_germplasm {
602 my $self = shift;
603 my $schema = shift;
604 my $accession_id = shift;
605 my $accession_name = shift;
607 if (!looks_like_number($accession_id)) {
608 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Verify Germplasm Id.'), 404)};
611 # Get the germplasm name from germplasmDbId. Check if a germplasm name passed exists
612 my $rs = $schema->resultset("Stock::Stock")->search({stock_id=>$accession_id});
613 if ($rs->count() eq 0 && ! defined $accession_name){
614 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Germplasm with that id does not exist.'), 404)};
615 } elsif ($rs->count() > 0) {
616 my $stock = $rs->first;
617 $accession_name = $stock->uniquename();
618 } else {
619 # Check that a germplasm exists with that name
620 my $rs = $schema->resultset("Stock::Stock")->search({uniquename=>$accession_name});
621 if ($rs->count() eq 0) {
622 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Germplasm with that name does not exist.'), 404)};
626 return {name => $accession_name};
629 sub observationunits_store {
630 my $self = shift;
631 my $data = shift;
632 my $c = shift;
633 my $user_id = shift;
635 my $schema = $self->bcs_schema;
636 my $dbh = $self->bcs_schema()->storage()->dbh();
637 my $person = CXGN::People::Person->new($dbh, $user_id);
638 my $user_name = $person->get_username;
640 my %study_plots;
641 my %seen_plot_numbers;
642 foreach my $params (@{$data}) {
643 my $study_id = $params->{studyDbId} || undef;
644 my $plot_name = $params->{observationUnitName} ? $params->{observationUnitName} : undef;
645 my $plot_number = $params->{observationUnitPosition}->{observationLevel}->{levelCode} ? $params->{observationUnitPosition}->{observationLevel}->{levelCode} : undef;
646 my $plot_parent_id = $params->{additionalInfo}->{observationUnitParent} ? $params->{additionalInfo}->{observationUnitParent} : undef;
647 my $accession_id = $params->{germplasmDbId} ? $params->{germplasmDbId} : undef;
648 my $accession_name = $params->{germplasmName} ? $params->{germplasmName} : undef;
649 my $entry_type = $params->{observationUnitPosition}->{entryType} ? $params->{observationUnitPosition}->{entryType} : undef;
650 my $is_a_control = $params->{additionalInfo}->{control} ? $params->{additionalInfo}->{control} : undef;
652 # BrAPI entryType overrides additionalinfo.control
653 if ($entry_type) {
654 $is_a_control = uc($entry_type) eq 'CHECK' ? 1 : 0;
657 my $range_number = $params->{additionalInfo}->{range} ? $params->{additionalInfo}->{range} : undef;
658 my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef;
659 my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef;
660 my $seedlot_id = $params->{seedLotDbId} ? $params->{seedLotDbId} : undef;
661 my $plot_geo_json = $params->{observationUnitPosition}->{geoCoordinates} ? $params->{observationUnitPosition}->{geoCoordinates} : undef;
662 my $levels = $params->{observationUnitPosition}->{observationLevelRelationships} ? $params->{observationUnitPosition}->{observationLevelRelationships} : undef;
663 my $ou_level = $params->{observationUnitPosition}->{observationLevel}->{levelName} || undef;
664 my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef;
665 my $raw_additional_info = $params->{additionalInfo} || undef;
666 my %specific_keys = map { $_ => 1 } ("observationUnitParent","control");
667 my %additional_info;
668 if (defined $raw_additional_info) {
669 foreach my $key (keys %$raw_additional_info) {
670 if (!exists($specific_keys{$key})) {
671 $additional_info{$key} = $raw_additional_info->{$key};
675 my $block_number;
676 my $rep_number;
678 # Required fields check
679 if (! defined $accession_id && ! defined $accession_name) {
680 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
683 if ($ou_level ne 'plant' && $ou_level ne 'plot') {
684 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Only "plot" or "plant" allowed for observation level.'), 400);
687 my $project = $self->bcs_schema->resultset("Project::Project")->find({ project_id => $study_id });
688 if (! defined $project) {
689 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf("A study with id $study_id does not exist"), 404);
692 # Get the germplasm name from germplasmDbId. Check if a germplasm name passed exists
693 my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
694 if ($germplasm_search_result->{error}) {
695 return $germplasm_search_result->{error};
696 } else {
697 $accession_name = $germplasm_search_result->{name};
700 foreach (@$levels){
701 if($_->{levelName} eq 'block'){
702 $block_number = $_->{levelCode} ? $_->{levelCode} : undef;
704 if($_->{levelName} eq 'rep'){
705 $rep_number = $_->{levelCode} ? $_->{levelCode} : undef;
706 $_->{levelName} = 'rep';
710 # The trial designer expects a list of plant names, this object is a plant, so add to single item list
711 my $plot_hash;
712 if ($ou_level eq 'plant') {
713 # Check that the parent already exists
714 my $plot_parent_name;
715 if ($plot_parent_id) {
716 my $rs = $schema->resultset("Stock::Stock")->search({stock_id=>$plot_parent_id});
717 if ($rs->count() eq 0){
718 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Plot with id %s does not exist.', $plot_parent_id), 404);
720 $plot_parent_name = $rs->first()->uniquename();
721 } else {
722 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('addtionalInfo.observationUnitParent for observation unit with level "plant" is required'), 404);
725 $plot_hash = {
726 plot_name => $plot_parent_name,
727 plant_names => [ $plot_name ],
728 # accession_name => $accession_name,
729 stock_name => $accession_name,
730 plot_number => $plot_number,
731 block_number => $block_number,
732 is_a_control => $is_a_control,
733 rep_number => $rep_number,
734 range_number => $range_number,
735 row_number => $row_number,
736 col_number => $col_number,
737 # plot_geo_json => $plot_geo_json,
738 additional_info => \%additional_info,
739 external_refs => $observationUnit_x_ref
741 } else {
742 $plot_hash = {
743 plot_name => $plot_name,
744 # accession_name => $accession_name,
745 stock_name => $accession_name,
746 plot_number => $plot_number,
747 block_number => $block_number,
748 is_a_control => $is_a_control,
749 rep_number => $rep_number,
750 range_number => $range_number,
751 row_number => $row_number,
752 col_number => $col_number,
753 # plot_geo_json => $plot_geo_json,
754 additional_info => \%additional_info,
755 external_refs => $observationUnit_x_ref
759 $study_plots{$study_id}{$plot_number} = $plot_hash;
760 $seen_plot_numbers{$study_id}{$plot_number}++;
763 # Check that the plot numbers passed are unique per study
764 foreach my $study_design (values %seen_plot_numbers) {
765 foreach my $seen_plot_number (keys %{$study_design}) {
766 if ($study_design->{$seen_plot_number} > 1) {
767 return CXGN::BrAPI::JSONResponse->return_error($self->status, "Plot number '$seen_plot_number' is duplicated in the data sent. Plot Number must be unique", 422);
772 my $coderef = sub {
773 foreach my $study_id (keys %study_plots) {
775 # Get the study design type
776 my $study = $study_plots{$study_id};
777 my $project = $self->bcs_schema->resultset("Project::Project")->find({ project_id => $study_id });
778 my $design_prop = $project->projectprops->find({ 'type.name' => 'design' }, { join => 'type' }); #there should be only one design prop.
779 if (!$design_prop) {
780 die {error => 'Study does not have a study type.', errorCode => 500};
782 my $design_type = $design_prop->value;
784 # Get the study location
785 my $location_id;
786 my $location_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'project location', 'project_property');
787 my $row = $self->bcs_schema()->resultset('Project::Projectprop')->find({
788 project_id => $project->project_id(),
789 type_id => $location_type_id->cvterm_id(),
791 if ($row) {
792 print('Row value: ' . $row->value());
793 $location_id = $row->value();
795 else {
796 die {error => sprintf('Erro retrieving the location of the study'), errorCode => 500};
799 my $trial_design_store = CXGN::Trial::TrialDesignStore->new({
800 bcs_schema => $schema,
801 trial_id => $study_id,
802 nd_geolocation_id => $location_id,
803 # nd_experiment_id => $nd_experiment->nd_experiment_id(), #optional
804 is_genotyping => 0,
805 new_treatment_has_plant_entries => 0,
806 new_treatment_has_subplot_entries => 0,
807 operator => $user_name,
808 trial_stock_type => 'accessions',
809 design_type => $design_type,
810 design => $study,
811 operator => $user_name
814 my $error;
815 my $validate_design_error = $trial_design_store->validate_design();
816 if ($validate_design_error) {
817 die {error => sprintf('Error validating study design: ' . $validate_design_error), errorCode => 422};
819 else {
820 $error = $trial_design_store->store();
821 if ($error) {
822 die {error => sprintf('ERROR store: ' . $error), errorCode => 500};
824 # Refresh the trial layout property
825 my %param = ( schema => $schema, trial_id => $study_id );
826 if ($design_type eq 'genotyping_plate'){
827 $param{experiment_type} = 'genotyping_layout';
828 } else {
829 $param{experiment_type} = 'field_layout';
831 my $trial_layout = CXGN::Trial::TrialLayout->new(\%param);
832 my $design = $trial_layout->generate_and_cache_layout();
834 foreach my $plot_num (keys %{$design}) {
835 my $observationUnit_x_ref = $study->{$plot_num}->{external_refs};
836 if ($observationUnit_x_ref){
837 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
838 bcs_schema => $self->bcs_schema,
839 table_name => 'stock',
840 table_id_key => 'stock_id',
841 external_references => $observationUnit_x_ref,
842 id => $design->{$plot_num}->{plot_id}
844 my $reference_result = $references->store();
851 my $error_resp;
852 try {
853 $schema->txn_do($coderef);
855 catch {
856 print "Error: :". Dumper($_);
857 # print Dumper("Error: $_\n");
858 $error_resp = CXGN::BrAPI::JSONResponse->return_error($self->status, $_->{error}, $_->{errorCode} || 500);
860 if ($error_resp) { return $error_resp; }
862 # Get our new OUs by name. Not ideal, but names are unique and its the quickest solution
863 my @observationUnitNames;
864 foreach my $ou (@{$data}) { push @observationUnitNames, $ou->{observationUnitName}; }
865 my $search_params = {observationUnitNames => \@observationUnitNames};
866 $self->page_size(scalar @{$data});
867 return $self->search($search_params, $c);
870 sub _order {
871 my $value = shift;
872 my %levels = (
873 "rep" => 0,
874 "block" => 1,
875 "plot" => 2,
876 "subplot"=> 3,
877 "plant"=> 4,
878 "tissue_sample"=> 5,
881 return $levels{$value} + 0;