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"),
77 ------------------------------------------------------------------------------
79 To Retrieve a single seedlot do:
81 my $seedlot = CXGN::Stock::Seedlot->new(
83 seedlot_id => $seedlot_id,
85 # You can access all seedlot accessors from here such as (you can also access all CXGN::Stock accessors):
86 my $uniquename => $seedlot->uniquename(),
87 my $seedlot_id => $seedlot->seedlot_id(),
88 my $current_count => $seedlot->current_count(),
89 my $location_code => $seedlot->location_code(),
90 my $breeding_program => $seedlot->breeding_program_name(),
91 my $organization_name => $seedlot->organization_name(),
92 my $population_name => $seedlot->population_name(),
93 my $accession => $seedlot->accession(),
94 my $cross => $seedlot->cross(),
96 ------------------------------------------------------------------------------
98 Seed transactions can be added using CXGN::Stock::Seedlot::Transaction.
102 Lukas Mueller <lam87@cornell.edu>
103 Nick MOrales <nm529@cornell.edu>
105 =head1 ACCESSORS & METHODS
109 package CXGN
::Stock
::Seedlot
;
113 extends
'CXGN::Stock';
116 use CXGN
::Stock
::Seedlot
::Transaction
;
117 use CXGN
::BreedersToolbox
::Projects
;
118 use SGN
::Model
::Cvterm
;
119 use CXGN
::List
::Validate
;
121 use CXGN
::Stock
::StockLookup
;
122 use CXGN
::Stock
::Search
;
124 =head2 Accessor seedlot_id()
126 the database id of the seedlot. Is equivalent to stock_id.
130 has
'seedlot_id' => (
135 =head2 Accessor location_code()
137 A string specifiying where the seedlot is stored. On the backend,
138 this is stored the nd_geolocation description field.
142 has
'location_code' => (
146 builder
=> '_retrieve_location',
149 has
'nd_geolocation_id' => (
154 =head2 Accessor box_name()
156 A string specifiying box where the seedlot is stored. On the backend,
157 this is stored as a stockprop.
165 builder
=> '_retrieve_box_name',
168 =head2 Accessor cross()
170 The crosses this seedlot is a "collection_of". Returns an arrayref of [$cross_stock_id, $cross_uniquename]
171 # for setter, use cross_stock_id
176 isa
=> 'ArrayRef|Undef',
179 builder
=> '_retrieve_cross',
182 has
'cross_stock_id' => (
187 =head2 Accessor accessions()
189 The accessions this seedlot is a "collection_of". Returns an arrayref of [$accession_stock_id, $accession_uniquename]
190 # for setter, use accession_stock_id
195 isa
=> 'ArrayRef|Undef',
198 builder
=> '_retrieve_accession',
201 has
'accession_stock_id' => (
206 =head2 Accessor transactions()
208 a ArrayRef of CXGN::Stock::Seedlot::Transaction objects
212 has
'transactions' => (
216 builder
=> '_build_transactions',
219 =head2 Accessor breeding_program
221 The breeding program this seedlot is from. Useful for tracking movement of seedlots across breeding programs
222 Use breeding_program_id as setter (to save and update seedlots).
226 has
'breeding_program_name' => (
230 builder
=> '_retrieve_breeding_program',
233 has
'breeding_program_id' => (
239 after
'stock_id' => sub {
242 return $self->seedlot_id($id);
246 =head2 Class method: list_seedlots()
248 Usage: my $seedlots = CXGN::Stock::Seedlot->list_seedlots($schema);
249 Desc: Class method that returns information on all seedlots
250 available in the system
251 Ret: ArrayRef of [ seedlot_id, seedlot name, location_code]
252 Args: $schema - Bio::Chado::Schema object
253 Side Effects: accesses the database
260 my $people_schema = shift;
261 my $phenome_schema = shift;
264 my $seedlot_name = shift;
265 my $breeding_program = shift;
266 my $location = shift;
267 my $minimum_count = shift;
268 my $contents_accession = shift; #arrayref of uniquenames
269 my $contents_cross = shift; #arrayref of uniquenames
270 my $exact_match_uniquenames = shift;
272 print STDERR
"SEARCHING SEEDLOTS\n";
275 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
276 my $cross_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "cross", "stock_type")->cvterm_id();
277 my $accession_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "accession", "stock_type")->cvterm_id();
278 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
279 my $current_count_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "current_count", "stock_property")->cvterm_id();
280 my $current_weight_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "current_weight_gram", "stock_property")->cvterm_id();
281 my $experiment_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot_experiment", "experiment_type")->cvterm_id();
284 $search_criteria{'me.type_id'} = $type_id;
285 $search_criteria{'stock_relationship_objects.type_id'} = $collection_of_cvterm_id;
287 $search_criteria{'me.uniquename'} = { 'ilike' => '%'.$seedlot_name.'%' };
289 if ($breeding_program) {
290 $search_criteria{'project.name'} = { 'ilike' => '%'.$breeding_program.'%' };
293 $search_criteria{'nd_geolocation.description'} = { 'ilike' => '%'.$location.'%' };
295 if ($contents_accession && scalar(@
$contents_accession)>0) {
296 $search_criteria{'subject.type_id'} = $accession_type_id;
297 if ($exact_match_uniquenames){
298 $search_criteria{'subject.uniquename'} = { -in => $contents_accession };
300 foreach (@
$contents_accession){
301 push @
{$search_criteria{'subject.uniquename'}}, { 'ilike' => '%'.$_.'%' };
305 if ($contents_cross && scalar(@
$contents_cross)>0) {
306 $search_criteria{'subject.type_id'} = $cross_type_id;
307 if ($exact_match_uniquenames){
308 $search_criteria{'subject.uniquename'} = { -in => $contents_cross };
310 foreach (@
$contents_cross){
311 push @
{$search_criteria{'subject.uniquename'}}, { 'ilike' => '%'.$_.'%' };
315 if ($minimum_count) {
316 $search_criteria{'stockprops.value' } = { '>' => $minimum_count };
318 #print STDERR Dumper \%search_criteria;
319 #$schema->storage->debug(1);
320 my $rs = $schema->resultset("Stock::Stock")->search(
324 {'nd_experiment_stocks' => {'nd_experiment' => [ {'nd_experiment_projects' => 'project' }, 'nd_geolocation' ] }},
325 {'stock_relationship_objects' => 'subject'}
327 '+select'=>['project.name', 'project.project_id', 'subject.stock_id', 'subject.uniquename', 'subject.type_id', 'nd_geolocation.description', 'nd_geolocation.nd_geolocation_id'],
328 '+as'=>['breeding_program_name', 'breeding_program_id', 'source_stock_id', 'source_uniquename', 'source_type_id', 'location', 'location_id'],
329 order_by
=> {-asc
=>'project.name'},
334 my %source_types_hash = ( $type_id => 'seedlot', $accession_type_id => 'accession', $cross_type_id => 'cross' );
335 my $records_total = $rs->count();
336 if (defined($limit) && defined($offset)){
337 $rs = $rs->slice($offset, $limit);
339 my %seen_seedlot_ids;
340 while (my $row = $rs->next()) {
341 $seen_seedlot_ids{$row->stock_id}++;
342 $unique_seedlots{$row->uniquename}->{seedlot_stock_id
} = $row->stock_id;
343 $unique_seedlots{$row->uniquename}->{seedlot_stock_uniquename
} = $row->uniquename;
344 $unique_seedlots{$row->uniquename}->{seedlot_stock_description
} = $row->description;
345 $unique_seedlots{$row->uniquename}->{breeding_program_name
} = $row->get_column('breeding_program_name');
346 $unique_seedlots{$row->uniquename}->{breeding_program_id
} = $row->get_column('breeding_program_id');
347 $unique_seedlots{$row->uniquename}->{location
} = $row->get_column('location');
348 $unique_seedlots{$row->uniquename}->{location_id
} = $row->get_column('location_id');
349 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')}];
351 #print STDERR Dumper \%unique_seedlots;
353 my @seen_seedlot_ids = keys %seen_seedlot_ids;
354 my $stock_lookup = CXGN
::Stock
::StockLookup
->new({ schema
=> $schema} );
355 my $owners_hash = $stock_lookup->get_owner_hash_lookup(\
@seen_seedlot_ids);
357 my $stock_search = CXGN
::Stock
::Search
->new({
359 people_schema
=>$people_schema,
360 phenome_schema
=>$phenome_schema,
361 stock_id_list
=>\
@seen_seedlot_ids,
362 stock_type_id
=>$type_id,
363 stockprop_columns_view
=>{'current_count'=>1, 'current_weight_gram'=>1, 'organization'=>1},
364 minimal_info
=>1, #for only returning stock_id and uniquenames
365 display_pedigree
=>0 #to calculate and display pedigree
367 my ($stocksearch_result, $records_stock_total) = $stock_search->search();
368 #print STDERR Dumper $result;
370 foreach (@
$stocksearch_result){
371 $stockprop_hash{$_->{stock_id
}} = $_;
375 foreach (sort keys %unique_seedlots){
376 my $owners = $owners_hash->{$unique_seedlots{$_}->{seedlot_stock_id
}};
379 push @owners_html ,'<a href="/solpeople/personal-info.pl?sp_person_id='.$_->[0].'">'.$_->[2].' '.$_->[3].'</a>';
381 my $owners_string = join ', ', @owners_html;
382 $unique_seedlots{$_}->{owners_string
} = $owners_string;
383 $unique_seedlots{$_}->{current_count
} = $stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{current_count
} ?
$stockprop_hash{$unique_seedlots{$_}->{seedlot_stock_id
}}->{current_count
} : 'NA';
384 $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';
385 push @seedlots, $unique_seedlots{$_};
387 #print STDERR Dumper \@seedlots;
388 return (\
@seedlots, $records_total);
392 =head2 Class method: verify_seedlot_stock_lists()
394 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_stock_lists($schema, $people_schema, $phenome_schema, \@stock_names, \@seedlot_names);
395 Desc: Class method that verifies if a given list of seedlots is valid for a given list of accessions
396 Ret: success or error
397 Args: $schema, $stock_names, $seedlot_names
398 Side Effects: accesses the database
402 sub verify_seedlot_stock_lists
{
405 my $people_schema = shift;
406 my $phenome_schema = shift;
407 my $stock_names = shift;
408 my $seedlot_names = shift;
413 $error .= "No accession list selected!";
415 if (!$seedlot_names) {
416 $error .= "No seedlot list supplied!";
419 $return{error
} = $error;
423 my @stock_names = @
$stock_names;
424 my @seedlot_names = @
$seedlot_names;
425 if (scalar(@stock_names)<1){
426 $error .= "Your accession list is empty!";
428 if (scalar(@seedlot_names)<1){
429 $error .= "Your seedlot list is empty!";
432 $return{error
} = $error;
436 my $lv = CXGN
::List
::Validate
->new();
437 my @accessions_missing = @
{$lv->validate($schema,'accessions',\
@stock_names)->{'missing'}};
438 my $lv_seedlots = CXGN
::List
::Validate
->new();
439 my @seedlots_missing = @
{$lv_seedlots->validate($schema,'seedlots',\
@seedlot_names)->{'missing'}};
441 if (scalar(@accessions_missing) > 0){
442 $error .= 'The following accessions are not valid in the database, so you must add them first: '.join ',', @accessions_missing;
444 if (scalar(@seedlots_missing) > 0){
445 $error .= 'The following seedlots are not valid in the database, so you must add them first: '.join ',', @seedlots_missing;
448 $return{error
} = $error;
452 my %selected_seedlots = map {$_=>1} @seedlot_names;
453 my %selected_accessions = map {$_=>1} @stock_names;
456 my $ac = CXGN
::BreedersToolbox
::Accessions
->new({schema
=>$schema, people_schema
=>$people_schema, phenome_schema
=>$phenome_schema});
457 my $possible_seedlots = $ac->get_possible_seedlots(\
@stock_names);
458 my %allowed_seedlots;
459 while (my($key,$val) = each %$possible_seedlots){
460 foreach my $seedlot (@
$val){
461 my $seedlot_name = $seedlot->{seedlot
}->[0];
462 if (exists($selected_accessions{$key}) && exists($selected_seedlots{$seedlot_name})){
463 push @
{$seedlot_hash{$key}}, $seedlot_name;
467 #if(scalar(keys %seedlot_hash) != scalar(@stock_names)){
468 # $error .= "Error: The seedlot list you select must include seedlots for all the accessions you have selected. ";
471 $return{error
} = $error;
473 $return{success
} = 1;
474 $return{seedlot_hash
} = \
%seedlot_hash;
480 =head2 Class method: verify_seedlot_plot_compatibility()
482 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_plot_compatibility($schema, [[$seedlot_name, $plot_name]]);
483 Desc: Class method that verifies if a given list of pairs of seedlot_name and plot_name have the same underlying accession.
484 Ret: success or error
485 Args: $schema, $stock_names, $seedlot_names
486 Side Effects: accesses the database
490 sub verify_seedlot_plot_compatibility
{
493 my $pairs = shift; #arrayref of [ [seedlot_name, plot_name] ]
498 $error .= "No pair array passed!";
501 $return{error
} = $error;
506 if (scalar(@pairs)<1){
507 $error .= "Your pairs list is empty!";
510 $return{error
} = $error;
514 my $plot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plot", "stock_type")->cvterm_id();
515 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
516 my $plot_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "plot_of", "stock_relationship")->cvterm_id();
517 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
519 my $seedlot_name = $_->[0];
520 my $plot_name = $_->[1];
522 #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.
523 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});
524 if (!$seedlot_rs->first){
525 $error .= "The seedlot: $seedlot_name is not linked to the same accession as the plot: $plot_name . ";
529 $return{error
} = $error;
531 $return{success
} = 1;
537 =head2 Class method: verify_seedlot_accessions()
539 Usage: my $seedlots = CXGN::Stock::Seedlot->verify_seedlot_accessions($schema, [[$seedlot_name, $accession_name]]);
540 Desc: Class method that verifies if a given list of pairs of seedlot_name and accession_name have the same underlying accession.
541 Ret: success or error
542 Args: $schema, $stock_names, $seedlot_names
543 Side Effects: accesses the database
547 sub verify_seedlot_accessions
{
550 my $pairs = shift; #arrayref of [ [seedlot_name, accession_name] ]
555 $error .= "No pair array passed!";
558 $return{error
} = $error;
563 if (scalar(@pairs)<1){
564 $error .= "Your pairs list is empty!";
567 $return{error
} = $error;
571 my $accession_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "accession", "stock_type")->cvterm_id();
572 my $seedlot_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "seedlot", "stock_type")->cvterm_id();
573 my $collection_of_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($schema, "collection_of", "stock_relationship")->cvterm_id();
575 my $seedlot_name = $_->[0];
576 my $accession_name = $_->[1];
578 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});
579 if (!$seedlot_rs->first){
580 $error .= "The seedlot: $seedlot_name is not linked to the accession: $accession_name.";
584 $return{error
} = $error;
586 $return{success
} = 1;
594 $args{stock_id
} = $args{seedlot_id
};
600 if ($self->stock_id()) {
601 $self->seedlot_id($self->stock_id);
602 $self->name($self->uniquename());
603 $self->seedlot_id($self->stock_id());
605 #print STDERR Dumper $self->seedlot_id;
608 sub _build_transactions
{
610 my $transactions = CXGN
::Stock
::Seedlot
::Transaction
->get_transactions_by_seedlot_id($self->schema(), $self->seedlot_id());
611 #print STDERR Dumper($transactions);
612 $self->transactions($transactions);
615 sub _store_seedlot_location
{
617 my $nd_geolocation = $self->schema()->resultset("NaturalDiversity::NdGeolocation")->find_or_create({
618 description
=> $self->location_code
620 $self->nd_geolocation_id($nd_geolocation->nd_geolocation_id);
623 sub _retrieve_location
{
625 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
626 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');
627 if ($nd_geolocation_rs->count != 1){
628 die "Seedlot does not have 1 nd_geolocation associated!\n";
630 my $nd_geolocation_id = $nd_geolocation_rs->first()->nd_geolocation_id();
631 my $location_code = $nd_geolocation_rs->first()->description();
632 $self->nd_geolocation_id($nd_geolocation_id);
633 $self->location_code($location_code);
636 sub _retrieve_box_name
{
638 $self->box_name($self->_retrieve_stockprop('location_code'));
641 sub _retrieve_breeding_program
{
643 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
644 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');
645 if ($project_rs->count != 1){
646 die "Seedlot does not have 1 breeding program project (".$project_rs->count.") associated!\n";
648 my $breeding_program_id = $project_rs->first()->project_id();
649 my $breeding_program_name = $project_rs->first()->name();
650 $self->breeding_program_id($breeding_program_id);
651 $self->breeding_program_name($breeding_program_name);
654 sub _store_seedlot_relationships
{
659 if ($self->accession_stock_id){
660 $error = $self->_store_seedlot_accession();
662 if ($self->cross_stock_id){
663 $error = $self->_store_seedlot_cross();
666 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
667 my $experiment = $self->schema->resultset('NaturalDiversity::NdExperiment')->create({
668 nd_geolocation_id
=> $self->nd_geolocation_id,
669 type_id
=> $experiment_type_id
671 $experiment->create_related('nd_experiment_stocks', { stock_id
=> $self->seedlot_id(), type_id
=> $experiment_type_id });
672 $experiment->create_related('nd_experiment_projects', { project_id
=> $self->breeding_program_id });
682 sub _update_seedlot_breeding_program
{
684 my $stock = $self->stock;
685 my $seedlot_experiment_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'seedlot_experiment', 'experiment_type')->cvterm_id();
686 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');
687 if($nd_exp_project->count != 1){
688 die "There should be exactly one nd_experiment_project for any single seedlot!";
690 my $nd_exp_proj = $nd_exp_project->first();
691 $nd_exp_proj->update({project_id
=>$self->breeding_program_id});
694 sub _update_seedlot_location
{
696 my $stock = $self->stock;
697 my $seedlot_experiment_cvterm_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'seedlot_experiment', 'experiment_type')->cvterm_id();
698 my $nd_exp = $stock->search_related('nd_experiment_stocks')->search_related('nd_experiment', {'nd_experiment.type_id'=>$seedlot_experiment_cvterm_id});
699 if($nd_exp->count != 1){
700 die "There should be exactly one nd_experiment for any single seedlot!";
702 my $nd = $nd_exp->first();
703 $nd->update({nd_geolocation_id
=>$self->nd_geolocation_id});
706 sub _store_seedlot_accession
{
708 my $accession_stock_id = $self->accession_stock_id;
710 my $organism_id = $self->schema->resultset('Stock::Stock')->find({stock_id
=> $accession_stock_id})->organism_id();
711 if ($self->organism_id){
712 if ($self->organism_id != $organism_id){
713 return "Accessions must all be the same organism, so that a population can group the seed lots.\n";
716 $self->organism_id($organism_id);
718 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
719 my $already_exists = $self->schema()->resultset("Stock::StockRelationship")->find({ object_id
=> $self->seedlot_id(), type_id
=> $type_id, subject_id
=>$accession_stock_id });
721 if ($already_exists) {
722 print STDERR
"Accession with id $accession_stock_id is already associated with seedlot id ".$self->seedlot_id()."\n";
723 return "Accession with id $accession_stock_id is already associated with seedlot id ".$self->seedlot_id();
725 my $row = $self->schema()->resultset("Stock::StockRelationship")->create({
726 object_id
=> $self->seedlot_id(),
727 subject_id
=> $accession_stock_id,
733 sub _update_content_stock_id
{
735 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
736 my $accession_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "accession", "stock_type")->cvterm_id();
737 my $cross_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "cross", "stock_type")->cvterm_id();
738 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'});
739 while (my $r=$acc_rs->next){
742 my $error = $self->_store_seedlot_accession();
743 $error = $self->_store_seedlot_cross();
747 sub _retrieve_accession
{
749 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
750 my $accession_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "accession", "stock_type")->cvterm_id();
751 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'});
754 if ($rs->count == 1){
755 $accession_id = $rs->first->subject_id;
759 $self->accession_stock_id($accession_id);
761 my $accession_rs = $self->schema()->resultset("Stock::Stock")->find({ stock_id
=> $accession_id });
762 $self->accession([$accession_rs->stock_id(), $accession_rs->uniquename()]);
766 sub _remove_accession
{
771 sub _store_seedlot_cross
{
773 my $cross_stock_id = $self->cross_stock_id;
774 my $organism_id = $self->schema->resultset('Stock::Stock')->find({stock_id
=> $cross_stock_id})->organism_id();
775 if ($self->organism_id){
776 if ($self->organism_id != $organism_id){
777 return "Crosses must all be the same organism to be in a seed lot.\n";
780 $self->organism_id($organism_id);
782 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
783 my $already_exists = $self->schema()->resultset("Stock::StockRelationship")->find({ object_id
=> $self->seedlot_id(), type_id
=> $type_id, subject_id
=>$cross_stock_id });
785 if ($already_exists) {
786 print STDERR
"Cross with id $cross_stock_id is already associated with seedlot id ".$self->seedlot_id()."\n";
787 return "Cross with id $cross_stock_id is already associated with seedlot id ".$self->seedlot_id();
789 my $row = $self->schema()->resultset("Stock::StockRelationship")->create({
790 object_id
=> $self->seedlot_id(),
791 subject_id
=> $cross_stock_id,
797 sub _retrieve_cross
{
799 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "collection_of", "stock_relationship")->cvterm_id();
800 my $cross_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "cross", "stock_type")->cvterm_id();
801 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'});
804 if ($rs->count == 1){
805 $cross_id = $rs->first->subject_id;
809 $self->cross_stock_id($cross_id);
811 my $cross_rs = $self->schema()->resultset("Stock::Stock")->find({ stock_id
=> $cross_id });
812 $self->cross([$cross_rs->stock_id(), $cross_rs->uniquename()]);
817 =head2 Method current_count()
819 Usage: my $current_count = $sl->current_count();
820 Desc: returns the current balance of seeds in the seedlot
823 Side Effects: retrieves transactions from db and calculates count
830 my $transactions = $self->transactions();
833 foreach my $t (@
$transactions) {
834 if ($t->amount() ne 'NA'){
835 $count += $t->amount() * $t->factor();
838 if ($count == 0 && scalar(@
$transactions)>0){
844 # It is convenient and also much faster to retrieve a single value for the current_count, rather than calculating it from the transactions.
845 sub set_current_count_property
{
847 my $current_count = $self->current_count();
848 my $current_count_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_count', 'stock_property');
849 my $stock = $self->stock();
850 my $recorded_current_count = $stock->find_related('stockprops', {'me.type_id'=>$current_count_cvterm->cvterm_id});
851 if($recorded_current_count){
852 $recorded_current_count->update({'value'=>$current_count});
854 $stock->create_stockprops({$current_count_cvterm->name() => $current_count});
856 return $current_count;
859 # It is convenient and also much faster to retrieve a single value for the current_count, rather than calculating it from the transactions.
860 sub get_current_count_property
{
862 my $current_count_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_count', 'stock_property');
863 my $recorded_current_count = $self->stock()->find_related('stockprops', {'me.type_id'=>$current_count_cvterm->cvterm_id});
864 return $recorded_current_count ?
$recorded_current_count->value() : '';
867 =head2 Method current_weight()
869 Usage: my $current_weight = $sl->current_weight();
870 Desc: returns the current weight of seeds in the seedlot
873 Side Effects: retrieves transactions from db and calculates weight
880 my $transactions = $self->transactions();
883 foreach my $t (@
$transactions) {
884 if ($t->weight_gram() ne 'NA'){
885 $weight += $t->weight_gram() * $t->factor();
888 if ($weight == 0 && scalar(@
$transactions)>0){
894 # It is convenient and also much faster to retrieve a single value for the current_weight, rather than calculating it from the transactions.
895 sub set_current_weight_property
{
897 my $current_weight = $self->current_weight();
898 my $current_weight_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_weight_gram', 'stock_property');
899 my $stock = $self->stock();
900 my $recorded_current_weight = $stock->find_related('stockprops', {'me.type_id'=>$current_weight_cvterm->cvterm_id});
901 if ($recorded_current_weight){
902 $recorded_current_weight->update({'value'=>$current_weight});
904 $stock->create_stockprops({$current_weight_cvterm->name() => $current_weight});
906 return $current_weight;
909 # It is convenient and also much faster to retrieve a single value for the current_weight, rather than calculating it from the transactions.
910 sub get_current_weight_property
{
912 my $current_count_cvterm = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'current_weight_gram', 'stock_property');
913 my $recorded_current_count = $self->stock()->find_related('stockprops', {'me.type_id'=>$current_count_cvterm->cvterm_id});
914 return $recorded_current_count ?
$recorded_current_count->value() : '';
918 sub _add_transaction
{
920 my $transaction = shift;
922 my $transactions = $self->transactions();
923 push @
$transactions, $transaction;
925 $self->transactions($transactions);
930 Usage: my $seedlot_id = $sl->store();
931 Desc: stores the current state of the object to the db. uses CXGN::Stock store as well.
934 Side Effects: accesses the db. Creates a new seedlot ID if not
945 #Creating new seedlot
947 $self->name($self->uniquename());
948 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema, 'seedlot', 'stock_type')->cvterm_id();
949 $self->type_id($type_id);
950 my $id = $self->SUPER::store
();
951 print STDERR
"Saving seedlot returned ID $id.".localtime."\n";
952 $self->seedlot_id($id);
953 $self->_store_seedlot_location();
954 $error = $self->_store_seedlot_relationships();
958 if ($self->box_name){
959 $self->_store_stockprop('location_code', $self->box_name);
962 } else { #Updating seedlot
964 #Attempting to update seedlot's accession. Will not proceed if seedlot has already been used in transactions.
965 if($self->accession_stock_id){
966 my $input_accession_id = $self->accession_stock_id;
967 my $transactions = $self->transactions();
968 my $stored_accession_id = $self->accession ?
$self->accession->[0] : 0;
969 $self->accession_stock_id($input_accession_id);
970 my $accessions_have_changed = $input_accession_id == $stored_accession_id ?
0 : 1;
971 if ($accessions_have_changed && scalar(@
$transactions)>1){
972 $error = "This seedlot ".$self->uniquename." has been used in transactions, so the contents (accessions) cannot be changed now!";
973 } elsif ($accessions_have_changed && scalar(@
$transactions) <= 1) {
974 $error = $self->_update_content_stock_id();
975 my $update_first_transaction_id = $transactions->[0]->update_transaction_object_id($self->accession_stock_id);
982 #Attempting to update seedlot's cross. Will not proceed if seedlot has already been used in transactions.
983 if($self->cross_stock_id){
984 my $input_cross_id = $self->cross_stock_id;
985 my $transactions = $self->transactions();
986 my $stored_cross_id = $self->cross ?
$self->cross->[0] : 0;
987 $self->cross_stock_id($input_cross_id);
988 my $crosses_have_changed = $input_cross_id == $stored_cross_id ?
0 : 1;
989 if ($crosses_have_changed && scalar(@
$transactions)>1){
990 $error = "This seedlot ".$self->uniquename." has been used in transactions, so the contents (crosses) cannot be changed now!";
991 } elsif ($crosses_have_changed && scalar(@
$transactions) <= 1) {
992 $error = $self->_update_content_stock_id();
993 my $update_first_transaction_id = $transactions->[0]->update_transaction_object_id($self->cross_stock_id);
1000 my $id = $self->SUPER::store
();
1001 print STDERR
"Updating seedlot returned ID $id.".localtime."\n";
1002 $self->seedlot_id($id);
1003 if($self->breeding_program_id){
1004 $self->_update_seedlot_breeding_program();
1006 if($self->location_code){
1007 $self->_store_seedlot_location();
1008 $self->_update_seedlot_location();
1010 if($self->box_name){
1011 $self->_update_stockprop('location_code', $self->box_name);
1016 my $transaction_error;
1018 $self->schema->txn_do($coderef);
1020 print STDERR
"Transaction Error: $_\n";
1021 $transaction_error = $_;
1023 if ($transaction_error){
1024 return { error
=>$transaction_error };
1026 return { success
=>1, seedlot_id
=>$self->stock_id() };
1032 Usage: my $error_message = $sl->delete();
1033 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.
1034 Ret: any error message. undef if no errors
1036 Side Effects: accesses the db. Deletes seedlot
1044 my $transactions = $self->transactions();
1045 if (scalar(@
$transactions)>1){
1046 $error = "This seedlot has been used in transactions and so cannot be deleted!";
1048 my $stock = $self->stock();
1049 my $experiment_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->schema(), "seedlot_experiment", "experiment_type")->cvterm_id();
1050 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});
1051 if ($nd_experiment_rs->count != 1){
1052 $error = "Seedlot does not have 1 nd_experiment associated!";
1054 my $nd_experiment = $nd_experiment_rs->first();
1055 $nd_experiment->delete();
1056 my $stock_owner_rs = $self->phenome_schema->resultset("StockOwner")->find({stock_id
=>$self->stock_id});
1057 if ($stock_owner_rs){
1058 $stock_owner_rs->delete();
1070 __PACKAGE__
->meta->make_immutable;