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 OpenILS::Application;
9 use base qw/OpenILS::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;
32 use OpenSRF::Utils::JSON;
43 # we need an XML parser
44 $_parser = new XML::LibXML;
47 $_xslt = new XML::LibXSLT;
49 # parse the MODS xslt ...
50 my $mods32_xslt = $_parser->parse_file(
51 OpenSRF::Utils::SettingsClient
53 ->config_value( dirs => 'xsl' ).
54 "/MARC21slim2MODS32.xsl"
56 # and stash a transformer
57 $record_xslt{mods32}{xslt} = $_xslt->parse_stylesheet( $mods32_xslt );
58 $record_xslt{mods32}{namespace_uri} = 'http://www.loc.gov/mods/v3';
59 $record_xslt{mods32}{docs} = 'http://www.loc.gov/mods/';
60 $record_xslt{mods32}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-2.xsd';
62 # parse the MODS xslt ...
63 my $mods3_xslt = $_parser->parse_file(
64 OpenSRF::Utils::SettingsClient
66 ->config_value( dirs => 'xsl' ).
67 "/MARC21slim2MODS3.xsl"
69 # and stash a transformer
70 $record_xslt{mods3}{xslt} = $_xslt->parse_stylesheet( $mods3_xslt );
71 $record_xslt{mods3}{namespace_uri} = 'http://www.loc.gov/mods/v3';
72 $record_xslt{mods3}{docs} = 'http://www.loc.gov/mods/';
73 $record_xslt{mods3}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-1.xsd';
75 # parse the MODS xslt ...
76 my $mods_xslt = $_parser->parse_file(
77 OpenSRF::Utils::SettingsClient
79 ->config_value( dirs => 'xsl' ).
80 "/MARC21slim2MODS.xsl"
82 # and stash a transformer
83 $record_xslt{mods}{xslt} = $_xslt->parse_stylesheet( $mods_xslt );
84 $record_xslt{mods}{namespace_uri} = 'http://www.loc.gov/mods/';
85 $record_xslt{mods}{docs} = 'http://www.loc.gov/mods/';
86 $record_xslt{mods}{schema_location} = 'http://www.loc.gov/standards/mods/mods.xsd';
88 # parse the ATOM entry xslt ...
89 my $atom_xslt = $_parser->parse_file(
90 OpenSRF::Utils::SettingsClient
92 ->config_value( dirs => 'xsl' ).
93 "/MARC21slim2ATOM.xsl"
95 # and stash a transformer
96 $record_xslt{atom}{xslt} = $_xslt->parse_stylesheet( $atom_xslt );
97 $record_xslt{atom}{namespace_uri} = 'http://www.w3.org/2005/Atom';
98 $record_xslt{atom}{docs} = 'http://www.ietf.org/rfc/rfc4287.txt';
100 # parse the RDFDC xslt ...
101 my $rdf_dc_xslt = $_parser->parse_file(
102 OpenSRF::Utils::SettingsClient
104 ->config_value( dirs => 'xsl' ).
105 "/MARC21slim2RDFDC.xsl"
107 # and stash a transformer
108 $record_xslt{rdf_dc}{xslt} = $_xslt->parse_stylesheet( $rdf_dc_xslt );
109 $record_xslt{rdf_dc}{namespace_uri} = 'http://purl.org/dc/elements/1.1/';
110 $record_xslt{rdf_dc}{schema_location} = 'http://purl.org/dc/elements/1.1/';
112 # parse the SRWDC xslt ...
113 my $srw_dc_xslt = $_parser->parse_file(
114 OpenSRF::Utils::SettingsClient
116 ->config_value( dirs => 'xsl' ).
117 "/MARC21slim2SRWDC.xsl"
119 # and stash a transformer
120 $record_xslt{srw_dc}{xslt} = $_xslt->parse_stylesheet( $srw_dc_xslt );
121 $record_xslt{srw_dc}{namespace_uri} = 'info:srw/schema/1/dc-schema';
122 $record_xslt{srw_dc}{schema_location} = 'http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd';
124 # parse the OAIDC xslt ...
125 my $oai_dc_xslt = $_parser->parse_file(
126 OpenSRF::Utils::SettingsClient
128 ->config_value( dirs => 'xsl' ).
129 "/MARC21slim2OAIDC.xsl"
131 # and stash a transformer
132 $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $oai_dc_xslt );
133 $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
134 $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
136 # parse the RSS xslt ...
137 my $rss_xslt = $_parser->parse_file(
138 OpenSRF::Utils::SettingsClient
140 ->config_value( dirs => 'xsl' ).
141 "/MARC21slim2RSS2.xsl"
143 # and stash a transformer
144 $record_xslt{rss2}{xslt} = $_xslt->parse_stylesheet( $rss_xslt );
146 register_record_transforms();
151 sub register_record_transforms {
152 for my $type ( keys %record_xslt ) {
153 __PACKAGE__->register_method(
154 method => 'retrieve_record_transform',
155 api_name => "open-ils.supercat.record.$type.retrieve",
159 { desc => "Returns the \U$type\E representation ".
160 "of the requested bibliographic record",
164 desc => 'An OpenILS biblio::record_entry id',
168 { desc => "The bib record in \U$type\E",
173 __PACKAGE__->register_method(
174 method => 'retrieve_isbn_transform',
175 api_name => "open-ils.supercat.isbn.$type.retrieve",
179 { desc => "Returns the \U$type\E representation ".
180 "of the requested bibliographic record",
188 { desc => "The bib record in \U$type\E",
197 my $stuff = NFC(shift());
198 $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
207 return unless ($tree && ref($tree->$field));
209 my @things = $filter->($tree);
210 for my $v ( @{$tree->$field} ){
211 push @things, $filter->($v);
212 push @things, tree_walker($v, $field, $filter);
223 my $page_size = shift || 9;
224 my $page = shift || 0;
226 my ($before_limit,$after_limit) = (0,0);
227 my ($before_offset,$after_offset) = (0,0);
230 $before_limit = $after_limit = int($page_size / 2);
231 $after_limit += 1 if ($page_size % 2);
233 $before_offset = $after_offset = int($page_size / 2);
234 $before_offset += 1 if ($page_size % 2);
235 $before_limit = $after_limit = $page_size;
238 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
240 my $o_search = { shortname => $ou };
241 if (!$ou || $ou eq '-') {
242 $o_search = { parent_ou => undef };
245 my $orgs = $_storage->request(
246 "open-ils.cstore.direct.actor.org_unit.search",
249 flesh_fields => { aou => [qw/children/] }
253 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
255 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
260 my $before = $_storage->request(
261 "open-ils.cstore.direct.asset.call_number.search.atomic",
262 { label => { "<" => { transform => "upper", value => ["upper", $label] } },
263 owning_lib => \@ou_ids,
267 flesh_fields => { acn => [qw/record owning_lib/] },
268 order_by => { acn => "upper(label) desc, id desc, owning_lib desc" },
269 limit => $before_limit,
270 offset => abs($page) * $page_size - $before_offset,
273 push @list, reverse(@$before);
277 my $after = $_storage->request(
278 "open-ils.cstore.direct.asset.call_number.search.atomic",
279 { label => { ">=" => { transform => "upper", value => ["upper", $label] } },
280 owning_lib => \@ou_ids,
284 flesh_fields => { acn => [qw/record owning_lib/] },
285 order_by => { acn => "upper(label), id, owning_lib" },
286 limit => $after_limit,
287 offset => abs($page) * $page_size - $after_offset,
295 __PACKAGE__->register_method(
296 method => 'cn_browse',
297 api_name => 'open-ils.supercat.call_number.browse',
302 Returns the XML representation of the requested bibliographic record's holdings
307 desc => 'The target call number lable',
309 { name => 'org_unit',
310 desc => 'The org unit shortname (or "-" or undef for global) to browse',
312 { name => 'page_size',
313 desc => 'Count of call numbers to retrieve, default is 9',
316 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
320 { desc => 'Call numbers with owning_lib and record fleshed',
326 sub new_record_holdings {
332 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
334 my $tree = $_storage->request(
335 "open-ils.cstore.direct.biblio.record_entry.retrieve",
339 bre => [qw/call_numbers/],
340 acn => [qw/copies owning_lib/],
341 acp => [qw/location status circ_lib stat_cat_entries notes/],
342 asce => [qw/stat_cat/],
347 my $o_search = { shortname => uc($ou) };
348 if (!$ou || $ou eq '-') {
349 $o_search = { parent_ou => undef };
352 my $orgs = $_storage->request(
353 "open-ils.cstore.direct.actor.org_unit.search",
356 flesh_fields => { aou => [qw/children/] }
360 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
362 $logger->debug("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
364 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
368 $client->respond("<holdings:volumes xmlns:holdings='http://open-ils.org/spec/holdings/v1'>");
370 for my $cn (@{$tree->call_numbers}) {
371 next unless ( $cn->deleted eq 'f' || $cn->deleted == 0 );
374 for my $c (@{$cn->copies}) {
375 next unless grep {$c->circ_lib->id == $_} @ou_ids;
376 next unless ( $c->deleted eq 'f' || $c->deleted == 0 );
382 (my $cn_class = $cn->class_name) =~ s/::/-/gso;
383 $cn_class =~ s/Fieldmapper-//gso;
384 my $cn_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cn_class/".$cn->id, $month, $day);
386 my $cn_lib = $cn->owning_lib->shortname;
388 my $cn_label = $cn->label;
390 my $xml = "<holdings:volume id='$cn_tag' lib='$cn_lib' label='$cn_label'><holdings:copies>";
392 for my $cp (@{$cn->copies}) {
394 next unless grep { $cp->circ_lib->id == $_ } @ou_ids;
395 next unless ( $cp->deleted eq 'f' || $cp->deleted == 0 );
397 (my $cp_class = $cp->class_name) =~ s/::/-/gso;
398 $cp_class =~ s/Fieldmapper-//gso;
399 my $cp_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cp_class/".$cp->id, $month, $day);
401 my $cp_stat = escape($cp->status->name);
402 my $cp_loc = escape($cp->location->name);
403 my $cp_lib = escape($cp->circ_lib->shortname);
404 my $cp_bc = escape($cp->barcode);
406 $xml .= "<holdings:copy id='$cp_tag' barcode='$cp_bc'><holdings:status>$cp_stat</holdings:status>".
407 "<holdings:location>$cp_loc</holdings:location><holdings:circlib>$cp_lib</holdings:circlib><holdings:copy_notes>";
410 for my $note ( @{$cp->notes} ) {
411 next unless ( $note->pub eq 't' );
412 $xml .= sprintf('<holdings:copy_note date="%s" title="%s">%s</holdings:copy_note>',$note->create_date, escape($note->title), escape($note->value));
416 $xml .= "</holdings:copy_notes><holdings:statcats>";
418 if ($cp->stat_cat_entries) {
419 for my $sce ( @{$cp->stat_cat_entries} ) {
420 next unless ( $sce->stat_cat->opac_visible eq 't' );
421 $xml .= sprintf('<holdings:statcat name="%s">%s</holdings:statcat>',escape($sce->stat_cat->name) ,escape($sce->value));
425 $xml .= "</holdings:statcats></holdings:copy>";
428 $xml .= "</holdings:copies></holdings:volume>";
430 $client->respond($xml)
433 return "</holdings:volumes>";
435 __PACKAGE__->register_method(
436 method => 'new_record_holdings',
437 api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
443 Returns the XML representation of the requested bibliographic record's holdings
448 desc => 'An OpenILS biblio::record_entry id',
452 { desc => 'Stream of bib record holdings hierarchy in XML',
462 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
464 my $recs = $_storage->request(
465 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
466 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
469 return undef unless (@$recs);
471 return ($self->method_lookup( 'open-ils.supercat.record.holdings_xml.retrieve')->run( $recs->[0]->record ))[0];
473 __PACKAGE__->register_method(
474 method => 'isbn_holdings',
475 api_name => 'open-ils.supercat.isbn.holdings_xml.retrieve',
480 Returns the XML representation of the requested bibliographic record's holdings
489 { desc => 'The bib record holdings hierarchy in XML',
496 $text =~ s/&/&/gsom;
497 $text =~ s/</</gsom;
498 $text =~ s/>/>/gsom;
499 $text =~ s/"/\\"/gsom;
506 my $when = shift || '1-01-01';
510 $type = 'authority' if ($self->api_name =~ /authority/o);
512 my $axis = 'create_date';
513 $axis = 'edit_date' if ($self->api_name =~ /edit/o);
515 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
517 return $_storage->request(
518 "open-ils.cstore.direct.$type.record_entry.id_list.atomic",
519 { $axis => { ">" => $when }, id => { '>' => 0 } },
520 { order_by => { bre => "$axis desc" }, limit => $limit }
524 for my $t ( qw/biblio authority/ ) {
525 for my $a ( qw/import edit/ ) {
527 __PACKAGE__->register_method(
528 method => 'recent_changes',
529 api_name => "open-ils.supercat.$t.record.$a.recent",
533 { desc => "Returns a list of recently ${a}ed $t records",
537 desc => "Date to start looking for ${a}ed records",
538 default => '1-01-01',
542 desc => "Maximum count to retrieve",
546 { desc => "An id list of $t records",
554 sub retrieve_record_marcxml {
559 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
561 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $rid )->gather(1);
562 return entityize( $record->marc ) if ($record);
566 __PACKAGE__->register_method(
567 method => 'retrieve_record_marcxml',
568 api_name => 'open-ils.supercat.record.marcxml.retrieve',
573 Returns the MARCXML representation of the requested bibliographic record
578 desc => 'An OpenILS biblio::record_entry id',
582 { desc => 'The bib record in MARCXML',
587 sub retrieve_isbn_marcxml {
592 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
594 my $recs = $_storage->request(
595 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
596 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
599 return undef unless (@$recs);
601 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
602 return entityize( $record->marc ) if ($record);
606 __PACKAGE__->register_method(
607 method => 'retrieve_isbn_marcxml',
608 api_name => 'open-ils.supercat.isbn.marcxml.retrieve',
613 Returns the MARCXML representation of the requested ISBN
618 desc => 'An ... um ... ISBN',
622 { desc => 'The bib record in MARCXML',
627 sub retrieve_record_transform {
632 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
634 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
637 my $record = $_storage->request(
638 'open-ils.cstore.direct.biblio.record_entry.retrieve',
642 return undef unless ($record);
644 return entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
647 sub retrieve_isbn_transform {
652 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
654 my $recs = $_storage->request(
655 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
656 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
659 return undef unless (@$recs);
661 (my $transform = $self->api_name) =~ s/^.+isbn\.([^\.]+)\.retrieve$/$1/o;
663 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
665 return undef unless ($record);
667 return entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
670 sub retrieve_record_objects {
675 $ids = [$ids] unless (ref $ids);
676 $ids = [grep {$_} @$ids];
678 return [] unless (@$ids);
680 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
681 return $_storage->request('open-ils.cstore.direct.biblio.record_entry.search.atomic' => { id => [grep {$_} @$ids] })->gather(1);
683 __PACKAGE__->register_method(
684 method => 'retrieve_record_objects',
685 api_name => 'open-ils.supercat.record.object.retrieve',
690 Returns the Fieldmapper object representation of the requested bibliographic records
695 desc => 'OpenILS biblio::record_entry ids',
699 { desc => 'The bib records',
705 sub retrieve_isbn_object {
710 return undef unless ($isbn);
712 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
713 my $recs = $_storage->request(
714 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
715 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
718 return undef unless (@$recs);
720 return $_storage->request(
721 'open-ils.cstore.direct.biblio.record_entry.search.atomic',
722 { id => $recs->[0]->record }
725 __PACKAGE__->register_method(
726 method => 'retrieve_isbn_object',
727 api_name => 'open-ils.supercat.isbn.object.retrieve',
732 Returns the Fieldmapper object representation of the requested bibliographic record
741 { desc => 'The bib record',
748 sub retrieve_metarecord_mods {
753 my $_storage = OpenSRF::AppSession->connect( 'open-ils.cstore' );
755 # Get the metarecord in question
758 'open-ils.cstore.direct.metabib.metarecord.retrieve' => $rid
761 # Now get the map of all bib records for the metarecord
764 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
768 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
770 # and retrieve the lead (master) record as MODS
772 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
773 ->run($mr->master_record);
774 my $master_mods = $_parser->parse_string($master)->documentElement;
775 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
777 # ... and a MODS clone to populate, with guts removed.
778 my $mods = $_parser->parse_string($master)->documentElement;
779 $mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
780 ($mods) = $mods->findnodes('//mods:mods');
781 $mods->removeChildNodes;
783 # Add the metarecord ID as a (locally defined) info URI
784 my $recordInfo = $mods
786 ->createElement("mods:recordInfo");
788 my $recordIdentifier = $mods
790 ->createElement("mods:recordIdentifier");
792 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
797 $recordIdentifier->appendTextNode(
798 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
801 $recordInfo->appendChild($recordIdentifier);
802 $mods->appendChild($recordInfo);
804 # Grab the title, author and ISBN for the master record and populate the metarecord
805 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
808 $title->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
809 $title = $mods->ownerDocument->importNode($title);
810 $mods->appendChild($title);
813 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
815 $author->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
816 $author = $mods->ownerDocument->importNode($author);
817 $mods->appendChild($author);
820 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
822 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
823 $isbn = $mods->ownerDocument->importNode($isbn);
824 $mods->appendChild($isbn);
827 # ... and loop over the constituent records
828 for my $map ( @$recs ) {
832 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
835 my $part_mods = $_parser->parse_string($rec);
836 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
837 ($part_mods) = $part_mods->findnodes('//mods:mods');
839 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
840 $node->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
841 $node = $mods->ownerDocument->importNode($node);
842 $mods->appendChild( $node );
845 my $relatedItem = $mods
847 ->createElement("mods:relatedItem");
849 $relatedItem->setAttribute( type => 'constituent' );
851 my $identifier = $mods
853 ->createElement("mods:identifier");
855 $identifier->setAttribute( type => 'uri' );
857 my $subRecordInfo = $mods
859 ->createElement("mods:recordInfo");
861 my $subRecordIdentifier = $mods
863 ->createElement("mods:recordIdentifier");
865 my $subid = $map->source;
866 $subRecordIdentifier->appendTextNode(
867 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
872 $subRecordInfo->appendChild($subRecordIdentifier);
874 $relatedItem->appendChild( $subRecordInfo );
876 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
877 $tor->setNamespace( "http://www.loc.gov/mods/", "mods", 1 ) if ($tor);
878 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
879 $relatedItem->appendChild($tor) if ($tor);
881 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
882 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
883 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
884 $relatedItem->appendChild( $part_isbn );
887 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
891 $mods->appendChild( $relatedItem );
895 $_storage->disconnect;
897 return entityize($mods->toString);
900 __PACKAGE__->register_method(
901 method => 'retrieve_metarecord_mods',
902 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
907 Returns the MODS representation of the requested metarecord
911 { name => 'metarecordId',
912 desc => 'An OpenILS metabib::metarecord id',
916 { desc => 'The metarecord in MODS',
921 sub list_metarecord_formats {
924 { namespace_uri => 'http://www.loc.gov/mods/',
925 docs => 'http://www.loc.gov/mods/',
926 schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
931 for my $type ( keys %metarecord_xslt ) {
934 { namespace_uri => $metarecord_xslt{$type}{namespace_uri},
935 docs => $metarecord_xslt{$type}{docs},
936 schema_location => $metarecord_xslt{$type}{schema_location},
943 __PACKAGE__->register_method(
944 method => 'list_metarecord_formats',
945 api_name => 'open-ils.supercat.metarecord.formats',
950 Returns the list of valid metarecord formats that supercat understands.
953 { desc => 'The format list',
959 sub list_record_formats {
962 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
963 docs => 'http://www.loc.gov/marcxml/',
964 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
969 for my $type ( keys %record_xslt ) {
972 { namespace_uri => $record_xslt{$type}{namespace_uri},
973 docs => $record_xslt{$type}{docs},
974 schema_location => $record_xslt{$type}{schema_location},
981 __PACKAGE__->register_method(
982 method => 'list_record_formats',
983 api_name => 'open-ils.supercat.record.formats',
988 Returns the list of valid record formats that supercat understands.
991 { desc => 'The format list',
995 __PACKAGE__->register_method(
996 method => 'list_record_formats',
997 api_name => 'open-ils.supercat.isbn.formats',
1001 { desc => <<" DESC",
1002 Returns the list of valid record formats that supercat understands.
1005 { desc => 'The format list',
1018 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
1019 unless (length($isbn) >= 10);
1021 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1023 # Create a storage session, since we'll be making muliple requests.
1026 # Find the record that has that ISBN.
1027 my $bibrec = $_storage->request(
1028 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
1029 { tag => '020', subfield => 'a', value => { like => lc($isbn).'%'} }
1032 # Go away if we don't have one.
1033 return {} unless (@$bibrec);
1035 # Find the metarecord for that bib record.
1036 my $mr = $_storage->request(
1037 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
1038 {source => $bibrec->[0]->record}
1041 # Find the other records for that metarecord.
1042 my $records = $_storage->request(
1043 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
1044 {metarecord => $mr->[0]->metarecord}
1047 # Just to be safe. There's currently no unique constraint on sources...
1048 my %unique_recs = map { ($_->source, 1) } @$records;
1049 my @rec_list = sort keys %unique_recs;
1051 # And now fetch the ISBNs for thos records.
1055 'open-ils.cstore.direct.metabib.full_rec.search',
1056 { tag => '020', subfield => 'a', record => $_ }
1057 )->gather(1) for (@rec_list);
1059 # We're done with the storage server session.
1060 $_storage->disconnect;
1062 # Return the oISBN data structure. This will be XMLized at a higher layer.
1064 { metarecord => $mr->[0]->metarecord,
1065 record_list => { map { $_ ? ($_->record, $_->value) : () } @$recs } };
1068 __PACKAGE__->register_method(
1070 api_name => 'open-ils.supercat.oisbn',
1074 { desc => <<" DESC",
1075 Returns the ISBN list for the metarecord of the requested isbn
1080 desc => 'An ISBN. Duh.',
1084 { desc => 'record to isbn map',