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;
43 # we need an XML parser
44 $_parser = new XML::LibXML;
47 $_xslt = new XML::LibXSLT;
49 # parse the MODS xslt ...
50 my $mods3_xslt = $_parser->parse_file(
51 OpenSRF::Utils::SettingsClient
53 ->config_value( dirs => 'xsl' ).
54 "/MARC21slim2MODS3.xsl"
56 # and stash a transformer
57 $record_xslt{mods3}{xslt} = $_xslt->parse_stylesheet( $mods3_xslt );
58 $record_xslt{mods3}{namespace_uri} = 'http://www.loc.gov/mods/v3';
59 $record_xslt{mods3}{docs} = 'http://www.loc.gov/mods/';
60 $record_xslt{mods3}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-1.xsd';
62 # parse the MODS xslt ...
63 my $mods_xslt = $_parser->parse_file(
64 OpenSRF::Utils::SettingsClient
66 ->config_value( dirs => 'xsl' ).
67 "/MARC21slim2MODS.xsl"
69 # and stash a transformer
70 $record_xslt{mods}{xslt} = $_xslt->parse_stylesheet( $mods_xslt );
71 $record_xslt{mods}{namespace_uri} = 'http://www.loc.gov/mods/';
72 $record_xslt{mods}{docs} = 'http://www.loc.gov/mods/';
73 $record_xslt{mods}{schema_location} = 'http://www.loc.gov/standards/mods/mods.xsd';
75 # parse the ATOM entry xslt ...
76 my $atom_xslt = $_parser->parse_file(
77 OpenSRF::Utils::SettingsClient
79 ->config_value( dirs => 'xsl' ).
80 "/MARC21slim2ATOM.xsl"
82 # and stash a transformer
83 $record_xslt{atom}{xslt} = $_xslt->parse_stylesheet( $atom_xslt );
84 $record_xslt{atom}{namespace_uri} = 'http://www.w3.org/2005/Atom';
85 $record_xslt{atom}{docs} = 'http://www.ietf.org/rfc/rfc4287.txt';
87 # parse the RDFDC xslt ...
88 my $rdf_dc_xslt = $_parser->parse_file(
89 OpenSRF::Utils::SettingsClient
91 ->config_value( dirs => 'xsl' ).
92 "/MARC21slim2RDFDC.xsl"
94 # and stash a transformer
95 $record_xslt{rdf_dc}{xslt} = $_xslt->parse_stylesheet( $rdf_dc_xslt );
96 $record_xslt{rdf_dc}{namespace_uri} = 'http://purl.org/dc/elements/1.1/';
97 $record_xslt{rdf_dc}{schema_location} = 'http://purl.org/dc/elements/1.1/';
99 # parse the SRWDC xslt ...
100 my $srw_dc_xslt = $_parser->parse_file(
101 OpenSRF::Utils::SettingsClient
103 ->config_value( dirs => 'xsl' ).
104 "/MARC21slim2SRWDC.xsl"
106 # and stash a transformer
107 $record_xslt{srw_dc}{xslt} = $_xslt->parse_stylesheet( $srw_dc_xslt );
108 $record_xslt{srw_dc}{namespace_uri} = 'info:srw/schema/1/dc-schema';
109 $record_xslt{srw_dc}{schema_location} = 'http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd';
111 # parse the OAIDC xslt ...
112 my $oai_dc_xslt = $_parser->parse_file(
113 OpenSRF::Utils::SettingsClient
115 ->config_value( dirs => 'xsl' ).
116 "/MARC21slim2OAIDC.xsl"
118 # and stash a transformer
119 $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $oai_dc_xslt );
120 $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
121 $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
123 # parse the RSS xslt ...
124 my $rss_xslt = $_parser->parse_file(
125 OpenSRF::Utils::SettingsClient
127 ->config_value( dirs => 'xsl' ).
128 "/MARC21slim2RSS2.xsl"
130 # and stash a transformer
131 $record_xslt{rss2}{xslt} = $_xslt->parse_stylesheet( $rss_xslt );
133 register_record_transforms();
138 sub register_record_transforms {
139 for my $type ( keys %record_xslt ) {
140 __PACKAGE__->register_method(
141 method => 'retrieve_record_transform',
142 api_name => "open-ils.supercat.record.$type.retrieve",
146 { desc => "Returns the \U$type\E representation ".
147 "of the requested bibliographic record",
151 desc => 'An OpenILS biblio::record_entry id',
155 { desc => "The bib record in \U$type\E",
164 my $stuff = NFC(shift());
165 $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
174 my @things = $filter->($tree);
175 for my $v ( @{$tree->$field} ){
176 push @things, $filter->($v);
177 push @things, tree_walker($v, $field, $filter);
188 my $page_size = shift || 9;
189 my $page = shift || 0;
191 my ($before_limit,$after_limit) = (0,0);
192 my ($before_offset,$after_offset) = (0,0);
195 $before_limit = $after_limit = int($page_size / 2);
196 $after_limit += 1 if ($page_size % 2);
198 $before_offset = $after_offset = int($page_size / 2);
199 $before_offset += 1 if ($page_size % 2);
200 $before_limit = $after_limit = $page_size;
203 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
205 my $o_search = { shortname => $ou };
206 if (!$ou || $ou eq '-') {
207 $o_search = { parent_ou => undef };
210 my $orgs = $_storage->request(
211 "open-ils.cstore.direct.actor.org_unit.search",
214 flesh_fields => { aou => [qw/children/] }
218 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id});
220 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
225 my $before = $_storage->request(
226 "open-ils.cstore.direct.asset.call_number.search.atomic",
227 { label => { "<" => { transform => "upper", value => ["upper", $label] } },
228 owning_lib => \@ou_ids,
231 flesh_fields => { acn => [qw/record owning_lib/] },
232 order_by => { acn => "upper(label) desc, id desc, owning_lib desc" },
233 limit => $before_limit,
234 offset => abs($page) * $page_size - $before_offset,
237 push @list, reverse(@$before);
241 my $after = $_storage->request(
242 "open-ils.cstore.direct.asset.call_number.search.atomic",
243 { label => { ">=" => { transform => "upper", value => ["upper", $label] } },
244 owning_lib => \@ou_ids,
247 flesh_fields => { acn => [qw/record owning_lib/] },
248 order_by => { acn => "upper(label), id, owning_lib" },
249 limit => $after_limit,
250 offset => abs($page) * $page_size - $after_offset,
258 __PACKAGE__->register_method(
259 method => 'cn_browse',
260 api_name => 'open-ils.supercat.call_number.browse',
265 Returns the XML representation of the requested bibliographic record's holdings
270 desc => 'The target call number lable',
272 { name => 'org_unit',
273 desc => 'The org unit shortname (or "-" or undef for global) to browse',
275 { name => 'page_size',
276 desc => 'Count of call numbers to retrieve, default is 9',
279 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
283 { desc => 'Call numbers with owning_lib and record fleshed',
289 sub new_record_holdings {
295 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
297 my $tree = $_storage->request(
298 "open-ils.cstore.direct.biblio.record_entry.retrieve",
302 bre => [qw/call_numbers/],
303 acn => [qw/copies owning_lib/],
304 acp => [qw/location status circ_lib stat_cat_entries notes/],
305 asce => [qw/stat_cat/],
310 my $o_search = { shortname => uc($ou) };
311 if (!$ou || $ou eq '-') {
312 $o_search = { parent_ou => undef };
315 my $orgs = $_storage->request(
316 "open-ils.cstore.direct.actor.org_unit.search",
319 flesh_fields => { aou => [qw/children/] }
323 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id});
325 $logger->debug("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
327 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
331 my $xml = "<hold:volumes xmlns:hold='http://open-ils.org/spec/holdings/v1'>";
333 for my $cn (@{$tree->call_numbers}) {
336 for my $c (@{$cn->copies}) {
337 next unless grep {$c->circ_lib->id == $_} @ou_ids;
342 (my $cn_class = $cn->class_name) =~ s/::/-/gso;
343 $cn_class =~ s/Fieldmapper-//gso;
344 my $cn_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cn_class/".$cn->id, $month, $day);
346 my $cn_lib = $cn->owning_lib->shortname;
348 my $cn_label = $cn->label;
350 $xml .= "<hold:volume id='$cn_tag' lib='$cn_lib' label='$cn_label'><hold:copies>";
352 for my $cp (@{$cn->copies}) {
354 next unless grep { $cp->circ_lib->id == $_ } @ou_ids;
356 (my $cp_class = $cp->class_name) =~ s/::/-/gso;
357 $cp_class =~ s/Fieldmapper-//gso;
358 my $cp_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cp_class/".$cp->id, $month, $day);
360 my $cp_stat = $cp->status->name;
362 my $cp_loc = $cp->location->name;
364 my $cp_lib = $cp->circ_lib->shortname;
366 my $cp_bc = $cp->barcode;
368 $xml .= "<hold:copy id='$cp_tag' barcode='$cp_bc'><hold:status>$cp_stat</hold:status>".
369 "<hold:location>$cp_loc</hold:location><hold:circlib>$cp_lib</hold:circlib><hold:notes>";
372 # for my $note ( @{$cp->notes} ) {
373 # next unless ( $sce->stat_cat->pub eq 't' );
374 # $xml .= sprintf('<hold:note date="%s" title="%s">%s</hold:note>',$note->create_date, escape($note->title), escape($note->value));
378 $xml .= "</hold:notes><hold:statcats>";
380 if ($cp->stat_cat_entries) {
381 for my $sce ( @{$cp->stat_cat_entries} ) {
382 next unless ( $sce->stat_cat->opac_visible eq 't' );
383 $xml .= sprintf('<hold:statcat name="%s">%s</hold:statcat>',escape($sce->stat_cat->name) ,escape($sce->value));
387 $xml .= "</hold:statcats></hold:copy>";
390 $xml .= "</hold:copies></hold:volume>";
393 $xml .= "</hold:volumes>";
397 __PACKAGE__->register_method(
398 method => 'new_record_holdings',
399 api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
404 Returns the XML representation of the requested bibliographic record's holdings
409 desc => 'An OpenILS biblio::record_entry id',
413 { desc => 'The bib record holdings hierarchy in XML',
420 sub record_holdings {
425 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
427 if (!$holdings_data_cache{status}) {
428 $holdings_data_cache{status} = {
429 map { ($_->id => $_) } @{ $_storage->request( "open-ils.cstore.direct.config.copy_status.search.atomic", {id => {'<>' => undef}} )->gather(1) }
431 $holdings_data_cache{location} = {
432 map { ($_->id => $_) } @{ $_storage->request( "open-ils.cstore.direct.asset.copy_location.retrieve.all.atomic", {id => {'<>' => undef}} )->gather(1) }
434 $holdings_data_cache{ou} =
438 } @{$_storage->request( "open-ils.cstore.direct.actor.org_unit.search.atomic" => { id => { '<>' => undef } } )->gather(1)}
440 $holdings_data_cache{statcat} =
444 } @{$_storage->request( "open-ils.cstore.direct.asset.stat_cat_entry.search.atomic" => { id => { '<>' => undef } } )->gather(1)}
449 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
453 my $xml = "<volumes xmlns='http://open-ils.org/spec/holdings/v1'>";
455 for my $cn ( @{$_storage->request( "open-ils.cstore.direct.asset.call_number.search.atomic" => {record => $bib} )->gather(1)} ) {
456 (my $cn_class = $cn->class_name) =~ s/::/-/gso;
457 $cn_class =~ s/Fieldmapper-//gso;
458 my $cn_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cn_class/".$cn->id, $month, $day);
460 my $cn_lib = $holdings_data_cache{ou}{$cn->owning_lib}->shortname;
462 my $cn_label = $cn->label;
464 $xml .= "<volume id='$cn_tag' lib='$cn_lib' label='$cn_label'><copies>";
466 for my $cp ( @{$_storage->request( "open-ils.cstore.direct.asset.copy.search.atomic" => {call_number => $cn->id} )->gather(1)} ) {
467 (my $cp_class = $cn->class_name) =~ s/::/-/gso;
468 $cp_class =~ s/Fieldmapper-//gso;
469 my $cp_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cp_class/".$cp->id, $month, $day);
471 my $cp_stat = $holdings_data_cache{status}{$cp->status}->name;
473 my $cp_loc = $holdings_data_cache{location}{$cp->location}->name;
475 my $cp_lib = $holdings_data_cache{ou}{$cp->circ_lib}->shortname;
477 my $cp_bc = $cp->barcode;
479 $xml .= "<copy id='$cp_tag' barcode='$cp_bc'><status>$cp_stat</status><location>$cp_loc</location><circlib>$cp_lib</circlib><notes>";
481 for my $note ( @{$_storage->request( "open-ils.cstore.direct.asset.copy_note.search.atomic" => {id => $cp->id, pub => "t" })->gather(1)} ) {
482 $xml .= sprintf('<note date="%s" title="%s">%s</note>',$note->create_date, escape($note->title), escape($note->value));
485 $xml .= "</notes><statcats>";
487 for my $sce ( @{$_storage->request( "open-ils.cstore.direct.asset.stat_cat_entry_copy_map.search.atomic" => { owning_copy => $cp->id })->gather(1)} ) {
488 my $sc = $holdings_data_cache{statcat}{$sce->stat_cat_entry};
489 $xml .= sprintf('<statcat>%s</statcat>',escape($sc->value));
492 $xml .= "</statcats></copy>";
498 $xml .= "</volumes>";
505 $text =~ s/&/&/gsom;
506 $text =~ s/</</gsom;
507 $text =~ s/>/>/gsom;
508 $text =~ s/"/\\"/gsom;
515 my $when = shift || '1-01-01';
519 $type = 'authority' if ($self->api_name =~ /authority/o);
521 my $axis = 'create_date';
522 $axis = 'edit_date' if ($self->api_name =~ /edit/o);
524 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
528 "open-ils.cstore.direct.$type.record_entry.id_list.atomic",
529 { $axis => { ">" => $when }, id => { '>' => 0 } },
530 { order_by => "$axis desc", limit => $limit } )
534 for my $t ( qw/biblio authority/ ) {
535 for my $a ( qw/import edit/ ) {
537 __PACKAGE__->register_method(
538 method => 'recent_changes',
539 api_name => "open-ils.supercat.$t.record.$a.recent",
543 { desc => "Returns a list of recently ${a}ed $t records",
547 desc => "Date to start looking for ${a}ed records",
552 desc => "Maximum count to retrieve",
556 { desc => "An id list of $t records",
564 sub retrieve_record_marcxml {
569 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
574 ->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $rid )
580 __PACKAGE__->register_method(
581 method => 'retrieve_record_marcxml',
582 api_name => 'open-ils.supercat.record.marcxml.retrieve',
587 Returns the MARCXML representation of the requested bibliographic record
592 desc => 'An OpenILS biblio::record_entry id',
596 { desc => 'The bib record in MARCXML',
601 sub retrieve_record_transform {
606 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
608 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
611 warn "Fetching record entry $rid\n";
612 my $marc = $_storage->request(
613 'open-ils.cstore.direct.biblio.record_entry.retrieve',
616 warn "Fetched record entry $rid\n";
618 $_storage->disconnect;
620 return entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $marc ) )->toString);
623 sub retrieve_record_objects {
628 $ids = [$ids] unless (ref $ids);
629 $ids = [grep {$_} @$ids];
631 return [] unless (@$ids);
633 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
634 return $_storage->request('open-ils.cstore.direct.biblio.record_entry.search.atomic' => { id => [grep {$_} @$ids] })->gather(1);
636 __PACKAGE__->register_method(
637 method => 'retrieve_record_objects',
638 api_name => 'open-ils.supercat.record.object.retrieve',
643 Returns the Fieldmapper object representation of the requested bibliographic records
648 desc => 'OpenILS biblio::record_entry ids',
652 { desc => 'The bib records',
659 sub retrieve_metarecord_mods {
664 my $_storage = OpenSRF::AppSession->connect( 'open-ils.cstore' );
666 # Get the metarecord in question
669 'open-ils.cstore.direct.metabib.metarecord.retrieve' => $rid
672 # Now get the map of all bib records for the metarecord
675 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
679 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
681 # and retrieve the lead (master) record as MODS
683 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
684 ->run($mr->master_record);
685 my $master_mods = $_parser->parse_string($master)->documentElement;
686 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
688 # ... and a MODS clone to populate, with guts removed.
689 my $mods = $_parser->parse_string($master)->documentElement;
690 $mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
691 ($mods) = $mods->findnodes('//mods:mods');
692 $mods->removeChildNodes;
694 # Add the metarecord ID as a (locally defined) info URI
695 my $recordInfo = $mods
697 ->createElement("mods:recordInfo");
699 my $recordIdentifier = $mods
701 ->createElement("mods:recordIdentifier");
703 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
708 $recordIdentifier->appendTextNode(
709 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
712 $recordInfo->appendChild($recordIdentifier);
713 $mods->appendChild($recordInfo);
715 # Grab the title, author and ISBN for the master record and populate the metarecord
716 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
719 $title->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
720 $title = $mods->ownerDocument->importNode($title);
721 $mods->appendChild($title);
724 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
726 $author->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
727 $author = $mods->ownerDocument->importNode($author);
728 $mods->appendChild($author);
731 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
733 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
734 $isbn = $mods->ownerDocument->importNode($isbn);
735 $mods->appendChild($isbn);
738 # ... and loop over the constituent records
739 for my $map ( @$recs ) {
743 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
746 my $part_mods = $_parser->parse_string($rec);
747 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
748 ($part_mods) = $part_mods->findnodes('//mods:mods');
750 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
751 $node->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
752 $node = $mods->ownerDocument->importNode($node);
753 $mods->appendChild( $node );
756 my $relatedItem = $mods
758 ->createElement("mods:relatedItem");
760 $relatedItem->setAttribute( type => 'constituent' );
762 my $identifier = $mods
764 ->createElement("mods:identifier");
766 $identifier->setAttribute( type => 'uri' );
768 my $subRecordInfo = $mods
770 ->createElement("mods:recordInfo");
772 my $subRecordIdentifier = $mods
774 ->createElement("mods:recordIdentifier");
776 my $subid = $map->source;
777 $subRecordIdentifier->appendTextNode(
778 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
783 $subRecordInfo->appendChild($subRecordIdentifier);
785 $relatedItem->appendChild( $subRecordInfo );
787 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
788 $tor->setNamespace( "http://www.loc.gov/mods/", "mods", 1 ) if ($tor);
789 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
790 $relatedItem->appendChild($tor) if ($tor);
792 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
793 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
794 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
795 $relatedItem->appendChild( $part_isbn );
798 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
802 $mods->appendChild( $relatedItem );
806 $_storage->disconnect;
808 return entityize($mods->toString);
811 __PACKAGE__->register_method(
812 method => 'retrieve_metarecord_mods',
813 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
818 Returns the MODS representation of the requested metarecord
822 { name => 'metarecordId',
823 desc => 'An OpenILS metabib::metarecord id',
827 { desc => 'The metarecord in MODS',
832 sub list_metarecord_formats {
835 { namespace_uri => 'http://www.loc.gov/mods/',
836 docs => 'http://www.loc.gov/mods/',
837 schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
842 for my $type ( keys %metarecord_xslt ) {
845 { namespace_uri => $metarecord_xslt{$type}{namespace_uri},
846 docs => $metarecord_xslt{$type}{docs},
847 schema_location => $metarecord_xslt{$type}{schema_location},
854 __PACKAGE__->register_method(
855 method => 'list_metarecord_formats',
856 api_name => 'open-ils.supercat.metarecord.formats',
861 Returns the list of valid metarecord formats that supercat understands.
864 { desc => 'The format list',
870 sub list_record_formats {
873 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
874 docs => 'http://www.loc.gov/marcxml/',
875 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
880 for my $type ( keys %record_xslt ) {
883 { namespace_uri => $record_xslt{$type}{namespace_uri},
884 docs => $record_xslt{$type}{docs},
885 schema_location => $record_xslt{$type}{schema_location},
892 __PACKAGE__->register_method(
893 method => 'list_record_formats',
894 api_name => 'open-ils.supercat.record.formats',
899 Returns the list of valid record formats that supercat understands.
902 { desc => 'The format list',
913 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
914 unless (length($isbn) >= 10);
916 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
918 # Create a storage session, since we'll be making muliple requests.
921 # Find the record that has that ISBN.
922 my $bibrec = $_storage->request(
923 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
924 { tag => '020', subfield => 'a', value => { ilike => $isbn.'%'} }
927 # Go away if we don't have one.
928 return {} unless (@$bibrec);
930 # Find the metarecord for that bib record.
931 my $mr = $_storage->request(
932 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
933 {source => $bibrec->[0]->record}
936 # Find the other records for that metarecord.
937 my $records = $_storage->request(
938 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
939 {metarecord => $mr->[0]->metarecord}
942 # Just to be safe. There's currently no unique constraint on sources...
943 my %unique_recs = map { ($_->source, 1) } @$records;
944 my @rec_list = sort keys %unique_recs;
946 # And now fetch the ISBNs for thos records.
947 my $recs = $_storage->request(
948 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
949 { tag => '020', subfield => 'a', record => \@rec_list }
952 # We're done with the storage server session.
953 $_storage->disconnect;
955 # Return the oISBN data structure. This will be XMLized at a higher layer.
957 { metarecord => $mr->[0]->metarecord,
958 record_list => { map { ($_->record, $_->value) } @$recs } };
961 __PACKAGE__->register_method(
963 api_name => 'open-ils.supercat.oisbn',
968 Returns the ISBN list for the metarecord of the requested isbn
973 desc => 'An ISBN. Duh.',
977 { desc => 'record to isbn map',