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