bug-2149, added new block to C4::Letters::SendAlerts() to email 'account creation...
[koha.git] / C4 / ImportBatch.pm
blob9f9c7f645e0cd3f821fc4b63963f513f4fca1eed
1 package C4::ImportBatch;
3 # Copyright (C) 2007 LibLime
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
20 use strict;
21 use C4::Context;
22 use C4::Koha;
23 use C4::Biblio;
24 use C4::Items;
25 use C4::Charset;
27 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
29 BEGIN {
30 # set the version for version checking
31 $VERSION = 3.01;
32 require Exporter;
33 @ISA = qw(Exporter);
34 @EXPORT = qw(
35 GetZ3950BatchId
36 GetImportRecordMarc
37 AddImportBatch
38 GetImportBatch
39 AddBiblioToBatch
40 ModBiblioInBatch
42 BatchStageMarcRecords
43 BatchFindBibDuplicates
44 BatchCommitBibRecords
45 BatchRevertBibRecords
47 GetAllImportBatches
48 GetImportBatchRangeDesc
49 GetNumberOfNonZ3950ImportBatches
50 GetImportBibliosRange
51 GetItemNumbersFromImportBatch
53 GetImportBatchStatus
54 SetImportBatchStatus
55 GetImportBatchOverlayAction
56 SetImportBatchOverlayAction
57 GetImportBatchNoMatchAction
58 SetImportBatchNoMatchAction
59 GetImportBatchItemAction
60 SetImportBatchItemAction
61 GetImportBatchMatcher
62 SetImportBatchMatcher
63 GetImportRecordOverlayStatus
64 SetImportRecordOverlayStatus
65 GetImportRecordStatus
66 SetImportRecordStatus
67 GetImportRecordMatches
68 SetImportRecordMatches
72 =head1 NAME
74 C4::ImportBatch - manage batches of imported MARC records
76 =head1 SYNOPSIS
78 =over 4
80 use C4::ImportBatch;
82 =back
84 =head1 FUNCTIONS
86 =head2 GetZ3950BatchId
88 =over 4
90 my $batchid = GetZ3950BatchId($z3950server);
92 =back
94 Retrieves the ID of the import batch for the Z39.50
95 reservoir for the given target. If necessary,
96 creates the import batch.
98 =cut
100 sub GetZ3950BatchId {
101 my ($z3950server) = @_;
103 my $dbh = C4::Context->dbh;
104 my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
105 WHERE batch_type = 'z3950'
106 AND file_name = ?");
107 $sth->execute($z3950server);
108 my $rowref = $sth->fetchrow_arrayref();
109 $sth->finish();
110 if (defined $rowref) {
111 return $rowref->[0];
112 } else {
113 my $batch_id = AddImportBatch('create_new', 'staged', 'z3950', $z3950server, '');
114 return $batch_id;
119 =head2 GetImportRecordMarc
121 =over 4
123 my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
125 =back
127 =cut
129 sub GetImportRecordMarc {
130 my ($import_record_id) = @_;
132 my $dbh = C4::Context->dbh;
133 my $sth = $dbh->prepare("SELECT marc, encoding FROM import_records WHERE import_record_id = ?");
134 $sth->execute($import_record_id);
135 my ($marc, $encoding) = $sth->fetchrow();
136 $sth->finish();
137 return $marc;
141 =head2 AddImportBatch
143 =over 4
145 my $batch_id = AddImportBatch($overlay_action, $import_status, $type, $file_name, $comments);
147 =back
149 =cut
151 sub AddImportBatch {
152 my ($overlay_action, $import_status, $type, $file_name, $comments) = @_;
154 my $dbh = C4::Context->dbh;
155 my $sth = $dbh->prepare("INSERT INTO import_batches (overlay_action, import_status, batch_type,
156 file_name, comments)
157 VALUES (?, ?, ?, ?, ?)");
158 $sth->execute($overlay_action, $import_status, $type, $file_name, $comments);
159 my $batch_id = $dbh->{'mysql_insertid'};
160 $sth->finish();
162 return $batch_id;
166 =head2 GetImportBatch
168 =over 4
170 my $row = GetImportBatch($batch_id);
172 =back
174 Retrieve a hashref of an import_batches row.
176 =cut
178 sub GetImportBatch {
179 my ($batch_id) = @_;
181 my $dbh = C4::Context->dbh;
182 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches WHERE import_batch_id = ?");
183 $sth->bind_param(1, $batch_id);
184 $sth->execute();
185 my $result = $sth->fetchrow_hashref;
186 $sth->finish();
187 return $result;
191 =head2 AddBiblioToBatch
193 =over 4
195 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence, $marc_record, $encoding, $z3950random, $update_counts);
197 =back
199 =cut
201 sub AddBiblioToBatch {
202 my $batch_id = shift;
203 my $record_sequence = shift;
204 my $marc_record = shift;
205 my $encoding = shift;
206 my $z3950random = shift;
207 my $update_counts = @_ ? shift : 1;
209 my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, $z3950random);
210 _add_biblio_fields($import_record_id, $marc_record);
211 _update_batch_record_counts($batch_id) if $update_counts;
212 return $import_record_id;
215 =head2 ModBiblioInBatch
217 =over 4
219 ModBiblioInBatch($import_record_id, $marc_record);
221 =back
223 =cut
225 sub ModBiblioInBatch {
226 my ($import_record_id, $marc_record) = @_;
228 _update_import_record_marc($import_record_id, $marc_record);
229 _update_biblio_fields($import_record_id, $marc_record);
233 =head2 BatchStageMarcRecords
235 =over 4
237 ($batch_id, $num_records, $num_items, @invalid_records) =
238 BatchStageMarcRecords($marc_flavor, $marc_records, $file_name,
239 $comments, $branch_code, $parse_items,
240 $leave_as_staging,
241 $progress_interval, $progress_callback);
243 =back
245 =cut
247 sub BatchStageMarcRecords {
248 my $marc_flavor = shift;
249 my $marc_records = shift;
250 my $file_name = shift;
251 my $comments = shift;
252 my $branch_code = shift;
253 my $parse_items = shift;
254 my $leave_as_staging = shift;
256 # optional callback to monitor status
257 # of job
258 my $progress_interval = 0;
259 my $progress_callback = undef;
260 if ($#_ == 1) {
261 $progress_interval = shift;
262 $progress_callback = shift;
263 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
264 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
267 my $batch_id = AddImportBatch('create_new', 'staging', 'batch', $file_name, $comments);
268 if ($parse_items) {
269 SetImportBatchItemAction($batch_id, 'always_add');
270 } else {
271 SetImportBatchItemAction($batch_id, 'ignore');
274 my @invalid_records = ();
275 my $num_valid = 0;
276 my $num_items = 0;
277 # FIXME - for now, we're dealing only with bibs
278 my $rec_num = 0;
279 foreach my $marc_blob (split(/\x1D/, $marc_records)) {
280 $marc_blob =~ s/^\s+//g;
281 $marc_blob =~ s/\s+$//g;
282 next unless $marc_blob;
283 $rec_num++;
284 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
285 &$progress_callback($rec_num);
287 my ($marc_record, $charset_guessed, $char_errors) =
288 MarcToUTF8Record($marc_blob, C4::Context->preference("marcflavour"));
289 my $import_record_id;
290 if (scalar($marc_record->fields()) == 0) {
291 push @invalid_records, $marc_blob;
292 } else {
293 $num_valid++;
294 $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $marc_flavor, int(rand(99999)), 0);
295 if ($parse_items) {
296 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
297 $num_items += scalar(@import_items_ids);
301 unless ($leave_as_staging) {
302 SetImportBatchStatus($batch_id, 'staged');
304 # FIXME branch_code, number of bibs, number of items
305 _update_batch_record_counts($batch_id);
306 return ($batch_id, $num_valid, $num_items, @invalid_records);
309 =head2 AddItemsToImportBiblio
311 =over 4
313 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, $update_counts);
315 =back
317 =cut
319 sub AddItemsToImportBiblio {
320 my $batch_id = shift;
321 my $import_record_id = shift;
322 my $marc_record = shift;
323 my $update_counts = @_ ? shift : 0;
325 my @import_items_ids = ();
327 my $dbh = C4::Context->dbh;
328 my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
329 foreach my $item_field ($marc_record->field($item_tag)) {
330 my $item_marc = MARC::Record->new();
331 $item_marc->leader("00000 a "); # must set Leader/09 to 'a'
332 $item_marc->append_fields($item_field);
333 $marc_record->delete_field($item_field);
334 my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
335 VALUES (?, ?, ?)");
336 $sth->bind_param(1, $import_record_id);
337 $sth->bind_param(2, 'staged');
338 $sth->bind_param(3, $item_marc->as_xml());
339 $sth->execute();
340 push @import_items_ids, $dbh->{'mysql_insertid'};
341 $sth->finish();
344 if ($#import_items_ids > -1) {
345 _update_batch_record_counts($batch_id) if $update_counts;
346 _update_import_record_marc($import_record_id, $marc_record);
348 return @import_items_ids;
351 =head2 BatchFindBibDuplicates
353 =over 4
355 my $num_with_matches = BatchFindBibDuplicates($batch_id, $matcher, $max_matches, $progress_interval, $progress_callback);
357 =back
359 Goes through the records loaded in the batch and attempts to
360 find duplicates for each one. Sets the matching status
361 of each record to "no_match" or "auto_match" as appropriate.
363 The $max_matches parameter is optional; if it is not supplied,
364 it defaults to 10.
366 The $progress_interval and $progress_callback parameters are
367 optional; if both are supplied, the sub referred to by
368 $progress_callback will be invoked every $progress_interval
369 records using the number of records processed as the
370 singular argument.
372 =cut
374 sub BatchFindBibDuplicates {
375 my $batch_id = shift;
376 my $matcher = shift;
377 my $max_matches = @_ ? shift : 10;
379 # optional callback to monitor status
380 # of job
381 my $progress_interval = 0;
382 my $progress_callback = undef;
383 if ($#_ == 1) {
384 $progress_interval = shift;
385 $progress_callback = shift;
386 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
387 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
390 my $dbh = C4::Context->dbh;
392 my $sth = $dbh->prepare("SELECT import_record_id, marc
393 FROM import_records
394 JOIN import_biblios USING (import_record_id)
395 WHERE import_batch_id = ?");
396 $sth->execute($batch_id);
397 my $num_with_matches = 0;
398 my $rec_num = 0;
399 while (my $rowref = $sth->fetchrow_hashref) {
400 $rec_num++;
401 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
402 &$progress_callback($rec_num);
404 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
405 my @matches = ();
406 if (defined $matcher) {
407 @matches = $matcher->get_matches($marc_record, $max_matches);
409 if (scalar(@matches) > 0) {
410 $num_with_matches++;
411 SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
412 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
413 } else {
414 SetImportRecordMatches($rowref->{'import_record_id'}, ());
415 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
418 $sth->finish();
419 return $num_with_matches;
422 =head2 BatchCommitBibRecords
424 =over 4
426 my ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored) =
427 BatchCommitBibRecords($batch_id, $progress_interval, $progress_callback);
429 =back
431 =cut
433 sub BatchCommitBibRecords {
434 my $batch_id = shift;
436 # optional callback to monitor status
437 # of job
438 my $progress_interval = 0;
439 my $progress_callback = undef;
440 if ($#_ == 1) {
441 $progress_interval = shift;
442 $progress_callback = shift;
443 $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
444 $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
447 my $num_added = 0;
448 my $num_updated = 0;
449 my $num_items_added = 0;
450 my $num_items_errored = 0;
451 my $num_ignored = 0;
452 # commit (i.e., save, all records in the batch)
453 # FIXME biblio only at the moment
454 SetImportBatchStatus('importing');
455 my $overlay_action = GetImportBatchOverlayAction($batch_id);
456 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
457 my $item_action = GetImportBatchItemAction($batch_id);
458 my $dbh = C4::Context->dbh;
459 my $sth = $dbh->prepare("SELECT import_record_id, status, overlay_status, marc, encoding
460 FROM import_records
461 JOIN import_biblios USING (import_record_id)
462 WHERE import_batch_id = ?");
463 $sth->execute($batch_id);
464 my $rec_num = 0;
465 while (my $rowref = $sth->fetchrow_hashref) {
466 $rec_num++;
467 if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
468 &$progress_callback($rec_num);
470 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
471 $num_ignored++;
474 my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
476 # remove any item tags - rely on BatchCommitItems
477 my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
478 foreach my $item_field ($marc_record->field($item_tag)) {
479 $marc_record->delete_field($item_field);
482 # decide what what to do with the bib and item records
483 my ($bib_result, $item_result, $bib_match) =
484 _get_commit_action($overlay_action, $nomatch_action, $item_action,
485 $rowref->{'overlay_status'}, $rowref->{'import_record_id'});
487 if ($bib_result eq 'create_new') {
488 $num_added++;
489 my ($biblionumber, $biblioitemnumber) = AddBiblio($marc_record, '');
490 my $sth = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
491 $sth->execute($biblionumber, $rowref->{'import_record_id'});
492 $sth->finish();
493 if ($item_result eq 'create_new') {
494 my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
495 $num_items_added += $bib_items_added;
496 $num_items_errored += $bib_items_errored;
498 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
499 } elsif ($bib_result eq 'replace') {
500 $num_updated++;
501 my $biblionumber = $bib_match;
502 my ($count, $oldbiblio) = GetBiblio($biblionumber);
503 my $oldxml = GetXmlBiblio($biblionumber);
505 # remove item fields so that they don't get
506 # added again if record is reverted
507 my $old_marc = MARC::Record->new_from_xml(StripNonXmlChars($oldxml), 'UTF-8', $rowref->{'encoding'});
508 foreach my $item_field ($old_marc->field($item_tag)) {
509 $old_marc->delete_field($item_field);
512 ModBiblio($marc_record, $biblionumber, $oldbiblio->{'frameworkcode'});
513 my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
514 $sth->execute($old_marc->as_xml(), $rowref->{'import_record_id'});
515 $sth->finish();
516 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
517 $sth2->execute($biblionumber, $rowref->{'import_record_id'});
518 $sth2->finish();
519 if ($item_result eq 'create_new') {
520 my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
521 $num_items_added += $bib_items_added;
522 $num_items_errored += $bib_items_errored;
524 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
525 SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
526 } elsif ($bib_result eq 'ignore') {
527 $num_ignored++;
528 my $biblionumber = $bib_match;
529 if (defined $biblionumber and $item_result eq 'create_new') {
530 my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
531 $num_items_added += $bib_items_added;
532 $num_items_errored += $bib_items_errored;
533 # still need to record the matched biblionumber so that the
534 # items can be reverted
535 my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
536 $sth2->execute($biblionumber, $rowref->{'import_record_id'});
537 SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
539 SetImportRecordStatus($rowref->{'import_record_id'}, 'ignored');
542 $sth->finish();
543 SetImportBatchStatus($batch_id, 'imported');
544 return ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored);
547 =head2 BatchCommitItems
549 =over 4
551 ($num_items_added, $num_items_errored) = BatchCommitItems($import_record_id, $biblionumber);
553 =back
555 =cut
557 sub BatchCommitItems {
558 my ($import_record_id, $biblionumber) = @_;
560 my $dbh = C4::Context->dbh;
562 my $num_items_added = 0;
563 my $num_items_errored = 0;
564 my $sth = $dbh->prepare("SELECT import_items_id, import_items.marcxml, encoding
565 FROM import_items
566 JOIN import_records USING (import_record_id)
567 WHERE import_record_id = ?
568 ORDER BY import_items_id");
569 $sth->bind_param(1, $import_record_id);
570 $sth->execute();
571 while (my $row = $sth->fetchrow_hashref()) {
572 my $item_marc = MARC::Record->new_from_xml(StripNonXmlChars($row->{'marcxml'}), 'UTF-8', $row->{'encoding'});
573 # FIXME - duplicate barcode check needs to become part of AddItemFromMarc()
574 my $item = TransformMarcToKoha($dbh, $item_marc);
575 my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
576 if ($duplicate_barcode) {
577 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, import_error = ? WHERE import_items_id = ?");
578 $updsth->bind_param(1, 'error');
579 $updsth->bind_param(2, 'duplicate item barcode');
580 $updsth->bind_param(3, $row->{'import_items_id'});
581 $updsth->execute();
582 $num_items_errored++;
583 } else {
584 my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItemFromMarc($item_marc, $biblionumber);
585 my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
586 $updsth->bind_param(1, 'imported');
587 $updsth->bind_param(2, $itemnumber);
588 $updsth->bind_param(3, $row->{'import_items_id'});
589 $updsth->execute();
590 $updsth->finish();
591 $num_items_added++;
594 $sth->finish();
595 return ($num_items_added, $num_items_errored);
598 =head2 BatchRevertBibRecords
600 =over 4
602 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored) = BatchRevertBibRecords($batch_id);
604 =back
606 =cut
608 sub BatchRevertBibRecords {
609 my $batch_id = shift;
611 my $num_deleted = 0;
612 my $num_errors = 0;
613 my $num_reverted = 0;
614 my $num_items_deleted = 0;
615 my $num_ignored = 0;
616 # commit (i.e., save, all records in the batch)
617 # FIXME biblio only at the moment
618 SetImportBatchStatus('reverting');
619 my $overlay_action = GetImportBatchOverlayAction($batch_id);
620 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
621 my $dbh = C4::Context->dbh;
622 my $sth = $dbh->prepare("SELECT import_record_id, status, overlay_status, marcxml_old, encoding, matched_biblionumber
623 FROM import_records
624 JOIN import_biblios USING (import_record_id)
625 WHERE import_batch_id = ?");
626 $sth->execute($batch_id);
627 while (my $rowref = $sth->fetchrow_hashref) {
628 if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
629 $num_ignored++;
632 my $bib_result = _get_revert_action($overlay_action, $rowref->{'overlay_status'}, $rowref->{'status'});
634 if ($bib_result eq 'delete') {
635 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
636 my $error = DelBiblio($rowref->{'matched_biblionumber'});
637 if (defined $error) {
638 $num_errors++;
639 } else {
640 $num_deleted++;
641 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
643 } elsif ($bib_result eq 'restore') {
644 $num_reverted++;
645 my $old_record = MARC::Record->new_from_xml(StripNonXmlChars($rowref->{'marcxml_old'}), 'UTF-8', $rowref->{'encoding'});
646 my $biblionumber = $rowref->{'matched_biblionumber'};
647 my ($count, $oldbiblio) = GetBiblio($biblionumber);
648 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
649 ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
650 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
651 } elsif ($bib_result eq 'ignore') {
652 $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
653 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
657 $sth->finish();
658 SetImportBatchStatus($batch_id, 'reverted');
659 return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
662 =head2 BatchRevertItems
664 =over 4
666 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
668 =back
670 =cut
672 sub BatchRevertItems {
673 my ($import_record_id, $biblionumber) = @_;
675 my $dbh = C4::Context->dbh;
676 my $num_items_deleted = 0;
678 my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
679 FROM import_items
680 JOIN items USING (itemnumber)
681 WHERE import_record_id = ?");
682 $sth->bind_param(1, $import_record_id);
683 $sth->execute();
684 while (my $row = $sth->fetchrow_hashref()) {
685 DelItem($dbh, $biblionumber, $row->{'itemnumber'});
686 my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
687 $updsth->bind_param(1, 'reverted');
688 $updsth->bind_param(2, $row->{'import_items_id'});
689 $updsth->execute();
690 $updsth->finish();
691 $num_items_deleted++;
693 $sth->finish();
694 return $num_items_deleted;
697 =head2 GetAllImportBatches
699 =over 4
701 my $results = GetAllImportBatches();
703 =back
705 Returns a references to an array of hash references corresponding
706 to all import_batches rows (of batch_type 'batch'), sorted in
707 ascending order by import_batch_id.
709 =cut
711 sub GetAllImportBatches {
712 my $dbh = C4::Context->dbh;
713 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
714 WHERE batch_type = 'batch'
715 ORDER BY import_batch_id ASC");
717 my $results = [];
718 $sth->execute();
719 while (my $row = $sth->fetchrow_hashref) {
720 push @$results, $row;
722 $sth->finish();
723 return $results;
726 =head2 GetImportBatchRangeDesc
728 =over 4
730 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
732 =back
734 Returns a reference to an array of hash references corresponding to
735 import_batches rows (sorted in descending order by import_batch_id)
736 start at the given offset.
738 =cut
740 sub GetImportBatchRangeDesc {
741 my ($offset, $results_per_group) = @_;
743 my $dbh = C4::Context->dbh;
744 my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
745 WHERE batch_type = 'batch'
746 ORDER BY import_batch_id DESC
747 LIMIT ? OFFSET ?");
748 $sth->bind_param(1, $results_per_group);
749 $sth->bind_param(2, $offset);
751 my $results = [];
752 $sth->execute();
753 while (my $row = $sth->fetchrow_hashref) {
754 push @$results, $row;
756 $sth->finish();
757 return $results;
760 =head2 GetItemNumbersFromImportBatch
762 =over 4
763 =back
764 =cut
766 sub GetItemNumbersFromImportBatch {
767 my ($batch_id) = @_;
768 my $dbh = C4::Context->dbh;
769 my $sth = $dbh->prepare("select itemnumber from import_batches,import_records,import_items where import_batches.import_batch_id=import_records.import_batch_id and import_records.import_record_id=import_items.import_record_id and import_batches.import_batch_id=?");
770 $sth->execute($batch_id);
771 my @items ;
772 while ( my ($itm) = $sth->fetchrow_array ) {
773 push @items, $itm;
775 return @items;
778 =head2 GetNumberOfImportBatches
780 =over 4
782 my $count = GetNumberOfImportBatches();
784 =back
786 =cut
788 sub GetNumberOfNonZ3950ImportBatches {
789 my $dbh = C4::Context->dbh;
790 my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type='batch'");
791 $sth->execute();
792 my ($count) = $sth->fetchrow_array();
793 $sth->finish();
794 return $count;
797 =head2 GetImportBibliosRange
799 =over 4
801 my $results = GetImportBibliosRange($batch_id, $offset, $results_per_group);
803 =back
805 Returns a reference to an array of hash references corresponding to
806 import_biblios/import_records rows for a given batch
807 starting at the given offset.
809 =cut
811 sub GetImportBibliosRange {
812 my ($batch_id, $offset, $results_per_group) = @_;
814 my $dbh = C4::Context->dbh;
815 my $sth = $dbh->prepare_cached("SELECT title, author, isbn, issn, import_record_id, record_sequence,
816 status, overlay_status
817 FROM import_records
818 JOIN import_biblios USING (import_record_id)
819 WHERE import_batch_id = ?
820 ORDER BY import_record_id LIMIT ? OFFSET ?");
821 $sth->bind_param(1, $batch_id);
822 $sth->bind_param(2, $results_per_group);
823 $sth->bind_param(3, $offset);
824 my $results = [];
825 $sth->execute();
826 while (my $row = $sth->fetchrow_hashref) {
827 push @$results, $row;
829 $sth->finish();
830 return $results;
834 =head2 GetBestRecordMatch
836 =over 4
838 my $record_id = GetBestRecordMatch($import_record_id);
840 =back
842 =cut
844 sub GetBestRecordMatch {
845 my ($import_record_id) = @_;
847 my $dbh = C4::Context->dbh;
848 my $sth = $dbh->prepare("SELECT candidate_match_id
849 FROM import_record_matches
850 WHERE import_record_id = ?
851 ORDER BY score DESC, candidate_match_id DESC");
852 $sth->execute($import_record_id);
853 my ($record_id) = $sth->fetchrow_array();
854 $sth->finish();
855 return $record_id;
858 =head2 GetImportBatchStatus
860 =over 4
862 my $status = GetImportBatchStatus($batch_id);
864 =back
866 =cut
868 sub GetImportBatchStatus {
869 my ($batch_id) = @_;
871 my $dbh = C4::Context->dbh;
872 my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE batch_id = ?");
873 $sth->execute($batch_id);
874 my ($status) = $sth->fetchrow_array();
875 $sth->finish();
876 return;
881 =head2 SetImportBatchStatus
883 =over 4
885 SetImportBatchStatus($batch_id, $new_status);
887 =back
889 =cut
891 sub SetImportBatchStatus {
892 my ($batch_id, $new_status) = @_;
894 my $dbh = C4::Context->dbh;
895 my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
896 $sth->execute($new_status, $batch_id);
897 $sth->finish();
901 =head2 GetImportBatchOverlayAction
903 =over 4
905 my $overlay_action = GetImportBatchOverlayAction($batch_id);
907 =back
909 =cut
911 sub GetImportBatchOverlayAction {
912 my ($batch_id) = @_;
914 my $dbh = C4::Context->dbh;
915 my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
916 $sth->execute($batch_id);
917 my ($overlay_action) = $sth->fetchrow_array();
918 $sth->finish();
919 return $overlay_action;
924 =head2 SetImportBatchOverlayAction
926 =over 4
928 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
930 =back
932 =cut
934 sub SetImportBatchOverlayAction {
935 my ($batch_id, $new_overlay_action) = @_;
937 my $dbh = C4::Context->dbh;
938 my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
939 $sth->execute($new_overlay_action, $batch_id);
940 $sth->finish();
944 =head2 GetImportBatchNoMatchAction
946 =over 4
948 my $nomatch_action = GetImportBatchNoMatchAction($batch_id);
950 =back
952 =cut
954 sub GetImportBatchNoMatchAction {
955 my ($batch_id) = @_;
957 my $dbh = C4::Context->dbh;
958 my $sth = $dbh->prepare("SELECT nomatch_action FROM import_batches WHERE import_batch_id = ?");
959 $sth->execute($batch_id);
960 my ($nomatch_action) = $sth->fetchrow_array();
961 $sth->finish();
962 return $nomatch_action;
967 =head2 SetImportBatchNoMatchAction
969 =over 4
971 SetImportBatchNoMatchAction($batch_id, $new_nomatch_action);
973 =back
975 =cut
977 sub SetImportBatchNoMatchAction {
978 my ($batch_id, $new_nomatch_action) = @_;
980 my $dbh = C4::Context->dbh;
981 my $sth = $dbh->prepare("UPDATE import_batches SET nomatch_action = ? WHERE import_batch_id = ?");
982 $sth->execute($new_nomatch_action, $batch_id);
983 $sth->finish();
987 =head2 GetImportBatchItemAction
989 =over 4
991 my $item_action = GetImportBatchItemAction($batch_id);
993 =back
995 =cut
997 sub GetImportBatchItemAction {
998 my ($batch_id) = @_;
1000 my $dbh = C4::Context->dbh;
1001 my $sth = $dbh->prepare("SELECT item_action FROM import_batches WHERE import_batch_id = ?");
1002 $sth->execute($batch_id);
1003 my ($item_action) = $sth->fetchrow_array();
1004 $sth->finish();
1005 return $item_action;
1010 =head2 SetImportBatchItemAction
1012 =over 4
1014 SetImportBatchItemAction($batch_id, $new_item_action);
1016 =back
1018 =cut
1020 sub SetImportBatchItemAction {
1021 my ($batch_id, $new_item_action) = @_;
1023 my $dbh = C4::Context->dbh;
1024 my $sth = $dbh->prepare("UPDATE import_batches SET item_action = ? WHERE import_batch_id = ?");
1025 $sth->execute($new_item_action, $batch_id);
1026 $sth->finish();
1030 =head2 GetImportBatchMatcher
1032 =over 4
1034 my $matcher_id = GetImportBatchMatcher($batch_id);
1036 =back
1038 =cut
1040 sub GetImportBatchMatcher {
1041 my ($batch_id) = @_;
1043 my $dbh = C4::Context->dbh;
1044 my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
1045 $sth->execute($batch_id);
1046 my ($matcher_id) = $sth->fetchrow_array();
1047 $sth->finish();
1048 return $matcher_id;
1053 =head2 SetImportBatchMatcher
1055 =over 4
1057 SetImportBatchMatcher($batch_id, $new_matcher_id);
1059 =back
1061 =cut
1063 sub SetImportBatchMatcher {
1064 my ($batch_id, $new_matcher_id) = @_;
1066 my $dbh = C4::Context->dbh;
1067 my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
1068 $sth->execute($new_matcher_id, $batch_id);
1069 $sth->finish();
1073 =head2 GetImportRecordOverlayStatus
1075 =over 4
1077 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
1079 =back
1081 =cut
1083 sub GetImportRecordOverlayStatus {
1084 my ($import_record_id) = @_;
1086 my $dbh = C4::Context->dbh;
1087 my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
1088 $sth->execute($import_record_id);
1089 my ($overlay_status) = $sth->fetchrow_array();
1090 $sth->finish();
1091 return $overlay_status;
1096 =head2 SetImportRecordOverlayStatus
1098 =over 4
1100 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
1102 =back
1104 =cut
1106 sub SetImportRecordOverlayStatus {
1107 my ($import_record_id, $new_overlay_status) = @_;
1109 my $dbh = C4::Context->dbh;
1110 my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
1111 $sth->execute($new_overlay_status, $import_record_id);
1112 $sth->finish();
1116 =head2 GetImportRecordStatus
1118 =over 4
1120 my $overlay_status = GetImportRecordStatus($import_record_id);
1122 =back
1124 =cut
1126 sub GetImportRecordStatus {
1127 my ($import_record_id) = @_;
1129 my $dbh = C4::Context->dbh;
1130 my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
1131 $sth->execute($import_record_id);
1132 my ($overlay_status) = $sth->fetchrow_array();
1133 $sth->finish();
1134 return $overlay_status;
1139 =head2 SetImportRecordStatus
1141 =over 4
1143 SetImportRecordStatus($import_record_id, $new_overlay_status);
1145 =back
1147 =cut
1149 sub SetImportRecordStatus {
1150 my ($import_record_id, $new_overlay_status) = @_;
1152 my $dbh = C4::Context->dbh;
1153 my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1154 $sth->execute($new_overlay_status, $import_record_id);
1155 $sth->finish();
1159 =head2 GetImportRecordMatches
1161 =over 4
1163 my $results = GetImportRecordMatches($import_record_id, $best_only);
1165 =back
1167 =cut
1169 sub GetImportRecordMatches {
1170 my $import_record_id = shift;
1171 my $best_only = @_ ? shift : 0;
1173 my $dbh = C4::Context->dbh;
1174 # FIXME currently biblio only
1175 my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber, score
1176 FROM import_records
1177 JOIN import_record_matches USING (import_record_id)
1178 JOIN biblio ON (biblionumber = candidate_match_id)
1179 WHERE import_record_id = ?
1180 ORDER BY score DESC, biblionumber DESC");
1181 $sth->bind_param(1, $import_record_id);
1182 my $results = [];
1183 $sth->execute();
1184 while (my $row = $sth->fetchrow_hashref) {
1185 push @$results, $row;
1186 last if $best_only;
1188 $sth->finish();
1190 return $results;
1195 =head2 SetImportRecordMatches
1197 =over 4
1199 SetImportRecordMatches($import_record_id, @matches);
1201 =back
1203 =cut
1205 sub SetImportRecordMatches {
1206 my $import_record_id = shift;
1207 my @matches = @_;
1209 my $dbh = C4::Context->dbh;
1210 my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1211 $delsth->execute($import_record_id);
1212 $delsth->finish();
1214 my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1215 VALUES (?, ?, ?)");
1216 foreach my $match (@matches) {
1217 $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1222 # internal functions
1224 sub _create_import_record {
1225 my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random) = @_;
1227 my $dbh = C4::Context->dbh;
1228 my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml,
1229 record_type, encoding, z3950random)
1230 VALUES (?, ?, ?, ?, ?, ?, ?)");
1231 $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml(),
1232 $record_type, $encoding, $z3950random);
1233 my $import_record_id = $dbh->{'mysql_insertid'};
1234 $sth->finish();
1235 return $import_record_id;
1238 sub _update_import_record_marc {
1239 my ($import_record_id, $marc_record) = @_;
1241 my $dbh = C4::Context->dbh;
1242 my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1243 WHERE import_record_id = ?");
1244 $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml(), $import_record_id);
1245 $sth->finish();
1248 sub _add_biblio_fields {
1249 my ($import_record_id, $marc_record) = @_;
1251 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1252 my $dbh = C4::Context->dbh;
1253 # FIXME no controlnumber, originalsource
1254 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1255 $isbn =~ s/\(.*$//;
1256 $isbn =~ tr/ -_//;
1257 $isbn = uc $isbn;
1258 my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1259 $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1260 $sth->finish();
1264 sub _update_biblio_fields {
1265 my ($import_record_id, $marc_record) = @_;
1267 my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1268 my $dbh = C4::Context->dbh;
1269 # FIXME no controlnumber, originalsource
1270 # FIXME 2 - should regularize normalization of ISBN wherever it is done
1271 $isbn =~ s/\(.*$//;
1272 $isbn =~ tr/ -_//;
1273 $isbn = uc $isbn;
1274 my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1275 WHERE import_record_id = ?");
1276 $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1277 $sth->finish();
1280 sub _parse_biblio_fields {
1281 my ($marc_record) = @_;
1283 my $dbh = C4::Context->dbh;
1284 my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1285 return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1289 sub _update_batch_record_counts {
1290 my ($batch_id) = @_;
1292 my $dbh = C4::Context->dbh;
1293 my $sth = $dbh->prepare_cached("UPDATE import_batches SET num_biblios = (
1294 SELECT COUNT(*)
1295 FROM import_records
1296 WHERE import_batch_id = import_batches.import_batch_id
1297 AND record_type = 'biblio')
1298 WHERE import_batch_id = ?");
1299 $sth->bind_param(1, $batch_id);
1300 $sth->execute();
1301 $sth->finish();
1302 $sth = $dbh->prepare_cached("UPDATE import_batches SET num_items = (
1303 SELECT COUNT(*)
1304 FROM import_records
1305 JOIN import_items USING (import_record_id)
1306 WHERE import_batch_id = import_batches.import_batch_id
1307 AND record_type = 'biblio')
1308 WHERE import_batch_id = ?");
1309 $sth->bind_param(1, $batch_id);
1310 $sth->execute();
1311 $sth->finish();
1315 sub _get_commit_action {
1316 my ($overlay_action, $nomatch_action, $item_action, $overlay_status, $import_record_id) = @_;
1318 my ($bib_result, $bib_match, $item_result);
1320 if ($overlay_status ne 'no_match') {
1321 $bib_match = GetBestRecordMatch($import_record_id);
1322 if ($overlay_action eq 'replace') {
1323 $bib_result = defined($bib_match) ? 'replace' : 'create_new';
1324 } elsif ($overlay_action eq 'create_new') {
1325 $bib_result = 'create_new';
1326 } elsif ($overlay_action eq 'ignore') {
1327 $bib_result = 'ignore';
1329 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_matches') ? 'create_new' : 'ignore';
1330 } else {
1331 $bib_result = $nomatch_action;
1332 $item_result = ($item_action eq 'always_add' or $item_action eq 'add_only_for_new') ? 'create_new' : 'ignore';
1335 return ($bib_result, $item_result, $bib_match);
1338 sub _get_revert_action {
1339 my ($overlay_action, $overlay_status, $status) = @_;
1341 my $bib_result;
1343 if ($status eq 'ignored') {
1344 $bib_result = 'ignore';
1345 } else {
1346 if ($overlay_action eq 'create_new') {
1347 $bib_result = 'delete';
1348 } else {
1349 $bib_result = ($overlay_status eq 'match_applied') ? 'restore' : 'delete';
1352 return $bib_result;
1356 __END__
1358 =head1 AUTHOR
1360 Koha Development Team <info@koha.org>
1362 Galen Charlton <galen.charlton@liblime.com>
1364 =cut