1 package C4::ImportBatch;
3 # Copyright (C) 2007 LibLime, 2012 C & P Bibliography Services
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
24 use C4::Koha qw( GetNormalizedISBN );
33 use C4::Items qw( AddItemFromMarc ModItemFromMarc );
34 use C4::Charset qw( MarcToUTF8Record SetUTF8Flag StripNonXmlChars );
35 use C4::AuthoritiesMarc qw( AddAuthority GuessAuthTypeCode GetAuthorityXML ModAuthority DelAuthority );
36 use C4::MarcModificationTemplates qw( ModifyRecordWithTemplate );
38 use Koha::SearchEngine;
39 use Koha::SearchEngine::Indexer;
40 use Koha::Plugins::Handler;
43 our (@ISA, @EXPORT_OK);
55 AddItemsToImportBiblio
66 GetStagedWebserviceBatches
67 GetImportBatchRangeDesc
68 GetNumberOfNonZ3950ImportBatches
71 GetItemNumbersFromImportBatch
75 GetImportBatchOverlayAction
76 SetImportBatchOverlayAction
77 GetImportBatchNoMatchAction
78 SetImportBatchNoMatchAction
79 GetImportBatchItemAction
80 SetImportBatchItemAction
83 GetImportRecordOverlayStatus
84 SetImportRecordOverlayStatus
87 SetMatchedBiblionumber
88 GetImportRecordMatches
89 SetImportRecordMatches
91 RecordsFromMARCXMLFile
92 RecordsFromISO2709File
99 C4::ImportBatch - manage batches of imported MARC records
107 =head2 GetZ3950BatchId
109 my $batchid = GetZ3950BatchId($z3950server);
111 Retrieves the ID of the import batch for the Z39.50
112 reservoir for the given target. If necessary,
113 creates the import batch.
117 sub GetZ3950BatchId {
118 my ($z3950server) = @_;
120 my $dbh = C4::Context->dbh;
121 my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
122 WHERE batch_type = 'z3950'
124 $sth->execute($z3950server);
125 my $rowref = $sth->fetchrow_arrayref();
127 if (defined $rowref) {
130 my $batch_id = AddImportBatch( {
131 overlay_action => 'create_new',
132 import_status => 'staged',
133 batch_type => 'z3950',
134 file_name => $z3950server,
141 =head2 GetWebserviceBatchId
143 my $batchid = GetWebserviceBatchId();
145 Retrieves the ID of the import batch for webservice.
146 If necessary, creates the import batch.
150 my $WEBSERVICE_BASE_QRY = <<EOQ;
151 SELECT import_batch_id FROM import_batches
152 WHERE batch_type = 'webservice'
153 AND import_status = 'staged'
155 sub GetWebserviceBatchId {
158 my $dbh = C4::Context->dbh;
159 my $sql = $WEBSERVICE_BASE_QRY;
161 foreach my $field (qw(matcher_id overlay_action nomatch_action item_action)) {
162 if (my $val = $params->{$field}) {
163 $sql .= " AND $field = ?";
167 my $id = $dbh->selectrow_array($sql, undef, @args);
170 $params->{batch_type} = 'webservice';
171 $params->{import_status} = 'staged';
172 return AddImportBatch($params);
175 =head2 GetImportRecordMarc
177 my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
181 sub GetImportRecordMarc {
182 my ($import_record_id) = @_;
184 my $dbh = C4::Context->dbh;
185 my ( $marc, $encoding ) = $dbh->selectrow_array(q|
186 SELECT marc, encoding
188 WHERE import_record_id = ?
189 |, undef, $import_record_id );
191 return $marc, $encoding;
194 sub EmbedItemsInImportBiblio {
195 my ( $record, $import_record_id ) = @_;
196 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
197 my $dbh = C4::Context->dbh;
198 my $import_items = $dbh->selectall_arrayref(q|
199 SELECT import_items.marcxml
201 WHERE import_record_id = ?
202 |, { Slice => {} }, $import_record_id );
204 for my $import_item ( @$import_items ) {
205 my $item_marc = MARC::Record::new_from_xml($import_item->{marcxml}, 'UTF-8');
206 push @item_fields, $item_marc->field($itemtag);
208 $record->append_fields(@item_fields);
212 =head2 AddImportBatch
214 my $batch_id = AddImportBatch($params_hash);
222 foreach (qw( matcher_id template_id branchcode
223 overlay_action nomatch_action item_action
224 import_status batch_type file_name comments record_type )) {
225 if (exists $params->{$_}) {
227 push @vals, $params->{$_};
230 my $dbh = C4::Context->dbh;
231 $dbh->do("INSERT INTO import_batches (".join( ',', @fields).")
232 VALUES (".join( ',', map '?', @fields).")",
235 return $dbh->{'mysql_insertid'};
238 =head2 GetImportBatch
240 my $row = GetImportBatch($batch_id);
242 Retrieve a hashref of an import_batches row.
249 my $dbh = C4::Context->dbh;
250 my $sth = $dbh->prepare_cached("SELECT b.*, p.name as profile FROM import_batches b LEFT JOIN import_batch_profiles p ON p.id = b.profile_id WHERE import_batch_id = ?");
251 $sth->bind_param(1, $batch_id);
253 my $result = $sth->fetchrow_hashref;
259 =head2 AddBiblioToBatch
261 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence,
262 $marc_record, $encoding, $update_counts);
266 sub AddBiblioToBatch {
267 my $batch_id = shift;
268 my $record_sequence = shift;
269 my $marc_record = shift;
270 my $encoding = shift;
271 my $update_counts = @_ ? shift : 1;
273 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, C4::Context->preference('marcflavour'));
274 _add_biblio_fields($import_record_id, $marc_record);
275 _update_batch_record_counts($batch_id) if $update_counts;
276 return $import_record_id;
279 =head2 AddAuthToBatch
281 my $import_record_id = AddAuthToBatch($batch_id, $record_sequence,
282 $marc_record, $encoding, $update_counts, [$marc_type]);
287 my $batch_id = shift;
288 my $record_sequence = shift;
289 my $marc_record = shift;
290 my $encoding = shift;
291 my $update_counts = @_ ? shift : 1;
292 my $marc_type = shift || C4::Context->preference('marcflavour');
294 $marc_type = 'UNIMARCAUTH' if $marc_type eq 'UNIMARC';
296 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'auth', $encoding, $marc_type);
297 _add_auth_fields($import_record_id, $marc_record);
298 _update_batch_record_counts($batch_id) if $update_counts;
299 return $import_record_id;
302 =head2 BatchStageMarcRecords
304 ( $batch_id, $num_records, $num_items, @invalid_records ) =
305 BatchStageMarcRecords(
306 $record_type, $encoding,
307 $marc_records, $file_name,
308 $marc_modification_template, $comments,
309 $branch_code, $parse_items,
310 $leave_as_staging, $progress_interval,
316 sub BatchStageMarcRecords {
317 my $record_type = shift;
318 my $encoding = shift;
319 my $marc_records = shift;
320 my $file_name = shift;
321 my $marc_modification_template = shift;
322 my $comments = shift;
323 my $branch_code = shift;
324 my $parse_items = shift;
325 my $leave_as_staging = shift;
327 # optional callback to monitor status
329 my $progress_interval = 0;
330 my $progress_callback = undef;
332 $progress_interval = shift;
333 $progress_callback = shift;
334 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
335 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
338 my $batch_id = AddImportBatch( {
339 overlay_action => 'create_new',
340 import_status => 'staging',
341 batch_type => 'batch',
342 file_name => $file_name,
343 comments => $comments,
344 record_type => $record_type,
347 SetImportBatchItemAction($batch_id, 'always_add');
349 SetImportBatchItemAction($batch_id, 'ignore');
353 my $marc_type = C4::Context->preference('marcflavour');
354 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
355 my @invalid_records = ();
358 # FIXME - for now, we're dealing only with bibs
360 foreach my $marc_record (@$marc_records) {
362 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
363 &$progress_callback($rec_num);
366 ModifyRecordWithTemplate( $marc_modification_template, $marc_record ) if ( $marc_modification_template );
368 my $import_record_id;
369 if (scalar($marc_record->fields()) == 0) {
370 push @invalid_records, $marc_record;
373 # Normalize the record so it doesn't have separated diacritics
374 SetUTF8Flag($marc_record);
377 if ($record_type eq 'biblio') {
378 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, 0);
380 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
381 $num_items += scalar(@import_items_ids);
383 } elsif ($record_type eq 'auth') {
384 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, 0, $marc_type);
388 unless ($leave_as_staging) {
389 SetImportBatchStatus($batch_id, 'staged');
391 # FIXME branch_code, number of bibs, number of items
392 _update_batch_record_counts($batch_id);
393 return ($batch_id, $num_valid, $num_items, @invalid_records);
396 =head2 AddItemsToImportBiblio
398 my @import_items_ids = AddItemsToImportBiblio($batch_id,
399 $import_record_id, $marc_record, $update_counts);
403 sub AddItemsToImportBiblio {
404 my $batch_id = shift;
405 my $import_record_id = shift;
406 my $marc_record = shift;
407 my $update_counts = @_ ? shift : 0;
409 my @import_items_ids = ();
411 my $dbh = C4::Context->dbh;
412 my ($item_tag,$item_subfield) = &GetMarcFromKohaField( "items.itemnumber" );
413 foreach my $item_field ($marc_record->field($item_tag)) {
414 my $item_marc = MARC::Record->new();
415 $item_marc->leader("00000 a "); # must set Leader/09 to 'a'
416 $item_marc->append_fields($item_field);
417 $marc_record->delete_field($item_field);
418 my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
420 $sth->bind_param(1, $import_record_id);
421 $sth->bind_param(2, 'staged');
422 $sth->bind_param(3, $item_marc->as_xml("USMARC"));
424 push @import_items_ids, $dbh->{'mysql_insertid'};
428 if ($#import_items_ids > -1) {
429 _update_batch_record_counts($batch_id) if $update_counts;
431 return @import_items_ids;
434 =head2 BatchFindDuplicates
436 my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
437 $max_matches, $progress_interval, $progress_callback);
439 Goes through the records loaded in the batch and attempts to
440 find duplicates for each one. Sets the matching status
441 of each record to "no_match" or "auto_match" as appropriate.
443 The $max_matches parameter is optional; if it is not supplied,
446 The $progress_interval and $progress_callback parameters are
447 optional; if both are supplied, the sub referred to by
448 $progress_callback will be invoked every $progress_interval
449 records using the number of records processed as the
454 sub BatchFindDuplicates {
455 my $batch_id = shift;
457 my $max_matches = @_ ? shift : 10;
459 # optional callback to monitor status
461 my $progress_interval = 0;
462 my $progress_callback = undef;
464 $progress_interval = shift;
465 $progress_callback = shift;
466 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
467 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
470 my $dbh = C4::Context->dbh;
472 my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
474 WHERE import_batch_id = ?");
475 $sth->execute($batch_id);
476 my $num_with_matches = 0;
478 while (my $rowref = $sth->fetchrow_hashref) {
480 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
481 &$progress_callback($rec_num);
483 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
485 if (defined $matcher) {
486 @matches = $matcher->get_matches($marc_record, $max_matches);
488 if (scalar(@matches) > 0) {
490 SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
491 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
493 SetImportRecordMatches($rowref->{'import_record_id'}, ());
494 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
498 return $num_with_matches;
501 =head2 BatchCommitRecords
503 my ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored) =
504 BatchCommitRecords($batch_id, $framework,
505 $progress_interval, $progress_callback);
509 sub BatchCommitRecords {
510 my $batch_id = shift;
511 my $framework = shift;
513 my $schema = Koha::Database->schema;
515 # optional callback to monitor status
517 my $progress_interval = 0;
518 my $progress_callback = undef;
520 $progress_interval = shift;
521 $progress_callback = shift;
522 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
523 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
529 my $num_items_added = 0;
530 my $num_items_replaced = 0;
531 my $num_items_errored = 0;
533 # commit (i.e., save, all records in the batch)
534 SetImportBatchStatus($batch_id, 'importing');
535 my $overlay_action = GetImportBatchOverlayAction($batch_id);
536 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
537 my $item_action = GetImportBatchItemAction($batch_id);
540 my $dbh = C4::Context->dbh;
541 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
543 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
544 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
545 WHERE import_batch_id = ?");
546 $sth->execute($batch_id);
547 my $marcflavour = C4::Context->preference('marcflavour');
549 my $userenv = C4::Context->userenv;
550 my $logged_in_patron = Koha::Patrons->find( $userenv->{number} );
554 $schema->txn_begin; # We commit in a transaction
555 while (my $rowref = $sth->fetchrow_hashref) {
556 $record_type = $rowref->{'record_type'};
560 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
561 # report progress and commit
563 &$progress_callback( $rec_num );
566 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
572 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
573 $marc_type = 'UNIMARCAUTH';
574 } elsif ($marcflavour eq 'UNIMARC') {
575 $marc_type = 'UNIMARC';
577 $marc_type = 'USMARC';
579 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
581 if ($record_type eq 'biblio') {
582 # remove any item tags - rely on _batchCommitItems
583 ($item_tag,$item_subfield) = &GetMarcFromKohaField( "items.itemnumber" );
584 foreach my $item_field ($marc_record->field($item_tag)) {
585 $marc_record->delete_field($item_field);
587 if(C4::Context->preference('autoControlNumber') eq 'biblionumber'){
588 my @control_num = $marc_record->field('001');
589 $marc_record->delete_fields(@control_num);
593 my ($record_result, $item_result, $record_match) =
594 _get_commit_action($overlay_action, $nomatch_action, $item_action,
595 $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
599 if ($record_result eq 'create_new') {
601 if ($record_type eq 'biblio') {
602 my $biblioitemnumber;
603 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework, { skip_record_index => 1 });
604 push @biblio_ids, $recordid;
605 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"; # FIXME call SetMatchedBiblionumber instead
606 if ($item_result eq 'create_new' || $item_result eq 'replace') {
607 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = _batchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result, $biblioitemnumber);
608 $num_items_added += $bib_items_added;
609 $num_items_replaced += $bib_items_replaced;
610 $num_items_errored += $bib_items_errored;
613 $recordid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
614 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
616 my $sth = $dbh->prepare_cached($query);
617 $sth->execute($recordid, $rowref->{'import_record_id'});
619 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
620 } elsif ($record_result eq 'replace') {
622 $recordid = $record_match;
624 if ($record_type eq 'biblio') {
625 my $oldbiblio = Koha::Biblios->find( $recordid );
626 $oldxml = GetXmlBiblio($recordid);
628 # remove item fields so that they don't get
629 # added again if record is reverted
630 # FIXME: GetXmlBiblio output should not contain item info any more! So the next foreach should not be needed. Does not hurt either; may remove old 952s that should not have been there anymore.
631 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
632 foreach my $item_field ($old_marc->field($item_tag)) {
633 $old_marc->delete_field($item_field);
635 $oldxml = $old_marc->as_xml($marc_type);
637 my $context = { source => 'batchimport' };
638 if ($logged_in_patron) {
639 $context->{categorycode} = $logged_in_patron->categorycode;
640 $context->{userid} = $logged_in_patron->userid;
646 $oldbiblio->frameworkcode,
648 overlay_context => $context,
649 skip_record_index => 1
652 push @biblio_ids, $recordid;
653 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"; # FIXME call SetMatchedBiblionumber instead
655 if ($item_result eq 'create_new' || $item_result eq 'replace') {
656 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = _batchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
657 $num_items_added += $bib_items_added;
658 $num_items_replaced += $bib_items_replaced;
659 $num_items_errored += $bib_items_errored;
662 $oldxml = GetAuthorityXML($recordid);
664 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
665 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
667 my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
668 $sth->execute($oldxml, $rowref->{'import_record_id'});
670 my $sth2 = $dbh->prepare_cached($query);
671 $sth2->execute($recordid, $rowref->{'import_record_id'});
673 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
674 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
675 } elsif ($record_result eq 'ignore') {
676 $recordid = $record_match;
678 if ($record_type eq 'biblio' and defined $recordid and ( $item_result eq 'create_new' || $item_result eq 'replace' ) ) {
679 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = _batchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
680 push @biblio_ids, $recordid if $bib_items_added || $bib_items_replaced;
681 $num_items_added += $bib_items_added;
682 $num_items_replaced += $bib_items_replaced;
683 $num_items_errored += $bib_items_errored;
684 # still need to record the matched biblionumber so that the
685 # items can be reverted
686 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"); # FIXME call SetMatchedBiblionumber instead
687 $sth2->execute($recordid, $rowref->{'import_record_id'});
688 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
690 SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
693 $schema->txn_commit; # Commit final records that may not have hit callback threshold
697 my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
698 $indexer->index_records( \@biblio_ids, "specialUpdate", "biblioserver" );
701 SetImportBatchStatus($batch_id, 'imported');
702 return ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored);
705 =head2 _batchCommitItems
707 ($num_items_added, $num_items_errored) =
708 _batchCommitItems($import_record_id, $biblionumber, [$action, $biblioitemnumber]);
710 Private function for batch committing item changes. We do not trigger a re-index here, that is left to the caller.
714 sub _batchCommitItems {
715 my ( $import_record_id, $biblionumber, $action, $biblioitemnumber ) = @_;
717 my $dbh = C4::Context->dbh;
719 my $num_items_added = 0;
720 my $num_items_errored = 0;
721 my $num_items_replaced = 0;
723 my $sth = $dbh->prepare( "
724 SELECT import_items_id, import_items.marcxml, encoding
726 JOIN import_records USING (import_record_id)
727 WHERE import_record_id = ?
728 ORDER BY import_items_id
730 $sth->bind_param( 1, $import_record_id );
733 while ( my $row = $sth->fetchrow_hashref() ) {
734 my $item_marc = MARC::Record->new_from_xml( StripNonXmlChars( $row->{'marcxml'} ), 'UTF-8', $row->{'encoding'} );
736 # Delete date_due subfield as to not accidentally delete item checkout due dates
737 my ( $MARCfield, $MARCsubfield ) = GetMarcFromKohaField( 'items.onloan' );
738 $item_marc->field($MARCfield)->delete_subfield( code => $MARCsubfield );
740 my $item = TransformMarcToKoha({ record => $item_marc, kohafields => ['items.barcode','items.itemnumber'] });
742 my $duplicate_barcode = exists( $item->{'barcode'} ) && Koha::Items->find({ barcode => $item->{'barcode'} });
743 my $duplicate_itemnumber = exists( $item->{'itemnumber'} );
745 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ?, import_error = ? WHERE import_items_id = ?");
746 if ( $action eq "replace" && $duplicate_itemnumber ) {
747 # Duplicate itemnumbers have precedence, that way we can update barcodes by overlaying
748 ModItemFromMarc( $item_marc, $biblionumber, $item->{itemnumber}, { skip_record_index => 1 } );
749 $updsth->bind_param( 1, 'imported' );
750 $updsth->bind_param( 2, $item->{itemnumber} );
751 $updsth->bind_param( 3, undef );
752 $updsth->bind_param( 4, $row->{'import_items_id'} );
755 $num_items_replaced++;
756 } elsif ( $action eq "replace" && $duplicate_barcode ) {
757 my $itemnumber = $duplicate_barcode->itemnumber;
758 ModItemFromMarc( $item_marc, $biblionumber, $itemnumber, { skip_record_index => 1 } );
759 $updsth->bind_param( 1, 'imported' );
760 $updsth->bind_param( 2, $item->{itemnumber} );
761 $updsth->bind_param( 3, undef );
762 $updsth->bind_param( 4, $row->{'import_items_id'} );
765 $num_items_replaced++;
766 } elsif ($duplicate_barcode) {
767 $updsth->bind_param( 1, 'error' );
768 $updsth->bind_param( 2, undef );
769 $updsth->bind_param( 3, 'duplicate item barcode' );
770 $updsth->bind_param( 4, $row->{'import_items_id'} );
772 $num_items_errored++;
774 # Remove the itemnumber if it exists, we want to create a new item
775 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
776 $item_marc->field($itemtag)->delete_subfield( code => $itemsubfield );
778 my ( $item_biblionumber, $biblioitemnumber, $itemnumber ) = AddItemFromMarc( $item_marc, $biblionumber, { biblioitemnumber => $biblioitemnumber, skip_record_index => 1 } );
780 $updsth->bind_param( 1, 'imported' );
781 $updsth->bind_param( 2, $itemnumber );
782 $updsth->bind_param( 3, undef );
783 $updsth->bind_param( 4, $row->{'import_items_id'} );
791 return ( $num_items_added, $num_items_replaced, $num_items_errored );
794 =head2 BatchRevertRecords
796 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted,
797 $num_ignored) = BatchRevertRecords($batch_id);
801 sub BatchRevertRecords {
802 my $batch_id = shift;
804 my $logger = Koha::Logger->get( { category => 'C4.ImportBatch' } );
806 $logger->trace("C4::ImportBatch::BatchRevertRecords( $batch_id )");
811 my $num_reverted = 0;
813 my $num_items_deleted = 0;
814 # commit (i.e., save, all records in the batch)
815 SetImportBatchStatus($batch_id, 'reverting');
816 my $overlay_action = GetImportBatchOverlayAction($batch_id);
817 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
818 my $dbh = C4::Context->dbh;
819 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
821 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
822 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
823 WHERE import_batch_id = ?");
824 $sth->execute($batch_id);
826 my $marcflavour = C4::Context->preference('marcflavour');
827 while (my $rowref = $sth->fetchrow_hashref) {
828 $record_type = $rowref->{'record_type'};
829 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
833 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
834 $marc_type = 'UNIMARCAUTH';
835 } elsif ($marcflavour eq 'UNIMARC') {
836 $marc_type = 'UNIMARC';
838 $marc_type = 'USMARC';
841 my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
843 if ($record_result eq 'delete') {
845 if ($record_type eq 'biblio') {
846 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
847 $error = DelBiblio($rowref->{'matched_biblionumber'});
849 DelAuthority({ authid => $rowref->{'matched_authid'} });
851 if (defined $error) {
855 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
857 } elsif ($record_result eq 'restore') {
859 my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
860 if ($record_type eq 'biblio') {
861 my $biblionumber = $rowref->{'matched_biblionumber'};
862 my $oldbiblio = Koha::Biblios->find( $biblionumber );
864 $logger->info("C4::ImportBatch::BatchRevertRecords: Biblio record $biblionumber does not exist, restoration of this record was skipped") unless $oldbiblio;
865 next unless $oldbiblio; # Record has since been deleted. Deleted records should stay deleted.
867 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
868 ModBiblio($old_record, $biblionumber, $oldbiblio->frameworkcode);
870 my $authid = $rowref->{'matched_authid'};
871 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
873 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
874 } elsif ($record_result eq 'ignore') {
875 if ($record_type eq 'biblio') {
876 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
878 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
881 if ($record_type eq 'biblio') {
882 # remove matched_biblionumber only if there is no 'imported' item left
883 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?"; # FIXME Remove me
884 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ? AND NOT EXISTS (SELECT * FROM import_items WHERE import_items.import_record_id=import_biblios.import_record_id and status='imported')";
886 $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
888 my $sth2 = $dbh->prepare_cached($query);
889 $sth2->execute($rowref->{'import_record_id'});
893 SetImportBatchStatus($batch_id, 'reverted');
894 return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
897 =head2 BatchRevertItems
899 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
903 sub BatchRevertItems {
904 my ($import_record_id, $biblionumber) = @_;
906 my $dbh = C4::Context->dbh;
907 my $num_items_deleted = 0;
909 my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
911 JOIN items USING (itemnumber)
912 WHERE import_record_id = ?");
913 $sth->bind_param(1, $import_record_id);
915 while (my $row = $sth->fetchrow_hashref()) {
916 my $item = Koha::Items->find($row->{itemnumber});
917 if ($item->safe_delete){
918 my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
919 $updsth->bind_param(1, 'reverted');
920 $updsth->bind_param(2, $row->{'import_items_id'});
923 $num_items_deleted++;
930 return $num_items_deleted;
935 CleanBatch($batch_id)
937 Deletes all staged records from the import batch
938 and sets the status of the batch to 'cleaned'. Note
939 that deleting a stage record does *not* affect
940 any record that has been committed to the database.
945 my $batch_id = shift;
946 return unless defined $batch_id;
948 C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
949 SetImportBatchStatus($batch_id, 'cleaned');
954 DeleteBatch($batch_id)
956 Deletes the record from the database. This can only be done
957 once the batch has been cleaned.
962 my $batch_id = shift;
963 return unless defined $batch_id;
965 my $dbh = C4::Context->dbh;
966 my $sth = $dbh->prepare('DELETE FROM import_batches WHERE import_batch_id = ?');
967 $sth->execute( $batch_id );
970 =head2 GetAllImportBatches
972 my $results = GetAllImportBatches();
974 Returns a references to an array of hash references corresponding
975 to all import_batches rows (of batch_type 'batch'), sorted in
976 ascending order by import_batch_id.
980 sub GetAllImportBatches {
981 my $dbh = C4::Context->dbh;
982 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
983 WHERE batch_type IN ('batch', 'webservice')
984 ORDER BY import_batch_id ASC");
988 while (my $row = $sth->fetchrow_hashref) {
989 push @$results, $row;
995 =head2 GetStagedWebserviceBatches
997 my $batch_ids = GetStagedWebserviceBatches();
999 Returns a references to an array of batch id's
1000 of batch_type 'webservice' that are not imported
1004 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
1005 SELECT import_batch_id FROM import_batches
1006 WHERE batch_type = 'webservice'
1007 AND import_status = 'staged'
1009 sub GetStagedWebserviceBatches {
1010 my $dbh = C4::Context->dbh;
1011 return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
1014 =head2 GetImportBatchRangeDesc
1016 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
1018 Returns a reference to an array of hash references corresponding to
1019 import_batches rows (sorted in descending order by import_batch_id)
1020 start at the given offset.
1024 sub GetImportBatchRangeDesc {
1025 my ($offset, $results_per_group) = @_;
1027 my $dbh = C4::Context->dbh;
1028 my $query = "SELECT b.*, p.name as profile FROM import_batches b
1029 LEFT JOIN import_batch_profiles p
1030 ON b.profile_id = p.id
1031 WHERE b.batch_type IN ('batch', 'webservice')
1032 ORDER BY b.import_batch_id DESC";
1034 if ($results_per_group){
1035 $query .= " LIMIT ?";
1036 push(@params, $results_per_group);
1039 $query .= " OFFSET ?";
1040 push(@params, $offset);
1042 my $sth = $dbh->prepare_cached($query);
1043 $sth->execute(@params);
1044 my $results = $sth->fetchall_arrayref({});
1049 =head2 GetItemNumbersFromImportBatch
1051 my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
1055 sub GetItemNumbersFromImportBatch {
1056 my ($batch_id) = @_;
1057 my $dbh = C4::Context->dbh;
1059 SELECT itemnumber FROM import_items
1060 INNER JOIN items USING (itemnumber)
1061 INNER JOIN import_records USING (import_record_id)
1062 WHERE import_batch_id = ?|;
1063 my $sth = $dbh->prepare( $sql );
1064 $sth->execute($batch_id);
1066 while ( my ($itm) = $sth->fetchrow_array ) {
1072 =head2 GetNumberOfImportBatches
1074 my $count = GetNumberOfImportBatches();
1078 sub GetNumberOfNonZ3950ImportBatches {
1079 my $dbh = C4::Context->dbh;
1080 my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
1082 my ($count) = $sth->fetchrow_array();
1087 =head2 GetImportBiblios
1089 my $results = GetImportBiblios($importid);
1093 sub GetImportBiblios {
1094 my ($import_record_id) = @_;
1096 my $dbh = C4::Context->dbh;
1097 my $query = "SELECT * FROM import_biblios WHERE import_record_id = ?";
1098 return $dbh->selectall_arrayref(
1106 =head2 GetImportRecordsRange
1108 my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
1110 Returns a reference to an array of hash references corresponding to
1111 import_biblios/import_auths/import_records rows for a given batch
1112 starting at the given offset.
1116 sub GetImportRecordsRange {
1117 my ( $batch_id, $offset, $results_per_group, $status, $parameters ) = @_;
1119 my $dbh = C4::Context->dbh;
1121 my $order_by = $parameters->{order_by} || 'import_record_id';
1122 ( $order_by ) = grep( { $_ eq $order_by } qw( import_record_id title status overlay_status ) ) ? $order_by : 'import_record_id';
1124 my $order_by_direction =
1125 uc( $parameters->{order_by_direction} // 'ASC' ) eq 'DESC' ? 'DESC' : 'ASC';
1127 $order_by .= " $order_by_direction, authorized_heading" if $order_by eq 'title';
1129 my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
1130 record_sequence, status, overlay_status,
1131 matched_biblionumber, matched_authid, record_type
1133 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
1134 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1135 WHERE import_batch_id = ?";
1137 push(@params, $batch_id);
1139 $query .= " AND status=?";
1140 push(@params,$status);
1143 $query.=" ORDER BY $order_by $order_by_direction";
1145 if($results_per_group){
1146 $query .= " LIMIT ?";
1147 push(@params, $results_per_group);
1150 $query .= " OFFSET ?";
1151 push(@params, $offset);
1153 my $sth = $dbh->prepare_cached($query);
1154 $sth->execute(@params);
1155 my $results = $sth->fetchall_arrayref({});
1161 =head2 GetBestRecordMatch
1163 my $record_id = GetBestRecordMatch($import_record_id);
1167 sub GetBestRecordMatch {
1168 my ($import_record_id) = @_;
1170 my $dbh = C4::Context->dbh;
1171 my $sth = $dbh->prepare("SELECT candidate_match_id
1172 FROM import_record_matches
1173 JOIN import_records ON ( import_record_matches.import_record_id = import_records.import_record_id )
1174 LEFT JOIN biblio ON ( candidate_match_id = biblio.biblionumber )
1175 LEFT JOIN auth_header ON ( candidate_match_id = auth_header.authid )
1176 WHERE import_record_matches.import_record_id = ? AND
1177 ( (import_records.record_type = 'biblio' AND biblio.biblionumber IS NOT NULL) OR
1178 (import_records.record_type = 'auth' AND auth_header.authid IS NOT NULL) )
1180 ORDER BY score DESC, candidate_match_id DESC");
1181 $sth->execute($import_record_id);
1182 my ($record_id) = $sth->fetchrow_array();
1187 =head2 GetImportBatchStatus
1189 my $status = GetImportBatchStatus($batch_id);
1193 sub GetImportBatchStatus {
1194 my ($batch_id) = @_;
1196 my $dbh = C4::Context->dbh;
1197 my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1198 $sth->execute($batch_id);
1199 my ($status) = $sth->fetchrow_array();
1205 =head2 SetImportBatchStatus
1207 SetImportBatchStatus($batch_id, $new_status);
1211 sub SetImportBatchStatus {
1212 my ($batch_id, $new_status) = @_;
1214 my $dbh = C4::Context->dbh;
1215 my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1216 $sth->execute($new_status, $batch_id);
1221 =head2 SetMatchedBiblionumber
1223 SetMatchedBiblionumber($import_record_id, $biblionumber);
1227 sub SetMatchedBiblionumber {
1228 my ($import_record_id, $biblionumber) = @_;
1230 my $dbh = C4::Context->dbh;
1232 q|UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?|,
1233 undef, $biblionumber, $import_record_id
1237 =head2 GetImportBatchOverlayAction
1239 my $overlay_action = GetImportBatchOverlayAction($batch_id);
1243 sub GetImportBatchOverlayAction {
1244 my ($batch_id) = @_;
1246 my $dbh = C4::Context->dbh;
1247 my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1248 $sth->execute($batch_id);
1249 my ($overlay_action) = $sth->fetchrow_array();
1251 return $overlay_action;
1256 =head2 SetImportBatchOverlayAction
1258 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1262 sub SetImportBatchOverlayAction {
1263 my ($batch_id, $new_overlay_action) = @_;
1265 my $dbh = C4::Context->dbh;
1266 my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1267 $sth->execute($new_overlay_action, $batch_id);
1272 =head2 GetImportBatchNoMatchAction
1274 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1278 sub GetImportBatchNoMatchAction {
1279 my ($batch_id) = @_;
1281 my $dbh = C4::Context->dbh;
1282 my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1283 $sth->execute($batch_id);
1284 my ($nomatch_action) = $sth->fetchrow_array();
1286 return $nomatch_action;
1291 =head2 SetImportBatchNoMatchAction
1293 SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1297 sub SetImportBatchNoMatchAction {
1298 my ($batch_id, $new_nomatch_action) = @_;
1300 my $dbh = C4::Context->dbh;
1301 my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1302 $sth->execute($new_nomatch_action, $batch_id);
1307 =head2 GetImportBatchItemAction
1309 my $item_action = GetImportBatchItemAction($batch_id);
1313 sub GetImportBatchItemAction {
1314 my ($batch_id) = @_;
1316 my $dbh = C4::Context->dbh;
1317 my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1318 $sth->execute($batch_id);
1319 my ($item_action) = $sth->fetchrow_array();
1321 return $item_action;
1326 =head2 SetImportBatchItemAction
1328 SetImportBatchItemAction($batch_id, $new_item_action);
1332 sub SetImportBatchItemAction {
1333 my ($batch_id, $new_item_action) = @_;
1335 my $dbh = C4::Context->dbh;
1336 my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1337 $sth->execute($new_item_action, $batch_id);
1342 =head2 GetImportBatchMatcher
1344 my $matcher_id = GetImportBatchMatcher($batch_id);
1348 sub GetImportBatchMatcher {
1349 my ($batch_id) = @_;
1351 my $dbh = C4::Context->dbh;
1352 my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1353 $sth->execute($batch_id);
1354 my ($matcher_id) = $sth->fetchrow_array();
1361 =head2 SetImportBatchMatcher
1363 SetImportBatchMatcher($batch_id, $new_matcher_id);
1367 sub SetImportBatchMatcher {
1368 my ($batch_id, $new_matcher_id) = @_;
1370 my $dbh = C4::Context->dbh;
1371 my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1372 $sth->execute($new_matcher_id, $batch_id);
1377 =head2 GetImportRecordOverlayStatus
1379 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1383 sub GetImportRecordOverlayStatus {
1384 my ($import_record_id) = @_;
1386 my $dbh = C4::Context->dbh;
1387 my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1388 $sth->execute($import_record_id);
1389 my ($overlay_status) = $sth->fetchrow_array();
1391 return $overlay_status;
1396 =head2 SetImportRecordOverlayStatus
1398 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1402 sub SetImportRecordOverlayStatus {
1403 my ($import_record_id, $new_overlay_status) = @_;
1405 my $dbh = C4::Context->dbh;
1406 my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1407 $sth->execute($new_overlay_status, $import_record_id);
1412 =head2 GetImportRecordStatus
1414 my $status = GetImportRecordStatus($import_record_id);
1418 sub GetImportRecordStatus {
1419 my ($import_record_id) = @_;
1421 my $dbh = C4::Context->dbh;
1422 my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1423 $sth->execute($import_record_id);
1424 my ($status) = $sth->fetchrow_array();
1431 =head2 SetImportRecordStatus
1433 SetImportRecordStatus($import_record_id, $new_status);
1437 sub SetImportRecordStatus {
1438 my ($import_record_id, $new_status) = @_;
1440 my $dbh = C4::Context->dbh;
1441 my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1442 $sth->execute($new_status, $import_record_id);
1447 =head2 GetImportRecordMatches
1449 my $results = GetImportRecordMatches($import_record_id, $best_only);
1453 sub GetImportRecordMatches {
1454 my $import_record_id = shift;
1455 my $best_only = @_ ? shift : 0;
1457 my $dbh = C4::Context->dbh;
1458 # FIXME currently biblio only
1459 my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1460 candidate_match_id, score, record_type,
1463 JOIN import_record_matches USING (import_record_id)
1464 LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1465 WHERE import_record_id = ?
1466 ORDER BY score DESC, biblionumber DESC");
1467 $sth->bind_param(1, $import_record_id);
1470 while (my $row = $sth->fetchrow_hashref) {
1471 if ($row->{'record_type'} eq 'auth') {
1472 $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1474 next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1475 push @$results, $row;
1484 =head2 SetImportRecordMatches
1486 SetImportRecordMatches($import_record_id, @matches);
1490 sub SetImportRecordMatches {
1491 my $import_record_id = shift;
1494 my $dbh = C4::Context->dbh;
1495 my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1496 $delsth->execute($import_record_id);
1499 my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score, chosen)
1500 VALUES (?, ?, ?, ?)");
1501 my $chosen = 1; #The first match is defaulted to be chosen
1502 foreach my $match (@matches) {
1503 $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'}, $chosen);
1504 $chosen = 0; #After the first we do not default to other matches
1508 =head2 RecordsFromISO2709File
1510 my ($errors, $records) = C4::ImportBatch::RecordsFromISO2709File($input_file, $record_type, $encoding);
1512 Reads ISO2709 binary porridge from the given file and creates MARC::Record-objects out of it.
1514 @PARAM1, String, absolute path to the ISO2709 file.
1515 @PARAM2, String, see stage_file.pl
1516 @PARAM3, String, should be utf8
1518 Returns two array refs.
1522 sub RecordsFromISO2709File {
1523 my ($input_file, $record_type, $encoding) = @_;
1526 my $marc_type = C4::Context->preference('marcflavour');
1527 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
1529 open my $fh, '<', $input_file or die "$0: cannot open input file $input_file: $!\n";
1535 next unless $_; # skip if record has only whitespace, as might occur
1536 # if file includes newlines between each MARC record
1537 my ($marc_record, $charset_guessed, $char_errors) = MarcToUTF8Record($_, $marc_type, $encoding);
1538 push @marc_records, $marc_record;
1539 if ($charset_guessed ne $encoding) {
1541 "Unexpected charset $charset_guessed, expecting $encoding";
1545 return ( \@errors, \@marc_records );
1548 =head2 RecordsFromMARCXMLFile
1550 my ($errors, $records) = C4::ImportBatch::RecordsFromMARCXMLFile($input_file, $encoding);
1552 Creates MARC::Record-objects out of the given MARCXML-file.
1554 @PARAM1, String, absolute path to the ISO2709 file.
1555 @PARAM2, String, should be utf8
1557 Returns two array refs.
1561 sub RecordsFromMARCXMLFile {
1562 my ( $filename, $encoding ) = @_;
1563 my $batch = MARC::File::XML->in( $filename );
1564 my ( @marcRecords, @errors, $record );
1566 eval { $record = $batch->next( $encoding ); };
1570 push @marcRecords, $record if $record;
1572 return (\@errors, \@marcRecords);
1575 =head2 RecordsFromMarcPlugin
1577 Converts text of input_file into array of MARC records with to_marc plugin
1581 sub RecordsFromMarcPlugin {
1582 my ($input_file, $plugin_class, $encoding) = @_;
1583 my ( $text, @return );
1584 return \@return if !$input_file || !$plugin_class;
1587 open my $fh, '<', $input_file or die "$0: cannot open input file $input_file: $!\n";
1597 # Convert to large MARC blob with plugin
1598 $text = Koha::Plugins::Handler->run({
1599 class => $plugin_class,
1600 method => 'to_marc',
1601 params => { data => $text },
1604 # Convert to array of MARC records
1606 my $marc_type = C4::Context->preference('marcflavour');
1607 foreach my $blob ( split(/\x1D/, $text) ) {
1608 next if $blob =~ /^\s*$/;
1609 my ($marcrecord) = MarcToUTF8Record($blob, $marc_type, $encoding);
1610 push @return, $marcrecord;
1616 # internal functions
1618 sub _create_import_record {
1619 my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $marc_type) = @_;
1621 my $dbh = C4::Context->dbh;
1622 my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, marcxml_old,
1623 record_type, encoding)
1624 VALUES (?, ?, ?, ?, ?, ?, ?)");
1625 $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type), '',
1626 $record_type, $encoding);
1627 my $import_record_id = $dbh->{'mysql_insertid'};
1629 return $import_record_id;
1632 sub _add_auth_fields {
1633 my ($import_record_id, $marc_record) = @_;
1636 if ($marc_record->field('001')) {
1637 $controlnumber = $marc_record->field('001')->data();
1639 my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1640 my $dbh = C4::Context->dbh;
1641 my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1642 $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1646 sub _add_biblio_fields {
1647 my ($import_record_id, $marc_record) = @_;
1649 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1650 my $dbh = C4::Context->dbh;
1651 # FIXME no controlnumber, originalsource
1652 $isbn = C4::Koha::GetNormalizedISBN($isbn);
1653 my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1654 $sth->execute($import_record_id, $title, $author, $isbn, $issn) or die $sth->errstr;
1659 sub _update_biblio_fields {
1660 my ($import_record_id, $marc_record) = @_;
1662 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1663 my $dbh = C4::Context->dbh;
1664 # FIXME no controlnumber, originalsource
1665 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1669 my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1670 WHERE import_record_id = ?");
1671 $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1675 sub _parse_biblio_fields {
1676 my ($marc_record) = @_;
1678 my $dbh = C4::Context->dbh;
1679 my $bibliofields = TransformMarcToKoha({ record => $marc_record, kohafields => ['biblio.title','biblio.author','biblioitems.isbn','biblioitems.issn'] });
1680 return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1684 sub _update_batch_record_counts {
1685 my ($batch_id) = @_;
1687 my $dbh = C4::Context->dbh;
1688 my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1692 WHERE import_batch_id = import_batches.import_batch_id),
1696 JOIN import_items USING (import_record_id)
1697 WHERE import_batch_id = import_batches.import_batch_id
1698 AND record_type = 'biblio')
1699 WHERE import_batch_id = ?");
1700 $sth->bind_param(1, $batch_id);
1705 sub _get_commit_action {
1706 my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1708 if ($record_type eq 'biblio') {
1709 my ($bib_result, $bib_match, $item_result);
1711 $bib_match = GetBestRecordMatch($import_record_id);
1712 if ($overlay_status ne 'no_match' && defined($bib_match)) {
1714 $bib_result = $overlay_action;
1716 if($item_action eq 'always_add' or $item_action eq 'add_only_for_matches'){
1717 $item_result = 'create_new';
1718 } elsif($item_action eq 'replace'){
1719 $item_result = 'replace';
1721 $item_result = 'ignore';
1725 $bib_result = $nomatch_action;
1726 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new') ? 'create_new' : 'ignore';
1728 return ($bib_result, $item_result, $bib_match);
1729 } else { # must be auths
1730 my ($auth_result, $auth_match);
1732 $auth_match = GetBestRecordMatch($import_record_id);
1733 if ($overlay_status ne 'no_match' && defined($auth_match)) {
1734 $auth_result = $overlay_action;
1736 $auth_result = $nomatch_action;
1739 return ($auth_result, undef, $auth_match);
1744 sub _get_revert_action {
1745 my ($overlay_action, $overlay_status, $status) = @_;
1749 if ($status eq 'ignored') {
1750 $bib_result = 'ignore';
1752 if ($overlay_action eq 'create_new') {
1753 $bib_result = 'delete';
1755 $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1766 Koha Development Team <http://koha-community.org/>
1768 Galen Charlton <galen.charlton@liblime.com>