4 CXGN::Trial - factory object for project entries (phenotyping trials, genotyping trials, crossing trials, and analyses
8 my $trial = CXGN::Trial->new( { bcs_schema => $schema, ... , trial_id => $trial_id });
10 If $trial_id is a phenotyping trial, the type of object returned will be CXGN::PhenotypingTrial.
14 Lukas Mueller <lam87@cornell.edu>
26 use CXGN
::Trial
::Folder
;
27 use CXGN
::Trial
::TrialLayout
;
28 use CXGN
::Trial
::TrialLayoutDownload
;
29 use SGN
::Model
::Cvterm
;
34 use File
::Basename qw
| basename dirname
|;
35 use CXGN
::BrAPI
::v2
::ExternalReferences
;
42 If $trial_id is a genotyping trial, the type of object returned will be CXGN::GenotypingTrial.
44 If $trial_id is a crossing trial, the type of object returned will be CXGN::CrossingTrial.
46 If $trial_id is an analysis, the type of object returned will be CXGN::Analysis.
50 Inheritance structure of Trial objects:
52 CXGN::Trial - Factory object (for backwards compatibility)
56 ---CXGN::PhenotypingTrial
58 | ---CXGN::GenotypingTrial
60 | ---CXGN::CrossingTrial
66 Lukas Mueller <lam87@cornell.edu>
68 Based on work by the entire group :-)
74 use CXGN
::PhenotypingTrial
;
75 use CXGN
::GenotypingTrial
;
76 use CXGN
::CrossingTrial
;
78 use CXGN
::SamplingTrial
;
79 use CXGN
::ManagementFactor
;
80 use CXGN
::GenotypeDataProject
;
81 use CXGN
::AerialImagingEventBandProject
;
82 use CXGN
::AerialImagingEventProject
;
88 my $schema = $args->{bcs_schema
};
89 my $trial_id = $args->{trial_id
};
91 my $trial_rs = $schema->resultset("Project::Projectprop")->search( { project_id
=> $trial_id },{ join => 'type' });
93 if ($trial_id && $trial_rs->count() == 0) {
94 return CXGN
::PhenotypingTrial
->new($args);
98 while (my $trial_row = $trial_rs->next()) {
99 my $name = $trial_row->type()->name();
100 my $val = $trial_row->value();
101 # print STDERR Dumper [$name, $val];
102 if ($val eq "genotyping_plate") {
103 return CXGN
::GenotypingTrial
->new($args);
105 elsif ($name eq "crossing_trial") {
106 return CXGN
::CrossingTrial
->new($args);
108 elsif ($name eq "analysis") {
109 return CXGN
::Analysis
->new($args);
111 elsif ($val eq "treatment") {
112 return CXGN
::ManagementFactor
->new($args);
114 elsif ($val eq "sampling_trial") {
115 return CXGN
::SamplingTrial
->new($args);
117 elsif (($val eq "genotype_data_project") || ($val eq "pcr_genotype_data_project")) {
118 return CXGN
::GenotypeDataProject
->new($args);
120 elsif ($val eq "drone_run") {
121 return CXGN
::AerialImagingEventProject
->new($args);
123 elsif ($val eq "drone_run_band") {
124 return CXGN
::AerialImagingEventBandProject
->new($args);
127 $object = CXGN
::PhenotypingTrial
->new($args);
133 =head2 class method get_all_locations()
135 Usage: my $locations = CXGN::Trial::get_all_locations($schema)
144 sub get_all_locations
{
146 my $location_id = shift;
151 $search_params{'nd_geolocation_id'} = $location_id;
154 my $loc = $schema->resultset('NaturalDiversity::NdGeolocation')->search( \
%search_params, {order_by
=> { -asc
=> 'nd_geolocation_id' }} );
155 while (my $s = $loc->next()) {
156 my $loc_props = $schema->resultset('NaturalDiversity::NdGeolocationprop')->search( { nd_geolocation_id
=> $s->nd_geolocation_id() }, {join=>'type', '+select'=>['me.value', 'type.name'], '+as'=>['value', 'cvterm_name'] } );
159 $attr{'geodetic datum'} = $s->geodetic_datum();
162 my $country_code = '';
163 my $location_type = '';
164 my $abbreviation = '';
167 while (my $sp = $loc_props->next()) {
168 if ($sp->get_column('cvterm_name') eq 'country_name') {
169 $country = $sp->get_column('value');
170 } elsif ($sp->get_column('cvterm_name') eq 'country_code') {
171 $country_code = $sp->get_column('value');
172 } elsif ($sp->get_column('cvterm_name') eq 'location_type') {
173 $location_type = $sp->get_column('value');
174 } elsif ($sp->get_column('cvterm_name') eq 'abbreviation') {
175 $abbreviation = $sp->get_column('value');
176 } elsif ($sp->get_column('cvterm_name') eq 'geolocation address') {
177 $address = $sp->get_column('value');
179 $attr{$sp->get_column('cvterm_name')} = $sp->get_column('value') ;
183 my @reference_locations = ($s->nd_geolocation_id());
184 my $references = CXGN
::BrAPI
::v2
::ExternalReferences
->new({
185 bcs_schema
=> $schema,
186 table_name
=> 'nd_geolocation',
187 table_id_key
=> 'nd_geolocation_id',
188 id
=> \
@reference_locations
190 my $external_references_search = $references->search();
191 my $external_references = $external_references_search->{$s->nd_geolocation_id()} || [];
194 push @locations, [$s->nd_geolocation_id(), $s->description(), $s->latitude(), $s->longitude(), $s->altitude(), $country, $country_code, \
%attr, $location_type, $abbreviation, $address, $external_references],
202 =head2 class method get_all_project_types()
204 Usage: my @cvterm_ids = CXGN::Trial::get_all_project_types($schema)
213 sub get_all_project_types
{
216 my $project_type_cv_id = $schema->resultset('Cv::Cv')->find( { name
=> 'project_type' } )->cv_id();
217 my $rs = $schema->resultset('Cv::Cvterm')->search( { cv_id
=> $project_type_cv_id }, {order_by
=>'me.cvterm_id'} );
219 if ($rs->count() > 0) {
220 @cvterm_ids = map { [ $_->cvterm_id(), $_->name(), $_->definition ] } ($rs->all());
226 =head2 function get_all_phenotype_metadata($schema, $n)
229 Usage: CXGN::Trial->get_phenotype_metadata($schema, 100);
230 Desc: retrieves maximally $n metadata.md_file entries for the any trial . These entries are created during StorePhenotypes.
238 sub get_all_phenotype_metadata
{
241 my $n = shift || 200;
244 my $q = "SELECT file_id, m.create_date, p.sp_person_id, p.username, basename, dirname, filetype FROM nd_experiment_project JOIN nd_experiment_phenotype USING(nd_experiment_id) JOIN phenome.nd_experiment_md_files ON (nd_experiment_phenotype.nd_experiment_id=nd_experiment_md_files.nd_experiment_id) LEFT JOIN metadata.md_files using(file_id) LEFT JOIN metadata.md_metadata as m using(metadata_id) LEFT JOIN sgn_people.sp_person as p ON (p.sp_person_id=m.create_person_id) WHERE m.obsolete = 0 and NOT (metadata.md_files.filetype='generated from plot from plant phenotypes') and NOT (metadata.md_files.filetype='direct phenotyping') ORDER BY file_id ASC LIMIT $n";
245 my $h = $schema->storage()->dbh()->prepare($q);
248 while (my ($file_id, $create_date, $person_id, $username, $basename, $dirname, $filetype) = $h->fetchrow_array()) {
249 $file_info{$file_id} = [$file_id, $create_date, $person_id, $username, $basename, $dirname, $filetype];
251 foreach (keys %file_info){
252 push @file_array, $file_info{$_};
260 =head2 function get_sorted_plots()
262 Usage: CXGN::Trial->get_sorted_plots($schema, $trials, $order, $start, $gaps)
263 Desc: Get an array of plot metadata (plot_id, plot_name, row_number, col_number, etc)
264 for plots in the trial. Sort the plots by the provided traversal parameters.
265 Requirements: The Trial(s) MUST have row/col positions for every plot AND there must not
266 be any overlapping plots (more than one plot with the same row/col position).
267 Ret: An array of sorted plot metadata
268 Args: trials = an arrayref of trial ids to include
269 order = the order to traverse the plots ('by_col_serpentine', 'by_col_zigzag', 'by_row_serpentine', 'by_row_zigzag')
270 start = the corner of the trial layout to start the traversal ('bottom_left', 'top_left', 'top_right', 'bottom_right')
271 borders = a hashref with keys top, right, bottom, left. If the value is 1, then include that side as a border
272 gaps = when set to 1, include missing plots / gaps as items in the order
278 sub get_sorted_plots
{
282 my $order = shift || 'by_row_serpentine';
283 my $start = shift || 'bottom_left';
284 my $borders = shift || { top
=> 0, right
=> 0, bottom
=> 0, left
=> 0 };
285 my $gaps = shift || 0;
289 my ($min_row, $max_row, $min_col, $max_col, %seen_row_cols);
290 foreach my $trial_id (@
$trials) {
292 # Get plot details from the stored layout information
293 my $trial_layout_download = CXGN
::Trial
::TrialLayoutDownload
->new({
295 trial_id
=> $trial_id,
296 data_level
=> 'plots',
297 selected_columns
=> {
298 "location_name"=>1,"trial_name"=>1,"plot_name"=>1,"plot_id"=>1,"plot_number"=>1,
299 "row_number"=>1,"col_number"=>1,"accession_name"=>1,"seedlot_name"=>1,
300 "rep_number"=>1,"block_number"=>1,"is_a_control"=>1,"accession_id"=>1
303 my $output = $trial_layout_download->get_layout_output()->{output
};
305 # Convert plot layout array into hash and add each plot to the plot_details array (of hashes)
306 # Check for plot row/col requirements
307 # Set the overall min/max row/col positions
308 my @outer_array = @
{$output};
309 my ($inner_array, @keys);
310 for my $i (0 .. $#outer_array) {
311 $inner_array = $outer_array[$i];
312 if (scalar @keys > 0) {
314 @detail_hash{@keys} = @
{$outer_array[$i]};
315 my $row = $detail_hash{'row_number'};
316 my $col = $detail_hash{'col_number'};
317 my $key = "$row|$col";
319 # Check for undefined row and column positions
320 if ( !defined($row) || !defined($col) ) {
321 return { error
=> "One or more plots do not have a row and/or column defined!" };
324 # Check for duplicate positions (plots with the same row / col positions)
325 if ( exists $seen_row_cols{$key} ) {
326 return { error
=> "One or more plots share the same row and column position!" };
329 # Set the min/max row/col
332 if ( !defined($min_row) || $row < $min_row ) {
335 if ( !defined($max_row) || $row > $max_row ) {
338 if ( !defined($min_col) || $col < $min_col ) {
341 if ( !defined($max_col) || $col > $max_col ) {
345 push(@plot_details, \
%detail_hash);
348 @keys = @
{$inner_array};
353 # Set starting position:
354 # right = col from max to min
355 # left = col from min to max
356 # top = row from max to min
357 # bottom = row from min to max
358 # Add a row/col on either side for the borders
359 my ($start_row, $end_row, $delta_row);
360 my ($start_col, $end_col, $delta_col);
361 if ( $start =~ /right/ ) {
362 $start_col = $max_col + 1;
363 $end_col = $min_col - 1;
367 $start_col = $min_col - 1;
368 $end_col = $max_col + 1;
371 if ( $start =~ /top/ ) {
372 $start_row = $max_row + 1;
373 $end_row = $min_row - 1;
377 $start_row = $min_row - 1;
378 $end_row = $max_row + 1;
382 # Set traversal order:
383 # by_col = first by column (outer loop) then by row (inner loop)
384 # by_row = first by row (outer loop) then by col (inner loop)
385 my ($outerloop_key, $outerloop_start, $outerloop_end, $outerloop_delta);
386 my ($innerloop_key, $innerloop_start, $innerloop_end, $innerloop_delta);
387 if ( $order =~ /by_col/ ) {
388 $outerloop_key = 'col_number';
389 $outerloop_start = $start_col;
390 $outerloop_end = $end_col;
391 $outerloop_delta = $delta_col;
392 $innerloop_key = 'row_number';
393 $innerloop_start = $start_row;
394 $innerloop_end = $end_row;
395 $innerloop_delta = $delta_row;
398 $outerloop_key = 'row_number';
399 $outerloop_start = $start_row;
400 $outerloop_end = $end_row;
401 $outerloop_delta = $delta_row;
402 $innerloop_key = 'col_number';
403 $innerloop_start = $start_col;
404 $innerloop_end = $end_col;
405 $innerloop_delta = $delta_col;
408 # Start the traversal
413 # Start the outerloop...
414 for ( my $o = $outerloop_start; $outerloop_delta > 0 ?
$o <= $outerloop_end : $o >= $outerloop_end; $o=$o+$outerloop_delta ) {
415 my $starting_p_order = $p_order;
417 # Invert the order of every other innerloop when serpentine
418 my $i_start = $innerloop_start;
419 my $i_end = $innerloop_end;
420 my $i_delta = $innerloop_delta;
421 if ( $order =~ /serpentine/ ) {
422 if ( $o_count % 2 ) {
423 $i_start = $innerloop_end;
424 $i_end = $innerloop_start;
425 $i_delta = $innerloop_delta*-1;
429 # Start the innerloop...
430 for ( my $i = $i_start; $i_delta > 0 ?
$i <= $i_end : $i >= $i_end; $i=$i+$i_delta ) {
436 # Determine border type based on current position
437 my $obt_start = $outerloop_key eq 'row_number' ?
($outerloop_delta > 0 ?
'bottom' : 'top') : ($outerloop_delta > 0 ?
'left' : 'right');
438 my $obt_end = $outerloop_key eq 'row_number' ?
($outerloop_delta > 0 ?
'top' : 'bottom') : ($outerloop_delta > 0 ?
'right' : 'left');
439 my $ibt_start = $innerloop_key eq 'col_number' ?
($innerloop_delta > 0 ?
'left' : 'right') : ($innerloop_delta > 0 ?
'bottom' : 'top');
440 my $ibt_end = $innerloop_key eq 'col_number' ?
($innerloop_delta > 0 ?
'right' : 'left') : ($innerloop_delta > 0 ?
'top' : 'bottom');
443 if ( $o == $outerloop_start && $i == $innerloop_start ) {
444 if ( $borders->{$obt_start} && $borders->{$ibt_start} ) {
445 push(@ordered_plots, {
448 border
=> $obt_start . "_" . $ibt_start,
449 $outerloop_key => $o,
457 elsif ( $o == $outerloop_end && $i == $innerloop_start ) {
458 if ( $borders->{$obt_end} && $borders->{$ibt_start} ) {
459 push(@ordered_plots, {
462 border
=> $obt_end . "_" . $ibt_start,
463 $outerloop_key => $o,
471 elsif ( $o == $outerloop_end && $i == $innerloop_end ) {
472 if ( $borders->{$obt_end} && $borders->{$ibt_end} ) {
473 push(@ordered_plots, {
476 border
=> $obt_end . "_" . $ibt_end,
477 $outerloop_key => $o,
485 elsif ( $o == $outerloop_start && $i == $innerloop_end ) {
486 if ( $borders->{$obt_start} && $borders->{$ibt_end} ) {
487 push(@ordered_plots, {
490 border
=> $obt_start . "_" . $ibt_end,
491 $outerloop_key => $o,
498 # Add outer start border
499 elsif ( $o == $outerloop_start ) {
500 if ( $borders->{$obt_start} ) {
501 push(@ordered_plots, {
504 border
=> $obt_start,
505 $outerloop_key => $o,
512 # Add outer end border
513 elsif ( $o == $outerloop_end ) {
514 if ( $borders->{$obt_end} ) {
515 push(@ordered_plots, {
519 $outerloop_key => $o,
526 # Add inner start border
527 elsif ( $i == $innerloop_start ) {
528 if ( $borders->{$ibt_start} ) {
529 push(@ordered_plots, {
532 border
=> $ibt_start,
533 $outerloop_key => $o,
540 # Add inner end border
541 elsif ( $i == $innerloop_end ) {
542 if ( $borders->{$ibt_end} ) {
543 push(@ordered_plots, {
547 $outerloop_key => $o,
560 # Find the plot with the matching row / col position
561 my ($p) = grep { $_->{$outerloop_key} == $o && $_->{$innerloop_key} == $i } @plot_details;
563 # Add the plot, if it's found
565 $p->{order
} = $p_order;
567 push(@ordered_plots, $p);
571 # Add a gap item when there is no plot, if requested
573 push(@ordered_plots, {
576 $outerloop_key => $o,
586 if ( $p_order > $starting_p_order ) {
591 return { plots
=> \
@ordered_plots };