8 use OpenSRF::EX qw/:try/;
9 use OpenSRF::AppSession;
10 use OpenSRF::Utils::JSON;
11 use OpenSRF::Utils::SettingsClient;
12 use OpenILS::Application::AppUtils;
13 use OpenILS::Utils::Fieldmapper;
14 use OpenILS::Utils::CStoreEditor;
18 use UNIVERSAL::require;
20 use Time::HiRes qw/time/;
24 my @formats = qw/USMARC UNIMARC XML BRE ARE/;
26 my ($config,$format,$encoding,$location,$dollarsign,$idl,$help,$holdings,$timeout,$export_mfhd,$type,$all_records) = ('/openils/conf/opensrf_core.xml','USMARC','MARC8','','$',0,undef,undef,0,undef,'biblio',undef);
30 'items' => \$holdings,
31 'mfhd' => \$export_mfhd,
32 'all' => \$all_records,
33 'location=s' => \$location,
34 'money=s' => \$dollarsign,
35 'config=s' => \$config,
36 'format=s' => \$format,
39 'encoding=s' => \$encoding,
40 'timeout=i' => \$timeout,
45 This script exports MARC authority, bibliographic, and serial holdings
46 records from an Evergreen database.
48 Input to this script can consist of a list of record IDs, with one record ID
49 per line, corresponding to the record ID in the Evergreen database table of
50 your requested record type.
52 Alternately, passing the --all option will attempt to export all records of
53 the specified type from the Evergreen database. The --all option starts at
54 record ID 1 and increments the ID by 1 until the largest ID in the database
55 is retrieved. This may not be very efficient for databases with large gaps
56 in their ID sequences.
59 --help or -h This screen.
60 --config or -c Configuration file [/openils/conf/opensrf_core.xml]
61 --format or -f Output format (USMARC, UNIMARC, XML, BRE, ARE) [USMARC]
62 --encoding or -e Output encoding (UTF-8, ISO-8859-?, MARC8) [MARC8]
63 --xml-idl or -x Location of the IDL XML
64 --timeout Timeout for exporting a single record; increase if you
65 are using --holdings and are exporting records that
66 have a lot of items attached to them.
67 --type or -t Record type (BIBLIO, AUTHORITY) [BIBLIO]
68 --all or -a Export all records; ignores input list
70 Additional options for type = 'BIBLIO':
71 --items or -i Include items (holdings) in the output
72 --money Currency symbol to use in item price field [\$]
73 --mfhd Export serial MFHD records for associated bib records
74 Not compatible with --format=BRE
75 --location or -l MARC Location Code for holdings from
76 http://www.loc.gov/marc/organizations/orgshome.html
80 To export a set of USMARC records in a file named "output_file" based on the
81 IDs contained in a file named "list_of_ids":
82 cat list_of_ids | $0 > output_file
84 To export a set of MARC21XML authority records in a file named "output.xml"
85 for all authority records in the database:
86 $0 --format XML --type AUTHORITY --all > output.xml
93 $format = uc($format);
94 $encoding = uc($encoding);
96 binmode(STDOUT, ':raw') if ($encoding ne 'UTF-8');
97 binmode(STDOUT, ':utf8') if ($encoding eq 'UTF-8');
99 if (!grep { $format eq $_ } @formats) {
100 die "Please select a supported format. ".
101 "Right now that means one of [".
102 join('|',@formats). "]\n";
105 if ($format ne 'XML') {
106 my $type = 'MARC::File::' . $format;
111 # set default timeout and/or correct silly user who
112 # supplied a negative timeout; default timeout of
113 # 300 seconds if exporting items determined empirically.
114 $timeout = $holdings ? 300 : 1;
117 OpenSRF::System->bootstrap_client( config_file => $config );
120 $idl = OpenSRF::Utils::SettingsClient->new->config_value("IDL");
123 Fieldmapper->import(IDL => $idl);
125 my $ses = OpenSRF::AppSession->create('open-ils.cstore');
126 OpenILS::Utils::CStoreEditor::init();
127 my $editor = OpenILS::Utils::CStoreEditor->new();
129 print <<HEADER if ($format eq 'XML');
130 <?xml version="1.0" encoding="$encoding"?>
131 <collection xmlns='http://www.loc.gov/MARC21/slim'>
144 my $last_time = time;
145 my %count = ('bib' => 0, 'did' => 0);
150 if ($type eq 'biblio') {
151 $top_record = $editor->search_biblio_record_entry([
153 {order_by => { 'bre' => 'id DESC' }, limit => 1}
155 } elsif ($type eq 'authority') {
156 $top_record = $editor->search_authority_record_entry([
158 {order_by => { 'are' => 'id DESC' }, limit => 1}
161 for (my $i = 0; $i++ < $top_record;) {
165 while ( my $i = <> ) {
170 print "</collection>\n" if ($format eq 'XML');
172 $speed = $count{did} / (time - $start);
173 my $time = time - $start;
176 Exports Attempted : $count{bib}
177 Exports Completed : $count{did}
178 Overall Speed : $speed
179 Total Time Elapsed: $time seconds
188 my $r = $ses->request( "open-ils.cstore.direct.$type.record_entry.retrieve", $id, $flesh );
189 my $s = $r->recv(timeout => $timeout);
191 warn "\n!!!!! Failed trying to read record $id\n";
195 warn "\n!!!!!! Failed trying to read record $id: " . $r->failed->stringify . "\n";
199 warn "\n!!!!!! Timed out trying to read record $id\n";
208 if ($format eq 'ARE' or $format eq 'BRE') {
209 print OpenSRF::Utils::JSON->perl2JSON($bib);
217 my $r = MARC::Record->new_from_xml( $bib->marc, $encoding, $format );
218 if ($type eq 'biblio') {
219 add_bib_holdings($bib, $r);
222 if ($format eq 'XML') {
223 my $xml = $r->as_xml_record;
224 $xml =~ s/^<\?.+?\?>$//mo;
226 } elsif ($format eq 'UNIMARC') {
228 } elsif ($format eq 'USMARC') {
237 import MARC::File::XML; # reset SAX parser so that one bad record doesn't kill the entire export
240 if ($export_mfhd and $type eq 'biblio') {
241 my $mfhds = $editor->search_serial_record_entry({record => $id, deleted => 'f'});
242 foreach my $mfhd (@$mfhds) {
244 my $r = MARC::Record->new_from_xml( $mfhd->marc, $encoding, $format );
246 if ($format eq 'XML') {
247 my $xml = $r->as_xml_record;
248 $xml =~ s/^<\?.+?\?>$//mo;
250 } elsif ($format eq 'UNIMARC') {
252 } elsif ($format eq 'USMARC') {
258 import MARC::File::XML; # reset SAX parser so that one bad record doesn't kill the entire export
263 stats() if (! ($count{bib} % 50 ));
270 $speed = $count{did} / (time - $start);
272 my $speed_now = ($count{did} - $count{did_last}) / (time - $count{time_last});
273 my $cn_speed = $count{cn} / (time - $start);
274 my $cp_speed = $count{cp} / (time - $start);
276 printf STDERR "\r $count{did} of $count{bib} @ \%0.4f/s ttl / \%0.4f/s rt ".
277 "($count{cn} CNs @ \%0.4f/s :: $count{cp} CPs @ \%0.4f/s)\r",
283 $count{did_last} = $count{did};
284 $count{time_last} = time;
287 sub get_bib_locations {
288 print STDERR "Retrieving Org Units ... ";
289 my $r = $ses->request( 'open-ils.cstore.direct.actor.org_unit.search', { id => { '!=' => undef } } );
291 while (my $o = $r->recv) {
292 die $r->failed->stringify if ($r->failed);
300 print STDERR "Retrieving Shelving locations ... ";
301 $r = $ses->request( 'open-ils.cstore.direct.asset.copy_location.search', { id => { '!=' => undef } } );
303 while (my $s = $r->recv) {
304 die $r->failed->stringify if ($r->failed);
307 $shelves{$s->id} = $s;
312 $flesh = { flesh => 2, flesh_fields => { bre => [ 'call_numbers' ], acn => [ 'copies' ] } };
315 sub add_bib_holdings {
319 my $cn_list = $bib->call_numbers;
320 if ($cn_list && @$cn_list) {
322 $count{cn} += @$cn_list;
324 my $cp_list = [ map { @{ $_->copies } } @$cn_list ];
325 if ($cp_list && @$cp_list) {
328 push @{$cn_map{$_->call_number}}, $_ for (@$cp_list);
330 for my $cn ( @$cn_list ) {
331 my $cn_map_list = $cn_map{$cn->id};
333 for my $cp ( @$cn_map_list ) {
340 b => $orgs{$cn->owning_lib}->shortname,
341 b => $orgs{$cp->circ_lib}->shortname,
342 c => $shelves{$cp->location}->name,
344 ($cp->circ_modifier ? ( g => $cp->circ_modifier ) : ()),
346 ($cp->price ? ( y => $dollarsign.$cp->price ) : ()),
347 ($cp->copy_number ? ( t => $cp->copy_number ) : ()),
348 ($cp->ref eq 't' ? ( x => 'reference' ) : ()),
349 ($cp->holdable eq 'f' ? ( x => 'unholdable' ) : ()),
350 ($cp->circulate eq 'f' ? ( x => 'noncirculating' ) : ()),
351 ($cp->opac_visible eq 'f' ? ( x => 'hidden' ) : ()),
355 stats() if (! ($count{cp} % 100 ));