do not let MARC::Batch open MARC files
[koha.git] / misc / migration_tools / bulkmarcimport.pl
1 #!/usr/bin/perl
2 # Import an iso2709 file into Koha 3
3
4 use strict;
5 # use warnings;
6 BEGIN {
7     # find Koha's Perl modules
8     # test carefully before changing this
9     use FindBin;
10     eval { require "$FindBin::Bin/../kohalib.pl" };
11 }
12
13 # Koha modules used
14 use MARC::File::USMARC;
15 use MARC::File::XML;
16 use MARC::Record;
17 use MARC::Batch;
18 use MARC::Charset;
19
20 use C4::Context;
21 use C4::Biblio;
22 use C4::Charset;
23 use C4::Items;
24 use Unicode::Normalize;
25 use Time::HiRes qw(gettimeofday);
26 use Getopt::Long;
27 use IO::File;
28
29 binmode(STDOUT, ":utf8");
30
31 my ( $input_marc_file, $number) = ('',0);
32 my ($version, $delete, $test_parameter, $skip_marc8_conversion, $char_encoding, $verbose, $commit, $fk_off,$format);
33
34 $|=1;
35
36 GetOptions(
37     'commit:f'    => \$commit,
38     'file:s'    => \$input_marc_file,
39     'n:f' => \$number,
40     'h' => \$version,
41     'd' => \$delete,
42     't' => \$test_parameter,
43     's' => \$skip_marc8_conversion,
44     'c:s' => \$char_encoding,
45     'v:s' => \$verbose,
46     'fk' => \$fk_off,
47     'm:s' => \$format,
48 );
49
50 if ($version || ($input_marc_file eq '')) {
51     print <<EOF
52 small script to import an iso2709 file into Koha.
53 parameters :
54 \th : this version/help screen
55 \tfile /path/to/file/to/dump : the file to import
56 \tv : verbose mode. 1 means "some infos", 2 means "MARC dumping"
57 \tfk : Turn off foreign key checks during import.
58 \tn : the number of records to import. If missing, all the file is imported
59 \tcommit : the number of records to wait before performing a 'commit' operation
60 \tt : test mode : parses the file, saying what he would do, but doing nothing.
61 \ts : skip automatic conversion of MARC-8 to UTF-8.  This option is 
62 \t    provided for debugging.
63 \tc : the characteristic MARC flavour. At the moment, only MARC21 and UNIMARC 
64 \tsupported. MARC21 by default.
65 \td : delete EVERYTHING related to biblio in koha-DB before import  :tables :
66 \t\tbiblio, \tbiblioitems,\titems
67 \tm : format, MARCXML or ISO2709 (defaults to ISO2709)
68 IMPORTANT : don't use this script before you've entered and checked your MARC parameters tables twice (or more!).
69 Otherwise, the import won't work correctly and you will get invalid data.
70
71 SAMPLE : 
72 \t\$ export KOHA_CONF=/etc/koha.conf
73 \t\$ perl misc/migration_tools/bulkmarcimport.pl -d -commit 1000 -file /home/jmf/koha.mrc -n 3000
74 EOF
75 ;#'
76 exit;
77 }
78
79 my $dbh = C4::Context->dbh;
80
81 # save the CataloguingLog property : we don't want to log a bulkmarcimport. It will slow the import & 
82 # will create problems in the action_logs table, that can't handle more than 1 entry per second per user.
83 my $CataloguingLog = C4::Context->preference('CataloguingLog');
84 $dbh->do("UPDATE systempreferences SET value=0 WHERE variable='CataloguingLog'");
85
86 if ($fk_off) {
87         $dbh->do("SET FOREIGN_KEY_CHECKS = 0");
88 }
89
90
91 if ($delete) {
92     print "deleting biblios\n";
93     $dbh->do("truncate biblio");
94     $dbh->do("truncate biblioitems");
95     $dbh->do("truncate items");
96     $dbh->do("truncate zebraqueue");
97 }
98
99
100
101 if ($test_parameter) {
102     print "TESTING MODE ONLY\n    DOING NOTHING\n===============\n";
103 }
104
105 my $marcFlavour = C4::Context->preference('marcflavour') || 'MARC21';
106
107 print "Characteristic MARC flavour: $marcFlavour\n" if $verbose;
108 my $starttime = gettimeofday;
109 my $batch;
110 my $fh = IO::File->new($input_marc_file); # don't let MARC::Batch open the file, as it applies the ':utf8' IO layer
111 if ($format =~ /XML/i) {
112     # ugly hack follows -- MARC::File::XML, when used by MARC::Batch,
113     # appears to try to convert incoming XML records from MARC-8
114     # to UTF-8.  Setting the BinaryEncoding key turns that off
115     # TODO: see what happens to ISO-8859-1 XML files.
116     # TODO: determine if MARC::Batch can be fixed to handle
117     #       XML records properly -- it probably should be
118     #       be using a proper push or pull XML parser to
119     #       extract the records, not using regexes to look
120     #       for <record>.*</record>.
121     $MARC::File::XML::_load_args{BinaryEncoding} = 'utf-8';
122     $batch = MARC::Batch->new( 'XML', $fh );
123 } else {
124     $batch = MARC::Batch->new( 'USMARC', $fh );
125 }
126 $batch->warnings_off();
127 $batch->strict_off();
128 my $i=0;
129 my $commitnum = 50;
130
131 if ($commit) {
132
133 $commitnum = $commit;
134
135 }
136
137 $dbh->{AutoCommit} = 0;
138 RECORD: while ( my $record = $batch->next() ) {
139     $i++;
140     print ".";
141     print "\r$i" unless $i % 100;
142
143     if ($record->encoding() eq 'MARC-8' and not $skip_marc8_conversion) {
144         # FIXME update condition
145         my ($guessed_charset, $charset_errors);
146         ($record, $guessed_charset, $charset_errors) = MarcToUTF8Record($record, $marcFlavour);
147         if ($guessed_charset eq 'failed') {
148             warn "ERROR: failed to perform character conversion for record $i\n";
149             next RECORD;            
150         }
151     }
152
153     unless ($test_parameter) {
154         my ( $biblionumber, $biblioitemnumber, $itemnumbers_ref, $errors_ref );
155         eval { ( $biblionumber, $biblioitemnumber ) = AddBiblio($record, '', { defer_marc_save => 1 }) };
156         if ( $@ ) {
157             warn "ERROR: Adding biblio $biblionumber failed: $@\n";
158             next RECORD;
159         } 
160         eval { ( $itemnumbers_ref, $errors_ref ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
161         if ( $@ ) {
162             warn "ERROR: Adding items to bib $biblionumber failed: $@\n";
163             # if we failed because of an exception, assume that 
164             # the MARC columns in biblioitems were not set.
165             ModBiblioMarc( $record, $biblionumber, '' );
166             next RECORD;
167         } 
168         if ($#{ $errors_ref } > -1) { 
169             report_item_errors($biblionumber, $errors_ref);
170         }
171
172         $dbh->commit() if (0 == $i % $commitnum);
173     }
174     last if $i == $number;
175 }
176 $dbh->commit();
177
178
179 if ($fk_off) {
180         $dbh->do("SET FOREIGN_KEY_CHECKS = 1");
181 }
182
183 # restore CataloguingLog
184 $dbh->do("UPDATE systempreferences SET value=$CataloguingLog WHERE variable='CataloguingLog'");
185
186 my $timeneeded = gettimeofday - $starttime;
187 print "\n$i MARC records done in $timeneeded seconds\n";
188
189 exit 0;
190
191 sub report_item_errors {
192     my $biblionumber = shift;
193     my $errors_ref = shift;
194
195     foreach my $error (@{ $errors_ref }) {
196         my $msg = "Item not added (bib $biblionumber, item tag #$error->{'item_sequence'}, barcode $error->{'item_barcode'}): ";
197         my $error_code = $error->{'error_code'};
198         $error_code =~ s/_/ /g;
199         $msg .= "$error_code $error->{'error_information'}";
200         print $msg, "\n";
201     }
202 }