bulkmarcimport.pl: XML input option documented
[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 binmode(STDOUT, ":utf8");
28
29 use Getopt::Long;
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 if ($format =~ /XML/i) {
111     # ugly hack follows -- MARC::File::XML, when used by MARC::Batch,
112     # appears to try to convert incoming XML records from MARC-8
113     # to UTF-8.  Setting the BinaryEncoding key turns that off
114     # TODO: see what happens to ISO-8859-1 XML files.
115     # TODO: determine if MARC::Batch can be fixed to handle
116     #       XML records properly -- it probably should be
117     #       be using a proper push or pull XML parser to
118     #       extract the records, not using regexes to look
119     #       for <record>.*</record>.
120     $MARC::File::XML::_load_args{BinaryEncoding} = 'utf-8';
121     $batch = MARC::Batch->new( 'XML', $input_marc_file );
122 } else {
123     $batch = MARC::Batch->new( 'USMARC', $input_marc_file );
124 }
125 $batch->warnings_off();
126 $batch->strict_off();
127 my $i=0;
128 my $commitnum = 50;
129
130 if ($commit) {
131
132 $commitnum = $commit;
133
134 }
135
136 $dbh->{AutoCommit} = 0;
137 RECORD: while ( my $record = $batch->next() ) {
138     $i++;
139     print ".";
140     print "\r$i" unless $i % 100;
141
142     if ($record->encoding() eq 'MARC-8' and not $skip_marc8_conversion) {
143         # FIXME update condition
144         my ($guessed_charset, $charset_errors);
145         ($record, $guessed_charset, $charset_errors) = MarcToUTF8Record($record, $marcFlavour);
146         if ($guessed_charset eq 'failed') {
147             warn "ERROR: failed to perform character conversion for record $i\n";
148             next RECORD;            
149         }
150     }
151
152     unless ($test_parameter) {
153         my ( $biblionumber, $biblioitemnumber, $itemnumbers_ref, $errors_ref );
154         eval { ( $biblionumber, $biblioitemnumber ) = AddBiblio($record, '', { defer_marc_save => 1 }) };
155         if ( $@ ) {
156             warn "ERROR: Adding biblio $biblionumber failed: $@\n";
157             next RECORD;
158         } 
159         eval { ( $itemnumbers_ref, $errors_ref ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
160         if ( $@ ) {
161             warn "ERROR: Adding items to bib $biblionumber failed: $@\n";
162             # if we failed because of an exception, assume that 
163             # the MARC columns in biblioitems were not set.
164             ModBiblioMarc( $record, $biblionumber, '' );
165             next RECORD;
166         } 
167         if ($#{ $errors_ref } > -1) { 
168             report_item_errors($biblionumber, $errors_ref);
169         }
170
171         $dbh->commit() if (0 == $i % $commitnum);
172     }
173     last if $i == $number;
174 }
175 $dbh->commit();
176
177
178 if ($fk_off) {
179         $dbh->do("SET FOREIGN_KEY_CHECKS = 1");
180 }
181
182 # restore CataloguingLog
183 $dbh->do("UPDATE systempreferences SET value=$CataloguingLog WHERE variable='CataloguingLog'");
184
185 my $timeneeded = gettimeofday - $starttime;
186 print "\n$i MARC records done in $timeneeded seconds\n";
187
188 exit 0;
189
190 sub report_item_errors {
191     my $biblionumber = shift;
192     my $errors_ref = shift;
193
194     foreach my $error (@{ $errors_ref }) {
195         my $msg = "Item not added (bib $biblionumber, item tag #$error->{'item_sequence'}, barcode $error->{'item_barcode'}): ";
196         my $error_code = $error->{'error_code'};
197         $error_code =~ s/_/ /g;
198         $msg .= "$error_code $error->{'error_information'}";
199         print $msg, "\n";
200     }
201 }