Bug 11850: Add -append option to bulkmarcimport.pl to append to logfile
[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::Koha;
24 use C4::Debug;
25 use C4::Charset;
26 use C4::Items;
27 use YAML;
28 use Unicode::Normalize;
29 use Time::HiRes qw(gettimeofday);
30 use Getopt::Long;
31 use IO::File;
32 use Pod::Usage;
33
34 use open qw( :std :encoding(UTF-8) );
35 binmode( STDOUT, ":encoding(UTF-8)" );
36 my ( $input_marc_file, $number, $offset) = ('',0,0);
37 my ($version, $delete, $test_parameter, $skip_marc8_conversion, $char_encoding, $verbose, $commit, $fk_off,$format,$biblios,$authorities,$keepids,$match, $isbn_check, $logfile);
38 my ( $insert, $filters, $update, $all, $yamlfile, $authtypes, $append );
39 my $cleanisbn = 1;
40 my ($sourcetag,$sourcesubfield,$idmapfl, $dedup_barcode);
41 my $framework = '';
42
43 $|=1;
44
45 GetOptions(
46     'commit:f'    => \$commit,
47     'file:s'    => \$input_marc_file,
48     'n:f' => \$number,
49     'o|offset:f' => \$offset,
50     'h' => \$version,
51     'd' => \$delete,
52     't|test' => \$test_parameter,
53     's' => \$skip_marc8_conversion,
54     'c:s' => \$char_encoding,
55     'v:s' => \$verbose,
56     'fk' => \$fk_off,
57     'm:s' => \$format,
58     'l:s' => \$logfile,
59     'append' => \$append,
60     'k|keepids:s' => \$keepids,
61     'b|biblios' => \$biblios,
62     'a|authorities' => \$authorities,
63     'authtypes:s' => \$authtypes,
64     'filter=s@'     => \$filters,
65     'insert'        => \$insert,
66     'update'        => \$update,
67     'all'           => \$all,
68     'match=s@'    => \$match,
69     'i|isbn' => \$isbn_check,
70     'x:s' => \$sourcetag,
71     'y:s' => \$sourcesubfield,
72     'idmap:s' => \$idmapfl,
73     'cleanisbn!'     => \$cleanisbn,
74     'yaml:s'        => \$yamlfile,
75     'dedupbarcode' => \$dedup_barcode,
76     'framework=s' => \$framework,
77 );
78 $biblios ||= !$authorities;
79 $insert  ||= !$update;
80 my $writemode = ($append) ? "a" : "w";
81
82 if ($all) {
83     $insert = 1;
84     $update = 1;
85 }
86
87 if ($version || ($input_marc_file eq '')) {
88     pod2usage( -verbose => 2 );
89     exit;
90 }
91
92 my $dbh = C4::Context->dbh;
93 my $heading_fields=get_heading_fields();
94
95 if (defined $idmapfl) {
96   open(IDMAP,">$idmapfl") or die "cannot open $idmapfl \n";
97 }
98
99 if ((not defined $sourcesubfield) && (not defined $sourcetag)){
100   $sourcetag="910";
101   $sourcesubfield="a";
102 }
103
104 # save the CataloguingLog property : we don't want to log a bulkmarcimport. It will slow the import & 
105 # will create problems in the action_logs table, that can't handle more than 1 entry per second per user.
106 my $CataloguingLog = C4::Context->preference('CataloguingLog');
107 $dbh->do("UPDATE systempreferences SET value=0 WHERE variable='CataloguingLog'");
108
109 if ($fk_off) {
110         $dbh->do("SET FOREIGN_KEY_CHECKS = 0");
111 }
112
113
114 if ($delete) {
115         if ($biblios){
116         print "deleting biblios\n";
117         $dbh->do("truncate biblio");
118         $dbh->do("truncate biblioitems");
119         $dbh->do("truncate items");
120         }
121         else {
122         print "deleting authorities\n";
123         $dbh->do("truncate auth_header");
124         }
125     $dbh->do("truncate zebraqueue");
126 }
127
128
129
130 if ($test_parameter) {
131     print "TESTING MODE ONLY\n    DOING NOTHING\n===============\n";
132 }
133
134 my $marcFlavour = C4::Context->preference('marcflavour') || 'MARC21';
135
136 print "Characteristic MARC flavour: $marcFlavour\n" if $verbose;
137 my $starttime = gettimeofday;
138 my $batch;
139 my $fh = IO::File->new($input_marc_file); # don't let MARC::Batch open the file, as it applies the ':utf8' IO layer
140 if (defined $format && $format =~ /XML/i) {
141     # ugly hack follows -- MARC::File::XML, when used by MARC::Batch,
142     # appears to try to convert incoming XML records from MARC-8
143     # to UTF-8.  Setting the BinaryEncoding key turns that off
144     # TODO: see what happens to ISO-8859-1 XML files.
145     # TODO: determine if MARC::Batch can be fixed to handle
146     #       XML records properly -- it probably should be
147     #       be using a proper push or pull XML parser to
148     #       extract the records, not using regexes to look
149     #       for <record>.*</record>.
150     $MARC::File::XML::_load_args{BinaryEncoding} = 'utf-8';
151     my $recordformat= ($marcFlavour eq "MARC21"?"USMARC":uc($marcFlavour));
152 #UNIMARC Authorities have a different way to manage encoding than UNIMARC biblios.
153     $recordformat=$recordformat."AUTH" if ($authorities and $marcFlavour ne "MARC21");
154     $MARC::File::XML::_load_args{RecordFormat} = $recordformat;
155     $batch = MARC::Batch->new( 'XML', $fh );
156 } else {
157     $batch = MARC::Batch->new( 'USMARC', $fh );
158 }
159 $batch->warnings_off();
160 $batch->strict_off();
161 my $i=0;
162 my $commitnum = $commit ? $commit : 50;
163 my $yamlhash;
164
165 # Skip file offset
166 if ( $offset ) {
167     print "Skipping file offset: $offset records\n";
168     $batch->next() while ($offset--);
169 }
170
171 my ($tagid,$subfieldid);
172 if ($authorities){
173           $tagid='001';
174 }
175 else {
176    ( $tagid, $subfieldid ) =
177             GetMarcFromKohaField( "biblio.biblionumber", $framework );
178         $tagid||="001";
179 }
180
181 # the SQL query to search on isbn
182 my $sth_isbn = $dbh->prepare("SELECT biblionumber,biblioitemnumber FROM biblioitems WHERE isbn=?");
183
184 $dbh->{AutoCommit} = 0;
185 my $loghandle;
186 if ($logfile){
187    $loghandle= IO::File->new($logfile, $writemode) ;
188    print $loghandle "id;operation;status\n";
189 }
190 RECORD: while (  ) {
191     my $record;
192     # get records
193     eval { $record = $batch->next() };
194     if ( $@ ) {
195         print "Bad MARC record $i: $@ skipped\n";
196         # FIXME - because MARC::Batch->next() combines grabbing the next
197         # blob and parsing it into one operation, a correctable condition
198         # such as a MARC-8 record claiming that it's UTF-8 can't be recovered
199         # from because we don't have access to the original blob.  Note
200         # that the staging import can deal with this condition (via
201         # C4::Charset::MarcToUTF8Record) because it doesn't use MARC::Batch.
202         next;
203     }
204     # skip if we get an empty record (that is MARC valid, but will result in AddBiblio failure
205     last unless ( $record );
206     $i++;
207     print ".";
208     print "\n$i" unless $i % 100;
209     
210     # transcode the record to UTF8 if needed & applicable.
211     if ($record->encoding() eq 'MARC-8' and not $skip_marc8_conversion) {
212         # FIXME update condition
213         my ($guessed_charset, $charset_errors);
214          ($record, $guessed_charset, $charset_errors) = MarcToUTF8Record($record, $marcFlavour.(($authorities and $marcFlavour ne "MARC21")?'AUTH':''));
215         if ($guessed_charset eq 'failed') {
216             warn "ERROR: failed to perform character conversion for record $i\n";
217             next RECORD;            
218         }
219     }
220     SetUTF8Flag($record);
221     my $isbn;
222     # remove trailing - in isbn (only for biblios, of course)
223     if ($biblios && $cleanisbn) {
224         my $tag = $marcFlavour eq 'UNIMARC' ? '010' : '020';
225         my $field = $record->field($tag);
226         my $isbn = $field && $field->subfield('a');
227         if ( $isbn ) {
228             $isbn =~ s/-//g;
229             $field->update('a' => $isbn);
230         }
231     }
232     my $id;
233     # search for duplicates (based on Local-number)
234     my $originalid;
235     $originalid = GetRecordId( $record, $tagid, $subfieldid );
236     if ($match) {
237         require C4::Search;
238         my $query = build_query( $match, $record );
239         my $server = ( $authorities ? 'authorityserver' : 'biblioserver' );
240         $debug && warn $query;
241         my ( $error, $results, $totalhits ) = C4::Search::SimpleSearch( $query, 0, 3, [$server] );
242         die "unable to search the database for duplicates : $error" if ( defined $error );
243         $debug && warn "$query $server : $totalhits";
244         if ( $results && scalar(@$results) == 1 ) {
245             my $marcrecord = C4::Search::new_record_from_zebra( $server, $results->[0] );
246             SetUTF8Flag($marcrecord);
247             $id = GetRecordId( $marcrecord, $tagid, $subfieldid );
248             if ( $authorities && $marcFlavour ) {
249                 #Skip if authority in database is the same as the on in database
250                 if ( $marcrecord->field('005') && $record->field('005') &&
251                      $marcrecord->field('005')->data && $record->field('005')->data &&
252                      $marcrecord->field('005')->data >= $record->field('005')->data ) {
253                     if ($yamlfile) {
254                         $yamlhash->{$originalid}->{'authid'} = $id;
255
256                         # we recover all subfields of the heading authorities
257                         my @subfields;
258                         foreach my $field ( $marcrecord->field("2..") ) {
259                             push @subfields, map { ( $_->[0] =~ /[a-z]/ ? $_->[1] : () ) } $field->subfields();
260                         }
261                         $yamlhash->{$originalid}->{'subfields'} = \@subfields;
262                     }
263                     next;
264                 }
265             }
266         } elsif ( $results && scalar(@$results) > 1 ) {
267             $debug && warn "more than one match for $query";
268         } else {
269             $debug && warn "nomatch for $query";
270         }
271     }
272     if ($keepids && $originalid) {
273             my $storeidfield;
274             if ( length($keepids) == 3 ) {
275                 $storeidfield = MARC::Field->new( $keepids, $originalid );
276             } else {
277                 $storeidfield = MARC::Field->new( substr( $keepids, 0, 3 ), "", "", substr( $keepids, 3, 1 ), $originalid );
278             }
279             $record->insert_fields_ordered($storeidfield);
280             $record->delete_field( $record->field($tagid) );
281     }
282     foreach my $stringfilter (@$filters) {
283         if ( length($stringfilter) == 3 ) {
284             foreach my $field ( $record->field($stringfilter) ) {
285                 $record->delete_field($field);
286                 $debug && warn "removed : ", $field->as_string;
287             }
288         } elsif ($stringfilter =~ /([0-9]{3})([a-z0-9])(.*)/) {
289             my $removetag = $1;
290             my $removesubfield = $2;
291             my $removematch = $3;
292             if ( ( $removetag > "010" ) && $removesubfield ) {
293                 foreach my $field ( $record->field($removetag) ) {
294                     $field->delete_subfield( code => "$removesubfield", match => $removematch );
295                     $debug && warn "Potentially removed : ", $field->subfield($removesubfield);
296                 }
297             }
298         }
299     }
300     unless ($test_parameter) {
301         if ($authorities){
302             use C4::AuthoritiesMarc;
303             my $authtypecode=GuessAuthTypeCode($record, $heading_fields);
304             my $authid= ($id?$id:GuessAuthId($record));
305             if ($authid && GetAuthority($authid) && $update ){
306             ## Authority has an id and is in database : Replace
307                 eval { ( $authid ) = ModAuthority($authid,$record, $authtypecode) };
308                 if ($@){
309                     warn "Problem with authority $authid Cannot Modify";
310                                         printlog({id=>$originalid||$id||$authid, op=>"edit",status=>"ERROR"}) if ($logfile);
311                 }
312                                 else{
313                                         printlog({id=>$originalid||$id||$authid, op=>"edit",status=>"ok"}) if ($logfile);
314                                 }
315             }  
316             elsif (defined $authid) {
317             ## An authid is defined but no authority in database : add
318                 eval { ( $authid ) = AddAuthority($record,$authid, $authtypecode) };
319                 if ($@){
320                     warn "Problem with authority $authid Cannot Add ".$@;
321                                         printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ERROR"}) if ($logfile);
322                 }
323                                 else{
324                                         printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ok"}) if ($logfile);
325                                 }
326             }
327                 else {
328             ## True insert in database
329                 eval { ( $authid ) = AddAuthority($record,"", $authtypecode) };
330                 if ($@){
331                     warn "Problem with authority $authid Cannot Add".$@;
332                                         printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ERROR"}) if ($logfile);
333                 }
334                                 else{
335                                         printlog({id=>$originalid||$id||$authid, op=>"insert",status=>"ok"}) if ($logfile);
336                                 }
337                 }
338             if ($yamlfile) {
339             $yamlhash->{$originalid}->{'authid'} = $authid;
340             my @subfields;
341             foreach my $field ( $record->field("2..") ) {
342                 push @subfields, map { ( $_->[0] =~ /[a-z]/ ? $_->[1] : () ) } $field->subfields();
343             }
344             $yamlhash->{$originalid}->{'subfields'} = \@subfields;
345             }
346         }
347         else {
348             my ( $biblionumber, $biblioitemnumber, $itemnumbers_ref, $errors_ref );
349             $biblionumber = $id;
350             # check for duplicate, based on ISBN (skip it if we already have found a duplicate with match parameter
351             if (!$biblionumber && $isbn_check && $isbn) {
352     #         warn "search ISBN : $isbn";
353                 $sth_isbn->execute($isbn);
354                 ($biblionumber,$biblioitemnumber) = $sth_isbn->fetchrow;
355             }
356                 if (defined $idmapfl) {
357                                 if ($sourcetag < "010"){
358                                         if ($record->field($sourcetag)){
359                                           my $source = $record->field($sourcetag)->data();
360                                           printf(IDMAP "%s|%s\n",$source,$biblionumber);
361                                         }
362                             } else {
363                                         my $source=$record->subfield($sourcetag,$sourcesubfield);
364                                         printf(IDMAP "%s|%s\n",$source,$biblionumber);
365                           }
366                         }
367                                         # create biblio, unless we already have it ( either match or isbn )
368             if ($biblionumber) {
369                 eval{$biblioitemnumber=GetBiblioData($biblionumber)->{biblioitemnumber};};
370                 if ($update) {
371                     eval { ( $biblionumber, $biblioitemnumber ) = ModBiblio( $record, $biblionumber, GetFrameworkcode($biblionumber) ) };
372                     if ($@) {
373                         warn "ERROR: Edit biblio $biblionumber failed: $@\n";
374                         printlog( { id => $id || $originalid || $biblionumber, op => "update", status => "ERROR" } ) if ($logfile);
375                         next RECORD;
376                     } else {
377                         printlog( { id => $id || $originalid || $biblionumber, op => "update", status => "ok" } ) if ($logfile);
378                     }
379                 } else {
380                     printlog( { id => $id || $originalid || $biblionumber, op => "insert", status => "warning : already in database" } ) if ($logfile);
381                 }
382             } else {
383                 if ($insert) {
384                     eval { ( $biblionumber, $biblioitemnumber ) = AddBiblio( $record, '', { defer_marc_save => 1 } ) };
385                     if ($@) {
386                         warn "ERROR: Adding biblio $biblionumber failed: $@\n";
387                         printlog( { id => $id || $originalid || $biblionumber, op => "insert", status => "ERROR" } ) if ($logfile);
388                         next RECORD;
389                     } else {
390                         printlog( { id => $id || $originalid || $biblionumber, op => "insert", status => "ok" } ) if ($logfile);
391                     }
392                 } else {
393                     printlog( { id => $id || $originalid || $biblionumber, op => "update", status => "warning : not in database" } ) if ($logfile);
394                 }
395             }
396             eval { ( $itemnumbers_ref, $errors_ref ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
397             my $error_adding = $@;
398             # Work on a clone so that if there are real errors, we can maybe
399             # fix them up later.
400                         my $clone_record = $record->clone();
401             C4::Biblio::_strip_item_fields($clone_record, '');
402             # This sets the marc fields if there was an error, and also calls
403             # defer_marc_save.
404             ModBiblioMarc( $clone_record, $biblionumber, $framework );
405             if ( $error_adding ) {
406                 warn "ERROR: Adding items to bib $biblionumber failed: $error_adding";
407                                 printlog({id=>$id||$originalid||$biblionumber, op=>"insertitem",status=>"ERROR"}) if ($logfile);
408                 # if we failed because of an exception, assume that 
409                 # the MARC columns in biblioitems were not set.
410                 next RECORD;
411             }
412                         else{
413                                 printlog({id=>$id||$originalid||$biblionumber, op=>"insert",status=>"ok"}) if ($logfile);
414                         }
415             if ($dedup_barcode && grep { exists $_->{error_code} && $_->{error_code} eq 'duplicate_barcode' } @$errors_ref) {
416                 # Find the record called 'barcode'
417                 my ($tag, $sub) = C4::Biblio::GetMarcFromKohaField('items.barcode', $framework);
418                 # Now remove any items that didn't have a duplicate_barcode error,
419                 # erase the barcodes on items that did, and re-add those items.
420                 my %dupes;
421                 foreach my $i (0 .. $#{$errors_ref}) {
422                     my $ref = $errors_ref->[$i];
423                     if ($ref && ($ref->{error_code} eq 'duplicate_barcode')) {
424                         $dupes{$ref->{item_sequence}} = 1;
425                         # Delete the error message because we're going to
426                         # retry this one.
427                         delete $errors_ref->[$i];
428                     }
429                 }
430                 my $seq = 0;
431                 foreach my $field ($record->field($tag)) {
432                     $seq++;
433                     if ($dupes{$seq}) {
434                         # Here we remove the barcode
435                         $field->delete_subfield(code => $sub);
436                     } else {
437                         # otherwise we delete the field because we don't want
438                         # two of them
439                         $record->delete_fields($field);
440                     }
441                 }
442                 # Now re-add the record as before, adding errors to the prev list
443                 my $more_errors;
444                 eval { ( $itemnumbers_ref, $more_errors ) = AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); };
445                 if ( $@ ) {
446                     warn "ERROR: Adding items to bib $biblionumber failed: $@\n";
447                     printlog({id=>$id||$originalid||$biblionumber, op=>"insertitem",status=>"ERROR"}) if ($logfile);
448                     # if we failed because of an exception, assume that
449                     # the MARC columns in biblioitems were not set.
450                     ModBiblioMarc( $record, $biblionumber, $framework );
451                     next RECORD;
452                 } else {
453                     printlog({id=>$id||$originalid||$biblionumber, op=>"insert",status=>"ok"}) if ($logfile);
454                 }
455                 push @$errors_ref, @{ $more_errors };
456             }
457             if ($#{ $errors_ref } > -1) {
458                 report_item_errors($biblionumber, $errors_ref);
459             }
460             $yamlhash->{$originalid} = $biblionumber if ($yamlfile);
461         }
462         $dbh->commit() if (0 == $i % $commitnum);
463     }
464     last if $i == $number;
465 }
466 $dbh->commit();
467 $dbh->{AutoCommit} = 1;
468
469
470 if ($fk_off) {
471         $dbh->do("SET FOREIGN_KEY_CHECKS = 1");
472 }
473
474 # restore CataloguingLog
475 $dbh->do("UPDATE systempreferences SET value=$CataloguingLog WHERE variable='CataloguingLog'");
476
477 my $timeneeded = gettimeofday - $starttime;
478 print "\n$i MARC records done in $timeneeded seconds\n";
479 if ($logfile){
480   print $loghandle "file : $input_marc_file\n";
481   print $loghandle "$i MARC records done in $timeneeded seconds\n";
482   $loghandle->close;
483 }
484 if ($yamlfile) {
485     open my $yamlfileout, q{>}, "$yamlfile" or die "cannot open $yamlfile \n";
486     print $yamlfileout Dump($yamlhash);
487 }
488 exit 0;
489
490 sub GetRecordId{
491         my $marcrecord=shift;
492         my $tag=shift;
493         my $subfield=shift;
494         my $id;
495         if ($tag lt "010"){
496                 return $marcrecord->field($tag)->data() if $marcrecord->field($tag);
497         } 
498         elsif ($subfield){
499                 if ($marcrecord->field($tag)){
500                         return $marcrecord->subfield($tag,$subfield);
501                 }
502         }
503         return $id;
504 }
505 sub build_query {
506         my $match = shift;
507         my $record=shift;
508         my @searchstrings;
509         foreach my $matchingpoint (@$match){
510           my $string = build_simplequery($matchingpoint,$record);
511           push @searchstrings,$string if (length($string)>0);
512         }
513     my $QParser;
514     $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
515     my $op;
516     if ($QParser) {
517         $op = '&&';
518     } else {
519         $op = 'and';
520     }
521     return join(" $op ",@searchstrings);
522 }
523 sub build_simplequery {
524         my $element=shift;
525         my $record=shift;
526     my @searchstrings;
527     my ($index,$recorddata)=split /,/,$element;
528     if ($recorddata=~/(\d{3})(.*)/) {
529         my ($tag,$subfields) =($1,$2);
530         foreach my $field ($record->field($tag)){
531                   if (length($field->as_string("$subfields"))>0){
532               push @searchstrings,"$index:\"".$field->as_string("$subfields")."\"";
533                   }
534         }
535     }
536     my $QParser;
537     $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser'));
538     my $op;
539     if ($QParser) {
540         $op = '&&';
541     } else {
542         $op = 'and';
543     }
544     return join(" $op ",@searchstrings);
545 }
546 sub report_item_errors {
547     my $biblionumber = shift;
548     my $errors_ref = shift;
549
550     foreach my $error (@{ $errors_ref }) {
551         next if !$error;
552         my $msg = "Item not added (bib $biblionumber, item tag #$error->{'item_sequence'}, barcode $error->{'item_barcode'}): ";
553         my $error_code = $error->{'error_code'};
554         $error_code =~ s/_/ /g;
555         $msg .= "$error_code $error->{'error_information'}";
556         print $msg, "\n";
557     }
558 }
559 sub printlog{
560         my $logelements=shift;
561     print $loghandle join( ";", map { defined $_ ? $_ : "" } @$logelements{qw<id op status>} ), "\n";
562 }
563 sub get_heading_fields{
564     my $headingfields;
565     if ($authtypes){
566         $headingfields=YAML::LoadFile($authtypes);
567         $headingfields={C4::Context->preference('marcflavour')=>$headingfields};
568         $debug && warn YAML::Dump($headingfields);
569     }
570     unless ($headingfields){
571         $headingfields=$dbh->selectall_hashref("SELECT auth_tag_to_report, authtypecode from auth_types",'auth_tag_to_report',{Slice=>{}});
572         $headingfields={C4::Context->preference('marcflavour')=>$headingfields};
573     }
574     return $headingfields;
575 }
576
577 =head1 NAME
578
579 bulkmarcimport.pl - Import bibliographic/authority records into Koha
580
581 =head1 USAGE
582
583  $ export KOHA_CONF=/etc/koha.conf
584  $ perl misc/migration_tools/bulkmarcimport.pl -d -commit 1000 \\
585     -file /home/jmf/koha.mrc -n 3000
586
587 =head1 WARNING
588
589 Don't use this script before you've entered and checked your MARC parameters
590 tables twice (or more!). Otherwise, the import won't work correctly and you
591 will get invalid data.
592
593 =head1 DESCRIPTION
594
595 =over
596
597 =item  B<-h>
598
599 This version/help screen
600
601 =item B<-b, -biblios>
602
603 Type of import: bibliographic records
604
605 =item B<-a, -authorities>
606
607 Type of import: authority records
608
609 =item B<-file>=I<FILE>
610
611 The I<FILE> to import
612
613 =item  B<-v>
614
615 Verbose mode. 1 means "some infos", 2 means "MARC dumping"
616
617 =item B<-fk>
618
619 Turn off foreign key checks during import.
620
621 =item B<-n>=I<NUMBER>
622
623 The I<NUMBER> of records to import. If missing, all the file is imported
624
625 =item B<-o, -offset>=I<NUMBER>
626
627 File offset before importing, ie I<NUMBER> of records to skip.
628
629 =item B<-commit>=I<NUMBER>
630
631 The I<NUMBER> of records to wait before performing a 'commit' operation
632
633 =item B<-l>
634
635 File logs actions done for each record and their status into file
636
637 =item B<-append>
638
639 If specified, data will be appended to the logfile. If not, the logfile will be erased for each execution.
640
641 =item B<-t, -test>
642
643 Test mode: parses the file, saying what he would do, but doing nothing.
644
645 =item B<-s>
646
647 Skip automatic conversion of MARC-8 to UTF-8.  This option is provided for
648 debugging.
649
650 =item B<-c>=I<CHARACTERISTIC>
651
652 The I<CHARACTERISTIC> MARC flavour. At the moment, only I<MARC21> and
653 I<UNIMARC> are supported. MARC21 by default.
654
655 =item B<-d>
656
657 Delete EVERYTHING related to biblio in koha-DB before import. Tables: biblio,
658 biblioitems, items
659
660 =item B<-m>=I<FORMAT>
661
662 Input file I<FORMAT>: I<MARCXML> or I<ISO2709> (defaults to ISO2709)
663
664 =item B<-authtypes>
665
666 file yamlfile with authoritiesTypes and distinguishable record field in order
667 to store the correct authtype
668
669 =item B<-yaml>
670
671 yaml file  format a yaml file with ids
672
673 =item B<-filter>
674
675 list of fields that will not be imported. Can be any from 000 to 999 or field,
676 subfield and subfield's matching value such as 200avalue
677
678 =item B<-insert>
679
680 if set, only insert when possible
681
682 =item B<-update>
683
684 if set, only updates (any biblio should have a matching record)
685
686 =item B<-all>
687
688 if set, do whatever is required
689
690 =item B<-k, -keepids>=<FIELD>
691
692 Field store ids in I<FIELD> (usefull for authorities, where 001 contains the
693 authid for Koha, that can contain a very valuable info for authorities coming
694 from LOC or BNF. useless for biblios probably)
695
696 =item B<-match>=<FIELD>
697
698 I<FIELD> matchindex,fieldtomatch matchpoint to use to deduplicate fieldtomatch
699 can be either 001 to 999 or field and list of subfields as such 100abcde
700
701 =item B<-i,-isbn>
702
703 If set, a search will be done on isbn, and, if the same isbn is found, the
704 biblio is not added. It's another method to deduplicate.  B<-match> & B<-isbn>
705 can be both set.
706
707 =item B<-cleanisbn>
708
709 Clean ISBN fields from entering biblio records, ie removes hyphens. By default,
710 ISBN are cleaned. --nocleanisbn will keep ISBN unchanged.
711
712 =item B<-x>=I<TAG>
713
714 Source bib I<TAG> for reporting the source bib number
715
716 =item B<-y>=I<SUBFIELD>
717
718 Source I<SUBFIELD> for reporting the source bib number
719
720 =item B<-idmap>=I<FILE>
721
722 I<FILE> for the koha bib and source id
723
724 =item B<-keepids>
725
726 Store ids in 009 (usefull for authorities, where 001 contains the authid for
727 Koha, that can contain a very valuable info for authorities coming from LOC or
728 BNF. useless for biblios probably)
729
730 =item B<-dedupbarcode>
731
732 If set, whenever a duplicate barcode is detected, it is removed and the attempt
733 to add the record is retried, thereby giving the record a blank barcode. This
734 is useful when something has set barcodes to be a biblio ID, or similar
735 (usually other software.)
736
737 =item B<-framework>
738
739 This is the code for the framework that the requested records will have attached
740 to them when they are created. If not specified, then the default framework
741 will be used.
742
743 =back
744
745 =cut
746