small text formatting change, added spaces
[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                                    JOIN items USING (itemnumber)
600                                    WHERE import_record_id = ?");
601     $sth->bind_param(1, $import_record_id);
602     $sth->execute();
603     while (my $row = $sth->fetchrow_hashref()) {
604         DelItem($dbh, $biblionumber, $row->{'itemnumber'});
605         my $updsth = $dbh->prepare("UPDATE import_items SET status = ? WHERE import_items_id = ?");
606         $updsth->bind_param(1, 'reverted');
607         $updsth->bind_param(2, $row->{'import_items_id'});
608         $updsth->execute();
609         $updsth->finish();
610         $num_items_deleted++;
611     }
612     $sth->finish();
613     return $num_items_deleted;
614 }
615
616 =head2 GetAllImportBatches
617
618 =over 4
619
620 my $results = GetAllImportBatches();
621
622 =back
623
624 Returns a references to an array of hash references corresponding
625 to all import_batches rows (of batch_type 'batch'), sorted in 
626 ascending order by import_batch_id.
627
628 =cut
629
630 sub  GetAllImportBatches {
631     my $dbh = C4::Context->dbh;
632     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
633                                     WHERE batch_type = 'batch'
634                                     ORDER BY import_batch_id ASC");
635
636     my $results = [];
637     $sth->execute();
638     while (my $row = $sth->fetchrow_hashref) {
639         push @$results, $row;
640     }
641     $sth->finish();
642     return $results;
643 }
644
645 =head2 GetImportBatchRangeDesc
646
647 =over 4
648
649 my $results = GetImportBatchRangeDesc($offset, $results_per_group);
650
651 =back
652
653 Returns a reference to an array of hash references corresponding to
654 import_batches rows (sorted in descending order by import_batch_id)
655 start at the given offset.
656
657 =cut
658
659 sub GetImportBatchRangeDesc {
660     my ($offset, $results_per_group) = @_;
661
662     my $dbh = C4::Context->dbh;
663     my $sth = $dbh->prepare_cached("SELECT * FROM import_batches
664                                     WHERE batch_type = 'batch'
665                                     ORDER BY import_batch_id DESC
666                                     LIMIT ? OFFSET ?");
667     $sth->bind_param(1, $results_per_group);
668     $sth->bind_param(2, $offset);
669
670     my $results = [];
671     $sth->execute();
672     while (my $row = $sth->fetchrow_hashref) {
673         push @$results, $row;
674     }
675     $sth->finish();
676     return $results;
677 }
678
679 =head2 GetNumberOfImportBatches 
680
681 =over 4
682
683 my $count = GetNumberOfImportBatches();
684
685 =back
686
687 =cut
688
689 sub GetNumberOfNonZ3950ImportBatches {
690     my $dbh = C4::Context->dbh;
691     my $sth = $dbh->prepare("SELECT COUNT(*) FROM import_batches WHERE batch_type='batch'");
692     $sth->execute();
693     my ($count) = $sth->fetchrow_array();
694     $sth->finish();
695     return $count;
696 }
697
698 =head2 GetImportBibliosRange
699
700 =over 4
701
702 my $results = GetImportBibliosRange($batch_id, $offset, $results_per_group);
703
704 =back
705
706 Returns a reference to an array of hash references corresponding to
707 import_biblios/import_records rows for a given batch
708 starting at the given offset.
709
710 =cut
711
712 sub GetImportBibliosRange {
713     my ($batch_id, $offset, $results_per_group) = @_;
714
715     my $dbh = C4::Context->dbh;
716     my $sth = $dbh->prepare_cached("SELECT title, author, isbn, issn, import_record_id, record_sequence,
717                                            status, overlay_status
718                                     FROM   import_records
719                                     JOIN   import_biblios USING (import_record_id)
720                                     WHERE  import_batch_id = ?
721                                     ORDER BY import_record_id LIMIT ? OFFSET ?");
722     $sth->bind_param(1, $batch_id);
723     $sth->bind_param(2, $results_per_group);
724     $sth->bind_param(3, $offset);
725     my $results = [];
726     $sth->execute();
727     while (my $row = $sth->fetchrow_hashref) {
728         push @$results, $row;
729     }
730     $sth->finish();
731     return $results;
732
733 }
734
735 =head2 GetBestRecordMatch
736
737 =over 4
738
739 my $record_id = GetBestRecordMatch($import_record_id);
740
741 =back
742
743 =cut
744
745 sub GetBestRecordMatch {
746     my ($import_record_id) = @_;
747
748     my $dbh = C4::Context->dbh;
749     my $sth = $dbh->prepare("SELECT candidate_match_id
750                              FROM   import_record_matches
751                              WHERE  import_record_id = ?
752                              ORDER BY score DESC, candidate_match_id DESC");
753     $sth->execute($import_record_id);
754     my ($record_id) = $sth->fetchrow_array();
755     $sth->finish();
756     return $record_id;
757 }
758
759 =head2 GetImportBatchStatus
760
761 =over 4
762
763 my $status = GetImportBatchStatus($batch_id);
764
765 =back
766
767 =cut
768
769 sub GetImportBatchStatus {
770     my ($batch_id) = @_;
771
772     my $dbh = C4::Context->dbh;
773     my $sth = $dbh->prepare("SELECT import_status FROM import_batches WHERE batch_id = ?");
774     $sth->execute($batch_id);
775     my ($status) = $sth->fetchrow_array();
776     $sth->finish();
777     return;
778
779 }
780
781
782 =head2 SetImportBatchStatus
783
784 =over 4
785
786 SetImportBatchStatus($batch_id, $new_status);
787
788 =back
789
790 =cut
791
792 sub SetImportBatchStatus {
793     my ($batch_id, $new_status) = @_;
794
795     my $dbh = C4::Context->dbh;
796     my $sth = $dbh->prepare("UPDATE import_batches SET import_status = ? WHERE import_batch_id = ?");
797     $sth->execute($new_status, $batch_id);
798     $sth->finish();
799
800 }
801
802 =head2 GetImportBatchOverlayAction
803
804 =over 4
805
806 my $overlay_action = GetImportBatchOverlayAction($batch_id);
807
808 =back
809
810 =cut
811
812 sub GetImportBatchOverlayAction {
813     my ($batch_id) = @_;
814
815     my $dbh = C4::Context->dbh;
816     my $sth = $dbh->prepare("SELECT overlay_action FROM import_batches WHERE import_batch_id = ?");
817     $sth->execute($batch_id);
818     my ($overlay_action) = $sth->fetchrow_array();
819     $sth->finish();
820     return $overlay_action;
821
822 }
823
824
825 =head2 SetImportBatchOverlayAction
826
827 =over 4
828
829 SetImportBatchOverlayAction($batch_id, $new_overlay_action);
830
831 =back
832
833 =cut
834
835 sub SetImportBatchOverlayAction {
836     my ($batch_id, $new_overlay_action) = @_;
837
838     my $dbh = C4::Context->dbh;
839     my $sth = $dbh->prepare("UPDATE import_batches SET overlay_action = ? WHERE import_batch_id = ?");
840     $sth->execute($new_overlay_action, $batch_id);
841     $sth->finish();
842
843 }
844
845 =head2 GetImportRecordOverlayStatus
846
847 =over 4
848
849 my $overlay_status = GetImportRecordOverlayStatus($import_record_id);
850
851 =back
852
853 =cut
854
855 sub GetImportRecordOverlayStatus {
856     my ($import_record_id) = @_;
857
858     my $dbh = C4::Context->dbh;
859     my $sth = $dbh->prepare("SELECT overlay_status FROM import_records WHERE import_record_id = ?");
860     $sth->execute($import_record_id);
861     my ($overlay_status) = $sth->fetchrow_array();
862     $sth->finish();
863     return $overlay_status;
864
865 }
866
867
868 =head2 SetImportRecordOverlayStatus
869
870 =over 4
871
872 SetImportRecordOverlayStatus($import_record_id, $new_overlay_status);
873
874 =back
875
876 =cut
877
878 sub SetImportRecordOverlayStatus {
879     my ($import_record_id, $new_overlay_status) = @_;
880
881     my $dbh = C4::Context->dbh;
882     my $sth = $dbh->prepare("UPDATE import_records SET overlay_status = ? WHERE import_record_id = ?");
883     $sth->execute($new_overlay_status, $import_record_id);
884     $sth->finish();
885
886 }
887
888 =head2 GetImportRecordStatus
889
890 =over 4
891
892 my $overlay_status = GetImportRecordStatus($import_record_id);
893
894 =back
895
896 =cut
897
898 sub GetImportRecordStatus {
899     my ($import_record_id) = @_;
900
901     my $dbh = C4::Context->dbh;
902     my $sth = $dbh->prepare("SELECT status FROM import_records WHERE import_record_id = ?");
903     $sth->execute($import_record_id);
904     my ($overlay_status) = $sth->fetchrow_array();
905     $sth->finish();
906     return $overlay_status;
907
908 }
909
910
911 =head2 SetImportRecordStatus
912
913 =over 4
914
915 SetImportRecordStatus($import_record_id, $new_overlay_status);
916
917 =back
918
919 =cut
920
921 sub SetImportRecordStatus {
922     my ($import_record_id, $new_overlay_status) = @_;
923
924     my $dbh = C4::Context->dbh;
925     my $sth = $dbh->prepare("UPDATE import_records SET status = ? WHERE import_record_id = ?");
926     $sth->execute($new_overlay_status, $import_record_id);
927     $sth->finish();
928
929 }
930
931 =head2 GetImportRecordMatches
932
933 =over 4
934
935 my $results = GetImportRecordMatches($import_record_id, $best_only);
936
937 =back
938
939 =cut
940
941 sub GetImportRecordMatches {
942     my $import_record_id = shift;
943     my $best_only = @_ ? shift : 0;
944
945     my $dbh = C4::Context->dbh;
946     # FIXME currently biblio only
947     my $sth = $dbh->prepare_cached("SELECT title, author, biblionumber, score
948                                     FROM import_records
949                                     JOIN import_record_matches USING (import_record_id)
950                                     JOIN biblio ON (biblionumber = candidate_match_id)
951                                     WHERE import_record_id = ?
952                                     ORDER BY score DESC, biblionumber DESC");
953     $sth->bind_param(1, $import_record_id);
954     my $results = [];
955     $sth->execute();
956     while (my $row = $sth->fetchrow_hashref) {
957         push @$results, $row;
958         last if $best_only;
959     }
960     $sth->finish();
961
962     return $results;
963     
964 }
965
966
967 =head2 SetImportRecordMatches
968
969 =over 4
970
971 SetImportRecordMatches($import_record_id, @matches);
972
973 =back
974
975 =cut
976
977 sub SetImportRecordMatches {
978     my $import_record_id = shift;
979     my @matches = @_;
980
981     my $dbh = C4::Context->dbh;
982     my $delsth = $dbh->prepare("DELETE FROM import_record_matches WHERE import_record_id = ?");
983     $delsth->execute($import_record_id);
984     $delsth->finish();
985
986     my $sth = $dbh->prepare("INSERT INTO import_record_matches (import_record_id, candidate_match_id, score)
987                                     VALUES (?, ?, ?)");
988     foreach my $match (@matches) {
989         $sth->execute($import_record_id, $match->{'record_id'}, $match->{'score'});
990     }
991 }
992
993
994 # internal functions
995
996 sub _create_import_record {
997     my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random) = @_;
998     
999     my $dbh = C4::Context->dbh;
1000     my $sth = $dbh->prepare("INSERT INTO import_records (import_batch_id, record_sequence, marc, marcxml, 
1001                                                          record_type, encoding, z3950random)
1002                                     VALUES (?, ?, ?, ?, ?, ?, ?)");
1003     $sth->execute($batch_id, $record_sequence, $marc_record->as_usmarc(), $marc_record->as_xml(),
1004                   $record_type, $encoding, $z3950random);
1005     my $import_record_id = $dbh->{'mysql_insertid'};
1006     $sth->finish();
1007     return $import_record_id;
1008 }
1009
1010 sub _update_import_record_marc {
1011     my ($import_record_id, $marc_record) = @_;
1012
1013     my $dbh = C4::Context->dbh;
1014     my $sth = $dbh->prepare("UPDATE import_records SET marc = ?, marcxml = ?
1015                              WHERE  import_record_id = ?");
1016     $sth->execute($marc_record->as_usmarc(), $marc_record->as_xml(), $import_record_id);
1017     $sth->finish();
1018 }
1019
1020 sub _add_biblio_fields {
1021     my ($import_record_id, $marc_record) = @_;
1022
1023     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1024     my $dbh = C4::Context->dbh;
1025     # FIXME no controlnumber, originalsource
1026     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1027     $isbn =~ s/\(.*$//;
1028     $isbn =~ tr/ -_//;  
1029     $isbn = uc $isbn;
1030     my $sth = $dbh->prepare("INSERT INTO import_biblios (import_record_id, title, author, isbn, issn) VALUES (?, ?, ?, ?, ?)");
1031     $sth->execute($import_record_id, $title, $author, $isbn, $issn);
1032     $sth->finish();
1033                 
1034 }
1035
1036 sub _update_biblio_fields {
1037     my ($import_record_id, $marc_record) = @_;
1038
1039     my ($title, $author, $isbn, $issn) = _parse_biblio_fields($marc_record);
1040     my $dbh = C4::Context->dbh;
1041     # FIXME no controlnumber, originalsource
1042     # FIXME 2 - should regularize normalization of ISBN wherever it is done
1043     $isbn =~ s/\(.*$//;
1044     $isbn =~ tr/ -_//;
1045     $isbn = uc $isbn;
1046     my $sth = $dbh->prepare("UPDATE import_biblios SET title = ?, author = ?, isbn = ?, issn = ?
1047                              WHERE  import_record_id = ?");
1048     $sth->execute($title, $author, $isbn, $issn, $import_record_id);
1049     $sth->finish();
1050 }
1051
1052 sub _parse_biblio_fields {
1053     my ($marc_record) = @_;
1054
1055     my $dbh = C4::Context->dbh;
1056     my $bibliofields = TransformMarcToKoha($dbh, $marc_record, '');
1057     return ($bibliofields->{'title'}, $bibliofields->{'author'}, $bibliofields->{'isbn'}, $bibliofields->{'issn'});
1058
1059 }
1060
1061 sub _update_batch_record_counts {
1062     my ($batch_id) = @_;
1063
1064     my $dbh = C4::Context->dbh;
1065     my $sth = $dbh->prepare_cached("UPDATE import_batches SET num_biblios = (
1066                                     SELECT COUNT(*)
1067                                     FROM import_records
1068                                     WHERE import_batch_id = import_batches.import_batch_id
1069                                     AND record_type = 'biblio')
1070                                     WHERE import_batch_id = ?");
1071     $sth->bind_param(1, $batch_id);
1072     $sth->execute();
1073     $sth->finish();
1074     $sth = $dbh->prepare_cached("UPDATE import_batches SET num_items = (
1075                                     SELECT COUNT(*)
1076                                     FROM import_records
1077                                     JOIN import_items USING (import_record_id)
1078                                     WHERE import_batch_id = import_batches.import_batch_id
1079                                     AND record_type = 'biblio')
1080                                     WHERE import_batch_id = ?");
1081     $sth->bind_param(1, $batch_id);
1082     $sth->execute();
1083     $sth->finish();
1084
1085 }
1086
1087 1;
1088
1089 =head1 AUTHOR
1090
1091 Koha Development Team <info@koha.org>
1092
1093 Galen Charlton <galen.charlton@liblime.com>
1094
1095 =cut