fix the observationUnitPUI url.
[sgn.git] / lib / CXGN / BrAPI / v2 / ObservationUnits.pm
blob895ad9b2690b085ca08fedf8c72c0bac7042ab26
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 $entry_type = $obs_unit->{is_a_control} ? 'check' : 'test';
190 my $replicate = $obs_unit->{rep};
191 my $block = $obs_unit->{block};
192 my $plot;
193 my $plant;
194 my $family_stock_id;
195 my $family_name;
197 ## Following code lines add observationUnitParent to additionalInfo, useful for BI
198 if ($obs_unit->{obsunit_type_name} eq 'plant') {
199 $plant = $obs_unit->{plant_number};
200 if ($plant_parents{$obs_unit->{obsunit_stock_id}}) {
201 my $plot_object = $plant_parents{$obs_unit->{obsunit_stock_id}};
202 $plot = $plot_object->{plot_number};
203 $additional_info->{observationUnitParent} = $plot_object->{id};
205 } else {
206 $plot = $obs_unit->{plot_number};
209 ## Format position coordinates
210 my $level_name = $obs_unit->{obsunit_type_name};
211 my $level_order = _order($level_name) + 0;
212 my $level_code = eval "\$$level_name" || "";
214 if ( $level_order_arrayref && ! grep { $_ eq $level_order } @{$level_order_arrayref} ) { next; }
215 if ( $level_code_arrayref && ! grep { $_ eq $level_code } @{$level_code_arrayref} ) { next; }
217 my @observationLevelRelationships;
218 if ($replicate) {
219 push @observationLevelRelationships, {
220 levelCode => $replicate,
221 levelName => "rep",
222 levelOrder => _order("rep"),
225 if ($block) {
226 push @observationLevelRelationships, {
227 levelCode => $block,
228 levelName => "block",
229 levelOrder => _order("block"),
232 if ($plot) {
233 push @observationLevelRelationships, {
234 levelCode => qq|$plot|,
235 levelName => "plot",
236 levelOrder => _order("plot"),
239 if ($plant) {
240 push @observationLevelRelationships, {
241 levelCode => $plant,
242 levelName => "plant",
243 levelOrder => _order("plant"),
247 my %observationUnitPosition = (
248 entryType => $entry_type,
249 geoCoordinates => $geo_coordinates,
250 positionCoordinateX => $obs_unit->{col_number} ? $obs_unit->{col_number} + 0 : undef,
251 positionCoordinateXType => 'GRID_COL',
252 positionCoordinateY => $obs_unit->{row_number} ? $obs_unit->{row_number} + 0 : undef,
253 positionCoordinateYType => 'GRID_ROW',
254 observationLevel => {
255 levelName => $level_name,
256 levelOrder => $level_order,
257 levelCode => $level_code,
259 observationLevelRelationships => \@observationLevelRelationships,
262 my $brapi_observationUnitPosition = decode_json(encode_json \%observationUnitPosition);
264 #Get external references
265 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
266 bcs_schema => $self->bcs_schema,
267 table_name => 'stock',
268 table_id_key => 'stock_id',
269 id => qq|$obs_unit->{obsunit_stock_id}|
271 my $external_references = $references->search();
272 my @formatted_external_references = %{$external_references} ? values %{$external_references} : [];
274 ## Get plot images
275 my @plot_image_ids;
276 eval {
277 my $image_id = CXGN::Stock->new({
278 schema => $self->bcs_schema,
279 stock_id => $obs_unit->{obsunit_stock_id},
281 @plot_image_ids = $image_id->get_image_ids();
283 my @ids;
284 foreach my $arrayimage (@plot_image_ids){
285 push @ids, $arrayimage->[0];
288 if ($obs_unit->{family_stock_id}) {
289 $additional_info->{familyDbId} = qq|$obs_unit->{family_stock_id}|;
290 $additional_info->{familyName} = $obs_unit->{family_uniquename};
293 push @data_window, {
294 externalReferences => @formatted_external_references,
295 additionalInfo => $additional_info,
296 germplasmDbId => $obs_unit->{germplasm_stock_id} ? qq|$obs_unit->{germplasm_stock_id}| : undef,
297 germplasmName => $obs_unit->{germplasm_uniquename} ? qq|$obs_unit->{germplasm_uniquename}| : undef,
298 crossDbId => $obs_unit->{cross_stock_id} ? qq|$obs_unit->{cross_stock_id}| : undef,
299 crossName => $obs_unit->{cross_uniquename} ? qq|$obs_unit->{cross_uniquename}| : undef,
300 locationDbId => qq|$obs_unit->{location_id}|,
301 locationName => $obs_unit->{location_name},
302 observationUnitDbId => qq|$obs_unit->{obsunit_stock_id}|,
303 observations => $brapi_observations,
304 observationUnitName => $obs_unit->{obsunit_uniquename},
305 observationUnitPosition => $brapi_observationUnitPosition,
306 observationUnitPUI => $main_production_site_url . "/stock/" . $obs_unit->{obsunit_stock_id} . "/view",
307 programName => $obs_unit->{breeding_program_name},
308 programDbId => qq|$obs_unit->{breeding_program_id}|,
309 seedLotDbId => $obs_unit->{seedlot_id} ? qq|$obs_unit->{seedlot_id}| : undef,
310 seedLotName => $obs_unit->{seedlot_name} ? qq|$obs_unit->{seedlot_name}| : undef,
311 studyDbId => qq|$obs_unit->{trial_id}|,
312 studyName => $obs_unit->{trial_name},
313 plotImageDbIds => \@ids,
314 treatments => \@brapi_treatments,
315 trialDbId => $obs_unit->{folder_id} ? qq|$obs_unit->{folder_id}| : qq|$obs_unit->{trial_id}|,
316 trialName => $obs_unit->{folder_name} ? $obs_unit->{folder_name} : $obs_unit->{trial_name},
318 $total_count = $obs_unit->{full_count};
321 print STDERR "ObservationUnits call Checkpoint 4: ".DateTime->now()."\n";
322 my $results = (data=>\@data_window);
324 return ($results,$total_count, $page_size,$page,$status);
327 sub _get_plants_plot_parent {
328 my $self = shift;
329 my $plant_id_array = shift;
330 my $schema = $self->bcs_schema;
332 my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant_of', 'stock_relationship')->cvterm_id();
333 my $plot_number_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot number', 'stock_property')->cvterm_id();
334 my $plant_ids_string = join ',', @{$plant_id_array};
335 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);";
336 my $h = $schema->storage->dbh()->prepare($select);
337 $h->execute();
339 my %plant_hash;
340 while (my ($plant_id, $plot_id, $plot_number) = $h->fetchrow_array()) {
341 $plant_hash{$plant_id} = { id => $plot_id, plot_number => $plot_number };
344 return %plant_hash;
347 sub detail {
348 my $self = shift;
349 my $observation_unit_db_id = shift;
350 my $c = shift;
352 my $search_params = {
353 observationUnitDbIds => [ $observation_unit_db_id ],
354 includeObservations => 'true'
357 my @data_files;
358 my ($data,$total_count, $page_size,$page,$status) = _search($self, $search_params, $c);
359 my $results = $data->[0];
360 my $pagination = CXGN::BrAPI::Pagination->pagination_response($total_count,$page_size,$page);
361 return CXGN::BrAPI::JSONResponse->return_success($results, $pagination, \@data_files, $status, 'Observation Units search result constructed');
364 sub observationunits_update {
365 my $self = shift;
366 my $data = shift;
367 my $c = shift;
369 my $page_size = $self->page_size;
370 my $page = $self->page;
371 my $status = $self->status;
373 my $dbh = $self->bcs_schema()->storage()->dbh();
374 my $schema = $self->bcs_schema;
375 my $plot_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plot', 'stock_type')->cvterm_id();
376 my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plant', 'stock_type')->cvterm_id();
377 my $stock_geo_json_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_geo_json', 'stock_property');
378 my $plot_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot number', 'stock_property');
379 my $plant_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant number', 'stock_property');
380 my $block_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'block', 'stock_property');
381 my $is_a_control_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'is a control', 'stock_property');
382 my $rep_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'replicate', 'stock_property');
383 my $range_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'range', 'stock_property');
384 my $row_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'row_number', 'stock_property');
385 my $col_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'col_number', 'stock_property');
386 my $additional_info_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'stock_additional_info', 'stock_property');
388 foreach my $params (@$data) {
389 my $observation_unit_db_id = $params->{observationUnitDbId} ? $params->{observationUnitDbId} : undef;
390 my $data_level = $params->{observationUnitLevelName}->[0] || 'all';
391 my $years_arrayref = $params->{seasonDbId} ? $params->{seasonDbId} : undef;
392 my $location_ids_arrayref = $params->{locationDbId} ? $params->{locationDbId} : undef;
393 my $study_ids_arrayref = $params->{studyDbId} ? $params->{studyDbId} : undef;
394 my $accession_id = $params->{germplasmDbId} ? $params->{germplasmDbId} : undef;
395 my $accession_name = $params->{germplasmName} ? $params->{germplasmName}: undef;
396 my $trait_list_arrayref = $params->{observationVariableDbId} ? $params->{observationVariableDbId} : undef;
397 my $program_ids_arrayref = $params->{programDbId} ? $params->{programDbId} : undef;
398 my $folder_ids_arrayref = $params->{trialDbId} ? $params->{trialDbId} : undef;
399 my $observationUnit_name = $params->{observationUnitName} ? $params->{observationUnitName} : undef;
400 my $observationUnit_position_arrayref = $params->{observationUnitPosition} ? $params->{observationUnitPosition} : undef;
401 my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef;
402 my $seedlot_id = $params->{seedLotDbId} || ""; #not implemented yet
403 my $treatments = $params->{treatments} || ""; #not implemented yet
405 my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef;
406 my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef;
407 my $plot_geo_json = $params->{observationUnitPosition}->{geoCoordinates} ? $params->{observationUnitPosition}->{geoCoordinates} : undef;
408 my $level_relations = $params->{observationUnitPosition}->{observationLevelRelationships} ? $params->{observationUnitPosition}->{observationLevelRelationships} : undef;
409 my $level_name = $params->{observationUnitPosition}->{observationLevel}->{levelName} || undef;
410 my $level_number = $params->{observationUnitPosition}->{observationLevel}->{levelCode} ? $params->{observationUnitPosition}->{observationLevel}->{levelCode} : undef;
411 my $raw_additional_info = $params->{additionalInfo} || undef;
412 my $is_a_control = $raw_additional_info->{control} ? $raw_additional_info->{control} : undef;
414 my $entry_type = $params->{observationUnitPosition}->{entryType} ? $params->{observationUnitPosition}->{entryType} : undef;
415 my $is_a_control = $params->{additionalInfo}->{control} ? $params->{additionalInfo}->{control} : undef;
417 # BrAPI entryType overrides additionalinfo.control
418 if ($entry_type) {
419 $is_a_control = uc($entry_type) eq 'CHECK' ? 1 : 0;
422 my $range_number = $raw_additional_info->{range} ? $raw_additional_info->{range} : undef;
423 my %specific_keys = map { $_ => 1 } ("observationUnitParent","control","range");
424 my %additional_info;
425 my $block_number;
426 my $rep_number;
428 foreach (@$level_relations){
429 if($_->{levelName} eq 'block'){
430 $block_number = $_->{levelCode} ? $_->{levelCode} : undef;
432 if($_->{levelName} eq 'rep'){
433 $rep_number = $_->{levelCode} ? $_->{levelCode} : undef;
434 $_->{levelName} = 'rep';
437 if (defined $raw_additional_info) {
438 foreach my $key (keys %$raw_additional_info) {
439 if (!exists($specific_keys{$key})) {
440 $additional_info{$key} = $raw_additional_info->{$key};
445 #Check if observation_unit_db_id is plot or plant and not other stock type
446 my $stock = $self->bcs_schema->resultset('Stock::Stock')->find({stock_id=>$observation_unit_db_id});
447 my $stock_type = $stock->type_id;
449 if (( $stock_type ne $plot_cvterm_id && $stock_type ne $plant_cvterm_id ) || ($level_name ne 'plant' && $level_name ne 'plot')){
450 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf("Only 'plot' or 'plant' allowed for observation level and observationUnitDbId."), 400);
453 #Update: accession
454 # if (! defined $accession_id && ! defined $accession_name) {
455 # return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
457 # my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
458 # if ($germplasm_search_result->{error}) {
459 # return $germplasm_search_result->{error};
460 # } else {
461 # $accession_name = $germplasm_search_result->{name};
465 if(defined $accession_id){
466 # Speed can be improved here by adding a simple query
467 my $layout_accession_search = CXGN::Trial::TrialLayoutSearch->new(
469 bcs_schema=>$schema,
470 data_level=>'all',
471 observation_unit_id_list=>[$observation_unit_db_id],
472 # experiment_type=>'field_layout',
473 include_observations=>1,
476 my ($data_accession,$data_accession_observations) = $layout_accession_search->search();
477 my $old_accession;
478 my $old_accession_id;
480 foreach my $obs_unit (@$data_accession){
481 $old_accession = $obs_unit->{germplasm_uniquename};
482 $old_accession_id = $obs_unit->{germplasm_stock_id};
485 if($accession_id ne $old_accession_id){
486 if (! defined $accession_id && ! defined $accession_name) {
487 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
489 my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
490 if ($germplasm_search_result->{error}) {
491 return $germplasm_search_result->{error};
492 } else {
493 $accession_name = $germplasm_search_result->{name};
496 #update accession
497 my $plot_of_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_of', 'stock_relationship')->cvterm_id();
498 if ($old_accession && $accession_id && $old_accession_id ne $accession_id) {
499 my $replace_plot_accession_fieldmap = CXGN::Trial::FieldMap->new({
500 bcs_schema => $schema,
501 trial_id => $study_ids_arrayref,
502 # new_accession => $accession_name,
503 # old_accession => $old_accession,
504 # old_plot_id => $observation_unit_db_id,
505 # old_plot_name => $observationUnit_name,
506 experiment_type => 'field_layout'
509 my $return_error = $replace_plot_accession_fieldmap->update_fieldmap_precheck();
510 if ($return_error) {
511 print STDERR Dumper $return_error;
512 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Something went wrong. Accession cannot be replaced.'));
515 my $replace_return_error = $replace_plot_accession_fieldmap->replace_plot_accession_fieldMap($observation_unit_db_id, $old_accession_id, $plot_of_type_id);
516 if ($replace_return_error) {
517 print STDERR Dumper $replace_return_error;
518 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Something went wrong. Accession cannot be replaced.'));
524 #Update: geo coordinates
525 my $geo_coordinates = $observationUnit_position_arrayref->{geoCoordinates} || undef;
526 if($geo_coordinates) {
528 my $geno_json_string = encode_json $geo_coordinates;
530 #sub upload coordinates
531 my $upload_plot_gps_txn = sub {
533 my $plots_rs = $schema->resultset("Stock::Stock")->search({stock_id => {-in=>$observation_unit_db_id}});
535 while (my $plot=$plots_rs->next){
536 my $previous_plot_gps_rs = $schema->resultset("Stock::Stockprop")->search({stock_id=>$plot->stock_id, type_id=>$stock_geo_json_cvterm->cvterm_id});
537 $previous_plot_gps_rs->delete_all();
538 $plot->create_stockprops({$stock_geo_json_cvterm->name() => $geno_json_string});
542 eval {
543 $schema->txn_do($upload_plot_gps_txn);
545 if ($@) {
546 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('An error condition occurred, was not able to upload trial plot GPS coordinates. ($@)'));
550 #update stockprops
551 if ($level_number){
552 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' }); }
553 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' }); }
555 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' }); }
556 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' }); }
557 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' }); }
558 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' }); }
559 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' }); }
560 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' }); }
561 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' }); }
563 #store/update external references
564 if ($observationUnit_x_ref){
565 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
566 bcs_schema => $self->bcs_schema,
567 table_name => 'stock',
568 table_id_key => 'stock_id',
569 external_references => $observationUnit_x_ref,
570 id => $observation_unit_db_id
572 my $reference_result = $references->store();
576 my @observation_unit_db_ids;
577 foreach my $params (@$data) { push @observation_unit_db_ids, $params->{observationUnitDbId}; }
579 my $search_params = {observationUnitDbIds => \@observation_unit_db_ids };
580 return $self->search($search_params, $c);
583 sub _get_existing_germplasm {
584 my $self = shift;
585 my $schema = shift;
586 my $accession_id = shift;
587 my $accession_name = shift;
589 if (!looks_like_number($accession_id)) {
590 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Verify Germplasm Id.'), 404)};
593 # Get the germplasm name from germplasmDbId. Check if a germplasm name passed exists
594 my $rs = $schema->resultset("Stock::Stock")->search({stock_id=>$accession_id});
595 if ($rs->count() eq 0 && ! defined $accession_name){
596 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Germplasm with that id does not exist.'), 404)};
597 } elsif ($rs->count() > 0) {
598 my $stock = $rs->first;
599 $accession_name = $stock->uniquename();
600 } else {
601 # Check that a germplasm exists with that name
602 my $rs = $schema->resultset("Stock::Stock")->search({uniquename=>$accession_name});
603 if ($rs->count() eq 0) {
604 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Germplasm with that name does not exist.'), 404)};
608 return {name => $accession_name};
611 sub observationunits_store {
612 my $self = shift;
613 my $data = shift;
614 my $c = shift;
615 my $user_id = shift;
617 my $schema = $self->bcs_schema;
618 my $dbh = $self->bcs_schema()->storage()->dbh();
619 my $person = CXGN::People::Person->new($dbh, $user_id);
620 my $user_name = $person->get_username;
622 my %study_plots;
623 my %seen_plot_numbers;
624 foreach my $params (@{$data}) {
625 my $study_id = $params->{studyDbId} || undef;
626 my $plot_name = $params->{observationUnitName} ? $params->{observationUnitName} : undef;
627 my $plot_number = $params->{observationUnitPosition}->{observationLevel}->{levelCode} ? $params->{observationUnitPosition}->{observationLevel}->{levelCode} : undef;
628 my $plot_parent_id = $params->{additionalInfo}->{observationUnitParent} ? $params->{additionalInfo}->{observationUnitParent} : undef;
629 my $accession_id = $params->{germplasmDbId} ? $params->{germplasmDbId} : undef;
630 my $accession_name = $params->{germplasmName} ? $params->{germplasmName} : undef;
631 my $entry_type = $params->{observationUnitPosition}->{entryType} ? $params->{observationUnitPosition}->{entryType} : undef;
632 my $is_a_control = $params->{additionalInfo}->{control} ? $params->{additionalInfo}->{control} : undef;
634 # BrAPI entryType overrides additionalinfo.control
635 if ($entry_type) {
636 $is_a_control = uc($entry_type) eq 'CHECK' ? 1 : 0;
639 my $range_number = $params->{additionalInfo}->{range} ? $params->{additionalInfo}->{range} : undef;
640 my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef;
641 my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef;
642 my $seedlot_id = $params->{seedLotDbId} ? $params->{seedLotDbId} : undef;
643 my $plot_geo_json = $params->{observationUnitPosition}->{geoCoordinates} ? $params->{observationUnitPosition}->{geoCoordinates} : undef;
644 my $levels = $params->{observationUnitPosition}->{observationLevelRelationships} ? $params->{observationUnitPosition}->{observationLevelRelationships} : undef;
645 my $ou_level = $params->{observationUnitPosition}->{observationLevel}->{levelName} || undef;
646 my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef;
647 my $raw_additional_info = $params->{additionalInfo} || undef;
648 my %specific_keys = map { $_ => 1 } ("observationUnitParent","control");
649 my %additional_info;
650 if (defined $raw_additional_info) {
651 foreach my $key (keys %$raw_additional_info) {
652 if (!exists($specific_keys{$key})) {
653 $additional_info{$key} = $raw_additional_info->{$key};
657 my $block_number;
658 my $rep_number;
660 # Required fields check
661 if (! defined $accession_id && ! defined $accession_name) {
662 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
665 if ($ou_level ne 'plant' && $ou_level ne 'plot') {
666 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Only "plot" or "plant" allowed for observation level.'), 400);
669 my $project = $self->bcs_schema->resultset("Project::Project")->find({ project_id => $study_id });
670 if (! defined $project) {
671 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf("A study with id $study_id does not exist"), 404);
674 # Get the germplasm name from germplasmDbId. Check if a germplasm name passed exists
675 my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
676 if ($germplasm_search_result->{error}) {
677 return $germplasm_search_result->{error};
678 } else {
679 $accession_name = $germplasm_search_result->{name};
682 foreach (@$levels){
683 if($_->{levelName} eq 'block'){
684 $block_number = $_->{levelCode} ? $_->{levelCode} : undef;
686 if($_->{levelName} eq 'rep'){
687 $rep_number = $_->{levelCode} ? $_->{levelCode} : undef;
688 $_->{levelName} = 'rep';
692 # The trial designer expects a list of plant names, this object is a plant, so add to single item list
693 my $plot_hash;
694 if ($ou_level eq 'plant') {
695 # Check that the parent already exists
696 my $plot_parent_name;
697 if ($plot_parent_id) {
698 my $rs = $schema->resultset("Stock::Stock")->search({stock_id=>$plot_parent_id});
699 if ($rs->count() eq 0){
700 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Plot with id %s does not exist.', $plot_parent_id), 404);
702 $plot_parent_name = $rs->first()->uniquename();
703 } else {
704 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('addtionalInfo.observationUnitParent for observation unit with level "plant" is required'), 404);
707 $plot_hash = {
708 plot_name => $plot_parent_name,
709 plant_names => [ $plot_name ],
710 # accession_name => $accession_name,
711 stock_name => $accession_name,
712 plot_number => $plot_number,
713 block_number => $block_number,
714 is_a_control => $is_a_control,
715 rep_number => $rep_number,
716 range_number => $range_number,
717 row_number => $row_number,
718 col_number => $col_number,
719 # plot_geo_json => $plot_geo_json,
720 additional_info => \%additional_info,
721 external_refs => $observationUnit_x_ref
723 } else {
724 $plot_hash = {
725 plot_name => $plot_name,
726 # accession_name => $accession_name,
727 stock_name => $accession_name,
728 plot_number => $plot_number,
729 block_number => $block_number,
730 is_a_control => $is_a_control,
731 rep_number => $rep_number,
732 range_number => $range_number,
733 row_number => $row_number,
734 col_number => $col_number,
735 # plot_geo_json => $plot_geo_json,
736 additional_info => \%additional_info,
737 external_refs => $observationUnit_x_ref
741 $study_plots{$study_id}{$plot_number} = $plot_hash;
742 $seen_plot_numbers{$study_id}{$plot_number}++;
745 # Check that the plot numbers passed are unique per study
746 foreach my $study_design (values %seen_plot_numbers) {
747 foreach my $seen_plot_number (keys %{$study_design}) {
748 if ($study_design->{$seen_plot_number} > 1) {
749 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);
754 my $coderef = sub {
755 foreach my $study_id (keys %study_plots) {
757 # Get the study design type
758 my $study = $study_plots{$study_id};
759 my $project = $self->bcs_schema->resultset("Project::Project")->find({ project_id => $study_id });
760 my $design_prop = $project->projectprops->find({ 'type.name' => 'design' }, { join => 'type' }); #there should be only one design prop.
761 if (!$design_prop) {
762 die {error => 'Study does not have a study type.', errorCode => 500};
764 my $design_type = $design_prop->value;
766 # Get the study location
767 my $location_id;
768 my $location_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'project location', 'project_property');
769 my $row = $self->bcs_schema()->resultset('Project::Projectprop')->find({
770 project_id => $project->project_id(),
771 type_id => $location_type_id->cvterm_id(),
773 if ($row) {
774 print('Row value: ' . $row->value());
775 $location_id = $row->value();
777 else {
778 die {error => sprintf('Erro retrieving the location of the study'), errorCode => 500};
781 my $trial_design_store = CXGN::Trial::TrialDesignStore->new({
782 bcs_schema => $schema,
783 trial_id => $study_id,
784 nd_geolocation_id => $location_id,
785 # nd_experiment_id => $nd_experiment->nd_experiment_id(), #optional
786 is_genotyping => 0,
787 new_treatment_has_plant_entries => 0,
788 new_treatment_has_subplot_entries => 0,
789 operator => $user_name,
790 trial_stock_type => 'accessions',
791 design_type => $design_type,
792 design => $study,
793 operator => $user_name
796 my $error;
797 my $validate_design_error = $trial_design_store->validate_design();
798 if ($validate_design_error) {
799 die {error => sprintf('Error validating study design: ' . $validate_design_error), errorCode => 422};
801 else {
802 $error = $trial_design_store->store();
803 if ($error) {
804 die {error => sprintf('ERROR store: ' . $error), errorCode => 500};
806 # Refresh the trial layout property
807 my %param = ( schema => $schema, trial_id => $study_id );
808 if ($design_type eq 'genotyping_plate'){
809 $param{experiment_type} = 'genotyping_layout';
810 } else {
811 $param{experiment_type} = 'field_layout';
813 my $trial_layout = CXGN::Trial::TrialLayout->new(\%param);
814 my $design = $trial_layout->generate_and_cache_layout();
816 foreach my $plot_num (keys %{$design}) {
817 my $observationUnit_x_ref = $study->{$plot_num}->{external_refs};
818 if ($observationUnit_x_ref){
819 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
820 bcs_schema => $self->bcs_schema,
821 table_name => 'stock',
822 table_id_key => 'stock_id',
823 external_references => $observationUnit_x_ref,
824 id => $design->{$plot_num}->{plot_id}
826 my $reference_result = $references->store();
833 my $error_resp;
834 try {
835 $schema->txn_do($coderef);
837 catch {
838 print "Error: :". Dumper($_);
839 # print Dumper("Error: $_\n");
840 $error_resp = CXGN::BrAPI::JSONResponse->return_error($self->status, $_->{error}, $_->{errorCode} || 500);
842 if ($error_resp) { return $error_resp; }
844 # Get our new OUs by name. Not ideal, but names are unique and its the quickest solution
845 my @observationUnitNames;
846 foreach my $ou (@{$data}) { push @observationUnitNames, $ou->{observationUnitName}; }
847 my $search_params = {observationUnitNames => \@observationUnitNames};
848 $self->page_size(scalar @{$data});
849 return $self->search($search_params, $c);
852 sub _order {
853 my $value = shift;
854 my %levels = (
855 "rep" => 0,
856 "block" => 1,
857 "plot" => 2,
858 "subplot"=> 3,
859 "plant"=> 4,
860 "tissue_sample"=> 5,
863 return $levels{$value} + 0;