Bugzilla – Attachment 121372 Details for
Bug 28445
Use the task queue for the batch delete and update items tool
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 28445: WIP - Use the task queue for the batch delete and update items tool
Bug-28445-WIP---Use-the-task-queue-for-the-batch-d.patch (text/plain), 38.24 KB, created by
Jonathan Druart
on 2021-05-25 12:15:05 UTC
(
hide
)
Description:
Bug 28445: WIP - Use the task queue for the batch delete and update items tool
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2021-05-25 12:15:05 UTC
Size:
38.24 KB
patch
obsolete
>From 10f1366a806c5ea3c680d1913801b499d8950bfc Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Tue, 25 May 2021 10:31:36 +0200 >Subject: [PATCH] Bug 28445: WIP - Use the task queue for the batch delete and > update items tool > >--- > Koha/BackgroundJob.pm | 8 +- > Koha/BackgroundJob/BatchDeleteItem.pm | 140 ++++++++ > Koha/BackgroundJob/BatchUpdateItem.pm | 313 ++++++++++++++++++ > .../apache-shared-intranet-plack.conf | 1 - > .../batch_item_record_modification.inc | 64 ++++ > .../prog/en/modules/tools/batchMod-edit.tt | 40 ++- > misc/background_jobs_worker.pl | 2 + > tools/batchMod.pl | 229 ++----------- > 8 files changed, 590 insertions(+), 207 deletions(-) > create mode 100644 Koha/BackgroundJob/BatchDeleteItem.pm > create mode 100644 Koha/BackgroundJob/BatchUpdateItem.pm > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc > >diff --git a/Koha/BackgroundJob.pm b/Koha/BackgroundJob.pm >index 9b6de247387..e043d219734 100644 >--- a/Koha/BackgroundJob.pm >+++ b/Koha/BackgroundJob.pm >@@ -26,8 +26,10 @@ use Koha::DateUtils qw( dt_from_string ); > use Koha::Exceptions; > use Koha::BackgroundJob::BatchUpdateBiblio; > use Koha::BackgroundJob::BatchUpdateAuthority; >+use Koha::BackgroundJob::BatchUpdateItem; > use Koha::BackgroundJob::BatchDeleteBiblio; > use Koha::BackgroundJob::BatchDeleteAuthority; >+use Koha::BackgroundJob::BatchDeleteItem; > > use base qw( Koha::Object ); > >@@ -157,11 +159,15 @@ sub process { > ? Koha::BackgroundJob::BatchUpdateBiblio->process($args) > : $job_type eq 'batch_authority_record_modification' > ? Koha::BackgroundJob::BatchUpdateAuthority->process($args) >+ : $job_type eq 'batch_item_record_modification' >+ ? Koha::BackgroundJob::BatchUpdateItem->process($args) > : $job_type eq 'batch_biblio_record_deletion' > ? Koha::BackgroundJob::BatchDeleteBiblio->process($args) > : $job_type eq 'batch_authority_record_deletion' > ? Koha::BackgroundJob::BatchDeleteAuthority->process($args) >- : Koha::Exceptions::Exception->throw('->process called without valid job_type'); >+ : $job_type eq 'batch_item_record_deletion' >+ ? Koha::BackgroundJob::BatchDeleteItem->process($args) >+ : Koha::Exceptions::Exception->throw('->process called without valid job_type') > } > > =head3 job_type >diff --git a/Koha/BackgroundJob/BatchDeleteItem.pm b/Koha/BackgroundJob/BatchDeleteItem.pm >new file mode 100644 >index 00000000000..6ef0ee53669 >--- /dev/null >+++ b/Koha/BackgroundJob/BatchDeleteItem.pm >@@ -0,0 +1,140 @@ >+package Koha::BackgroundJob::BatchDeleteItem; >+ >+use Modern::Perl; >+use JSON qw( encode_json decode_json ); >+ >+use Koha::BackgroundJobs; >+use Koha::DateUtils qw( dt_from_string ); >+ >+use base 'Koha::BackgroundJob'; >+ >+sub job_type { >+ return 'batch_item_record_deletion'; >+} >+ >+sub process { >+ my ( $self, $args ) = @_; >+ >+ my $job_type = $args->{job_type}; >+ >+ my $job = Koha::BackgroundJobs->find( $args->{job_id} ); >+ >+ if ( !exists $args->{job_id} || !$job || $job->status eq 'cancelled' ) { >+ return; >+ } >+ >+ # FIXME If the job has already been started, but started again (worker has been restart for instance) >+ # Then we will start from scratch and so double delete the same records >+ >+ my $job_progress = 0; >+ $job->started_on(dt_from_string) >+ ->progress($job_progress) >+ ->status('started') >+ ->store; >+ >+ my @record_ids = @{ $args->{record_ids} }; >+ >+ my $report = { >+ total_records => scalar @record_ids, >+ total_success => 0, >+ }; >+ my @messages; >+ my $schema = Koha::Database->new->schema; >+ RECORD_IDS: for my $record_id ( sort { $a <=> $b } @record_ids ) { >+ >+ last if $job->get_from_storage->status eq 'cancelled'; >+ >+ try { >+ >+ my $schema = Koha::Database->new->schema; >+ $schema->txn_do( >+ sub { >+ my $item = Koha::Items->find($record_id); >+ next unless $item; >+ >+ my $biblionumber = $item->biblionumber; >+ my $return = $item->safe_delete; >+ if ( ref( $return ) ) { >+ $deleted_items++; >+ push @$upd_biblionumbers, $biblionumber; >+ } >+ else { >+ push @messages, { >+ type => 'error', >+ code => 'item_not_deleted', >+ itemnumber => $item->itemnumber >+ biblionumber => $biblionumber, >+ barcode => $item->barcode, >+ title => $item->biblio->title, >+ reason => $return, >+ } >+ } >+ >+ # If there are no items left, delete the biblio >+ if ($del_records) { >+ my $itemscount = Koha::Biblios->find( $biblionumber )->items->count; >+ if ( $itemscount == 0 ) { >+ my $error = C4::Biblio::DelBiblio( $biblionumber, { skip_record_index => 1 } ); >+ unless ($error) { >+ push @$del_biblionumbers, $biblionumber; >+ if ( $src eq 'CATALOGUING' ) { >+ # We are coming catalogue/detail.pl, there were items from a single bib record >+ $template->param( biblio_deleted => 1 ); >+ } >+ } >+ } >+ } >+ } >+ ); >+ } >+ catch { >+ >+ warn $_; >+ >+ if ( $_->isa('Koha::Exceptions::Exception') ) { >+ $template->param( deletion_failed => 1 ); >+ } >+ die "Something terrible has happened!" >+ if ($_ =~ /Rollback failed/); # Rollback failed >+ }; >+ >+ $report->{total_success}++; >+ $schema->storage->txn_commit; >+ $job->progress( ++$job_progress )->store; >+ } >+ >+ $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once >+ >+ # Don't send specialUpdate for records we are going to delete >+ my %del_bib_hash = map{ $_ => undef } @$del_biblionumbers; >+ @$upd_biblionumbers = grep( ! exists( $del_bib_hash{$_} ), @$upd_biblionumbers ); >+ >+ my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); >+ $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; >+ $indexer->index_records( $del_biblionumbers, 'recordDelete', "biblioserver", undef ) if @$del_biblionumbers; >+ >+ my $job_data = decode_json $job->data; >+ $job_data->{messages} = \@messages; >+ $job_data->{report} = $report; >+ >+ $job->ended_on(dt_from_string) >+ ->data(encode_json $job_data); >+ $job->status('finished') if $job->status ne 'cancelled'; >+ $job->store; >+} >+ >+sub enqueue { >+ my ( $self, $args) = @_; >+ >+ # TODO Raise exception instead >+ return unless exists $args->{record_ids}; >+ >+ my @record_ids = @{ $args->{record_ids} }; >+ >+ $self->SUPER::enqueue({ >+ job_size => scalar @record_ids, >+ job_args => {record_ids => \@record_ids,} >+ }); >+} >+ >+1; >diff --git a/Koha/BackgroundJob/BatchUpdateItem.pm b/Koha/BackgroundJob/BatchUpdateItem.pm >new file mode 100644 >index 00000000000..8fe0a75764b >--- /dev/null >+++ b/Koha/BackgroundJob/BatchUpdateItem.pm >@@ -0,0 +1,313 @@ >+package Koha::BackgroundJob::BatchUpdateItem; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use JSON qw( encode_json decode_json ); >+use List::MoreUtils qw( uniq ); >+use Try::Tiny; >+ >+use Koha::BackgroundJobs; >+use Koha::DateUtils qw( dt_from_string ); >+use Koha::SearchEngine::Indexer; >+ >+use C4::Biblio; >+use C4::Items; >+use MARC::Record; >+use MARC::Field; >+ >+use base 'Koha::BackgroundJob'; >+ >+=head1 NAME >+ >+Koha::BackgroundJob::BatchUpdateItem - Batch update item records >+ >+This is a subclass of Koha::BackgroundJob. >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 job_type >+ >+Define the job type of this job: batch_item_record_modification >+ >+=cut >+ >+sub job_type { >+ return 'batch_item_record_modification'; >+} >+ >+=head3 process >+ >+Process the modification. >+ >+=cut >+ >+sub process { >+ my ( $self, $args ) = @_; >+ >+ my $job = Koha::BackgroundJobs->find( $args->{job_id} ); >+ >+ if ( !exists $args->{job_id} || !$job || $job->status eq 'cancelled' ) { >+ return; >+ } >+ >+ # FIXME If the job has already been started, but started again (worker has been restart for instance) >+ # Then we will start from scratch and so double process the same records >+ >+ my $job_progress = 0; >+ $job->started_on(dt_from_string) >+ ->progress($job_progress) >+ ->status('started') >+ ->store; >+ >+ my @record_ids = @{ $args->{record_ids} }; >+ >+ my $report = { >+ total_records => scalar @record_ids, >+ total_success => 0, >+ }; >+ my @messages; >+ >+ my $tags = $args->{tags}; >+ my $subfields = $args->{subfield}; >+ my $values = $args->{values}; >+ my $indicator = $args->{indicator}; >+ my $ind_tag = $args->{ind_tag}; >+ my $searches = $args->{regex_search}; >+ my $replaces = $args->{regex_replace}; >+ my $modifiers = $args->{regex_modifiers}; >+ my $disabled = $args->{disabled}; >+ my $exclude_from_local_holds_priority = $args->{exclude_from_local_holds_priority}; >+ >+ # Is there something to modify ? >+ # TODO : We shall use this var to warn the user in case no modification was done to the items >+ my $values_to_modify = scalar(grep {!/^$/} @$values) || scalar(grep {!/^$/} @$searches); >+ my $values_to_blank = scalar(@$disabled); >+ >+ >+ #initializing values for updates >+ my ( $itemtagfield, $itemtagsubfield ) = C4::Biblio::GetMarcFromKohaField("items.itemnumber"); >+ my $marcitem; >+ if ($values_to_modify) { >+ my $xml = >+ C4::Biblio::TransformHtmlToXml( $tags, $subfields, $values, $indicator, >+ $ind_tag, 'ITEM' ); >+ $marcitem = MARC::Record::new_from_xml( $xml, 'UTF-8' ); >+ } >+ if ($values_to_blank) { >+ foreach my $disabledsubf (@$disabled) { >+ if ( $marcitem && $marcitem->field($itemtagfield) ) { >+ $marcitem->field($itemtagfield)->update( $disabledsubf => "" ); >+ } >+ else { >+ $marcitem = MARC::Record->new(); >+ $marcitem->append_fields( >+ MARC::Field->new( >+ $itemtagfield, '', '', $disabledsubf => "" >+ ) >+ ); >+ } >+ } >+ } >+ >+ my $upd_biblionumbers; >+ RECORD_IDS: for my $itemnumber ( sort { $a <=> $b } @record_ids ) { >+ >+ last if $job->get_from_storage->status eq 'cancelled'; >+ >+ try { >+ my $schema = Koha::Database->new->schema; >+ $schema->txn_do( >+ sub { >+ my $item = Koha::Items->find($itemnumber); >+ next unless $item; >+ >+ my $modified_holds_priority = 0; >+ if ( defined $exclude_from_local_holds_priority >+ && $exclude_from_local_holds_priority ne "" ) >+ { >+ if ( !defined $item->exclude_from_local_holds_priority >+ || $item->exclude_from_local_holds_priority != >+ $exclude_from_local_holds_priority ) >+ { >+ $item->exclude_from_local_holds_priority( >+ $exclude_from_local_holds_priority)->store; >+ $modified_holds_priority = 1; >+ } >+ } >+ my $modified = 0; >+ if ( $values_to_modify || $values_to_blank ) { >+ my $localmarcitem = C4::Items::Item2Marc($item->unblessed); >+ >+ for ( my $i = 0 ; $i < @$tags ; $i++ ) { >+ my $search = $searches->[$i]; >+ next unless $search; >+ >+ my $tag = $tags->[$i]; >+ my $subfield = $subfields->[$i]; >+ my $replace = $replaces->[$i]; >+ >+ my $value = $localmarcitem->field( $tag )->subfield( $subfield ); >+ my $old_value = $value; >+ >+ my @available_modifiers = qw( i g ); >+ my $retained_modifiers = q||; >+ for my $modifier ( split //, $modifiers->[$i] ) { >+ $retained_modifiers .= $modifier >+ if grep {/$modifier/} @available_modifiers; >+ } >+ if ( $retained_modifiers =~ m/^(ig|gi)$/ ) { >+ $value =~ s/$search/$replace/ig; >+ } >+ elsif ( $retained_modifiers eq 'i' ) { >+ $value =~ s/$search/$replace/i; >+ } >+ elsif ( $retained_modifiers eq 'g' ) { >+ $value =~ s/$search/$replace/g; >+ } >+ else { >+ $value =~ s/$search/$replace/; >+ } >+ >+ my @fields_to = $localmarcitem->field($tag); >+ foreach my $field_to_update ( @fields_to ) { >+ unless ( $old_value eq $value ) { >+ $modified++; >+ $field_to_update->update( $subfield => $value ); >+ } >+ } >+ } >+ >+ $modified += UpdateMarcWith( $marcitem, $localmarcitem ); >+ if ($modified) { >+ eval { >+ if ( >+ my $item = ModItemFromMarc( >+ $localmarcitem, >+ $item->biblionumber, >+ $itemnumber, >+ { skip_record_index => 1 }, >+ ) >+ ) >+ { >+ LostItem( >+ $itemnumber, >+ 'batchmod', >+ undef, >+ { skip_record_index => 1 } >+ ) if $item->{itemlost} >+ and not $item->itemlost; >+ } >+ }; >+ push @$upd_biblionumbers, $item->biblionumber; >+ } >+ } >+ } >+ ); >+ >+ $report->{modified_items}++ if $modified || $modified_holds_priority; >+ $report->{modified_fields} += $modified + $modified_holds_priority; >+ >+ push @messages, { >+ type => 'success', >+ code => 'item_modified', >+ itemnumber => $item->itemnumber, >+ biblionumber => $item->biblionumber, >+ }; >+ $report->{total_success}++; >+ >+ } catch { >+ warn $_; >+ push @messages, { >+ type => 'error', >+ code => 'item_not_modified', >+ itemnumber => $item->itemnumber, >+ biblionumber => $item->biblionumber, >+ }; >+ >+ }; >+ >+ $job->progress( ++$job_progress )->store; >+ } >+ >+ $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once >+ my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); >+ $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; >+ >+ my $job_data = decode_json $job->data; >+ $job_data->{messages} = \@messages; >+ $job_data->{report} = $report; >+ >+ $job->ended_on(dt_from_string) >+ ->data(encode_json $job_data); >+ $job->status('finished') if $job->status ne 'cancelled'; >+ $job->store; >+} >+ >+=head3 enqueue >+ >+Enqueue the new job >+ >+=cut >+ >+sub enqueue { >+ my ( $self, $args) = @_; >+ >+ # TODO Raise exception instead >+ return unless exists $args->{record_ids}; >+ >+ my @record_ids = @{ $args->{record_ids} }; >+ >+ $self->SUPER::enqueue({ >+ job_size => scalar @record_ids, >+ job_args => {%$args}, >+ }); >+} >+ >+#BE WARN : it is not the general case >+# This function can be OK in the item marc record special case >+# Where subfield is not repeated >+# And where we are sure that field should correspond >+# And $tag>10 >+sub UpdateMarcWith { >+ my ($marcfrom,$marcto)=@_; >+ my ( $itemtag, $itemtagsubfield) = C4::Biblio::GetMarcFromKohaField( "items.itemnumber" ); >+ my $fieldfrom=$marcfrom->field($itemtag); >+ my @fields_to=$marcto->field($itemtag); >+ my $modified = 0; >+ >+ return $modified unless $fieldfrom; >+ >+ foreach my $subfield ( $fieldfrom->subfields() ) { >+ foreach my $field_to_update ( @fields_to ) { >+ if ( $subfield->[1] ) { >+ unless ( $field_to_update->subfield($subfield->[0]) eq $subfield->[1] ) { >+ $modified++; >+ $field_to_update->update( $subfield->[0] => $subfield->[1] ); >+ } >+ } >+ else { >+ $modified++; >+ $field_to_update->delete_subfield( code => $subfield->[0] ); >+ } >+ } >+ } >+ return $modified; >+} >+ >+1; >diff --git a/debian/templates/apache-shared-intranet-plack.conf b/debian/templates/apache-shared-intranet-plack.conf >index 029eca8d537..311b5d7f37d 100644 >--- a/debian/templates/apache-shared-intranet-plack.conf >+++ b/debian/templates/apache-shared-intranet-plack.conf >@@ -13,7 +13,6 @@ > #Â don't break under plack/starman > ProxyPass "/cgi-bin/koha/offline_circ/process_koc.pl" "!" > ProxyPass "/cgi-bin/koha/tools/background-job-progress.pl" "!" >- ProxyPass "/cgi-bin/koha/tools/batchMod.pl" "!" > ProxyPass "/cgi-bin/koha/tools/export.pl" "!" > ProxyPass "/cgi-bin/koha/tools/manage-marc-import.pl" "!" > ProxyPass "/cgi-bin/koha/tools/stage-marc-import.pl" "!" >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc >new file mode 100644 >index 00000000000..51a447e3b2c >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/background_jobs/batch_item_record_modification.inc >@@ -0,0 +1,64 @@ >+[% BLOCK report %] >+ [% SET report = job.report %] >+ [% IF report %] >+ [% IF report.total_records == report.total_success %] >+ <div class="dialog message"> >+ All records have successfully been modified! <a href="/cgi-bin/koha/tools/batch_record_modification.pl" title="New batch record modification">New batch record modification</a> >+ [% IF lists.count %] >+ <br /> >+ Add modified records to the following list: >+ <select name="add_bibs_to_list" id="add_bibs_to_list"> >+ <option value="">Select a list</option> >+ [% FOREACH list IN lists %] >+ <option class="shelf" value="[% list.shelfnumber | html %]">[% list.shelfname | html %]</option> >+ [% END %] >+ </select> >+ [% END %] >+ </div> >+ [% ELSE %] >+ <div class="dialog message"> >+ [% report.total_success | html %] / [% report.total_records | html %] records have successfully been modified. Some errors occurred. >+ [% IF job.status == 'cancelled' %]The job has been cancelled before it finished.[% END %] >+ <a href="/cgi-bin/koha/tools/batch_record_modification.pl" title="New batch record modification">New batch record modification</a> >+ </div> >+ [% END %] >+ [% END %] >+[% END %] >+ >+[% BLOCK detail %] >+ [% FOR m IN job.messages %] >+ <div class="dialog message"> >+ [% IF m.type == 'success' %] >+ <i class="fa fa-check success"></i> >+ [% ELSIF m.type == 'warning' %] >+ <i class="fa fa-warning warn"></i> >+ [% ELSIF m.type == 'error' %] >+ <i class="fa fa-exclamation error"></i> >+ [% END %] >+ [% SWITCH m.code %] >+ [% CASE 'biblio_not_modified' %] >+ Bibliographic record <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% m.biblionumber | uri %]">[% m.biblionumber | html %]</a> has not been modified. An error occurred on modifying it.[% IF m.error %] ([% m.error | html %])[% END %]. >+ [% CASE 'biblio_modified' %] >+ Bibliographic record <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% m.biblionumber | uri %]">[% m.biblionumber | html %]</a> has successfully been modified. >+ [% END %] >+ </div> >+ [% END %] >+[% END %] >+ >+[% BLOCK js %] >+ $("#add_bibs_to_list").change(function(){ >+ var selected = $("#add_bibs_to_list").find("option:selected"); >+ if ( selected.attr("class") == "shelf" ){ >+ var shelfnumber = selected.attr("value"); >+ var bibs = new Array(); >+ [% FOREACH message IN job.messages %] >+ [% IF message.code == 'biblio_modified' %] >+ bibs.push("biblionumber="+[% message.biblionumber | html %]); >+ [% END %] >+ [% END %] >+ var bibstring = bibs.join("&"); >+ window.open('/cgi-bin/koha/virtualshelves/addbybiblionumber.pl?shelfnumber='+shelfnumber+'&confirm=1&'+bibstring, 'popup', 'width=500,height=500,toolbar=false,scrollbars=yes,resizeable=yes'); >+ return false; >+ } >+ }); >+[% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt >index e07e44b2280..5ac6499632c 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batchMod-edit.tt >@@ -31,16 +31,15 @@ > > <div class="main container-fluid"> > >- [% IF ( show ) %] >- <h1>Batch item modification</h1> >- [% ELSE %] >- <h1>Batch item modification results</h1> >+ <h1>Batch item modification</h1> >+ [% IF op == 'enqueued' %] > <div class="dialog message"> >- [% IF (modified_items) %] >- [% modified_items | html %] item(s) modified (with [% modified_fields | html %] field(s) modified). >- [% ELSE %] >- No items modified. >- [% END %] >+ <p>The job has been enqueued! It will be processed as soon as possible.</p> >+ <p><a href="/cgi-bin/koha/admin/background_jobs.pl?op=view&id=[% job_id | uri %]" title="View detail of the enqueued job">View detail of the enqueued job</a> >+ | <a href="/cgi-bin/koha/tools/batchMod.pl" title="New batch item modification">New batch item modification</a></p> >+ </div> >+ [% END %] >+ > <fieldset class="action"> > [% IF src == 'CATALOGUING' # from catalogue/detail.pl > Edit items in a batch%] > [% IF searchid %] >@@ -55,7 +54,24 @@ > [% END %] > </fieldset> > </div> <!-- /.dialog.message --> >- [% END # /IF show %] >+ >+ [% FOREACH message IN messages %] >+ [% IF message.type == 'success' %] >+ <div class="dialog message"> >+ [% ELSIF message.type == 'warning' %] >+ <div class="dialog alert"> >+ [% ELSIF message.type == 'error' %] >+ <div class="dialog alert" style="margin:auto;"> >+ [% END %] >+ [% IF message.code == 'cannot_enqueue_job' %] >+ Cannot enqueue this job. >+ [% END %] >+ [% IF message.error %] >+ (The error was: [% message.error | html %], see the Koha log file for more information). >+ [% END %] >+ </div> >+ [% END %] >+ > > [% IF ( barcode_not_unique ) %] > <div class="dialog alert"> >@@ -151,6 +167,8 @@ > [% END %] > </p> <!-- /#selections --> > >+[#% TODO We need to refactor the pl to make this reusable from batchMod and BatchUpdateItem %] >+ [% PROCESS item_table headers => item_header_loop, items => item_loop %] > <table id="itemst"> > <thead> > <tr> >@@ -359,7 +377,7 @@ > <a class="btn btn-default" href="/cgi-bin/koha/tools/batchMod.pl"><i class="fa fa-check-square-o"></i> Return to batch item modification</a> > [% END %] > </fieldset> <!-- /.action --> >- [% END #/IF show %] >+ [% END %] > </form> > > [% MACRO jsinclude BLOCK %] >diff --git a/misc/background_jobs_worker.pl b/misc/background_jobs_worker.pl >index 0642faf71bb..bde1e8e473f 100755 >--- a/misc/background_jobs_worker.pl >+++ b/misc/background_jobs_worker.pl >@@ -31,8 +31,10 @@ try { > my @job_types = qw( > batch_biblio_record_modification > batch_authority_record_modification >+ batch_item_record_modification > batch_biblio_record_deletion > batch_authority_record_deletion >+ batch_item_record_deletion > ); > > if ( $conn ) { >diff --git a/tools/batchMod.pl b/tools/batchMod.pl >index fa4a86ec967..8f5c27fcb4e 100755 >--- a/tools/batchMod.pl >+++ b/tools/batchMod.pl >@@ -45,6 +45,8 @@ use Koha::Items; > use Koha::ItemTypes; > use Koha::Patrons; > use Koha::SearchEngine::Indexer; >+use Koha::BackgroundJob::BatchDeleteItem; >+use Koha::BackgroundJob::BatchUpdateItem; > > my $input = CGI->new; > my $dbh = C4::Context->dbh; >@@ -102,74 +104,13 @@ my $modified_fields = 0; # Numbers of modified fields > my %cookies = parse CGI::Cookie($cookie); > my $sessionID = $cookies{'CGISESSID'}->value; > >+my @messages; > > #--- ---------------------------------------------------------------------------- > if ($op eq "action") { > #------------------------------------------------------------------------------- >- my @tags = $input->multi_param('tag'); >- my @subfields = $input->multi_param('subfield'); >- my @values = $input->multi_param('field_value'); >- my @searches = $input->multi_param('regex_search'); >- my @replaces = $input->multi_param('regex_replace'); >- my @modifiers = $input->multi_param('regex_modifiers'); >- my @disabled = $input->multi_param('disable_input'); >- # build indicator hash. >- my @ind_tag = $input->multi_param('ind_tag'); >- my @indicator = $input->multi_param('indicator'); >- >- # Is there something to modify ? >- # TODO : We shall use this var to warn the user in case no modification was done to the items >- my $values_to_modify = scalar(grep {!/^$/} @values) || scalar(grep {!/^$/} @searches); >- my $values_to_blank = scalar(@disabled); >- >- my $marcitem; >- >- # Once the job is done >- if ($completedJobID) { >- # If we have a reasonable amount of items, we display them >- my $max_items = $del ? C4::Context->preference("MaxItemsToDisplayForBatchDel") : C4::Context->preference("MaxItemsToDisplayForBatchMod"); >- if (scalar(@itemnumbers) <= $max_items ){ >- if (scalar(@itemnumbers) <= 1000 ) { >- $items_display_hashref=BuildItemsData(@itemnumbers); >- } else { >- # Else, we only display the barcode >- my @simple_items_display = map { >- my $itemnumber = $_; >- my $item = Koha::Items->find($itemnumber); >- { >- itemnumber => $itemnumber, >- barcode => $item ? ( $item->barcode // q{} ) : q{}, >- biblionumber => $item ? $item->biblio->biblionumber : q{}, >- }; >- } @itemnumbers; >- $template->param("simple_items_display" => \@simple_items_display); >- } >- } else { >- $template->param( "too_many_items_display" => scalar(@itemnumbers) ); >- $template->param( "job_completed" => 1 ); >- } >- >- } else { >- # While the job is getting done >- >- #initializing values for updates >- my ( $itemtagfield, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); >- if ($values_to_modify){ >- my $xml = TransformHtmlToXml(\@tags,\@subfields,\@values,\@indicator,\@ind_tag, 'ITEM'); >- $marcitem = MARC::Record::new_from_xml($xml, 'UTF-8'); >- } >- if ($values_to_blank){ >- foreach my $disabledsubf (@disabled){ >- if ($marcitem && $marcitem->field($itemtagfield)){ >- $marcitem->field($itemtagfield)->update( $disabledsubf => "" ); >- } >- else { >- $marcitem = MARC::Record->new(); >- $marcitem->append_fields( MARC::Field->new( $itemtagfield, '', '', $disabledsubf => "" ) ); >- } >- } >- } > >+ if ( $del ) { > my $upd_biblionumbers; > my $del_biblionumbers; > try { >@@ -218,84 +159,6 @@ if ($op eq "action") { > } > } > } >- else { >- my $modified_holds_priority = 0; >- if ( defined $exclude_from_local_holds_priority && $exclude_from_local_holds_priority ne "" ) { >- if(!defined $item->exclude_from_local_holds_priority || $item->exclude_from_local_holds_priority != $exclude_from_local_holds_priority) { >- $item->exclude_from_local_holds_priority($exclude_from_local_holds_priority)->store; >- $modified_holds_priority = 1; >- } >- } >- my $modified = 0; >- if ( $values_to_modify || $values_to_blank ) { >- my $localmarcitem = Item2Marc($itemdata); >- >- for ( my $i = 0 ; $i < @tags ; $i++ ) { >- my $search = $searches[$i]; >- next unless $search; >- >- my $tag = $tags[$i]; >- my $subfield = $subfields[$i]; >- my $replace = $replaces[$i]; >- >- my $value = $localmarcitem->field( $tag )->subfield( $subfield ); >- my $old_value = $value; >- >- my @available_modifiers = qw( i g ); >- my $retained_modifiers = q||; >- for my $modifier ( split //, $modifiers[$i] ) { >- $retained_modifiers .= $modifier >- if grep {/$modifier/} @available_modifiers; >- } >- if ( $retained_modifiers =~ m/^(ig|gi)$/ ) { >- $value =~ s/$search/$replace/ig; >- } >- elsif ( $retained_modifiers eq 'i' ) { >- $value =~ s/$search/$replace/i; >- } >- elsif ( $retained_modifiers eq 'g' ) { >- $value =~ s/$search/$replace/g; >- } >- else { >- $value =~ s/$search/$replace/; >- } >- >- my @fields_to = $localmarcitem->field($tag); >- foreach my $field_to_update ( @fields_to ) { >- unless ( $old_value eq $value ) { >- $modified++; >- $field_to_update->update( $subfield => $value ); >- } >- } >- } >- >- $modified += UpdateMarcWith( $marcitem, $localmarcitem ); >- if ($modified) { >- eval { >- if ( >- my $item = ModItemFromMarc( >- $localmarcitem, >- $itemdata->{biblionumber}, >- $itemnumber, >- { skip_record_index => 1 }, >- ) >- ) >- { >- LostItem( >- $itemnumber, >- 'batchmod', >- undef, >- { skip_record_index => 1 } >- ) if $item->{itemlost} >- and not $itemdata->{itemlost}; >- } >- }; >- push @$upd_biblionumbers, $itemdata->{'biblionumber'}; >- } >- } >- $modified_items++ if $modified || $modified_holds_priority; >- $modified_fields += $modified + $modified_holds_priority; >- } > $i++; > } > if (@not_deleted) { >@@ -321,15 +184,40 @@ if ($op eq "action") { > my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX }); > $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers; > $indexer->index_records( $del_biblionumbers, 'recordDelete', "biblioserver", undef ) if @$del_biblionumbers; >+ } else { >+ my $params = { >+ tags => [ $input->multi_param('tag') ], >+ subfields => [ $input->multi_param('subfield') ], >+ values => [ $input->multi_param('field_value') ], >+ searches => [ $input->multi_param('regex_search') ], >+ replaces => [ $input->multi_param('regex_replace') ], >+ modifiers => [ $input->multi_param('regex_modifiers') ], >+ disabled => [ $input->multi_param('disable_input') ], >+ ind_tag => [ $input->multi_param('ind_tag') ], >+ indicator => [ $input->multi_param('indicator') ], >+ record_ids => \@itemnumbers, >+ }; >+ try { >+ my $job_id = Koha::BackgroundJob::BatchUpdateItem->new->enqueue($params); >+ $nextop = 'enqueued'; >+ $template->param( >+ job_id => $job_id, >+ ); >+ } catch { >+ push @messages, { >+ type => 'error', >+ code => 'cannot_enqueue_job', >+ error => $_, >+ }; >+ $template->param( view => 'errors' ); >+ }; > } > >- # Calling the template >- $template->param( >- modified_items => $modified_items, >- modified_fields => $modified_fields, >- ); >- > } >+ >+$template->param( >+ messages => \@messages, >+); > # > #------------------------------------------------------------------------------- > # build screen with existing items. and "new" one >@@ -719,50 +607,3 @@ sub BuildItemsData{ > > return { item_loop => \@item_value_loop, item_header_loop => \@header_loop }; > } >- >-#BE WARN : it is not the general case >-# This function can be OK in the item marc record special case >-# Where subfield is not repeated >-# And where we are sure that field should correspond >-# And $tag>10 >-sub UpdateMarcWith { >- my ($marcfrom,$marcto)=@_; >- my ( $itemtag, $itemtagsubfield) = &GetMarcFromKohaField( "items.itemnumber" ); >- my $fieldfrom=$marcfrom->field($itemtag); >- my @fields_to=$marcto->field($itemtag); >- my $modified = 0; >- >- return $modified unless $fieldfrom; >- >- foreach my $subfield ( $fieldfrom->subfields() ) { >- foreach my $field_to_update ( @fields_to ) { >- if ( $subfield->[1] ) { >- unless ( $field_to_update->subfield($subfield->[0]) eq $subfield->[1] ) { >- $modified++; >- $field_to_update->update( $subfield->[0] => $subfield->[1] ); >- } >- } >- else { >- $modified++; >- $field_to_update->delete_subfield( code => $subfield->[0] ); >- } >- } >- } >- return $modified; >-} >- >-sub find_value { >- my ($tagfield,$insubfield,$record) = @_; >- my $result; >- my $indicator; >- foreach my $field ($record->field($tagfield)) { >- my @subfields = $field->subfields(); >- foreach my $subfield (@subfields) { >- if (@$subfield[0] eq $insubfield) { >- $result .= @$subfield[1]; >- $indicator = $field->indicator(1).$field->indicator(2); >- } >- } >- } >- return($indicator,$result); >-} >-- >2.20.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 28445
:
121372
|
123221
|
123222
|
123223
|
123224
|
123225
|
123226
|
123227
|
123228
|
123229
|
123239
|
123240
|
125801
|
125802
|
125803
|
125804
|
125805
|
125808
|
125810
|
125811
|
125812
|
125817
|
125865
|
126046
|
126047
|
126136
|
126137
|
126138
|
126139
|
126140
|
126141
|
126142
|
126217
|
126218
|
126219
|
126223
|
126328
|
126329
|
126330
|
126331
|
126332
|
126333
|
126334
|
126335
|
126336
|
126337
|
126338
|
126339
|
126352
|
126353
|
126354
|
126355
|
126356
|
126357
|
126358
|
126359
|
126360
|
126361
|
126362
|
126363
|
126364
|
126365
|
126366
|
126367
|
126374
|
126375
|
126376
|
126377
|
126378
|
126379
|
126380
|
126381
|
126382
|
126383
|
126384
|
126385
|
126386
|
126387
|
126388
|
126389
|
127033
|
127037
|
127038
|
127039
|
128007
|
128008
|
128009