1 # We'll be working with XML, so...
4 use Unicode::Normalize;
6 # ... and this has some handy common methods
7 use OpenILS::Application::AppUtils;
9 my $parser = new XML::LibXML;
10 my $U = 'OpenILS::Application::AppUtils';
13 package OpenILS::Application::SuperCat;
18 # All OpenSRF applications must be based on OpenSRF::Application or
19 # a subclass thereof. Makes sense, eh?
20 use OpenILS::Application;
21 use base qw/OpenILS::Application/;
23 # This is the client class, used for connecting to open-ils.storage
24 use OpenSRF::AppSession;
26 # This is an extention of Error.pm that supplies some error types to throw
27 use OpenSRF::EX qw(:try);
29 # This is a helper class for querying the OpenSRF Settings application ...
30 use OpenSRF::Utils::SettingsClient;
32 # ... and here we have the built in logging helper ...
33 use OpenSRF::Utils::Logger qw($logger);
35 # ... and this is our OpenILS object (en|de)coder and psuedo-ORM package.
36 use OpenILS::Utils::Fieldmapper;
47 # we need an XML parser
48 $_parser = new XML::LibXML;
51 $_xslt = new XML::LibXSLT;
53 # parse the MODS xslt ...
54 my $mods33_xslt = $_parser->parse_file(
55 OpenSRF::Utils::SettingsClient
57 ->config_value( dirs => 'xsl' ).
58 "/MARC21slim2MODS33.xsl"
60 # and stash a transformer
61 $record_xslt{mods33}{xslt} = $_xslt->parse_stylesheet( $mods33_xslt );
62 $record_xslt{mods33}{namespace_uri} = 'http://www.loc.gov/mods/v3';
63 $record_xslt{mods33}{docs} = 'http://www.loc.gov/mods/';
64 $record_xslt{mods33}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-3.xsd';
66 # parse the MODS xslt ...
67 my $mods32_xslt = $_parser->parse_file(
68 OpenSRF::Utils::SettingsClient
70 ->config_value( dirs => 'xsl' ).
71 "/MARC21slim2MODS32.xsl"
73 # and stash a transformer
74 $record_xslt{mods32}{xslt} = $_xslt->parse_stylesheet( $mods32_xslt );
75 $record_xslt{mods32}{namespace_uri} = 'http://www.loc.gov/mods/v3';
76 $record_xslt{mods32}{docs} = 'http://www.loc.gov/mods/';
77 $record_xslt{mods32}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-2.xsd';
79 # parse the MODS xslt ...
80 my $mods3_xslt = $_parser->parse_file(
81 OpenSRF::Utils::SettingsClient
83 ->config_value( dirs => 'xsl' ).
84 "/MARC21slim2MODS3.xsl"
86 # and stash a transformer
87 $record_xslt{mods3}{xslt} = $_xslt->parse_stylesheet( $mods3_xslt );
88 $record_xslt{mods3}{namespace_uri} = 'http://www.loc.gov/mods/v3';
89 $record_xslt{mods3}{docs} = 'http://www.loc.gov/mods/';
90 $record_xslt{mods3}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-1.xsd';
92 # parse the MODS xslt ...
93 my $mods_xslt = $_parser->parse_file(
94 OpenSRF::Utils::SettingsClient
96 ->config_value( dirs => 'xsl' ).
97 "/MARC21slim2MODS.xsl"
99 # and stash a transformer
100 $record_xslt{mods}{xslt} = $_xslt->parse_stylesheet( $mods_xslt );
101 $record_xslt{mods}{namespace_uri} = 'http://www.loc.gov/mods/';
102 $record_xslt{mods}{docs} = 'http://www.loc.gov/mods/';
103 $record_xslt{mods}{schema_location} = 'http://www.loc.gov/standards/mods/mods.xsd';
105 # parse the ATOM entry xslt ...
106 my $atom_xslt = $_parser->parse_file(
107 OpenSRF::Utils::SettingsClient
109 ->config_value( dirs => 'xsl' ).
110 "/MARC21slim2ATOM.xsl"
112 # and stash a transformer
113 $record_xslt{atom}{xslt} = $_xslt->parse_stylesheet( $atom_xslt );
114 $record_xslt{atom}{namespace_uri} = 'http://www.w3.org/2005/Atom';
115 $record_xslt{atom}{docs} = 'http://www.ietf.org/rfc/rfc4287.txt';
117 # parse the RDFDC xslt ...
118 my $rdf_dc_xslt = $_parser->parse_file(
119 OpenSRF::Utils::SettingsClient
121 ->config_value( dirs => 'xsl' ).
122 "/MARC21slim2RDFDC.xsl"
124 # and stash a transformer
125 $record_xslt{rdf_dc}{xslt} = $_xslt->parse_stylesheet( $rdf_dc_xslt );
126 $record_xslt{rdf_dc}{namespace_uri} = 'http://purl.org/dc/elements/1.1/';
127 $record_xslt{rdf_dc}{schema_location} = 'http://purl.org/dc/elements/1.1/';
129 # parse the SRWDC xslt ...
130 my $srw_dc_xslt = $_parser->parse_file(
131 OpenSRF::Utils::SettingsClient
133 ->config_value( dirs => 'xsl' ).
134 "/MARC21slim2SRWDC.xsl"
136 # and stash a transformer
137 $record_xslt{srw_dc}{xslt} = $_xslt->parse_stylesheet( $srw_dc_xslt );
138 $record_xslt{srw_dc}{namespace_uri} = 'info:srw/schema/1/dc-schema';
139 $record_xslt{srw_dc}{schema_location} = 'http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd';
141 # parse the OAIDC xslt ...
142 my $oai_dc_xslt = $_parser->parse_file(
143 OpenSRF::Utils::SettingsClient
145 ->config_value( dirs => 'xsl' ).
146 "/MARC21slim2OAIDC.xsl"
148 # and stash a transformer
149 $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $oai_dc_xslt );
150 $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
151 $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
153 # parse the RSS xslt ...
154 my $rss_xslt = $_parser->parse_file(
155 OpenSRF::Utils::SettingsClient
157 ->config_value( dirs => 'xsl' ).
158 "/MARC21slim2RSS2.xsl"
160 # and stash a transformer
161 $record_xslt{rss2}{xslt} = $_xslt->parse_stylesheet( $rss_xslt );
163 # parse the FGDC xslt ...
164 my $fgdc_xslt = $_parser->parse_file(
165 OpenSRF::Utils::SettingsClient
167 ->config_value( dirs => 'xsl' ).
168 "/MARC21slim2FGDC.xsl"
170 # and stash a transformer
171 $record_xslt{fgdc}{xslt} = $_xslt->parse_stylesheet( $fgdc_xslt );
172 $record_xslt{fgdc}{docs} = 'http://www.fgdc.gov/metadata/csdgm/index_html';
173 $record_xslt{fgdc}{schema_location} = 'http://www.fgdc.gov/metadata/fgdc-std-001-1998.xsd';
175 register_record_transforms();
180 sub register_record_transforms {
181 for my $type ( keys %record_xslt ) {
182 __PACKAGE__->register_method(
183 method => 'retrieve_record_transform',
184 api_name => "open-ils.supercat.record.$type.retrieve",
188 { desc => "Returns the \U$type\E representation ".
189 "of the requested bibliographic record",
193 desc => 'An OpenILS biblio::record_entry id',
197 { desc => "The bib record in \U$type\E",
202 __PACKAGE__->register_method(
203 method => 'retrieve_isbn_transform',
204 api_name => "open-ils.supercat.isbn.$type.retrieve",
208 { desc => "Returns the \U$type\E representation ".
209 "of the requested bibliographic record",
217 { desc => "The bib record in \U$type\E",
229 return unless ($tree && ref($tree->$field));
231 my @things = $filter->($tree);
232 for my $v ( @{$tree->$field} ){
233 push @things, $filter->($v);
234 push @things, tree_walker($v, $field, $filter);
245 my $page_size = shift || 9;
246 my $page = shift || 0;
248 my ($before_limit,$after_limit) = (0,0);
249 my ($before_offset,$after_offset) = (0,0);
252 $before_limit = $after_limit = int($page_size / 2);
253 $after_limit += 1 if ($page_size % 2);
255 $before_offset = $after_offset = int($page_size / 2);
256 $before_offset += 1 if ($page_size % 2);
257 $before_limit = $after_limit = $page_size;
260 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
262 my $o_search = { shortname => $ou };
263 if (!$ou || $ou eq '-') {
264 $o_search = { parent_ou => undef };
267 my $orgs = $_storage->request(
268 "open-ils.cstore.direct.actor.org_unit.search",
271 flesh_fields => { aou => [qw/children/] }
275 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
277 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
282 my $before = $_storage->request(
283 "open-ils.cstore.direct.asset.call_number.search.atomic",
284 { label => { "<" => { transform => "upper", value => ["upper", $label] } },
285 owning_lib => \@ou_ids,
289 flesh_fields => { acn => [qw/record owning_lib/] },
290 order_by => { acn => "upper(label) desc, id desc, owning_lib desc" },
291 limit => $before_limit,
292 offset => abs($page) * $page_size - $before_offset,
295 push @list, reverse(@$before);
299 my $after = $_storage->request(
300 "open-ils.cstore.direct.asset.call_number.search.atomic",
301 { label => { ">=" => { transform => "upper", value => ["upper", $label] } },
302 owning_lib => \@ou_ids,
306 flesh_fields => { acn => [qw/record owning_lib/] },
307 order_by => { acn => "upper(label), id, owning_lib" },
308 limit => $after_limit,
309 offset => abs($page) * $page_size - $after_offset,
317 __PACKAGE__->register_method(
318 method => 'cn_browse',
319 api_name => 'open-ils.supercat.call_number.browse',
324 Returns the XML representation of the requested bibliographic record's holdings
329 desc => 'The target call number lable',
331 { name => 'org_unit',
332 desc => 'The org unit shortname (or "-" or undef for global) to browse',
334 { name => 'page_size',
335 desc => 'Count of call numbers to retrieve, default is 9',
338 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
342 { desc => 'Call numbers with owning_lib and record fleshed',
348 sub new_books_by_item {
353 my $page_size = shift || 10;
354 my $page = shift || 1;
356 my $offset = $page_size * ($page - 1);
358 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
361 if ($ou && $ou ne '-') {
362 my $orgs = $_storage->request(
363 "open-ils.cstore.direct.actor.org_unit.search",
364 { shortname => $ou },
366 flesh_fields => { aou => [qw/children/] }
369 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
372 $logger->debug("Searching for records with new copies at orgs [".join(',',@ou_ids)."], based on $ou");
373 my $cns = $_storage->request(
374 "open-ils.cstore.json_query.atomic",
375 { select => { acn => ['record'],
376 acp => [{ aggregate => 1 => transform => max => column => create_date => alias => 'create_date'}]
378 from => { 'acn' => { 'acp' => { field => call_number => fkey => 'id' } } },
380 { '+acp' => { deleted => 'f', (@ou_ids) ? ( circ_lib => \@ou_ids) : () },
381 '+acn' => { record => { '>' => 0 } },
383 order_by => { acp => { create_date => { transform => 'max', direction => 'desc' } } },
389 return [ map { $_->{record} } @$cns ];
391 __PACKAGE__->register_method(
392 method => 'new_books_by_item',
393 api_name => 'open-ils.supercat.new_book_list',
398 Returns the XML representation of the requested bibliographic record's holdings
402 { name => 'org_unit',
403 desc => 'The org unit shortname (or "-" or undef for global) to list',
405 { name => 'page_size',
406 desc => 'Count of records to retrieve, default is 10',
409 desc => 'The page of records to retrieve, calculated based on page_size. Starts at 1.',
413 { desc => 'Record IDs',
422 return tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
424 __PACKAGE__->register_method(
425 method => 'general_browse',
426 api_name => 'open-ils.supercat.title.browse',
427 tag => 'tnf', subfield => 'a',
431 { desc => "Returns a list of the requested org-scoped record ids held",
433 [ { name => 'value', desc => 'The target title', type => 'string' },
434 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
435 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
436 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
437 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
440 __PACKAGE__->register_method(
441 method => 'general_browse',
442 api_name => 'open-ils.supercat.author.browse',
443 tag => [qw/100 110 111/], subfield => 'a',
447 { desc => "Returns a list of the requested org-scoped record ids held",
449 [ { name => 'value', desc => 'The target author', type => 'string' },
450 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
451 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
452 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
453 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
456 __PACKAGE__->register_method(
457 method => 'general_browse',
458 api_name => 'open-ils.supercat.subject.browse',
459 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
463 { desc => "Returns a list of the requested org-scoped record ids held",
465 [ { name => 'value', desc => 'The target subject', type => 'string' },
466 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
467 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
468 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
469 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
472 __PACKAGE__->register_method(
473 method => 'general_browse',
474 api_name => 'open-ils.supercat.topic.browse',
475 tag => [qw/650 690/], subfield => 'a',
479 { desc => "Returns a list of the requested org-scoped record ids held",
481 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
482 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
483 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
484 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
485 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
488 __PACKAGE__->register_method(
489 method => 'general_browse',
490 api_name => 'open-ils.supercat.series.browse',
491 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
495 { desc => "Returns a list of the requested org-scoped record ids held",
497 [ { name => 'value', desc => 'The target series', type => 'string' },
498 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
499 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
500 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
501 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
511 my $subfield = shift;
514 my $page_size = shift || 9;
515 my $page = shift || 0;
517 my ($before_limit,$after_limit) = (0,0);
518 my ($before_offset,$after_offset) = (0,0);
521 $before_limit = $after_limit = int($page_size / 2);
522 $after_limit += 1 if ($page_size % 2);
524 $before_offset = $after_offset = int($page_size / 2);
525 $before_offset += 1 if ($page_size % 2);
526 $before_limit = $after_limit = $page_size;
529 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
532 if ($ou && $ou ne '-') {
533 my $orgs = $_storage->request(
534 "open-ils.cstore.direct.actor.org_unit.search",
535 { shortname => $ou },
537 flesh_fields => { aou => [qw/children/] }
540 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
543 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
548 my $before = $_storage->request(
549 "open-ils.cstore.json_query.atomic",
550 { select => { mfr => [qw/record value/] },
555 subfield => $subfield,
556 value => { '<' => lc($value) }
560 { select=> { acp => [ 'id' ] },
561 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
563 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
564 '+acp' => { deleted => 'f', (@ou_ids) ? ( circ_lib => \@ou_ids) : () }
570 { select=> { auri => [ 'id' ] },
571 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
573 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
574 '+auri' => { active => 't' }
581 order_by => { mfr => { value => 'desc' } },
582 limit => $before_limit,
583 offset => abs($page) * $page_size - $before_offset,
586 push @list, map { $_->{record} } reverse(@$before);
590 my $after = $_storage->request(
591 "open-ils.cstore.json_query.atomic",
592 { select => { mfr => [qw/record value/] },
597 subfield => $subfield,
598 value => { '>=' => lc($value) }
602 { select=> { acp => [ 'id' ] },
603 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
605 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
606 '+acp' => { deleted => 'f', (@ou_ids) ? ( circ_lib => \@ou_ids) : () }
612 { select=> { auri => [ 'id' ] },
613 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
615 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
616 '+auri' => { active => 't' }
623 order_by => { mfr => { value => 'asc' } },
624 limit => $after_limit,
625 offset => abs($page) * $page_size - $after_offset,
628 push @list, map { $_->{record} } @$after;
633 __PACKAGE__->register_method(
634 method => 'tag_sf_browse',
635 api_name => 'open-ils.supercat.tag.browse',
640 Returns a list of the requested org-scoped record ids held
645 desc => 'The target MARC tag',
647 { name => 'subfield',
648 desc => 'The target MARC subfield',
651 desc => 'The target string',
653 { name => 'org_unit',
654 desc => 'The org unit shortname (or "-" or undef for global) to browse',
656 { name => 'page_size',
657 desc => 'Count of call numbers to retrieve, default is 9',
660 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
664 { desc => 'Record IDs that have copies at the relevant org units',
669 sub general_authority_browse {
672 return tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
674 __PACKAGE__->register_method(
675 method => 'general_authority_browse',
676 api_name => 'open-ils.supercat.authority.title.browse',
677 tag => '130', subfield => 'a',
681 { desc => "Returns a list of the requested authority record ids held",
683 [ { name => 'value', desc => 'The target title', type => 'string' },
684 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
685 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
686 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
689 __PACKAGE__->register_method(
690 method => 'general_authority_browse',
691 api_name => 'open-ils.supercat.authority.author.browse',
692 tag => [qw/100 110 111/], subfield => 'a',
696 { desc => "Returns a list of the requested authority record ids held",
698 [ { name => 'value', desc => 'The target author', type => 'string' },
699 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
700 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
701 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
704 __PACKAGE__->register_method(
705 method => 'general_authority_browse',
706 api_name => 'open-ils.supercat.authority.subject.browse',
707 tag => [qw/148 150 151 155/], subfield => 'a',
711 { desc => "Returns a list of the requested authority record ids held",
713 [ { name => 'value', desc => 'The target subject', type => 'string' },
714 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
715 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
716 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
719 __PACKAGE__->register_method(
720 method => 'general_authority_browse',
721 api_name => 'open-ils.supercat.authority.topic.browse',
722 tag => '150', subfield => 'a',
726 { desc => "Returns a list of the requested authority record ids held",
728 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
729 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
730 { name => 'page', desc => 'The page of records retrieve, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
731 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
735 sub authority_tag_sf_browse {
740 my $subfield = shift;
742 my $page_size = shift || 9;
743 my $page = shift || 0;
745 my ($before_limit,$after_limit) = (0,0);
746 my ($before_offset,$after_offset) = (0,0);
749 $before_limit = $after_limit = int($page_size / 2);
750 $after_limit += 1 if ($page_size % 2);
752 $before_offset = $after_offset = int($page_size / 2);
753 $before_offset += 1 if ($page_size % 2);
754 $before_limit = $after_limit = $page_size;
757 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
762 my $before = $_storage->request(
763 "open-ils.cstore.json_query.atomic",
764 { select => { afr => [qw/record value/] },
766 where => { tag => $tag, subfield => $subfield, value => { '<' => lc($value) } },
767 order_by => { afr => { value => 'desc' } },
768 limit => $before_limit,
769 offset => abs($page) * $page_size - $before_offset,
772 push @list, map { $_->{record} } reverse(@$before);
776 my $after = $_storage->request(
777 "open-ils.cstore.json_query.atomic",
778 { select => { afr => [qw/record value/] },
780 where => { tag => $tag, subfield => $subfield, value => { '>=' => lc($value) } },
781 order_by => { mfr => { value => 'asc' } },
782 limit => $after_limit,
783 offset => abs($page) * $page_size - $after_offset,
786 push @list, map { $_->{record} } @$after;
791 __PACKAGE__->register_method(
792 method => 'authority_tag_sf_browse',
793 api_name => 'open-ils.supercat.authority.tag.browse',
798 Returns a list of the requested authority record ids held
803 desc => 'The target Authority MARC tag',
805 { name => 'subfield',
806 desc => 'The target Authority MARC subfield',
809 desc => 'The target string',
811 { name => 'page_size',
812 desc => 'Count of call numbers to retrieve, default is 9',
815 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
819 { desc => 'Authority Record IDs that are near the target string',
824 sub holding_data_formats {
827 namespace_uri => 'http://www.loc.gov/MARC21/slim',
828 docs => 'http://www.loc.gov/marcxml/',
829 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
833 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acn.formats', api_level => 1 );
834 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acp.formats', api_level => 1 );
835 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.auri.formats', api_level => 1 );
838 __PACKAGE__->register_method(
839 method => 'retrieve_uri',
840 api_name => 'open-ils.supercat.auri.marcxml.retrieve',
845 Returns a fleshed call number object
850 desc => 'An OpenILS asset::uri id',
854 { desc => 'fleshed uri',
862 my $args = shift || {};
864 return OpenILS::Application::SuperCat::unAPI
865 ->new(OpenSRF::AppSession
866 ->create( 'open-ils.cstore' )
868 "open-ils.cstore.direct.asset.uri.retrieve",
872 auri => [qw/call_number_maps/],
873 auricnm => [qw/call_number/],
874 acn => [qw/owning_lib record/],
881 __PACKAGE__->register_method(
882 method => 'retrieve_copy',
883 api_name => 'open-ils.supercat.acp.marcxml.retrieve',
888 Returns a fleshed call number object
893 desc => 'An OpenILS asset::copy id',
897 { desc => 'fleshed copy',
905 my $args = shift || {};
907 return OpenILS::Application::SuperCat::unAPI
908 ->new(OpenSRF::AppSession
909 ->create( 'open-ils.cstore' )
911 "open-ils.cstore.direct.asset.copy.retrieve",
915 acn => [qw/owning_lib record/],
916 acp => [qw/call_number location status circ_lib stat_cat_entries notes/],
923 __PACKAGE__->register_method(
924 method => 'retrieve_callnumber',
925 api_name => 'open-ils.supercat.acn.marcxml.retrieve',
931 Returns a fleshed call number object
936 desc => 'An OpenILS asset::call_number id',
940 { desc => 'call number with copies',
944 sub retrieve_callnumber {
948 my $args = shift || {};
950 return OpenILS::Application::SuperCat::unAPI
951 ->new(OpenSRF::AppSession
952 ->create( 'open-ils.cstore' )
954 "open-ils.cstore.direct.asset.call_number.retrieve",
958 acn => [qw/owning_lib record copies uri_maps/],
959 auricnm => [qw/uri/],
960 acp => [qw/location status circ_lib stat_cat_entries notes/],
968 __PACKAGE__->register_method(
969 method => 'basic_record_holdings',
970 api_name => 'open-ils.supercat.record.basic_holdings.retrieve',
976 Returns a basic hash representation of the requested bibliographic record's holdings
981 desc => 'An OpenILS biblio::record_entry id',
985 { desc => 'Hash of bib record holdings hierarchy (call numbers and copies)',
989 sub basic_record_holdings {
995 # holdings hold an array of call numbers, which hold an array of copies
996 # holdings => [ label: { library, [ copies: { barcode, location, status, circ_lib } ] } ]
999 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1001 my $tree = $_storage->request(
1002 "open-ils.cstore.direct.biblio.record_entry.retrieve",
1006 bre => [qw/call_numbers/],
1007 acn => [qw/copies owning_lib/],
1008 acp => [qw/location status circ_lib/],
1013 my $o_search = { shortname => uc($ou) };
1014 if (!$ou || $ou eq '-') {
1015 $o_search = { parent_ou => undef };
1018 my $orgs = $_storage->request(
1019 "open-ils.cstore.direct.actor.org_unit.search",
1022 flesh_fields => { aou => [qw/children/] }
1026 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1028 $logger->debug("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
1030 for my $cn (@{$tree->call_numbers}) {
1031 next unless ( $cn->deleted eq 'f' || $cn->deleted == 0 );
1034 for my $c (@{$cn->copies}) {
1035 next unless grep {$c->circ_lib->id == $_} @ou_ids;
1036 next unless ( $c->deleted eq 'f' || $c->deleted == 0 );
1042 $holdings{$cn->label}{'owning_lib'} = $cn->owning_lib->shortname;
1044 for my $cp (@{$cn->copies}) {
1046 next unless grep { $cp->circ_lib->id == $_ } @ou_ids;
1047 next unless ( $cp->deleted eq 'f' || $cp->deleted == 0 );
1049 push @{$holdings{$cn->label}{'copies'}}, {
1050 barcode => $cp->barcode,
1051 status => $cp->status->name,
1052 location => $cp->location->name,
1053 circlib => $cp->circ_lib->shortname
1062 #__PACKAGE__->register_method(
1063 # method => 'new_record_holdings',
1064 # api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
1069 # { desc => <<" DESC",
1070 #Returns the XML representation of the requested bibliographic record's holdings
1074 # { name => 'bibId',
1075 # desc => 'An OpenILS biblio::record_entry id',
1076 # type => 'number' },
1079 # { desc => 'Stream of bib record holdings hierarchy in XML',
1080 # type => 'string' }
1085 sub new_record_holdings {
1093 $paging = [-1,0] if (!$paging or !ref($paging) or @$paging == 0);
1094 my $limit = $$paging[0];
1095 my $offset = $$paging[1] || 0;
1097 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1099 my $o_search = { shortname => uc($ou) };
1100 if (!$ou || $ou eq '-') {
1101 $o_search = { parent_ou => undef };
1104 my $orgs = $_storage->request(
1105 "open-ils.cstore.direct.actor.org_unit.search",
1108 flesh_fields => { aou => [qw/children/] }
1112 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1114 $logger->info("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
1116 my %subselect = ( '-or' => [
1117 { owning_lib => \@ou_ids },
1121 call_number => { '=' => {'+acn'=>'id'} },
1123 circ_lib => \@ou_ids
1129 if ($flesh and $flesh eq 'uris') {
1131 owning_lib => \@ou_ids,
1133 from => { auricnm => 'auri' },
1135 call_number => { '=' => {'+acn'=>'id'} },
1136 '+auri' => { active => 't' }
1143 my $cns = $_storage->request(
1144 "open-ils.cstore.direct.asset.call_number.search.atomic",
1151 acn => [qw/copies owning_lib uri_maps/],
1152 auricnm => [qw/uri/],
1153 acp => [qw/circ_lib location status stat_cat_entries notes/],
1154 asce => [qw/stat_cat/],
1156 ( $limit > -1 ? ( limit => $limit ) : () ),
1157 ( $offset ? ( offset => $offset ) : () ),
1158 order_by => { acn => { label => {} } }
1162 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
1166 $client->respond("<volumes xmlns='http://open-ils.org/spec/holdings/v1'>\n");
1168 for my $cn (@$cns) {
1169 next unless (@{$cn->copies} > 0 or (ref($cn->uri_maps) and @{$cn->uri_maps}));
1171 # We don't want O:A:S:unAPI::acn to return the record, we've got that already
1172 # In the context of BibTemplate, copies aren't necessary because we pull those
1173 # in a separate call
1175 OpenILS::Application::SuperCat::unAPI::acn
1177 ->as_xml( {no_record => 1, no_copies => ($flesh ? 0 : 1)} )
1181 return "</volumes>\n";
1183 __PACKAGE__->register_method(
1184 method => 'new_record_holdings',
1185 api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
1190 { desc => <<" DESC",
1191 Returns the XML representation of the requested bibliographic record's holdings
1196 desc => 'An OpenILS biblio::record_entry ID',
1198 { name => 'orgUnit',
1199 desc => 'An OpenILS actor::org_unit short name that limits the scope of returned holdings',
1201 { name => 'hideCopies',
1202 desc => 'Flag that prevents the inclusion of copies in the returned holdings',
1203 type => 'boolean' },
1205 desc => 'Arry of limit and offset for holdings paging',
1209 { desc => 'Stream of bib record holdings hierarchy in XML',
1219 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1221 my $recs = $_storage->request(
1222 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
1223 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
1226 return undef unless (@$recs);
1228 return ($self->method_lookup( 'open-ils.supercat.record.holdings_xml.retrieve')->run( $recs->[0]->record ))[0];
1230 __PACKAGE__->register_method(
1231 method => 'isbn_holdings',
1232 api_name => 'open-ils.supercat.isbn.holdings_xml.retrieve',
1236 { desc => <<" DESC",
1237 Returns the XML representation of the requested bibliographic record's holdings
1246 { desc => 'The bib record holdings hierarchy in XML',
1254 return '' unless $text;
1255 $text =~ s/&/&/gsom;
1256 $text =~ s/</</gsom;
1257 $text =~ s/>/>/gsom;
1258 $text =~ s/"/\\"/gsom;
1262 sub recent_changes {
1265 my $when = shift || '1-01-01';
1268 my $type = 'biblio';
1269 $type = 'authority' if ($self->api_name =~ /authority/o);
1271 my $axis = 'create_date';
1272 $axis = 'edit_date' if ($self->api_name =~ /edit/o);
1274 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1276 return $_storage->request(
1277 "open-ils.cstore.direct.$type.record_entry.id_list.atomic",
1278 { $axis => { ">" => $when }, id => { '>' => 0 } },
1279 { order_by => { bre => "$axis desc" }, limit => $limit }
1283 for my $t ( qw/biblio authority/ ) {
1284 for my $a ( qw/import edit/ ) {
1286 __PACKAGE__->register_method(
1287 method => 'recent_changes',
1288 api_name => "open-ils.supercat.$t.record.$a.recent",
1292 { desc => "Returns a list of recently ${a}ed $t records",
1296 desc => "Date to start looking for ${a}ed records",
1297 default => '1-01-01',
1301 desc => "Maximum count to retrieve",
1305 { desc => "An id list of $t records",
1313 sub retrieve_record_marcxml {
1318 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1320 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $rid )->gather(1);
1321 return $U->entityize( $record->marc ) if ($record);
1325 __PACKAGE__->register_method(
1326 method => 'retrieve_record_marcxml',
1327 api_name => 'open-ils.supercat.record.marcxml.retrieve',
1331 { desc => <<" DESC",
1332 Returns the MARCXML representation of the requested bibliographic record
1337 desc => 'An OpenILS biblio::record_entry id',
1341 { desc => 'The bib record in MARCXML',
1346 sub retrieve_isbn_marcxml {
1351 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1353 my $recs = $_storage->request(
1354 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
1355 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
1358 return undef unless (@$recs);
1360 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
1361 return $U->entityize( $record->marc ) if ($record);
1365 __PACKAGE__->register_method(
1366 method => 'retrieve_isbn_marcxml',
1367 api_name => 'open-ils.supercat.isbn.marcxml.retrieve',
1371 { desc => <<" DESC",
1372 Returns the MARCXML representation of the requested ISBN
1377 desc => 'An ... um ... ISBN',
1381 { desc => 'The bib record in MARCXML',
1386 sub retrieve_record_transform {
1391 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
1393 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1394 #$_storage->connect;
1396 my $record = $_storage->request(
1397 'open-ils.cstore.direct.biblio.record_entry.retrieve',
1401 return undef unless ($record);
1403 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
1406 sub retrieve_isbn_transform {
1411 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1413 my $recs = $_storage->request(
1414 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
1415 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
1418 return undef unless (@$recs);
1420 (my $transform = $self->api_name) =~ s/^.+isbn\.([^\.]+)\.retrieve$/$1/o;
1422 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
1424 return undef unless ($record);
1426 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
1429 sub retrieve_record_objects {
1434 $ids = [$ids] unless (ref $ids);
1435 $ids = [grep {$_} @$ids];
1437 return [] unless (@$ids);
1439 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1440 return $_storage->request('open-ils.cstore.direct.biblio.record_entry.search.atomic' => { id => [grep {$_} @$ids] })->gather(1);
1442 __PACKAGE__->register_method(
1443 method => 'retrieve_record_objects',
1444 api_name => 'open-ils.supercat.record.object.retrieve',
1448 { desc => <<" DESC",
1449 Returns the Fieldmapper object representation of the requested bibliographic records
1454 desc => 'OpenILS biblio::record_entry ids',
1458 { desc => 'The bib records',
1464 sub retrieve_isbn_object {
1469 return undef unless ($isbn);
1471 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1472 my $recs = $_storage->request(
1473 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
1474 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
1477 return undef unless (@$recs);
1479 return $_storage->request(
1480 'open-ils.cstore.direct.biblio.record_entry.search.atomic',
1481 { id => $recs->[0]->record }
1484 __PACKAGE__->register_method(
1485 method => 'retrieve_isbn_object',
1486 api_name => 'open-ils.supercat.isbn.object.retrieve',
1490 { desc => <<" DESC",
1491 Returns the Fieldmapper object representation of the requested bibliographic record
1500 { desc => 'The bib record',
1507 sub retrieve_metarecord_mods {
1512 my $_storage = OpenSRF::AppSession->connect( 'open-ils.cstore' );
1514 # Get the metarecord in question
1517 'open-ils.cstore.direct.metabib.metarecord.retrieve' => $rid
1520 # Now get the map of all bib records for the metarecord
1523 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
1524 {metarecord => $rid}
1527 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
1529 # and retrieve the lead (master) record as MODS
1531 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
1532 ->run($mr->master_record);
1533 my $master_mods = $_parser->parse_string($master)->documentElement;
1534 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1536 # ... and a MODS clone to populate, with guts removed.
1537 my $mods = $_parser->parse_string($master)->documentElement;
1538 $mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1539 ($mods) = $mods->findnodes('//mods:mods');
1540 $mods->removeChildNodes;
1542 # Add the metarecord ID as a (locally defined) info URI
1543 my $recordInfo = $mods
1545 ->createElement("mods:recordInfo");
1547 my $recordIdentifier = $mods
1549 ->createElement("mods:recordIdentifier");
1551 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
1556 $recordIdentifier->appendTextNode(
1557 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
1560 $recordInfo->appendChild($recordIdentifier);
1561 $mods->appendChild($recordInfo);
1563 # Grab the title, author and ISBN for the master record and populate the metarecord
1564 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
1567 $title->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1568 $title = $mods->ownerDocument->importNode($title);
1569 $mods->appendChild($title);
1572 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
1574 $author->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1575 $author = $mods->ownerDocument->importNode($author);
1576 $mods->appendChild($author);
1579 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
1581 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1582 $isbn = $mods->ownerDocument->importNode($isbn);
1583 $mods->appendChild($isbn);
1586 # ... and loop over the constituent records
1587 for my $map ( @$recs ) {
1591 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
1592 ->run($map->source);
1594 my $part_mods = $_parser->parse_string($rec);
1595 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1596 ($part_mods) = $part_mods->findnodes('//mods:mods');
1598 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
1599 $node->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1600 $node = $mods->ownerDocument->importNode($node);
1601 $mods->appendChild( $node );
1604 my $relatedItem = $mods
1606 ->createElement("mods:relatedItem");
1608 $relatedItem->setAttribute( type => 'constituent' );
1610 my $identifier = $mods
1612 ->createElement("mods:identifier");
1614 $identifier->setAttribute( type => 'uri' );
1616 my $subRecordInfo = $mods
1618 ->createElement("mods:recordInfo");
1620 my $subRecordIdentifier = $mods
1622 ->createElement("mods:recordIdentifier");
1624 my $subid = $map->source;
1625 $subRecordIdentifier->appendTextNode(
1626 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
1631 $subRecordInfo->appendChild($subRecordIdentifier);
1633 $relatedItem->appendChild( $subRecordInfo );
1635 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
1636 $tor->setNamespace( "http://www.loc.gov/mods/", "mods", 1 ) if ($tor);
1637 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
1638 $relatedItem->appendChild($tor) if ($tor);
1640 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
1641 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
1642 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
1643 $relatedItem->appendChild( $part_isbn );
1646 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
1650 $mods->appendChild( $relatedItem );
1654 $_storage->disconnect;
1656 return $U->entityize($mods->toString);
1659 __PACKAGE__->register_method(
1660 method => 'retrieve_metarecord_mods',
1661 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
1665 { desc => <<" DESC",
1666 Returns the MODS representation of the requested metarecord
1670 { name => 'metarecordId',
1671 desc => 'An OpenILS metabib::metarecord id',
1675 { desc => 'The metarecord in MODS',
1680 sub list_metarecord_formats {
1683 { namespace_uri => 'http://www.loc.gov/mods/',
1684 docs => 'http://www.loc.gov/mods/',
1685 schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
1690 for my $type ( keys %metarecord_xslt ) {
1693 { namespace_uri => $metarecord_xslt{$type}{namespace_uri},
1694 docs => $metarecord_xslt{$type}{docs},
1695 schema_location => $metarecord_xslt{$type}{schema_location},
1702 __PACKAGE__->register_method(
1703 method => 'list_metarecord_formats',
1704 api_name => 'open-ils.supercat.metarecord.formats',
1708 { desc => <<" DESC",
1709 Returns the list of valid metarecord formats that supercat understands.
1712 { desc => 'The format list',
1718 sub list_record_formats {
1721 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
1722 docs => 'http://www.loc.gov/marcxml/',
1723 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
1728 for my $type ( keys %record_xslt ) {
1731 { namespace_uri => $record_xslt{$type}{namespace_uri},
1732 docs => $record_xslt{$type}{docs},
1733 schema_location => $record_xslt{$type}{schema_location},
1740 __PACKAGE__->register_method(
1741 method => 'list_record_formats',
1742 api_name => 'open-ils.supercat.record.formats',
1746 { desc => <<" DESC",
1747 Returns the list of valid record formats that supercat understands.
1750 { desc => 'The format list',
1754 __PACKAGE__->register_method(
1755 method => 'list_record_formats',
1756 api_name => 'open-ils.supercat.isbn.formats',
1760 { desc => <<" DESC",
1761 Returns the list of valid record formats that supercat understands.
1764 { desc => 'The format list',
1777 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
1778 unless (length($isbn) >= 10);
1780 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1782 # Create a storage session, since we'll be making muliple requests.
1785 # Find the record that has that ISBN.
1786 my $bibrec = $_storage->request(
1787 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
1788 { tag => '020', subfield => 'a', value => { like => lc($isbn).'%'} }
1791 # Go away if we don't have one.
1792 return {} unless (@$bibrec);
1794 # Find the metarecord for that bib record.
1795 my $mr = $_storage->request(
1796 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
1797 {source => $bibrec->[0]->record}
1800 # Find the other records for that metarecord.
1801 my $records = $_storage->request(
1802 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
1803 {metarecord => $mr->[0]->metarecord}
1806 # Just to be safe. There's currently no unique constraint on sources...
1807 my %unique_recs = map { ($_->source, 1) } @$records;
1808 my @rec_list = sort keys %unique_recs;
1810 # And now fetch the ISBNs for thos records.
1814 'open-ils.cstore.direct.metabib.full_rec.search',
1815 { tag => '020', subfield => 'a', record => $_ }
1816 )->gather(1) for (@rec_list);
1818 # We're done with the storage server session.
1819 $_storage->disconnect;
1821 # Return the oISBN data structure. This will be XMLized at a higher layer.
1823 { metarecord => $mr->[0]->metarecord,
1824 record_list => { map { $_ ? ($_->record, $_->value) : () } @$recs } };
1827 __PACKAGE__->register_method(
1829 api_name => 'open-ils.supercat.oisbn',
1833 { desc => <<" DESC",
1834 Returns the ISBN list for the metarecord of the requested isbn
1839 desc => 'An ISBN. Duh.',
1843 { desc => 'record to isbn map',
1848 package OpenILS::Application::SuperCat::unAPI;
1849 use base qw/OpenILS::Application::SuperCat/;
1852 die "dummy superclass, use a real class";
1858 return unless ($obj);
1860 $class = ref($class) || $class;
1862 if ($class eq __PACKAGE__) {
1863 return unless (ref($obj));
1864 $class .= '::' . $obj->json_hint;
1867 return bless { obj => $obj } => $class;
1872 return $self->{obj};
1875 package OpenILS::Application::SuperCat::unAPI::auri;
1876 use base qw/OpenILS::Application::SuperCat::unAPI/;
1882 my $xml = ' <uri xmlns="http://open-ils.org/spec/holdings/v1" ';
1883 $xml .= 'id="tag:open-ils.org:asset-uri/' . $self->obj->id . '" ';
1884 $xml .= 'use_restriction="' . $self->escape( $self->obj->use_restriction ) . '" ';
1885 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
1886 $xml .= 'href="' . $self->escape( $self->obj->href ) . '">';
1888 if (!$args->{no_volumes}) {
1889 if (ref($self->obj->call_number_maps) && @{ $self->obj->call_number_maps }) {
1890 $xml .= " <volumes>\n" . join(
1893 OpenILS::Application::SuperCat::unAPI
1894 ->new( $_->call_number )
1895 ->as_xml({ %$args, no_uris=>1, no_copies=>1 })
1896 } @{ $self->obj->call_number_maps }
1897 ) . " </volumes>\n";
1900 $xml .= " <volumes/>\n";
1904 $xml .= " </uri>\n";
1909 package OpenILS::Application::SuperCat::unAPI::acn;
1910 use base qw/OpenILS::Application::SuperCat::unAPI/;
1916 my $xml = ' <volume xmlns="http://open-ils.org/spec/holdings/v1" ';
1918 $xml .= 'id="tag:open-ils.org:asset-call_number/' . $self->obj->id . '" ';
1919 $xml .= 'lib="' . $self->escape( $self->obj->owning_lib->shortname ) . '" ';
1920 $xml .= 'label="' . $self->escape( $self->obj->label ) . '">';
1923 if (!$args->{no_copies}) {
1924 if (ref($self->obj->copies) && @{ $self->obj->copies }) {
1925 $xml .= " <copies>\n" . join(
1928 OpenILS::Application::SuperCat::unAPI
1930 ->as_xml({ %$args, no_volume=>1 })
1931 } @{ $self->obj->copies }
1935 $xml .= " <copies/>\n";
1939 if (!$args->{no_uris}) {
1940 if (ref($self->obj->uri_maps) && @{ $self->obj->uri_maps }) {
1941 $xml .= " <uris>\n" . join(
1944 OpenILS::Application::SuperCat::unAPI
1946 ->as_xml({ %$args, no_volumes=>1 })
1947 } @{ $self->obj->uri_maps }
1951 $xml .= " <uris/>\n";
1956 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
1957 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
1958 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
1959 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
1962 unless ($args->{no_record}) {
1963 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
1965 my $r_doc = $parser->parse_string($self->obj->record->marc);
1966 $r_doc->documentElement->setAttribute( id => $rec_tag );
1967 $xml .= $U->entityize($r_doc->documentElement->toString);
1970 $xml .= " </volume>\n";
1975 package OpenILS::Application::SuperCat::unAPI::acp;
1976 use base qw/OpenILS::Application::SuperCat::unAPI/;
1982 my $xml = ' <copy xmlns="http://open-ils.org/spec/holdings/v1" '.
1983 'id="tag:open-ils.org:asset-copy/' . $self->obj->id . '" ';
1985 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
1986 create_date edit_date copy_number circulate deposit ref holdable deleted
1987 deposit_amount price barcode circ_modifier circ_as_type opac_visible
1992 $xml .= ' <status ident="' . $self->obj->status->id . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
1993 $xml .= ' <location ident="' . $self->obj->location->id . '">' . $self->escape( $self->obj->location->name ) . "</location>\n";
1994 $xml .= ' <circlib ident="' . $self->obj->circ_lib->id . '">' . $self->escape( $self->obj->circ_lib->name ) . "</circlib>\n";
1996 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
1997 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
1998 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
1999 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'"/>';
2002 $xml .= " <copy_notes>\n";
2003 if (ref($self->obj->notes) && $self->obj->notes) {
2004 for my $note ( @{$self->obj->notes} ) {
2005 next unless ( $note->pub eq 't' );
2006 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
2011 $xml .= " </copy_notes>\n";
2012 $xml .= " <statcats>\n";
2014 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
2015 for my $sce ( @{$self->obj->stat_cat_entries} ) {
2016 next unless ( $sce->stat_cat->opac_visible eq 't' );
2017 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
2021 $xml .= " </statcats>\n";
2023 unless ($args->{no_volume}) {
2024 if (ref($self->obj->call_number)) {
2025 $xml .= OpenILS::Application::SuperCat::unAPI
2026 ->new( $self->obj->call_number )
2027 ->as_xml({ %$args, no_copies=>1 });
2029 $xml .= " <volume/>\n";
2033 $xml .= " </copy>\n";
2040 # vim: noet:ts=4:sw=4