From a048340dc7b16df07e1223aa8915dd6a15041997 Mon Sep 17 00:00:00 2001 From: titima15 Date: Wed, 11 Feb 2026 10:28:44 -0500 Subject: [PATCH 1/9] seedlot inventory upload generic --- .../Plugin/SeedlotInventoryGeneric.pm | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm diff --git a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm new file mode 100644 index 0000000000..f4bf6268bb --- /dev/null +++ b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm @@ -0,0 +1,138 @@ +package CXGN::Stock::Seedlot::ParseUpload::Plugin::SeedlotInventoryGeneric; + +use Moose::Role; +use CXGN::File::Parse; +use CXGN::Stock::StockLookup; +use SGN::Model::Cvterm; +use Data::Dumper; +use CXGN::List::Validate; + +sub _validate_with_plugin { + my $self = shift; + + my $filename = $self->get_filename(); + my $schema = $self->get_chado_schema(); + + my @error_messages; + my %errors; + my %missing_seedlots; + + my $parser = CXGN::File::Parse->new ( + file => $filename, + required_columns => [ 'box_id', 'seed_id', 'inventory_date', 'inventory_person'], + optional_columns => ['weight_gram', 'amount'], + column_aliases => { + 'box_id' => ['box id', 'box name', box_name], + 'seed_id' => ['seed id', 'seedlot_name', 'seedlot name'], + 'inventory_date' => ['inventory date'], + 'inventory_person' => ['inventory person'], + 'weight_gram' => ['weight(g)', 'weight gram'], + }, + ); + + my $parsed = $parser->parse(); + my $parsed_errors = $parsed->{errors}; + my $parsed_columns = $parsed->{columns}; + my $parsed_data = $parsed->{data}; + my $parsed_values = $parsed->{values}; + my $additional_columns = $parsed->{additional_columns}; + + if ( $parsed_errors && scalar(@$parsed_errors) > 0 ) { + $errors{'error_messages'} = $parsed_errors; + $self->_set_parse_errors(\%errors); + return; + } + + if ( $additional_columns && scalar(@$additional_columns) > 0 ) { + $errors{'error_messages'} = [ + "The following columns are not recognized: " . join(', ', @$additional_columns) . ". Please check the spreadsheet format for the allowed columns." + ]; + $self->_set_parse_errors(\%errors); + return; + } + + for my $row ( @$parsed_data ) { + my $row_num = $row->{_row}; + my $seedlot_name = $row->{'seedlot_id'}; + my $amount = $row->{'amount'}; + my $weight = $row->{'weight_gram'}; + + if ( (!$amount || $amount eq '') && (!$weight || $weight eq '')) { + push @error_messages, "On row:$row_num you must provide either a weight in grams or a seed count amount."; + } + } + + my $seen_seedlot_names = $parsed_values->{'seedlot_id'}; + + my $seedlot_validator = CXGN::List::Validate->new(); + my @seedlots_missing = @{$seedlot_validator->validate($schema,'seedlots',$seen_seedlot_names)->{'missing'}}; + + if (scalar(@seedlots_missing) > 0) { + push @error_messages, "The following seedlots are not in the database as uniquenames: ".join(',',@seedlots_missing); + } + + if (scalar(@error_messages) >= 1) { + $errors{'error_messages'} = \@error_messages; + $self->_set_parse_errors(\%errors); + return; + } else { + $self->_set_parsed_data($parsed); + } + + return 1; + +} + +sub _parse_with_plugin { + my $self = shift; + my $schema = $self->get_chado_schema(); + my $parsed = $self->_parsed_data(); + my $parsed_data = $parsed->{data}; + my $parsed_values = $parsed->{values}; + my %parsed_result; + my %seen_seedlot_names; + + my $accession_names = $parsed_values->{'accession_name'}; + my $seedlot_names = $parsed_values->{'seedlot_name'}; + + for my $row (@$parsed_data) { + my $row_num; + my $seed_id; + my $box_id; + my $inventory_date; + my $inventory_person; + my $weight_gram; + my $amount; + $seen_seedlot_names{$seed_id}++; + + $row_num = $row->{_row}; + $seed_id = $row->{'seed_id'}; + $box_id = $row->{'box_id'}; + $inventory_date = $row->{'inventory_date'}; + $inventory_person = $row->{'inventory_person'}; + $weight_gram = $row->{'weight_gram'}; + $amount = $row->{'amount'}; + + $parse_result{$seed_id} = { + box_id => $box_id, + seedlot_name => $seed_id, + inventory_date => $inventory_date, + inventory_person => $inventory_person, + weight_gram => $weight_gram + }; + } + + my @seedlot_names = keys %seen_seedlot_names; + my $seedlots_rs = $schema->resultset("Stock::Stock")->search({uniquename => {-in => \@seedlot_names}}); + while (my $r = $seedlots_rs->next){ + $parse_result{$r->uniquename}->{seedlot_id} = $r->stock_id; + } + + $self->_set_parsed_data(\%parsed_result); + + return 1; + +} + + +1; From 46ff8fd7bbcf7d4a8d3b0f57496454ae88d836f2 Mon Sep 17 00:00:00 2001 From: titima15 Date: Wed, 11 Feb 2026 10:53:28 -0500 Subject: [PATCH 2/9] option to use amount for seedlot inventory --- lib/SGN/Controller/AJAX/Seedlot.pm | 52 +++++++++++++++++++----------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/lib/SGN/Controller/AJAX/Seedlot.pm b/lib/SGN/Controller/AJAX/Seedlot.pm index f5c73f0dfe..90e2fc6b25 100644 --- a/lib/SGN/Controller/AJAX/Seedlot.pm +++ b/lib/SGN/Controller/AJAX/Seedlot.pm @@ -912,41 +912,55 @@ sub upload_seedlots_inventory_POST : Args(0) { while (my ($key, $val) = each(%$parsed_data)){ my $sl = CXGN::Stock::Seedlot->new(schema => $schema, seedlot_id => $val->{seedlot_id}); $sl->box_name($val->{box_id}); - $sl->description($val->{description}); +# $sl->description($val->{description}); - print STDERR "QUALITY: $val->{quality}\n"; - $sl->quality($val->{quality}); +# print STDERR "QUALITY: $val->{quality}\n"; +# $sl->quality($val->{quality}); my $return = $sl->store(); - my $current_stored_count = $sl->get_current_count_property(); + my $current_stored_count = $sl->get_current_count_property(); my $current_stored_weight = $sl->get_current_weight_property(); - - my $weight_difference = $val->{weight_gram} - $current_stored_weight; + my $weight_difference; + my $amount_difference; my $factor; - if ($weight_difference >= 0){ - $factor = 1; - } else { - $factor = -1; - $weight_difference = $weight_difference * -1; #Store positive values only + if ($val->{weight_gram}) { + $weight_difference = $val->{weight_gram} - $current_stored_weight; + if ($weight_difference >= 0){ + $factor = 1; + } else { + $factor = -1; + $weight_difference = $weight_difference * -1; #Store positive values only + } + } + + if ($val->{amount}) { + $amount_difference = $val->{amount} - $current_stored_count; + if ($amount_difference >= 0){ + $factor = 1; + } else { + $factor = -1; + $amount_difference = $amount_difference * -1; #Store positive values only + } } my $transaction = CXGN::Stock::Seedlot::Transaction->new(schema => $schema); $transaction->factor($factor); - my $from_stock_id = $val->{seedlot_id}; - my $from_stock_name = $val->{seedlot_name}; +# my $from_stock_id = $val->{seedlot_id}; +# my $from_stock_name = $val->{seedlot_name}; - if ($val->{source_id}) { - $from_stock_id = $val->{source_id}; - $from_stock_name = $val->{source}; - } +# if ($val->{source_id}) { +# $from_stock_id = $val->{source_id}; +# $from_stock_name = $val->{source}; +# } - $transaction->from_stock([ $from_stock_id, $from_stock_name ]); +# $transaction->from_stock([ $from_stock_id, $from_stock_name ]); $transaction->to_stock([$val->{seedlot_id}, $val->{seedlot_name}]); $transaction->weight_gram($weight_difference); + $transaction->amount($amount_difference); $transaction->timestamp($val->{inventory_date}); - $transaction->description('Seed inventory CSV upload.'); + $transaction->description('Seed inventory upload.'); $transaction->operator($val->{inventory_person}); $transaction->store(); From 43300bb01b292979d06a99540a90de9c74acbc49 Mon Sep 17 00:00:00 2001 From: titima15 Date: Wed, 11 Feb 2026 14:35:20 -0500 Subject: [PATCH 3/9] fix plugin --- .../Plugin/SeedlotInventoryGeneric.pm | 33 +++++++++++++------ lib/SGN/Controller/AJAX/Seedlot.pm | 32 +++++++----------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm index f4bf6268bb..d310323435 100644 --- a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm +++ b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm @@ -22,11 +22,13 @@ sub _validate_with_plugin { required_columns => [ 'box_id', 'seed_id', 'inventory_date', 'inventory_person'], optional_columns => ['weight_gram', 'amount'], column_aliases => { - 'box_id' => ['box id', 'box name', box_name], + 'box_id' => ['box id', 'box name', 'box_name'], 'seed_id' => ['seed id', 'seedlot_name', 'seedlot name'], 'inventory_date' => ['inventory date'], 'inventory_person' => ['inventory person'], 'weight_gram' => ['weight(g)', 'weight gram'], + 'amount' => ['count'], + }, ); @@ -57,7 +59,12 @@ sub _validate_with_plugin { my $amount = $row->{'amount'}; my $weight = $row->{'weight_gram'}; - if ( (!$amount || $amount eq '') && (!$weight || $weight eq '')) { + if (!$amount || $amount eq '') { + $amount = 'NA'; + } elsif (!$weight || $weight eq '') { + $weight = 'NA'; + } + if ($amount eq 'NA' && $weight eq 'NA') { push @error_messages, "On row:$row_num you must provide either a weight in grams or a seed count amount."; } } @@ -101,33 +108,39 @@ sub _parse_with_plugin { my $box_id; my $inventory_date; my $inventory_person; - my $weight_gram; + my $weight; my $amount; - $seen_seedlot_names{$seed_id}++; - $row_num = $row->{_row}; $seed_id = $row->{'seed_id'}; $box_id = $row->{'box_id'}; $inventory_date = $row->{'inventory_date'}; $inventory_person = $row->{'inventory_person'}; - $weight_gram = $row->{'weight_gram'}; + $weight = $row->{'weight_gram'}; $amount = $row->{'amount'}; + $seen_seedlot_names{$seed_id}++; + + if (!$amount || $amount eq '') { + $amount = 'NA'; + } elsif (!$weight || $weight eq '') { + $weight = 'NA'; + } - $parse_result{$seed_id} = { + $parsed_result{$seed_id} = { box_id => $box_id, seedlot_name => $seed_id, inventory_date => $inventory_date, inventory_person => $inventory_person, - weight_gram => $weight_gram + weight_gram => $weight, + amount => $amount }; } my @seedlot_names = keys %seen_seedlot_names; my $seedlots_rs = $schema->resultset("Stock::Stock")->search({uniquename => {-in => \@seedlot_names}}); while (my $r = $seedlots_rs->next){ - $parse_result{$r->uniquename}->{seedlot_id} = $r->stock_id; + $parsed_result{$r->uniquename}{seedlot_id} = $r->stock_id; } - + print STDERR "PARSED RESULT PLUGIN =".Dumper(\%parsed_result)."\n"; $self->_set_parsed_data(\%parsed_result); return 1; diff --git a/lib/SGN/Controller/AJAX/Seedlot.pm b/lib/SGN/Controller/AJAX/Seedlot.pm index 90e2fc6b25..ce78240a2c 100644 --- a/lib/SGN/Controller/AJAX/Seedlot.pm +++ b/lib/SGN/Controller/AJAX/Seedlot.pm @@ -887,7 +887,7 @@ sub upload_seedlots_inventory_POST : Args(0) { } unlink $upload_tempfile; my $parser = CXGN::Stock::Seedlot::ParseUpload->new(chado_schema => $schema, filename => $archived_filename_with_path); - $parser->load_plugin('SeedlotInventoryCSV'); + $parser->load_plugin('SeedlotInventoryGeneric'); my $parsed_data = $parser->parse(); #print STDERR Dumper $parsed_data; @@ -907,16 +907,11 @@ sub upload_seedlots_inventory_POST : Args(0) { $c->stash->{rest} = {error_string => $return_error, missing_seedlots => $parse_errors->{'missing_seedlots'} }; $c->detach(); } - + print STDERR "PARSED DATA =".Dumper($parsed_data)."\n"; eval { while (my ($key, $val) = each(%$parsed_data)){ my $sl = CXGN::Stock::Seedlot->new(schema => $schema, seedlot_id => $val->{seedlot_id}); $sl->box_name($val->{box_id}); -# $sl->description($val->{description}); - -# print STDERR "QUALITY: $val->{quality}\n"; -# $sl->quality($val->{quality}); - my $return = $sl->store(); my $current_stored_count = $sl->get_current_count_property(); @@ -924,8 +919,11 @@ sub upload_seedlots_inventory_POST : Args(0) { my $weight_difference; my $amount_difference; my $factor; - if ($val->{weight_gram}) { - $weight_difference = $val->{weight_gram} - $current_stored_weight; + my $inventory_weight = $val->{weight_gram}; + my $inventory_amount = $val->{amount}; + + if (defined $inventory_weight) { + $weight_difference = $inventory_weight - $current_stored_weight; if ($weight_difference >= 0){ $factor = 1; } else { @@ -934,8 +932,8 @@ sub upload_seedlots_inventory_POST : Args(0) { } } - if ($val->{amount}) { - $amount_difference = $val->{amount} - $current_stored_count; + if (defined $inventory_amount) { + $amount_difference = $inventory_amount - $current_stored_count; if ($amount_difference >= 0){ $factor = 1; } else { @@ -947,15 +945,9 @@ sub upload_seedlots_inventory_POST : Args(0) { my $transaction = CXGN::Stock::Seedlot::Transaction->new(schema => $schema); $transaction->factor($factor); -# my $from_stock_id = $val->{seedlot_id}; -# my $from_stock_name = $val->{seedlot_name}; - -# if ($val->{source_id}) { -# $from_stock_id = $val->{source_id}; -# $from_stock_name = $val->{source}; -# } - -# $transaction->from_stock([ $from_stock_id, $from_stock_name ]); + my $from_stock_id = $val->{seedlot_id}; + my $from_stock_name = $val->{seedlot_name}; + $transaction->from_stock([ $from_stock_id, $from_stock_name ]); $transaction->to_stock([$val->{seedlot_id}, $val->{seedlot_name}]); $transaction->weight_gram($weight_difference); $transaction->amount($amount_difference); From 269e1f8af49e35db6930641d0204b9d06bb4d6a5 Mon Sep 17 00:00:00 2001 From: titima15 Date: Wed, 11 Feb 2026 16:18:33 -0500 Subject: [PATCH 4/9] explicitly indicate amount or weight --- .../Seedlot/ParseUpload/Plugin/SeedlotInventoryCSV.pm | 5 +++++ .../ParseUpload/Plugin/SeedlotInventoryGeneric.pm | 2 +- lib/SGN/Controller/AJAX/Seedlot.pm | 11 +++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryCSV.pm b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryCSV.pm index f023a8d4b9..8614883fb5 100644 --- a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryCSV.pm +++ b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryCSV.pm @@ -6,6 +6,11 @@ use Data::Dumper; use Text::CSV; use CXGN::List::Validate; +# +# DEPRECATED: This plugin has been replaced by the SeedlotInventoryGeneric plugin +# + + sub _validate_with_plugin { my $self = shift; my $filename = $self->get_filename(); diff --git a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm index d310323435..2fc480b014 100644 --- a/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm +++ b/lib/CXGN/Stock/Seedlot/ParseUpload/Plugin/SeedlotInventoryGeneric.pm @@ -140,7 +140,7 @@ sub _parse_with_plugin { while (my $r = $seedlots_rs->next){ $parsed_result{$r->uniquename}{seedlot_id} = $r->stock_id; } - print STDERR "PARSED RESULT PLUGIN =".Dumper(\%parsed_result)."\n"; + $self->_set_parsed_data(\%parsed_result); return 1; diff --git a/lib/SGN/Controller/AJAX/Seedlot.pm b/lib/SGN/Controller/AJAX/Seedlot.pm index ce78240a2c..21419a76de 100644 --- a/lib/SGN/Controller/AJAX/Seedlot.pm +++ b/lib/SGN/Controller/AJAX/Seedlot.pm @@ -922,7 +922,7 @@ sub upload_seedlots_inventory_POST : Args(0) { my $inventory_weight = $val->{weight_gram}; my $inventory_amount = $val->{amount}; - if (defined $inventory_weight) { + if ($inventory_weight ne 'NA') { $weight_difference = $inventory_weight - $current_stored_weight; if ($weight_difference >= 0){ $factor = 1; @@ -932,7 +932,7 @@ sub upload_seedlots_inventory_POST : Args(0) { } } - if (defined $inventory_amount) { + if ($inventory_amount ne 'NA') { $amount_difference = $inventory_amount - $current_stored_count; if ($amount_difference >= 0){ $factor = 1; @@ -949,8 +949,11 @@ sub upload_seedlots_inventory_POST : Args(0) { my $from_stock_name = $val->{seedlot_name}; $transaction->from_stock([ $from_stock_id, $from_stock_name ]); $transaction->to_stock([$val->{seedlot_id}, $val->{seedlot_name}]); - $transaction->weight_gram($weight_difference); - $transaction->amount($amount_difference); + if ($inventory_weight ne 'NA') { + $transaction->weight_gram($weight_difference); + } elsif ($inventory_amount ne 'NA') { + $transaction->amount($amount_difference); + } $transaction->timestamp($val->{inventory_date}); $transaction->description('Seed inventory upload.'); $transaction->operator($val->{inventory_person}); From 1ccb0368f2ff89aae469e1a258dc903d4d8d59f8 Mon Sep 17 00:00:00 2001 From: titima15 Date: Wed, 11 Feb 2026 16:56:49 -0500 Subject: [PATCH 5/9] modify spreadsheet info --- .../upload_seedlots_inventory_dialogs.mas | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mason/breeders_toolbox/upload_seedlots_inventory_dialogs.mas b/mason/breeders_toolbox/upload_seedlots_inventory_dialogs.mas index 9403f576b3..fc61e955aa 100644 --- a/mason/breeders_toolbox/upload_seedlots_inventory_dialogs.mas +++ b/mason/breeders_toolbox/upload_seedlots_inventory_dialogs.mas @@ -154,10 +154,6 @@