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;
17 use OpenILS::Utils::Normalize qw( naco_normalize );
19 # All OpenSRF applications must be based on OpenSRF::Application or
20 # a subclass thereof. Makes sense, eh?
21 use OpenILS::Application;
22 use base qw/OpenILS::Application/;
24 # This is the client class, used for connecting to open-ils.storage
25 use OpenSRF::AppSession;
27 # This is an extension of Error.pm that supplies some error types to throw
28 use OpenSRF::EX qw(:try);
30 # This is a helper class for querying the OpenSRF Settings application ...
31 use OpenSRF::Utils::SettingsClient;
33 # ... and here we have the built in logging helper ...
34 use OpenSRF::Utils::Logger qw($logger);
36 # ... and this is our OpenILS object (en|de)coder and psuedo-ORM package.
37 use OpenILS::Utils::Fieldmapper;
39 use OpenILS::Utils::CStoreEditor q/:funcs/;
48 %authority_browse_axis_cache,
52 # we need an XML parser
53 $_parser = new XML::LibXML;
56 $_xslt = new XML::LibXSLT;
58 # parse the MODS xslt ...
59 my $mods33_xslt = $_parser->parse_file(
60 OpenSRF::Utils::SettingsClient
62 ->config_value( dirs => 'xsl' ).
63 "/MARC21slim2MODS33.xsl"
65 # and stash a transformer
66 $record_xslt{mods33}{xslt} = $_xslt->parse_stylesheet( $mods33_xslt );
67 $record_xslt{mods33}{namespace_uri} = 'http://www.loc.gov/mods/v3';
68 $record_xslt{mods33}{docs} = 'http://www.loc.gov/mods/';
69 $record_xslt{mods33}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-3.xsd';
71 # parse the MODS xslt ...
72 my $mods32_xslt = $_parser->parse_file(
73 OpenSRF::Utils::SettingsClient
75 ->config_value( dirs => 'xsl' ).
76 "/MARC21slim2MODS32.xsl"
78 # and stash a transformer
79 $record_xslt{mods32}{xslt} = $_xslt->parse_stylesheet( $mods32_xslt );
80 $record_xslt{mods32}{namespace_uri} = 'http://www.loc.gov/mods/v3';
81 $record_xslt{mods32}{docs} = 'http://www.loc.gov/mods/';
82 $record_xslt{mods32}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-2.xsd';
84 # parse the MODS xslt ...
85 my $mods3_xslt = $_parser->parse_file(
86 OpenSRF::Utils::SettingsClient
88 ->config_value( dirs => 'xsl' ).
89 "/MARC21slim2MODS3.xsl"
91 # and stash a transformer
92 $record_xslt{mods3}{xslt} = $_xslt->parse_stylesheet( $mods3_xslt );
93 $record_xslt{mods3}{namespace_uri} = 'http://www.loc.gov/mods/v3';
94 $record_xslt{mods3}{docs} = 'http://www.loc.gov/mods/';
95 $record_xslt{mods3}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-1.xsd';
97 # parse the MODS xslt ...
98 my $mods_xslt = $_parser->parse_file(
99 OpenSRF::Utils::SettingsClient
101 ->config_value( dirs => 'xsl' ).
102 "/MARC21slim2MODS.xsl"
104 # and stash a transformer
105 $record_xslt{mods}{xslt} = $_xslt->parse_stylesheet( $mods_xslt );
106 $record_xslt{mods}{namespace_uri} = 'http://www.loc.gov/mods/';
107 $record_xslt{mods}{docs} = 'http://www.loc.gov/mods/';
108 $record_xslt{mods}{schema_location} = 'http://www.loc.gov/standards/mods/mods.xsd';
110 # parse the ATOM entry xslt ...
111 my $atom_xslt = $_parser->parse_file(
112 OpenSRF::Utils::SettingsClient
114 ->config_value( dirs => 'xsl' ).
115 "/MARC21slim2ATOM.xsl"
117 # and stash a transformer
118 $record_xslt{atom}{xslt} = $_xslt->parse_stylesheet( $atom_xslt );
119 $record_xslt{atom}{namespace_uri} = 'http://www.w3.org/2005/Atom';
120 $record_xslt{atom}{docs} = 'http://www.ietf.org/rfc/rfc4287.txt';
122 # parse the RDFDC xslt ...
123 my $rdf_dc_xslt = $_parser->parse_file(
124 OpenSRF::Utils::SettingsClient
126 ->config_value( dirs => 'xsl' ).
127 "/MARC21slim2RDFDC.xsl"
129 # and stash a transformer
130 $record_xslt{rdf_dc}{xslt} = $_xslt->parse_stylesheet( $rdf_dc_xslt );
131 $record_xslt{rdf_dc}{namespace_uri} = 'http://purl.org/dc/elements/1.1/';
132 $record_xslt{rdf_dc}{schema_location} = 'http://purl.org/dc/elements/1.1/';
134 # parse the SRWDC xslt ...
135 my $srw_dc_xslt = $_parser->parse_file(
136 OpenSRF::Utils::SettingsClient
138 ->config_value( dirs => 'xsl' ).
139 "/MARC21slim2SRWDC.xsl"
141 # and stash a transformer
142 $record_xslt{srw_dc}{xslt} = $_xslt->parse_stylesheet( $srw_dc_xslt );
143 $record_xslt{srw_dc}{namespace_uri} = 'info:srw/schema/1/dc-schema';
144 $record_xslt{srw_dc}{schema_location} = 'http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd';
146 # parse the OAIDC xslt ...
147 my $oai_dc_xslt = $_parser->parse_file(
148 OpenSRF::Utils::SettingsClient
150 ->config_value( dirs => 'xsl' ).
151 "/MARC21slim2OAIDC.xsl"
153 # and stash a transformer
154 $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $oai_dc_xslt );
155 $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
156 $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
158 # parse the RSS xslt ...
159 my $rss_xslt = $_parser->parse_file(
160 OpenSRF::Utils::SettingsClient
162 ->config_value( dirs => 'xsl' ).
163 "/MARC21slim2RSS2.xsl"
165 # and stash a transformer
166 $record_xslt{rss2}{xslt} = $_xslt->parse_stylesheet( $rss_xslt );
168 # parse the FGDC xslt ...
169 my $fgdc_xslt = $_parser->parse_file(
170 OpenSRF::Utils::SettingsClient
172 ->config_value( dirs => 'xsl' ).
173 "/MARC21slim2FGDC.xsl"
175 # and stash a transformer
176 $record_xslt{fgdc}{xslt} = $_xslt->parse_stylesheet( $fgdc_xslt );
177 $record_xslt{fgdc}{docs} = 'http://www.fgdc.gov/metadata/csdgm/index_html';
178 $record_xslt{fgdc}{schema_location} = 'http://www.fgdc.gov/metadata/fgdc-std-001-1998.xsd';
180 register_record_transforms();
185 sub register_record_transforms {
186 for my $type ( keys %record_xslt ) {
187 __PACKAGE__->register_method(
188 method => 'retrieve_record_transform',
189 api_name => "open-ils.supercat.record.$type.retrieve",
193 { desc => "Returns the \U$type\E representation ".
194 "of the requested bibliographic record",
198 desc => 'An OpenILS biblio::record_entry id',
202 { desc => "The bib record in \U$type\E",
207 __PACKAGE__->register_method(
208 method => 'retrieve_isbn_transform',
209 api_name => "open-ils.supercat.isbn.$type.retrieve",
213 { desc => "Returns the \U$type\E representation ".
214 "of the requested bibliographic record",
222 { desc => "The bib record in \U$type\E",
234 return unless ($tree && ref($tree->$field));
236 my @things = $filter->($tree);
237 for my $v ( @{$tree->$field} ){
238 push @things, $filter->($v);
239 push @things, tree_walker($v, $field, $filter);
244 # find a label_sortkey for a call number with a label which is equal
245 # (or close to) a given label value
246 sub _label_sortkey_from_label {
247 my ($label, $_storage, $ou_ids, $cp_filter) = @_;
249 my $closest_cn = $_storage->request(
250 "open-ils.cstore.direct.asset.call_number.search.atomic",
251 { label => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
252 owning_lib => $ou_ids,
257 order_by => { acn => "oils_text_as_bytea(label), id" }
261 return $closest_cn->[0]->label_sortkey;
263 return '~~~'; #fallback to high ascii value, we are at the end
273 my $page_size = shift || 9;
274 my $page = shift || 0;
275 my $statuses = shift || [];
276 my $copy_locations = shift || [];
278 my ($before_limit,$after_limit) = (0,0);
279 my ($before_offset,$after_offset) = (0,0);
282 $before_limit = $after_limit = int($page_size / 2);
283 $after_limit += 1 if ($page_size % 2);
285 $before_offset = $after_offset = int($page_size / 2);
286 $before_offset += 1 if ($page_size % 2);
287 $before_limit = $after_limit = $page_size;
290 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
292 my $o_search = { shortname => $ou };
293 if (!$ou || $ou eq '-') {
294 $o_search = { parent_ou => undef };
297 my $orgs = $_storage->request(
298 "open-ils.cstore.direct.actor.org_unit.search",
301 flesh_fields => { aou => [qw/children/] }
305 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
307 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
312 if (@$statuses || @$copy_locations) {
317 call_number => { '=' => { '+acn' => 'id' } },
319 ((@$statuses) ? ( status => $statuses) : ()),
320 ((@$copy_locations) ? ( location => $copy_locations) : ())
326 my $label_sortkey = _label_sortkey_from_label($label, $_storage, \@ou_ids, \@cp_filter);
329 my $before = $_storage->request(
330 "open-ils.cstore.direct.asset.call_number.search.atomic",
331 { label_sortkey => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
332 owning_lib => \@ou_ids,
337 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
338 order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" },
339 limit => $before_limit,
340 offset => abs($page) * $page_size - $before_offset,
343 push @list, reverse(@$before);
347 my $after = $_storage->request(
348 "open-ils.cstore.direct.asset.call_number.search.atomic",
349 { label_sortkey => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
350 owning_lib => \@ou_ids,
355 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
356 order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" },
357 limit => $after_limit,
358 offset => abs($page) * $page_size - $after_offset,
366 __PACKAGE__->register_method(
367 method => 'cn_browse',
368 api_name => 'open-ils.supercat.call_number.browse',
373 Returns the XML representation of the requested bibliographic record's holdings
378 desc => 'The target call number label',
380 { name => 'org_unit',
381 desc => 'The org unit shortname (or "-" or undef for global) to browse',
383 { name => 'page_size',
384 desc => 'Count of call numbers to retrieve, default is 9',
387 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
389 { name => 'statuses',
390 desc => 'Array of statuses to filter copies by, optional and can be undef.',
392 { name => 'locations',
393 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
397 { desc => 'Call numbers with owning_lib and record fleshed',
408 my $limit = shift || 10;
409 my $page = shift || 0;
410 my $statuses = shift || [];
411 my $copy_locations = shift || [];
414 my $offset = abs($page) * $limit;
415 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
417 my $o_search = { shortname => $ou };
418 if (!$ou || $ou eq '-') {
419 $o_search = { parent_ou => undef };
422 my $orgs = $_storage->request(
423 "open-ils.cstore.direct.actor.org_unit.search",
426 flesh_fields => { aou => [qw/children/] }
430 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
432 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
437 if (@$statuses || @$copy_locations) {
442 call_number => { '=' => { '+acn' => 'id' } },
444 ((@$statuses) ? ( status => $statuses) : ()),
445 ((@$copy_locations) ? ( location => $copy_locations) : ())
451 my $label_sortkey = _label_sortkey_from_label($label, $_storage, \@ou_ids, \@cp_filter);
454 my $before = $_storage->request(
455 "open-ils.cstore.direct.asset.call_number.search.atomic",
456 { label_sortkey => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
457 owning_lib => \@ou_ids,
462 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
463 order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" },
468 push @list, reverse(@$before);
472 my $after = $_storage->request(
473 "open-ils.cstore.direct.asset.call_number.search.atomic",
474 { label_sortkey => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
475 owning_lib => \@ou_ids,
480 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
481 order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" },
491 __PACKAGE__->register_method(
492 method => 'cn_startwith',
493 api_name => 'open-ils.supercat.call_number.startwith',
498 Returns the XML representation of the requested bibliographic record's holdings
503 desc => 'The target call number label',
505 { name => 'org_unit',
506 desc => 'The org unit shortname (or "-" or undef for global) to browse',
508 { name => 'page_size',
509 desc => 'Count of call numbers to retrieve, default is 9',
512 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
514 { name => 'statuses',
515 desc => 'Array of statuses to filter copies by, optional and can be undef.',
517 { name => 'locations',
518 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
522 { desc => 'Call numbers with owning_lib and record fleshed',
528 sub new_books_by_item {
533 my $page_size = shift || 10;
534 my $page = shift || 1;
535 my $statuses = shift || [];
536 my $copy_locations = shift || [];
538 my $offset = $page_size * ($page - 1);
540 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
543 if ($ou && $ou ne '-') {
544 my $orgs = $_storage->request(
545 "open-ils.cstore.direct.actor.org_unit.search",
546 { shortname => $ou },
548 flesh_fields => { aou => [qw/children/] }
551 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
554 $logger->debug("Searching for records with new copies at orgs [".join(',',@ou_ids)."], based on $ou");
555 my $cns = $_storage->request(
556 "open-ils.cstore.json_query.atomic",
557 { select => { acn => ['record'],
558 acp => [{ aggregate => 1 => transform => max => column => create_date => alias => 'create_date'}]
560 from => { 'acn' => { 'acp' => { field => call_number => fkey => 'id' } } },
564 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
565 ((@$statuses) ? ( status => $statuses) : ()),
566 ((@$copy_locations) ? ( location => $copy_locations) : ())
568 '+acn' => { record => { '>' => 0 } },
570 order_by => { acp => { create_date => { transform => 'max', direction => 'desc' } } },
576 return [ map { $_->{record} } @$cns ];
578 __PACKAGE__->register_method(
579 method => 'new_books_by_item',
580 api_name => 'open-ils.supercat.new_book_list',
585 Returns the XML representation of the requested bibliographic record's holdings
589 { name => 'org_unit',
590 desc => 'The org unit shortname (or "-" or undef for global) to list',
592 { name => 'page_size',
593 desc => 'Count of records to retrieve, default is 10',
596 desc => 'The page of records to retrieve, calculated based on page_size. Starts at 1.',
598 { name => 'statuses',
599 desc => 'Array of statuses to filter copies by, optional and can be undef.',
601 { name => 'locations',
602 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
606 { desc => 'Record IDs',
615 return tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
617 __PACKAGE__->register_method(
618 method => 'general_browse',
619 api_name => 'open-ils.supercat.title.browse',
620 tag => 'tnf', subfield => 'a',
624 { desc => "Returns a list of the requested org-scoped record IDs held",
626 [ { name => 'value', desc => 'The target title', type => 'string' },
627 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
628 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
629 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
630 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
631 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
632 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
635 __PACKAGE__->register_method(
636 method => 'general_browse',
637 api_name => 'open-ils.supercat.author.browse',
638 tag => [qw/100 110 111/], subfield => 'a',
642 { desc => "Returns a list of the requested org-scoped record IDs held",
644 [ { name => 'value', desc => 'The target author', type => 'string' },
645 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
646 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
647 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
648 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
649 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
650 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
653 __PACKAGE__->register_method(
654 method => 'general_browse',
655 api_name => 'open-ils.supercat.subject.browse',
656 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
660 { desc => "Returns a list of the requested org-scoped record IDs held",
662 [ { name => 'value', desc => 'The target subject', type => 'string' },
663 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
664 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
665 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
666 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
667 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
668 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
671 __PACKAGE__->register_method(
672 method => 'general_browse',
673 api_name => 'open-ils.supercat.topic.browse',
674 tag => [qw/650 690/], subfield => 'a',
678 { desc => "Returns a list of the requested org-scoped record IDs held",
680 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
681 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
682 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
683 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
684 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
685 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
686 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
689 __PACKAGE__->register_method(
690 method => 'general_browse',
691 api_name => 'open-ils.supercat.series.browse',
692 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
696 { desc => "Returns a list of the requested org-scoped record IDs held",
698 [ { name => 'value', desc => 'The target series', type => 'string' },
699 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
700 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
701 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
702 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
703 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
704 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
714 my $subfield = shift;
717 my $page_size = shift || 9;
718 my $page = shift || 0;
719 my $statuses = shift || [];
720 my $copy_locations = shift || [];
722 my ($before_limit,$after_limit) = (0,0);
723 my ($before_offset,$after_offset) = (0,0);
726 $before_limit = $after_limit = int($page_size / 2);
727 $after_limit += 1 if ($page_size % 2);
729 $before_offset = $after_offset = int($page_size / 2);
730 $before_offset += 1 if ($page_size % 2);
731 $before_limit = $after_limit = $page_size;
734 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
737 if ($ou && $ou ne '-') {
738 my $orgs = $_storage->request(
739 "open-ils.cstore.direct.actor.org_unit.search",
740 { shortname => $ou },
742 flesh_fields => { aou => [qw/children/] }
745 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
748 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
753 my $before = $_storage->request(
754 "open-ils.cstore.json_query.atomic",
755 { select => { mfr => [qw/record value/] },
760 subfield => $subfield,
761 value => { '<' => lc($value) }
765 { select=> { acp => [ 'id' ] },
766 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
768 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
771 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
772 ((@$statuses) ? ( status => $statuses) : ()),
773 ((@$copy_locations) ? ( location => $copy_locations) : ())
780 { select=> { auri => [ 'id' ] },
781 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
783 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
784 '+auri' => { active => 't' }
791 order_by => { mfr => { value => 'desc' } },
792 limit => $before_limit,
793 offset => abs($page) * $page_size - $before_offset,
796 push @list, map { $_->{record} } reverse(@$before);
800 my $after = $_storage->request(
801 "open-ils.cstore.json_query.atomic",
802 { select => { mfr => [qw/record value/] },
807 subfield => $subfield,
808 value => { '>=' => lc($value) }
812 { select=> { acp => [ 'id' ] },
813 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
815 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
818 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
819 ((@$statuses) ? ( status => $statuses) : ()),
820 ((@$copy_locations) ? ( location => $copy_locations) : ())
827 { select=> { auri => [ 'id' ] },
828 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
830 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
831 '+auri' => { active => 't' }
838 order_by => { mfr => { value => 'asc' } },
839 limit => $after_limit,
840 offset => abs($page) * $page_size - $after_offset,
843 push @list, map { $_->{record} } @$after;
848 __PACKAGE__->register_method(
849 method => 'tag_sf_browse',
850 api_name => 'open-ils.supercat.tag.browse',
855 Returns a list of the requested org-scoped record IDs held
860 desc => 'The target MARC tag',
862 { name => 'subfield',
863 desc => 'The target MARC subfield',
866 desc => 'The target string',
868 { name => 'org_unit',
869 desc => 'The org unit shortname (or "-" or undef for global) to browse',
871 { name => 'page_size',
872 desc => 'Count of call numbers to retrieve, default is 9',
875 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
877 { name => 'statuses',
878 desc => 'Array of statuses to filter copies by, optional and can be undef.',
880 { name => 'locations',
881 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
885 { desc => 'Record IDs that have copies at the relevant org units',
890 sub grab_authority_browse_axes {
892 unless(scalar(keys(%authority_browse_axis_cache))) {
893 my $axes = new_editor->search_authority_browse_axis([
894 { code => { '<>' => undef } },
895 { flesh => 2, flesh_fields => { aba => ['fields'], acsafm => ['bib_fields','sub_entries'] } }
897 $authority_browse_axis_cache{$_->code} = $_ for (@$axes);
900 return [keys %authority_browse_axis_cache];
902 __PACKAGE__->register_method(
903 method => 'grab_authority_browse_axes',
904 api_name => 'open-ils.supercat.authority.browse_axis_list',
907 note => "Returns a list of valid authority browse/startswith axes"
910 sub axis_authority_browse {
915 $axis =~ s/^authority\.//;
916 $axis =~ s/(\.refs)$//;
919 return undef unless ( grep { /$axis/ } @{ grab_authority_browse_axes() } );
922 for my $f (@{$authority_browse_axis_cache{$axis}->fields}) {
925 push @tags, $_->tag for @{$f->sub_entries};
929 return authority_tag_sf_browse($self, $client, \@tags, 'a', @_); # XXX TODO figure out something more correct for the subfield param
931 __PACKAGE__->register_method(
932 method => 'axis_authority_browse',
933 api_name => 'open-ils.supercat.authority.browse.by_axis',
937 { desc => "Returns a list of the requested authority record IDs held",
939 [ { name => 'axis', desc => 'The target axis', type => 'string' },
940 { name => 'value', desc => 'The target value', type => 'string' },
941 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
942 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
943 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
949 sub general_authority_browse {
952 return authority_tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
954 __PACKAGE__->register_method(
955 method => 'general_authority_browse',
956 api_name => 'open-ils.supercat.authority.title.browse',
957 tag => ['130'], subfield => 'a',
961 { desc => "Returns a list of the requested authority record IDs held",
963 [ { name => 'value', desc => 'The target title', type => 'string' },
964 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
965 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
966 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
969 __PACKAGE__->register_method(
970 method => 'general_authority_browse',
971 api_name => 'open-ils.supercat.authority.author.browse',
972 tag => [qw/100 110 111/], subfield => 'a',
976 { desc => "Returns a list of the requested authority record IDs held",
978 [ { name => 'value', desc => 'The target author', type => 'string' },
979 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
980 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
981 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
984 __PACKAGE__->register_method(
985 method => 'general_authority_browse',
986 api_name => 'open-ils.supercat.authority.subject.browse',
987 tag => [qw/148 150 151 155/], subfield => 'a',
991 { desc => "Returns a list of the requested authority record IDs held",
993 [ { name => 'value', desc => 'The target subject', type => 'string' },
994 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
995 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
996 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
999 __PACKAGE__->register_method(
1000 method => 'general_authority_browse',
1001 api_name => 'open-ils.supercat.authority.topic.browse',
1002 tag => ['150'], subfield => 'a',
1006 { desc => "Returns a list of the requested authority record IDs held",
1008 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1009 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1010 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1011 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1014 __PACKAGE__->register_method(
1015 method => 'general_authority_browse',
1016 api_name => 'open-ils.supercat.authority.title.refs.browse',
1017 tag => ['130'], subfield => 'a',
1021 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1023 [ { name => 'value', desc => 'The target title', type => 'string' },
1024 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1025 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1026 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1029 __PACKAGE__->register_method(
1030 method => 'general_authority_browse',
1031 api_name => 'open-ils.supercat.authority.author.refs.browse',
1032 tag => [qw/100 110 111/], subfield => 'a',
1036 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1038 [ { name => 'value', desc => 'The target author', type => 'string' },
1039 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1040 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1041 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1044 __PACKAGE__->register_method(
1045 method => 'general_authority_browse',
1046 api_name => 'open-ils.supercat.authority.subject.refs.browse',
1047 tag => [qw/148 150 151 155/], subfield => 'a',
1051 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1053 [ { name => 'value', desc => 'The target subject', type => 'string' },
1054 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1055 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1056 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1059 __PACKAGE__->register_method(
1060 method => 'general_authority_browse',
1061 api_name => 'open-ils.supercat.authority.topic.refs.browse',
1062 tag => ['150'], subfield => 'a',
1066 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1068 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1069 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1070 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1071 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1077 sub authority_tag_sf_browse {
1082 my $subfield = shift;
1084 my $page_size = shift || 9;
1085 my $page = shift || 0;
1087 # Match authority.full_rec normalization
1088 $value = naco_normalize($value, $subfield);
1090 my ($before_limit,$after_limit) = (0,0);
1091 my ($before_offset,$after_offset) = (0,0);
1094 $before_limit = $after_limit = int($page_size / 2);
1095 $after_limit += 1 if ($page_size % 2);
1097 $before_offset = $after_offset = int($page_size / 2);
1098 $before_offset += 1 if ($page_size % 2);
1099 $before_limit = $after_limit = $page_size;
1102 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1104 # .refs variant includes 4xx and 5xx variants for see / see also
1106 foreach my $tagname (@$tag) {
1107 push(@ref_tags, $tagname);
1108 if ($self->api_name =~ /\.refs\./) {
1109 push(@ref_tags, '4' . substr($tagname, 1, 2));
1110 push(@ref_tags, '5' . substr($tagname, 1, 2));
1116 my $before = $_storage->request(
1117 "open-ils.cstore.json_query.atomic",
1118 { select => { afr => [qw/record value/] },
1119 from => { 'are', 'afr' },
1121 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
1122 '+are' => { 'deleted' => 'f' }
1124 order_by => { afr => { value => 'desc' } },
1125 limit => $before_limit,
1126 offset => abs($page) * $page_size - $before_offset,
1129 push @list, map { $_->{record} } reverse(@$before);
1133 my $after = $_storage->request(
1134 "open-ils.cstore.json_query.atomic",
1135 { select => { afr => [qw/record value/] },
1136 from => { 'are', 'afr' },
1138 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
1139 '+are' => { 'deleted' => 'f' }
1141 order_by => { afr => { value => 'asc' } },
1142 limit => $after_limit,
1143 offset => abs($page) * $page_size - $after_offset,
1146 push @list, map { $_->{record} } @$after;
1149 # If we're not pulling in see/see also references, just return the raw list
1150 if ($self->api_name !~ /\.refs\./) {
1154 # Remove dupe record IDs that turn up due to 4xx and 5xx matches
1157 foreach my $record (@list) {
1158 next if exists $seen{$record};
1159 push @retlist, int($record);
1165 __PACKAGE__->register_method(
1166 method => 'authority_tag_sf_browse',
1167 api_name => 'open-ils.supercat.authority.tag.browse',
1171 { desc => <<" DESC",
1172 Returns a list of the requested authority record IDs held
1177 desc => 'The target Authority MARC tag',
1179 { name => 'subfield',
1180 desc => 'The target Authority MARC subfield',
1183 desc => 'The target string',
1185 { name => 'page_size',
1186 desc => 'Count of call numbers to retrieve, default is 9',
1189 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1193 { desc => 'Authority Record IDs that are near the target string',
1198 sub general_startwith {
1201 return tag_sf_startwith($self, $client, $self->{tag}, $self->{subfield}, @_);
1203 __PACKAGE__->register_method(
1204 method => 'general_startwith',
1205 api_name => 'open-ils.supercat.title.startwith',
1206 tag => 'tnf', subfield => 'a',
1210 { desc => "Returns a list of the requested org-scoped record IDs held",
1212 [ { name => 'value', desc => 'The target title', type => 'string' },
1213 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1214 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1215 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1216 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1217 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1218 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1221 __PACKAGE__->register_method(
1222 method => 'general_startwith',
1223 api_name => 'open-ils.supercat.author.startwith',
1224 tag => [qw/100 110 111/], subfield => 'a',
1228 { desc => "Returns a list of the requested org-scoped record IDs held",
1230 [ { name => 'value', desc => 'The target author', type => 'string' },
1231 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1232 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1233 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1234 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1235 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1236 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1239 __PACKAGE__->register_method(
1240 method => 'general_startwith',
1241 api_name => 'open-ils.supercat.subject.startwith',
1242 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
1246 { desc => "Returns a list of the requested org-scoped record IDs held",
1248 [ { name => 'value', desc => 'The target subject', type => 'string' },
1249 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1250 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1251 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1252 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1253 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1254 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1257 __PACKAGE__->register_method(
1258 method => 'general_startwith',
1259 api_name => 'open-ils.supercat.topic.startwith',
1260 tag => [qw/650 690/], subfield => 'a',
1264 { desc => "Returns a list of the requested org-scoped record IDs held",
1266 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1267 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1268 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1269 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1270 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1271 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1272 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1275 __PACKAGE__->register_method(
1276 method => 'general_startwith',
1277 api_name => 'open-ils.supercat.series.startwith',
1278 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
1282 { desc => "Returns a list of the requested org-scoped record IDs held",
1284 [ { name => 'value', desc => 'The target series', type => 'string' },
1285 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1286 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1287 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1288 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1289 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1290 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1295 sub tag_sf_startwith {
1300 my $subfield = shift;
1303 my $limit = shift || 10;
1304 my $page = shift || 0;
1305 my $statuses = shift || [];
1306 my $copy_locations = shift || [];
1308 my $offset = $limit * abs($page);
1309 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1312 if ($ou && $ou ne '-') {
1313 my $orgs = $_storage->request(
1314 "open-ils.cstore.direct.actor.org_unit.search",
1315 { shortname => $ou },
1317 flesh_fields => { aou => [qw/children/] }
1320 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1323 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
1328 my $before = $_storage->request(
1329 "open-ils.cstore.json_query.atomic",
1330 { select => { mfr => [qw/record value/] },
1335 subfield => $subfield,
1336 value => { '<' => lc($value) }
1340 { select=> { acp => [ 'id' ] },
1341 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
1343 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
1346 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
1347 ((@$statuses) ? ( status => $statuses) : ()),
1348 ((@$copy_locations) ? ( location => $copy_locations) : ())
1355 { select=> { auri => [ 'id' ] },
1356 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
1358 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
1359 '+auri' => { active => 't' }
1366 order_by => { mfr => { value => 'desc' } },
1371 push @list, map { $_->{record} } reverse(@$before);
1375 my $after = $_storage->request(
1376 "open-ils.cstore.json_query.atomic",
1377 { select => { mfr => [qw/record value/] },
1382 subfield => $subfield,
1383 value => { '>=' => lc($value) }
1387 { select=> { acp => [ 'id' ] },
1388 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
1390 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
1393 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
1394 ((@$statuses) ? ( status => $statuses) : ()),
1395 ((@$copy_locations) ? ( location => $copy_locations) : ())
1402 { select=> { auri => [ 'id' ] },
1403 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
1405 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
1406 '+auri' => { active => 't' }
1413 order_by => { mfr => { value => 'asc' } },
1418 push @list, map { $_->{record} } @$after;
1423 __PACKAGE__->register_method(
1424 method => 'tag_sf_startwith',
1425 api_name => 'open-ils.supercat.tag.startwith',
1429 { desc => <<" DESC",
1430 Returns a list of the requested org-scoped record IDs held
1435 desc => 'The target MARC tag',
1437 { name => 'subfield',
1438 desc => 'The target MARC subfield',
1441 desc => 'The target string',
1443 { name => 'org_unit',
1444 desc => 'The org unit shortname (or "-" or undef for global) to browse',
1446 { name => 'page_size',
1447 desc => 'Count of call numbers to retrieve, default is 9',
1450 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1452 { name => 'statuses',
1453 desc => 'Array of statuses to filter copies by, optional and can be undef.',
1455 { name => 'locations',
1456 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
1460 { desc => 'Record IDs that have copies at the relevant org units',
1465 sub axis_authority_startwith {
1470 $axis =~ s/^authority\.//;
1471 $axis =~ s/(\.refs)$//;
1474 return undef unless ( grep { /$axis/ } @{ grab_authority_browse_axes() } );
1477 for my $f (@{$authority_browse_axis_cache{$axis}->fields}) {
1478 push @tags, $f->tag;
1480 push @tags, $_->tag for @{$f->sub_entries};
1484 return authority_tag_sf_startwith($self, $client, \@tags, 'a', @_); # XXX TODO figure out something more correct for the subfield param
1486 __PACKAGE__->register_method(
1487 method => 'axis_authority_startwith',
1488 api_name => 'open-ils.supercat.authority.startwith.by_axis',
1492 { desc => "Returns a list of the requested authority record IDs held",
1494 [ { name => 'axis', desc => 'The target axis', type => 'string' },
1495 { name => 'value', desc => 'The target value', type => 'string' },
1496 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1497 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1498 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1504 sub general_authority_startwith {
1507 return authority_tag_sf_startwith($self, $client, $self->{tag}, $self->{subfield}, @_);
1509 __PACKAGE__->register_method(
1510 method => 'general_authority_startwith',
1511 api_name => 'open-ils.supercat.authority.title.startwith',
1512 tag => ['130'], subfield => 'a',
1516 { desc => "Returns a list of the requested authority record IDs held",
1518 [ { name => 'value', desc => 'The target title', type => 'string' },
1519 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1520 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1521 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1524 __PACKAGE__->register_method(
1525 method => 'general_authority_startwith',
1526 api_name => 'open-ils.supercat.authority.author.startwith',
1527 tag => [qw/100 110 111/], subfield => 'a',
1531 { desc => "Returns a list of the requested authority record IDs held",
1533 [ { name => 'value', desc => 'The target author', type => 'string' },
1534 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1535 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1536 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1539 __PACKAGE__->register_method(
1540 method => 'general_authority_startwith',
1541 api_name => 'open-ils.supercat.authority.subject.startwith',
1542 tag => [qw/148 150 151 155/], subfield => 'a',
1546 { desc => "Returns a list of the requested authority record IDs held",
1548 [ { name => 'value', desc => 'The target subject', type => 'string' },
1549 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1550 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1551 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1554 __PACKAGE__->register_method(
1555 method => 'general_authority_startwith',
1556 api_name => 'open-ils.supercat.authority.topic.startwith',
1557 tag => ['150'], subfield => 'a',
1561 { desc => "Returns a list of the requested authority record IDs held",
1563 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1564 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1565 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1566 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1569 __PACKAGE__->register_method(
1570 method => 'general_authority_startwith',
1571 api_name => 'open-ils.supercat.authority.title.refs.startwith',
1572 tag => ['130'], subfield => 'a',
1576 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1578 [ { name => 'value', desc => 'The target title', type => 'string' },
1579 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1580 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1581 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1584 __PACKAGE__->register_method(
1585 method => 'general_authority_startwith',
1586 api_name => 'open-ils.supercat.authority.author.refs.startwith',
1587 tag => [qw/100 110 111/], subfield => 'a',
1591 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1593 [ { name => 'value', desc => 'The target author', type => 'string' },
1594 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1595 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1596 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1599 __PACKAGE__->register_method(
1600 method => 'general_authority_startwith',
1601 api_name => 'open-ils.supercat.authority.subject.refs.startwith',
1602 tag => [qw/148 150 151 155/], subfield => 'a',
1606 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1608 [ { name => 'value', desc => 'The target subject', type => 'string' },
1609 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1610 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1611 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1614 __PACKAGE__->register_method(
1615 method => 'general_authority_startwith',
1616 api_name => 'open-ils.supercat.authority.topic.refs.startwith',
1617 tag => ['150'], subfield => 'a',
1621 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1623 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1624 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1625 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1626 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1632 sub authority_tag_sf_startwith {
1637 my $subfield = shift;
1640 my $limit = shift || 10;
1641 my $page = shift || 0;
1643 # Match authority.full_rec normalization
1644 $value = naco_normalize($value, $subfield);
1646 my $ref_limit = $limit;
1647 my $offset = $limit * abs($page);
1648 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1651 # .refs variant includes 4xx and 5xx variants for see / see also
1652 foreach my $tagname (@$tag) {
1653 push(@ref_tags, $tagname);
1654 if ($self->api_name =~ /\.refs\./) {
1655 push(@ref_tags, '4' . substr($tagname, 1, 2));
1656 push(@ref_tags, '5' . substr($tagname, 1, 2));
1663 # Don't skip the first actual page of results in descending order
1664 $offset = $offset - $limit;
1666 my $before = $_storage->request(
1667 "open-ils.cstore.json_query.atomic",
1668 { select => { afr => [qw/record value/] },
1669 from => { 'afr', 'are' },
1671 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
1672 '+are' => { deleted => 'f' }
1674 order_by => { afr => { value => 'desc' } },
1675 limit => $ref_limit,
1679 push @list, map { $_->{record} } reverse(@$before);
1683 my $after = $_storage->request(
1684 "open-ils.cstore.json_query.atomic",
1685 { select => { afr => [qw/record value/] },
1686 from => { 'afr', 'are' },
1688 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
1689 '+are' => { deleted => 'f' }
1691 order_by => { afr => { value => 'asc' } },
1692 limit => $ref_limit,
1696 push @list, map { $_->{record} } @$after;
1699 # If we're not pulling in see/see also references, just return the raw list
1700 if ($self->api_name !~ /\.refs\./) {
1704 # Remove dupe record IDs that turn up due to 4xx and 5xx matches
1707 foreach my $record (@list) {
1708 next if exists $seen{$record};
1709 push @retlist, int($record);
1715 __PACKAGE__->register_method(
1716 method => 'authority_tag_sf_startwith',
1717 api_name => 'open-ils.supercat.authority.tag.startwith',
1721 { desc => <<" DESC",
1722 Returns a list of the requested authority record IDs held
1727 desc => 'The target Authority MARC tag',
1729 { name => 'subfield',
1730 desc => 'The target Authority MARC subfield',
1733 desc => 'The target string',
1735 { name => 'page_size',
1736 desc => 'Count of call numbers to retrieve, default is 10',
1739 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1743 { desc => 'Authority Record IDs that are near the target string',
1749 sub holding_data_formats {
1752 namespace_uri => 'http://www.loc.gov/MARC21/slim',
1753 docs => 'http://www.loc.gov/marcxml/',
1754 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
1758 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acn.formats', api_level => 1 );
1759 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acp.formats', api_level => 1 );
1760 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.auri.formats', api_level => 1 );
1763 __PACKAGE__->register_method(
1764 method => 'retrieve_uri',
1765 api_name => 'open-ils.supercat.auri.marcxml.retrieve',
1769 { desc => <<" DESC",
1770 Returns a fleshed call number object
1775 desc => 'An OpenILS asset::uri id',
1779 { desc => 'fleshed uri',
1787 my $args = shift || {};
1789 return OpenILS::Application::SuperCat::unAPI
1790 ->new(OpenSRF::AppSession
1791 ->create( 'open-ils.cstore' )
1793 "open-ils.cstore.direct.asset.uri.retrieve",
1797 auri => [qw/call_number_maps/],
1798 auricnm => [qw/call_number/],
1799 acn => [qw/owning_lib record prefix suffix/],
1806 __PACKAGE__->register_method(
1807 method => 'retrieve_copy',
1808 api_name => 'open-ils.supercat.acp.marcxml.retrieve',
1812 { desc => <<" DESC",
1813 Returns a fleshed call number object
1818 desc => 'An OpenILS asset::copy id',
1822 { desc => 'fleshed copy',
1830 my $args = shift || {};
1832 return OpenILS::Application::SuperCat::unAPI
1833 ->new(OpenSRF::AppSession
1834 ->create( 'open-ils.cstore' )
1836 "open-ils.cstore.direct.asset.copy.retrieve",
1840 acn => [qw/owning_lib record prefix suffix/],
1841 acp => [qw/call_number location status circ_lib stat_cat_entries notes parts/],
1848 __PACKAGE__->register_method(
1849 method => 'retrieve_callnumber',
1850 api_name => 'open-ils.supercat.acn.marcxml.retrieve',
1855 { desc => <<" DESC",
1856 Returns a fleshed call number object
1861 desc => 'An OpenILS asset::call_number id',
1865 { desc => 'call number with copies',
1869 sub retrieve_callnumber {
1873 my $args = shift || {};
1875 return OpenILS::Application::SuperCat::unAPI
1876 ->new(OpenSRF::AppSession
1877 ->create( 'open-ils.cstore' )
1879 "open-ils.cstore.direct.asset.call_number.retrieve",
1883 acn => [qw/owning_lib record copies uri_maps prefix suffix/],
1884 auricnm => [qw/uri/],
1885 acp => [qw/location status circ_lib stat_cat_entries notes parts/],
1893 __PACKAGE__->register_method(
1894 method => 'basic_record_holdings',
1895 api_name => 'open-ils.supercat.record.basic_holdings.retrieve',
1900 { desc => <<" DESC",
1901 Returns a basic hash representation of the requested bibliographic record's holdings
1906 desc => 'An OpenILS biblio::record_entry id',
1910 { desc => 'Hash of bib record holdings hierarchy (call numbers and copies)',
1914 sub basic_record_holdings {
1920 # holdings hold an array of call numbers, which hold an array of copies
1921 # holdings => [ label: { library, [ copies: { barcode, location, status, circ_lib } ] } ]
1924 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1926 my $tree = $_storage->request(
1927 "open-ils.cstore.direct.biblio.record_entry.retrieve",
1931 bre => [qw/call_numbers/],
1932 acn => [qw/copies owning_lib prefix suffix/],
1933 acp => [qw/location status circ_lib parts/],
1938 my $o_search = { shortname => uc($ou) };
1939 if (!$ou || $ou eq '-') {
1940 $o_search = { parent_ou => undef };
1943 my $orgs = $_storage->request(
1944 "open-ils.cstore.direct.actor.org_unit.search",
1947 flesh_fields => { aou => [qw/children/] }
1951 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1953 $logger->debug("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
1955 for my $cn (@{$tree->call_numbers}) {
1956 next unless ( $cn->deleted eq 'f' || $cn->deleted == 0 );
1959 for my $c (@{$cn->copies}) {
1960 next unless grep {$c->circ_lib->id == $_} @ou_ids;
1961 next unless ( $c->deleted eq 'f' || $c->deleted == 0 );
1967 $holdings{$cn->label}{'owning_lib'} = $cn->owning_lib->shortname;
1969 for my $cp (@{$cn->copies}) {
1971 next unless grep { $cp->circ_lib->id == $_ } @ou_ids;
1972 next unless ( $cp->deleted eq 'f' || $cp->deleted == 0 );
1974 push @{$holdings{$cn->label}{'copies'}}, {
1975 barcode => $cp->barcode,
1976 status => $cp->status->name,
1977 location => $cp->location->name,
1978 circlib => $cp->circ_lib->shortname
1987 #__PACKAGE__->register_method(
1988 # method => 'new_record_holdings',
1989 # api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
1994 # { desc => <<" DESC",
1995 #Returns the XML representation of the requested bibliographic record's holdings
1999 # { name => 'bibId',
2000 # desc => 'An OpenILS biblio::record_entry id',
2001 # type => 'number' },
2004 # { desc => 'Stream of bib record holdings hierarchy in XML',
2005 # type => 'string' }
2010 sub new_record_holdings {
2019 $paging = [-1,0] if (!$paging or !ref($paging) or @$paging == 0);
2020 my $limit = $$paging[0];
2021 my $offset = $$paging[1] || 0;
2023 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2024 my $_search = OpenSRF::AppSession->create( 'open-ils.search' );
2026 my $o_search = { shortname => uc($ou) };
2027 if (!$ou || $ou eq '-') {
2028 $o_search = { parent_ou => undef };
2031 my $one_org = $_storage->request(
2032 "open-ils.cstore.direct.actor.org_unit.search",
2036 my $count_req = $_search->request('open-ils.search.biblio.record.copy_count' => $one_org->id => $bib);
2037 my $staff_count_req = $_search->request('open-ils.search.biblio.record.copy_count.staff' => $one_org->id => $bib);
2039 my $orgs = $_storage->request(
2040 'open-ils.cstore.json_query.atomic',
2041 { from => [ 'actor.org_unit_descendants', defined($depth) ? ( $one_org->id, $depth ) : ( $one_org->id ) ] }
2045 my @ou_ids = map { $_->{id} } @$orgs;
2047 $logger->info("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
2049 my %subselect = ( '-or' => [
2050 { owning_lib => \@ou_ids },
2054 call_number => { '=' => {'+acn'=>'id'} },
2056 circ_lib => \@ou_ids
2062 if ($flesh and $flesh eq 'uris') {
2064 owning_lib => \@ou_ids,
2066 from => { auricnm => 'auri' },
2068 call_number => { '=' => {'+acn'=>'id'} },
2069 '+auri' => { active => 't' }
2076 my $cns = $_storage->request(
2077 "open-ils.cstore.direct.asset.call_number.search.atomic",
2084 acn => [qw/copies owning_lib uri_maps prefix suffix/],
2085 auricnm => [qw/uri/],
2086 acp => [qw/circ_lib location status stat_cat_entries notes parts/],
2087 asce => [qw/stat_cat/],
2089 ( $limit > -1 ? ( limit => $limit ) : () ),
2090 ( $offset ? ( offset => $offset ) : () ),
2091 order_by => { acn => { label_sortkey => {} } }
2095 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
2099 $client->respond("<holdings xmlns='http://open-ils.org/spec/holdings/v1'><counts>\n");
2101 my $copy_counts = $count_req->gather(1);
2102 my $staff_copy_counts = $staff_count_req->gather(1);
2104 for my $c (@$copy_counts) {
2105 $$c{transcendant} ||= 0;
2106 my $out = "<count type='public'";
2107 $out .= " $_='$$c{$_}'" for (qw/count available unshadow transcendant org_unit depth/);
2108 $client->respond("$out/>\n")
2111 for my $c (@$staff_copy_counts) {
2112 $$c{transcendant} ||= 0;
2113 my $out = "<count type='staff'";
2114 $out .= " $_='$$c{$_}'" for (qw/count available unshadow transcendant org_unit depth/);
2115 $client->respond("$out/>\n")
2118 $client->respond("</counts><volumes>\n");
2120 for my $cn (@$cns) {
2121 next unless (@{$cn->copies} > 0 or (ref($cn->uri_maps) and @{$cn->uri_maps}));
2123 # We don't want O:A:S:unAPI::acn to return the record, we've got that already
2124 # In the context of BibTemplate, copies aren't necessary because we pull those
2125 # in a separate call
2127 OpenILS::Application::SuperCat::unAPI::acn
2129 ->as_xml( {no_record => 1, no_copies => ($flesh ? 0 : 1)} )
2133 $client->respond("</volumes><subscriptions>\n");
2135 $logger->info("Searching for serial holdings at orgs [".join(',',@ou_ids)."], based on $ou");
2137 %subselect = ( '-or' => [
2138 { owning_lib => \@ou_ids },
2141 where => { holding_lib => \@ou_ids }
2146 my $ssubs = $_storage->request(
2147 "open-ils.cstore.direct.serial.subscription.search.atomic",
2148 { record_entry => $bib,
2153 ssub => [qw/distributions issuances scaps owning_lib/],
2154 sdist => [qw/basic_summary supplement_summary index_summary streams holding_lib/],
2155 sstr => [qw/items/],
2156 sitem => [qw/notes unit/],
2157 sunit => [qw/notes location status circ_lib stat_cat_entries call_number/],
2158 acn => [qw/owning_lib prefix suffix/],
2160 ( $limit > -1 ? ( limit => $limit ) : () ),
2161 ( $offset ? ( offset => $offset ) : () ),
2173 date_expected => {},
2180 for my $ssub (@$ssubs) {
2181 next unless (@{$ssub->distributions} or @{$ssub->issuances} or @{$ssub->scaps});
2183 # We don't want O:A:S:unAPI::ssub to return the record, we've got that already
2184 # In the context of BibTemplate, copies aren't necessary because we pull those
2185 # in a separate call
2187 OpenILS::Application::SuperCat::unAPI::ssub
2189 ->as_xml( {no_record => 1, no_items => ($flesh ? 0 : 1)} )
2194 return "</subscriptions></holdings>\n";
2196 __PACKAGE__->register_method(
2197 method => 'new_record_holdings',
2198 api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
2203 { desc => <<" DESC",
2204 Returns the XML representation of the requested bibliographic record's holdings
2209 desc => 'An OpenILS biblio::record_entry ID',
2211 { name => 'orgUnit',
2212 desc => 'An OpenILS actor::org_unit short name that limits the scope of returned holdings',
2215 desc => 'An OpenILS actor::org_unit_type depththat limits the scope of returned holdings',
2217 { name => 'hideCopies',
2218 desc => 'Flag that prevents the inclusion of copies in the returned holdings',
2219 type => 'boolean' },
2221 desc => 'Arry of limit and offset for holdings paging',
2225 { desc => 'Stream of bib record holdings hierarchy in XML',
2235 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2237 my $recs = $_storage->request(
2238 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2239 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2242 return undef unless (@$recs);
2244 return ($self->method_lookup( 'open-ils.supercat.record.holdings_xml.retrieve')->run( $recs->[0]->record ))[0];
2246 __PACKAGE__->register_method(
2247 method => 'isbn_holdings',
2248 api_name => 'open-ils.supercat.isbn.holdings_xml.retrieve',
2252 { desc => <<" DESC",
2253 Returns the XML representation of the requested bibliographic record's holdings
2262 { desc => 'The bib record holdings hierarchy in XML',
2270 return '' unless $text;
2271 $text =~ s/&/&/gsom;
2272 $text =~ s/</</gsom;
2273 $text =~ s/>/>/gsom;
2274 $text =~ s/"/"/gsom;
2275 $text =~ s/'/'/gsom;
2279 sub recent_changes {
2282 my $when = shift || '1-01-01';
2285 my $type = 'biblio';
2288 if ($self->api_name =~ /authority/o) {
2289 $type = 'authority';
2293 my $axis = 'create_date';
2294 $axis = 'edit_date' if ($self->api_name =~ /edit/o);
2296 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2298 return $_storage->request(
2299 "open-ils.cstore.direct.$type.record_entry.id_list.atomic",
2300 { $axis => { ">" => $when }, id => { '>' => 0 }, deleted => 'f', active => 't' },
2301 { order_by => { $hint => "$axis desc" }, limit => $limit }
2305 for my $t ( qw/biblio authority/ ) {
2306 for my $a ( qw/import edit/ ) {
2308 __PACKAGE__->register_method(
2309 method => 'recent_changes',
2310 api_name => "open-ils.supercat.$t.record.$a.recent",
2314 { desc => "Returns a list of recently ${a}ed $t records",
2318 desc => "Date to start looking for ${a}ed records",
2319 default => '1-01-01',
2323 desc => "Maximum count to retrieve",
2327 { desc => "An id list of $t records",
2335 sub retrieve_authority_marcxml {
2340 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2342 my $record = $_storage->request( 'open-ils.cstore.direct.authority.record_entry.retrieve' => $rid )->gather(1);
2343 return $U->entityize( $record->marc ) if ($record);
2347 __PACKAGE__->register_method(
2348 method => 'retrieve_authority_marcxml',
2349 api_name => 'open-ils.supercat.authority.marcxml.retrieve',
2353 { desc => <<" DESC",
2354 Returns the MARCXML representation of the requested authority record
2358 { name => 'authorityId',
2359 desc => 'An OpenILS authority::record_entry id',
2363 { desc => 'The authority record in MARCXML',
2368 sub retrieve_record_marcxml {
2373 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2375 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $rid )->gather(1);
2376 return $U->entityize( $record->marc ) if ($record);
2380 __PACKAGE__->register_method(
2381 method => 'retrieve_record_marcxml',
2382 api_name => 'open-ils.supercat.record.marcxml.retrieve',
2386 { desc => <<" DESC",
2387 Returns the MARCXML representation of the requested bibliographic record
2392 desc => 'An OpenILS biblio::record_entry id',
2396 { desc => 'The bib record in MARCXML',
2401 sub retrieve_isbn_marcxml {
2406 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2408 my $recs = $_storage->request(
2409 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2410 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2413 return undef unless (@$recs);
2415 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
2416 return $U->entityize( $record->marc ) if ($record);
2420 __PACKAGE__->register_method(
2421 method => 'retrieve_isbn_marcxml',
2422 api_name => 'open-ils.supercat.isbn.marcxml.retrieve',
2426 { desc => <<" DESC",
2427 Returns the MARCXML representation of the requested ISBN
2432 desc => 'An ... um ... ISBN',
2436 { desc => 'The bib record in MARCXML',
2441 sub retrieve_record_transform {
2446 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
2448 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2449 #$_storage->connect;
2451 my $record = $_storage->request(
2452 'open-ils.cstore.direct.biblio.record_entry.retrieve',
2456 return undef unless ($record);
2458 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
2461 sub retrieve_isbn_transform {
2466 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2468 my $recs = $_storage->request(
2469 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2470 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2473 return undef unless (@$recs);
2475 (my $transform = $self->api_name) =~ s/^.+isbn\.([^\.]+)\.retrieve$/$1/o;
2477 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
2479 return undef unless ($record);
2481 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
2484 sub retrieve_record_objects {
2489 my $type = 'biblio';
2491 if ($self->api_name =~ /authority/) {
2492 $type = 'authority';
2495 $ids = [$ids] unless (ref $ids);
2496 $ids = [grep {$_} @$ids];
2498 return [] unless (@$ids);
2500 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2501 return $_storage->request("open-ils.cstore.direct.$type.record_entry.search.atomic" => { id => [grep {$_} @$ids] })->gather(1);
2503 __PACKAGE__->register_method(
2504 method => 'retrieve_record_objects',
2505 api_name => 'open-ils.supercat.record.object.retrieve',
2509 { desc => <<" DESC",
2510 Returns the Fieldmapper object representation of the requested bibliographic records
2515 desc => 'OpenILS biblio::record_entry ids',
2519 { desc => 'The bib records',
2524 __PACKAGE__->register_method(
2525 method => 'retrieve_record_objects',
2526 api_name => 'open-ils.supercat.authority.object.retrieve',
2530 { desc => <<" DESC",
2531 Returns the Fieldmapper object representation of the requested authority records
2535 { name => 'authIds',
2536 desc => 'OpenILS authority::record_entry ids',
2540 { desc => 'The authority records',
2545 sub retrieve_isbn_object {
2550 return undef unless ($isbn);
2552 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2553 my $recs = $_storage->request(
2554 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2555 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2558 return undef unless (@$recs);
2560 return $_storage->request(
2561 'open-ils.cstore.direct.biblio.record_entry.search.atomic',
2562 { id => $recs->[0]->record }
2565 __PACKAGE__->register_method(
2566 method => 'retrieve_isbn_object',
2567 api_name => 'open-ils.supercat.isbn.object.retrieve',
2571 { desc => <<" DESC",
2572 Returns the Fieldmapper object representation of the requested bibliographic record
2581 { desc => 'The bib record',
2588 sub retrieve_metarecord_mods {
2593 my $_storage = OpenSRF::AppSession->connect( 'open-ils.cstore' );
2595 # Get the metarecord in question
2598 'open-ils.cstore.direct.metabib.metarecord.retrieve' => $rid
2601 # Now get the map of all bib records for the metarecord
2604 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2605 {metarecord => $rid}
2608 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
2610 # and retrieve the lead (master) record as MODS
2612 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
2613 ->run($mr->master_record);
2614 my $master_mods = $_parser->parse_string($master)->documentElement;
2615 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods" );
2616 $master_mods->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2618 # ... and a MODS clone to populate, with guts removed.
2619 my $mods = $_parser->parse_string($master)->documentElement;
2620 $mods->setNamespace( "http://www.loc.gov/mods/", "mods" ); # modsCollection element
2621 $mods->setNamespace('http://www.loc.gov/mods/', undef, 1);
2622 ($mods) = $mods->findnodes('//mods:mods');
2623 #$mods->setNamespace( "http://www.loc.gov/mods/", "mods" ); # mods element
2624 $mods->removeChildNodes;
2625 $mods->setNamespace('http://www.loc.gov/mods/', undef, 1);
2627 # Add the metarecord ID as a (locally defined) info URI
2628 my $recordInfo = $mods
2630 ->createElement("recordInfo");
2632 my $recordIdentifier = $mods
2634 ->createElement("recordIdentifier");
2636 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
2641 $recordIdentifier->appendTextNode(
2642 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
2645 $recordInfo->appendChild($recordIdentifier);
2646 $mods->appendChild($recordInfo);
2648 # Grab the title, author and ISBN for the master record and populate the metarecord
2649 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
2652 $title->setNamespace( "http://www.loc.gov/mods/", "mods" );
2653 $title->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2654 $title = $mods->ownerDocument->importNode($title);
2655 $mods->appendChild($title);
2658 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
2660 $author->setNamespace( "http://www.loc.gov/mods/", "mods" );
2661 $author->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2662 $author = $mods->ownerDocument->importNode($author);
2663 $mods->appendChild($author);
2666 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
2668 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods" );
2669 $isbn->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2670 $isbn = $mods->ownerDocument->importNode($isbn);
2671 $mods->appendChild($isbn);
2674 # ... and loop over the constituent records
2675 for my $map ( @$recs ) {
2679 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
2680 ->run($map->source);
2682 my $part_mods = $_parser->parse_string($rec);
2683 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods" );
2684 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2685 ($part_mods) = $part_mods->findnodes('//mods:mods');
2687 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
2688 $node->setNamespace( "http://www.loc.gov/mods/", "mods" );
2689 $node->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2690 $node = $mods->ownerDocument->importNode($node);
2691 $mods->appendChild( $node );
2694 my $relatedItem = $mods
2696 ->createElement("relatedItem");
2698 $relatedItem->setAttribute( type => 'constituent' );
2700 my $identifier = $mods
2702 ->createElement("identifier");
2704 $identifier->setAttribute( type => 'uri' );
2706 my $subRecordInfo = $mods
2708 ->createElement("recordInfo");
2710 my $subRecordIdentifier = $mods
2712 ->createElement("recordIdentifier");
2714 my $subid = $map->source;
2715 $subRecordIdentifier->appendTextNode(
2716 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
2721 $subRecordInfo->appendChild($subRecordIdentifier);
2723 $relatedItem->appendChild( $subRecordInfo );
2725 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
2726 $tor->setNamespace( "http://www.loc.gov/mods/", "mods" );
2727 $tor->setNamespace( "http://www.loc.gov/mods/", undef, 1 ) if ($tor);
2728 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
2729 $relatedItem->appendChild($tor) if ($tor);
2731 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
2732 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods" );
2733 $part_isbn->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2734 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
2735 $relatedItem->appendChild( $part_isbn );
2738 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
2742 $mods->appendChild( $relatedItem );
2746 $_storage->disconnect;
2748 return $U->entityize($mods->toString);
2751 __PACKAGE__->register_method(
2752 method => 'retrieve_metarecord_mods',
2753 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
2757 { desc => <<" DESC",
2758 Returns the MODS representation of the requested metarecord
2762 { name => 'metarecordId',
2763 desc => 'An OpenILS metabib::metarecord id',
2767 { desc => 'The metarecord in MODS',
2772 sub list_metarecord_formats {
2775 { namespace_uri => 'http://www.loc.gov/mods/',
2776 docs => 'http://www.loc.gov/mods/',
2777 schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
2782 for my $type ( keys %metarecord_xslt ) {
2785 { namespace_uri => $metarecord_xslt{$type}{namespace_uri},
2786 docs => $metarecord_xslt{$type}{docs},
2787 schema_location => $metarecord_xslt{$type}{schema_location},
2794 __PACKAGE__->register_method(
2795 method => 'list_metarecord_formats',
2796 api_name => 'open-ils.supercat.metarecord.formats',
2800 { desc => <<" DESC",
2801 Returns the list of valid metarecord formats that supercat understands.
2804 { desc => 'The format list',
2810 sub list_authority_formats {
2813 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
2814 docs => 'http://www.loc.gov/marcxml/',
2815 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
2820 # for my $type ( keys %record_xslt ) {
2823 # { namespace_uri => $record_xslt{$type}{namespace_uri},
2824 # docs => $record_xslt{$type}{docs},
2825 # schema_location => $record_xslt{$type}{schema_location},
2832 __PACKAGE__->register_method(
2833 method => 'list_authority_formats',
2834 api_name => 'open-ils.supercat.authority.formats',
2838 { desc => <<" DESC",
2839 Returns the list of valid authority formats that supercat understands.
2842 { desc => 'The format list',
2847 sub list_record_formats {
2850 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
2851 docs => 'http://www.loc.gov/marcxml/',
2852 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
2857 for my $type ( keys %record_xslt ) {
2860 { namespace_uri => $record_xslt{$type}{namespace_uri},
2861 docs => $record_xslt{$type}{docs},
2862 schema_location => $record_xslt{$type}{schema_location},
2869 __PACKAGE__->register_method(
2870 method => 'list_record_formats',
2871 api_name => 'open-ils.supercat.record.formats',
2875 { desc => <<" DESC",
2876 Returns the list of valid record formats that supercat understands.
2879 { desc => 'The format list',
2883 __PACKAGE__->register_method(
2884 method => 'list_record_formats',
2885 api_name => 'open-ils.supercat.isbn.formats',
2889 { desc => <<" DESC",
2890 Returns the list of valid record formats that supercat understands.
2893 { desc => 'The format list',
2906 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
2907 unless (length($isbn) >= 10);
2909 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2911 # Create a storage session, since we'll be making muliple requests.
2914 # Find the record that has that ISBN.
2915 my $bibrec = $_storage->request(
2916 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2917 { tag => '020', subfield => 'a', value => { like => lc($isbn).'%'} }
2920 # Go away if we don't have one.
2921 return {} unless (@$bibrec);
2923 # Find the metarecord for that bib record.
2924 my $mr = $_storage->request(
2925 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2926 {source => $bibrec->[0]->record}
2929 # Find the other records for that metarecord.
2930 my $records = $_storage->request(
2931 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2932 {metarecord => $mr->[0]->metarecord}
2935 # Just to be safe. There's currently no unique constraint on sources...
2936 my %unique_recs = map { ($_->source, 1) } @$records;
2937 my @rec_list = sort keys %unique_recs;
2939 # And now fetch the ISBNs for thos records.
2943 'open-ils.cstore.direct.metabib.full_rec.search',
2944 { tag => '020', subfield => 'a', record => $_ }
2945 )->gather(1) for (@rec_list);
2947 # We're done with the storage server session.
2948 $_storage->disconnect;
2950 # Return the oISBN data structure. This will be XMLized at a higher layer.
2952 { metarecord => $mr->[0]->metarecord,
2953 record_list => { map { $_ ? ($_->record, $_->value) : () } @$recs } };
2956 __PACKAGE__->register_method(
2958 api_name => 'open-ils.supercat.oisbn',
2962 { desc => <<" DESC",
2963 Returns the ISBN list for the metarecord of the requested isbn
2968 desc => 'An ISBN. Duh.',
2972 { desc => 'record to isbn map',
2977 sub return_bib_search_aliases {
2980 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2982 my $cmsa = $_storage->request(
2983 'open-ils.cstore.direct.config.metabib_search_alias.search.atomic',
2984 { alias => { '!=' => undef } }
2988 if ($_->alias =~ /\./) {
2989 my ($qualifier, $name) = $_->alias =~ m/^(.+?)\.(.+)$/;
2990 $aliases{$qualifier}{$name}{'index'} = $_->alias;
2991 # We will add a 'title' property in a subsequent schema
2992 $aliases{$qualifier}{$name}{'title'} = $name;
2994 # au/kw/se/su/ti go into the default 'eg' qualifier
2995 $aliases{'eg'}{$_->alias}{'index'} = $_->alias;
2996 $aliases{'eg'}{$_->alias}{'title'} = $_->alias;
3003 __PACKAGE__->register_method(
3004 method => 'return_bib_search_aliases',
3005 api_name => 'open-ils.supercat.biblio.search_aliases',
3009 { desc => <<" DESC",
3010 Returns the set of qualified search aliases in the system
3014 { desc => 'Hash of qualified search aliases',
3020 package OpenILS::Application::SuperCat::unAPI;
3021 use base qw/OpenILS::Application::SuperCat/;
3024 die "dummy superclass, use a real class";
3030 return unless ($obj);
3032 $class = ref($class) || $class;
3034 if ($class eq __PACKAGE__) {
3035 return unless (ref($obj));
3036 $class .= '::' . $obj->json_hint;
3039 return bless { obj => $obj } => $class;
3044 return $self->{obj};
3047 package OpenILS::Application::SuperCat::unAPI::auri;
3048 use base qw/OpenILS::Application::SuperCat::unAPI/;
3054 my $xml = ' <uri xmlns="http://open-ils.org/spec/holdings/v1" ';
3055 $xml .= 'id="tag:open-ils.org:asset-uri/' . $self->obj->id . '" ';
3056 $xml .= 'use_restriction="' . $self->escape( $self->obj->use_restriction ) . '" ';
3057 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
3058 $xml .= 'href="' . $self->escape( $self->obj->href ) . '">';
3060 if (!$args->{no_volumes}) {
3061 if (ref($self->obj->call_number_maps) && @{ $self->obj->call_number_maps }) {
3062 $xml .= " <volumes>\n" . join(
3065 OpenILS::Application::SuperCat::unAPI
3066 ->new( $_->call_number )
3067 ->as_xml({ %$args, no_uris=>1, no_copies=>1 })
3068 } @{ $self->obj->call_number_maps }
3069 ) . " </volumes>\n";
3072 $xml .= " <volumes/>\n";
3076 $xml .= " </uri>\n";
3081 package OpenILS::Application::SuperCat::unAPI::acn;
3082 use base qw/OpenILS::Application::SuperCat::unAPI/;
3088 my $xml = ' <volume xmlns="http://open-ils.org/spec/holdings/v1" ';
3090 $xml .= 'id="tag:open-ils.org:asset-call_number/' . $self->obj->id . '" ';
3091 $xml .= 'lib="' . $self->escape( $self->obj->owning_lib->shortname ) . '" ';
3092 $xml .= 'opac_visible="' . $self->obj->owning_lib->opac_visible . '" ';
3093 $xml .= 'deleted="' . $self->obj->deleted . '" ';
3094 $xml .= 'label="' . $self->escape( $self->obj->label ) . '">';
3097 if (!$args->{no_copies}) {
3098 if (ref($self->obj->copies) && @{ $self->obj->copies }) {
3099 $xml .= " <copies>\n" . join(
3102 OpenILS::Application::SuperCat::unAPI
3104 ->as_xml({ %$args, no_volume=>1 })
3105 } @{ $self->obj->copies }
3109 $xml .= " <copies/>\n";
3113 if (!$args->{no_uris}) {
3114 if (ref($self->obj->uri_maps) && @{ $self->obj->uri_maps }) {
3115 $xml .= " <uris>\n" . join(
3118 OpenILS::Application::SuperCat::unAPI
3120 ->as_xml({ %$args, no_volumes=>1 })
3121 } @{ $self->obj->uri_maps }
3125 $xml .= " <uris/>\n";
3130 $xml .= ' <prefix ';
3131 $xml .= 'ident="' . $self->obj->prefix->id . '" ';
3132 $xml .= 'id="tag:open-ils.org:asset-call_number_prefix/' . $self->obj->prefix->id . '" ';
3133 $xml .= 'label_sortkey="'.$self->escape( $self->obj->prefix->label_sortkey ) .'">';
3134 $xml .= $self->escape( $self->obj->prefix->label ) .'</prefix>';
3137 $xml .= ' <suffix ';
3138 $xml .= 'ident="' . $self->obj->suffix->id . '" ';
3139 $xml .= 'id="tag:open-ils.org:asset-call_number_suffix/' . $self->obj->suffix->id . '" ';
3140 $xml .= 'label_sortkey="'.$self->escape( $self->obj->suffix->label_sortkey ) .'">';
3141 $xml .= $self->escape( $self->obj->suffix->label ) .'</suffix>';
3144 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3145 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
3146 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
3147 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
3150 unless ($args->{no_record}) {
3151 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
3153 my $r_doc = $parser->parse_string($self->obj->record->marc);
3154 $r_doc->documentElement->setAttribute( id => $rec_tag );
3155 $xml .= $U->entityize($r_doc->documentElement->toString);
3158 $xml .= " </volume>\n";
3163 package OpenILS::Application::SuperCat::unAPI::ssub;
3164 use base qw/OpenILS::Application::SuperCat::unAPI/;
3170 my $xml = ' <subscription xmlns="http://open-ils.org/spec/holdings/v1" ';
3172 $xml .= 'id="tag:open-ils.org:serial-subscription/' . $self->obj->id . '" ';
3173 $xml .= 'start="' . $self->escape( $self->obj->start_date ) . '" ';
3174 $xml .= 'end="' . $self->escape( $self->obj->end_date ) . '" ';
3175 $xml .= 'expected_date_offset="' . $self->escape( $self->obj->expected_date_offset ) . '">';
3178 if (!$args->{no_distributions}) {
3179 if (ref($self->obj->distributions) && @{ $self->obj->distributions }) {
3180 $xml .= " <distributions>\n" . join(
3183 OpenILS::Application::SuperCat::unAPI
3185 ->as_xml({ %$args, no_subscription=>1, no_issuance=>1 })
3186 } @{ $self->obj->distributions }
3187 ) . " </distributions>\n";
3190 $xml .= " <distributions/>\n";
3194 if (!$args->{no_captions_and_patterns}) {
3195 if (ref($self->obj->scaps) && @{ $self->obj->scaps }) {
3196 $xml .= " <captions_and_patterns>\n" . join(
3199 OpenILS::Application::SuperCat::unAPI
3201 ->as_xml({ %$args, no_subscription=>1 })
3202 } @{ $self->obj->scaps }
3203 ) . " </captions_and_patterns>\n";
3206 $xml .= " <captions_and_patterns/>\n";
3210 if (!$args->{no_issuances}) {
3211 if (ref($self->obj->issuances) && @{ $self->obj->issuances }) {
3212 $xml .= " <issuances>\n" . join(
3215 OpenILS::Application::SuperCat::unAPI
3217 ->as_xml({ %$args, no_subscription=>1, no_items=>1 })
3218 } @{ $self->obj->issuances }
3219 ) . " </issuances>\n";
3222 $xml .= " <issuances/>\n";
3227 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3228 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
3229 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
3230 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
3233 unless ($args->{no_record}) {
3234 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
3236 my $r_doc = $parser->parse_string($self->obj->record_entry->marc);
3237 $r_doc->documentElement->setAttribute( id => $rec_tag );
3238 $xml .= $U->entityize($r_doc->documentElement->toString);
3241 $xml .= " </subscription>\n";
3246 package OpenILS::Application::SuperCat::unAPI::ssum_base;
3247 use base qw/OpenILS::Application::SuperCat::unAPI/;
3253 (my $type = ref($self)) =~ s/^.+([^:]+)$/$1/;
3255 my $xml = " <serial_summary xmlns=\"http://open-ils.org/spec/holdings/v1\" type=\"$type\" ";
3257 $xml .= "id=\"tag:open-ils.org:serial-summary-$type/" . $self->obj->id . '" ';
3258 $xml .= 'generated_coverage="' . $self->escape( $self->obj->generated_coverage ) . '" ';
3259 $xml .= 'show_generated="' . $self->escape( $self->obj->show_generated ) . '" ';
3260 $xml .= 'textual_holdings="' . $self->escape( $self->obj->textual_holdings ) . '">';
3263 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->distribution )->as_xml({ %$args, no_summaries=>1 }) if (!$args->{no_distribution});
3265 $xml .= " </serial_summary>\n";
3271 package OpenILS::Application::SuperCat::unAPI::sssum;
3272 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3274 package OpenILS::Application::SuperCat::unAPI::sbsum;
3275 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3277 package OpenILS::Application::SuperCat::unAPI::sisum;
3278 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3280 package OpenILS::Application::SuperCat::unAPI::sdist;
3281 use base qw/OpenILS::Application::SuperCat::unAPI/;
3287 my $xml = ' <distribution xmlns="http://open-ils.org/spec/holdings/v1" ';
3289 $xml .= 'id="tag:open-ils.org:serial-distribution/' . $self->obj->id . '" ';
3290 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
3291 $xml .= 'unit_label_prefix="' . $self->escape( $self->obj->unit_label_prefix ) . '" ';
3292 $xml .= 'unit_label_suffix="' . $self->escape( $self->obj->unit_label_suffix ) . '">';
3295 if (!$args->{no_distributions}) {
3296 if (ref($self->obj->streams) && @{ $self->obj->streams }) {
3297 $xml .= " <streams>\n" . join(
3300 OpenILS::Application::SuperCat::unAPI
3302 ->as_xml({ %$args, no_distribution=>1 })
3303 } @{ $self->obj->streams }
3304 ) . " </streams>\n";
3307 $xml .= " <streams/>\n";
3311 if (!$args->{no_summaries}) {
3312 $xml .= " <summaries>\n";
3316 OpenILS::Application::SuperCat::unAPI
3318 ->as_xml({ %$args, no_distribution=>1 }) : ""
3319 } ($self->obj->basic_summary, $self->obj->supplement_summary, $self->obj->index_summary)
3322 $xml .= " </summaries>\n";
3326 $xml .= ' <holding_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3327 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->holding_lib->id . '" ';
3328 $xml .= 'shortname="'.$self->escape( $self->obj->holding_lib->shortname ) .'" ';
3329 $xml .= 'name="'.$self->escape( $self->obj->holding_lib->name ) .'"/>';
3332 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_distributions=>1 }) if (!$args->{no_subscription});
3334 if (!$args->{no_record} && $self->obj->record_entry) {
3335 my $rec_tag = "tag:open-ils.org:serial-record_entry/".$self->obj->record_entry->id ;
3337 my $r_doc = $parser->parse_string($self->obj->record_entry->marc);
3338 $r_doc->documentElement->setAttribute( id => $rec_tag );
3339 $xml .= $U->entityize($r_doc->documentElement->toString);
3342 $xml .= " </distribution>\n";
3347 package OpenILS::Application::SuperCat::unAPI::sstr;
3348 use base qw/OpenILS::Application::SuperCat::unAPI/;
3354 my $xml = ' <stream xmlns="http://open-ils.org/spec/holdings/v1" ';
3356 $xml .= 'id="tag:open-ils.org:serial-stream/' . $self->obj->id . '" ';
3357 $xml .= 'routing_label="' . $self->escape( $self->obj->routing_label ) . '">';
3360 if (!$args->{no_items}) {
3361 if (ref($self->obj->items) && @{ $self->obj->items }) {
3362 $xml .= " <items>\n" . join(
3365 OpenILS::Application::SuperCat::unAPI
3367 ->as_xml({ %$args, no_stream=>1 })
3368 } @{ $self->obj->items }
3372 $xml .= " <items/>\n";
3376 #XXX routing_list_user's?
3378 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->distribution )->as_xml({ %$args, no_streams=>1 }) if (!$args->{no_distribution});
3380 $xml .= " </stream>\n";
3385 package OpenILS::Application::SuperCat::unAPI::sitem;
3386 use base qw/OpenILS::Application::SuperCat::unAPI/;
3392 my $xml = ' <serial_item xmlns="http://open-ils.org/spec/holdings/v1" ';
3394 $xml .= 'id="tag:open-ils.org:serial-item/' . $self->obj->id . '" ';
3395 $xml .= 'date_expected="' . $self->escape( $self->obj->date_expected ) . '"';
3396 $xml .= ' date_received="' . $self->escape( $self->obj->date_received ) .'"'if ($self->obj->date_received);
3398 if ($args->{no_issuance}) {
3399 my $siss = ref($self->obj->issuance) ? $self->obj->issuance->id : $self->obj->issuance;
3400 $xml .= ' issuance="tag:open-ils.org:serial-issuance/' . $siss . '"';
3405 if (ref($self->obj->notes) && $self->obj->notes) {
3406 $xml .= " <notes>\n";
3407 for my $note ( @{$self->obj->notes} ) {
3408 next unless ( $note->pub eq 't' );
3409 $xml .= sprintf(' <note date="%s" title="%s">%s</note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3412 $xml .= " </notes>\n";
3414 $xml .= " <notes/>\n";
3417 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->issuance )->as_xml({ %$args, no_items=>1 }) if (!$args->{no_issuance});
3418 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->stream )->as_xml({ %$args, no_items=>1 }) if (!$args->{no_stream});
3419 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->unit )->as_xml({ %$args, no_items=>1, no_volumes=>1 }) if ($self->obj->unit && !$args->{no_unit});
3420 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->uri )->as_xml({ %$args, no_items=>1, no_volumes=>1 }) if ($self->obj->uri && !$args->{no_uri});
3422 $xml .= " </serial_item>\n";
3427 package OpenILS::Application::SuperCat::unAPI::sunit;
3428 use base qw/OpenILS::Application::SuperCat::unAPI/;
3434 my $xml = ' <serial_unit xmlns="http://open-ils.org/spec/holdings/v1" '.
3435 'id="tag:open-ils.org:serial-unit/' . $self->obj->id . '" ';
3437 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3438 create_date edit_date copy_number circulate deposit ref holdable deleted
3439 deposit_amount price barcode circ_modifier circ_as_type opac_visible cost
3440 status_changed_time floating mint_condition detailed_contents sort_key summary_contents
3445 $xml .= ' <status ident="' . $self->obj->status->id . '" opac_visible="' . $self->obj->status->opac_visible . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
3446 $xml .= ' <location ident="' . $self->obj->location->id . '">' . $self->escape( $self->obj->location->name ) . "</location>\n";
3447 $xml .= ' <circlib ident="' . $self->obj->circ_lib->id . '">' . $self->escape( $self->obj->circ_lib->name ) . "</circlib>\n";
3449 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3450 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
3451 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
3452 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'"/>';
3455 $xml .= " <copy_notes>\n";
3456 if (ref($self->obj->notes) && $self->obj->notes) {
3457 for my $note ( @{$self->obj->notes} ) {
3458 next unless ( $note->pub eq 't' );
3459 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3464 $xml .= " </copy_notes>\n";
3465 $xml .= " <statcats>\n";
3467 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
3468 for my $sce ( @{$self->obj->stat_cat_entries} ) {
3469 next unless ( $sce->stat_cat->opac_visible eq 't' );
3470 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
3474 $xml .= " </statcats>\n";
3476 unless ($args->{no_volume}) {
3477 if (ref($self->obj->call_number)) {
3478 $xml .= OpenILS::Application::SuperCat::unAPI
3479 ->new( $self->obj->call_number )
3480 ->as_xml({ %$args, no_copies=>1 });
3482 $xml .= " <volume/>\n";
3486 $xml .= " </serial_unit>\n";
3491 package OpenILS::Application::SuperCat::unAPI::scap;
3492 use base qw/OpenILS::Application::SuperCat::unAPI/;
3498 my $xml = ' <caption_and_pattern xmlns="http://open-ils.org/spec/holdings/v1" '.
3499 'id="tag:open-ils.org:serial-caption_and_pattern/' . $self->obj->id . '" ';
3501 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3502 create_date type active pattern_code enum_1 enum_2 enum_3 enum_4
3503 enum_5 enum_6 chron_1 chron_2 chron_3 chron_4 chron_5 start_date end_date
3506 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_captions_and_patterns=>1 }) if (!$args->{no_subscription});
3507 $xml .= " </caption_and_pattern>\n";
3512 package OpenILS::Application::SuperCat::unAPI::siss;
3513 use base qw/OpenILS::Application::SuperCat::unAPI/;
3519 my $xml = ' <issuance xmlns="http://open-ils.org/spec/holdings/v1" '.
3520 'id="tag:open-ils.org:serial-issuance/' . $self->obj->id . '" ';
3522 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" '
3523 for (qw/create_date edit_date label date_published holding_code holding_type holding_link_id/);
3527 if (!$args->{no_items}) {
3528 if (ref($self->obj->items) && @{ $self->obj->items }) {
3529 $xml .= " <items>\n" . join(
3532 OpenILS::Application::SuperCat::unAPI
3534 ->as_xml({ %$args, no_stream=>1 })
3535 } @{ $self->obj->items }
3539 $xml .= " <items/>\n";
3543 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_issuances=>1 }) if (!$args->{no_subscription});
3544 $xml .= " </issuance>\n";
3549 package OpenILS::Application::SuperCat::unAPI::acp;
3550 use base qw/OpenILS::Application::SuperCat::unAPI/;
3556 my $xml = ' <copy xmlns="http://open-ils.org/spec/holdings/v1" '.
3557 'id="tag:open-ils.org:asset-copy/' . $self->obj->id . '" ';
3559 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3560 create_date edit_date copy_number circulate deposit ref holdable deleted
3561 deposit_amount price barcode circ_modifier circ_as_type opac_visible
3566 $xml .= ' <status ident="' . $self->obj->status->id . '" opac_visible="' . $self->obj->status->opac_visible . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
3567 $xml .= ' <location ident="' . $self->obj->location->id . '" opac_visible="'.$self->obj->location->opac_visible.'">' . $self->escape( $self->obj->location->name ) . "</location>\n";
3568 $xml .= ' <circlib ident="' . $self->obj->circ_lib->id . '" opac_visible="'.$self->obj->circ_lib->opac_visible.'">' . $self->escape( $self->obj->circ_lib->name ) . "</circlib>\n";
3570 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3571 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
3572 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
3573 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'" opac_visible="'.$self->obj->circ_lib->opac_visible.'"/>';
3576 $xml .= " <monograph_parts>\n";
3577 if (ref($self->obj->parts) && $self->obj->parts) {
3578 for my $part ( @{$self->obj->parts} ) {
3579 $xml .= sprintf(' <monograph_part record="%s" sortkey="%s">%s</monograph_part>',$part->record, $self->escape($part->label_sortkey), $self->escape($part->label));
3584 $xml .= " </monograph_parts>\n";
3585 $xml .= " <copy_notes>\n";
3586 if (ref($self->obj->notes) && $self->obj->notes) {
3587 for my $note ( @{$self->obj->notes} ) {
3588 next unless ( $note->pub eq 't' );
3589 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3594 $xml .= " </copy_notes>\n";
3595 $xml .= " <statcats>\n";
3597 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
3598 for my $sce ( @{$self->obj->stat_cat_entries} ) {
3599 next unless ( $sce->stat_cat->opac_visible eq 't' );
3600 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
3604 $xml .= " </statcats>\n";
3606 unless ($args->{no_volume}) {
3607 if (ref($self->obj->call_number)) {
3608 $xml .= OpenILS::Application::SuperCat::unAPI
3609 ->new( $self->obj->call_number )
3610 ->as_xml({ %$args, no_copies=>1 });
3612 $xml .= " <volume/>\n";
3616 $xml .= " </copy>\n";