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