From b7e8d2d5f17e388241d9a709f8f5b537c0ee1f39 Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Tue, 10 May 2016 18:36:40 +0000 Subject: [PATCH] [WIP] Bug 16528 - Add ability to parallel process fines to fines.pl --- misc/cronjobs/fines.pl | 155 ++++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 61 deletions(-) diff --git a/misc/cronjobs/fines.pl b/misc/cronjobs/fines.pl index 6c3f830..63f5d26 100755 --- a/misc/cronjobs/fines.pl +++ b/misc/cronjobs/fines.pl @@ -26,30 +26,32 @@ # You should have received a copy of the GNU General Public License # along with Koha; if not, see . -use strict; -use warnings; -use 5.010; +use Modern::Perl; -use C4::Context; -use C4::Overdues; -use Getopt::Long; use Carp; use File::Spec; +use Getopt::Long; +use Parallel::ForkManager; + +use C4::Context; +use C4::Overdues; +use C4::Log; use Koha::Calendar; use Koha::DateUtils; -use C4::Log; my $help; my $verbose; my $output_dir; my $log; +my $max_processes = 0; GetOptions( - 'h|help' => \$help, - 'v|verbose' => \$verbose, - 'l|log' => \$log, - 'o|out:s' => \$output_dir, + 'h|help' => \$help, + 'v|verbose' => \$verbose, + 'l|log' => \$log, + 'o|out:s' => \$output_dir, + 'm|max-processes:s' => \$max_processes, ); my $usage = << 'ENDUSAGE'; @@ -63,6 +65,7 @@ This script has the following parameters : -l --log: log the output to a file (optional if the -o parameter is given) -o --out: ouput directory for logs (defaults to env or /tmp if !exist) -v --verbose + -m --max-processes: The maximum number of concurrent fine calculating processes ENDUSAGE @@ -97,62 +100,92 @@ if ($filename) { } my $counted = 0; my $overdues = Getoverdues(); -for my $overdue ( @{$overdues} ) { - next if $overdue->{itemlost}; - if ( !defined $overdue->{borrowernumber} ) { - carp -"ERROR in Getoverdues : issues.borrowernumber IS NULL. Repair 'issues' table now! Skipping record.\n"; - next; - } - my $borrower = BorType( $overdue->{borrowernumber} ); - my $branchcode = - ( $control eq 'ItemHomeLibrary' ) ? $overdue->{homebranch} - : ( $control eq 'PatronLibrary' ) ? $borrower->{branchcode} - : $overdue->{branchcode}; - -# In final case, CircControl must be PickupLibrary. (branchcode comes from issues table here). - if ( !exists $is_holiday{$branchcode} ) { - $is_holiday{$branchcode} = set_holiday( $branchcode, $today ); - } +# Break out overdues per patron, this is needed in case we process overdues +# in parallel. All overdues for a single patron still need to be handled by +# a single process in case there are fine limits in place that need to account +# for previous fines. If they were in parallel the math would be incorrect. +my $overdues_by_patron; +map { push( @{$overdues_by_patron->{ $_->{borrowernumber} } }, $_ ) } @$overdues; + +my $pm = Parallel::ForkManager->new( $max_processes, '/tmp' ); +$pm->run_on_finish( # called BEFORE the first call to start() + sub { + my ( $pid, $exit_code, $ident, $exit_signal, $core_dump, $data ) = @_; - my $datedue = dt_from_string( $overdue->{date_due} ); - if ( DateTime->compare( $datedue, $today ) == 1 ) { - next; # not overdue + $counted++ if $exit_code; } - ++$counted; - - my ( $amount, $type, $unitcounttotal ) = - CalcFine( $overdue, $borrower->{categorycode}, - $branchcode, $datedue, $today ); - $type ||= q{}; - - # Don't update the fine if today is a holiday. - # This ensures that dropbox mode will remove the correct amount of fine. - if ( $mode eq 'production' && !$is_holiday{$branchcode} ) { - if ( $amount > 0 ) { - UpdateFine( - { - issue_id => $overdue->{issue_id}, - itemnumber => $overdue->{itemnumber}, - borrowernumber => $overdue->{borrowernumber}, - amount => $amount, - type => $type, - due => output_pref($datedue), - } - ); +); + +OVERDUES_LOOP: +for my $patron_id ( keys %$overdues_by_patron ) { + my @overdues = @{$overdues_by_patron->{$patron_id}}; + + my $pid = $pm->start and next OVERDUES_LOOP; + for my $overdue ( @overdues ) { + my $counted = 0; + next if $overdue->{itemlost}; + + if ( !defined $overdue->{borrowernumber} ) { + carp "ERROR in Getoverdues : issues.borrowernumber IS NULL. Repair 'issues' table now! Skipping record.\n"; + next; } + + + my $borrower = BorType( $overdue->{borrowernumber} ); + my $branchcode = + ( $control eq 'ItemHomeLibrary' ) ? $overdue->{homebranch} + : ( $control eq 'PatronLibrary' ) ? $borrower->{branchcode} + : $overdue->{branchcode}; + + # In final case, CircControl must be PickupLibrary. (branchcode comes from issues table here). + if ( !exists $is_holiday{$branchcode} ) { + $is_holiday{$branchcode} = set_holiday( $branchcode, $today ); + } + + my $datedue = dt_from_string( $overdue->{date_due} ); + if ( DateTime->compare( $datedue, $today ) == 1 ) { + $pm->finish(0); # not overdue + } + + my ( $amount, $type, $unitcounttotal ) = + CalcFine( $overdue, $borrower->{categorycode}, + $branchcode, $datedue, $today ); + $type ||= q{}; + + # Don't update the fine if today is a holiday. + # This ensures that dropbox mode will remove the correct amount of fine. + if ( $mode eq 'production' && !$is_holiday{$branchcode} ) { + if ( $amount > 0 ) { + UpdateFine( + { + issue_id => $overdue->{issue_id}, + itemnumber => $overdue->{itemnumber}, + borrowernumber => $overdue->{borrowernumber}, + amount => $amount, + type => $type, + due => output_pref($datedue), + } + ); + } + } + if ($filename) { + my @cells; + push @cells, + map { defined $borrower->{$_} ? $borrower->{$_} : q{} } + @borrower_fields; + push @cells, map { $overdue->{$_} } @item_fields; + push @cells, $type, $unitcounttotal, $amount; + say {$fh} join $delim, @cells; + } + + $counted++; } - if ($filename) { - my @cells; - push @cells, - map { defined $borrower->{$_} ? $borrower->{$_} : q{} } - @borrower_fields; - push @cells, map { $overdue->{$_} } @item_fields; - push @cells, $type, $unitcounttotal, $amount; - say {$fh} join $delim, @cells; - } + $pm->finish($counted); } + +$pm->wait_all_children; + if ($filename){ close $fh; } -- 2.1.4