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