]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/extras/import/marc2bre.pl
adding support for a --marctype flag, useful for supplying a MARCXML stream instead...
[Evergreen.git] / Open-ILS / src / extras / import / marc2bre.pl
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4
5 use lib '/openils/lib/perl5/';
6
7 use OpenSRF::System;
8 use OpenSRF::Application;
9 use OpenSRF::EX qw/:try/;
10 use OpenSRF::AppSession;
11 use OpenSRF::MultiSession;
12 use OpenSRF::Utils::SettingsClient;
13 use OpenILS::Application::AppUtils;
14 use OpenILS::Utils::Fieldmapper;
15 use Digest::MD5 qw/md5_hex/;
16 use JSON;
17 use Data::Dumper;
18 use Unicode::Normalize;
19 use Encode;
20
21 use bytes;
22
23 use FileHandle;
24 use Time::HiRes qw/time/;
25 use Getopt::Long;
26 use MARC::Batch;
27 use MARC::File::XML ( BinaryEncoding => 'utf-8' );
28 use MARC::Charset;
29
30 MARC::Charset->ignore_errors(1);
31
32 my ($id_field, $recid, $user, $config, $marctype, $keyfile, $dontuse_file, $enc, @files, @trash_fields) =
33         ('', 1, 1, '/openils/conf/bootstrap.conf', 'USMARC');
34
35 GetOptions(
36         'marctype=i'    => \$marctype,
37         'startid=i'     => \$recid,
38         'idfield=s'     => \$id_field,
39         'user=s'        => \$user,
40         'encoding=s'    => \$enc,
41         'keyfile=s'     => \$keyfile,
42         'config=s'      => \$config,
43         'file=s'        => \@files,
44         'trash=s'       => \@trash_fields,
45         'dontuse=s'     => \$dontuse_file
46 );
47
48 if ($enc) {
49         MARC::Charset->ignore_errors(1);
50         MARC::Charset->assume_encoding($enc);
51 }
52
53 @files = @ARGV if (!@files);
54
55 my @ses;
56 my @req;
57 my %processing_cache;
58
59 my $startid = $recid;
60
61 my %source_map = (      
62         o  => 'OCLC',
63         i  => 'ISxN',    
64         l  => 'LCCN',
65         s  => 'System',  
66         g  => 'Gutenberg',  
67 );                              
68
69
70 OpenSRF::System->bootstrap_client( config_file => $config );
71 Fieldmapper->import(IDL => OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
72
73 my %keymap;
74 if ($keyfile) {
75         open F, $keyfile or die "Couldn't open key file $keyfile";
76         while (<F>) {
77                 if ( /^(\d+)\|(\S+)/o ) {
78                         $keymap{$1} = $2;
79                 }
80         }
81         close(F);
82 }
83
84 my %dontuse_id;
85 if ($dontuse_file) {
86         open F, $dontuse_file or die "Couldn't open used-id file $dontuse_file";
87         while (<F>) {
88                 chomp;
89                 s/^\s*//;
90                 s/\s*$//;
91                 $dontuse_id{$_} = 1;
92         }
93         close(F);
94 }
95
96 select STDERR; $| = 1;
97 select STDOUT; $| = 1;
98
99 my $batch = new MARC::Batch ( $marctype, @files );
100 $batch->strict_off();
101 $batch->warnings_off();
102
103 my %used_ids;
104 my $starttime = time;
105 my $rec;
106 my $count = 0;
107 while ( try { $rec = $batch->next } otherwise { $rec = -1 } ) {
108         next if ($rec == -1);
109         my $id;
110
111         $recid++;
112         while ($used_ids{$recid}) {
113                 $recid++;
114         }
115         $used_ids{$recid} = 1;
116
117         if ($id_field) {
118                 my $field = $rec->field($id_field);
119                 if ($field) {
120                         if ($field->is_control_field) {
121                                 $id = $field->data;
122                         } else {
123                                 $id = $field->subfield('a');
124                         }
125
126                         $id =~ s/\D+//gso;
127                 }
128         }
129
130         if (!$id) {
131                 $id = $recid;
132         }
133
134         if ($keyfile) {
135                 if (my $tcn = $keymap{$id}) {
136                         $rec->delete_field( $_ ) for ($rec->field($id_field));
137                         $rec->append_fields( MARC::Field->new( $id_field, '', '', 'a', $tcn ) );
138                 } else {
139                         $count++;
140                         next;
141                 }
142         }
143
144         $rec = preprocess($rec);
145         $rec->delete_field( $_ ) for ($rec->field($id_field));
146
147         if (!$rec) {
148                 next;
149         }
150
151         my $tcn_value = $rec->subfield('901' => 'a') || "SYS$id";
152         my $tcn_source = $rec->subfield('901' => 'b') || 'System';
153
154         (my $xml = $rec->as_xml_record()) =~ s/\n//sog;
155         $xml =~ s/^<\?xml.+\?\s*>//go;
156         $xml =~ s/>\s+</></go;
157         $xml =~ s/\p{Cc}//go;
158         $xml = entityize($xml);
159
160         my $bib = new Fieldmapper::biblio::record_entry;
161         $bib->id($id);
162         $bib->active('t');
163         $bib->deleted('f');
164         $bib->marc($xml);
165         $bib->creator($user);
166         $bib->create_date('now');
167         $bib->editor($user);
168         $bib->edit_date('now');
169         $bib->tcn_source($tcn_source);
170         $bib->tcn_value($tcn_value);
171         $bib->last_xact_id('IMPORT-'.$starttime);
172
173         print JSON->perl2JSON($bib)."\n";
174         $dontuse_id{$tcn_value} = 1;
175
176         $count++;
177
178         if (!($count % 50)) {
179                 print STDERR "\r$count\t". $count / (time - $starttime);
180         }
181 }
182
183 sub preprocess {
184         my $rec = shift;
185
186         my ($id, $source, $value) = ('','','');
187
188         if (!$id) {
189                 my $f = $rec->field('001');
190                 $id = $f->data if ($f);
191         }
192
193         if (!$id || $dontuse_id{$source.$id}) {
194                 my $f = $rec->field('000');
195                 $id = $f->data if ($f);
196                 $source = 'g' if ($f); # only PG seems to use this
197         }
198
199         if (!$id || $dontuse_id{$source.$id}) {
200                 my $f = $rec->field('020');
201                 $id = $f->subfield('a') if ($f);
202                 $source = 'i' if ($f);
203         }
204
205         if (!$id || $dontuse_id{$source.$id}) {
206                 my $f = $rec->field('022');
207                 $id = $f->subfield('a') if ($f);
208                 $source = 'i' if ($f);
209         }
210
211         if (!$id || $dontuse_id{$source.$id}) {
212                 my $f = $rec->field('010');
213                 $id = $f->subfield('a') if ($f);
214                 $source = 'l' if ($f);
215         }
216
217 #        if (!$id) {
218 #                my $f = $rec->field($id_field);
219 #                $id = $f->subfield('a') if ($f);
220 #        }
221
222         $rec->delete_field($_) for ($rec->field($id_field, @trash_fields));
223
224         if ($id) {
225                 $id =~ s/\s*$//o;
226                 $id =~ s/^\s*//o;
227                 $id =~ s/^(\S+).*$/$1/o;
228
229                 $id = $source.$id if ($source);
230
231                 ($source, $value) = $id =~ /^(.)(.+)$/o;
232                 if ($id =~ /^o(\d+)$/o) {
233                         $id = "ocm$1";
234                         $source = 'o';
235                 }
236         }
237
238         if ($id && $dontuse_id{$id}) {
239                 warn "\n!!! ID $id is already in use\n";
240                 $id = '';
241         }
242
243         if (!$id) {
244                 $source = 's';
245                 $id = 's'.$recid;
246         }
247
248         my $tcn = MARC::Field->new(
249                 '901' => ('', ''),
250                 a => $id,
251                 b => do { $source_map{$source} || 'System' },
252         );
253
254         $rec->append_fields($tcn);
255
256         return $rec;
257 }
258
259 sub entityize {
260         my $stuff = shift;
261         my $form = shift;
262
263         if ($form and $form eq 'D') {
264                 $stuff = NFD($stuff);
265         } else {
266                 $stuff = NFC($stuff);
267         }
268
269         $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
270         return $stuff;
271 }
272