help.pl - bugfix module usage (HTML::Template::Pro)
[koha.git] / C4 / ImportBatch.pm
1 package C4::ImportBatch;
2
3 # Copyright (C) 2007 LibLime
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
15 #
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
19
20 use strict;
21 use C4::Context;
22 use C4::Koha;
23 use C4::Biblio;
24 use C4::Items;
25
26 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
27
28 BEGIN {
29         # set the version for version checking
30         $VERSION = 3.01;
31         require Exporter;
32         @ISA    = qw(Exporter);
33         @EXPORT = qw(
34     GetZ3950BatchId
35     GetImportRecordMarc
36     AddImportBatch
37     GetImportBatch
38     AddBiblioToBatch
39     ModBiblioInBatch
40
41     BatchStageMarcRecords
42     BatchFindBibDuplicates
43     BatchCommitBibRecords
44     BatchRevertBibRecords
45
46     GetAllImportBatches
47     GetImportBatchRangeDesc
48     GetNumberOfNonZ3950ImportBatches
49     GetImportBibliosRange
50     
51     GetImportBatchStatus
52     SetImportBatchStatus
53     GetImportBatchOverlayAction
54     SetImportBatchOverlayAction
55     GetImportBatchMatcher
56     SetImportBatchMatcher
57     GetImportRecordOverlayStatus
58     SetImportRecordOverlayStatus
59     GetImportRecordStatus
60     SetImportRecordStatus
61     GetImportRecordMatches
62     SetImportRecordMatches
63         );
64 }
65
66 =head1 NAME
67
68 C4::ImportBatch - manage batches of imported MARC records
69
70 =head1 SYNOPSIS
71
72 =over 4
73
74 use C4::ImportBatch;
75
76 =back
77
78 =head1 FUNCTIONS
79
80 =head2 GetZ3950BatchId
81
82 =over 4
83
84 my $batchid = GetZ3950BatchId($z3950server);
85
86 =back
87
88 Retrieves the ID of the import batch for the Z39.50
89 reservoir for the given target.  If necessary,
90 creates the import batch.
91
92 =cut
93
94 sub GetZ3950BatchId {
95     my ($z3950server) = @_;
96
97     my $dbh = C4::Context->dbh;
98     my $sth = $dbh->prepare("SELECT import_batch_id FROM import_batches
99                              WHERE  batch_type = 'z3950'
100                              AND    file_name = ?");
101     $sth->execute($z3950server);
102     my $rowref = $sth->fetchrow_arrayref();
103     $sth->finish();
104     if (defined $rowref) {
105         return $rowref->[0];
106     } else {
107         my $batch_id = AddImportBatch('create_new', 'staged', 'z3950', $z3950server, '');
108         return $batch_id;
109     }
110     
111 }
112
113 =head2 GetImportRecordMarc
114
115 =over 4
116
117 my ($marcblob, $encoding) = GetImportRecordMarc($import_record_id);
118
119 =back
120
121 =cut
122
123 sub GetImportRecordMarc {
124     my ($import_record_id) = @_;
125
126     my $dbh = C4::Context->dbh;
127     my $sth = $dbh->prepare("SELECT marc, encoding FROM import_records WHERE import_record_id = ?");
128     $sth->execute($import_record_id);
129     my ($marc, $encoding) = $sth->fetchrow();
130     $sth->finish();
131     return $marc;
132
133 }
134
135 =head2 AddImportBatch
136
137 =over 4
138
139 my $batch_id = AddImportBatch($overlay_action, $import_status, $type, $file_name, $comments);
140
141 =back
142
143 =cut
144
145 sub AddImportBatch {
146     my ($overlay_action, $import_status, $type, $file_name, $comments) = @_;
147
148     my $dbh = C4::Context->dbh;
149     my $sth = $dbh->prepare("INSERT INTO import_batches (overlay_action, import_status, batch_type,
150                                                          file_name, comments)
151                                     VALUES (?, ?, ?, ?, ?)");
152     $sth->execute($overlay_action, $import_status, $type, $file_name, $comments);
153     my $batch_id = $dbh->{'mysql_insertid'};
154     $sth->finish();
155
156     return $batch_id;
157
158 }
159
160 =head2 GetImportBatch 
161
162 =over 4
163
164 my $row = GetImportBatch($batch_id);
165
166 =back
167
168 Retrieve a hashref of an import_batches row.
169
170 =cut
171
172 sub GetImportBatch {
173     my ($batch_id) = @_;
174
175     my $dbh = C4::Context->dbh;
176     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches WHERE import_batch_id = ?");
177     $sth->bind_param(1, $batch_id);
178     $sth->execute();
179     my $result = $sth->fetchrow_hashref;
180     $sth->finish();
181     return $result;
182
183 }
184
185 =head2 AddBiblioToBatch 
186
187 =over 4
188
189 my $import_record_id = AddBiblioToBatch($batch_id, $record_sequence, $marc_record, $encoding, $z3950random, $update_counts);
190
191 =back
192
193 =cut
194
195 sub AddBiblioToBatch {
196     my $batch_id = shift;
197     my $record_sequence = shift;
198     my $marc_record = shift;
199     my $encoding = shift;
200     my $z3950random = shift;
201     my $update_counts = @_ ? shift : 1;
202
203     my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, $z3950random);
204     _add_biblio_fields($import_record_id, $marc_record);
205     _update_batch_record_counts($batch_id) if $update_counts;
206     return $import_record_id;
207 }
208
209 =head2 ModBiblioInBatch
210
211 =over 4
212
213 ModBiblioInBatch($import_record_id, $marc_record);
214
215 =back
216
217 =cut
218
219 sub ModBiblioInBatch {
220     my ($import_record_id, $marc_record) = @_;
221
222     _update_import_record_marc($import_record_id, $marc_record);
223     _update_biblio_fields($import_record_id, $marc_record);
224
225 }
226
227 =head2 BatchStageMarcRecords
228
229 =over 4
230
231 ($batch_id, $num_records, $num_items, @invalid_records) = 
232     BatchStageMarcRecords($marc_flavor, $marc_records, $file_name, 
233                           $comments, $branch_code, $parse_items,
234                           $leave_as_staging, 
235                           $progress_interval, $progress_callback);
236
237 =back
238
239 =cut
240
241 sub  BatchStageMarcRecords {
242     my $marc_flavor = shift;
243     my $marc_records = shift;
244     my $file_name = shift;
245     my $comments = shift;
246     my $branch_code = shift;
247     my $parse_items = shift;
248     my $leave_as_staging = shift;
249    
250     # optional callback to monitor status 
251     # of job
252     my $progress_interval = 0;
253     my $progress_callback = undef;
254     if ($#_ == 1) {
255         $progress_interval = shift;
256         $progress_callback = shift;
257         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
258         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
259     } 
260     
261     my $batch_id = AddImportBatch('create_new', 'staging', 'batch', $file_name, $comments);
262     my @invalid_records = ();
263     my $num_valid = 0;
264     my $num_items = 0;
265     # FIXME - for now, we're dealing only with bibs
266     my $rec_num = 0;
267     foreach my $marc_blob (split(/\x1D/, $marc_records)) {
268         $rec_num++;
269         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
270             &$progress_callback($rec_num);
271         }
272         my $marc_record = FixEncoding($marc_blob, "\x1D");
273         my $import_record_id;
274         if (scalar($marc_record->fields()) == 0) {
275             push @invalid_records, $marc_blob;
276         } else {
277             $num_valid++;
278             $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $marc_flavor, int(rand(99999)), 0);
279             if ($parse_items) {
280                 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
281                 $num_items += scalar(@import_items_ids);
282             }
283         }
284     }
285     unless ($leave_as_staging) {
286         SetImportBatchStatus($batch_id, 'staged');
287     }
288     # FIXME branch_code, number of bibs, number of items
289     _update_batch_record_counts($batch_id);
290     return ($batch_id, $num_valid, $num_items, @invalid_records);
291 }
292
293 =head2 AddItemsToImportBiblio
294
295 =over 4
296
297 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, $update_counts);
298
299 =back
300
301 =cut
302
303 sub AddItemsToImportBiblio {
304     my $batch_id = shift;
305     my $import_record_id = shift;
306     my $marc_record = shift;
307     my $update_counts = @_ ? shift : 0;
308
309     my @import_items_ids = ();
310    
311     my $dbh = C4::Context->dbh; 
312     my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
313     foreach my $item_field ($marc_record->field($item_tag)) {
314         my $item_marc = MARC::Record->new();
315         $item_marc->append_fields($item_field);
316         $marc_record->delete_field($item_field);
317         my $sth = $dbh->prepare_cached("INSERT INTO import_items (import_record_id, status, marcxml)
318                                         VALUES (?, ?, ?)");
319         $sth->bind_param(1, $import_record_id);
320         $sth->bind_param(2, 'staged');
321         $sth->bind_param(3, $item_marc->as_xml());
322         $sth->execute();
323         push @import_items_ids, $dbh->{'mysql_insertid'};
324         $sth->finish();
325     }
326
327     if ($#import_items_ids > -1) {
328         _update_batch_record_counts($batch_id) if $update_counts;
329         _update_import_record_marc($import_record_id, $marc_record);
330     }
331     return @import_items_ids;
332 }
333
334 =head2 BatchFindBibDuplicates
335
336 =over 4
337
338 my $num_with_matches = BatchFindBibDuplicates($batch_id, $matcher, $max_matches, $progress_interval, $progress_callback);
339
340 =back
341
342 Goes through the records loaded in the batch and attempts to 
343 find duplicates for each one.  Sets the overlay action to
344 "replace" if it was "create_new", and sets the overlay status
345 of each record to "no_match" or "auto_match" as appropriate.
346
347 The $max_matches parameter is optional; if it is not supplied,
348 it defaults to 10.
349
350 The $progress_interval and $progress_callback parameters are 
351 optional; if both are supplied, the sub referred to by
352 $progress_callback will be invoked every $progress_interval
353 records using the number of records processed as the 
354 singular argument.
355
356 =cut
357
358 sub BatchFindBibDuplicates {
359     my $batch_id = shift;
360     my $matcher = shift;
361     my $max_matches = @_ ? shift : 10;
362
363     # optional callback to monitor status 
364     # of job
365     my $progress_interval = 0;
366     my $progress_callback = undef;
367     if ($#_ == 1) {
368         $progress_interval = shift;
369         $progress_callback = shift;
370         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
371         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
372     }
373
374     my $dbh = C4::Context->dbh;
375     my $old_overlay_action = GetImportBatchOverlayAction($batch_id);
376     if ($old_overlay_action eq "create_new") {
377         SetImportBatchOverlayAction($batch_id, 'replace');
378     }
379
380     my $sth = $dbh->prepare("SELECT import_record_id, marc
381                              FROM import_records
382                              JOIN import_biblios USING (import_record_id)
383                              WHERE import_batch_id = ?");
384     $sth->execute($batch_id);
385     my $num_with_matches = 0;
386     my $rec_num = 0;
387     while (my $rowref = $sth->fetchrow_hashref) {
388         $rec_num++;
389         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
390             &$progress_callback($rec_num);
391         }
392         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
393         my @matches = ();
394         if (defined $matcher) {
395             @matches = $matcher->get_matches($marc_record, $max_matches);
396         }
397         if (scalar(@matches) > 0) {
398             $num_with_matches++;
399             SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
400             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
401         } else {
402             SetImportRecordMatches($rowref->{'import_record_id'}, ());
403             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
404         }
405     }
406     $sth->finish();
407     return $num_with_matches;
408 }
409
410 =head2 BatchCommitBibRecords
411
412 =over 4
413
414 my ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored) = 
415     BatchCommitBibRecords($batch_id, $progress_interval, $progress_callback);
416
417 =back
418
419 =cut
420
421 sub BatchCommitBibRecords {
422     my $batch_id = shift;
423
424     # optional callback to monitor status 
425     # of job
426     my $progress_interval = 0;
427     my $progress_callback = undef;
428     if ($#_ == 1) {
429         $progress_interval = shift;
430         $progress_callback = shift;
431         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
432         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
433     }
434
435     my $num_added = 0;
436     my $num_updated = 0;
437     my $num_items_added = 0;
438     my $num_items_errored = 0;
439     my $num_ignored = 0;
440     # commit (i.e., save, all records in the batch)
441     # FIXME biblio only at the moment
442     SetImportBatchStatus('importing');
443     my $overlay_action = GetImportBatchOverlayAction($batch_id);
444     my $dbh = C4::Context->dbh;
445     my $sth = $dbh->prepare("SELECT import_record_id, status, overlay_status, marc, encoding
446                              FROM import_records
447                              JOIN import_biblios USING (import_record_id)
448                              WHERE import_batch_id = ?");
449     $sth->execute($batch_id);
450     my $rec_num = 0;
451     while (my $rowref = $sth->fetchrow_hashref) {
452         $rec_num++;
453         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
454             &$progress_callback($rec_num);
455         }
456         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
457             $num_ignored++;
458         }
459
460         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
461
462         # remove any item tags - rely on BatchCommitItems
463         my ($item_tag,$item_subfield) = &GetMarcFromKohaField("items.itemnumber",'');
464         foreach my $item_field ($marc_record->field($item_tag)) {
465             $marc_record->delete_field($item_field);
466         }
467
468         if ($overlay_action eq 'create_new' or
469             ($overlay_action eq 'replace' and $rowref->{'overlay_status'} eq 'no_match')) {
470             $num_added++;
471             my ($biblionumber, $biblioitemnumber) = AddBiblio($marc_record, '');
472             my $sth = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
473             $sth->execute($biblionumber, $rowref->{'import_record_id'});
474             $sth->finish();
475             my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
476             $num_items_added += $bib_items_added;
477             $num_items_errored += $bib_items_errored;
478             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
479         } else {
480             $num_updated++;
481             my $biblionumber = GetBestRecordMatch($rowref->{'import_record_id'});
482             my ($count, $oldbiblio) = GetBiblio($biblionumber);
483             my $oldxml = GetXmlBiblio($biblionumber);
484
485             # remove item fields so that they don't get
486             # added again if record is reverted
487             my $old_marc = MARC::Record->new_from_xml($oldxml, 'UTF-8', $rowref->{'encoding'});
488             foreach my $item_field ($old_marc->field($item_tag)) {
489                 $old_marc->delete_field($item_field);
490             }
491
492             ModBiblio($marc_record, $biblionumber, $oldbiblio->{'frameworkcode'});
493             my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
494             $sth->execute($old_marc->as_xml(), $rowref->{'import_record_id'});
495             $sth->finish();
496             my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
497             $sth2->execute($biblionumber, $rowref->{'import_record_id'});
498             $sth2->finish();
499             my ($bib_items_added, $bib_items_errored) = BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
500             $num_items_added += $bib_items_added;
501             $num_items_errored += $bib_items_errored;
502             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
503             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
504         }
505     }
506     $sth->finish();
507     SetImportBatchStatus($batch_id, 'imported');
508     return ($num_added, $num_updated, $num_items_added, $num_items_errored, $num_ignored);
509 }
510
511 =head2 BatchCommitItems
512
513 =over 4
514
515 ($num_items_added, $num_items_errored) = BatchCommitItems($import_record_id, $biblionumber);
516
517 =back
518
519 =cut
520
521 sub BatchCommitItems {
522     my ($import_record_id, $biblionumber) = @_;
523
524     my $dbh = C4::Context->dbh;
525
526     my $num_items_added = 0;
527     my $num_items_errored = 0;
528     my $sth = $dbh->prepare("SELECT import_items_id, import_items.marcxml, encoding
529                              FROM import_items
530                              JOIN import_records USING (import_record_id)
531                              WHERE import_record_id = ?
532                              ORDER BY import_items_id");
533     $sth->bind_param(1, $import_record_id);
534     $sth->execute();
535     while (my $row = $sth->fetchrow_hashref()) {
536         my $item_marc = MARC::Record->new_from_xml($row->{'marcxml'}, 'UTF-8', $row->{'encoding'});
537         # FIXME - duplicate barcode check needs to become part of AddItemFromMarc()
538         my $item = TransformMarcToKoha($dbh, $item_marc);
539         my $duplicate_barcode = exists($item->{'barcode'}) && GetItemnumberFromBarcode($item->{'barcode'});
540         if ($duplicate_barcode) {
541             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, import_error = ? WHERE import_items_id = ?");
542             $updsth->bind_param(1, 'error');
543             $updsth->bind_param(2, 'duplicate item barcode');
544             $updsth->bind_param(3, $row->{'import_items_id'});
545             $updsth->execute();
546             $num_items_errored++;
547         } else {
548             my ($item_biblionumber, $biblioitemnumber, $itemnumber) = AddItemFromMarc($item_marc, $biblionumber);
549             my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
550             $updsth->bind_param(1, 'imported');
551             $updsth->bind_param(2, $itemnumber);
552             $updsth->bind_param(3, $row->{'import_items_id'});
553             $updsth->execute();
554             $updsth->finish();
555             $num_items_added++;
556         }
557     }
558     $sth->finish();
559     return ($num_items_added, $num_items_errored);
560 }
561
562 =head2 BatchRevertBibRecords
563
564 =over 4
565
566 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored) = BatchRevertBibRecords($batch_id);
567
568 =back
569
570 =cut
571
572 sub BatchRevertBibRecords {
573     my $batch_id = shift;
574
575     my $num_deleted = 0;
576     my $num_errors = 0;
577     my $num_reverted = 0;
578     my $num_items_deleted = 0;
579     my $num_ignored = 0;
580     # commit (i.e., save, all records in the batch)
581     # FIXME biblio only at the moment
582     SetImportBatchStatus('reverting');
583     my $overlay_action = GetImportBatchOverlayAction($batch_id);
584     my $dbh = C4::Context->dbh;
585     my $sth = $dbh->prepare("SELECT import_record_id, status, overlay_status, marcxml_old, encoding, matched_biblionumber
586                              FROM import_records
587                              JOIN import_biblios USING (import_record_id)
588                              WHERE import_batch_id = ?");
589     $sth->execute($batch_id);
590     while (my $rowref = $sth->fetchrow_hashref) {
591         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
592             $num_ignored++;
593         }
594         if ($overlay_action eq 'create_new' or
595             ($overlay_action eq 'replace' and $rowref->{'overlay_status'} eq 'no_match')) {
596             $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
597             my $error = DelBiblio($rowref->{'matched_biblionumber'});
598             if (defined $error) {
599                 $num_errors++;
600             } else {
601                 $num_deleted++;
602                 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
603             }
604         } else {
605             $num_reverted++;
606             my $old_record = MARC::Record->new_from_xml($rowref->{'marcxml_old'}, 'UTF-8', $rowref->{'encoding'});
607             my $biblionumber = $rowref->{'matched_biblionumber'};
608             my ($count, $oldbiblio) = GetBiblio($biblionumber);
609             $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
610             ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
611             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
612         }
613     }
614     $sth->finish();
615     SetImportBatchStatus($batch_id, 'reverted');
616     return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
617 }
618
619 =head2 BatchRevertItems
620
621 =over 4
622
623 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
624
625 =back
626
627 =cut
628
629 sub BatchRevertItems {
630     my ($import_record_id, $biblionumber) = @_;
631
632     my $dbh = C4::Context->dbh;
633     my $num_items_deleted = 0;
634
635     my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
636                                    FROM import_items
637                                    JOIN items USING (itemnumber)
638                                    WHERE import_record_id = ?");
639     $sth->bind_param(1, $import_record_id);
640     $sth->execute();
641     while (my $row = $sth->fetchrow_hashref()) {
642         DelItem($dbh, $biblionumber, $row->{'itemnumber'});
643         my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
644         $updsth->bind_param(1, 'reverted');
645         $updsth->bind_param(2, $row->{'import_items_id'});
646         $updsth->execute();
647         $updsth->finish();
648         $num_items_deleted++;
649     }
650     $sth->finish();
651     return $num_items_deleted;
652 }
653
654 =head2 GetAllImportBatches
655
656 =over 4
657
658 my $results = GetAllImportBatches();
659
660 =back
661
662 Returns a references to an array of hash references corresponding
663 to all import_batches rows (of batch_type 'batch'), sorted in 
664 ascending order by import_batch_id.
665
666 =cut
667
668 sub  GetAllImportBatches {
669     my $dbh = C4::Context->dbh;
670     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
671                                     WHERE batch_type = 'batch'
672                                     ORDER BY import_batch_id ASC");
673
674     my $results = [];
675     $sth->execute();
676     while (my $row = $sth->fetchrow_hashref) {
677         push @$results, $row;
678     }
679     $sth->finish();
680     return $results;
681 }
682
683 =head2 GetImportBatchRangeDesc
684
685 =over 4
686
687 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
688
689 =back
690
691 Returns a reference to an array of hash references corresponding to
692 import_batches rows (sorted in descending order by import_batch_id)
693 start at the given offset.
694
695 =cut
696
697 sub GetImportBatchRangeDesc {
698     my ($offset, $results_per_group) = @_;
699
700     my $dbh = C4::Context->dbh;
701     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
702                                     WHERE batch_type = 'batch'
703                                     ORDER BY import_batch_id DESC
704                                     LIMIT ? OFFSET ?");
705     $sth->bind_param(1, $results_per_group);
706     $sth->bind_param(2, $offset);
707
708     my $results = [];
709     $sth->execute();
710     while (my $row = $sth->fetchrow_hashref) {
711         push @$results, $row;
712     }
713     $sth->finish();
714     return $results;
715 }
716
717 =head2 GetNumberOfImportBatches 
718
719 =over 4
720
721 my $count = GetNumberOfImportBatches();
722
723 =back
724
725 =cut
726
727 sub GetNumberOfNonZ3950ImportBatches {
728     my $dbh = C4::Context->dbh;
729     my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type='batch'");
730     $sth->execute();
731     my ($count) = $sth->fetchrow_array();
732     $sth->finish();
733     return $count;
734 }
735
736 =head2 GetImportBibliosRange
737
738 =over 4
739
740 my $results = GetImportBibliosRange($batch_id, $offset, $results_per_group);
741
742 =back
743
744 Returns a reference to an array of hash references corresponding to
745 import_biblios/import_records rows for a given batch
746 starting at the given offset.
747
748 =cut
749
750 sub GetImportBibliosRange {
751     my ($batch_id, $offset, $results_per_group) = @_;
752
753     my $dbh = C4::Context->dbh;
754     my $sth = $dbh->prepare_cached("SELECT title, author, isbn, issn, import_record_id, record_sequence,
755                                            status, overlay_status
756                                     FROM   import_records
757                                     JOIN   import_biblios USING (import_record_id)
758                                     WHERE  import_batch_id = ?
759                                     ORDER BY import_record_id LIMIT ? OFFSET ?");
760     $sth->bind_param(1, $batch_id);
761     $sth->bind_param(2, $results_per_group);
762     $sth->bind_param(3, $offset);
763     my $results = [];
764     $sth->execute();
765     while (my $row = $sth->fetchrow_hashref) {
766         push @$results, $row;
767     }
768     $sth->finish();
769     return $results;
770
771 }
772
773 =head2 GetBestRecordMatch
774
775 =over 4
776
777 my $record_id = GetBestRecordMatch($import_record_id);
778
779 =back
780
781 =cut
782
783 sub GetBestRecordMatch {
784     my ($import_record_id) = @_;
785
786     my $dbh = C4::Context->dbh;
787     my $sth = $dbh->prepare("SELECT candidate_match_id
788                              FROM   import_record_matches
789                              WHERE  import_record_id = ?
790                              ORDER BY score DESC, candidate_match_id DESC");
791     $sth->execute($import_record_id);
792     my ($record_id) = $sth->fetchrow_array();
793     $sth->finish();
794     return $record_id;
795 }
796
797 =head2 GetImportBatchStatus
798
799 =over 4
800
801 my $status = GetImportBatchStatus($batch_id);
802
803 =back
804
805 =cut
806
807 sub GetImportBatchStatus {
808     my ($batch_id) = @_;
809
810     my $dbh = C4::Context->dbh;
811     my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE batch_id = ?");
812     $sth->execute($batch_id);
813     my ($status) = $sth->fetchrow_array();
814     $sth->finish();
815     return;
816
817 }
818
819
820 =head2 SetImportBatchStatus
821
822 =over 4
823
824 SetImportBatchStatus($batch_id, $new_status);
825
826 =back
827
828 =cut
829
830 sub SetImportBatchStatus {
831     my ($batch_id, $new_status) = @_;
832
833     my $dbh = C4::Context->dbh;
834     my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
835     $sth->execute($new_status, $batch_id);
836     $sth->finish();
837
838 }
839
840 =head2 GetImportBatchOverlayAction
841
842 =over 4
843
844 my $overlay_action = GetImportBatchOverlayAction($batch_id);
845
846 =back
847
848 =cut
849
850 sub GetImportBatchOverlayAction {
851     my ($batch_id) = @_;
852
853     my $dbh = C4::Context->dbh;
854     my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
855     $sth->execute($batch_id);
856     my ($overlay_action) = $sth->fetchrow_array();
857     $sth->finish();
858     return $overlay_action;
859
860 }
861
862
863 =head2 SetImportBatchOverlayAction
864
865 =over 4
866
867 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
868
869 =back
870
871 =cut
872
873 sub SetImportBatchOverlayAction {
874     my ($batch_id, $new_overlay_action) = @_;
875
876     my $dbh = C4::Context->dbh;
877     my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
878     $sth->execute($new_overlay_action, $batch_id);
879     $sth->finish();
880
881 }
882
883 =head2 GetImportBatchMatcher
884
885 =over 4
886
887 my $matcher_id = GetImportBatchMatcher($batch_id);
888
889 =back
890
891 =cut
892
893 sub GetImportBatchMatcher {
894     my ($batch_id) = @_;
895
896     my $dbh = C4::Context->dbh;
897     my $sth = $dbh->prepare("SELECT matcher_id FROM import_batches WHERE import_batch_id = ?");
898     $sth->execute($batch_id);
899     my ($matcher_id) = $sth->fetchrow_array();
900     $sth->finish();
901     return $matcher_id;
902
903 }
904
905
906 =head2 SetImportBatchMatcher
907
908 =over 4
909
910 SetImportBatchMatcher($batch_id, $new_matcher_id);
911
912 =back
913
914 =cut
915
916 sub SetImportBatchMatcher {
917     my ($batch_id, $new_matcher_id) = @_;
918
919     my $dbh = C4::Context->dbh;
920     my $sth = $dbh->prepare("UPDATE import_batches SET matcher_id = ? WHERE import_batch_id = ?");
921     $sth->execute($new_matcher_id, $batch_id);
922     $sth->finish();
923
924 }
925
926 =head2 GetImportRecordOverlayStatus
927
928 =over 4
929
930 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
931
932 =back
933
934 =cut
935
936 sub GetImportRecordOverlayStatus {
937     my ($import_record_id) = @_;
938
939     my $dbh = C4::Context->dbh;
940     my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
941     $sth->execute($import_record_id);
942     my ($overlay_status) = $sth->fetchrow_array();
943     $sth->finish();
944     return $overlay_status;
945
946 }
947
948
949 =head2 SetImportRecordOverlayStatus
950
951 =over 4
952
953 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
954
955 =back
956
957 =cut
958
959 sub SetImportRecordOverlayStatus {
960     my ($import_record_id, $new_overlay_status) = @_;
961
962     my $dbh = C4::Context->dbh;
963     my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
964     $sth->execute($new_overlay_status, $import_record_id);
965     $sth->finish();
966
967 }
968
969 =head2 GetImportRecordStatus
970
971 =over 4
972
973 my $overlay_status = GetImportRecordStatus($import_record_id);
974
975 =back
976
977 =cut
978
979 sub GetImportRecordStatus {
980     my ($import_record_id) = @_;
981
982     my $dbh = C4::Context->dbh;
983     my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
984     $sth->execute($import_record_id);
985     my ($overlay_status) = $sth->fetchrow_array();
986     $sth->finish();
987     return $overlay_status;
988
989 }
990
991
992 =head2 SetImportRecordStatus
993
994 =over 4
995
996 SetImportRecordStatus($import_record_id, $new_overlay_status);
997
998 =back
999
1000 =cut
1001
1002 sub SetImportRecordStatus {
1003     my ($import_record_id, $new_overlay_status) = @_;
1004
1005     my $dbh = C4::Context->dbh;
1006     my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
1007     $sth->execute($new_overlay_status, $import_record_id);
1008     $sth->finish();
1009
1010 }
1011
1012 =head2 GetImportRecordMatches
1013
1014 =over 4
1015
1016 my $results = GetImportRecordMatches($import_record_id, $best_only);
1017
1018 =back
1019
1020 =cut
1021
1022 sub GetImportRecordMatches {
1023     my $import_record_id = shift;
1024     my $best_only = @_ ? shift : 0;
1025
1026     my $dbh = C4::Context->dbh;
1027     # FIXME currently biblio only
1028     my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber, score
1029                                     FROM import_records
1030                                     JOIN import_record_matches USING (import_record_id)
1031                                     JOIN biblio ON (biblionumber = candidate_match_id)
1032                                     WHERE import_record_id = ?
1033                                     ORDER BY score DESC, biblionumber DESC");
1034     $sth->bind_param(1, $import_record_id);
1035     my $results = [];
1036     $sth->execute();
1037     while (my $row = $sth->fetchrow_hashref) {
1038         push @$results, $row;
1039         last if $best_only;
1040     }
1041     $sth->finish();
1042
1043     return $results;
1044     
1045 }
1046
1047
1048 =head2 SetImportRecordMatches
1049
1050 =over 4
1051
1052 SetImportRecordMatches($import_record_id, @matches);
1053
1054 =back
1055
1056 =cut
1057
1058 sub SetImportRecordMatches {
1059     my $import_record_id = shift;
1060     my @matches = @_;
1061
1062     my $dbh = C4::Context->dbh;
1063     my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
1064     $delsth->execute($import_record_id);
1065     $delsth->finish();
1066
1067     my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
1068                                     VALUES (?, ?, ?)");
1069     foreach my $match (@matches) {
1070         $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
1071     }
1072 }
1073
1074
1075 # internal functions
1076
1077 sub _create_import_record {
1078     my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random) = @_;
1079
1080     my $dbh = C4::Context->dbh;
1081     my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, 
1082                                                          record_type, encoding, z3950random)
1083                                     VALUES (?, ?, ?, ?, ?, ?, ?)");
1084     $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml(),
1085                   $record_type, $encoding, $z3950random);
1086     my $import_record_id = $dbh->{'mysql_insertid'};
1087     $sth->finish();
1088     return $import_record_id;
1089 }
1090
1091 sub _update_import_record_marc {
1092     my ($import_record_id, $marc_record) = @_;
1093
1094     my $dbh = C4::Context->dbh;
1095     my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1096                              WHERE  import_record_id = ?");
1097     $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml(), $import_record_id);
1098     $sth->finish();
1099 }
1100
1101 sub _add_biblio_fields {
1102     my ($import_record_id, $marc_record) = @_;
1103
1104     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1105     my $dbh = C4::Context->dbh;
1106     # FIXME no controlnumber, originalsource
1107     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1108     $isbn =~ s/\(.*$//;
1109     $isbn =~ tr/ -_//;  
1110     $isbn = uc $isbn;
1111     my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1112     $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1113     $sth->finish();
1114                 
1115 }
1116
1117 sub _update_biblio_fields {
1118     my ($import_record_id, $marc_record) = @_;
1119
1120     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1121     my $dbh = C4::Context->dbh;
1122     # FIXME no controlnumber, originalsource
1123     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1124     $isbn =~ s/\(.*$//;
1125     $isbn =~ tr/ -_//;
1126     $isbn = uc $isbn;
1127     my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1128                              WHERE  import_record_id = ?");
1129     $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1130     $sth->finish();
1131 }
1132
1133 sub _parse_biblio_fields {
1134     my ($marc_record) = @_;
1135
1136     my $dbh = C4::Context->dbh;
1137     my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1138     return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1139
1140 }
1141
1142 sub _update_batch_record_counts {
1143     my ($batch_id) = @_;
1144
1145     my $dbh = C4::Context->dbh;
1146     my $sth = $dbh->prepare_cached("UPDATE import_batches SET num_biblios = (
1147                                     SELECT COUNT(*)
1148                                     FROM import_records
1149                                     WHERE import_batch_id = import_batches.import_batch_id
1150                                     AND record_type = 'biblio')
1151                                     WHERE import_batch_id = ?");
1152     $sth->bind_param(1, $batch_id);
1153     $sth->execute();
1154     $sth->finish();
1155     $sth = $dbh->prepare_cached("UPDATE import_batches SET num_items = (
1156                                     SELECT COUNT(*)
1157                                     FROM import_records
1158                                     JOIN import_items USING (import_record_id)
1159                                     WHERE import_batch_id = import_batches.import_batch_id
1160                                     AND record_type = 'biblio')
1161                                     WHERE import_batch_id = ?");
1162     $sth->bind_param(1, $batch_id);
1163     $sth->execute();
1164     $sth->finish();
1165
1166 }
1167
1168 1;
1169 __END__
1170
1171 =head1 AUTHOR
1172
1173 Koha Development Team <info@koha.org>
1174
1175 Galen Charlton <galen.charlton@liblime.com>
1176
1177 =cut