Bug 8136 Changes the expected lenght of 100$a in rebuild_zebra.pl
[koha.git] / misc / migration_tools / rebuild_zebra.pl
1 #!/usr/bin/perl
2
3 use strict;
4 #use warnings; FIXME - Bug 2505
5
6 use C4::Context;
7 use Getopt::Long;
8 use File::Temp qw/ tempdir /;
9 use File::Path;
10 use C4::Biblio;
11 use C4::AuthoritiesMarc;
12 use C4::Items;
13
14
15 # script that checks zebradir structure & create directories & mandatory files if needed
16 #
17 #
18
19 $|=1; # flushes output
20 # If the cron job starts us in an unreadable dir, we will break without
21 # this.
22 chdir $ENV{HOME} if (!(-r '.'));
23 my $directory;
24 my $nosanitize;
25 my $skip_export;
26 my $keep_export;
27 my $reset;
28 my $biblios;
29 my $authorities;
30 my $noxml;
31 my $noshadow;
32 my $do_munge;
33 my $want_help;
34 my $as_xml;
35 my $process_zebraqueue;
36 my $do_not_clear_zebraqueue;
37 my $length;
38 my $where;
39 my $offset;
40 my $verbose_logging = 0;
41 my $zebraidx_log_opt = " -v none,fatal,warn ";
42 my $result = GetOptions(
43     'd:s'           => \$directory,
44     'r|reset'       => \$reset,
45     's'             => \$skip_export,
46     'k'             => \$keep_export,
47     'nosanitize'    => \$nosanitize,
48     'b'             => \$biblios,
49     'noxml'         => \$noxml,
50     'w'             => \$noshadow,
51     'munge-config'  => \$do_munge,
52     'a'             => \$authorities,
53     'h|help'        => \$want_help,
54         'x'                             => \$as_xml,
55     'y'             => \$do_not_clear_zebraqueue,
56     'z'             => \$process_zebraqueue,
57     'where:s'        => \$where,
58     'length:i'        => \$length,
59     'offset:i'      => \$offset,
60     'v+'             => \$verbose_logging,
61 );
62
63
64 if (not $result or $want_help) {
65     print_usage();
66     exit 0;
67 }
68
69 if (not $biblios and not $authorities) {
70     my $msg = "Must specify -b or -a to reindex bibs or authorities\n";
71     $msg   .= "Please do '$0 --help' to see usage.\n";
72     die $msg;
73 }
74
75 if ( !$as_xml and $nosanitize ) {
76     my $msg = "Cannot specify both -no_xml and -nosanitize\n";
77     $msg   .= "Please do '$0 --help' to see usage.\n";
78     die $msg;
79 }
80
81 if ($process_zebraqueue and ($skip_export or $reset)) {
82     my $msg = "Cannot specify -r or -s if -z is specified\n";
83     $msg   .= "Please do '$0 --help' to see usage.\n";
84     die $msg;
85 }
86
87 if ($process_zebraqueue and $do_not_clear_zebraqueue) {
88     my $msg = "Cannot specify both -y and -z\n";
89     $msg   .= "Please do '$0 --help' to see usage.\n";
90     die $msg;
91 }
92
93 if ($noshadow) {
94     $noshadow = ' -n ';
95 }
96
97 #  -v is for verbose, which seems backwards here because of how logging is set
98 #    on the CLI of zebraidx.  It works this way.  The default is to not log much
99 if ($verbose_logging >= 2) {
100     $zebraidx_log_opt = '-v none,fatal,warn,all';
101 }
102
103 my $use_tempdir = 0;
104 unless ($directory) {
105     $use_tempdir = 1;
106     $directory = tempdir(CLEANUP => ($keep_export ? 0 : 1));
107
108
109
110 my $biblioserverdir = C4::Context->zebraconfig('biblioserver')->{directory};
111 my $authorityserverdir = C4::Context->zebraconfig('authorityserver')->{directory};
112
113 my $kohadir = C4::Context->config('intranetdir');
114 my $bib_index_mode = C4::Context->config('zebra_bib_index_mode') || 'grs1';
115 my $auth_index_mode = C4::Context->config('zebra_auth_index_mode') || 'dom';
116
117 my $dbh = C4::Context->dbh;
118 my ($biblionumbertagfield,$biblionumbertagsubfield) = &GetMarcFromKohaField("biblio.biblionumber","");
119 my ($biblioitemnumbertagfield,$biblioitemnumbertagsubfield) = &GetMarcFromKohaField("biblioitems.biblioitemnumber","");
120
121 if ( $verbose_logging ) {
122     print "Zebra configuration information\n";
123     print "================================\n";
124     print "Zebra biblio directory      = $biblioserverdir\n";
125     print "Zebra authorities directory = $authorityserverdir\n";
126     print "Koha directory              = $kohadir\n";
127     print "BIBLIONUMBER in :     $biblionumbertagfield\$$biblionumbertagsubfield\n";
128     print "BIBLIOITEMNUMBER in : $biblioitemnumbertagfield\$$biblioitemnumbertagsubfield\n";
129     print "================================\n";
130 }
131
132 if ($do_munge) {
133     munge_config();
134 }
135
136 if ($authorities) {
137     index_records('authority', $directory, $skip_export, $process_zebraqueue, $as_xml, $noxml, $nosanitize, $do_not_clear_zebraqueue, $verbose_logging, $zebraidx_log_opt, $authorityserverdir);
138 } else {
139     print "skipping authorities\n" if ( $verbose_logging );
140 }
141
142 if ($biblios) {
143     index_records('biblio', $directory, $skip_export, $process_zebraqueue, $as_xml, $noxml, $nosanitize, $do_not_clear_zebraqueue, $verbose_logging, $zebraidx_log_opt, $biblioserverdir);
144 } else {
145     print "skipping biblios\n" if ( $verbose_logging );
146 }
147
148
149 if ( $verbose_logging ) {
150     print "====================\n";
151     print "CLEANING\n";
152     print "====================\n";
153 }
154 if ($keep_export) {
155     print "NOTHING cleaned : the export $directory has been kept.\n";
156     print "You can re-run this script with the -s ";
157     if ($use_tempdir) {
158         print " and -d $directory parameters";
159     } else {
160         print "parameter";
161     }
162     print "\n";
163     print "if you just want to rebuild zebra after changing the record.abs\n";
164     print "or another zebra config file\n";
165 } else {
166     unless ($use_tempdir) {
167         # if we're using a temporary directory
168         # created by File::Temp, it will be removed
169         # automatically.
170         rmtree($directory, 0, 1);
171         print "directory $directory deleted\n";
172     }
173 }
174
175 # This checks to see if the zebra directories exist under the provided path.
176 # If they don't, then zebra is likely to spit the dummy. This returns true
177 # if the directories had to be created, false otherwise.
178 sub check_zebra_dirs {
179         my ($base) = shift() . '/';
180         my $needed_repairing = 0;
181         my @dirs = ( '', 'key', 'register', 'shadow', 'tmp' );
182         foreach my $dir (@dirs) {
183                 my $bdir = $base . $dir;
184         if (! -d $bdir) {
185                 $needed_repairing = 1;
186                 mkdir $bdir || die "Unable to create '$bdir': $!\n";
187                 print "$0: needed to create '$bdir'\n";
188         }
189     }
190     return $needed_repairing;
191 }       # ----------  end of subroutine check_zebra_dirs  ----------
192
193 sub index_records {
194     my ($record_type, $directory, $skip_export, $process_zebraqueue, $as_xml, $noxml, $nosanitize, $do_not_clear_zebraqueue, $verbose_logging, $zebraidx_log_opt, $server_dir) = @_;
195
196     my $num_records_exported = 0;
197     my $records_deleted;
198     my $need_reset = check_zebra_dirs($server_dir);
199     if ($need_reset) {
200         print "$0: found broken zebra server directories: forcing a rebuild\n";
201         $reset = 1;
202     }
203     if ($skip_export && $verbose_logging) {
204         print "====================\n";
205         print "SKIPPING $record_type export\n";
206         print "====================\n";
207     } else {
208         if ( $verbose_logging ) {
209             print "====================\n";
210             print "exporting $record_type\n";
211             print "====================\n";
212         }
213         mkdir "$directory" unless (-d $directory);
214         mkdir "$directory/$record_type" unless (-d "$directory/$record_type");
215         if ($process_zebraqueue) {
216             my $entries = select_zebraqueue_records($record_type, 'deleted');
217             mkdir "$directory/del_$record_type" unless (-d "$directory/del_$record_type");
218             $records_deleted = generate_deleted_marc_records($record_type, $entries, "$directory/del_$record_type", $as_xml);
219             mark_zebraqueue_batch_done($entries);
220             $entries = select_zebraqueue_records($record_type, 'updated');
221             mkdir "$directory/upd_$record_type" unless (-d "$directory/upd_$record_type");
222             $num_records_exported = export_marc_records_from_list($record_type, 
223                                                                   $entries, "$directory/upd_$record_type", $as_xml, $noxml, $records_deleted);
224             mark_zebraqueue_batch_done($entries);
225         } else {
226             my $sth = select_all_records($record_type);
227             $num_records_exported = export_marc_records_from_sth($record_type, $sth, "$directory/$record_type", $as_xml, $noxml, $nosanitize);
228             unless ($do_not_clear_zebraqueue) {
229                 mark_all_zebraqueue_done($record_type);
230             }
231         }
232     }
233     
234     #
235     # and reindexing everything
236     #
237     if ( $verbose_logging ) {
238         print "====================\n";
239         print "REINDEXING zebra\n";
240         print "====================\n";
241     }
242         my $record_fmt = ($as_xml) ? 'marcxml' : 'iso2709' ;
243     if ($process_zebraqueue) {
244         do_indexing($record_type, 'delete', "$directory/del_$record_type", $reset, $noshadow, $record_fmt, $zebraidx_log_opt) 
245             if %$records_deleted;
246         do_indexing($record_type, 'update', "$directory/upd_$record_type", $reset, $noshadow, $record_fmt, $zebraidx_log_opt)
247             if $num_records_exported;
248     } else {
249         do_indexing($record_type, 'update', "$directory/$record_type", $reset, $noshadow, $record_fmt, $zebraidx_log_opt)
250             if ($num_records_exported or $skip_export);
251     }
252 }
253
254
255 sub select_zebraqueue_records {
256     my ($record_type, $update_type) = @_;
257
258     my $server = ($record_type eq 'biblio') ? 'biblioserver' : 'authorityserver';
259     my $op = ($update_type eq 'deleted') ? 'recordDelete' : 'specialUpdate';
260
261     my $sth = $dbh->prepare("SELECT id, biblio_auth_number 
262                              FROM zebraqueue
263                              WHERE server = ?
264                              AND   operation = ?
265                              AND   done = 0
266                              ORDER BY id DESC");
267     $sth->execute($server, $op);
268     my $entries = $sth->fetchall_arrayref({});
269 }
270
271 sub mark_all_zebraqueue_done {
272     my ($record_type) = @_;
273
274     my $server = ($record_type eq 'biblio') ? 'biblioserver' : 'authorityserver';
275
276     my $sth = $dbh->prepare("UPDATE zebraqueue SET done = 1
277                              WHERE server = ?
278                              AND done = 0");
279     $sth->execute($server);
280 }
281
282 sub mark_zebraqueue_batch_done {
283     my ($entries) = @_;
284
285     $dbh->{AutoCommit} = 0;
286     my $sth = $dbh->prepare("UPDATE zebraqueue SET done = 1 WHERE id = ?");
287     $dbh->commit();
288     foreach my $id (map { $_->{id} } @$entries) {
289         $sth->execute($id);
290     }
291     $dbh->{AutoCommit} = 1;
292 }
293
294 sub select_all_records {
295     my $record_type = shift;
296     return ($record_type eq 'biblio') ? select_all_biblios() : select_all_authorities();
297 }
298
299 sub select_all_authorities {
300     my $strsth=qq{SELECT authid FROM auth_header};
301     $strsth.=qq{ WHERE $where } if ($where);
302     $strsth.=qq{ LIMIT $length } if ($length && !$offset);
303     $strsth.=qq{ LIMIT $offset,$length } if ($length && $offset);
304     my $sth = $dbh->prepare($strsth);
305     $sth->execute();
306     return $sth;
307 }
308
309 sub select_all_biblios {
310     my $strsth = qq{ SELECT biblionumber FROM biblioitems };
311     $strsth.=qq{ WHERE $where } if ($where);
312     $strsth.=qq{ LIMIT $length } if ($length && !$offset);
313     $strsth.=qq{ LIMIT $offset,$length } if ($offset);
314     my $sth = $dbh->prepare($strsth);
315     $sth->execute();
316     return $sth;
317 }
318
319 sub include_xml_wrapper {
320     my $as_xml = shift;
321     my $record_type = shift;
322
323     return 0 unless $as_xml;
324     return 1 if $record_type eq 'biblio' and $bib_index_mode eq 'dom';
325     return 1 if $record_type eq 'authority' and $auth_index_mode eq 'dom';
326     return 0;
327
328 }
329
330 sub export_marc_records_from_sth {
331     my ($record_type, $sth, $directory, $as_xml, $noxml, $nosanitize) = @_;
332
333     my $num_exported = 0;
334     open my $fh, '>:encoding(UTF-8) ', "$directory/exported_records" or die $!;
335     if (include_xml_wrapper($as_xml, $record_type)) {
336         # include XML declaration and root element
337         print {$fh} '<?xml version="1.0" encoding="UTF-8"?><collection>';
338     }
339     my $i = 0;
340     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",'');
341     while (my ($record_number) = $sth->fetchrow_array) {
342         print "." if ( $verbose_logging );
343         print "\r$i" unless ($i++ %100 or !$verbose_logging);
344         if ( $nosanitize ) {
345             my $marcxml = $record_type eq 'biblio'
346                           ? GetXmlBiblio( $record_number )
347                           : GetAuthorityXML( $record_number );
348             if ($record_type eq 'biblio'){
349                 my @items = GetItemsInfo($record_number);
350                 if (@items){
351                     my $record = MARC::Record->new;
352                     $record->encoding('UTF-8');
353                     my @itemsrecord;
354                     foreach my $item (@items){
355                         my $record = Item2Marc($item, $record_number);                        
356                         push @itemsrecord, $record->field($itemtag);
357                     }
358                     $record->insert_fields_ordered(@itemsrecord);
359                     my $itemsxml = $record->as_xml_record();
360                     $marcxml =
361                         substr($marcxml, 0, length($marcxml)-10) .
362                         substr($itemsxml, index($itemsxml, "</leader>\n", 0) + 10);
363                 }
364             }
365             if ( $marcxml ) {
366                 print {$fh} $marcxml if $marcxml;
367                 $num_exported++;
368             }
369             next;
370         }
371         my ($marc) = get_corrected_marc_record($record_type, $record_number, $noxml);
372         if (defined $marc) {
373             eval {
374                 my $rec;
375                 if ($as_xml) {
376                     $rec = $marc->as_xml_record(C4::Context->preference('marcflavour'));
377                     $rec =~ s!<\?xml version="1.0" encoding="UTF-8"\?>\n!!;
378                 } else {
379                     $rec = $marc->as_usmarc();
380                 }
381                 print {$fh} $rec;
382                 $num_exported++;
383             };
384             if ($@) {
385               warn "Error exporting record $record_number ($record_type) ".($noxml ? "not XML" : "XML");
386             }
387         }
388     }
389     print "\nRecords exported: $num_exported\n" if ( $verbose_logging );
390     print {$fh} '</collection>' if (include_xml_wrapper($as_xml, $record_type));
391     close $fh;
392     return $num_exported;
393 }
394
395 sub export_marc_records_from_list {
396     my ($record_type, $entries, $directory, $as_xml, $noxml, $records_deleted) = @_;
397
398     my $num_exported = 0;
399     open my $fh, '>:encoding(UTF-8)', "$directory/exported_records" or die $!;
400     if (include_xml_wrapper($as_xml, $record_type)) {
401         # include XML declaration and root element
402         print {$fh} '<?xml version="1.0" encoding="UTF-8"?><collection>';
403     }
404     my $i = 0;
405
406     # Skip any deleted records. We check for this anyway, but this reduces error spam
407     my %found = %$records_deleted;
408     foreach my $record_number ( map { $_->{biblio_auth_number} }
409                                 grep { !$found{ $_->{biblio_auth_number} }++ }
410                                 @$entries ) {
411         print "." if ( $verbose_logging );
412         print "\r$i" unless ($i++ %100 or !$verbose_logging);
413         my ($marc) = get_corrected_marc_record($record_type, $record_number, $noxml);
414         if (defined $marc) {
415             eval {
416                 my $rec;
417                 if ($as_xml) {
418                     $rec = $marc->as_xml_record(C4::Context->preference('marcflavour'));
419                     $rec =~ s!<\?xml version="1.0" encoding="UTF-8"\?>\n!!;
420                 } else {
421                     $rec = $marc->as_usmarc();
422                 }
423                 print {$fh} $rec;
424                 $num_exported++;
425             };
426             if ($@) {
427               warn "Error exporting record $record_number ($record_type) ".($noxml ? "not XML" : "XML");
428             }
429             $num_exported++;
430         }
431     }
432     print "\nRecords exported: $num_exported\n" if ( $verbose_logging );
433     print {$fh} '</collection>' if (include_xml_wrapper($as_xml, $record_type));
434     close $fh;
435     return $num_exported;
436 }
437
438 sub generate_deleted_marc_records {
439     my ($record_type, $entries, $directory, $as_xml) = @_;
440
441     my $records_deleted = {};
442     open my $fh, '>:encoding(UTF-8)', "$directory/exported_records" or die $!;
443     if (include_xml_wrapper($as_xml, $record_type)) {
444         # include XML declaration and root element
445         print {$fh} '<?xml version="1.0" encoding="UTF-8"?><collection>';
446     }
447     my $i = 0;
448     foreach my $record_number (map { $_->{biblio_auth_number} } @$entries ) {
449         print "\r$i" unless ($i++ %100 or !$verbose_logging);
450         print "." if ( $verbose_logging );
451
452         my $marc = MARC::Record->new();
453         if ($record_type eq 'biblio') {
454             fix_biblio_ids($marc, $record_number, $record_number);
455         } else {
456             fix_authority_id($marc, $record_number);
457         }
458         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
459             fix_unimarc_100($marc);
460         }
461
462         my $rec;
463         if ($as_xml) {
464             $rec = $marc->as_xml_record(C4::Context->preference('marcflavour'));
465             $rec =~ s!<\?xml version="1.0" encoding="UTF-8"\?>\n!!;
466         } else {
467             $rec = $marc->as_usmarc();
468         }
469         print {$fh} $rec;
470
471         $records_deleted->{$record_number} = 1;
472     }
473     print "\nRecords exported: $i\n" if ( $verbose_logging );
474     print {$fh} '</collection>' if (include_xml_wrapper($as_xml, $record_type));
475     close $fh;
476     return $records_deleted;
477     
478
479 }
480
481 sub get_corrected_marc_record {
482     my ($record_type, $record_number, $noxml) = @_;
483
484     my $marc = get_raw_marc_record($record_type, $record_number, $noxml); 
485
486     if (defined $marc) {
487         fix_leader($marc);
488         if ($record_type eq 'authority') {
489             fix_authority_id($marc, $record_number);
490         }
491         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
492             fix_unimarc_100($marc);
493         }
494     }
495
496     return $marc;
497 }
498
499 sub get_raw_marc_record {
500     my ($record_type, $record_number, $noxml) = @_;
501   
502     my $marc; 
503     if ($record_type eq 'biblio') {
504         if ($noxml) {
505             my $fetch_sth = $dbh->prepare_cached("SELECT marc FROM biblioitems WHERE biblionumber = ?");
506             $fetch_sth->execute($record_number);
507             if (my ($blob) = $fetch_sth->fetchrow_array) {
508                 $marc = MARC::Record->new_from_usmarc($blob);
509                 unless ($marc) {
510                     warn "error creating MARC::Record from $blob";
511                 }
512             }
513             # failure to find a bib is not a problem -
514             # a delete could have been done before
515             # trying to process a record update
516
517             $fetch_sth->finish();
518             return unless $marc;
519         } else {
520             eval { $marc = GetMarcBiblio($record_number, 1); };
521             if ($@ || !$marc) {
522                 # here we do warn since catching an exception
523                 # means that the bib was found but failed
524                 # to be parsed
525                 warn "error retrieving biblio $record_number";
526                 return;
527             }
528         }
529     } else {
530         eval { $marc = GetAuthority($record_number); };
531         if ($@) {
532             warn "error retrieving authority $record_number";
533             return;
534         }
535     }
536     return $marc;
537 }
538
539 sub fix_leader {
540     # FIXME - this routine is suspect
541     # It blanks the Leader/00-05 and Leader/12-16 to
542     # force them to be recalculated correct when
543     # the $marc->as_usmarc() or $marc->as_xml() is called.
544     # But why is this necessary?  It would be a serious bug
545     # in MARC::Record (definitely) and MARC::File::XML (arguably) 
546     # if they are emitting incorrect leader values.
547     my $marc = shift;
548
549     my $leader = $marc->leader;
550     substr($leader,  0, 5) = '     ';
551     substr($leader, 10, 7) = '22     ';
552     $marc->leader(substr($leader, 0, 24));
553 }
554
555 sub fix_biblio_ids {
556     # FIXME - it is essential to ensure that the biblionumber is present,
557     #         otherwise, Zebra will choke on the record.  However, this
558     #         logic belongs in the relevant C4::Biblio APIs.
559     my $marc = shift;
560     my $biblionumber = shift;
561     my $biblioitemnumber;
562     if (@_) {
563         $biblioitemnumber = shift;
564     } else {    
565         my $sth = $dbh->prepare(
566             "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
567         $sth->execute($biblionumber);
568         ($biblioitemnumber) = $sth->fetchrow_array;
569         $sth->finish;
570         unless ($biblioitemnumber) {
571             warn "failed to get biblioitemnumber for biblio $biblionumber";
572             return 0;
573         }
574     }
575
576     # FIXME - this is cheating on two levels
577     # 1. C4::Biblio::_koha_marc_update_bib_ids is meant to be an internal function
578     # 2. Making sure that the biblionumber and biblioitemnumber are correct and
579     #    present in the MARC::Record object ought to be part of GetMarcBiblio.
580     #
581     # On the other hand, this better for now than what rebuild_zebra.pl used to
582     # do, which was duplicate the code for inserting the biblionumber 
583     # and biblioitemnumber
584     C4::Biblio::_koha_marc_update_bib_ids($marc, '', $biblionumber, $biblioitemnumber);
585
586     return 1;
587 }
588
589 sub fix_authority_id {
590     # FIXME - as with fix_biblio_ids, the authid must be present
591     #         for Zebra's sake.  However, this really belongs
592     #         in C4::AuthoritiesMarc.
593     my ($marc, $authid) = @_;
594     unless ($marc->field('001') and $marc->field('001')->data() eq $authid){
595         $marc->delete_field($marc->field('001'));
596         $marc->insert_fields_ordered(MARC::Field->new('001',$authid));
597     }
598 }
599
600 sub fix_unimarc_100 {
601     # FIXME - again, if this is necessary, it belongs in C4::AuthoritiesMarc.
602     my $marc = shift;
603
604     my $string;
605     if ( length($marc->subfield( 100, "a" )) == 36 ) {
606         $string = $marc->subfield( 100, "a" );
607         my $f100 = $marc->field(100);
608         $marc->delete_field($f100);
609     }
610     else {
611         $string = POSIX::strftime( "%Y%m%d", localtime );
612         $string =~ s/\-//g;
613         $string = sprintf( "%-*s", 35, $string );
614     }
615     substr( $string, 22, 6, "frey50" );
616     unless ( length($marc->subfield( 100, "a" )) == 36 ) {
617         $marc->delete_field($marc->field(100));
618         $marc->insert_grouped_field(MARC::Field->new( 100, "", "", "a" => $string ));
619     }
620 }
621
622 sub do_indexing {
623     my ($record_type, $op, $record_dir, $reset_index, $noshadow, $record_format, $zebraidx_log_opt) = @_;
624
625     my $zebra_server  = ($record_type eq 'biblio') ? 'biblioserver' : 'authorityserver';
626     my $zebra_db_name = ($record_type eq 'biblio') ? 'biblios' : 'authorities';
627     my $zebra_config  = C4::Context->zebraconfig($zebra_server)->{'config'};
628     my $zebra_db_dir  = C4::Context->zebraconfig($zebra_server)->{'directory'};
629
630     system("zebraidx -c $zebra_config $zebraidx_log_opt -g $record_format -d $zebra_db_name init") if $reset_index;
631     system("zebraidx -c $zebra_config $zebraidx_log_opt $noshadow -g $record_format -d $zebra_db_name $op $record_dir");
632     system("zebraidx -c $zebra_config $zebraidx_log_opt -g $record_format -d $zebra_db_name commit") unless $noshadow;
633
634 }
635
636 sub print_usage {
637     print <<_USAGE_;
638 $0: reindex MARC bibs and/or authorities in Zebra.
639
640 Use this batch job to reindex all biblio or authority
641 records in your Koha database.  This job is useful
642 only if you are using Zebra; if you are using the 'NoZebra'
643 mode, this job should not be used.
644
645 Parameters:
646     -b                      index bibliographic records
647
648     -a                      index authority records
649
650     -z                      select only updated and deleted
651                             records marked in the zebraqueue
652                             table.  Cannot be used with -r
653                             or -s.
654
655     -r                      clear Zebra index before
656                             adding records to index
657
658     -d                      Temporary directory for indexing.
659                             If not specified, one is automatically
660                             created.  The export directory
661                             is automatically deleted unless
662                             you supply the -k switch.
663
664     -k                      Do not delete export directory.
665
666     -s                      Skip export.  Used if you have
667                             already exported the records 
668                             in a previous run.
669
670     -noxml                  index from ISO MARC blob
671                             instead of MARC XML.  This
672                             option is recommended only
673                             for advanced user.
674
675     -x                      export and index as xml instead of is02709 (biblios only).
676                             use this if you might have records > 99,999 chars,
677                                                         
678     -nosanitize             export biblio/authority records directly from DB marcxml
679                             field without sanitizing records. It speed up
680                             dump process but could fail if DB contains badly
681                             encoded records. Works only with -x,
682
683     -w                      skip shadow indexing for this batch
684
685     -y                      do NOT clear zebraqueue after indexing; normally,
686                             after doing batch indexing, zebraqueue should be
687                             marked done for the affected record type(s) so that
688                             a running zebraqueue_daemon doesn't try to reindex
689                             the same records - specify -y to override this.  
690                             Cannot be used with -z.
691
692     -v                      increase the amount of logging.  Normally only 
693                             warnings and errors from the indexing are shown.
694                             Use log level 2 (-v -v) to include all Zebra logs.
695
696     --length   1234         how many biblio you want to export
697     --offset 1243           offset you want to start to
698                                 example: --offset 500 --length=500 will result in a LIMIT 500,1000 (exporting 1000 records, starting by the 500th one)
699                                 note that the numbers are NOT related to biblionumber, that's the intended behaviour.
700     --where                 let you specify a WHERE query, like itemtype='BOOK'
701                             or something like that
702
703     --munge-config          Deprecated option to try
704                             to fix Zebra config files.
705     --help or -h            show this message.
706 _USAGE_
707 }
708
709 # FIXME: the following routines are deprecated and 
710 # will be removed once it is determined whether
711 # a script to fix Zebra configuration files is 
712 # actually needed.
713 sub munge_config {
714 #
715 # creating zebra-biblios.cfg depending on system
716 #
717
718 # getting zebraidx directory
719 my $zebraidxdir;
720 foreach (qw(/usr/local/bin/zebraidx
721         /opt/bin/zebraidx
722         /usr/bin/zebraidx
723         )) {
724     if ( -f $_ ) {
725         $zebraidxdir=$_;
726     }
727 }
728
729 unless ($zebraidxdir) {
730     print qq|
731     ERROR: could not find zebraidx directory
732     ERROR: Either zebra is not installed,
733     ERROR: or it's in a directory I don't checked.
734     ERROR: do a which zebraidx and edit this file to add the result you get
735 |;
736     exit;
737 }
738 $zebraidxdir =~ s/\/bin\/.*//;
739 print "Info : zebra is in $zebraidxdir \n";
740
741 # getting modules directory
742 my $modulesdir;
743 foreach (qw(/usr/local/lib/idzebra-2.0/modules/mod-grs-xml.so
744             /usr/local/lib/idzebra/modules/mod-grs-xml.so
745             /usr/lib/idzebra/modules/mod-grs-xml.so
746             /usr/lib/idzebra-2.0/modules/mod-grs-xml.so
747         )) {
748     if ( -f $_ ) {
749         $modulesdir=$_;
750     }
751 }
752
753 unless ($modulesdir) {
754     print qq|
755     ERROR: could not find mod-grs-xml.so directory
756     ERROR: Either zebra is not properly compiled (libxml2 is not setup and you don t have mod-grs-xml.so,
757     ERROR: or it's in a directory I don't checked.
758     ERROR: find where mod-grs-xml.so is and edit this file to add the result you get
759 |;
760     exit;
761 }
762 $modulesdir =~ s/\/modules\/.*//;
763 print "Info: zebra modules dir : $modulesdir\n";
764
765 # getting tab directory
766 my $tabdir;
767 foreach (qw(/usr/local/share/idzebra/tab/explain.att
768             /usr/local/share/idzebra-2.0/tab/explain.att
769             /usr/share/idzebra/tab/explain.att
770             /usr/share/idzebra-2.0/tab/explain.att
771         )) {
772     if ( -f $_ ) {
773         $tabdir=$_;
774     }
775 }
776
777 unless ($tabdir) {
778     print qq|
779     ERROR: could not find explain.att directory
780     ERROR: Either zebra is not properly compiled,
781     ERROR: or it's in a directory I don't checked.
782     ERROR: find where explain.att is and edit this file to add the result you get
783 |;
784     exit;
785 }
786 $tabdir =~ s/\/tab\/.*//;
787 print "Info: tab dir : $tabdir\n";
788
789 #
790 # AUTHORITIES creating directory structure
791 #
792 my $created_dir_or_file = 0;
793 if ($authorities) {
794     if ( $verbose_logging ) {
795         print "====================\n";
796         print "checking directories & files for authorities\n";
797         print "====================\n";
798     }
799     unless (-d "$authorityserverdir") {
800         system("mkdir -p $authorityserverdir");
801         print "Info: created $authorityserverdir\n";
802         $created_dir_or_file++;
803     }
804     unless (-d "$authorityserverdir/lock") {
805         mkdir "$authorityserverdir/lock";
806         print "Info: created $authorityserverdir/lock\n";
807         $created_dir_or_file++;
808     }
809     unless (-d "$authorityserverdir/register") {
810         mkdir "$authorityserverdir/register";
811         print "Info: created $authorityserverdir/register\n";
812         $created_dir_or_file++;
813     }
814     unless (-d "$authorityserverdir/shadow") {
815         mkdir "$authorityserverdir/shadow";
816         print "Info: created $authorityserverdir/shadow\n";
817         $created_dir_or_file++;
818     }
819     unless (-d "$authorityserverdir/tab") {
820         mkdir "$authorityserverdir/tab";
821         print "Info: created $authorityserverdir/tab\n";
822         $created_dir_or_file++;
823     }
824     unless (-d "$authorityserverdir/key") {
825         mkdir "$authorityserverdir/key";
826         print "Info: created $authorityserverdir/key\n";
827         $created_dir_or_file++;
828     }
829     
830     unless (-d "$authorityserverdir/etc") {
831         mkdir "$authorityserverdir/etc";
832         print "Info: created $authorityserverdir/etc\n";
833         $created_dir_or_file++;
834     }
835     
836     #
837     # AUTHORITIES : copying mandatory files
838     #
839     # the record model, depending on marc flavour
840     unless (-f "$authorityserverdir/tab/record.abs") {
841         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
842             system("cp -f $kohadir/etc/zebradb/marc_defs/unimarc/authorities/record.abs $authorityserverdir/tab/record.abs");
843             print "Info: copied record.abs for UNIMARC\n";
844         } else {
845             system("cp -f $kohadir/etc/zebradb/marc_defs/marc21/authorities/record.abs $authorityserverdir/tab/record.abs");
846             print "Info: copied record.abs for USMARC\n";
847         }
848         $created_dir_or_file++;
849     }
850     unless (-f "$authorityserverdir/tab/sort-string-utf.chr") {
851         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $authorityserverdir/tab/sort-string-utf.chr");
852         print "Info: copied sort-string-utf.chr\n";
853         $created_dir_or_file++;
854     }
855     unless (-f "$authorityserverdir/tab/word-phrase-utf.chr") {
856         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $authorityserverdir/tab/word-phrase-utf.chr");
857         print "Info: copied word-phase-utf.chr\n";
858         $created_dir_or_file++;
859     }
860     unless (-f "$authorityserverdir/tab/auth1.att") {
861         system("cp -f $kohadir/etc/zebradb/authorities/etc/bib1.att $authorityserverdir/tab/auth1.att");
862         print "Info: copied auth1.att\n";
863         $created_dir_or_file++;
864     }
865     unless (-f "$authorityserverdir/tab/default.idx") {
866         system("cp -f $kohadir/etc/zebradb/etc/default.idx $authorityserverdir/tab/default.idx");
867         print "Info: copied default.idx\n";
868         $created_dir_or_file++;
869     }
870     
871     unless (-f "$authorityserverdir/etc/ccl.properties") {
872 #         system("cp -f $kohadir/etc/zebradb/ccl.properties ".C4::Context->zebraconfig('authorityserver')->{ccl2rpn});
873         system("cp -f $kohadir/etc/zebradb/ccl.properties $authorityserverdir/etc/ccl.properties");
874         print "Info: copied ccl.properties\n";
875         $created_dir_or_file++;
876     }
877     unless (-f "$authorityserverdir/etc/pqf.properties") {
878 #         system("cp -f $kohadir/etc/zebradb/pqf.properties ".C4::Context->zebraconfig('authorityserver')->{ccl2rpn});
879         system("cp -f $kohadir/etc/zebradb/pqf.properties $authorityserverdir/etc/pqf.properties");
880         print "Info: copied pqf.properties\n";
881         $created_dir_or_file++;
882     }
883     
884     #
885     # AUTHORITIES : copying mandatory files
886     #
887     unless (-f C4::Context->zebraconfig('authorityserver')->{config}) {
888     open my $zd, '>:encoding(UTF-8)' ,C4::Context->zebraconfig('authorityserver')->{config};
889     print {$zd} "
890 # generated by KOHA/misc/migration_tools/rebuild_zebra.pl 
891 profilePath:\${srcdir:-.}:$authorityserverdir/tab/:$tabdir/tab/:\${srcdir:-.}/tab/
892
893 encoding: UTF-8
894 # Files that describe the attribute sets supported.
895 attset: auth1.att
896 attset: explain.att
897 attset: gils.att
898
899 modulePath:$modulesdir/modules/
900 # Specify record type
901 iso2709.recordType:grs.marcxml.record
902 recordType:grs.xml
903 recordId: (auth1,Local-Number)
904 storeKeys:1
905 storeData:1
906
907
908 # Lock File Area
909 lockDir: $authorityserverdir/lock
910 perm.anonymous:r
911 perm.kohaadmin:rw
912 register: $authorityserverdir/register:4G
913 shadow: $authorityserverdir/shadow:4G
914
915 # Temp File area for result sets
916 setTmpDir: $authorityserverdir/tmp
917
918 # Temp File area for index program
919 keyTmpDir: $authorityserverdir/key
920
921 # Approx. Memory usage during indexing
922 memMax: 40M
923 rank:rank-1
924     ";
925         print "Info: creating zebra-authorities.cfg\n";
926         $created_dir_or_file++;
927     }
928     
929     if ($created_dir_or_file) {
930         print "Info: created : $created_dir_or_file directories & files\n";
931     } else {
932         print "Info: file & directories OK\n";
933     }
934     
935 }
936 if ($biblios) {
937     if ( $verbose_logging ) {
938         print "====================\n";
939         print "checking directories & files for biblios\n";
940         print "====================\n";
941     }
942
943     #
944     # BIBLIOS : creating directory structure
945     #
946     unless (-d "$biblioserverdir") {
947         system("mkdir -p $biblioserverdir");
948         print "Info: created $biblioserverdir\n";
949         $created_dir_or_file++;
950     }
951     unless (-d "$biblioserverdir/lock") {
952         mkdir "$biblioserverdir/lock";
953         print "Info: created $biblioserverdir/lock\n";
954         $created_dir_or_file++;
955     }
956     unless (-d "$biblioserverdir/register") {
957         mkdir "$biblioserverdir/register";
958         print "Info: created $biblioserverdir/register\n";
959         $created_dir_or_file++;
960     }
961     unless (-d "$biblioserverdir/shadow") {
962         mkdir "$biblioserverdir/shadow";
963         print "Info: created $biblioserverdir/shadow\n";
964         $created_dir_or_file++;
965     }
966     unless (-d "$biblioserverdir/tab") {
967         mkdir "$biblioserverdir/tab";
968         print "Info: created $biblioserverdir/tab\n";
969         $created_dir_or_file++;
970     }
971     unless (-d "$biblioserverdir/key") {
972         mkdir "$biblioserverdir/key";
973         print "Info: created $biblioserverdir/key\n";
974         $created_dir_or_file++;
975     }
976     unless (-d "$biblioserverdir/etc") {
977         mkdir "$biblioserverdir/etc";
978         print "Info: created $biblioserverdir/etc\n";
979         $created_dir_or_file++;
980     }
981     
982     #
983     # BIBLIOS : copying mandatory files
984     #
985     # the record model, depending on marc flavour
986     unless (-f "$biblioserverdir/tab/record.abs") {
987         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
988             system("cp -f $kohadir/etc/zebradb/marc_defs/unimarc/biblios/record.abs $biblioserverdir/tab/record.abs");
989             print "Info: copied record.abs for UNIMARC\n";
990         } else {
991             system("cp -f $kohadir/etc/zebradb/marc_defs/marc21/biblios/record.abs $biblioserverdir/tab/record.abs");
992             print "Info: copied record.abs for USMARC\n";
993         }
994         $created_dir_or_file++;
995     }
996     unless (-f "$biblioserverdir/tab/sort-string-utf.chr") {
997         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $biblioserverdir/tab/sort-string-utf.chr");
998         print "Info: copied sort-string-utf.chr\n";
999         $created_dir_or_file++;
1000     }
1001     unless (-f "$biblioserverdir/tab/word-phrase-utf.chr") {
1002         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $biblioserverdir/tab/word-phrase-utf.chr");
1003         print "Info: copied word-phase-utf.chr\n";
1004         $created_dir_or_file++;
1005     }
1006     unless (-f "$biblioserverdir/tab/bib1.att") {
1007         system("cp -f $kohadir/etc/zebradb/biblios/etc/bib1.att $biblioserverdir/tab/bib1.att");
1008         print "Info: copied bib1.att\n";
1009         $created_dir_or_file++;
1010     }
1011     unless (-f "$biblioserverdir/tab/default.idx") {
1012         system("cp -f $kohadir/etc/zebradb/etc/default.idx $biblioserverdir/tab/default.idx");
1013         print "Info: copied default.idx\n";
1014         $created_dir_or_file++;
1015     }
1016     unless (-f "$biblioserverdir/etc/ccl.properties") {
1017 #         system("cp -f $kohadir/etc/zebradb/ccl.properties ".C4::Context->zebraconfig('biblioserver')->{ccl2rpn});
1018         system("cp -f $kohadir/etc/zebradb/ccl.properties $biblioserverdir/etc/ccl.properties");
1019         print "Info: copied ccl.properties\n";
1020         $created_dir_or_file++;
1021     }
1022     unless (-f "$biblioserverdir/etc/pqf.properties") {
1023 #         system("cp -f $kohadir/etc/zebradb/pqf.properties ".C4::Context->zebraconfig('biblioserver')->{ccl2rpn});
1024         system("cp -f $kohadir/etc/zebradb/pqf.properties $biblioserverdir/etc/pqf.properties");
1025         print "Info: copied pqf.properties\n";
1026         $created_dir_or_file++;
1027     }
1028     
1029     #
1030     # BIBLIOS : copying mandatory files
1031     #
1032     unless (-f C4::Context->zebraconfig('biblioserver')->{config}) {
1033     open my $zd, '>:encoding(UTF-8)', C4::Context->zebraconfig('biblioserver')->{config};
1034     print {$zd} "
1035 # generated by KOHA/misc/migrtion_tools/rebuild_zebra.pl 
1036 profilePath:\${srcdir:-.}:$biblioserverdir/tab/:$tabdir/tab/:\${srcdir:-.}/tab/
1037
1038 encoding: UTF-8
1039 # Files that describe the attribute sets supported.
1040 attset:bib1.att
1041 attset:explain.att
1042 attset:gils.att
1043
1044 modulePath:$modulesdir/modules/
1045 # Specify record type
1046 iso2709.recordType:grs.marcxml.record
1047 recordType:grs.xml
1048 recordId: (bib1,Local-Number)
1049 storeKeys:1
1050 storeData:1
1051
1052
1053 # Lock File Area
1054 lockDir: $biblioserverdir/lock
1055 perm.anonymous:r
1056 perm.kohaadmin:rw
1057 register: $biblioserverdir/register:4G
1058 shadow: $biblioserverdir/shadow:4G
1059
1060 # Temp File area for result sets
1061 setTmpDir: $biblioserverdir/tmp
1062
1063 # Temp File area for index program
1064 keyTmpDir: $biblioserverdir/key
1065
1066 # Approx. Memory usage during indexing
1067 memMax: 40M
1068 rank:rank-1
1069     ";
1070         print "Info: creating zebra-biblios.cfg\n";
1071         $created_dir_or_file++;
1072     }
1073     
1074     if ($created_dir_or_file) {
1075         print "Info: created : $created_dir_or_file directories & files\n";
1076     } else {
1077         print "Info: file & directories OK\n";
1078     }
1079     
1080 }
1081 }