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