new batch job to stage a file of MARC biblios for import.
[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::Matcher;
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     GetImportRecordOverlayStatus
73     SetImportRecordOverlayStatus
74     GetImportRecordStatus
75     SetImportRecordStatus
76     GetImportRecordMatches
77     SetImportRecordMatches
78 );
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 = $matcher->get_matches($marc_record, $max_matches);
394         if (scalar(@matches) > 0) {
395             $num_with_matches++;
396             SetImportRecordMatches($rowref->{'import_record_id'}, @matches);
397             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'auto_match');
398         } else {
399             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'no_match');
400         }
401     }
402     $sth->finish();
403     return $num_with_matches;
404 }
405
406 =head2 BatchCommitBibRecords
407
408 =over 4
409
410 my ($num_added, $num_updated, $num_items_added, $num_ignored) = 
411     BatchCommitBibRecords($batch_id, $progress_interval, $progress_callback);
412
413 =back
414
415 =cut
416
417 sub BatchCommitBibRecords {
418     my $batch_id = shift;
419
420     # optional callback to monitor status 
421     # of job
422     my $progress_interval = 0;
423     my $progress_callback = undef;
424     if ($#_ == 1) {
425         $progress_interval = shift;
426         $progress_callback = shift;
427         $progress_interval = 0 unless $progress_interval =~ /^\d+$/ and $progress_interval > 0;
428         $progress_interval = 0 unless 'CODE' eq ref $progress_callback;
429     }
430
431     my $num_added = 0;
432     my $num_updated = 0;
433     my $num_items_added = 0;
434     my $num_ignored = 0;
435     # commit (i.e., save, all records in the batch)
436     # FIXME biblio only at the moment
437     SetImportBatchStatus('importing');
438     my $overlay_action = GetImportBatchOverlayAction($batch_id);
439     my $dbh = C4::Context->dbh;
440     my $sth = $dbh->prepare("SELECT import_record_id, status, overlay_status, marc
441                              FROM import_records
442                              JOIN import_biblios USING (import_record_id)
443                              WHERE import_batch_id = ?");
444     $sth->execute($batch_id);
445     my $rec_num = 0;
446     while (my $rowref = $sth->fetchrow_hashref) {
447         $rec_num++;
448         if ($progress_interval and (0 == ($rec_num % $progress_interval))) {
449             &$progress_callback($rec_num);
450         }
451         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'imported') {
452             $num_ignored++;
453         }
454         my $marc_record = MARC::Record->new_from_usmarc($rowref->{'marc'});
455         if ($overlay_action eq 'create_new' or
456             ($overlay_action eq 'replace' and $rowref->{'overlay_status'} eq 'no_match')) {
457             $num_added++;
458             my ($biblionumber, $biblioitemnumber) = AddBiblio($marc_record, '');
459             my $sth = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
460             $sth->execute($biblionumber, $rowref->{'import_record_id'});
461             $sth->finish();
462             $num_items_added += BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
463             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
464         } else {
465             $num_updated++;
466             my $biblionumber = GetBestRecordMatch($rowref->{'import_record_id'});
467             my ($count, $oldbiblio) = GetBiblio($biblionumber);
468             my $oldxml = GetXmlBiblio($biblionumber);
469             ModBiblio($marc_record, $biblionumber, $oldbiblio->{'frameworkcode'});
470             my $sth = $dbh->prepare_cached("UPDATE import_records SET marcxml_old = ? WHERE import_record_id = ?");
471             $sth->execute($oldxml, $rowref->{'import_record_id'});
472             $sth->finish();
473             my $sth2 = $dbh->prepare_cached("UPDATE import_biblios SET matched_biblionumber = ? WHERE import_record_id = ?");
474             $sth2->execute($biblionumber, $rowref->{'import_record_id'});
475             $sth2->finish();
476             $num_items_added += BatchCommitItems($rowref->{'import_record_id'}, $biblionumber);
477             SetImportRecordOverlayStatus($rowref->{'import_record_id'}, 'match_applied');
478             SetImportRecordStatus($rowref->{'import_record_id'}, 'imported');
479         }
480     }
481     $sth->finish();
482     SetImportBatchStatus($batch_id, 'imported');
483     return ($num_added, $num_updated, $num_items_added, $num_ignored);
484 }
485
486 =head2 BatchCommitItems
487
488 =over 4
489
490 $num_items_added = BatchCommitItems($import_record_id, $biblionumber);
491
492 =back
493
494 =cut
495
496 sub BatchCommitItems {
497     my ($import_record_id, $biblionumber) = @_;
498
499     my $dbh = C4::Context->dbh;
500
501     my $num_items_added = 0;
502     my $sth = $dbh->prepare("SELECT import_items_id, import_items.marcxml, encoding
503                              FROM import_items
504                              JOIN import_records USING (import_record_id)
505                              WHERE import_record_id = ?
506                              ORDER BY import_items_id");
507     $sth->bind_param(1, $import_record_id);
508     $sth->execute();
509     while (my $row = $sth->fetchrow_hashref()) {
510         my $item_marc = MARC::Record->new_from_xml($row->{'marcxml'}, 'UTF-8', $row->{'encoding'});
511         my ($item_biblionumber, $biblionumber, $itemnumber) = AddItem($item_marc, $biblionumber);
512         my $updsth = $dbh->prepare("UPDATE import_items SET status = ?, itemnumber = ? WHERE import_items_id = ?");
513         $updsth->bind_param(1, 'imported');
514         $updsth->bind_param(2, $itemnumber);
515         $updsth->bind_param(3, $row->{'import_items_id'});
516         $updsth->execute();
517         $updsth->finish();
518         $num_items_added++;
519     }
520     $sth->finish();
521     return $num_items_added;
522 }
523
524 =head2 BatchRevertBibRecords
525
526 =over 4
527
528 my ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored) = BatchRevertBibRecords($batch_id);
529
530 =back
531
532 =cut
533
534 sub BatchRevertBibRecords {
535     my $batch_id = shift;
536
537     my $num_deleted = 0;
538     my $num_errors = 0;
539     my $num_reverted = 0;
540     my $num_items_deleted = 0;
541     my $num_ignored = 0;
542     # commit (i.e., save, all records in the batch)
543     # FIXME biblio only at the moment
544     SetImportBatchStatus('reverting');
545     my $overlay_action = GetImportBatchOverlayAction($batch_id);
546     my $dbh = C4::Context->dbh;
547     my $sth = $dbh->prepare("SELECT import_record_id, status, overlay_status, marcxml_old, encoding, matched_biblionumber
548                              FROM import_records
549                              JOIN import_biblios USING (import_record_id)
550                              WHERE import_batch_id = ?");
551     $sth->execute($batch_id);
552     while (my $rowref = $sth->fetchrow_hashref) {
553         if ($rowref->{'status'} eq 'error' or $rowref->{'status'} eq 'reverted') {
554             $num_ignored++;
555         }
556         if ($overlay_action eq 'create_new' or
557             ($overlay_action eq 'replace' and $rowref->{'overlay_status'} eq 'no_match')) {
558             $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
559             my $error = DelBiblio($rowref->{'matched_biblionumber'});
560             if (defined $error) {
561                 $num_errors++;
562             } else {
563                 $num_deleted++;
564                 SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
565             }
566         } else {
567             $num_reverted++;
568             my $old_record = MARC::Record->new_from_xml($rowref->{'marcxml_old'}, 'UTF-8', $rowref->{'encoding'});
569             my $biblionumber = $rowref->{'matched_biblionumber'};
570             my ($count, $oldbiblio) = GetBiblio($biblionumber);
571             $num_items_deleted += BatchRevertItems($rowref->{'import_record_id'}, $rowref->{'matched_biblionumber'});
572             ModBiblio($old_record, $biblionumber, $oldbiblio->{'frameworkcode'});
573             SetImportRecordStatus($rowref->{'import_record_id'}, 'reverted');
574         }
575     }
576     $sth->finish();
577     SetImportBatchStatus($batch_id, 'reverted');
578     return ($num_deleted, $num_errors, $num_reverted, $num_items_deleted, $num_ignored);
579 }
580
581 =head2 BatchRevertItems
582
583 =over 4
584
585 my $num_items_deleted = BatchRevertItems($import_record_id, $biblionumber);
586
587 =back
588
589 =cut
590
591 sub BatchRevertItems {
592     my ($import_record_id, $biblionumber) = @_;
593
594     my $dbh = C4::Context->dbh;
595     my $num_items_deleted = 0;
596
597     my $sth = $dbh->prepare_cached("SELECT import_items_id, itemnumber
598                                    FROM import_items
599                                    WHERE import_record_id = ?");
600     $sth->bind_param(1, $import_record_id);
601     $sth->execute();
602     while (my $row = $sth->fetchrow_hashref()) {
603         DelItem($dbh, $biblionumber, $row->{'itemnumber'});
604         my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
605         $updsth->bind_param(1, 'reverted');
606         $updsth->bind_param(2, $row->{'import_items_id'});
607         $updsth->execute();
608         $updsth->finish();
609         $num_items_deleted++;
610     }
611     $sth->finish();
612     return $num_items_deleted;
613 }
614
615 =head2 GetAllImportBatches
616
617 =over 4
618
619 my $results = GetAllImportBatches();
620
621 =back
622
623 Returns a references to an array of hash references corresponding
624 to all import_batches rows (of batch_type 'batch'), sorted in 
625 ascending order by import_batch_id.
626
627 =cut
628
629 sub  GetAllImportBatches {
630     my $dbh = C4::Context->dbh;
631     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
632                                     WHERE batch_type = 'batch'
633                                     ORDER BY import_batch_id ASC");
634
635     my $results = [];
636     $sth->execute();
637     while (my $row = $sth->fetchrow_hashref) {
638         push @$results, $row;
639     }
640     $sth->finish();
641     return $results;
642 }
643
644 =head2 GetImportBatchRangeDesc
645
646 =over 4
647
648 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
649
650 =back
651
652 Returns a reference to an array of hash references corresponding to
653 import_batches rows (sorted in descending order by import_batch_id)
654 start at the given offset.
655
656 =cut
657
658 sub GetImportBatchRangeDesc {
659     my ($offset, $results_per_group) = @_;
660
661     my $dbh = C4::Context->dbh;
662     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
663                                     WHERE batch_type = 'batch'
664                                     ORDER BY import_batch_id DESC
665                                     LIMIT ? OFFSET ?");
666     $sth->bind_param(1, $results_per_group);
667     $sth->bind_param(2, $offset);
668
669     my $results = [];
670     $sth->execute();
671     while (my $row = $sth->fetchrow_hashref) {
672         push @$results, $row;
673     }
674     $sth->finish();
675     return $results;
676 }
677
678 =head2 GetNumberOfImportBatches 
679
680 =over 4
681
682 my $count = GetNumberOfImportBatches();
683
684 =back
685
686 =cut
687
688 sub GetNumberOfNonZ3950ImportBatches {
689     my $dbh = C4::Context->dbh;
690     my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type='batch'");
691     $sth->execute();
692     my ($count) = $sth->fetchrow_array();
693     $sth->finish();
694     return $count;
695 }
696
697 =head2 GetImportBibliosRange
698
699 =over 4
700
701 my $results = GetImportBibliosRange($batch_id, $offset, $results_per_group);
702
703 =back
704
705 Returns a reference to an array of hash references corresponding to
706 import_biblios/import_records rows for a given batch
707 starting at the given offset.
708
709 =cut
710
711 sub GetImportBibliosRange {
712     my ($batch_id, $offset, $results_per_group) = @_;
713
714     my $dbh = C4::Context->dbh;
715     my $sth = $dbh->prepare_cached("SELECT title, author, isbn, issn, import_record_id, record_sequence,
716                                            status, overlay_status
717                                     FROM   import_records
718                                     JOIN   import_biblios USING (import_record_id)
719                                     WHERE  import_batch_id = ?
720                                     ORDER BY import_record_id LIMIT ? OFFSET ?");
721     $sth->bind_param(1, $batch_id);
722     $sth->bind_param(2, $results_per_group);
723     $sth->bind_param(3, $offset);
724     my $results = [];
725     $sth->execute();
726     while (my $row = $sth->fetchrow_hashref) {
727         push @$results, $row;
728     }
729     $sth->finish();
730     return $results;
731
732 }
733
734 =head2 GetBestRecordMatch
735
736 =over 4
737
738 my $record_id = GetBestRecordMatch($import_record_id);
739
740 =back
741
742 =cut
743
744 sub GetBestRecordMatch {
745     my ($import_record_id) = @_;
746
747     my $dbh = C4::Context->dbh;
748     my $sth = $dbh->prepare("SELECT candidate_match_id
749                              FROM   import_record_matches
750                              WHERE  import_record_id = ?
751                              ORDER BY score DESC, candidate_match_id DESC");
752     $sth->execute($import_record_id);
753     my ($record_id) = $sth->fetchrow_array();
754     $sth->finish();
755     return $record_id;
756 }
757
758 =head2 GetImportBatchStatus
759
760 =over 4
761
762 my $status = GetImportBatchStatus($batch_id);
763
764 =back
765
766 =cut
767
768 sub GetImportBatchStatus {
769     my ($batch_id) = @_;
770
771     my $dbh = C4::Context->dbh;
772     my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE batch_id = ?");
773     $sth->execute($batch_id);
774     my ($status) = $sth->fetchrow_array();
775     $sth->finish();
776     return;
777
778 }
779
780
781 =head2 SetImportBatchStatus
782
783 =over 4
784
785 SetImportBatchStatus($batch_id, $new_status);
786
787 =back
788
789 =cut
790
791 sub SetImportBatchStatus {
792     my ($batch_id, $new_status) = @_;
793
794     my $dbh = C4::Context->dbh;
795     my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
796     $sth->execute($new_status, $batch_id);
797     $sth->finish();
798
799 }
800
801 =head2 GetImportBatchOverlayAction
802
803 =over 4
804
805 my $overlay_action = GetImportBatchOverlayAction($batch_id);
806
807 =back
808
809 =cut
810
811 sub GetImportBatchOverlayAction {
812     my ($batch_id) = @_;
813
814     my $dbh = C4::Context->dbh;
815     my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
816     $sth->execute($batch_id);
817     my ($overlay_action) = $sth->fetchrow_array();
818     $sth->finish();
819     return $overlay_action;
820
821 }
822
823
824 =head2 SetImportBatchOverlayAction
825
826 =over 4
827
828 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
829
830 =back
831
832 =cut
833
834 sub SetImportBatchOverlayAction {
835     my ($batch_id, $new_overlay_action) = @_;
836
837     my $dbh = C4::Context->dbh;
838     my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
839     $sth->execute($new_overlay_action, $batch_id);
840     $sth->finish();
841
842 }
843
844 =head2 GetImportRecordOverlayStatus
845
846 =over 4
847
848 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
849
850 =back
851
852 =cut
853
854 sub GetImportRecordOverlayStatus {
855     my ($import_record_id) = @_;
856
857     my $dbh = C4::Context->dbh;
858     my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
859     $sth->execute($import_record_id);
860     my ($overlay_status) = $sth->fetchrow_array();
861     $sth->finish();
862     return $overlay_status;
863
864 }
865
866
867 =head2 SetImportRecordOverlayStatus
868
869 =over 4
870
871 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
872
873 =back
874
875 =cut
876
877 sub SetImportRecordOverlayStatus {
878     my ($import_record_id, $new_overlay_status) = @_;
879
880     my $dbh = C4::Context->dbh;
881     my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
882     $sth->execute($new_overlay_status, $import_record_id);
883     $sth->finish();
884
885 }
886
887 =head2 GetImportRecordStatus
888
889 =over 4
890
891 my $overlay_status = GetImportRecordStatus($import_record_id);
892
893 =back
894
895 =cut
896
897 sub GetImportRecordStatus {
898     my ($import_record_id) = @_;
899
900     my $dbh = C4::Context->dbh;
901     my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
902     $sth->execute($import_record_id);
903     my ($overlay_status) = $sth->fetchrow_array();
904     $sth->finish();
905     return $overlay_status;
906
907 }
908
909
910 =head2 SetImportRecordStatus
911
912 =over 4
913
914 SetImportRecordStatus($import_record_id, $new_overlay_status);
915
916 =back
917
918 =cut
919
920 sub SetImportRecordStatus {
921     my ($import_record_id, $new_overlay_status) = @_;
922
923     my $dbh = C4::Context->dbh;
924     my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
925     $sth->execute($new_overlay_status, $import_record_id);
926     $sth->finish();
927
928 }
929
930 =head2 GetImportRecordMatches
931
932 =over 4
933
934 my $results = GetImportRecordMatches($import_record_id, $best_only);
935
936 =back
937
938 =cut
939
940 sub GetImportRecordMatches {
941     my $import_record_id = shift;
942     my $best_only = @_ ? shift : 0;
943
944     my $dbh = C4::Context->dbh;
945     # FIXME currently biblio only
946     my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber, score
947                                     FROM import_records
948                                     JOIN import_record_matches USING (import_record_id)
949                                     JOIN biblio ON (biblionumber = candidate_match_id)
950                                     WHERE import_record_id = ?
951                                     ORDER BY score DESC, biblionumber DESC");
952     $sth->bind_param(1, $import_record_id);
953     my $results = [];
954     $sth->execute();
955     while (my $row = $sth->fetchrow_hashref) {
956         push @$results, $row;
957         last if $best_only;
958     }
959     $sth->finish();
960
961     return $results;
962     
963 }
964
965
966 =head2 SetImportRecordMatches
967
968 =over 4
969
970 SetImportRecordMatches($import_record_id, @matches);
971
972 =back
973
974 =cut
975
976 sub SetImportRecordMatches {
977     my $import_record_id = shift;
978     my @matches = @_;
979
980     my $dbh = C4::Context->dbh;
981     my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
982     $delsth->execute($import_record_id);
983     $delsth->finish();
984
985     my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
986                                     VALUES (?, ?, ?)");
987     foreach my $match (@matches) {
988         $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
989     }
990 }
991
992
993 # internal functions
994
995 sub _create_import_record {
996     my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random) = @_;
997     
998     my $dbh = C4::Context->dbh;
999     my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, 
1000                                                          record_type, encoding, z3950random)
1001                                     VALUES (?, ?, ?, ?, ?, ?, ?)");
1002     $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml(),
1003                   $record_type, $encoding, $z3950random);
1004     my $import_record_id = $dbh->{'mysql_insertid'};
1005     $sth->finish();
1006     return $import_record_id;
1007 }
1008
1009 sub _update_import_record_marc {
1010     my ($import_record_id, $marc_record) = @_;
1011
1012     my $dbh = C4::Context->dbh;
1013     my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1014                              WHERE  import_record_id = ?");
1015     $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml(), $import_record_id);
1016     $sth->finish();
1017 }
1018
1019 sub _add_biblio_fields {
1020     my ($import_record_id, $marc_record) = @_;
1021
1022     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1023     my $dbh = C4::Context->dbh;
1024     # FIXME no controlnumber, originalsource
1025     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1026     $isbn =~ s/\(.*$//;
1027     $isbn =~ tr/ -_//;  
1028     $isbn = uc $isbn;
1029     my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1030     $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1031     $sth->finish();
1032                 
1033 }
1034
1035 sub _update_biblio_fields {
1036     my ($import_record_id, $marc_record) = @_;
1037
1038     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1039     my $dbh = C4::Context->dbh;
1040     # FIXME no controlnumber, originalsource
1041     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1042     $isbn =~ s/\(.*$//;
1043     $isbn =~ tr/ -_//;
1044     $isbn = uc $isbn;
1045     my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1046                              WHERE  import_record_id = ?");
1047     $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1048     $sth->finish();
1049 }
1050
1051 sub _parse_biblio_fields {
1052     my ($marc_record) = @_;
1053
1054     my $dbh = C4::Context->dbh;
1055     my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1056     return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1057
1058 }
1059
1060 sub _update_batch_record_counts {
1061     my ($batch_id) = @_;
1062
1063     my $dbh = C4::Context->dbh;
1064     my $sth = $dbh->prepare_cached("UPDATE import_batches SET num_biblios = (
1065                                     SELECT COUNT(*)
1066                                     FROM import_records
1067                                     WHERE import_batch_id = import_batches.import_batch_id
1068                                     AND record_type = 'biblio')
1069                                     WHERE import_batch_id = ?");
1070     $sth->bind_param(1, $batch_id);
1071     $sth->execute();
1072     $sth->finish();
1073     $sth = $dbh->prepare_cached("UPDATE import_batches SET num_items = (
1074                                     SELECT COUNT(*)
1075                                     FROM import_records
1076                                     JOIN import_items USING (import_record_id)
1077                                     WHERE import_batch_id = import_batches.import_batch_id
1078                                     AND record_type = 'biblio')
1079                                     WHERE import_batch_id = ?");
1080     $sth->bind_param(1, $batch_id);
1081     $sth->execute();
1082     $sth->finish();
1083
1084 }
1085
1086 1;
1087
1088 =head1 AUTHOR
1089
1090 Koha Development Team <info@koha.org>
1091
1092 Galen Charlton <galen.charlton@liblime.com>
1093
1094 =cut