4 CXGN::Stock::Seedlot - a class to represent seedlots in the database
8 CXGN::Stock::Seedlot inherits from CXGN::Stock.
10 To create a new seedlot do:
11 #Seedlot can either be from an accession or a cross, therefore, supply an accession_stock_id OR a cross_stock_id here
13 my $sl = CXGN::Stock::Seedlot->new(schema => $schema);
14 $sl->uniquename($seedlot_uniquename);
15 $sl->location_code($location_code);
16 $sl->accession_stock_id($accession_id);
17 $sl->cross_stock_id($cross_id);
18 $sl->organization_name($organization);
19 $sl->population_name($population_name);
20 $sl->breeding_program_id($breeding_program_id);
21 my $return = $sl->store();
22 my $seedlot_id = $return->{seedlot_id};
24 #The first transaction is between the accession_stock_id OR cross_stock_id that you specified above and the new seedlot created.
25 my $transaction = CXGN::Stock::Seedlot::Transaction->new(schema => $schema);
26 $transaction->factor(1);
27 $transaction->from_stock([$from_stock_id, $from_stock_uniquename]);
28 $transaction->to_stock([$seedlot_id, $seedlot_uniquename]);
29 $transaction->amount($amount);
30 $transaction->timestamp($timestamp);
31 $transaction->description($description);
32 $transaction->operator($operator);
33 $transaction->store();
35 $sl->set_current_count_property();
37 $phenome_schema->resultset("StockOwner")->find_or_create({
38 stock_id => $seedlot_id,
39 sp_person_id => $user_id,
42 -------------------------------------------------------------------------------
44 To Update or Edit a seedlot do:
46 my $seedlot = CXGN::Stock::Seedlot->new(
48 seedlot_id => $seedlot_id,
50 $seedlot->name($seedlot_name);
51 $seedlot->uniquename($seedlot_name);
52 $seedlot->breeding_program_id($breeding_program_id);
53 $seedlot->organization_name($organization);
54 $seedlot->location_code($location);
55 $seedlot->accession_stock_id($accession_id);
56 $seedlot->cross_stock_id($cross_id);
57 $seedlot->population_name($population);
58 my $return = $seedlot->store();
60 ------------------------------------------------------------------------------
62 To Search Across Seedlots do:
63 # This is different from CXGN::Stock::Search in that is retrieves information pertinent to seedlots like location and current count
65 my ($list, $records_total) = CXGN::Stock::Seedlot->list_seedlots(
66 $c->dbic_schema("Bio::Chado::Schema"),
76 $exact_match_uniquenames,
80 ------------------------------------------------------------------------------
82 To Retrieve a single seedlot do:
84 my $seedlot = CXGN::Stock::Seedlot->new(
86 seedlot_id => $seedlot_id,
88 # You can access all seedlot accessors from here such as (you can also access all CXGN::Stock accessors):
89 my $uniquename => $seedlot->uniquename(),
90 my $seedlot_id => $seedlot->seedlot_id(),
91 my $current_count => $seedlot->current_count(),
92 my $location_code => $seedlot->location_code(),
93 my $breeding_program => $seedlot->breeding_program_name(),
94 my $organization_name => $seedlot->organization_name(),
95 my $population_name => $seedlot->population_name(),
96 my $accession => $seedlot->accession(),
97 my $cross => $seedlot->cross(),
99 ------------------------------------------------------------------------------
101 Seed transactions can be added using CXGN::Stock::Seedlot::Transaction.
103 ------------------------------------------------------------------------------
105 Seed Maintenance Events can be stored and retrieved using the helper functions
106 in this Seedlot class.
108 To add a Maintenance Event:
110 my $seedlot = CXGN::Stock::Seedlot->new( schema => $schema, seedlot_id => $seedlot_id );
113 cvterm_id => $cvterm_id,
116 operator => $operator,
117 timestamp => $timestamp
120 my $stored_events = $seedlot->store_events(\@events);
125 Lukas Mueller <lam87@cornell.edu>
126 Nick Morales <nm529@cornell.edu>
128 =head1 ACCESSORS & METHODS
132 package CXGN
::Stock
::Seedlot
;
137 extends
'CXGN::Stock';
140 use CXGN
::Stock
::Seedlot
::Transaction
;
141 use CXGN
::BreedersToolbox
::Projects
;
142 use SGN
::Model
::Cvterm
;
143 use CXGN
::List
::Validate
;
145 use CXGN
::Stock
::StockLookup
;
146 use CXGN
::Stock
::Search
;
149 =head2 Accessor seedlot_id()
151 the database id of the seedlot. Is equivalent to stock_id.
155 has
'seedlot_id' => (
160 =head2 Accessor location_code()
162 A string specifiying where the seedlot is stored. On the backend,
163 this is stored the nd_geolocation description field.
167 has
'location_code' => (
171 builder
=> '_retrieve_location',
174 has
'nd_geolocation_id' => (
178 builder
=> '_retrieve_location_id',
181 =head2 Accessor box_name()
183 A string specifiying box where the seedlot is stored. On the backend,
184 this is stored as a stockprop.
192 builder
=> '_retrieve_box_name',
195 =head2 Accessor cross()
197 The crosses this seedlot is a "collection_of". Returns an arrayref of [$cross_stock_id, $cross_uniquename]
198 # for setter, use cross_stock_id
203 isa
=> 'ArrayRef|Undef',
206 builder
=> '_retrieve_cross',
209 has
'cross_stock_id' => (
214 =head2 Accessor quality()
216 Allows to store a string describing the quality of this seedlot. A seedlot with no quality issues has no data stored here. Requested initially by AC.
224 builder
=> '_retrieve_quality',
228 =head2 Accessor source()
230 The source of this seedlot. This can be the plant, plot, or accession it was sourced from, in a seed multiplication experiment, for example, in the absence of a cross experiment. Requested initially by AC.
238 builder
=> '_retrieve_source',
242 =head2 Accessor accessions()
244 The accessions this seedlot is a "collection_of". Returns an arrayref of [$accession_stock_id, $accession_uniquename]
245 # for setter, use accession_stock_id
250 isa
=> 'ArrayRef|Undef',
253 builder
=> '_retrieve_accession',
256 has
'accession_stock_id' => (
261 =head2 Accessor transactions()
263 a ArrayRef of CXGN::Stock::Seedlot::Transaction objects
267 has
'transactions' => (
271 builder
=> '_build_transactions',
274 =head2 Accessor breeding_program
276 The breeding program this seedlot is from. Useful for tracking movement of seedlots across breeding programs
277 Use breeding_program_id as setter (to save and update seedlots).
281 has
'breeding_program_name' => (
285 builder
=> '_retrieve_breeding_program',
288 has
'breeding_program_id' => (
292 builder
=> '_retrieve_breeding_program_id',
296 after
'stock_id' => sub {
299 return $self->seedlot_id($id);
303 =head2 Class method: list_seedlots()
305 Usage: my $seedlots = CXGN::Stock::Seedlot->list_seedlots($schema);
306 Desc: Class method that returns information on all seedlots
307 available in the system
308 Ret: ArrayRef of [ seedlot_id, seedlot name, location_code]
309 Args: $schema - Bio::Chado::Schema object
310 Side Effects: accesses the database
317 my $people_schema = shift;
318 my $phenome_schema = shift;
321 my $seedlot_name = shift;
322 my $description = shift;
323 my $breeding_program = shift;
324 my $location = shift;
325 my $minimum_count = shift;
326 my $contents_accession = shift; #arrayref of uniquenames
327 my $contents_cross = shift; #arrayref of uniquenames
328 my $exact_match_uniquenames = shift;
329 my $minimum_weight = shift;
330 my $seedlot_id = shift; #added for BrAPI
331 my $accession_id = shift; #added for BrAPI
333 my $only_good_quality = shift;
334 my $box_name = shift;
335 my $contents_cross_db_id = shift;
336 my $trial_name = shift; # name of trial used in a transaction (must also specify trial_usage)
337 my $trial_usage = shift; # transaction type (either 'source', 'sink', or 'source|sink')
338 # where the trial is the source, sink, or either of the matching seedlot's seed
342 $schema->storage->debug(1);
346 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
347 my $cross_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "cross", "stock_type")->cvterm_id();
348 my $accession_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "accession", "stock_type")->cvterm_id();
349 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
350 my $current_count_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "current_count", "stock_property")->cvterm_id();
351 my $current_weight_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "current_weight_gram", "stock_property")->cvterm_id();
352 my $experiment_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot_experiment", "experiment_type")->cvterm_id();
353 my $seedlot_quality_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot_quality", "stock_property")->cvterm_id();
354 my $location_code_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "location_code", "stock_property")->cvterm_id();
357 $search_criteria{'me.type_id'} = $type_id;
358 $search_criteria{'stock_relationship_objects.type_id'} = $collection_of_cvterm_id;
360 # print STDERR "Adding seedlot name ($seedlot_name) to query...\n";
361 $search_criteria{'me.uniquename'} = { 'ilike' => '%'.$seedlot_name.'%' };
364 # print STDERR "Adding seedlot_id ($seedlot_id) to query...\n";
365 $search_criteria{'me.stock_id'} = { -in => $seedlot_id };
367 if ($breeding_program) {
368 # print STDERR "Adding breeding_program $breeding_program to query...\n";
369 $search_criteria{'project.name'} = { 'ilike' => '%'.$breeding_program.'%' };
372 # print STDERR "Adding location $location to query...\n";
373 $search_criteria{'nd_geolocation.description'} = { 'ilike' => '%'.$location.'%' };
375 if ($contents_accession && scalar(@
$contents_accession)>0) {
376 # print STDERR "Adding contents accession: $contents_accession ...\n";
377 $search_criteria{'subject.type_id'} = $accession_type_id;
378 if ($exact_match_uniquenames){
379 $search_criteria{'subject.uniquename'} = { -in => $contents_accession };
381 foreach (@
$contents_accession){
382 push @
{$search_criteria{'subject.uniquename'}}, { 'ilike' => '%'.$_.'%' };
386 if ($accession_id && ref($accession_id) && scalar(@
$accession_id)>0) {
387 print STDERR
"Accession ID is ";
388 print Dumper
$accession_id;
389 $search_criteria{'subject.type_id'} = $accession_type_id;
390 $search_criteria{'subject.stock_id'} = { -in => $accession_id };
392 if ($contents_cross && scalar(@
$contents_cross)>0) {
393 $search_criteria{'subject.type_id'} = $cross_type_id;
394 if ($exact_match_uniquenames){
395 $search_criteria{'subject.uniquename'} = { -in => $contents_cross };
397 foreach (@
$contents_cross){
398 push @
{$search_criteria{'subject.uniquename'}}, { 'ilike' => '%'.$_.'%' };
403 if ($contents_cross_db_id && scalar(@
$contents_cross_db_id)>0) {
404 $search_criteria{'subject.type_id'} = $cross_type_id;
405 $search_criteria{'subject.stock_id'} = { -in => $contents_cross_db_id };
408 my @seedlot_search_joins = (
409 {'nd_experiment_stocks' => {'nd_experiment' => [ {'nd_experiment_projects' => 'project' }, 'nd_geolocation' ] }},
410 {'stock_relationship_objects' => 'subject'}
413 if ($minimum_count || $minimum_weight || $quality || $only_good_quality || $box_name) {
414 if ($minimum_count) {
415 print STDERR
"Minimum count $minimum_count\n";
416 $search_criteria{'stockprops.value'} = { '<>' => 'NA' };
417 $search_criteria{'stockprops.value::numeric'} = { '>=' => $minimum_count };
418 $search_criteria{'stockprops.type_id'} = $current_count_cvterm_id;
419 } elsif ($minimum_weight) {
420 print STDERR
"Minimum weight $minimum_weight\n";
421 $search_criteria{'stockprops.value'} = { '<>' => 'NA' };
422 $search_criteria{'stockprops.value::numeric'} = { '>=' => $minimum_weight };
423 $search_criteria{'stockprops.type_id'} = $current_weight_cvterm_id;
426 print STDERR
"Quality $quality\n";
427 $search_criteria{'stockprops.value' } = { '=' => $quality };
428 $search_criteria{'stockprops.type_id' } = $seedlot_quality_cvterm_id;
431 print STDERR
"Box Name $box_name\n";
432 $search_criteria{'stockprops.value'} = { 'ilike' => '%'.$box_name.'%' };
433 $search_criteria{'stockprops.type_id'} = $location_code_cvterm_id;
435 push @seedlot_search_joins, 'stockprops';
438 if ($trial_name && $trial_usage) {
440 # Build query to get stocks that match the requested transactions
442 my $q = "SELECT subject_id, object_id";
443 $q .= " FROM public.stock_relationship";
444 $q .= " WHERE type_id = (SELECT cvterm_id FROM public.cvterm WHERE name = 'seed transaction')";
446 # Subquery to get stocks (plots, etc) in requested trial
447 my $sq = "SELECT DISTINCT(stock_id) FROM public.materialized_phenoview WHERE trial_id = (SELECT project_id FROM public.project WHERE name = ?)";
450 # Add source transaction (plot --> seedlot)
451 if ( $trial_usage =~ m/source/ ) {
452 push @filters, "object_id IN ($sq)";
453 push @phs, $trial_name;
456 # Add sink transaction (seedlot --> plot)
457 if ( $trial_usage =~ m/sink/ ) {
458 push @filters, "subject_id IN ($sq)";
459 push @phs, $trial_name;
462 # Add filters to main query
463 $q .= " AND (" . join(" OR ", @filters) . ")";
467 my $h = $schema->storage->dbh()->prepare($q);
469 while ( my ($subject_id, $object_id) = $h->fetchrow_array() ) {
470 push @seedlot_ids, $subject_id;
471 push @seedlot_ids, $object_id;
474 # Add Seedlot IDs as filter to overall seedlot query
475 $search_criteria{'me.stock_id'} = { -in => \
@seedlot_ids };
478 my $rs = $schema->resultset("Stock::Stock")->search(
481 join => \
@seedlot_search_joins,
482 '+select'=>['project.name', 'project.project_id', 'subject.stock_id', 'subject.uniquename', 'subject.type_id', 'nd_geolocation.description', 'nd_geolocation.nd_geolocation_id'],
483 '+as'=>['breeding_program_name', 'breeding_program_id', 'source_stock_id', 'source_uniquename', 'source_type_id', 'location', 'location_id'],
484 order_by
=> {-asc
=>'project.name'},
489 my %source_types_hash = ( $type_id => 'seedlot', $accession_type_id => 'accession', $cross_type_id => 'cross' );
490 my $records_total = $rs->count();
491 if (defined($limit) && defined($offset)){
492 $rs = $rs->slice($offset, $limit);
494 my %seen_seedlot_ids;
495 while (my $row = $rs->next()) {
496 $seen_seedlot_ids{$row->stock_id}++;
498 $unique_seedlots{$row->uniquename}->{seedlot_stock_id
} = $row->stock_id;
499 $unique_seedlots{$row->uniquename}->{seedlot_stock_uniquename
} = $row->uniquename;
500 $unique_seedlots{$row->uniquename}->{seedlot_stock_description
} = $row->description;
501 $unique_seedlots{$row->uniquename}->{breeding_program_name
} = $row->get_column('breeding_program_name');
502 $unique_seedlots{$row->uniquename}->{breeding_program_id
} = $row->get_column('breeding_program_id');
503 $unique_seedlots{$row->uniquename}->{location
} = $row->get_column('location');
504 $unique_seedlots{$row->uniquename}->{location_id
} = $row->get_column('location_id');
506 push @
{$unique_seedlots{$row->uniquename}->{source_stocks
}}, [$row->get_column('source_stock_id'), $row->get_column('source_uniquename'), $source_types_hash{$row->get_column('source_type_id')}];
509 my @seen_seedlot_ids = keys %seen_seedlot_ids;
510 my $stock_lookup = CXGN
::Stock
::StockLookup
->new({ schema
=> $schema} );
511 my $owners_hash = $stock_lookup->get_owner_hash_lookup(\
@seen_seedlot_ids);
513 my $stock_search = CXGN
::Stock
::Search
->new({
515 people_schema
=>$people_schema,
516 phenome_schema
=>$phenome_schema,
517 stock_id_list
=>\
@seen_seedlot_ids,
518 stock_type_id
=>$type_id,
519 stockprop_columns_view
=>{'current_count'=>1, 'current_weight_gram'=>1, 'organization'=>1, 'location_code'=>1, 'seedlot_quality'=>1},
520 minimal_info
=>1, #for only returning stock_id and uniquenames
521 display_pedigree
=>0 #to calculate and display pedigree
523 my ($stocksearch_result, $records_stock_total) = $stock_search->search();
526 foreach (@
$stocksearch_result){
527 $stockprop_hash{$_->{stock_id
}} = $_;
531 foreach (sort keys %unique_seedlots){
532 my $owners = $owners_hash->{$unique_seedlots{$_}->{seedlot_stock_id
}};
535 push @owners_html ,'<a href="/solpeople/personal-info.pl?sp_person_id='.$_->[0].'">'.$_->[2].' '.$_->[3].'</a>';
537 my $owners_string = join ', ', @owners_html;
538 $unique_seedlots{$_}->{owners_string
} = $owners_string;
539 $unique_seedlots{$_}->{organization
} = $stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{organization
} ?
$stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{organization
} : 'NA';
540 $unique_seedlots{$_}->{box
} = $stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{location_code
} ?
$stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{location_code
} : 'NA';
541 $unique_seedlots{$_}->{seedlot_quality
} = $stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{seedlot_quality
} ?
$stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{seedlot_quality
} : '';
542 $unique_seedlots{$_}->{current_count
} = $stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{current_count
} ?
$stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{current_count
} : 'NA';
543 $unique_seedlots{$_}->{current_weight_gram
} = $stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{current_weight_gram
} ?
$stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{current_weight_gram
} : 'NA';
545 push @seedlots, $unique_seedlots{$_};
549 return (\
@seedlots, $records_total);
553 =head2 Class method: verify_seedlot_stock_lists()
555 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_stock_lists($schema, $people_schema, $phenome_schema, \@stock_names, \@seedlot_names);
556 Desc: Class method that verifies if a given list of seedlots is valid for a given list of accessions
557 Ret: success or error
558 Args: $schema, $stock_names, $seedlot_names
559 Side Effects: accesses the database
563 sub verify_seedlot_stock_lists
{
566 my $people_schema = shift;
567 my $phenome_schema = shift;
568 my $stock_names = shift;
569 my $seedlot_names = shift;
574 $error .= "No accession list selected!";
576 if (!$seedlot_names) {
577 $error .= "No seedlot list supplied!";
580 $return{error
} = $error;
584 my @stock_names = @
$stock_names;
585 my @seedlot_names = @
$seedlot_names;
586 if (scalar(@stock_names)<1){
587 $error .= "Your accession list is empty!";
589 if (scalar(@seedlot_names)<1){
590 $error .= "Your seedlot list is empty!";
593 $return{error
} = $error;
597 my $lv = CXGN
::List
::Validate
->new();
598 my @accessions_missing = @
{$lv->validate($schema,'accessions',\
@stock_names)->{'missing'}};
599 my $lv_seedlots = CXGN
::List
::Validate
->new();
600 my @seedlots_missing = @
{$lv_seedlots->validate($schema,'seedlots',\
@seedlot_names)->{'missing'}};
602 if (scalar(@accessions_missing) > 0){
603 $error .= 'The following accessions are not valid in the database, so you must add them first: '.join ',', @accessions_missing;
605 if (scalar(@seedlots_missing) > 0){
606 $error .= 'The following seedlots are not valid in the database, so you must add them first: '.join ',', @seedlots_missing;
609 $return{error
} = $error;
613 my %selected_seedlots = map {$_=>1} @seedlot_names;
614 my %selected_accessions = map {$_=>1} @stock_names;
617 my $ac = CXGN
::BreedersToolbox
::Accessions
->new({schema
=>$schema, people_schema
=>$people_schema, phenome_schema
=>$phenome_schema});
618 my $possible_seedlots = $ac->get_possible_seedlots(\
@stock_names);
619 my %allowed_seedlots;
620 while (my($key,$val) = each %$possible_seedlots){
621 foreach my $seedlot (@
$val){
622 my $seedlot_name = $seedlot->{seedlot
}->[0];
623 if (exists($selected_accessions{$key}) && exists($selected_seedlots{$seedlot_name})){
624 push @
{$seedlot_hash{$key}}, $seedlot_name;
628 #if(scalar(keys %seedlot_hash) != scalar(@stock_names)){
629 # $error .= "Error: The seedlot list you select must include seedlots for all the accessions you have selected. ";
632 $return{error
} = $error;
634 $return{success
} = 1;
635 $return{seedlot_hash
} = \
%seedlot_hash;
641 =head2 Class method: verify_seedlot_plot_compatibility()
643 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_plot_compatibility($schema, [[$seedlot_name, $plot_name]]);
644 Desc: Class method that verifies if a given list of pairs of seedlot_name and plot_name have the same underlying accession.
645 Ret: success or error
646 Args: $schema, $stock_names, $seedlot_names
647 Side Effects: accesses the database
651 sub verify_seedlot_plot_compatibility
{
654 my $pairs = shift; #arrayref of [ [seedlot_name, plot_name] ]
659 $error .= "No pair array passed!";
662 $return{error
} = $error;
667 if (scalar(@pairs)<1){
668 $error .= "Your pairs list is empty!";
671 $return{error
} = $error;
675 my $plot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plot", "stock_type")->cvterm_id();
676 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
677 my $plot_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plot_of", "stock_relationship")->cvterm_id();
678 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
680 my $seedlot_name = $_->[0];
681 my $plot_name = $_->[1];
683 #The plot is linked to one accession via 'plot_of'. That accession is then linked to many seedlots via 'collection_of'. Here we can check if the provided seedlot is one of the seedlots linked to the plot's accession.
684 my $seedlot_rs = $schema->resultset("Stock::Stock")->search({'me.uniquename'=>$plot_name, 'me.type_id'=>$plot_cvterm_id})->search_related('stock_relationship_subjects', {'stock_relationship_subjects.type_id'=>$plot_of_cvterm_id})->search_related('object')->search_related('stock_relationship_subjects', {'stock_relationship_subjects_2.type_id'=>$collection_of_cvterm_id})->search_related('object', {'object_2.uniquename'=>$seedlot_name, 'object_2.type_id'=>$seedlot_cvterm_id});
685 if (!$seedlot_rs->first){
686 $error .= "The seedlot: $seedlot_name is not linked to the same accession as the plot: $plot_name . ";
690 $return{error
} = $error;
692 $return{success
} = 1;
698 =head2 Class method: verify_seedlot_accessions_crosses()
700 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_accessions_crosses($schema, [[$seedlot_name, $accession_name]]);
701 Desc: Class method that verifies if a given list of pairs of seedlot_name and accession_name or seedlot_name and cross unique id have the same underlying accession/cross_unique_id.
702 Ret: success or error
703 Args: $schema, $stock_names, $seedlot_names
704 Side Effects: accesses the database
708 sub verify_seedlot_accessions_crosses
{
711 my $pairs = shift; #arrayref of [ [seedlot_name, accession_name] ] #note: the variable accession_name can be either accession or cross stock type
716 $error .= "No pair array passed!";
719 $return{error
} = $error;
724 if (scalar(@pairs)<1){
725 $error .= "Your pairs list is empty!";
728 $return{error
} = $error;
732 my %seen_accession_names;
734 $seen_accession_names{$_->[1]}++;
736 my $accession_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'accession', 'stock_type')->cvterm_id();
737 my $cross_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'cross', 'stock_type')->cvterm_id();
738 my $synonym_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'stock_synonym', 'stock_property')->cvterm_id();
739 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
740 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
742 my @accessions = keys %seen_accession_names;
743 my $acc_synonym_rs = $schema->resultset("Stock::Stock")->search({
744 'me.is_obsolete' => { '!=' => 't' },
745 'stockprops.value' => { -in => \
@accessions},
746 'me.type_id' => $accession_cvterm_id,
747 'stockprops.type_id' => $synonym_cvterm_id
748 },{join => 'stockprops', '+select'=>['stockprops.value'], '+as'=>['synonym']});
749 my %acc_synonyms_lookup;
750 while (my $r=$acc_synonym_rs->next){
751 $acc_synonyms_lookup{$r->get_column('synonym')}->{$r->uniquename} = $r->stock_id;
755 my $seedlot_name = $_->[0];
756 my $accession_name = $_->[1];
758 if ($acc_synonyms_lookup{$accession_name}){
759 my @accession_names = keys %{$acc_synonyms_lookup{$accession_name}};
760 if (scalar(@accession_names)>1){
761 print STDERR
"There is more than one uniquename for this synonym $accession_name. this should not happen!\n";
763 $accession_name = $accession_names[0];
766 my $seedlot_rs = $schema->resultset("Stock::Stock")->search({'me.uniquename'=>$seedlot_name, 'me.type_id'=>$seedlot_cvterm_id})->search_related('stock_relationship_objects', {'stock_relationship_objects.type_id'=>$collection_of_cvterm_id})->search_related('subject', {'subject.uniquename'=>$accession_name, 'subject.type_id'=>[$accession_cvterm_id, $cross_cvterm_id]});
767 if (!$seedlot_rs->first){
768 $error .= "The seedlot: $seedlot_name is not linked to the accession/cross_unique_id: $accession_name.";
772 $return{error
} = $error;
774 $return{success
} = 1;
780 =head2 Class method: verify_seedlot_seedlot_compatibility()
782 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_seedlot_compatibility($schema, [[$seedlot_name_1, $seedlot_name_2]]);
783 Desc: Class method that verifies if a given list of pairs of seedlot_names have the same content.
784 Ret: success or error
785 Args: $schema, $seedlot_name_1, $seedlot_name_2
786 Side Effects: accesses the database
790 sub verify_seedlot_seedlot_compatibility
{
798 $error .= "No pair array passed!";
801 $return{error
} = $error;
806 if (scalar(@pairs)<1){
807 $error .= "Your pairs list is empty!";
810 $return{error
} = $error;
814 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
815 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
817 my $seedlot_name_1 = $_->[0];
818 my $seedlot_name_2 = $_->[1];
820 my $seedlot_rs_1 = $schema->resultset("Stock::Stock")->find({'uniquename' => $seedlot_name_1,'type_id' => $seedlot_cvterm_id});
821 my $seedlot_id_1 = $seedlot_rs_1->stock_id();
822 my $seedlot_1_content = $schema->resultset("Stock::StockRelationship")->find({ object_id
=> $seedlot_id_1, type_id
=> $collection_of_cvterm_id});
823 my $content_1_id = $seedlot_1_content->subject_id();
825 my $seedlot_rs_2 = $schema->resultset("Stock::Stock")->find({'uniquename' => $seedlot_name_2,'type_id' => $seedlot_cvterm_id});
826 my $seedlot_id_2 = $seedlot_rs_2->stock_id();
827 my $seedlot_2_content = $schema->resultset("Stock::StockRelationship")->find({ object_id
=> $seedlot_id_2, type_id
=> $collection_of_cvterm_id});
828 my $content_2_id = $seedlot_2_content->subject_id();
830 if ($content_1_id ne $content_2_id){
831 $error .= "The seedlots: $seedlot_name_1 and $seedlot_name_2 have different contents.";
837 $return{error
} = $error;
839 $return{success
} = 1;
845 =head2 Class method: verify_all_seedlots_compatibility()
847 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_all_seedlots_compatibility($schema, [$new_seedlot_name, \%seedlot_names]);
848 Desc: Class method that verifies if a new seedlot name is associated with only one content.
849 Ret: success or error
850 Args: $schema, \@new_seedlot_and_associated_seedlots
851 Side Effects: accesses the database
855 sub verify_all_seedlots_compatibility
{
858 my $new_seedlot_and_associated_seedlots = shift;
862 if (!$new_seedlot_and_associated_seedlots){
863 $error .= "No seedlot names passed!";
866 $return{error
} = $error;
870 my $new_seedlot_name = $new_seedlot_and_associated_seedlots->[0];
871 my $associated_seedlots = $new_seedlot_and_associated_seedlots->[1];
872 my @seedlot_names = keys %{$associated_seedlots};
874 if (scalar(@seedlot_names)<1){
875 $error .= "No associated seedlot!";
878 $return{error
} = $error;
882 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
883 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
885 foreach my $each_seedlot(@seedlot_names){
886 my $seedlot_rs = $schema->resultset("Stock::Stock")->find({'uniquename' => $each_seedlot,'type_id' => $seedlot_cvterm_id});
887 my $seedlot_id = $seedlot_rs->stock_id();
888 my $seedlot_content = $schema->resultset("Stock::StockRelationship")->find({ object_id
=> $seedlot_id, type_id
=> $collection_of_cvterm_id});
889 my $content_id = $seedlot_content->subject_id();
890 $seen_content{$content_id}++;
893 my $content_count = keys %seen_content;
894 if ($content_count > 1) {
895 $error = "You assigned more than one content to this new seedlot name: $new_seedlot_name "
899 $return{error
} = $error;
901 $return{success
} = 1;
907 =head2 Class method: verify_accession_content_source_compatibility()
909 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_accession_content_source_compatibility($schema, [[$accession_name, $source_name]]);
910 Desc: Class method that verifies if accession of a seedlot source is the same as accession content.
911 Ret: success or error
912 Args: $schema, $accession_name, $source_name
913 Side Effects: accesses the database
917 sub verify_accession_content_source_compatibility
{
925 $error .= "No pair array passed!";
928 $return{error
} = $error;
933 if (scalar(@pairs)<1){
934 $error .= "Your pairs list is empty!";
937 $return{error
} = $error;
941 my $accession_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "accession", "stock_type")->cvterm_id();
942 my $plot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plot", "stock_type")->cvterm_id();
943 my $subplot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "subplot", "stock_type")->cvterm_id();
944 my $plant_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plant", "stock_type")->cvterm_id();
946 my $plot_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plot_of", "stock_relationship")->cvterm_id();
947 my $subplot_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "subplot_of", "stock_relationship")->cvterm_id();
948 my $plant_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plant_of", "stock_relationship")->cvterm_id();
950 foreach my $each_pair (@pairs){
954 my $accession_source_relationship_type;
955 my $source_accession_id;
956 my $accession_name = $each_pair->[0];
957 my $source_name = $each_pair->[1];
959 my $accession_rs = $schema->resultset("Stock::Stock")->find({'uniquename' => $accession_name,'type_id' => $accession_cvterm_id});
961 $accession_id = $accession_rs->stock_id();
964 my $source_rs = $schema->resultset("Stock::Stock")->find({'uniquename' => $source_name});
966 $source_id = $source_rs->stock_id();
967 $source_type_id = $source_rs->type_id();
969 if ($source_type_id eq $plot_cvterm_id) {
970 $accession_source_relationship_type = $plot_of_cvterm_id;
971 } elsif ($source_type_id eq $subplot_cvterm_id) {
972 $accession_source_relationship_type = $subplot_of_cvterm_id;
973 } elsif ($source_type_id eq $plant_cvterm_id) {
974 $accession_source_relationship_type = $plant_of_cvterm_id;
976 $error .= "The source name: $source_name is not a plot, subplot or plant stock type.";
980 if ($accession_id && $source_id && $accession_source_relationship_type) {
981 my $accession_source_relationship_rs = $schema->resultset("Stock::StockRelationship")->find({ subject_id
=> $source_id, type_id
=> $accession_source_relationship_type});
982 $source_accession_id = $accession_source_relationship_rs->object_id();
984 if ($accession_id ne $source_accession_id){
985 $error .= "The source name: $source_name is not linked to the same accession as the access content: $accession_name"."<br>";
991 $return{error
} = $error;
993 $return{success
} = 1;
999 =head2 Class method: get_content_id()
1003 sub get_content_id
{
1006 my $seedlot_id = shift;
1007 my $accession_stock_id;
1009 my @return_content_id = ();
1011 my $accession_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'accession', 'stock_type')->cvterm_id();
1012 my $cross_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'cross', 'stock_type')->cvterm_id();
1013 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
1014 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
1016 my $seedlot_content = $schema->resultset("Stock::StockRelationship")->find({ object_id
=> $seedlot_id, type_id
=> $collection_of_cvterm_id});
1017 my $content_id = $seedlot_content->subject_id();
1019 my $check_content_type = $schema->resultset("Stock::Stock")->find({'stock_id' => $content_id});
1020 my $type_id = $check_content_type->type_id();
1021 if ($type_id eq $accession_cvterm_id) {
1022 $accession_stock_id = $content_id;
1023 } elsif ($type_id eq $cross_cvterm_id) {
1024 $cross_stock_id = $content_id;
1026 @return_content_id = ($accession_stock_id, $cross_stock_id);
1028 return \
@return_content_id;
1035 $args{stock_id
} = $args{seedlot_id
};
1041 if ($self->stock_id()) {
1042 $self->seedlot_id($self->stock_id);
1043 $self->name($self->uniquename());
1044 $self->seedlot_id($self->stock_id());
1048 sub _build_transactions
{
1050 my $transactions = CXGN
::Stock
::Seedlot
::Transaction
->get_transactions_by_seedlot_id($self->schema(), $self->seedlot_id());
1051 $self->transactions($transactions);
1054 sub _store_seedlot_location
{
1056 my $nd_geolocation = $self->schema()->resultset("NaturalDiversity::NdGeolocation")->find_or_create({
1057 description
=> $self->location_code
1059 $self->nd_geolocation_id($nd_geolocation->nd_geolocation_id);
1062 sub _retrieve_location
{
1064 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1065 my $nd_geolocation_rs = $self->schema()->resultset('Stock::Stock')->search({'me.stock_id'=>$self->seedlot_id})->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$experiment_type_id})->search_related('nd_geolocation');
1066 if ($nd_geolocation_rs->count != 1){
1067 die "Seedlot does not have 1 nd_geolocation associated!\n";
1069 my $nd_geolocation_id = $nd_geolocation_rs->first()->nd_geolocation_id();
1070 my $location_code = $nd_geolocation_rs->first()->description();
1071 $self->nd_geolocation_id($nd_geolocation_id);
1072 $self->location_code($location_code);
1075 sub _retrieve_location_id
{
1077 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1078 my $nd_geolocation_rs = $self->schema()->resultset('Stock::Stock')->search({'me.stock_id'=>$self->seedlot_id})->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$experiment_type_id})->search_related('nd_geolocation');
1079 if ($nd_geolocation_rs->count != 1){
1080 die "Seedlot does not have 1 nd_geolocation associated!\n";
1082 my $nd_geolocation_id = $nd_geolocation_rs->first()->nd_geolocation_id();
1083 my $location_code = $nd_geolocation_rs->first()->description();
1084 $self->nd_geolocation_id($nd_geolocation_id);
1087 sub _retrieve_box_name
{
1089 $self->box_name($self->_retrieve_stockprop('location_code'));
1092 sub _retrieve_breeding_program
{
1094 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1095 my $project_rs = $self->schema()->resultset('Stock::Stock')->search({'me.stock_id'=>$self->seedlot_id})->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$experiment_type_id})->search_related('nd_experiment_projects')->search_related('project');
1096 if ($project_rs->count != 1){
1097 die "Seedlot does not have 1 breeding program project (".$project_rs->count.") associated!\n";
1099 my $breeding_program_id = $project_rs->first()->project_id();
1100 my $breeding_program_name = $project_rs->first()->name();
1101 $self->breeding_program_id($breeding_program_id);
1102 $self->breeding_program_name($breeding_program_name);
1105 sub _retrieve_breeding_program_id
{
1107 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1108 my $project_rs = $self->schema()->resultset('Stock::Stock')->search({'me.stock_id'=>$self->seedlot_id})->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$experiment_type_id})->search_related('nd_experiment_projects')->search_related('project');
1109 if ($project_rs->count != 1){
1110 die "Seedlot does not have 1 breeding program project (".$project_rs->count.") associated!\n";
1112 my $breeding_program_id = $project_rs->first()->project_id();
1113 my $breeding_program_name = $project_rs->first()->name();
1114 $self->breeding_program_id($breeding_program_id);
1117 sub _store_seedlot_relationships
{
1122 if ($self->accession_stock_id){
1123 $error = $self->_store_seedlot_accession();
1125 if ($self->cross_stock_id){
1126 $error = $self->_store_seedlot_cross();
1129 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1130 my $experiment = $self->schema->resultset('NaturalDiversity::NdExperiment')->create({
1131 nd_geolocation_id
=> $self->nd_geolocation_id,
1132 type_id
=> $experiment_type_id
1134 $experiment->create_related('nd_experiment_stocks', { stock_id
=> $self->seedlot_id(), type_id
=> $experiment_type_id });
1135 $experiment->create_related('nd_experiment_projects', { project_id
=> $self->breeding_program_id });
1145 sub _update_seedlot_breeding_program
{
1147 my $stock = $self->stock;
1148 my $seedlot_experiment_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'seedlot_experiment', 'experiment_type')->cvterm_id();
1149 my $nd_exp_project = $stock->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$seedlot_experiment_cvterm_id})->search_related('nd_experiment_projects');
1150 if($nd_exp_project->count != 1){
1151 die "There should be exactly one nd_experiment_project for any single seedlot!";
1153 my $nd_exp_proj = $nd_exp_project->first();
1154 $nd_exp_proj->update({project_id
=>$self->breeding_program_id});
1157 sub _update_seedlot_location
{
1159 my $stock = $self->stock;
1160 my $seedlot_experiment_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'seedlot_experiment', 'experiment_type')->cvterm_id();
1161 my $nd_exp = $stock->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$seedlot_experiment_cvterm_id});
1162 if($nd_exp->count != 1){
1163 die "There should be exactly one nd_experiment for any single seedlot!";
1165 my $nd = $nd_exp->first();
1166 $nd->update({nd_geolocation_id
=>$self->nd_geolocation_id});
1169 sub _store_seedlot_accession
{
1171 my $accession_stock_id = $self->accession_stock_id;
1173 my $organism_id = $self->schema->resultset('Stock::Stock')->find({stock_id
=> $accession_stock_id})->organism_id();
1174 if ($self->organism_id){
1175 if ($self->organism_id != $organism_id){
1176 return "Accessions must all be the same organism, so that a population can group the seed lots.\n";
1179 $self->organism_id($organism_id);
1181 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
1182 my $already_exists = $self->schema()->resultset("Stock::StockRelationship")->find({ object_id
=> $self->seedlot_id(), type_id
=> $type_id, subject_id
=>$accession_stock_id });
1184 if ($already_exists) {
1185 print STDERR
"Accession with id $accession_stock_id is already associated with seedlot id ".$self->seedlot_id()."\n";
1186 return "Accession with id $accession_stock_id is already associated with seedlot id ".$self->seedlot_id();
1188 my $row = $self->schema()->resultset("Stock::StockRelationship")->create({
1189 object_id
=> $self->seedlot_id(),
1190 subject_id
=> $accession_stock_id,
1191 type_id
=> $type_id,
1196 sub _update_content_stock_id
{
1198 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
1199 my $accession_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "accession", "stock_type")->cvterm_id();
1200 my $cross_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "cross", "stock_type")->cvterm_id();
1202 my $acc_rs = $self->stock->search_related('stock_relationship_objects', {'me.type_id'=>$type_id, 'subject.type_id'=>[$accession_type_id,$cross_type_id]}, {'join'=>'subject'});
1204 while (my $r=$acc_rs->next){
1208 if ($self->accession_stock_id){
1209 $error = $self->_store_seedlot_accession();
1211 if ($self->cross_stock_id){
1212 $error = $self->_store_seedlot_cross();
1217 sub _retrieve_accession
{
1219 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
1220 my $accession_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "accession", "stock_type")->cvterm_id();
1221 my $rs = $self->schema()->resultset("Stock::StockRelationship")->search({ 'me.type_id' => $type_id, 'me.object_id' => $self->seedlot_id(), 'subject.type_id'=>$accession_type_id }, {'join'=>'subject'});
1224 if ($rs->count == 1){
1225 $accession_id = $rs->first->subject_id;
1229 $self->accession_stock_id($accession_id);
1231 my $accession_rs = $self->schema()->resultset("Stock::Stock")->find({ stock_id
=> $accession_id });
1232 $self->accession([$accession_rs->stock_id(), $accession_rs->uniquename()]);
1236 sub _remove_accession
{
1241 sub _store_seedlot_cross
{
1243 my $cross_stock_id = $self->cross_stock_id;
1244 my $organism_id = $self->schema->resultset('Stock::Stock')->find({stock_id
=> $cross_stock_id})->organism_id();
1245 if ($self->organism_id){
1246 if ($self->organism_id != $organism_id){
1247 return "Crosses must all be the same organism to be in a seed lot.\n";
1250 $self->organism_id($organism_id);
1252 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
1253 my $already_exists = $self->schema()->resultset("Stock::StockRelationship")->find({ object_id
=> $self->seedlot_id(), type_id
=> $type_id, subject_id
=>$cross_stock_id });
1255 if ($already_exists) {
1256 print STDERR
"Cross with id $cross_stock_id is already associated with seedlot id ".$self->seedlot_id()."\n";
1257 return "Cross with id $cross_stock_id is already associated with seedlot id ".$self->seedlot_id();
1259 my $row = $self->schema()->resultset("Stock::StockRelationship")->create({
1260 object_id
=> $self->seedlot_id(),
1261 subject_id
=> $cross_stock_id,
1262 type_id
=> $type_id,
1267 sub _retrieve_cross
{
1269 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
1270 my $cross_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "cross", "stock_type")->cvterm_id();
1271 my $rs = $self->schema()->resultset("Stock::StockRelationship")->search({ 'me.type_id' => $type_id, 'me.object_id' => $self->seedlot_id(), 'subject.type_id'=>$cross_type_id }, {'join'=>'subject'});
1274 if ($rs->count == 1){
1275 $cross_id = $rs->first->subject_id;
1279 $self->cross_stock_id($cross_id);
1281 my $cross_rs = $self->schema()->resultset("Stock::Stock")->find({ stock_id
=> $cross_id });
1282 $self->cross([$cross_rs->stock_id(), $cross_rs->uniquename()]);
1287 =head2 Method current_count()
1289 Usage: my $current_count = $sl->current_count();
1290 Desc: returns the current balance of seeds in the seedlot
1293 Side Effects: retrieves transactions from db and calculates count
1300 my $transactions = $self->transactions();
1303 foreach my $t (@
$transactions) {
1304 if ($t->amount() ne 'NA'){
1305 $count += $t->amount() * $t->factor();
1308 if ($count == 0 && scalar(@
$transactions)>0){
1314 # It is convenient and also much faster to retrieve a single value for the current_count, rather than calculating it from the transactions.
1315 sub set_current_count_property
{
1317 my $current_count = $self->current_count();
1318 my $current_count_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_count', 'stock_property');
1319 my $stock = $self->stock();
1320 my $recorded_current_count = $stock->find_related('stockprops', {'me.type_id'=>$current_count_cvterm->cvterm_id});
1321 if($recorded_current_count){
1322 $recorded_current_count->update({'value'=>$current_count});
1324 $stock->create_stockprops({$current_count_cvterm->name() => $current_count});
1326 return $current_count;
1329 # It is convenient and also much faster to retrieve a single value for the current_count, rather than calculating it from the transactions.
1330 sub get_current_count_property
{
1332 my $current_count_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_count', 'stock_property');
1333 my $recorded_current_count = $self->stock()->find_related('stockprops', {'me.type_id'=>$current_count_cvterm->cvterm_id});
1334 return $recorded_current_count ?
$recorded_current_count->value() : '';
1337 =head2 _retrieve_quality
1348 sub _retrieve_quality
{
1350 $self->quality($self->_retrieve_stockprop('seedlot_quality'));
1355 =head2 Method current_weight()
1357 Usage: my $current_weight = $sl->current_weight();
1358 Desc: returns the current weight of seeds in the seedlot
1361 Side Effects: retrieves transactions from db and calculates weight
1366 sub current_weight
{
1368 my $transactions = $self->transactions();
1371 foreach my $t (@
$transactions) {
1372 if ($t->weight_gram() ne 'NA'){
1373 $weight += $t->weight_gram() * $t->factor();
1376 if ($weight == 0 && scalar(@
$transactions)>0){
1382 # It is convenient and also much faster to retrieve a single value for the current_weight, rather than calculating it from the transactions.
1383 sub set_current_weight_property
{
1385 my $current_weight = $self->current_weight();
1386 my $current_weight_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_weight_gram', 'stock_property');
1387 my $stock = $self->stock();
1388 my $recorded_current_weight = $stock->find_related('stockprops', {'me.type_id'=>$current_weight_cvterm->cvterm_id});
1389 if ($recorded_current_weight){
1390 $recorded_current_weight->update({'value'=>$current_weight});
1392 $stock->create_stockprops({$current_weight_cvterm->name() => $current_weight});
1394 return $current_weight;
1397 # It is convenient and also much faster to retrieve a single value for the current_weight, rather than calculating it from the transactions.
1398 sub get_current_weight_property
{
1400 my $current_count_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_weight_gram', 'stock_property');
1401 my $recorded_current_count = $self->stock()->find_related('stockprops', {'me.type_id'=>$current_count_cvterm->cvterm_id});
1402 return $recorded_current_count ?
$recorded_current_count->value() : '';
1406 sub _add_transaction
{
1408 my $transaction = shift;
1410 my $transactions = $self->transactions();
1411 push @
$transactions, $transaction;
1413 $self->transactions($transactions);
1418 Usage: my $seedlot_id = $sl->store();
1419 Desc: stores the current state of the object to the db. uses CXGN::Stock store as well.
1420 Ret: the seedlot id.
1422 Side Effects: accesses the db. Creates a new seedlot ID if not
1433 #Creating new seedlot
1435 $self->name($self->uniquename());
1436 $self->type('seedlot');
1437 my $id = $self->SUPER::store
();
1438 print STDERR
"Saving seedlot returned ID $id.".localtime."\n";
1439 $self->seedlot_id($id);
1440 $self->_store_seedlot_location();
1441 $error = $self->_store_seedlot_relationships();
1445 if ($self->box_name){
1446 $self->_store_stockprop('location_code', $self->box_name);
1448 if ($self->quality()) {
1449 $self->_store_stockprop('seedlot_quality', $self->quality());
1452 } else { #Updating seedlot
1454 #Attempting to update seedlot's accession. Will not proceed if seedlot has already been used in transactions.
1455 if($self->accession_stock_id){
1456 my $input_accession_id = $self->accession_stock_id;
1457 my $transactions = $self->transactions();
1458 my $stored_accession_id = $self->accession ?
$self->accession->[0] : 0;
1459 $self->accession_stock_id($input_accession_id);
1460 my $accessions_have_changed = $input_accession_id == $stored_accession_id ?
0 : 1;
1461 if ($accessions_have_changed && scalar(@
$transactions)>1){
1462 $error = "This seedlot ".$self->uniquename." has been used in transactions, so the contents (accessions) cannot be changed now!";
1463 } elsif ($accessions_have_changed && scalar(@
$transactions) <= 1) {
1464 $error = $self->_update_content_stock_id();
1465 my $update_first_transaction_id = $transactions->[0]->update_transaction_object_id($self->accession_stock_id);
1472 #Attempting to update seedlot's cross. Will not proceed if seedlot has already been used in transactions.
1473 if($self->cross_stock_id){
1474 my $input_cross_id = $self->cross_stock_id;
1475 my $transactions = $self->transactions();
1476 my $stored_cross_id = $self->cross ?
$self->cross->[0] : 0;
1477 $self->cross_stock_id($input_cross_id);
1478 my $crosses_have_changed = $input_cross_id == $stored_cross_id ?
0 : 1;
1479 if ($crosses_have_changed && scalar(@
$transactions)>1){
1480 $error = "This seedlot ".$self->uniquename." has been used in transactions, so the contents (crosses) cannot be changed now!";
1481 } elsif ($crosses_have_changed && scalar(@
$transactions) <= 1) {
1482 $error = $self->_update_content_stock_id();
1483 my $update_first_transaction_id = $transactions->[0]->update_transaction_object_id($self->cross_stock_id);
1490 my $id = $self->SUPER::store
();
1491 print STDERR
"Updating seedlot returned ID $id.".localtime."\n";
1492 $self->seedlot_id($id);
1493 if($self->breeding_program_id){
1494 $self->_update_seedlot_breeding_program();
1496 if($self->location_code){
1497 $self->_store_seedlot_location();
1498 $self->_update_seedlot_location();
1500 if($self->box_name){
1501 $self->_update_stockprop('location_code', $self->box_name);
1503 if($self->quality) {
1504 $self->_update_stockprop('seedlot_quality', $self->quality());
1509 my $transaction_error;
1511 $self->schema->txn_do($coderef);
1513 print STDERR
"Transaction Error: $_\n";
1514 $transaction_error = $_;
1516 if ($transaction_error){
1517 return { error
=>$transaction_error };
1519 return { success
=>1, seedlot_id
=>$self->stock_id() };
1525 Usage: my $error_message = $sl->delete();
1526 Desc: delete the seedlot from the database. only possible to delete a seedlot that has not been used in any transactions other than the transaction that initiated it.
1527 Ret: any error message. undef if no errors
1529 Side Effects: accesses the db. Deletes seedlot
1537 my $transactions = $self->transactions();
1538 my $name = $self->name();
1539 if (scalar(@
$transactions)>1){
1540 $error = "Seedlot '$name' has been used in transactions and so cannot be deleted!";
1542 my $stock = $self->stock();
1543 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1544 my $nd_experiment_rs = $self->schema()->resultset('Stock::Stock')->search({'me.stock_id'=>$self->seedlot_id})->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$experiment_type_id});
1545 if ($nd_experiment_rs->count != 1){
1546 $error = "Seedlot '$name' should have only one associated nd_experiment!";
1548 my $nd_experiment = $nd_experiment_rs->first();
1549 $nd_experiment->delete();
1550 my $stock_owner_rs = $self->phenome_schema->resultset("StockOwner")->find({stock_id
=>$self->stock_id});
1551 if ($stock_owner_rs){
1552 $stock_owner_rs->delete();
1562 ### CLASS FUNCTION DELETE_USING_LIST
1564 sub delete_verify_using_list
{
1567 my $phenome_schema = shift;
1568 my $list_id = shift;
1570 my $list = CXGN
::List
->new( { dbh
=> $schema->storage->dbh(), list_id
=> $list_id } );
1571 my $type_row = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", 'stock_type');
1575 $type_id = $type_row->cvterm_id();
1578 print STDERR
"TYPE ID = $type_id\n";
1580 my $elements = $list->elements();
1585 print STDERR
"ELEMENTS ".join(",", @
$elements);
1586 my $delete_count = 0;
1587 foreach my $ele (@
$elements) {
1588 print STDERR
"start deletion for seedlot ".Dumper
($ele)."...\n";
1589 my $rs = $schema->resultset("Stock::Stock")->search( { uniquename
=> $ele, type_id
=> $type_id });
1590 if ($rs->count() == 0) {
1591 print STDERR
"No such seedlot $ele\n";
1592 push @errors, [ $ele, "No seedlot named '$ele' could be found in the database" ];
1595 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot_experiment", "experiment_type")->cvterm_id();
1596 my $seedlot_id = $rs->next()->stock_id();
1597 print STDERR
"SEEDLOT ID: $seedlot_id\n";
1598 my $nd_experiment_rs = $schema->resultset('Stock::Stock')->search({'me.stock_id'=> $seedlot_id})->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$experiment_type_id});
1599 if ($nd_experiment_rs->count != 1){
1600 my $error = "Seedlot '$ele' should have only one associated nd_experiment!";
1601 push @errors, [ $ele, $error];
1609 return ( \
@ok, \
@errors );
1614 sub delete_using_list
{
1617 my $phenome_schema = shift;
1618 my $list_id = shift;
1620 my $list = CXGN
::List
->new( { dbh
=> $schema->storage->dbh(), list_id
=> $list_id } );
1621 my $type_row = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", 'stock_type');
1625 $type_id = $type_row->cvterm_id();
1628 print STDERR
"TYPE ID = $type_id\n";
1630 my $elements = $list->elements();
1633 my $delete_count = 0;
1634 foreach my $ele (@
$elements) {
1635 print STDERR
"start deletion for seedlot ".Dumper
($ele)."...\n";
1636 my $rs = $schema->resultset("Stock::Stock")->search( { uniquename
=> $ele, type_id
=> $type_id });
1637 if ($rs->count() == 0) {
1638 print STDERR
"No such seedlot $ele\n";
1639 push @errors, "No seedlot named '$ele' could be found in the database";
1642 my $seedlot = CXGN
::Stock
::Seedlot
->new( schema
=> $schema, phenome_schema
=> $phenome_schema, seedlot_id
=> $rs->next()->stock_id());
1643 my $error = $seedlot->delete();
1645 print STDERR
"Error during seedlot deletion: $error\n";
1646 push @errors, $error;
1653 return ( scalar(@
$elements), $delete_count, \
@errors );
1658 # SEEDLOT MAINTENANCE EVENT FUNCTIONS
1663 Usage: my @events = $sl->get_events();
1664 Desc: get all of seedlot maintenance events associated with the seedlot
1665 Args: page = (optional) the page number of results to return
1666 pageSize = (optional) the number of results per page to return
1667 Ret: a hash with the results metadata and the matching seedlot events:
1668 - page: current page number
1669 - maxPage: the number of the last page
1670 - pageSize: (max) number of results per page
1671 - total: total number of results
1672 - results: an arrayref of hases of the seedlot's stored events, with the following keys:
1673 - stock_id: the unique id of the seedlot
1674 - uniquename: the unique name of the seedlot
1675 - stockprop_id: the unique id of the maintenance event
1676 - cvterm_id: id of seedlot maintenance event ontology term
1677 - cvterm_name: name of seedlot maintenance event ontology term
1678 - value: value of the seedlot maintenance event
1679 - notes: additional notes/comments about the event
1680 - operator: username of the person creating the event
1681 - timestamp: timestamp string of when the event was created ('YYYY-MM-DD HH:MM:SS' format)
1688 my $pageSize = shift;
1689 my $schema = $self->schema();
1690 my $seedlot_name = $self->uniquename();
1691 my $m = CXGN
::Stock
::Seedlot
::Maintenance
->new({ bcs_schema
=> $schema });
1693 return $m->filter_events({ names
=> [$seedlot_name] }, $page, $pageSize);
1699 Usage: my $event = $sl->get_event($id);
1700 Desc: get the specified seedlot maintenance event associated with the seedlot
1701 Args: id = stockprop_id of maintenance event
1702 Ret: a hashref of the seedlot maintenance event, with the following keys:
1703 - stock_id: the unique id of the seedlot
1704 - uniquename: the unique name of the seedlot
1705 - stockprop_id: the unique id of the maintenance event
1706 - cvterm_id: id of seedlot maintenance event ontology term
1707 - cvterm_name: name of seedlot maintenance event ontology term
1708 - value: value of the seedlot maintenance event
1709 - notes: additional notes/comments about the event
1710 - operator: username of the person creating the event
1711 - timestamp: timestamp string of when the event was created ('YYYY-MM-DD HH:MM:SS' format)
1717 my $event_id = shift;
1718 my $schema = $self->schema();
1719 my $seedlot_name = $self->uniquename();
1720 my $m = CXGN
::Stock
::Seedlot
::Maintenance
->new({ bcs_schema
=> $schema });
1722 my $events = $m->filter_events({ names
=> [$seedlot_name], events
=> [$event_id] });
1723 return $events->{'results'}->[0];
1727 =head2 store_events()
1729 Usage: my @events = ({ cvterm_id => $cvterm_id, value => $value, notes => $notes, operator => $operator, timestamp => $timestamp }, ... );
1730 my $stored_events = $sl->store_events(\@events);
1731 Desc: store one or more seedlot maintenance events in the database as a JSON stockprop associated with the seedlot's stock entry.
1732 this function uses the CXGN::Stock::Seedlot::Maintenance class to store the JSON stockprop
1733 Args: $events = arrayref of hashes of the event properties, with the following keys:
1734 - cvterm_id: id of seedlot maintenance event ontology term
1735 - value: value of the seedlot maintenance event
1736 - notes: (optional) additional notes/comments about the event
1737 - operator: username of the person creating the event
1738 - timestamp: timestamp string of when the event was created ('YYYY-MM-DD HH:MM:SS' format)
1739 Ret: an arrayref of hashes of the processed/stored events (includes stockprop_id), with the following keys:
1740 - stockprop_id: the unique id of the maintenance event
1741 - stock_id: the unique id of the seedlot
1742 - cvterm_id: id of seedlot maintenance event ontology term
1743 - cvterm_name: name of seedlot maintenance event ontology term
1744 - value: value of the seedlot maintenance event
1745 - notes: additional notes/comments about the event
1746 - operator: username of the person creating the event
1747 - timestamp: timestamp string of when the event was created ('YYYY-MM-DD HH:MM:SS' format)
1748 the function will die on a caught error
1755 my $schema = $self->schema();
1756 my $seedlot_id = $self->seedlot_id();
1758 # Process the passed events
1759 my @processed_events = ();
1760 foreach my $event (@
$events) {
1761 my $cvterm_id = $event->{cvterm_id
};
1762 my $value = $event->{value
};
1763 my $notes = $event->{notes
};
1764 my $operator = $event->{operator
};
1765 my $timestamp = $event->{timestamp
};
1767 # Check for required parameters
1768 if ( !defined $cvterm_id || $cvterm_id eq '' ) {
1769 die "cvterm_id is required!";
1771 if ( !defined $value || $value eq '' ) {
1772 die "value is required!";
1774 if ( !defined $operator || $operator eq '' ) {
1775 die "operator is required!";
1777 if ( !defined $timestamp || $timestamp eq '' ) {
1778 die "timestamp is required!";
1780 if ( $timestamp !~ /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/ ) {
1781 die "timestamp not valid format [YYYY-MM-DD HH:MM:SS]!";
1784 # Find matching cvterm by id
1785 my $cvterm_rs = $schema->resultset("Cv::Cvterm")->search({ cvterm_id
=> $cvterm_id })->first();
1786 if ( !defined $cvterm_rs ) {
1787 die "cvterm_id $cvterm_id not found!";
1789 my $cvterm_name = $cvterm_rs->name();
1791 # Save processed event
1792 my %processed_event = (
1793 cvterm_id
=> $cvterm_id,
1794 cvterm_name
=> $cvterm_name,
1797 operator
=> $operator,
1798 timestamp
=> $timestamp
1800 push(@processed_events, \
%processed_event);
1803 # Store the processed events
1804 foreach my $processed_event (@processed_events) {
1805 my $event_obj = CXGN
::Stock
::Seedlot
::Maintenance
->new({ bcs_schema
=> $schema, parent_id
=> $seedlot_id });
1806 $event_obj->cvterm_id($processed_event->{cvterm_id
});
1807 $event_obj->cvterm_name($processed_event->{cvterm_name
});
1808 $event_obj->value($processed_event->{value
});
1809 $event_obj->notes($processed_event->{notes
});
1810 $event_obj->operator($processed_event->{operator
});
1811 $event_obj->timestamp($processed_event->{timestamp
});
1812 my $stockprop_id = $event_obj->store_by_rank();
1813 $processed_event->{stockprop_id
} = $stockprop_id;
1814 $processed_event->{stock_id
} = $seedlot_id;
1817 # Return the processed events
1818 return(\
@processed_events);
1822 =head2 remove_event()
1824 Usage: $sl->remove_event($id)
1825 Desc: delete the specified seedlot maintenance event from the database
1826 Args: $id = stockprop_id of the seedlot maintenance event
1833 my $event_id = shift;
1834 my $seedlot_id = $self->seedlot_id();
1835 my $schema = $self->schema();
1836 my $m = CXGN
::Stock
::Seedlot
::Maintenance
->new({ bcs_schema
=> $schema, parent_id
=> $seedlot_id, prop_id
=> $event_id });
1842 =head2 get_seedlot_species()
1844 Usage: $seedlot->get_seedlot_species($id)
1845 Desc: retrieve species of seedlot content
1850 sub get_seedlot_species
{
1852 my $schema = $self->schema();
1853 my $seedlot_id = $self->seedlot_id();
1855 my $collection_of_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, 'collection_of', 'stock_relationship')->cvterm_id();
1857 my $q = "SELECT organism.species FROM stock_relationship
1858 JOIN stock ON (stock_relationship.subject_id = stock.stock_id) AND stock_relationship.type_id = ?
1859 JOIN organism ON (stock.organism_id = organism.organism_id)
1860 WHERE stock_relationship.object_id = ?";
1862 my $h = $schema->storage->dbh()->prepare($q);
1863 $h->execute($collection_of_type_id, $seedlot_id);
1866 while(my($species) = $h->fetchrow_array()){
1867 push @data, [$species];
1870 my $species_info = $data[0][0];
1872 return $species_info
1880 __PACKAGE__
->meta->make_immutable;