Merge pull request #5191 from solgenomics/topic/quality_control
[sgn.git] / lib / CXGN / BrAPI / v2 / ObservationUnits.pm
blob580b61fad46a165582bd2b703961c8c5ca129cf1
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 foreach my $treatment (@$treatments) {
155 while (my ($factor, $modality) = each %$treatment) {
156 my $modality = $modality ? $modality : undef;
157 push @brapi_treatments, {
158 factor => $factor,
159 modality => $modality,
165 ## Getting gps coordinates
166 my $sp_rs ='';
167 eval {
168 $sp_rs = $self->bcs_schema->resultset("Stock::Stockprop")->search({ type_id => $plot_geo_json_type_id, stock_id => $obs_unit->{obsunit_stock_id} });
170 my %geolocation_lookup;
171 while( my $r = $sp_rs->next()){
172 $geolocation_lookup{$r->stock_id} = $r->value;
174 my $geo_coordinates_string = $geolocation_lookup{$obs_unit->{obsunit_stock_id}} ?$geolocation_lookup{$obs_unit->{obsunit_stock_id}} : undef;
175 my $geo_coordinates;
177 if ($geo_coordinates_string){
178 $geo_coordinates = decode_json $geo_coordinates_string;
181 ## Getting additional info
182 my $additional_info;
184 my $rs = $self->bcs_schema->resultset("Stock::Stockprop")->search({ type_id => $stock_additional_info_type_id, stock_id => $obs_unit->{obsunit_stock_id} });
185 if ($rs->count() > 0){
186 my $additional_info_json = $rs->first()->value();
187 $additional_info = $additional_info_json ? decode_json($additional_info_json) : undef;
190 my %numbers;
192 my $entry_type = $obs_unit->{is_a_control} ? 'check' : 'test';
193 $numbers{entry_type} = $entry_type;
195 my $replicate = $obs_unit->{rep};
196 $numbers{replicate} = $replicate;
198 my $block = $obs_unit->{block};
199 $numbers{block} = $block;
201 my $plot;
203 my $plant;
205 my $family_stock_id;
207 my $family_name;
209 ## Following code lines add observationUnitParent to additionalInfo, useful for BI
210 if ($obs_unit->{obsunit_type_name} eq 'plant') {
211 $plant = $obs_unit->{plant_number};
213 $numbers{plant} = $plant;
215 if ($plant_parents{$obs_unit->{obsunit_stock_id}}) {
216 my $plot_object = $plant_parents{$obs_unit->{obsunit_stock_id}};
217 $plot = $plot_object->{plot_number};
219 $numbers{plot} = $plot;
221 $additional_info->{observationUnitParent} = $plot_object->{id};
223 } else {
224 $plot = $obs_unit->{plot_number};
225 $numbers{plot} = $plot;
228 ## Format position coordinates
229 my $level_name = $obs_unit->{obsunit_type_name};
231 # print STDERR "LEVEL NAME: ".Dumper(\%numbers);
233 my $level_order = _order($level_name) + 0;
235 my $level_code = $numbers{$level_name}; ###### eval "\$$level_name" || "";
237 if ( $level_order_arrayref && ! grep { $_ eq $level_order } @{$level_order_arrayref} ) { next; }
238 if ( $level_code_arrayref && ! grep { $_ eq $level_code } @{$level_code_arrayref} ) { next; }
240 my @observationLevelRelationships;
241 if ($replicate) {
242 push @observationLevelRelationships, {
243 levelCode => $replicate,
244 levelName => "rep",
245 levelOrder => _order("rep"),
248 if ($block) {
249 push @observationLevelRelationships, {
250 levelCode => $block,
251 levelName => "block",
252 levelOrder => _order("block"),
255 if ($plot) {
256 push @observationLevelRelationships, {
257 levelCode => qq|$plot|,
258 levelName => "plot",
259 levelOrder => _order("plot"),
262 if ($plant) {
263 push @observationLevelRelationships, {
264 levelCode => $plant,
265 levelName => "plant",
266 levelOrder => _order("plant"),
270 my %observationUnitPosition = (
271 entryType => $entry_type,
272 geoCoordinates => $geo_coordinates,
273 positionCoordinateX => $obs_unit->{col_number} ? $obs_unit->{col_number} + 0 : undef,
274 positionCoordinateXType => 'GRID_COL',
275 positionCoordinateY => $obs_unit->{row_number} ? $obs_unit->{row_number} + 0 : undef,
276 positionCoordinateYType => 'GRID_ROW',
277 observationLevel => {
278 levelName => $level_name,
279 levelOrder => $level_order,
280 levelCode => $level_code,
282 observationLevelRelationships => \@observationLevelRelationships,
285 my $brapi_observationUnitPosition = decode_json(encode_json \%observationUnitPosition);
287 #Get external references
288 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
289 bcs_schema => $self->bcs_schema,
290 table_name => 'stock',
291 table_id_key => 'stock_id',
292 id => qq|$obs_unit->{obsunit_stock_id}|
294 my $external_references = $references->search();
295 my @formatted_external_references = %{$external_references} ? values %{$external_references} : [];
297 ## Get plot images
298 my @plot_image_ids;
299 eval {
300 my $image_id = CXGN::Stock->new({
301 schema => $self->bcs_schema,
302 stock_id => $obs_unit->{obsunit_stock_id},
304 @plot_image_ids = $image_id->get_image_ids();
306 my @ids;
307 foreach my $arrayimage (@plot_image_ids){
308 push @ids, $arrayimage->[0];
311 if ($obs_unit->{family_stock_id}) {
312 $additional_info->{familyDbId} = qq|$obs_unit->{family_stock_id}|;
313 $additional_info->{familyName} = $obs_unit->{family_uniquename};
316 push @data_window, {
317 externalReferences => @formatted_external_references,
318 additionalInfo => $additional_info,
319 germplasmDbId => $obs_unit->{germplasm_stock_id} ? qq|$obs_unit->{germplasm_stock_id}| : undef,
320 germplasmName => $obs_unit->{germplasm_uniquename} ? qq|$obs_unit->{germplasm_uniquename}| : undef,
321 crossDbId => $obs_unit->{cross_stock_id} ? qq|$obs_unit->{cross_stock_id}| : undef,
322 crossName => $obs_unit->{cross_uniquename} ? qq|$obs_unit->{cross_uniquename}| : undef,
323 locationDbId => qq|$obs_unit->{location_id}|,
324 locationName => $obs_unit->{location_name},
325 observationUnitDbId => qq|$obs_unit->{obsunit_stock_id}|,
326 observations => $brapi_observations,
327 observationUnitName => $obs_unit->{obsunit_uniquename},
328 observationUnitPosition => $brapi_observationUnitPosition,
329 observationUnitPUI => $main_production_site_url . "/stock/" . $obs_unit->{obsunit_stock_id} . "/view",
330 programName => $obs_unit->{breeding_program_name},
331 programDbId => qq|$obs_unit->{breeding_program_id}|,
332 seedLotDbId => $obs_unit->{seedlot_id} ? qq|$obs_unit->{seedlot_id}| : undef,
333 seedLotName => $obs_unit->{seedlot_name} ? qq|$obs_unit->{seedlot_name}| : undef,
334 studyDbId => qq|$obs_unit->{trial_id}|,
335 studyName => $obs_unit->{trial_name},
336 plotImageDbIds => \@ids,
337 treatments => \@brapi_treatments,
338 trialDbId => $obs_unit->{folder_id} ? qq|$obs_unit->{folder_id}| : qq|$obs_unit->{trial_id}|,
339 trialName => $obs_unit->{folder_name} ? $obs_unit->{folder_name} : $obs_unit->{trial_name},
341 $total_count = $obs_unit->{full_count};
344 print STDERR "ObservationUnits call Checkpoint 4: ".DateTime->now()."\n";
345 my $results = (data=>\@data_window);
347 return ($results,$total_count, $page_size,$page,$status);
350 sub _get_plants_plot_parent {
351 my $self = shift;
352 my $plant_id_array = shift;
353 my $schema = $self->bcs_schema;
355 my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant_of', 'stock_relationship')->cvterm_id();
356 my $plot_number_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot number', 'stock_property')->cvterm_id();
357 my $plant_ids_string = join ',', @{$plant_id_array};
358 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);";
359 my $h = $schema->storage->dbh()->prepare($select);
360 $h->execute();
362 my %plant_hash;
363 while (my ($plant_id, $plot_id, $plot_number) = $h->fetchrow_array()) {
364 $plant_hash{$plant_id} = { id => $plot_id, plot_number => $plot_number };
367 return %plant_hash;
370 sub detail {
371 my $self = shift;
372 my $observation_unit_db_id = shift;
373 my $c = shift;
375 my $search_params = {
376 observationUnitDbIds => [ $observation_unit_db_id ],
377 includeObservations => 'true'
380 my @data_files;
381 my ($data,$total_count, $page_size,$page,$status) = _search($self, $search_params, $c);
382 my $results = $data->[0];
383 my $pagination = CXGN::BrAPI::Pagination->pagination_response($total_count,$page_size,$page);
384 return CXGN::BrAPI::JSONResponse->return_success($results, $pagination, \@data_files, $status, 'Observation Units search result constructed');
387 sub observationunits_update {
388 my $self = shift;
389 my $data = shift;
390 my $c = shift;
392 my $page_size = $self->page_size;
393 my $page = $self->page;
394 my $status = $self->status;
396 my $dbh = $self->bcs_schema()->storage()->dbh();
397 my $schema = $self->bcs_schema;
398 my $plot_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plot', 'stock_type')->cvterm_id();
399 my $plant_cvterm_id = SGN::Model::Cvterm->get_cvterm_row($self->bcs_schema, 'plant', 'stock_type')->cvterm_id();
400 my $stock_geo_json_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_geo_json', 'stock_property');
401 my $plot_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot number', 'stock_property');
402 my $plant_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'plant number', 'stock_property');
403 my $block_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'block', 'stock_property');
404 my $is_a_control_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'is a control', 'stock_property');
405 my $rep_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'replicate', 'stock_property');
406 my $range_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'range', 'stock_property');
407 my $row_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'row_number', 'stock_property');
408 my $col_number_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'col_number', 'stock_property');
409 my $additional_info_cvterm = SGN::Model::Cvterm->get_cvterm_row($schema, 'stock_additional_info', 'stock_property');
411 foreach my $params (@$data) {
412 my $observation_unit_db_id = $params->{observationUnitDbId} ? $params->{observationUnitDbId} : undef;
413 my $data_level = $params->{observationUnitLevelName}->[0] || 'all';
414 my $years_arrayref = $params->{seasonDbId} ? $params->{seasonDbId} : undef;
415 my $location_ids_arrayref = $params->{locationDbId} ? $params->{locationDbId} : undef;
416 my $study_ids_arrayref = $params->{studyDbId} ? $params->{studyDbId} : undef;
417 my $accession_id = $params->{germplasmDbId} ? $params->{germplasmDbId} : undef;
418 my $accession_name = $params->{germplasmName} ? $params->{germplasmName}: undef;
419 my $trait_list_arrayref = $params->{observationVariableDbId} ? $params->{observationVariableDbId} : undef;
420 my $program_ids_arrayref = $params->{programDbId} ? $params->{programDbId} : undef;
421 my $folder_ids_arrayref = $params->{trialDbId} ? $params->{trialDbId} : undef;
422 my $observationUnit_name = $params->{observationUnitName} ? $params->{observationUnitName} : undef;
423 my $observationUnit_position_arrayref = $params->{observationUnitPosition} ? $params->{observationUnitPosition} : undef;
424 my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef;
425 my $seedlot_id = $params->{seedLotDbId} || ""; #not implemented yet
426 my $treatments = $params->{treatments} || ""; #not implemented yet
428 my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef;
429 my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef;
430 my $plot_geo_json = $params->{observationUnitPosition}->{geoCoordinates} ? $params->{observationUnitPosition}->{geoCoordinates} : undef;
431 my $level_relations = $params->{observationUnitPosition}->{observationLevelRelationships} ? $params->{observationUnitPosition}->{observationLevelRelationships} : undef;
432 my $level_name = $params->{observationUnitPosition}->{observationLevel}->{levelName} || undef;
433 my $level_number = $params->{observationUnitPosition}->{observationLevel}->{levelCode} ? $params->{observationUnitPosition}->{observationLevel}->{levelCode} : undef;
434 my $raw_additional_info = $params->{additionalInfo} || undef;
435 my $is_a_control = $raw_additional_info->{control} ? $raw_additional_info->{control} : undef;
437 my $entry_type = $params->{observationUnitPosition}->{entryType} ? $params->{observationUnitPosition}->{entryType} : undef;
438 my $is_a_control = $params->{additionalInfo}->{control} ? $params->{additionalInfo}->{control} : undef;
440 # BrAPI entryType overrides additionalinfo.control
441 if ($entry_type) {
442 $is_a_control = uc($entry_type) eq 'CHECK' ? 1 : 0;
445 my $range_number = $raw_additional_info->{range} ? $raw_additional_info->{range} : undef;
446 my %specific_keys = map { $_ => 1 } ("observationUnitParent","control","range");
447 my %additional_info;
448 my $block_number;
449 my $rep_number;
451 foreach (@$level_relations){
452 if($_->{levelName} eq 'block'){
453 $block_number = $_->{levelCode} ? $_->{levelCode} : undef;
455 if($_->{levelName} eq 'rep'){
456 $rep_number = $_->{levelCode} ? $_->{levelCode} : undef;
457 $_->{levelName} = 'rep';
460 if (defined $raw_additional_info) {
461 foreach my $key (keys %$raw_additional_info) {
462 if (!exists($specific_keys{$key})) {
463 $additional_info{$key} = $raw_additional_info->{$key};
468 #Check if observation_unit_db_id is plot or plant and not other stock type
469 my $stock = $self->bcs_schema->resultset('Stock::Stock')->find({stock_id=>$observation_unit_db_id});
470 my $stock_type = $stock->type_id;
472 if (( $stock_type ne $plot_cvterm_id && $stock_type ne $plant_cvterm_id ) || ($level_name ne 'plant' && $level_name ne 'plot')){
473 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf("Only 'plot' or 'plant' allowed for observation level and observationUnitDbId."), 400);
476 #Update: accession
477 # if (! defined $accession_id && ! defined $accession_name) {
478 # return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
480 # my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
481 # if ($germplasm_search_result->{error}) {
482 # return $germplasm_search_result->{error};
483 # } else {
484 # $accession_name = $germplasm_search_result->{name};
488 if(defined $accession_id){
489 # Speed can be improved here by adding a simple query
490 my $layout_accession_search = CXGN::Trial::TrialLayoutSearch->new(
492 bcs_schema=>$schema,
493 data_level=>'all',
494 observation_unit_id_list=>[$observation_unit_db_id],
495 # experiment_type=>'field_layout',
496 include_observations=>1,
499 my ($data_accession,$data_accession_observations) = $layout_accession_search->search();
500 my $old_accession;
501 my $old_accession_id;
503 foreach my $obs_unit (@$data_accession){
504 $old_accession = $obs_unit->{germplasm_uniquename};
505 $old_accession_id = $obs_unit->{germplasm_stock_id};
508 if($accession_id ne $old_accession_id){
509 if (! defined $accession_id && ! defined $accession_name) {
510 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
512 my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
513 if ($germplasm_search_result->{error}) {
514 return $germplasm_search_result->{error};
515 } else {
516 $accession_name = $germplasm_search_result->{name};
519 #update accession
520 my $plot_of_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'plot_of', 'stock_relationship')->cvterm_id();
521 if ($old_accession && $accession_id && $old_accession_id ne $accession_id) {
522 my $replace_plot_accession_fieldmap = CXGN::Trial::FieldMap->new({
523 bcs_schema => $schema,
524 trial_id => $study_ids_arrayref,
525 # new_accession => $accession_name,
526 # old_accession => $old_accession,
527 # old_plot_id => $observation_unit_db_id,
528 # old_plot_name => $observationUnit_name,
529 experiment_type => 'field_layout'
532 my $return_error = $replace_plot_accession_fieldmap->update_fieldmap_precheck();
533 if ($return_error) {
534 print STDERR Dumper $return_error;
535 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Something went wrong. Accession cannot be replaced.'));
538 my $replace_return_error = $replace_plot_accession_fieldmap->replace_plot_accession_fieldMap($observation_unit_db_id, $old_accession_id, $plot_of_type_id);
539 if ($replace_return_error) {
540 print STDERR Dumper $replace_return_error;
541 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Something went wrong. Accession cannot be replaced.'));
547 #Update: geo coordinates
548 my $geo_coordinates = $observationUnit_position_arrayref->{geoCoordinates} || undef;
549 if($geo_coordinates) {
551 my $geno_json_string = encode_json $geo_coordinates;
553 #sub upload coordinates
554 my $upload_plot_gps_txn = sub {
556 my $plots_rs = $schema->resultset("Stock::Stock")->search({stock_id => {-in=>$observation_unit_db_id}});
558 while (my $plot=$plots_rs->next){
559 my $previous_plot_gps_rs = $schema->resultset("Stock::Stockprop")->search({stock_id=>$plot->stock_id, type_id=>$stock_geo_json_cvterm->cvterm_id});
560 $previous_plot_gps_rs->delete_all();
561 $plot->create_stockprops({$stock_geo_json_cvterm->name() => $geno_json_string});
565 eval {
566 $schema->txn_do($upload_plot_gps_txn);
568 if ($@) {
569 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('An error condition occurred, was not able to upload trial plot GPS coordinates. ($@)'));
573 #update stockprops
574 if ($level_number){
575 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' }); }
576 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' }); }
578 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' }); }
579 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' }); }
580 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' }); }
581 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' }); }
582 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' }); }
583 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' }); }
584 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' }); }
586 #store/update external references
587 if ($observationUnit_x_ref){
588 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
589 bcs_schema => $self->bcs_schema,
590 table_name => 'stock',
591 table_id_key => 'stock_id',
592 external_references => $observationUnit_x_ref,
593 id => $observation_unit_db_id
595 my $reference_result = $references->store();
599 my @observation_unit_db_ids;
600 foreach my $params (@$data) { push @observation_unit_db_ids, $params->{observationUnitDbId}; }
602 my $search_params = {observationUnitDbIds => \@observation_unit_db_ids };
603 return $self->search($search_params, $c);
606 sub _get_existing_germplasm {
607 my $self = shift;
608 my $schema = shift;
609 my $accession_id = shift;
610 my $accession_name = shift;
612 if (!looks_like_number($accession_id)) {
613 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Verify Germplasm Id.'), 404)};
616 # Get the germplasm name from germplasmDbId. Check if a germplasm name passed exists
617 my $rs = $schema->resultset("Stock::Stock")->search({stock_id=>$accession_id});
618 if ($rs->count() eq 0 && ! defined $accession_name){
619 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Germplasm with that id does not exist.'), 404)};
620 } elsif ($rs->count() > 0) {
621 my $stock = $rs->first;
622 $accession_name = $stock->uniquename();
623 } else {
624 # Check that a germplasm exists with that name
625 my $rs = $schema->resultset("Stock::Stock")->search({uniquename=>$accession_name});
626 if ($rs->count() eq 0) {
627 return {error => CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Germplasm with that name does not exist.'), 404)};
631 return {name => $accession_name};
634 sub observationunits_store {
635 my $self = shift;
636 my $data = shift;
637 my $c = shift;
638 my $user_id = shift;
640 my $schema = $self->bcs_schema;
641 my $dbh = $self->bcs_schema()->storage()->dbh();
642 my $person = CXGN::People::Person->new($dbh, $user_id);
643 my $user_name = $person->get_username;
645 my %study_plots;
646 my %seen_plot_numbers;
647 foreach my $params (@{$data}) {
648 my $study_id = $params->{studyDbId} || undef;
649 my $plot_name = $params->{observationUnitName} ? $params->{observationUnitName} : undef;
650 my $plot_number = $params->{observationUnitPosition}->{observationLevel}->{levelCode} ? $params->{observationUnitPosition}->{observationLevel}->{levelCode} : undef;
651 my $plot_parent_id = $params->{additionalInfo}->{observationUnitParent} ? $params->{additionalInfo}->{observationUnitParent} : undef;
652 my $accession_id = $params->{germplasmDbId} ? $params->{germplasmDbId} : undef;
653 my $accession_name = $params->{germplasmName} ? $params->{germplasmName} : undef;
654 my $entry_type = $params->{observationUnitPosition}->{entryType} ? $params->{observationUnitPosition}->{entryType} : undef;
655 my $is_a_control = $params->{additionalInfo}->{control} ? $params->{additionalInfo}->{control} : undef;
657 # BrAPI entryType overrides additionalinfo.control
658 if ($entry_type) {
659 $is_a_control = uc($entry_type) eq 'CHECK' ? 1 : 0;
662 my $range_number = $params->{additionalInfo}->{range} ? $params->{additionalInfo}->{range} : undef;
663 my $row_number = $params->{observationUnitPosition}->{positionCoordinateY} ? $params->{observationUnitPosition}->{positionCoordinateY} : undef;
664 my $col_number = $params->{observationUnitPosition}->{positionCoordinateX} ? $params->{observationUnitPosition}->{positionCoordinateX} : undef;
665 my $seedlot_id = $params->{seedLotDbId} ? $params->{seedLotDbId} : undef;
666 my $plot_geo_json = $params->{observationUnitPosition}->{geoCoordinates} ? $params->{observationUnitPosition}->{geoCoordinates} : undef;
667 my $levels = $params->{observationUnitPosition}->{observationLevelRelationships} ? $params->{observationUnitPosition}->{observationLevelRelationships} : undef;
668 my $ou_level = $params->{observationUnitPosition}->{observationLevel}->{levelName} || undef;
669 my $observationUnit_x_ref = $params->{externalReferences} ? $params->{externalReferences} : undef;
670 my $raw_additional_info = $params->{additionalInfo} || undef;
671 my %specific_keys = map { $_ => 1 } ("observationUnitParent","control");
672 my %additional_info;
673 if (defined $raw_additional_info) {
674 foreach my $key (keys %$raw_additional_info) {
675 if (!exists($specific_keys{$key})) {
676 $additional_info{$key} = $raw_additional_info->{$key};
680 my $block_number;
681 my $rep_number;
683 # Required fields check
684 if (! defined $accession_id && ! defined $accession_name) {
685 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Either germplasmDbId or germplasmName is required.'), 400);
688 if ($ou_level ne 'plant' && $ou_level ne 'plot') {
689 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Only "plot" or "plant" allowed for observation level.'), 400);
692 my $project = $self->bcs_schema->resultset("Project::Project")->find({ project_id => $study_id });
693 if (! defined $project) {
694 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf("A study with id $study_id does not exist"), 404);
697 # Get the germplasm name from germplasmDbId. Check if a germplasm name passed exists
698 my $germplasm_search_result = $self->_get_existing_germplasm($schema, $accession_id, $accession_name);
699 if ($germplasm_search_result->{error}) {
700 return $germplasm_search_result->{error};
701 } else {
702 $accession_name = $germplasm_search_result->{name};
705 foreach (@$levels){
706 if($_->{levelName} eq 'block'){
707 $block_number = $_->{levelCode} ? $_->{levelCode} : undef;
709 if($_->{levelName} eq 'rep'){
710 $rep_number = $_->{levelCode} ? $_->{levelCode} : undef;
711 $_->{levelName} = 'rep';
715 # The trial designer expects a list of plant names, this object is a plant, so add to single item list
716 my $plot_hash;
717 if ($ou_level eq 'plant') {
718 # Check that the parent already exists
719 my $plot_parent_name;
720 if ($plot_parent_id) {
721 my $rs = $schema->resultset("Stock::Stock")->search({stock_id=>$plot_parent_id});
722 if ($rs->count() eq 0){
723 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('Plot with id %s does not exist.', $plot_parent_id), 404);
725 $plot_parent_name = $rs->first()->uniquename();
726 } else {
727 return CXGN::BrAPI::JSONResponse->return_error($self->status, sprintf('addtionalInfo.observationUnitParent for observation unit with level "plant" is required'), 404);
730 $plot_hash = {
731 plot_name => $plot_parent_name,
732 plant_names => [ $plot_name ],
733 # accession_name => $accession_name,
734 stock_name => $accession_name,
735 plot_number => $plot_number,
736 block_number => $block_number,
737 is_a_control => $is_a_control,
738 rep_number => $rep_number,
739 range_number => $range_number,
740 row_number => $row_number,
741 col_number => $col_number,
742 # plot_geo_json => $plot_geo_json,
743 additional_info => \%additional_info,
744 external_refs => $observationUnit_x_ref
746 } else {
747 $plot_hash = {
748 plot_name => $plot_name,
749 # accession_name => $accession_name,
750 stock_name => $accession_name,
751 plot_number => $plot_number,
752 block_number => $block_number,
753 is_a_control => $is_a_control,
754 rep_number => $rep_number,
755 range_number => $range_number,
756 row_number => $row_number,
757 col_number => $col_number,
758 # plot_geo_json => $plot_geo_json,
759 additional_info => \%additional_info,
760 external_refs => $observationUnit_x_ref
764 $study_plots{$study_id}{$plot_number} = $plot_hash;
765 $seen_plot_numbers{$study_id}{$plot_number}++;
768 # Check that the plot numbers passed are unique per study
769 foreach my $study_design (values %seen_plot_numbers) {
770 foreach my $seen_plot_number (keys %{$study_design}) {
771 if ($study_design->{$seen_plot_number} > 1) {
772 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);
777 my $coderef = sub {
778 foreach my $study_id (keys %study_plots) {
780 # Get the study design type
781 my $study = $study_plots{$study_id};
782 my $project = $self->bcs_schema->resultset("Project::Project")->find({ project_id => $study_id });
783 my $design_prop = $project->projectprops->find({ 'type.name' => 'design' }, { join => 'type' }); #there should be only one design prop.
784 if (!$design_prop) {
785 die {error => 'Study does not have a study type.', errorCode => 500};
787 my $design_type = $design_prop->value;
789 # Get the study location
790 my $location_id;
791 my $location_type_id = SGN::Model::Cvterm->get_cvterm_row($schema, 'project location', 'project_property');
792 my $row = $self->bcs_schema()->resultset('Project::Projectprop')->find({
793 project_id => $project->project_id(),
794 type_id => $location_type_id->cvterm_id(),
796 if ($row) {
797 print('Row value: ' . $row->value());
798 $location_id = $row->value();
800 else {
801 die {error => sprintf('Erro retrieving the location of the study'), errorCode => 500};
804 my $trial_design_store = CXGN::Trial::TrialDesignStore->new({
805 bcs_schema => $schema,
806 trial_id => $study_id,
807 nd_geolocation_id => $location_id,
808 # nd_experiment_id => $nd_experiment->nd_experiment_id(), #optional
809 is_genotyping => 0,
810 new_treatment_has_plant_entries => 0,
811 new_treatment_has_subplot_entries => 0,
812 operator => $user_name,
813 trial_stock_type => 'accessions',
814 design_type => $design_type,
815 design => $study,
816 operator => $user_name
819 my $error;
820 my $validate_design_error = $trial_design_store->validate_design();
821 if ($validate_design_error) {
822 die {error => sprintf('Error validating study design: ' . $validate_design_error), errorCode => 422};
824 else {
825 $error = $trial_design_store->store();
826 if ($error) {
827 die {error => sprintf('ERROR store: ' . $error), errorCode => 500};
829 # Refresh the trial layout property
830 my %param = ( schema => $schema, trial_id => $study_id );
831 if ($design_type eq 'genotyping_plate'){
832 $param{experiment_type} = 'genotyping_layout';
833 } else {
834 $param{experiment_type} = 'field_layout';
836 my $trial_layout = CXGN::Trial::TrialLayout->new(\%param);
837 my $design = $trial_layout->generate_and_cache_layout();
839 foreach my $plot_num (keys %{$design}) {
840 my $observationUnit_x_ref = $study->{$plot_num}->{external_refs};
841 if ($observationUnit_x_ref){
842 my $references = CXGN::BrAPI::v2::ExternalReferences->new({
843 bcs_schema => $self->bcs_schema,
844 table_name => 'stock',
845 table_id_key => 'stock_id',
846 external_references => $observationUnit_x_ref,
847 id => $design->{$plot_num}->{plot_id}
849 my $reference_result = $references->store();
856 my $error_resp;
857 try {
858 $schema->txn_do($coderef);
860 catch {
861 print "Error: :". Dumper($_);
862 # print Dumper("Error: $_\n");
863 $error_resp = CXGN::BrAPI::JSONResponse->return_error($self->status, $_->{error}, $_->{errorCode} || 500);
865 if ($error_resp) { return $error_resp; }
867 # Get our new OUs by name. Not ideal, but names are unique and its the quickest solution
868 my @observationUnitNames;
869 foreach my $ou (@{$data}) { push @observationUnitNames, $ou->{observationUnitName}; }
870 my $search_params = {observationUnitNames => \@observationUnitNames};
871 $self->page_size(scalar @{$data});
872 return $self->search($search_params, $c);
875 sub _order {
876 my $value = shift;
877 my %levels = (
878 "rep" => 0,
879 "block" => 1,
880 "plot" => 2,
881 "subplot"=> 3,
882 "plant"=> 4,
883 "tissue_sample"=> 5,
886 return $levels{$value} + 0;