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