IMPORTANT - refactor MARC character set handling
[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 # Uncomment the line below and use MARC::File::XML again when it works better.
16 # -- thd
17 # use MARC::File::XML;
18 use MARC::Record;
19 use MARC::Batch;
20 use MARC::Charset;
21
22 use C4::Context;
23 use C4::Biblio;
24 use C4::Charset;
25 use C4::Items;
26 use Unicode::Normalize;
27 use Time::HiRes qw(gettimeofday);
28 use Getopt::Long;
29 binmode(STDOUT, ":utf8");
30
31 use Getopt::Long;
32
33 my ( $input_marc_file, $number) = ('',0);
34 my ($version, $delete, $test_parameter, $skip_marc8_conversion, $char_encoding, $verbose, $commit, $fk_off,$format);
35
36 $|=1;
37
38 GetOptions(
39     'commit:f'    => \$commit,
40     'file:s'    => \$input_marc_file,
41     'n:f' => \$number,
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 an iso2709 file into Koha.
55 parameters :
56 \th : this version/help screen
57 \tfile /path/to/file/to/dump : the file to import
58 \tv : verbose mode. 1 means "some infos", 2 means "MARC dumping"
59 \tfk : Turn off foreign key checks during import.
60 \tn : the number of records to import. If missing, all the file is imported
61 \tcommit : the number of records to wait before performing a 'commit' operation
62 \tt : test mode : parses the file, saying what he would do, but doing nothing.
63 \ts : skip automatic conversion of MARC-8 to UTF-8.  This option is 
64 \t    provided for debugging.
65 \tc : the characteristic MARC flavour. At the moment, only MARC21 and UNIMARC 
66 \tsupported. MARC21 by default.
67 \td : delete EVERYTHING related to biblio in koha-DB before import  :tables :
68 \t\tbiblio, \tbiblioitems,\titems
69 IMPORTANT : don't use this script before you've entered and checked your MARC parameters tables twice (or more!).
70 Otherwise, the import won't work correctly and you will get invalid data.
71
72 SAMPLE : 
73 \t\$ export KOHA_CONF=/etc/koha.conf
74 \t\$ perl misc/migration_tools/bulkmarcimport.pl -d -commit 1000 -file /home/jmf/koha.mrc -n 3000
75 EOF
76 ;#'
77 exit;
78 }
79
80 my $dbh = C4::Context->dbh;
81
82 # save the CataloguingLog property : we don't want to log a bulkmarcimport. It will slow the import & 
83 # will create problems in the action_logs table, that can't handle more than 1 entry per second per user.
84 my $CataloguingLog = C4::Context->preference('CataloguingLog');
85 $dbh->do("UPDATE systempreferences SET value=0 WHERE variable='CataloguingLog'");
86
87 if ($delete) {
88     print "deleting biblios\n";
89     $dbh->do("truncate biblio");
90     $dbh->do("truncate biblioitems");
91     $dbh->do("truncate items");
92     $dbh->do("truncate zebraqueue");
93 }
94 if ($fk_off) {
95         $dbh->do("SET FOREIGN_KEY_CHECKS = 0");
96 }
97 if ($test_parameter) {
98     print "TESTING MODE ONLY\n    DOING NOTHING\n===============\n";
99 }
100
101 my $marcFlavour = C4::Context->preference('marcflavour') || 'MARC21';
102
103 print "Characteristic MARC flavour: $marcFlavour\n" if $verbose;
104 my $starttime = gettimeofday;
105 my $batch;
106 if ($format =~ /XML/i) {
107     $batch = MARC::Batch->new( 'XML', $input_marc_file );
108 } else {
109     $batch = MARC::Batch->new( 'USMARC', $input_marc_file );
110 }
111 $batch->warnings_off();
112 $batch->strict_off();
113 my $i=0;
114 my $commitnum = 50;
115
116 if ($commit) {
117
118 $commitnum = $commit;
119
120 }
121
122 $dbh->{AutoCommit} = 0;
123 RECORD: while ( my $record = $batch->next() ) {
124     $i++;
125     print ".";
126     print "\r$i" unless $i % 100;
127
128     if ($record->encoding() eq 'MARC-8' and not $skip_marc8_conversion) {
129         # FIXME update condition
130         my ($guessed_charset, $charset_errors);
131         ($record, $guessed_charset, $charset_errors) = MarcToUTF8Record($record, $marcFlavour);
132         if ($guessed_charset eq 'failed') {
133             warn "ERROR: failed to perform character conversion for record $i\n";
134             next RECORD;            
135         }
136     }
137
138     unless ($test_parameter) {
139         my ( $biblionumber, $biblioitemnumber, $itemnumbers_ref, $errors_ref );
140         eval { ( $biblionumber, $biblioitemnumber ) = AddBiblio($record, '', { defer_marc_save => 1 }) };
141         if ( $@ ) {
142             warn "ERROR: Adding biblio $biblionumber failed: $@\n";
143             next RECORD;
144         } 
145         eval { ( $itemnumbers_ref, $errors_ref ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
146         if ( $@ ) {
147             warn "ERROR: Adding items to bib $biblionumber failed: $@\n";
148             # if we failed because of an exception, assume that 
149             # the MARC columns in biblioitems were not set.
150             ModBiblioMarc( $record, $biblionumber, '' );
151             next RECORD;
152         } 
153         if ($#{ $errors_ref } > -1) { 
154             report_item_errors($biblionumber, $errors_ref);
155         }
156
157         $dbh->commit() if (0 == $i % $commitnum);
158     }
159     last if $i == $number;
160 }
161 $dbh->commit();
162
163
164 if ($fk_off) {
165         $dbh->do("SET FOREIGN_KEY_CHECKS = 1");
166 }
167
168 # restore CataloguingLog
169 $dbh->do("UPDATE systempreferences SET value=$CataloguingLog WHERE variable='CataloguingLog'");
170
171 my $timeneeded = gettimeofday - $starttime;
172 print "\n$i MARC records done in $timeneeded seconds\n";
173
174 exit 0;
175
176 sub report_item_errors {
177     my $biblionumber = shift;
178     my $errors_ref = shift;
179
180     foreach my $error (@{ $errors_ref }) {
181         my $msg = "Item not added (bib $biblionumber, item tag #$error->{'item_sequence'}, barcode $error->{'item_barcode'}): ";
182         my $error_code = $error->{'error_code'};
183         $error_code =~ s/_/ /g;
184         $msg .= "$error_code $error->{'error_information'}";
185         print $msg, "\n";
186     }
187 }