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