1 package OpenILS::Application::SuperCat;
6 # All OpenSRF applications must be based on OpenSRF::Application or
7 # a subclass thereof. Makes sense, eh?
8 use OpenSRF::Application;
9 use base qw/OpenSRF::Application/;
11 # This is the client class, used for connecting to open-ils.storage
12 use OpenSRF::AppSession;
14 # This is an extention of Error.pm that supplies some error types to throw
15 use OpenSRF::EX qw(:try);
17 # This is a helper class for querying the OpenSRF Settings application ...
18 use OpenSRF::Utils::SettingsClient;
20 # ... and here we have the built in logging helper ...
21 use OpenSRF::Utils::Logger qw($logger);
23 # ... and this is our OpenILS object (en|de)coder and psuedo-ORM package.
24 use OpenILS::Utils::Fieldmapper;
27 # We'll be working with XML, so...
30 use Unicode::Normalize;
42 # we need an XML parser
43 $_parser = new XML::LibXML;
46 $_xslt = new XML::LibXSLT;
48 # parse the MODS xslt ...
49 my $mods_xslt = $_parser->parse_file(
50 OpenSRF::Utils::SettingsClient
52 ->config_value( dirs => 'xsl' ).
53 "/MARC21slim2MODS.xsl"
55 # and stash a transformer
56 $xslt{mods} = $_xslt->parse_stylesheet( $mods_xslt );
59 # parse the RDFDC xslt ...
60 my $rdfdc_xslt = $_parser->parse_file(
61 OpenSRF::Utils::SettingsClient
63 ->config_value( dirs => 'xsl' ).
64 "/MARC21slim2RDFDC.xsl"
66 # and stash a transformer
67 $xslt{rdfdc} = $_xslt->parse_stylesheet( $rdfdc_xslt );
70 # parse the SRWDC xslt ...
71 my $srwdc_xslt = $_parser->parse_file(
72 OpenSRF::Utils::SettingsClient
74 ->config_value( dirs => 'xsl' ).
75 "/MARC21slim2SRWDC.xsl"
77 # and stash a transformer
78 $xslt{srwdc} = $_xslt->parse_stylesheet( $srwdc_xslt );
81 # parse the OAIDC xslt ...
82 my $oaidc_xslt = $_parser->parse_file(
83 OpenSRF::Utils::SettingsClient
85 ->config_value( dirs => 'xsl' ).
86 "/MARC21slim2OAIDC.xsl"
88 # and stash a transformer
89 $xslt{oaidc} = $_xslt->parse_stylesheet( $oaidc_xslt );
92 # parse the RSS xslt ...
93 my $rss_xslt = $_parser->parse_file(
94 OpenSRF::Utils::SettingsClient
96 ->config_value( dirs => 'xsl' ).
97 "/MARC21slim2RSS2.xsl"
99 # and stash a transformer
100 $xslt{rss2} = $_xslt->parse_stylesheet( $rss_xslt );
103 # and finally, a storage server session
104 $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
106 register_record_transforms();
111 sub register_record_transforms {
112 for my $type ( keys %xslt ) {
113 __PACKAGE__->register_method(
114 method => 'retrieve_record_transform',
115 api_name => "open-ils.supercat.record.$type.retrieve",
120 Returns the \U$type\E representation of the requested bibliographic record
125 desc => 'An OpenILS biblio::record_entry id',
129 { desc => "The bib record in \U$type\E",
138 my $stuff = NFC(shift());
139 $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
144 sub retrieve_record_marcxml {
152 ->request( 'open-ils.storage.direct.biblio.record_entry.retrieve' => $rid )
158 __PACKAGE__->register_method(
159 method => 'retrieve_record_marcxml',
160 api_name => 'open-ils.supercat.record.marcxml.retrieve',
165 Returns the MARCXML representation of the requested bibliographic record
170 desc => 'An OpenILS biblio::record_entry id',
174 { desc => 'The bib record in MARCXML',
179 sub retrieve_record_transform {
184 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
186 my $marc = $_storage->request(
187 'open-ils.storage.direct.biblio.record_entry.retrieve',
191 return entityize($xslt{$transform}->transform( $_parser->parse_string( $marc ) )->toString);
195 sub retrieve_metarecord_mods {
203 # Get the metarecord in question
206 'open-ils.storage.direct.metabib.metarecord.retrieve' => $rid
209 # Now get the map of all bib records for the metarecord
212 'open-ils.storage.direct.metabib.metarecord_source_map.search.metarecord.atomic',
216 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
218 # and retrieve the lead (master) record as MODS
220 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
221 ->run($mr->master_record);
222 my $master_mods = $_parser->parse_string($master)->documentElement;
223 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
225 # ... and a MODS clone to populate, with guts removed.
226 my $mods = $_parser->parse_string($master)->documentElement;
227 $mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
228 ($mods) = $mods->findnodes('//mods:mods');
229 $mods->removeChildNodes;
231 # Add the metarecord ID as a (locally defined) info URI
232 my $recordInfo = $mods
234 ->createElement("mods:recordInfo");
236 my $recordIdentifier = $mods
238 ->createElement("mods:recordIdentifier");
240 $recordIdentifier->setAttribute( source => 'oils:/metabib-metarecord/' );
243 $recordIdentifier->appendTextNode( $id );
245 $recordInfo->appendChild($recordIdentifier);
246 $mods->appendChild($recordInfo);
248 # Grab the title, author and ISBN for the master record and populate the metarecord
249 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
252 $title->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
253 $title = $mods->ownerDocument->importNode($title);
254 $mods->appendChild($title);
257 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
259 $author->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
260 $author = $mods->ownerDocument->importNode($author);
261 $mods->appendChild($author);
264 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
266 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
267 $isbn = $mods->ownerDocument->importNode($isbn);
268 $mods->appendChild($isbn);
271 # ... and loop over the constituent records
272 for my $map ( @$recs ) {
276 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
279 my $part_mods = $_parser->parse_string($rec);
280 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
281 ($part_mods) = $part_mods->findnodes('//mods:mods');
283 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
284 $node->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
285 $node = $mods->ownerDocument->importNode($node);
286 $mods->appendChild( $node );
289 my $relatedItem = $mods
291 ->createElement("mods:relatedItem");
293 $relatedItem->setAttribute( type => 'constituent' );
295 my $identifier = $mods
297 ->createElement("mods:identifier");
299 $identifier->setAttribute( type => 'uri' );
301 my $subRecordInfo = $mods
303 ->createElement("mods:recordInfo");
305 my $subRecordIdentifier = $mods
307 ->createElement("mods:recordIdentifier");
309 $subRecordIdentifier->setAttribute( source => 'oils:/biblio-record_entry/' );
311 my $subid = $map->source;
312 $subRecordIdentifier->appendTextNode( $subid );
313 $subRecordInfo->appendChild($subRecordIdentifier);
315 $relatedItem->appendChild( $subRecordInfo );
317 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
318 $tor->setNamespace( "http://www.loc.gov/mods/", "mods", 1 ) if ($tor);
319 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
320 $relatedItem->appendChild($tor) if ($tor);
322 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
323 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
324 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
325 $relatedItem->appendChild( $part_isbn );
328 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
332 $mods->appendChild( $relatedItem );
336 $_storage->disconnect;
338 return entityize($mods->toString);
341 __PACKAGE__->register_method(
342 method => 'retrieve_metarecord_mods',
343 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
348 Returns the MODS representation of the requested metarecord
352 { name => 'metarecordId',
353 desc => 'An OpenILS metabib::metarecord id',
357 { desc => 'The metarecord in MODS',
367 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
368 unless (length($isbn) >= 10);
370 # Create a storage session, since we'll be making muliple requests.
373 # Find the record that has that ISBN.
374 my $bibrec = $_storage->request(
375 'open-ils.storage.direct.metabib.full_rec.search_where.atomic',
376 { tag => '020', subfield => 'a', value => { like => $isbn.'%'} }
379 # Go away if we don't have one.
380 return {} unless (@$bibrec);
382 # Find the metarecord for that bib record.
383 my $mr = $_storage->request(
384 'open-ils.storage.direct.metabib.metarecord_source_map.search.source.atomic',
388 # Find the other records for that metarecord.
389 my $records = $_storage->request(
390 'open-ils.storage.direct.metabib.metarecord_source_map.search.metarecord.atomic',
394 # Just to be safe. There's currently no unique constraint on sources...
395 my %unique_recs = map { ($_->source, 1) } @$records;
396 my @rec_list = sort keys %unique_recs;
398 # And now fetch the ISBNs for thos records.
399 my $recs = $_storage->request(
400 'open-ils.storage.direct.metabib.full_rec.search_where.atomic',
401 { tag => '020', subfield => 'a', record => \@rec_list }
404 # We're done with the storage server session.
405 $_storage->disconnect;
407 # Return the oISBN data structure. This will be XMLized at a higher layer.
409 { metarecord => $mr->[0]->metarecord,
410 record_list => { map { ($_->record, $_->value) } @$recs } };
413 __PACKAGE__->register_method(
415 api_name => 'open-ils.supercat.oisbn',
420 Returns the ISBN list for the metarecord of the requested isbn
425 desc => 'An ISBN. Duh.',
429 { desc => 'record to isbn map',