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::Plugins::Handler;
41 our (@ISA, @EXPORT_OK);
49 GetImportRecordMarcXML
50 GetRecordFromImportBiblio
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 GetRecordFromImportBiblio {
195 my ( $import_record_id, $embed_items ) = @_;
197 my ($marc) = GetImportRecordMarc($import_record_id);
198 my $record = MARC::Record->new_from_usmarc($marc);
200 EmbedItemsInImportBiblio( $record, $import_record_id ) if $embed_items;
205 sub EmbedItemsInImportBiblio {
206 my ( $record, $import_record_id ) = @_;
207 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
208 my $dbh = C4::Context->dbh;
209 my $import_items = $dbh->selectall_arrayref(q|
210 SELECT import_items.marcxml
212 WHERE import_record_id = ?
213 |, { Slice => {} }, $import_record_id );
215 for my $import_item ( @$import_items ) {
216 my $item_marc = MARC::Record::new_from_xml($import_item->{marcxml}, 'UTF-8');
217 push @item_fields, $item_marc->field($itemtag);
219 $record->append_fields(@item_fields);
223 =head2 GetImportRecordMarcXML
225 my $marcxml = GetImportRecordMarcXML($import_record_id);
229 sub GetImportRecordMarcXML {
230 my ($import_record_id) = @_;
232 my $dbh = C4::Context->dbh;
233 my $sth = $dbh->prepare("SELECT marcxml FROM import_records WHERE import_record_id = ?");
234 $sth->execute($import_record_id);
235 my ($marcxml) = $sth->fetchrow();
241 =head2 AddImportBatch
243 my $batch_id = AddImportBatch($params_hash);
251 foreach (qw( matcher_id template_id branchcode
252 overlay_action nomatch_action item_action
253 import_status batch_type file_name comments record_type )) {
254 if (exists $params->{$_}) {
256 push @vals, $params->{$_};
259 my $dbh = C4::Context->dbh;
260 $dbh->do("INSERT INTO import_batches (".join( ',', @fields).")
261 VALUES (".join( ',', map '?', @fields).")",
264 return $dbh->{'mysql_insertid'};
267 =head2 GetImportBatch
269 my $row = GetImportBatch($batch_id);
271 Retrieve a hashref of an import_batches row.
278 my $dbh = C4::Context->dbh;
279 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 = ?");
280 $sth->bind_param(1, $batch_id);
282 my $result = $sth->fetchrow_hashref;
288 =head2 AddBiblioToBatch
290 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence,
291 $marc_record, $encoding, $update_counts);
295 sub AddBiblioToBatch {
296 my $batch_id = shift;
297 my $record_sequence = shift;
298 my $marc_record = shift;
299 my $encoding = shift;
300 my $update_counts = @_ ? shift : 1;
302 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, C4::Context->preference('marcflavour'));
303 _add_biblio_fields($import_record_id, $marc_record);
304 _update_batch_record_counts($batch_id) if $update_counts;
305 return $import_record_id;
308 =head2 AddAuthToBatch
310 my $import_record_id = AddAuthToBatch($batch_id, $record_sequence,
311 $marc_record, $encoding, $update_counts, [$marc_type]);
316 my $batch_id = shift;
317 my $record_sequence = shift;
318 my $marc_record = shift;
319 my $encoding = shift;
320 my $update_counts = @_ ? shift : 1;
321 my $marc_type = shift || C4::Context->preference('marcflavour');
323 $marc_type = 'UNIMARCAUTH' if $marc_type eq 'UNIMARC';
325 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'auth', $encoding, $marc_type);
326 _add_auth_fields($import_record_id, $marc_record);
327 _update_batch_record_counts($batch_id) if $update_counts;
328 return $import_record_id;
331 =head2 ModAuthInBatch
333 ModAuthInBatch($import_record_id, $marc_record);
338 my ($import_record_id, $marc_record) = @_;
340 my $marcflavour = C4::Context->preference('marcflavour');
341 _update_import_record_marc($import_record_id, $marc_record, $marcflavour eq 'UNIMARC' ? 'UNIMARCAUTH' : 'USMARC');
345 =head2 BatchStageMarcRecords
347 ( $batch_id, $num_records, $num_items, @invalid_records ) =
348 BatchStageMarcRecords(
349 $record_type, $encoding,
350 $marc_records, $file_name,
351 $marc_modification_template, $comments,
352 $branch_code, $parse_items,
353 $leave_as_staging, $progress_interval,
359 sub BatchStageMarcRecords {
360 my $record_type = shift;
361 my $encoding = shift;
362 my $marc_records = shift;
363 my $file_name = shift;
364 my $marc_modification_template = shift;
365 my $comments = shift;
366 my $branch_code = shift;
367 my $parse_items = shift;
368 my $leave_as_staging = shift;
370 # optional callback to monitor status
372 my $progress_interval = 0;
373 my $progress_callback = undef;
375 $progress_interval = shift;
376 $progress_callback = shift;
377 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
378 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
381 my $batch_id = AddImportBatch( {
382 overlay_action => 'create_new',
383 import_status => 'staging',
384 batch_type => 'batch',
385 file_name => $file_name,
386 comments => $comments,
387 record_type => $record_type,
390 SetImportBatchItemAction($batch_id, 'always_add');
392 SetImportBatchItemAction($batch_id, 'ignore');
396 my $marc_type = C4::Context->preference('marcflavour');
397 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
398 my @invalid_records = ();
401 # FIXME - for now, we're dealing only with bibs
403 foreach my $marc_record (@$marc_records) {
405 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
406 &$progress_callback($rec_num);
409 ModifyRecordWithTemplate( $marc_modification_template, $marc_record ) if ( $marc_modification_template );
411 my $import_record_id;
412 if (scalar($marc_record->fields()) == 0) {
413 push @invalid_records, $marc_record;
416 # Normalize the record so it doesn't have separated diacritics
417 SetUTF8Flag($marc_record);
420 if ($record_type eq 'biblio') {
421 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0);
423 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
424 $num_items += scalar(@import_items_ids);
426 } elsif ($record_type eq 'auth') {
427 $import_record_id = AddAuthToBatch($batch_id, $rec_num, $marc_record, $encoding, int(rand(99999)), 0, $marc_type);
431 unless ($leave_as_staging) {
432 SetImportBatchStatus($batch_id, 'staged');
434 # FIXME branch_code, number of bibs, number of items
435 _update_batch_record_counts($batch_id);
436 return ($batch_id, $num_valid, $num_items, @invalid_records);
439 =head2 AddItemsToImportBiblio
441 my @import_items_ids = AddItemsToImportBiblio($batch_id,
442 $import_record_id, $marc_record, $update_counts);
446 sub AddItemsToImportBiblio {
447 my $batch_id = shift;
448 my $import_record_id = shift;
449 my $marc_record = shift;
450 my $update_counts = @_ ? shift : 0;
452 my @import_items_ids = ();
454 my $dbh = C4::Context->dbh;
455 my ($item_tag,$item_subfield) = &GetMarcFromKohaField( "items.itemnumber" );
456 foreach my $item_field ($marc_record->field($item_tag)) {
457 my $item_marc = MARC::Record->new();
458 $item_marc->leader("00000 a "); # must set Leader/09 to 'a'
459 $item_marc->append_fields($item_field);
460 $marc_record->delete_field($item_field);
461 my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
463 $sth->bind_param(1, $import_record_id);
464 $sth->bind_param(2, 'staged');
465 $sth->bind_param(3, $item_marc->as_xml("USMARC"));
467 push @import_items_ids, $dbh->{'mysql_insertid'};
471 if ($#import_items_ids > -1) {
472 _update_batch_record_counts($batch_id) if $update_counts;
473 _update_import_record_marc($import_record_id, $marc_record, C4::Context->preference('marcflavour'));
475 return @import_items_ids;
478 =head2 BatchFindDuplicates
480 my $num_with_matches = BatchFindDuplicates($batch_id, $matcher,
481 $max_matches, $progress_interval, $progress_callback);
483 Goes through the records loaded in the batch and attempts to
484 find duplicates for each one. Sets the matching status
485 of each record to "no_match" or "auto_match" as appropriate.
487 The $max_matches parameter is optional; if it is not supplied,
490 The $progress_interval and $progress_callback parameters are
491 optional; if both are supplied, the sub referred to by
492 $progress_callback will be invoked every $progress_interval
493 records using the number of records processed as the
498 sub BatchFindDuplicates {
499 my $batch_id = shift;
501 my $max_matches = @_ ? shift : 10;
503 # optional callback to monitor status
505 my $progress_interval = 0;
506 my $progress_callback = undef;
508 $progress_interval = shift;
509 $progress_callback = shift;
510 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
511 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
514 my $dbh = C4::Context->dbh;
516 my $sth = $dbh->prepare("SELECT import_record_id, record_type, marc
518 WHERE import_batch_id = ?");
519 $sth->execute($batch_id);
520 my $num_with_matches = 0;
522 while (my $rowref = $sth->fetchrow_hashref) {
524 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
525 &$progress_callback($rec_num);
527 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
529 if (defined $matcher) {
530 @matches = $matcher->get_matches($marc_record, $max_matches);
532 if (scalar(@matches) > 0) {
534 SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
535 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
537 SetImportRecordMatches($rowref->{'import_record_id'}, ());
538 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
542 return $num_with_matches;
545 =head2 BatchCommitRecords
547 my ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored) =
548 BatchCommitRecords($batch_id, $framework,
549 $progress_interval, $progress_callback);
553 sub BatchCommitRecords {
554 my $batch_id = shift;
555 my $framework = shift;
557 # optional callback to monitor status
559 my $progress_interval = 0;
560 my $progress_callback = undef;
562 $progress_interval = shift;
563 $progress_callback = shift;
564 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
565 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
571 my $num_items_added = 0;
572 my $num_items_replaced = 0;
573 my $num_items_errored = 0;
575 # commit (i.e., save, all records in the batch)
576 SetImportBatchStatus($batch_id, 'importing');
577 my $overlay_action = GetImportBatchOverlayAction($batch_id);
578 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
579 my $item_action = GetImportBatchItemAction($batch_id);
582 my $dbh = C4::Context->dbh;
583 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marc, encoding
585 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
586 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
587 WHERE import_batch_id = ?");
588 $sth->execute($batch_id);
589 my $marcflavour = C4::Context->preference('marcflavour');
591 my $userenv = C4::Context->userenv;
592 my $logged_in_patron = Koha::Patrons->find( $userenv->{number} );
595 while (my $rowref = $sth->fetchrow_hashref) {
596 $record_type = $rowref->{'record_type'};
598 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
599 &$progress_callback($rec_num);
601 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
607 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
608 $marc_type = 'UNIMARCAUTH';
609 } elsif ($marcflavour eq 'UNIMARC') {
610 $marc_type = 'UNIMARC';
612 $marc_type = 'USMARC';
614 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
616 if ($record_type eq 'biblio') {
617 # remove any item tags - rely on BatchCommitItems
618 ($item_tag,$item_subfield) = &GetMarcFromKohaField( "items.itemnumber" );
619 foreach my $item_field ($marc_record->field($item_tag)) {
620 $marc_record->delete_field($item_field);
624 my ($record_result, $item_result, $record_match) =
625 _get_commit_action($overlay_action, $nomatch_action, $item_action,
626 $rowref->{'overlay_status'}, $rowref->{'import_record_id'}, $record_type);
630 if ($record_result eq 'create_new') {
632 if ($record_type eq 'biblio') {
633 my $biblioitemnumber;
634 ($recordid, $biblioitemnumber) = AddBiblio($marc_record, $framework);
635 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"; # FIXME call SetMatchedBiblionumber instead
636 if ($item_result eq 'create_new' || $item_result eq 'replace') {
637 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
638 $num_items_added += $bib_items_added;
639 $num_items_replaced += $bib_items_replaced;
640 $num_items_errored += $bib_items_errored;
643 $recordid = AddAuthority($marc_record, undef, GuessAuthTypeCode($marc_record));
644 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
646 my $sth = $dbh->prepare_cached($query);
647 $sth->execute($recordid, $rowref->{'import_record_id'});
649 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
650 } elsif ($record_result eq 'replace') {
652 $recordid = $record_match;
654 if ($record_type eq 'biblio') {
655 my $oldbiblio = Koha::Biblios->find( $recordid );
656 $oldxml = GetXmlBiblio($recordid);
658 # remove item fields so that they don't get
659 # added again if record is reverted
660 # 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.
661 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'}, $marc_type);
662 foreach my $item_field ($old_marc->field($item_tag)) {
663 $old_marc->delete_field($item_field);
665 $oldxml = $old_marc->as_xml($marc_type);
667 my $context = { source => 'batchimport' };
668 if ($logged_in_patron) {
669 $context->{categorycode} = $logged_in_patron->categorycode;
670 $context->{userid} = $logged_in_patron->userid;
673 ModBiblio($marc_record, $recordid, $oldbiblio->frameworkcode, {
674 overlay_context => $context
676 $query = "UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"; # FIXME call SetMatchedBiblionumber instead
678 if ($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 $num_items_added += $bib_items_added;
681 $num_items_replaced += $bib_items_replaced;
682 $num_items_errored += $bib_items_errored;
685 $oldxml = GetAuthorityXML($recordid);
687 ModAuthority($recordid, $marc_record, GuessAuthTypeCode($marc_record));
688 $query = "UPDATE import_auths SET matched_authid = ? WHERE import_record_id = ?";
690 my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
691 $sth->execute($oldxml, $rowref->{'import_record_id'});
693 my $sth2 = $dbh->prepare_cached($query);
694 $sth2->execute($recordid, $rowref->{'import_record_id'});
696 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
697 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
698 } elsif ($record_result eq 'ignore') {
699 $recordid = $record_match;
701 if ($record_type eq 'biblio' and defined $recordid and ( $item_result eq 'create_new' || $item_result eq 'replace' ) ) {
702 my ($bib_items_added, $bib_items_replaced, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $recordid, $item_result);
703 $num_items_added += $bib_items_added;
704 $num_items_replaced += $bib_items_replaced;
705 $num_items_errored += $bib_items_errored;
706 # still need to record the matched biblionumber so that the
707 # items can be reverted
708 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?"); # FIXME call SetMatchedBiblionumber instead
709 $sth2->execute($recordid, $rowref->{'import_record_id'});
710 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
712 SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
716 SetImportBatchStatus($batch_id, 'imported');
717 return ($num_added, $num_updated, $num_items_added, $num_items_replaced, $num_items_errored, $num_ignored);
720 =head2 BatchCommitItems
722 ($num_items_added, $num_items_errored) =
723 BatchCommitItems($import_record_id, $biblionumber);
727 sub BatchCommitItems {
728 my ( $import_record_id, $biblionumber, $action ) = @_;
730 my $dbh = C4::Context->dbh;
732 my $num_items_added = 0;
733 my $num_items_errored = 0;
734 my $num_items_replaced = 0;
736 my $sth = $dbh->prepare( "
737 SELECT import_items_id, import_items.marcxml, encoding
739 JOIN import_records USING (import_record_id)
740 WHERE import_record_id = ?
741 ORDER BY import_items_id
743 $sth->bind_param( 1, $import_record_id );
746 while ( my $row = $sth->fetchrow_hashref() ) {
747 my $item_marc = MARC::Record->new_from_xml( StripNonXmlChars( $row->{'marcxml'} ), 'UTF-8', $row->{'encoding'} );
749 # Delete date_due subfield as to not accidentally delete item checkout due dates
750 my ( $MARCfield, $MARCsubfield ) = GetMarcFromKohaField( 'items.onloan' );
751 $item_marc->field($MARCfield)->delete_subfield( code => $MARCsubfield );
753 my $item = TransformMarcToKoha( $item_marc );
755 my $duplicate_barcode = exists( $item->{'barcode'} ) && Koha::Items->find({ barcode => $item->{'barcode'} });
756 my $duplicate_itemnumber = exists( $item->{'itemnumber'} );
758 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ?, import_error = ? WHERE import_items_id = ?");
759 if ( $action eq "replace" && $duplicate_itemnumber ) {
760 # Duplicate itemnumbers have precedence, that way we can update barcodes by overlaying
761 ModItemFromMarc( $item_marc, $biblionumber, $item->{itemnumber} );
762 $updsth->bind_param( 1, 'imported' );
763 $updsth->bind_param( 2, $item->{itemnumber} );
764 $updsth->bind_param( 3, undef );
765 $updsth->bind_param( 4, $row->{'import_items_id'} );
768 $num_items_replaced++;
769 } elsif ( $action eq "replace" && $duplicate_barcode ) {
770 my $itemnumber = $duplicate_barcode->itemnumber;
771 ModItemFromMarc( $item_marc, $biblionumber, $itemnumber );
772 $updsth->bind_param( 1, 'imported' );
773 $updsth->bind_param( 2, $item->{itemnumber} );
774 $updsth->bind_param( 3, undef );
775 $updsth->bind_param( 4, $row->{'import_items_id'} );
778 $num_items_replaced++;
779 } elsif ($duplicate_barcode) {
780 $updsth->bind_param( 1, 'error' );
781 $updsth->bind_param( 2, undef );
782 $updsth->bind_param( 3, 'duplicate item barcode' );
783 $updsth->bind_param( 4, $row->{'import_items_id'} );
785 $num_items_errored++;
787 # Remove the itemnumber if it exists, we want to create a new item
788 my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber" );
789 $item_marc->field($itemtag)->delete_subfield( code => $itemsubfield );
791 my ( $item_biblionumber, $biblioitemnumber, $itemnumber ) = AddItemFromMarc( $item_marc, $biblionumber );
793 $updsth->bind_param( 1, 'imported' );
794 $updsth->bind_param( 2, $itemnumber );
795 $updsth->bind_param( 3, undef );
796 $updsth->bind_param( 4, $row->{'import_items_id'} );
804 return ( $num_items_added, $num_items_replaced, $num_items_errored );
807 =head2 BatchRevertRecords
809 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted,
810 $num_ignored) = BatchRevertRecords($batch_id);
814 sub BatchRevertRecords {
815 my $batch_id = shift;
817 my $logger = Koha::Logger->get( { category => 'C4.ImportBatch' } );
819 $logger->trace("C4::ImportBatch::BatchRevertRecords( $batch_id )");
824 my $num_reverted = 0;
826 my $num_items_deleted = 0;
827 # commit (i.e., save, all records in the batch)
828 SetImportBatchStatus($batch_id, 'reverting');
829 my $overlay_action = GetImportBatchOverlayAction($batch_id);
830 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
831 my $dbh = C4::Context->dbh;
832 my $sth = $dbh->prepare("SELECT import_records.import_record_id, record_type, status, overlay_status, marcxml_old, encoding, matched_biblionumber, matched_authid
834 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
835 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
836 WHERE import_batch_id = ?");
837 $sth->execute($batch_id);
839 my $marcflavour = C4::Context->preference('marcflavour');
840 while (my $rowref = $sth->fetchrow_hashref) {
841 $record_type = $rowref->{'record_type'};
842 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
846 if ($marcflavour eq 'UNIMARC' && $record_type eq 'auth') {
847 $marc_type = 'UNIMARCAUTH';
848 } elsif ($marcflavour eq 'UNIMARC') {
849 $marc_type = 'UNIMARC';
851 $marc_type = 'USMARC';
854 my $record_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
856 if ($record_result eq 'delete') {
858 if ($record_type eq 'biblio') {
859 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
860 $error = DelBiblio($rowref->{'matched_biblionumber'});
862 DelAuthority({ authid => $rowref->{'matched_authid'} });
864 if (defined $error) {
868 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
870 } elsif ($record_result eq 'restore') {
872 my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'}, $marc_type);
873 if ($record_type eq 'biblio') {
874 my $biblionumber = $rowref->{'matched_biblionumber'};
875 my $oldbiblio = Koha::Biblios->find( $biblionumber );
877 $logger->info("C4::ImportBatch::BatchRevertRecords: Biblio record $biblionumber does not exist, restoration of this record was skipped") unless $oldbiblio;
878 next unless $oldbiblio; # Record has since been deleted. Deleted records should stay deleted.
880 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
881 ModBiblio($old_record, $biblionumber, $oldbiblio->frameworkcode);
883 my $authid = $rowref->{'matched_authid'};
884 ModAuthority($authid, $old_record, GuessAuthTypeCode($old_record));
886 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
887 } elsif ($record_result eq 'ignore') {
888 if ($record_type eq 'biblio') {
889 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
891 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
894 if ($record_type eq 'biblio') {
895 # remove matched_biblionumber only if there is no 'imported' item left
896 $query = "UPDATE import_biblios SET matched_biblionumber = NULL WHERE import_record_id = ?"; # FIXME Remove me
897 $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')";
899 $query = "UPDATE import_auths SET matched_authid = NULL WHERE import_record_id = ?";
901 my $sth2 = $dbh->prepare_cached($query);
902 $sth2->execute($rowref->{'import_record_id'});
906 SetImportBatchStatus($batch_id, 'reverted');
907 return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
910 =head2 BatchRevertItems
912 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
916 sub BatchRevertItems {
917 my ($import_record_id, $biblionumber) = @_;
919 my $dbh = C4::Context->dbh;
920 my $num_items_deleted = 0;
922 my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
924 JOIN items USING (itemnumber)
925 WHERE import_record_id = ?");
926 $sth->bind_param(1, $import_record_id);
928 while (my $row = $sth->fetchrow_hashref()) {
929 my $item = Koha::Items->find($row->{itemnumber});
930 if ($item->safe_delete){
931 my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
932 $updsth->bind_param(1, 'reverted');
933 $updsth->bind_param(2, $row->{'import_items_id'});
936 $num_items_deleted++;
943 return $num_items_deleted;
948 CleanBatch($batch_id)
950 Deletes all staged records from the import batch
951 and sets the status of the batch to 'cleaned'. Note
952 that deleting a stage record does *not* affect
953 any record that has been committed to the database.
958 my $batch_id = shift;
959 return unless defined $batch_id;
961 C4::Context->dbh->do('DELETE FROM import_records WHERE import_batch_id = ?', {}, $batch_id);
962 SetImportBatchStatus($batch_id, 'cleaned');
967 DeleteBatch($batch_id)
969 Deletes the record from the database. This can only be done
970 once the batch has been cleaned.
975 my $batch_id = shift;
976 return unless defined $batch_id;
978 my $dbh = C4::Context->dbh;
979 my $sth = $dbh->prepare('DELETE FROM import_batches WHERE import_batch_id = ?');
980 $sth->execute( $batch_id );
983 =head2 GetAllImportBatches
985 my $results = GetAllImportBatches();
987 Returns a references to an array of hash references corresponding
988 to all import_batches rows (of batch_type 'batch'), sorted in
989 ascending order by import_batch_id.
993 sub GetAllImportBatches {
994 my $dbh = C4::Context->dbh;
995 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
996 WHERE batch_type IN ('batch', 'webservice')
997 ORDER BY import_batch_id ASC");
1001 while (my $row = $sth->fetchrow_hashref) {
1002 push @$results, $row;
1008 =head2 GetStagedWebserviceBatches
1010 my $batch_ids = GetStagedWebserviceBatches();
1012 Returns a references to an array of batch id's
1013 of batch_type 'webservice' that are not imported
1017 my $PENDING_WEBSERVICE_BATCHES_QRY = <<EOQ;
1018 SELECT import_batch_id FROM import_batches
1019 WHERE batch_type = 'webservice'
1020 AND import_status = 'staged'
1022 sub GetStagedWebserviceBatches {
1023 my $dbh = C4::Context->dbh;
1024 return $dbh->selectcol_arrayref($PENDING_WEBSERVICE_BATCHES_QRY);
1027 =head2 GetImportBatchRangeDesc
1029 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
1031 Returns a reference to an array of hash references corresponding to
1032 import_batches rows (sorted in descending order by import_batch_id)
1033 start at the given offset.
1037 sub GetImportBatchRangeDesc {
1038 my ($offset, $results_per_group) = @_;
1040 my $dbh = C4::Context->dbh;
1041 my $query = "SELECT b.*, p.name as profile FROM import_batches b
1042 LEFT JOIN import_batch_profiles p
1043 ON b.profile_id = p.id
1044 WHERE b.batch_type IN ('batch', 'webservice')
1045 ORDER BY b.import_batch_id DESC";
1047 if ($results_per_group){
1048 $query .= " LIMIT ?";
1049 push(@params, $results_per_group);
1052 $query .= " OFFSET ?";
1053 push(@params, $offset);
1055 my $sth = $dbh->prepare_cached($query);
1056 $sth->execute(@params);
1057 my $results = $sth->fetchall_arrayref({});
1062 =head2 GetItemNumbersFromImportBatch
1064 my @itemsnos = GetItemNumbersFromImportBatch($batch_id);
1068 sub GetItemNumbersFromImportBatch {
1069 my ($batch_id) = @_;
1070 my $dbh = C4::Context->dbh;
1072 SELECT itemnumber FROM import_items
1073 INNER JOIN items USING (itemnumber)
1074 INNER JOIN import_records USING (import_record_id)
1075 WHERE import_batch_id = ?|;
1076 my $sth = $dbh->prepare( $sql );
1077 $sth->execute($batch_id);
1079 while ( my ($itm) = $sth->fetchrow_array ) {
1085 =head2 GetNumberOfImportBatches
1087 my $count = GetNumberOfImportBatches();
1091 sub GetNumberOfNonZ3950ImportBatches {
1092 my $dbh = C4::Context->dbh;
1093 my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type != 'z3950'");
1095 my ($count) = $sth->fetchrow_array();
1100 =head2 GetImportBiblios
1102 my $results = GetImportBiblios($importid);
1106 sub GetImportBiblios {
1107 my ($import_record_id) = @_;
1109 my $dbh = C4::Context->dbh;
1110 my $query = "SELECT * FROM import_biblios WHERE import_record_id = ?";
1111 return $dbh->selectall_arrayref(
1119 =head2 GetImportRecordsRange
1121 my $results = GetImportRecordsRange($batch_id, $offset, $results_per_group);
1123 Returns a reference to an array of hash references corresponding to
1124 import_biblios/import_auths/import_records rows for a given batch
1125 starting at the given offset.
1129 sub GetImportRecordsRange {
1130 my ( $batch_id, $offset, $results_per_group, $status, $parameters ) = @_;
1132 my $dbh = C4::Context->dbh;
1134 my $order_by = $parameters->{order_by} || 'import_record_id';
1135 ( $order_by ) = grep( { $_ eq $order_by } qw( import_record_id title status overlay_status ) ) ? $order_by : 'import_record_id';
1137 my $order_by_direction =
1138 uc( $parameters->{order_by_direction} // 'ASC' ) eq 'DESC' ? 'DESC' : 'ASC';
1140 $order_by .= " $order_by_direction, authorized_heading" if $order_by eq 'title';
1142 my $query = "SELECT title, author, isbn, issn, authorized_heading, import_records.import_record_id,
1143 record_sequence, status, overlay_status,
1144 matched_biblionumber, matched_authid, record_type
1146 LEFT JOIN import_auths ON (import_records.import_record_id=import_auths.import_record_id)
1147 LEFT JOIN import_biblios ON (import_records.import_record_id=import_biblios.import_record_id)
1148 WHERE import_batch_id = ?";
1150 push(@params, $batch_id);
1152 $query .= " AND status=?";
1153 push(@params,$status);
1156 $query.=" ORDER BY $order_by $order_by_direction";
1158 if($results_per_group){
1159 $query .= " LIMIT ?";
1160 push(@params, $results_per_group);
1163 $query .= " OFFSET ?";
1164 push(@params, $offset);
1166 my $sth = $dbh->prepare_cached($query);
1167 $sth->execute(@params);
1168 my $results = $sth->fetchall_arrayref({});
1174 =head2 GetBestRecordMatch
1176 my $record_id = GetBestRecordMatch($import_record_id);
1180 sub GetBestRecordMatch {
1181 my ($import_record_id) = @_;
1183 my $dbh = C4::Context->dbh;
1184 my $sth = $dbh->prepare("SELECT candidate_match_id
1185 FROM import_record_matches
1186 JOIN import_records ON ( import_record_matches.import_record_id = import_records.import_record_id )
1187 LEFT JOIN biblio ON ( candidate_match_id = biblio.biblionumber )
1188 LEFT JOIN auth_header ON ( candidate_match_id = auth_header.authid )
1189 WHERE import_record_matches.import_record_id = ? AND
1190 ( (import_records.record_type = 'biblio' AND biblio.biblionumber IS NOT NULL) OR
1191 (import_records.record_type = 'auth' AND auth_header.authid IS NOT NULL) )
1193 ORDER BY score DESC, candidate_match_id DESC");
1194 $sth->execute($import_record_id);
1195 my ($record_id) = $sth->fetchrow_array();
1200 =head2 GetImportBatchStatus
1202 my $status = GetImportBatchStatus($batch_id);
1206 sub GetImportBatchStatus {
1207 my ($batch_id) = @_;
1209 my $dbh = C4::Context->dbh;
1210 my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE import_batch_id = ?");
1211 $sth->execute($batch_id);
1212 my ($status) = $sth->fetchrow_array();
1218 =head2 SetImportBatchStatus
1220 SetImportBatchStatus($batch_id, $new_status);
1224 sub SetImportBatchStatus {
1225 my ($batch_id, $new_status) = @_;
1227 my $dbh = C4::Context->dbh;
1228 my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
1229 $sth->execute($new_status, $batch_id);
1234 =head2 SetMatchedBiblionumber
1236 SetMatchedBiblionumber($import_record_id, $biblionumber);
1240 sub SetMatchedBiblionumber {
1241 my ($import_record_id, $biblionumber) = @_;
1243 my $dbh = C4::Context->dbh;
1245 q|UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?|,
1246 undef, $biblionumber, $import_record_id
1250 =head2 GetImportBatchOverlayAction
1252 my $overlay_action = GetImportBatchOverlayAction($batch_id);
1256 sub GetImportBatchOverlayAction {
1257 my ($batch_id) = @_;
1259 my $dbh = C4::Context->dbh;
1260 my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
1261 $sth->execute($batch_id);
1262 my ($overlay_action) = $sth->fetchrow_array();
1264 return $overlay_action;
1269 =head2 SetImportBatchOverlayAction
1271 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
1275 sub SetImportBatchOverlayAction {
1276 my ($batch_id, $new_overlay_action) = @_;
1278 my $dbh = C4::Context->dbh;
1279 my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
1280 $sth->execute($new_overlay_action, $batch_id);
1285 =head2 GetImportBatchNoMatchAction
1287 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
1291 sub GetImportBatchNoMatchAction {
1292 my ($batch_id) = @_;
1294 my $dbh = C4::Context->dbh;
1295 my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
1296 $sth->execute($batch_id);
1297 my ($nomatch_action) = $sth->fetchrow_array();
1299 return $nomatch_action;
1304 =head2 SetImportBatchNoMatchAction
1306 SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
1310 sub SetImportBatchNoMatchAction {
1311 my ($batch_id, $new_nomatch_action) = @_;
1313 my $dbh = C4::Context->dbh;
1314 my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
1315 $sth->execute($new_nomatch_action, $batch_id);
1320 =head2 GetImportBatchItemAction
1322 my $item_action = GetImportBatchItemAction($batch_id);
1326 sub GetImportBatchItemAction {
1327 my ($batch_id) = @_;
1329 my $dbh = C4::Context->dbh;
1330 my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1331 $sth->execute($batch_id);
1332 my ($item_action) = $sth->fetchrow_array();
1334 return $item_action;
1339 =head2 SetImportBatchItemAction
1341 SetImportBatchItemAction($batch_id, $new_item_action);
1345 sub SetImportBatchItemAction {
1346 my ($batch_id, $new_item_action) = @_;
1348 my $dbh = C4::Context->dbh;
1349 my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1350 $sth->execute($new_item_action, $batch_id);
1355 =head2 GetImportBatchMatcher
1357 my $matcher_id = GetImportBatchMatcher($batch_id);
1361 sub GetImportBatchMatcher {
1362 my ($batch_id) = @_;
1364 my $dbh = C4::Context->dbh;
1365 my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1366 $sth->execute($batch_id);
1367 my ($matcher_id) = $sth->fetchrow_array();
1374 =head2 SetImportBatchMatcher
1376 SetImportBatchMatcher($batch_id, $new_matcher_id);
1380 sub SetImportBatchMatcher {
1381 my ($batch_id, $new_matcher_id) = @_;
1383 my $dbh = C4::Context->dbh;
1384 my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1385 $sth->execute($new_matcher_id, $batch_id);
1390 =head2 GetImportRecordOverlayStatus
1392 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1396 sub GetImportRecordOverlayStatus {
1397 my ($import_record_id) = @_;
1399 my $dbh = C4::Context->dbh;
1400 my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1401 $sth->execute($import_record_id);
1402 my ($overlay_status) = $sth->fetchrow_array();
1404 return $overlay_status;
1409 =head2 SetImportRecordOverlayStatus
1411 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1415 sub SetImportRecordOverlayStatus {
1416 my ($import_record_id, $new_overlay_status) = @_;
1418 my $dbh = C4::Context->dbh;
1419 my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1420 $sth->execute($new_overlay_status, $import_record_id);
1425 =head2 GetImportRecordStatus
1427 my $status = GetImportRecordStatus($import_record_id);
1431 sub GetImportRecordStatus {
1432 my ($import_record_id) = @_;
1434 my $dbh = C4::Context->dbh;
1435 my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1436 $sth->execute($import_record_id);
1437 my ($status) = $sth->fetchrow_array();
1444 =head2 SetImportRecordStatus
1446 SetImportRecordStatus($import_record_id, $new_status);
1450 sub SetImportRecordStatus {
1451 my ($import_record_id, $new_status) = @_;
1453 my $dbh = C4::Context->dbh;
1454 my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1455 $sth->execute($new_status, $import_record_id);
1460 =head2 GetImportRecordMatches
1462 my $results = GetImportRecordMatches($import_record_id, $best_only);
1466 sub GetImportRecordMatches {
1467 my $import_record_id = shift;
1468 my $best_only = @_ ? shift : 0;
1470 my $dbh = C4::Context->dbh;
1471 # FIXME currently biblio only
1472 my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber,
1473 candidate_match_id, score, record_type,
1476 JOIN import_record_matches USING (import_record_id)
1477 LEFT JOIN biblio ON (biblionumber = candidate_match_id)
1478 WHERE import_record_id = ?
1479 ORDER BY score DESC, biblionumber DESC");
1480 $sth->bind_param(1, $import_record_id);
1483 while (my $row = $sth->fetchrow_hashref) {
1484 if ($row->{'record_type'} eq 'auth') {
1485 $row->{'authorized_heading'} = C4::AuthoritiesMarc::GetAuthorizedHeading( { authid => $row->{'candidate_match_id'} } );
1487 next if ($row->{'record_type'} eq 'biblio' && not $row->{'biblionumber'});
1488 push @$results, $row;
1497 =head2 SetImportRecordMatches
1499 SetImportRecordMatches($import_record_id, @matches);
1503 sub SetImportRecordMatches {
1504 my $import_record_id = shift;
1507 my $dbh = C4::Context->dbh;
1508 my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1509 $delsth->execute($import_record_id);
1512 my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score, chosen)
1513 VALUES (?, ?, ?, ?)");
1514 my $chosen = 1; #The first match is defaulted to be chosen
1515 foreach my $match (@matches) {
1516 $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'}, $chosen);
1517 $chosen = 0; #After the first we do not default to other matches
1521 =head2 RecordsFromISO2709File
1523 my ($errors, $records) = C4::ImportBatch::RecordsFromISO2709File($input_file, $record_type, $encoding);
1525 Reads ISO2709 binary porridge from the given file and creates MARC::Record-objects out of it.
1527 @PARAM1, String, absolute path to the ISO2709 file.
1528 @PARAM2, String, see stage_file.pl
1529 @PARAM3, String, should be utf8
1531 Returns two array refs.
1535 sub RecordsFromISO2709File {
1536 my ($input_file, $record_type, $encoding) = @_;
1539 my $marc_type = C4::Context->preference('marcflavour');
1540 $marc_type .= 'AUTH' if ($marc_type eq 'UNIMARC' && $record_type eq 'auth');
1542 open my $fh, '<', $input_file or die "$0: cannot open input file $input_file: $!\n";
1548 next unless $_; # skip if record has only whitespace, as might occur
1549 # if file includes newlines between each MARC record
1550 my ($marc_record, $charset_guessed, $char_errors) = MarcToUTF8Record($_, $marc_type, $encoding);
1551 push @marc_records, $marc_record;
1552 if ($charset_guessed ne $encoding) {
1554 "Unexpected charset $charset_guessed, expecting $encoding";
1558 return ( \@errors, \@marc_records );
1561 =head2 RecordsFromMARCXMLFile
1563 my ($errors, $records) = C4::ImportBatch::RecordsFromMARCXMLFile($input_file, $encoding);
1565 Creates MARC::Record-objects out of the given MARCXML-file.
1567 @PARAM1, String, absolute path to the ISO2709 file.
1568 @PARAM2, String, should be utf8
1570 Returns two array refs.
1574 sub RecordsFromMARCXMLFile {
1575 my ( $filename, $encoding ) = @_;
1576 my $batch = MARC::File::XML->in( $filename );
1577 my ( @marcRecords, @errors, $record );
1579 eval { $record = $batch->next( $encoding ); };
1583 push @marcRecords, $record if $record;
1585 return (\@errors, \@marcRecords);
1588 =head2 RecordsFromMarcPlugin
1590 Converts text of input_file into array of MARC records with to_marc plugin
1594 sub RecordsFromMarcPlugin {
1595 my ($input_file, $plugin_class, $encoding) = @_;
1596 my ( $text, @return );
1597 return \@return if !$input_file || !$plugin_class;
1600 open my $fh, '<', $input_file or die "$0: cannot open input file $input_file: $!\n";
1610 # Convert to large MARC blob with plugin
1611 $text = Koha::Plugins::Handler->run({
1612 class => $plugin_class,
1613 method => 'to_marc',
1614 params => { data => $text },
1617 # Convert to array of MARC records
1619 my $marc_type = C4::Context->preference('marcflavour');
1620 foreach my $blob ( split(/\x1D/, $text) ) {
1621 next if $blob =~ /^\s*$/;
1622 my ($marcrecord) = MarcToUTF8Record($blob, $marc_type, $encoding);
1623 push @return, $marcrecord;
1629 # internal functions
1631 sub _create_import_record {
1632 my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $marc_type) = @_;
1634 my $dbh = C4::Context->dbh;
1635 my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, marcxml_old,
1636 record_type, encoding)
1637 VALUES (?, ?, ?, ?, ?, ?, ?)");
1638 $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml($marc_type), '',
1639 $record_type, $encoding);
1640 my $import_record_id = $dbh->{'mysql_insertid'};
1642 return $import_record_id;
1645 sub _update_import_record_marc {
1646 my ($import_record_id, $marc_record, $marc_type) = @_;
1648 my $dbh = C4::Context->dbh;
1649 my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1650 WHERE import_record_id = ?");
1651 $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml($marc_type), $import_record_id);
1655 sub _add_auth_fields {
1656 my ($import_record_id, $marc_record) = @_;
1659 if ($marc_record->field('001')) {
1660 $controlnumber = $marc_record->field('001')->data();
1662 my $authorized_heading = C4::AuthoritiesMarc::GetAuthorizedHeading({ record => $marc_record });
1663 my $dbh = C4::Context->dbh;
1664 my $sth = $dbh->prepare("INSERT INTO import_auths (import_record_id, control_number, authorized_heading) VALUES (?, ?, ?)");
1665 $sth->execute($import_record_id, $controlnumber, $authorized_heading);
1669 sub _add_biblio_fields {
1670 my ($import_record_id, $marc_record) = @_;
1672 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1673 my $dbh = C4::Context->dbh;
1674 # FIXME no controlnumber, originalsource
1675 $isbn = C4::Koha::GetNormalizedISBN($isbn);
1676 my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1677 $sth->execute($import_record_id, $title, $author, $isbn, $issn) or die $sth->errstr;
1682 sub _update_biblio_fields {
1683 my ($import_record_id, $marc_record) = @_;
1685 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1686 my $dbh = C4::Context->dbh;
1687 # FIXME no controlnumber, originalsource
1688 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1692 my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1693 WHERE import_record_id = ?");
1694 $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1698 sub _parse_biblio_fields {
1699 my ($marc_record) = @_;
1701 my $dbh = C4::Context->dbh;
1702 my $bibliofields = TransformMarcToKoha($marc_record, '');
1703 return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1707 sub _update_batch_record_counts {
1708 my ($batch_id) = @_;
1710 my $dbh = C4::Context->dbh;
1711 my $sth = $dbh->prepare_cached("UPDATE import_batches SET
1715 WHERE import_batch_id = import_batches.import_batch_id),
1719 JOIN import_items USING (import_record_id)
1720 WHERE import_batch_id = import_batches.import_batch_id
1721 AND record_type = 'biblio')
1722 WHERE import_batch_id = ?");
1723 $sth->bind_param(1, $batch_id);
1728 sub _get_commit_action {
1729 my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id, $record_type) = @_;
1731 if ($record_type eq 'biblio') {
1732 my ($bib_result, $bib_match, $item_result);
1734 $bib_match = GetBestRecordMatch($import_record_id);
1735 if ($overlay_status ne 'no_match' && defined($bib_match)) {
1737 $bib_result = $overlay_action;
1739 if($item_action eq 'always_add' or $item_action eq 'add_only_for_matches'){
1740 $item_result = 'create_new';
1741 } elsif($item_action eq 'replace'){
1742 $item_result = 'replace';
1744 $item_result = 'ignore';
1748 $bib_result = $nomatch_action;
1749 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new') ? 'create_new' : 'ignore';
1751 return ($bib_result, $item_result, $bib_match);
1752 } else { # must be auths
1753 my ($auth_result, $auth_match);
1755 $auth_match = GetBestRecordMatch($import_record_id);
1756 if ($overlay_status ne 'no_match' && defined($auth_match)) {
1757 $auth_result = $overlay_action;
1759 $auth_result = $nomatch_action;
1762 return ($auth_result, undef, $auth_match);
1767 sub _get_revert_action {
1768 my ($overlay_action, $overlay_status, $status) = @_;
1772 if ($status eq 'ignored') {
1773 $bib_result = 'ignore';
1775 if ($overlay_action eq 'create_new') {
1776 $bib_result = 'delete';
1778 $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1789 Koha Development Team <http://koha-community.org/>
1791 Galen Charlton <galen.charlton@liblime.com>