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();
182 register_new_authorities_methods();
187 sub register_record_transforms {
188 for my $type ( keys %record_xslt ) {
189 __PACKAGE__->register_method(
190 method => 'retrieve_record_transform',
191 api_name => "open-ils.supercat.record.$type.retrieve",
195 { desc => "Returns the \U$type\E representation ".
196 "of the requested bibliographic record",
200 desc => 'An OpenILS biblio::record_entry id',
204 { desc => "The bib record in \U$type\E",
209 __PACKAGE__->register_method(
210 method => 'retrieve_isbn_transform',
211 api_name => "open-ils.supercat.isbn.$type.retrieve",
215 { desc => "Returns the \U$type\E representation ".
216 "of the requested bibliographic record",
224 { desc => "The bib record in \U$type\E",
231 sub register_new_authorities_methods {
232 my %register_args = (
233 method => "generic_new_authorities_method",
237 desc => q/Generated method/,
240 desc => "An axis, an authority tag number, or a bibliographic tag number, depending on invocation",
243 desc => "A search term",
246 desc => "zero-based page number of results",
248 {name => "page size",
249 desc => "number of results per page",
253 desc => "A list of authority record IDs", type => "array"
258 foreach my $how (qw/axis atag btag/) {
259 foreach my $action (qw/browse_center browse_top
260 search_rank search_heading/) {
262 $register_args{api_name} =
263 "open-ils.supercat.authority.$action.by_$how";
264 __PACKAGE__->register_method(%register_args);
266 $register_args{api_name} =
267 "open-ils.supercat.authority.$action.by_$how.refs";
268 __PACKAGE__->register_method(%register_args);
274 sub generic_new_authorities_method {
278 # We want to be extra careful with these arguments, since the next
279 # thing we're doing with them is passing them to a DB procedure.
282 my $page = int(shift || 0);
283 my $page_size = shift;
285 # undef ok, but other non numbers not ok
286 $page_size = int($page_size) if defined $page_size;
288 # Figure out how we were called and what DB procedure we'll call in turn.
289 $self->api_name =~ /\.by_(\w+)($|\.)/;
293 $self->api_name =~ /authority\.(\w+)\./;
296 my $method = "${metaaxis}_$action";
297 $method .= "_refs" if $refs;
299 # Match authority.full_rec normalization
300 # XXX don't know whether we need second arg 'subfield'?
301 $term = naco_normalize($term);
303 my $storage = create OpenSRF::AppSession("open-ils.storage");
304 my $list = $storage->request(
305 "open-ils.storage.authority.in_db.browse_or_search",
306 $method, $what, $term, $page, $page_size
320 return unless ($tree && ref($tree->$field));
322 my @things = $filter->($tree);
323 for my $v ( @{$tree->$field} ){
324 push @things, $filter->($v);
325 push @things, tree_walker($v, $field, $filter);
330 # find a label_sortkey for a call number with a label which is equal
331 # (or close to) a given label value
332 sub _label_sortkey_from_label {
333 my ($label, $_storage, $ou_ids, $cp_filter) = @_;
335 my $closest_cn = $_storage->request(
336 "open-ils.cstore.direct.asset.call_number.search.atomic",
337 { label => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
338 owning_lib => $ou_ids,
343 order_by => { acn => "oils_text_as_bytea(label), id" }
347 return $closest_cn->[0]->label_sortkey;
349 return '~~~'; #fallback to high ascii value, we are at the end
359 my $page_size = shift || 9;
360 my $page = shift || 0;
361 my $statuses = shift || [];
362 my $copy_locations = shift || [];
364 my ($before_limit,$after_limit) = (0,0);
365 my ($before_offset,$after_offset) = (0,0);
368 $before_limit = $after_limit = int($page_size / 2);
369 $after_limit += 1 if ($page_size % 2);
371 $before_offset = $after_offset = int($page_size / 2);
372 $before_offset += 1 if ($page_size % 2);
373 $before_limit = $after_limit = $page_size;
376 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
378 my $o_search = { shortname => $ou };
379 if (!$ou || $ou eq '-') {
380 $o_search = { parent_ou => undef };
383 my $orgs = $_storage->request(
384 "open-ils.cstore.direct.actor.org_unit.search",
387 flesh_fields => { aou => [qw/children/] }
391 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
393 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
398 if (@$statuses || @$copy_locations) {
403 call_number => { '=' => { '+acn' => 'id' } },
405 ((@$statuses) ? ( status => $statuses) : ()),
406 ((@$copy_locations) ? ( location => $copy_locations) : ())
412 my $label_sortkey = _label_sortkey_from_label($label, $_storage, \@ou_ids, \@cp_filter);
415 my $before = $_storage->request(
416 "open-ils.cstore.direct.asset.call_number.search.atomic",
417 { label_sortkey => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
418 owning_lib => \@ou_ids,
423 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
424 order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" },
425 limit => $before_limit,
426 offset => abs($page) * $page_size - $before_offset,
429 push @list, reverse(@$before);
433 my $after = $_storage->request(
434 "open-ils.cstore.direct.asset.call_number.search.atomic",
435 { label_sortkey => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
436 owning_lib => \@ou_ids,
441 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
442 order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" },
443 limit => $after_limit,
444 offset => abs($page) * $page_size - $after_offset,
452 __PACKAGE__->register_method(
453 method => 'cn_browse',
454 api_name => 'open-ils.supercat.call_number.browse',
459 Returns the XML representation of the requested bibliographic record's holdings
464 desc => 'The target call number label',
466 { name => 'org_unit',
467 desc => 'The org unit shortname (or "-" or undef for global) to browse',
469 { name => 'page_size',
470 desc => 'Count of call numbers to retrieve, default is 9',
473 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
475 { name => 'statuses',
476 desc => 'Array of statuses to filter copies by, optional and can be undef.',
478 { name => 'locations',
479 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
483 { desc => 'Call numbers with owning_lib and record fleshed',
494 my $limit = shift || 10;
495 my $page = shift || 0;
496 my $statuses = shift || [];
497 my $copy_locations = shift || [];
500 my $offset = abs($page) * $limit;
501 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
503 my $o_search = { shortname => $ou };
504 if (!$ou || $ou eq '-') {
505 $o_search = { parent_ou => undef };
508 my $orgs = $_storage->request(
509 "open-ils.cstore.direct.actor.org_unit.search",
512 flesh_fields => { aou => [qw/children/] }
516 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
518 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
523 if (@$statuses || @$copy_locations) {
528 call_number => { '=' => { '+acn' => 'id' } },
530 ((@$statuses) ? ( status => $statuses) : ()),
531 ((@$copy_locations) ? ( location => $copy_locations) : ())
537 my $label_sortkey = _label_sortkey_from_label($label, $_storage, \@ou_ids, \@cp_filter);
540 my $before = $_storage->request(
541 "open-ils.cstore.direct.asset.call_number.search.atomic",
542 { label_sortkey => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
543 owning_lib => \@ou_ids,
548 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
549 order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" },
554 push @list, reverse(@$before);
558 my $after = $_storage->request(
559 "open-ils.cstore.direct.asset.call_number.search.atomic",
560 { label_sortkey => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label_sortkey] } },
561 owning_lib => \@ou_ids,
566 flesh_fields => { acn => [qw/record owning_lib prefix suffix/] },
567 order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" },
577 __PACKAGE__->register_method(
578 method => 'cn_startwith',
579 api_name => 'open-ils.supercat.call_number.startwith',
584 Returns the XML representation of the requested bibliographic record's holdings
589 desc => 'The target call number label',
591 { name => 'org_unit',
592 desc => 'The org unit shortname (or "-" or undef for global) to browse',
594 { name => 'page_size',
595 desc => 'Count of call numbers to retrieve, default is 9',
598 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
600 { name => 'statuses',
601 desc => 'Array of statuses to filter copies by, optional and can be undef.',
603 { name => 'locations',
604 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
608 { desc => 'Call numbers with owning_lib and record fleshed',
614 sub new_books_by_item {
619 my $page_size = shift || 10;
620 my $page = shift || 1;
621 my $statuses = shift || [];
622 my $copy_locations = shift || [];
624 my $offset = $page_size * ($page - 1);
626 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
629 if ($ou && $ou ne '-') {
630 my $orgs = $_storage->request(
631 "open-ils.cstore.direct.actor.org_unit.search",
632 { shortname => $ou },
634 flesh_fields => { aou => [qw/children/] }
637 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
640 $logger->debug("Searching for records with new copies at orgs [".join(',',@ou_ids)."], based on $ou");
641 my $cns = $_storage->request(
642 "open-ils.cstore.json_query.atomic",
643 { select => { acn => ['record'],
644 acp => [{ aggregate => 1 => transform => max => column => create_date => alias => 'create_date'}]
646 from => { 'acn' => { 'acp' => { field => call_number => fkey => 'id' } } },
650 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
651 ((@$statuses) ? ( status => $statuses) : ()),
652 ((@$copy_locations) ? ( location => $copy_locations) : ())
654 '+acn' => { record => { '>' => 0 } },
656 order_by => { acp => { create_date => { transform => 'max', direction => 'desc' } } },
662 return [ map { $_->{record} } @$cns ];
664 __PACKAGE__->register_method(
665 method => 'new_books_by_item',
666 api_name => 'open-ils.supercat.new_book_list',
671 Returns the XML representation of the requested bibliographic record's holdings
675 { name => 'org_unit',
676 desc => 'The org unit shortname (or "-" or undef for global) to list',
678 { name => 'page_size',
679 desc => 'Count of records to retrieve, default is 10',
682 desc => 'The page of records to retrieve, calculated based on page_size. Starts at 1.',
684 { name => 'statuses',
685 desc => 'Array of statuses to filter copies by, optional and can be undef.',
687 { name => 'locations',
688 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
692 { desc => 'Record IDs',
701 return tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
703 __PACKAGE__->register_method(
704 method => 'general_browse',
705 api_name => 'open-ils.supercat.title.browse',
706 tag => 'tnf', subfield => 'a',
710 { desc => "Returns a list of the requested org-scoped record IDs held",
712 [ { name => 'value', desc => 'The target title', type => 'string' },
713 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
714 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
715 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
716 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
717 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
718 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
721 __PACKAGE__->register_method(
722 method => 'general_browse',
723 api_name => 'open-ils.supercat.author.browse',
724 tag => [qw/100 110 111/], subfield => 'a',
728 { desc => "Returns a list of the requested org-scoped record IDs held",
730 [ { name => 'value', desc => 'The target author', type => 'string' },
731 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
732 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
733 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
734 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
735 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
736 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
739 __PACKAGE__->register_method(
740 method => 'general_browse',
741 api_name => 'open-ils.supercat.subject.browse',
742 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
746 { desc => "Returns a list of the requested org-scoped record IDs held",
748 [ { name => 'value', desc => 'The target subject', type => 'string' },
749 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
750 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
751 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
752 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
753 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
754 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
757 __PACKAGE__->register_method(
758 method => 'general_browse',
759 api_name => 'open-ils.supercat.topic.browse',
760 tag => [qw/650 690/], subfield => 'a',
764 { desc => "Returns a list of the requested org-scoped record IDs held",
766 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
767 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
768 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
769 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
770 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
771 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
772 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
775 __PACKAGE__->register_method(
776 method => 'general_browse',
777 api_name => 'open-ils.supercat.series.browse',
778 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
782 { desc => "Returns a list of the requested org-scoped record IDs held",
784 [ { name => 'value', desc => 'The target series', type => 'string' },
785 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
786 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
787 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
788 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
789 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
790 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
800 my $subfield = shift;
803 my $page_size = shift || 9;
804 my $page = shift || 0;
805 my $statuses = shift || [];
806 my $copy_locations = shift || [];
808 my ($before_limit,$after_limit) = (0,0);
809 my ($before_offset,$after_offset) = (0,0);
812 $before_limit = $after_limit = int($page_size / 2);
813 $after_limit += 1 if ($page_size % 2);
815 $before_offset = $after_offset = int($page_size / 2);
816 $before_offset += 1 if ($page_size % 2);
817 $before_limit = $after_limit = $page_size;
820 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
823 if ($ou && $ou ne '-') {
824 my $orgs = $_storage->request(
825 "open-ils.cstore.direct.actor.org_unit.search",
826 { shortname => $ou },
828 flesh_fields => { aou => [qw/children/] }
831 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
834 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
839 my $before = $_storage->request(
840 "open-ils.cstore.json_query.atomic",
841 { select => { mfr => [qw/record value/] },
846 subfield => $subfield,
847 value => { '<' => lc($value) }
851 { select=> { acp => [ 'id' ] },
852 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
854 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
857 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
858 ((@$statuses) ? ( status => $statuses) : ()),
859 ((@$copy_locations) ? ( location => $copy_locations) : ())
866 { select=> { auri => [ 'id' ] },
867 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
869 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
870 '+auri' => { active => 't' }
877 order_by => { mfr => { value => 'desc' } },
878 limit => $before_limit,
879 offset => abs($page) * $page_size - $before_offset,
882 push @list, map { $_->{record} } reverse(@$before);
886 my $after = $_storage->request(
887 "open-ils.cstore.json_query.atomic",
888 { select => { mfr => [qw/record value/] },
893 subfield => $subfield,
894 value => { '>=' => lc($value) }
898 { select=> { acp => [ 'id' ] },
899 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
901 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
904 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
905 ((@$statuses) ? ( status => $statuses) : ()),
906 ((@$copy_locations) ? ( location => $copy_locations) : ())
913 { select=> { auri => [ 'id' ] },
914 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
916 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
917 '+auri' => { active => 't' }
924 order_by => { mfr => { value => 'asc' } },
925 limit => $after_limit,
926 offset => abs($page) * $page_size - $after_offset,
929 push @list, map { $_->{record} } @$after;
934 __PACKAGE__->register_method(
935 method => 'tag_sf_browse',
936 api_name => 'open-ils.supercat.tag.browse',
941 Returns a list of the requested org-scoped record IDs held
946 desc => 'The target MARC tag',
948 { name => 'subfield',
949 desc => 'The target MARC subfield',
952 desc => 'The target string',
954 { name => 'org_unit',
955 desc => 'The org unit shortname (or "-" or undef for global) to browse',
957 { name => 'page_size',
958 desc => 'Count of call numbers to retrieve, default is 9',
961 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
963 { name => 'statuses',
964 desc => 'Array of statuses to filter copies by, optional and can be undef.',
966 { name => 'locations',
967 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
971 { desc => 'Record IDs that have copies at the relevant org units',
976 sub grab_authority_browse_axes {
977 my ($self, $client, $full) = @_;
979 unless(scalar(keys(%authority_browse_axis_cache))) {
980 my $axes = new_editor->search_authority_browse_axis([
981 { code => { '<>' => undef } },
982 { flesh => 2, flesh_fields => { aba => ['fields'], acsaf => ['bib_fields','sub_entries'] } }
984 $authority_browse_axis_cache{$_->code} = $_ for (@$axes);
989 map { $authority_browse_axis_cache{$_} } sort keys %authority_browse_axis_cache
992 return [keys %authority_browse_axis_cache];
995 __PACKAGE__->register_method(
996 method => 'grab_authority_browse_axes',
997 api_name => 'open-ils.supercat.authority.browse_axis_list',
1001 { desc => "Returns a list of valid authority browse/startswith axes",
1003 { name => 'full', desc => 'Optional. If true, return array containing the full object for each axis, sorted by code. Otherwise just return an array of the codes.', type => 'number' }
1005 'return' => { desc => 'Axis codes or whole axes, see "full" param', type => 'array' }
1009 sub axis_authority_browse {
1014 $axis =~ s/^authority\.//;
1015 $axis =~ s/(\.refs)$//;
1018 return undef unless ( grep { /$axis/ } @{ grab_authority_browse_axes() } );
1021 for my $f (@{$authority_browse_axis_cache{$axis}->fields}) {
1022 push @tags, $f->tag;
1024 push @tags, $_->tag for @{$f->sub_entries};
1028 return authority_tag_sf_browse($self, $client, \@tags, 'a', @_); # XXX TODO figure out something more correct for the subfield param
1030 __PACKAGE__->register_method(
1031 method => 'axis_authority_browse',
1032 api_name => 'open-ils.supercat.authority.browse.by_axis',
1036 { desc => "Returns a list of the requested authority record IDs held",
1038 [ { name => 'axis', desc => 'The target axis', type => 'string' },
1039 { name => 'value', desc => 'The target value', type => 'string' },
1040 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1041 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1042 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1048 sub general_authority_browse {
1051 return authority_tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
1053 __PACKAGE__->register_method(
1054 method => 'general_authority_browse',
1055 api_name => 'open-ils.supercat.authority.title.browse',
1056 tag => ['130'], subfield => 'a',
1060 { desc => "Returns a list of the requested authority record IDs held",
1062 [ { name => 'value', desc => 'The target title', type => 'string' },
1063 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1064 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1065 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1068 __PACKAGE__->register_method(
1069 method => 'general_authority_browse',
1070 api_name => 'open-ils.supercat.authority.author.browse',
1071 tag => [qw/100 110 111/], subfield => 'a',
1075 { desc => "Returns a list of the requested authority record IDs held",
1077 [ { name => 'value', desc => 'The target author', type => 'string' },
1078 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1079 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1080 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1083 __PACKAGE__->register_method(
1084 method => 'general_authority_browse',
1085 api_name => 'open-ils.supercat.authority.subject.browse',
1086 tag => [qw/148 150 151 155/], subfield => 'a',
1090 { desc => "Returns a list of the requested authority record IDs held",
1092 [ { name => 'value', desc => 'The target subject', type => 'string' },
1093 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1094 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1095 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1098 __PACKAGE__->register_method(
1099 method => 'general_authority_browse',
1100 api_name => 'open-ils.supercat.authority.topic.browse',
1101 tag => ['150'], subfield => 'a',
1105 { desc => "Returns a list of the requested authority record IDs held",
1107 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1108 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1109 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1110 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1113 __PACKAGE__->register_method(
1114 method => 'general_authority_browse',
1115 api_name => 'open-ils.supercat.authority.title.refs.browse',
1116 tag => ['130'], subfield => 'a',
1120 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1122 [ { name => 'value', desc => 'The target title', type => 'string' },
1123 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1124 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1125 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1128 __PACKAGE__->register_method(
1129 method => 'general_authority_browse',
1130 api_name => 'open-ils.supercat.authority.author.refs.browse',
1131 tag => [qw/100 110 111/], subfield => 'a',
1135 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1137 [ { name => 'value', desc => 'The target author', type => 'string' },
1138 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1139 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1140 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1143 __PACKAGE__->register_method(
1144 method => 'general_authority_browse',
1145 api_name => 'open-ils.supercat.authority.subject.refs.browse',
1146 tag => [qw/148 150 151 155/], subfield => 'a',
1150 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1152 [ { name => 'value', desc => 'The target subject', type => 'string' },
1153 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1154 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1155 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1158 __PACKAGE__->register_method(
1159 method => 'general_authority_browse',
1160 api_name => 'open-ils.supercat.authority.topic.refs.browse',
1161 tag => ['150'], subfield => 'a',
1165 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1167 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1168 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1169 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1170 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1176 sub authority_tag_sf_browse {
1181 my $subfield = shift;
1183 my $page_size = shift || 9;
1184 my $page = shift || 0;
1186 # Match authority.full_rec normalization
1187 $value = naco_normalize($value, $subfield);
1189 my ($before_limit,$after_limit) = (0,0);
1190 my ($before_offset,$after_offset) = (0,0);
1193 $before_limit = $after_limit = int($page_size / 2);
1194 $after_limit += 1 if ($page_size % 2);
1196 $before_offset = $after_offset = int($page_size / 2);
1197 $before_offset += 1 if ($page_size % 2);
1198 $before_limit = $after_limit = $page_size;
1201 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1203 # .refs variant includes 4xx and 5xx variants for see / see also
1205 foreach my $tagname (@$tag) {
1206 push(@ref_tags, $tagname);
1207 if ($self->api_name =~ /\.refs\./) {
1208 push(@ref_tags, '4' . substr($tagname, 1, 2));
1209 push(@ref_tags, '5' . substr($tagname, 1, 2));
1215 my $before = $_storage->request(
1216 "open-ils.cstore.json_query.atomic",
1217 { select => { afr => [qw/record value/] },
1219 where => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
1220 order_by => { afr => { value => 'desc' } },
1221 limit => $before_limit,
1222 offset => abs($page) * $page_size - $before_offset,
1225 push @list, map { $_->{record} } reverse(@$before);
1229 my $after = $_storage->request(
1230 "open-ils.cstore.json_query.atomic",
1231 { select => { afr => [qw/record value/] },
1233 where => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
1234 order_by => { afr => { value => 'asc' } },
1235 limit => $after_limit,
1236 offset => abs($page) * $page_size - $after_offset,
1239 push @list, map { $_->{record} } @$after;
1242 # If we're not pulling in see/see also references, just return the raw list
1243 if ($self->api_name !~ /\.refs\./) {
1247 # Remove dupe record IDs that turn up due to 4xx and 5xx matches
1250 foreach my $record (@list) {
1251 next if exists $seen{$record};
1252 push @retlist, int($record);
1258 __PACKAGE__->register_method(
1259 method => 'authority_tag_sf_browse',
1260 api_name => 'open-ils.supercat.authority.tag.browse',
1264 { desc => <<" DESC",
1265 Returns a list of the requested authority record IDs held
1270 desc => 'The target Authority MARC tag',
1272 { name => 'subfield',
1273 desc => 'The target Authority MARC subfield',
1276 desc => 'The target string',
1278 { name => 'page_size',
1279 desc => 'Count of call numbers to retrieve, default is 9',
1282 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1286 { desc => 'Authority Record IDs that are near the target string',
1291 sub general_startwith {
1294 return tag_sf_startwith($self, $client, $self->{tag}, $self->{subfield}, @_);
1296 __PACKAGE__->register_method(
1297 method => 'general_startwith',
1298 api_name => 'open-ils.supercat.title.startwith',
1299 tag => 'tnf', subfield => 'a',
1303 { desc => "Returns a list of the requested org-scoped record IDs held",
1305 [ { name => 'value', desc => 'The target title', type => 'string' },
1306 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1307 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1308 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1309 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1310 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1311 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1314 __PACKAGE__->register_method(
1315 method => 'general_startwith',
1316 api_name => 'open-ils.supercat.author.startwith',
1317 tag => [qw/100 110 111/], subfield => 'a',
1321 { desc => "Returns a list of the requested org-scoped record IDs held",
1323 [ { name => 'value', desc => 'The target author', type => 'string' },
1324 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1325 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1326 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1327 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1328 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1329 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1332 __PACKAGE__->register_method(
1333 method => 'general_startwith',
1334 api_name => 'open-ils.supercat.subject.startwith',
1335 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
1339 { desc => "Returns a list of the requested org-scoped record IDs held",
1341 [ { name => 'value', desc => 'The target subject', type => 'string' },
1342 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1343 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1344 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1345 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1346 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1347 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1350 __PACKAGE__->register_method(
1351 method => 'general_startwith',
1352 api_name => 'open-ils.supercat.topic.startwith',
1353 tag => [qw/650 690/], subfield => 'a',
1357 { desc => "Returns a list of the requested org-scoped record IDs held",
1359 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1360 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1361 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1362 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1363 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1364 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1365 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1368 __PACKAGE__->register_method(
1369 method => 'general_startwith',
1370 api_name => 'open-ils.supercat.series.startwith',
1371 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
1375 { desc => "Returns a list of the requested org-scoped record IDs held",
1377 [ { name => 'value', desc => 'The target series', type => 'string' },
1378 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1379 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1380 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1381 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1382 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1383 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1388 sub tag_sf_startwith {
1393 my $subfield = shift;
1396 my $limit = shift || 10;
1397 my $page = shift || 0;
1398 my $statuses = shift || [];
1399 my $copy_locations = shift || [];
1401 my $offset = $limit * abs($page);
1402 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1405 if ($ou && $ou ne '-') {
1406 my $orgs = $_storage->request(
1407 "open-ils.cstore.direct.actor.org_unit.search",
1408 { shortname => $ou },
1410 flesh_fields => { aou => [qw/children/] }
1413 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1416 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
1421 my $before = $_storage->request(
1422 "open-ils.cstore.json_query.atomic",
1423 { select => { mfr => [qw/record value/] },
1428 subfield => $subfield,
1429 value => { '<' => lc($value) }
1433 { select=> { acp => [ 'id' ] },
1434 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
1436 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
1439 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
1440 ((@$statuses) ? ( status => $statuses) : ()),
1441 ((@$copy_locations) ? ( location => $copy_locations) : ())
1448 { select=> { auri => [ 'id' ] },
1449 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
1451 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
1452 '+auri' => { active => 't' }
1459 order_by => { mfr => { value => 'desc' } },
1464 push @list, map { $_->{record} } reverse(@$before);
1468 my $after = $_storage->request(
1469 "open-ils.cstore.json_query.atomic",
1470 { select => { mfr => [qw/record value/] },
1475 subfield => $subfield,
1476 value => { '>=' => lc($value) }
1480 { select=> { acp => [ 'id' ] },
1481 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
1483 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
1486 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
1487 ((@$statuses) ? ( status => $statuses) : ()),
1488 ((@$copy_locations) ? ( location => $copy_locations) : ())
1495 { select=> { auri => [ 'id' ] },
1496 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
1498 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
1499 '+auri' => { active => 't' }
1506 order_by => { mfr => { value => 'asc' } },
1511 push @list, map { $_->{record} } @$after;
1516 __PACKAGE__->register_method(
1517 method => 'tag_sf_startwith',
1518 api_name => 'open-ils.supercat.tag.startwith',
1522 { desc => <<" DESC",
1523 Returns a list of the requested org-scoped record IDs held
1528 desc => 'The target MARC tag',
1530 { name => 'subfield',
1531 desc => 'The target MARC subfield',
1534 desc => 'The target string',
1536 { name => 'org_unit',
1537 desc => 'The org unit shortname (or "-" or undef for global) to browse',
1539 { name => 'page_size',
1540 desc => 'Count of call numbers to retrieve, default is 9',
1543 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1545 { name => 'statuses',
1546 desc => 'Array of statuses to filter copies by, optional and can be undef.',
1548 { name => 'locations',
1549 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
1553 { desc => 'Record IDs that have copies at the relevant org units',
1558 sub axis_authority_startwith {
1563 $axis =~ s/^authority\.//;
1564 $axis =~ s/(\.refs)$//;
1567 return undef unless ( grep { /$axis/ } @{ grab_authority_browse_axes() } );
1570 for my $f (@{$authority_browse_axis_cache{$axis}->fields}) {
1571 push @tags, $f->tag;
1573 push @tags, $_->tag for @{$f->sub_entries};
1577 return authority_tag_sf_startwith($self, $client, \@tags, 'a', @_); # XXX TODO figure out something more correct for the subfield param
1579 __PACKAGE__->register_method(
1580 method => 'axis_authority_startwith',
1581 api_name => 'open-ils.supercat.authority.startwith.by_axis',
1585 { desc => "Returns a list of the requested authority record IDs held",
1587 [ { name => 'axis', desc => 'The target axis', type => 'string' },
1588 { name => 'value', desc => 'The target value', type => 'string' },
1589 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1590 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1591 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1597 sub general_authority_startwith {
1600 return authority_tag_sf_startwith($self, $client, $self->{tag}, $self->{subfield}, @_);
1602 __PACKAGE__->register_method(
1603 method => 'general_authority_startwith',
1604 api_name => 'open-ils.supercat.authority.title.startwith',
1605 tag => ['130'], subfield => 'a',
1609 { desc => "Returns a list of the requested authority record IDs held",
1611 [ { name => 'value', desc => 'The target title', type => 'string' },
1612 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1613 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1614 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1617 __PACKAGE__->register_method(
1618 method => 'general_authority_startwith',
1619 api_name => 'open-ils.supercat.authority.author.startwith',
1620 tag => [qw/100 110 111/], subfield => 'a',
1624 { desc => "Returns a list of the requested authority record IDs held",
1626 [ { name => 'value', desc => 'The target author', type => 'string' },
1627 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1628 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1629 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1632 __PACKAGE__->register_method(
1633 method => 'general_authority_startwith',
1634 api_name => 'open-ils.supercat.authority.subject.startwith',
1635 tag => [qw/148 150 151 155/], subfield => 'a',
1639 { desc => "Returns a list of the requested authority record IDs held",
1641 [ { name => 'value', desc => 'The target subject', type => 'string' },
1642 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1643 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1644 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1647 __PACKAGE__->register_method(
1648 method => 'general_authority_startwith',
1649 api_name => 'open-ils.supercat.authority.topic.startwith',
1650 tag => ['150'], subfield => 'a',
1654 { desc => "Returns a list of the requested authority record IDs held",
1656 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1657 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1658 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1659 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1662 __PACKAGE__->register_method(
1663 method => 'general_authority_startwith',
1664 api_name => 'open-ils.supercat.authority.title.refs.startwith',
1665 tag => ['130'], subfield => 'a',
1669 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1671 [ { name => 'value', desc => 'The target title', type => 'string' },
1672 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1673 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1674 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1677 __PACKAGE__->register_method(
1678 method => 'general_authority_startwith',
1679 api_name => 'open-ils.supercat.authority.author.refs.startwith',
1680 tag => [qw/100 110 111/], subfield => 'a',
1684 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1686 [ { name => 'value', desc => 'The target author', type => 'string' },
1687 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1688 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1689 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1692 __PACKAGE__->register_method(
1693 method => 'general_authority_startwith',
1694 api_name => 'open-ils.supercat.authority.subject.refs.startwith',
1695 tag => [qw/148 150 151 155/], subfield => 'a',
1699 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1701 [ { name => 'value', desc => 'The target subject', type => 'string' },
1702 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1703 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1704 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1707 __PACKAGE__->register_method(
1708 method => 'general_authority_startwith',
1709 api_name => 'open-ils.supercat.authority.topic.refs.startwith',
1710 tag => ['150'], subfield => 'a',
1714 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1716 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1717 { name => 'page_size', desc => 'Count of records to retrieve, default is 10', type => 'number' },
1718 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1719 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1725 sub authority_tag_sf_startwith {
1730 my $subfield = shift;
1733 my $limit = shift || 10;
1734 my $page = shift || 0;
1736 # Match authority.full_rec normalization
1737 $value = naco_normalize($value, $subfield);
1739 my $ref_limit = $limit;
1740 my $offset = $limit * abs($page);
1741 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1744 # .refs variant includes 4xx and 5xx variants for see / see also
1745 foreach my $tagname (@$tag) {
1746 push(@ref_tags, $tagname);
1747 if ($self->api_name =~ /\.refs\./) {
1748 push(@ref_tags, '4' . substr($tagname, 1, 2));
1749 push(@ref_tags, '5' . substr($tagname, 1, 2));
1756 # Don't skip the first actual page of results in descending order
1757 $offset = $offset - $limit;
1759 my $before = $_storage->request(
1760 "open-ils.cstore.json_query.atomic",
1761 { select => { afr => [qw/record value/] },
1763 where => { tag => \@ref_tags, subfield => $subfield, value => { '<' => $value } },
1764 order_by => { afr => { value => 'desc' } },
1765 limit => $ref_limit,
1769 push @list, map { $_->{record} } reverse(@$before);
1773 my $after = $_storage->request(
1774 "open-ils.cstore.json_query.atomic",
1775 { select => { afr => [qw/record value/] },
1777 where => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => $value } },
1778 order_by => { afr => { value => 'asc' } },
1779 limit => $ref_limit,
1783 push @list, map { $_->{record} } @$after;
1786 # If we're not pulling in see/see also references, just return the raw list
1787 if ($self->api_name !~ /\.refs\./) {
1791 # Remove dupe record IDs that turn up due to 4xx and 5xx matches
1794 foreach my $record (@list) {
1795 next if exists $seen{$record};
1796 push @retlist, int($record);
1802 __PACKAGE__->register_method(
1803 method => 'authority_tag_sf_startwith',
1804 api_name => 'open-ils.supercat.authority.tag.startwith',
1808 { desc => <<" DESC",
1809 Returns a list of the requested authority record IDs held
1814 desc => 'The target Authority MARC tag',
1816 { name => 'subfield',
1817 desc => 'The target Authority MARC subfield',
1820 desc => 'The target string',
1822 { name => 'page_size',
1823 desc => 'Count of call numbers to retrieve, default is 10',
1826 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1830 { desc => 'Authority Record IDs that are near the target string',
1836 sub holding_data_formats {
1839 namespace_uri => 'http://www.loc.gov/MARC21/slim',
1840 docs => 'http://www.loc.gov/marcxml/',
1841 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
1845 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acn.formats', api_level => 1 );
1846 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acp.formats', api_level => 1 );
1847 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.auri.formats', api_level => 1 );
1850 __PACKAGE__->register_method(
1851 method => 'retrieve_uri',
1852 api_name => 'open-ils.supercat.auri.marcxml.retrieve',
1856 { desc => <<" DESC",
1857 Returns a fleshed call number object
1862 desc => 'An OpenILS asset::uri id',
1866 { desc => 'fleshed uri',
1874 my $args = shift || {};
1876 return OpenILS::Application::SuperCat::unAPI
1877 ->new(OpenSRF::AppSession
1878 ->create( 'open-ils.cstore' )
1880 "open-ils.cstore.direct.asset.uri.retrieve",
1884 auri => [qw/call_number_maps/],
1885 auricnm => [qw/call_number/],
1886 acn => [qw/owning_lib record prefix suffix/],
1893 __PACKAGE__->register_method(
1894 method => 'retrieve_copy',
1895 api_name => 'open-ils.supercat.acp.marcxml.retrieve',
1899 { desc => <<" DESC",
1900 Returns a fleshed call number object
1905 desc => 'An OpenILS asset::copy id',
1909 { desc => 'fleshed copy',
1917 my $args = shift || {};
1919 return OpenILS::Application::SuperCat::unAPI
1920 ->new(OpenSRF::AppSession
1921 ->create( 'open-ils.cstore' )
1923 "open-ils.cstore.direct.asset.copy.retrieve",
1927 acn => [qw/owning_lib record prefix suffix/],
1928 acp => [qw/call_number location status circ_lib stat_cat_entries notes parts/],
1935 __PACKAGE__->register_method(
1936 method => 'retrieve_callnumber',
1937 api_name => 'open-ils.supercat.acn.marcxml.retrieve',
1942 { desc => <<" DESC",
1943 Returns a fleshed call number object
1948 desc => 'An OpenILS asset::call_number id',
1952 { desc => 'call number with copies',
1956 sub retrieve_callnumber {
1960 my $args = shift || {};
1962 return OpenILS::Application::SuperCat::unAPI
1963 ->new(OpenSRF::AppSession
1964 ->create( 'open-ils.cstore' )
1966 "open-ils.cstore.direct.asset.call_number.retrieve",
1970 acn => [qw/owning_lib record copies uri_maps prefix suffix/],
1971 auricnm => [qw/uri/],
1972 acp => [qw/location status circ_lib stat_cat_entries notes parts/],
1980 __PACKAGE__->register_method(
1981 method => 'basic_record_holdings',
1982 api_name => 'open-ils.supercat.record.basic_holdings.retrieve',
1987 { desc => <<" DESC",
1988 Returns a basic hash representation of the requested bibliographic record's holdings
1993 desc => 'An OpenILS biblio::record_entry id',
1997 { desc => 'Hash of bib record holdings hierarchy (call numbers and copies)',
2001 sub basic_record_holdings {
2007 # holdings hold an array of call numbers, which hold an array of copies
2008 # holdings => [ label: { library, [ copies: { barcode, location, status, circ_lib } ] } ]
2011 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2013 my $tree = $_storage->request(
2014 "open-ils.cstore.direct.biblio.record_entry.retrieve",
2018 bre => [qw/call_numbers/],
2019 acn => [qw/copies owning_lib prefix suffix/],
2020 acp => [qw/location status circ_lib parts/],
2025 my $o_search = { shortname => uc($ou) };
2026 if (!$ou || $ou eq '-') {
2027 $o_search = { parent_ou => undef };
2030 my $orgs = $_storage->request(
2031 "open-ils.cstore.direct.actor.org_unit.search",
2034 flesh_fields => { aou => [qw/children/] }
2038 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
2040 $logger->debug("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
2042 for my $cn (@{$tree->call_numbers}) {
2043 next unless ( $cn->deleted eq 'f' || $cn->deleted == 0 );
2046 for my $c (@{$cn->copies}) {
2047 next unless grep {$c->circ_lib->id == $_} @ou_ids;
2048 next unless ( $c->deleted eq 'f' || $c->deleted == 0 );
2054 $holdings{$cn->label}{'owning_lib'} = $cn->owning_lib->shortname;
2056 for my $cp (@{$cn->copies}) {
2058 next unless grep { $cp->circ_lib->id == $_ } @ou_ids;
2059 next unless ( $cp->deleted eq 'f' || $cp->deleted == 0 );
2061 push @{$holdings{$cn->label}{'copies'}}, {
2062 barcode => $cp->barcode,
2063 status => $cp->status->name,
2064 location => $cp->location->name,
2065 circlib => $cp->circ_lib->shortname
2074 #__PACKAGE__->register_method(
2075 # method => 'new_record_holdings',
2076 # api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
2081 # { desc => <<" DESC",
2082 #Returns the XML representation of the requested bibliographic record's holdings
2086 # { name => 'bibId',
2087 # desc => 'An OpenILS biblio::record_entry id',
2088 # type => 'number' },
2091 # { desc => 'Stream of bib record holdings hierarchy in XML',
2092 # type => 'string' }
2097 sub new_record_holdings {
2106 $paging = [-1,0] if (!$paging or !ref($paging) or @$paging == 0);
2107 my $limit = $$paging[0];
2108 my $offset = $$paging[1] || 0;
2110 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2111 my $_search = OpenSRF::AppSession->create( 'open-ils.search' );
2113 my $o_search = { shortname => uc($ou) };
2114 if (!$ou || $ou eq '-') {
2115 $o_search = { parent_ou => undef };
2118 my $one_org = $_storage->request(
2119 "open-ils.cstore.direct.actor.org_unit.search",
2123 my $count_req = $_search->request('open-ils.search.biblio.record.copy_count' => $one_org->id => $bib);
2124 my $staff_count_req = $_search->request('open-ils.search.biblio.record.copy_count.staff' => $one_org->id => $bib);
2126 my $orgs = $_storage->request(
2127 'open-ils.cstore.json_query.atomic',
2128 { from => [ 'actor.org_unit_descendants', defined($depth) ? ( $one_org->id, $depth ) : ( $one_org->id ) ] }
2132 my @ou_ids = map { $_->{id} } @$orgs;
2134 $logger->info("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
2136 my %subselect = ( '-or' => [
2137 { owning_lib => \@ou_ids },
2141 call_number => { '=' => {'+acn'=>'id'} },
2143 circ_lib => \@ou_ids
2149 # we are dealing with -full or -uris, so we need to flesh things out
2152 # either way we're going to need uris
2153 # get all the uris up the tree (see also ba47ecc6196)
2155 my $uri_orgs = $_storage->request(
2156 'open-ils.cstore.json_query.atomic',
2157 { from => [ 'actor.org_unit_ancestors', $one_org->id ] }
2160 my @uri_ou_ids = map { $_->{id} } @$uri_orgs;
2162 # we have a -uris, just get the uris
2165 owning_lib => \@uri_ou_ids,
2167 from => { auricnm => 'auri' },
2169 call_number => { '=' => {'+acn'=>'id'} },
2170 '+auri' => { active => 't' }
2174 # we have a -full, get all the things
2175 } elsif ($flesh == 1) {
2176 %subselect = ( '-or' => [
2177 { owning_lib => \@ou_ids },
2181 call_number => { '=' => {'+acn'=>'id'} },
2183 circ_lib => \@ou_ids
2188 { owning_lib => \@uri_ou_ids },
2190 from => { auricnm => 'auri' },
2192 call_number => { '=' => {'+acn'=>'id'} },
2193 '+auri' => { active => 't' }
2201 my $cns = $_storage->request(
2202 "open-ils.cstore.direct.asset.call_number.search.atomic",
2209 acn => [qw/copies owning_lib uri_maps prefix suffix/],
2210 auricnm => [qw/uri/],
2211 acp => [qw/circ_lib location status stat_cat_entries notes parts/],
2212 asce => [qw/stat_cat/],
2214 ( $limit > -1 ? ( limit => $limit ) : () ),
2215 ( $offset ? ( offset => $offset ) : () ),
2216 order_by => { acn => { label_sortkey => {} } }
2220 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
2224 $client->respond("<holdings xmlns='http://open-ils.org/spec/holdings/v1'><counts>\n");
2226 my $copy_counts = $count_req->gather(1);
2227 my $staff_copy_counts = $staff_count_req->gather(1);
2229 for my $c (@$copy_counts) {
2230 $$c{transcendant} ||= 0;
2231 my $out = "<count type='public'";
2232 $out .= " $_='$$c{$_}'" for (qw/count available unshadow transcendant org_unit depth/);
2233 $client->respond("$out/>\n")
2236 for my $c (@$staff_copy_counts) {
2237 $$c{transcendant} ||= 0;
2238 my $out = "<count type='staff'";
2239 $out .= " $_='$$c{$_}'" for (qw/count available unshadow transcendant org_unit depth/);
2240 $client->respond("$out/>\n")
2243 $client->respond("</counts><volumes>\n");
2245 for my $cn (@$cns) {
2246 next unless (@{$cn->copies} > 0 or (ref($cn->uri_maps) and @{$cn->uri_maps}));
2248 # We don't want O:A:S:unAPI::acn to return the record, we've got that already
2249 # In the context of BibTemplate, copies aren't necessary because we pull those
2250 # in a separate call
2252 OpenILS::Application::SuperCat::unAPI::acn
2254 ->as_xml( {no_record => 1, no_copies => ($flesh ? 0 : 1)} )
2258 $client->respond("</volumes><subscriptions>\n");
2260 $logger->info("Searching for serial holdings at orgs [".join(',',@ou_ids)."], based on $ou");
2262 %subselect = ( '-or' => [
2263 { owning_lib => \@ou_ids },
2266 where => { holding_lib => \@ou_ids }
2271 my $ssubs = $_storage->request(
2272 "open-ils.cstore.direct.serial.subscription.search.atomic",
2273 { record_entry => $bib,
2278 ssub => [qw/distributions issuances scaps owning_lib/],
2279 sdist => [qw/basic_summary supplement_summary index_summary streams holding_lib/],
2280 sstr => [qw/items/],
2281 sitem => [qw/notes unit/],
2282 sunit => [qw/notes location status circ_lib stat_cat_entries call_number/],
2283 acn => [qw/owning_lib prefix suffix/],
2285 ( $limit > -1 ? ( limit => $limit ) : () ),
2286 ( $offset ? ( offset => $offset ) : () ),
2298 date_expected => {},
2305 for my $ssub (@$ssubs) {
2306 next unless (@{$ssub->distributions} or @{$ssub->issuances} or @{$ssub->scaps});
2308 # We don't want O:A:S:unAPI::ssub to return the record, we've got that already
2309 # In the context of BibTemplate, copies aren't necessary because we pull those
2310 # in a separate call
2312 OpenILS::Application::SuperCat::unAPI::ssub
2314 ->as_xml( {no_record => 1, no_items => ($flesh ? 0 : 1)} )
2319 return "</subscriptions></holdings>\n";
2321 __PACKAGE__->register_method(
2322 method => 'new_record_holdings',
2323 api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
2328 { desc => <<" DESC",
2329 Returns the XML representation of the requested bibliographic record's holdings
2334 desc => 'An OpenILS biblio::record_entry ID',
2336 { name => 'orgUnit',
2337 desc => 'An OpenILS actor::org_unit short name that limits the scope of returned holdings',
2340 desc => 'An OpenILS actor::org_unit_type depththat limits the scope of returned holdings',
2342 { name => 'hideCopies',
2343 desc => 'Flag that prevents the inclusion of copies in the returned holdings',
2344 type => 'boolean' },
2346 desc => 'Arry of limit and offset for holdings paging',
2350 { desc => 'Stream of bib record holdings hierarchy in XML',
2360 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2362 my $recs = $_storage->request(
2363 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2364 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2367 return undef unless (@$recs);
2369 return ($self->method_lookup( 'open-ils.supercat.record.holdings_xml.retrieve')->run( $recs->[0]->record ))[0];
2371 __PACKAGE__->register_method(
2372 method => 'isbn_holdings',
2373 api_name => 'open-ils.supercat.isbn.holdings_xml.retrieve',
2377 { desc => <<" DESC",
2378 Returns the XML representation of the requested bibliographic record's holdings
2387 { desc => 'The bib record holdings hierarchy in XML',
2395 return '' unless $text;
2396 $text =~ s/&/&/gsom;
2397 $text =~ s/</</gsom;
2398 $text =~ s/>/>/gsom;
2399 $text =~ s/"/"/gsom;
2400 $text =~ s/'/'/gsom;
2404 sub recent_changes {
2407 my $when = shift || '1-01-01';
2410 my $type = 'biblio';
2413 if ($self->api_name =~ /authority/o) {
2414 $type = 'authority';
2418 my $axis = 'create_date';
2419 $axis = 'edit_date' if ($self->api_name =~ /edit/o);
2421 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2423 return $_storage->request(
2424 "open-ils.cstore.direct.$type.record_entry.id_list.atomic",
2425 { $axis => { ">" => $when }, id => { '>' => 0 }, deleted => 'f', active => 't' },
2426 { order_by => { $hint => "$axis desc" }, limit => $limit }
2430 for my $t ( qw/biblio authority/ ) {
2431 for my $a ( qw/import edit/ ) {
2433 __PACKAGE__->register_method(
2434 method => 'recent_changes',
2435 api_name => "open-ils.supercat.$t.record.$a.recent",
2439 { desc => "Returns a list of recently ${a}ed $t records",
2443 desc => "Date to start looking for ${a}ed records",
2444 default => '1-01-01',
2448 desc => "Maximum count to retrieve",
2452 { desc => "An id list of $t records",
2460 sub retrieve_authority_marcxml {
2465 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2467 my $record = $_storage->request( 'open-ils.cstore.direct.authority.record_entry.retrieve' => $rid )->gather(1);
2468 return $U->entityize( $record->marc ) if ($record);
2472 __PACKAGE__->register_method(
2473 method => 'retrieve_authority_marcxml',
2474 api_name => 'open-ils.supercat.authority.marcxml.retrieve',
2478 { desc => <<" DESC",
2479 Returns the MARCXML representation of the requested authority record
2483 { name => 'authorityId',
2484 desc => 'An OpenILS authority::record_entry id',
2488 { desc => 'The authority record in MARCXML',
2493 sub retrieve_record_marcxml {
2498 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2500 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $rid )->gather(1);
2501 return $U->entityize( $record->marc ) if ($record);
2505 __PACKAGE__->register_method(
2506 method => 'retrieve_record_marcxml',
2507 api_name => 'open-ils.supercat.record.marcxml.retrieve',
2511 { desc => <<" DESC",
2512 Returns the MARCXML representation of the requested bibliographic record
2517 desc => 'An OpenILS biblio::record_entry id',
2521 { desc => 'The bib record in MARCXML',
2526 sub retrieve_isbn_marcxml {
2531 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2533 my $recs = $_storage->request(
2534 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2535 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2538 return undef unless (@$recs);
2540 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
2541 return $U->entityize( $record->marc ) if ($record);
2545 __PACKAGE__->register_method(
2546 method => 'retrieve_isbn_marcxml',
2547 api_name => 'open-ils.supercat.isbn.marcxml.retrieve',
2551 { desc => <<" DESC",
2552 Returns the MARCXML representation of the requested ISBN
2557 desc => 'An ... um ... ISBN',
2561 { desc => 'The bib record in MARCXML',
2566 sub retrieve_record_transform {
2571 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
2573 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2574 #$_storage->connect;
2576 my $record = $_storage->request(
2577 'open-ils.cstore.direct.biblio.record_entry.retrieve',
2581 return undef unless ($record);
2583 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
2586 sub retrieve_isbn_transform {
2591 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2593 my $recs = $_storage->request(
2594 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2595 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2598 return undef unless (@$recs);
2600 (my $transform = $self->api_name) =~ s/^.+isbn\.([^\.]+)\.retrieve$/$1/o;
2602 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
2604 return undef unless ($record);
2606 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
2609 sub retrieve_record_objects {
2614 my $type = 'biblio';
2616 if ($self->api_name =~ /authority/) {
2617 $type = 'authority';
2620 $ids = [$ids] unless (ref $ids);
2621 $ids = [grep {$_} @$ids];
2623 return [] unless (@$ids);
2625 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2626 return $_storage->request("open-ils.cstore.direct.$type.record_entry.search.atomic" => { id => [grep {$_} @$ids] })->gather(1);
2628 __PACKAGE__->register_method(
2629 method => 'retrieve_record_objects',
2630 api_name => 'open-ils.supercat.record.object.retrieve',
2634 { desc => <<" DESC",
2635 Returns the Fieldmapper object representation of the requested bibliographic records
2640 desc => 'OpenILS biblio::record_entry ids',
2644 { desc => 'The bib records',
2649 __PACKAGE__->register_method(
2650 method => 'retrieve_record_objects',
2651 api_name => 'open-ils.supercat.authority.object.retrieve',
2655 { desc => <<" DESC",
2656 Returns the Fieldmapper object representation of the requested authority records
2660 { name => 'authIds',
2661 desc => 'OpenILS authority::record_entry ids',
2665 { desc => 'The authority records',
2670 sub retrieve_isbn_object {
2675 return undef unless ($isbn);
2677 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2678 my $recs = $_storage->request(
2679 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2680 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2683 return undef unless (@$recs);
2685 return $_storage->request(
2686 'open-ils.cstore.direct.biblio.record_entry.search.atomic',
2687 { id => $recs->[0]->record }
2690 __PACKAGE__->register_method(
2691 method => 'retrieve_isbn_object',
2692 api_name => 'open-ils.supercat.isbn.object.retrieve',
2696 { desc => <<" DESC",
2697 Returns the Fieldmapper object representation of the requested bibliographic record
2706 { desc => 'The bib record',
2713 sub retrieve_metarecord_mods {
2718 my $_storage = OpenSRF::AppSession->connect( 'open-ils.cstore' );
2720 # Get the metarecord in question
2723 'open-ils.cstore.direct.metabib.metarecord.retrieve' => $rid
2726 # Now get the map of all bib records for the metarecord
2729 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2730 {metarecord => $rid}
2733 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
2735 # and retrieve the lead (master) record as MODS
2737 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
2738 ->run($mr->master_record);
2739 my $master_mods = $_parser->parse_string($master)->documentElement;
2740 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods" );
2741 $master_mods->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2743 # ... and a MODS clone to populate, with guts removed.
2744 my $mods = $_parser->parse_string($master)->documentElement;
2745 $mods->setNamespace( "http://www.loc.gov/mods/", "mods" ); # modsCollection element
2746 $mods->setNamespace('http://www.loc.gov/mods/', undef, 1);
2747 ($mods) = $mods->findnodes('//mods:mods');
2748 #$mods->setNamespace( "http://www.loc.gov/mods/", "mods" ); # mods element
2749 $mods->removeChildNodes;
2750 $mods->setNamespace('http://www.loc.gov/mods/', undef, 1);
2752 # Add the metarecord ID as a (locally defined) info URI
2753 my $recordInfo = $mods
2755 ->createElement("recordInfo");
2757 my $recordIdentifier = $mods
2759 ->createElement("recordIdentifier");
2761 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
2766 $recordIdentifier->appendTextNode(
2767 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
2770 $recordInfo->appendChild($recordIdentifier);
2771 $mods->appendChild($recordInfo);
2773 # Grab the title, author and ISBN for the master record and populate the metarecord
2774 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
2777 $title->setNamespace( "http://www.loc.gov/mods/", "mods" );
2778 $title->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2779 $title = $mods->ownerDocument->importNode($title);
2780 $mods->appendChild($title);
2783 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
2785 $author->setNamespace( "http://www.loc.gov/mods/", "mods" );
2786 $author->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2787 $author = $mods->ownerDocument->importNode($author);
2788 $mods->appendChild($author);
2791 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
2793 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods" );
2794 $isbn->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2795 $isbn = $mods->ownerDocument->importNode($isbn);
2796 $mods->appendChild($isbn);
2799 # ... and loop over the constituent records
2800 for my $map ( @$recs ) {
2804 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
2805 ->run($map->source);
2807 my $part_mods = $_parser->parse_string($rec);
2808 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods" );
2809 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2810 ($part_mods) = $part_mods->findnodes('//mods:mods');
2812 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
2813 $node->setNamespace( "http://www.loc.gov/mods/", "mods" );
2814 $node->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2815 $node = $mods->ownerDocument->importNode($node);
2816 $mods->appendChild( $node );
2819 my $relatedItem = $mods
2821 ->createElement("relatedItem");
2823 $relatedItem->setAttribute( type => 'constituent' );
2825 my $identifier = $mods
2827 ->createElement("identifier");
2829 $identifier->setAttribute( type => 'uri' );
2831 my $subRecordInfo = $mods
2833 ->createElement("recordInfo");
2835 my $subRecordIdentifier = $mods
2837 ->createElement("recordIdentifier");
2839 my $subid = $map->source;
2840 $subRecordIdentifier->appendTextNode(
2841 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
2846 $subRecordInfo->appendChild($subRecordIdentifier);
2848 $relatedItem->appendChild( $subRecordInfo );
2850 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
2851 $tor->setNamespace( "http://www.loc.gov/mods/", "mods" );
2852 $tor->setNamespace( "http://www.loc.gov/mods/", undef, 1 ) if ($tor);
2853 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
2854 $relatedItem->appendChild($tor) if ($tor);
2856 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
2857 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods" );
2858 $part_isbn->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2859 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
2860 $relatedItem->appendChild( $part_isbn );
2863 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
2867 $mods->appendChild( $relatedItem );
2871 $_storage->disconnect;
2873 return $U->entityize($mods->toString);
2876 __PACKAGE__->register_method(
2877 method => 'retrieve_metarecord_mods',
2878 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
2882 { desc => <<" DESC",
2883 Returns the MODS representation of the requested metarecord
2887 { name => 'metarecordId',
2888 desc => 'An OpenILS metabib::metarecord id',
2892 { desc => 'The metarecord in MODS',
2897 sub list_metarecord_formats {
2900 { namespace_uri => 'http://www.loc.gov/mods/',
2901 docs => 'http://www.loc.gov/mods/',
2902 schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
2907 for my $type ( keys %metarecord_xslt ) {
2910 { namespace_uri => $metarecord_xslt{$type}{namespace_uri},
2911 docs => $metarecord_xslt{$type}{docs},
2912 schema_location => $metarecord_xslt{$type}{schema_location},
2919 __PACKAGE__->register_method(
2920 method => 'list_metarecord_formats',
2921 api_name => 'open-ils.supercat.metarecord.formats',
2925 { desc => <<" DESC",
2926 Returns the list of valid metarecord formats that supercat understands.
2929 { desc => 'The format list',
2935 sub list_authority_formats {
2938 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
2939 docs => 'http://www.loc.gov/marcxml/',
2940 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
2945 # for my $type ( keys %record_xslt ) {
2948 # { namespace_uri => $record_xslt{$type}{namespace_uri},
2949 # docs => $record_xslt{$type}{docs},
2950 # schema_location => $record_xslt{$type}{schema_location},
2957 __PACKAGE__->register_method(
2958 method => 'list_authority_formats',
2959 api_name => 'open-ils.supercat.authority.formats',
2963 { desc => <<" DESC",
2964 Returns the list of valid authority formats that supercat understands.
2967 { desc => 'The format list',
2972 sub list_record_formats {
2975 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
2976 docs => 'http://www.loc.gov/marcxml/',
2977 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
2982 for my $type ( keys %record_xslt ) {
2985 { namespace_uri => $record_xslt{$type}{namespace_uri},
2986 docs => $record_xslt{$type}{docs},
2987 schema_location => $record_xslt{$type}{schema_location},
2994 __PACKAGE__->register_method(
2995 method => 'list_record_formats',
2996 api_name => 'open-ils.supercat.record.formats',
3000 { desc => <<" DESC",
3001 Returns the list of valid record formats that supercat understands.
3004 { desc => 'The format list',
3008 __PACKAGE__->register_method(
3009 method => 'list_record_formats',
3010 api_name => 'open-ils.supercat.isbn.formats',
3014 { desc => <<" DESC",
3015 Returns the list of valid record formats that supercat understands.
3018 { desc => 'The format list',
3031 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
3032 unless (length($isbn) >= 10);
3034 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
3036 # Create a storage session, since we'll be making muliple requests.
3039 # Find the record that has that ISBN.
3040 my $bibrec = $_storage->request(
3041 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
3042 { tag => '020', subfield => 'a', value => { like => lc($isbn).'%'} }
3045 # Go away if we don't have one.
3046 return {} unless (@$bibrec);
3048 # Find the metarecord for that bib record.
3049 my $mr = $_storage->request(
3050 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
3051 {source => $bibrec->[0]->record}
3054 # Find the other records for that metarecord.
3055 my $records = $_storage->request(
3056 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
3057 {metarecord => $mr->[0]->metarecord}
3060 # Just to be safe. There's currently no unique constraint on sources...
3061 my %unique_recs = map { ($_->source, 1) } @$records;
3062 my @rec_list = sort keys %unique_recs;
3064 # And now fetch the ISBNs for thos records.
3068 'open-ils.cstore.direct.metabib.full_rec.search',
3069 { tag => '020', subfield => 'a', record => $_ }
3070 )->gather(1) for (@rec_list);
3072 # We're done with the storage server session.
3073 $_storage->disconnect;
3075 # Return the oISBN data structure. This will be XMLized at a higher layer.
3077 { metarecord => $mr->[0]->metarecord,
3078 record_list => { map { $_ ? ($_->record, $_->value) : () } @$recs } };
3081 __PACKAGE__->register_method(
3083 api_name => 'open-ils.supercat.oisbn',
3087 { desc => <<" DESC",
3088 Returns the ISBN list for the metarecord of the requested isbn
3093 desc => 'An ISBN. Duh.',
3097 { desc => 'record to isbn map',
3102 sub return_bib_search_aliases {
3105 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
3107 my $cmsa = $_storage->request(
3108 'open-ils.cstore.direct.config.metabib_search_alias.search.atomic',
3109 { alias => { '!=' => undef } }
3113 if ($_->alias =~ /\./) {
3114 my ($qualifier, $name) = $_->alias =~ m/^(.+?)\.(.+)$/;
3115 $aliases{$qualifier}{$name}{'index'} = $_->alias;
3116 # We will add a 'title' property in a subsequent schema
3117 $aliases{$qualifier}{$name}{'title'} = $name;
3119 # au/kw/se/su/ti go into the default 'eg' qualifier
3120 $aliases{'eg'}{$_->alias}{'index'} = $_->alias;
3121 $aliases{'eg'}{$_->alias}{'title'} = $_->alias;
3128 __PACKAGE__->register_method(
3129 method => 'return_bib_search_aliases',
3130 api_name => 'open-ils.supercat.biblio.search_aliases',
3134 { desc => <<" DESC",
3135 Returns the set of qualified search aliases in the system
3139 { desc => 'Hash of qualified search aliases',
3145 package OpenILS::Application::SuperCat::unAPI;
3146 use base qw/OpenILS::Application::SuperCat/;
3149 die "dummy superclass, use a real class";
3155 return unless ($obj);
3157 $class = ref($class) || $class;
3159 if ($class eq __PACKAGE__) {
3160 return unless (ref($obj));
3161 $class .= '::' . $obj->json_hint;
3164 return bless { obj => $obj } => $class;
3169 return $self->{obj};
3172 package OpenILS::Application::SuperCat::unAPI::auri;
3173 use base qw/OpenILS::Application::SuperCat::unAPI/;
3179 my $xml = ' <uri xmlns="http://open-ils.org/spec/holdings/v1" ';
3180 $xml .= 'id="tag:open-ils.org:asset-uri/' . $self->obj->id . '" ';
3181 $xml .= 'use_restriction="' . $self->escape( $self->obj->use_restriction ) . '" ';
3182 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
3183 $xml .= 'href="' . $self->escape( $self->obj->href ) . '">';
3185 if (!$args->{no_volumes}) {
3186 if (ref($self->obj->call_number_maps) && @{ $self->obj->call_number_maps }) {
3187 $xml .= " <volumes>\n" . join(
3190 OpenILS::Application::SuperCat::unAPI
3191 ->new( $_->call_number )
3192 ->as_xml({ %$args, no_uris=>1, no_copies=>1 })
3193 } @{ $self->obj->call_number_maps }
3194 ) . " </volumes>\n";
3197 $xml .= " <volumes/>\n";
3201 $xml .= " </uri>\n";
3206 package OpenILS::Application::SuperCat::unAPI::acn;
3207 use base qw/OpenILS::Application::SuperCat::unAPI/;
3213 my $xml = ' <volume xmlns="http://open-ils.org/spec/holdings/v1" ';
3215 $xml .= 'id="tag:open-ils.org:asset-call_number/' . $self->obj->id . '" ';
3216 $xml .= 'lib="' . $self->escape( $self->obj->owning_lib->shortname ) . '" ';
3217 $xml .= 'opac_visible="' . $self->obj->owning_lib->opac_visible . '" ';
3218 $xml .= 'deleted="' . $self->obj->deleted . '" ';
3219 $xml .= 'label="' . $self->escape( $self->obj->label ) . '">';
3222 if (!$args->{no_copies}) {
3223 if (ref($self->obj->copies) && @{ $self->obj->copies }) {
3224 $xml .= " <copies>\n" . join(
3227 OpenILS::Application::SuperCat::unAPI
3229 ->as_xml({ %$args, no_volume=>1 })
3230 } @{ $self->obj->copies }
3234 $xml .= " <copies/>\n";
3238 if (!$args->{no_uris}) {
3239 if (ref($self->obj->uri_maps) && @{ $self->obj->uri_maps }) {
3240 $xml .= " <uris>\n" . join(
3243 OpenILS::Application::SuperCat::unAPI
3245 ->as_xml({ %$args, no_volumes=>1 })
3246 } @{ $self->obj->uri_maps }
3250 $xml .= " <uris/>\n";
3255 $xml .= ' <prefix ';
3256 $xml .= 'ident="' . $self->obj->prefix->id . '" ';
3257 $xml .= 'id="tag:open-ils.org:asset-call_number_prefix/' . $self->obj->prefix->id . '" ';
3258 $xml .= 'label_sortkey="'.$self->escape( $self->obj->prefix->label_sortkey ) .'">';
3259 $xml .= $self->escape( $self->obj->prefix->label ) .'</prefix>';
3262 $xml .= ' <suffix ';
3263 $xml .= 'ident="' . $self->obj->suffix->id . '" ';
3264 $xml .= 'id="tag:open-ils.org:asset-call_number_suffix/' . $self->obj->suffix->id . '" ';
3265 $xml .= 'label_sortkey="'.$self->escape( $self->obj->suffix->label_sortkey ) .'">';
3266 $xml .= $self->escape( $self->obj->suffix->label ) .'</suffix>';
3269 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3270 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
3271 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
3272 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
3275 unless ($args->{no_record}) {
3276 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
3278 my $r_doc = $parser->parse_string($self->obj->record->marc);
3279 $r_doc->documentElement->setAttribute( id => $rec_tag );
3280 $xml .= $U->entityize($r_doc->documentElement->toString);
3283 $xml .= " </volume>\n";
3288 package OpenILS::Application::SuperCat::unAPI::ssub;
3289 use base qw/OpenILS::Application::SuperCat::unAPI/;
3295 my $xml = ' <subscription xmlns="http://open-ils.org/spec/holdings/v1" ';
3297 $xml .= 'id="tag:open-ils.org:serial-subscription/' . $self->obj->id . '" ';
3298 $xml .= 'start="' . $self->escape( $self->obj->start_date ) . '" ';
3299 $xml .= 'end="' . $self->escape( $self->obj->end_date ) . '" ';
3300 $xml .= 'expected_date_offset="' . $self->escape( $self->obj->expected_date_offset ) . '">';
3303 if (!$args->{no_distributions}) {
3304 if (ref($self->obj->distributions) && @{ $self->obj->distributions }) {
3305 $xml .= " <distributions>\n" . join(
3308 OpenILS::Application::SuperCat::unAPI
3310 ->as_xml({ %$args, no_subscription=>1, no_issuance=>1 })
3311 } @{ $self->obj->distributions }
3312 ) . " </distributions>\n";
3315 $xml .= " <distributions/>\n";
3319 if (!$args->{no_captions_and_patterns}) {
3320 if (ref($self->obj->scaps) && @{ $self->obj->scaps }) {
3321 $xml .= " <captions_and_patterns>\n" . join(
3324 OpenILS::Application::SuperCat::unAPI
3326 ->as_xml({ %$args, no_subscription=>1 })
3327 } @{ $self->obj->scaps }
3328 ) . " </captions_and_patterns>\n";
3331 $xml .= " <captions_and_patterns/>\n";
3335 if (!$args->{no_issuances}) {
3336 if (ref($self->obj->issuances) && @{ $self->obj->issuances }) {
3337 $xml .= " <issuances>\n" . join(
3340 OpenILS::Application::SuperCat::unAPI
3342 ->as_xml({ %$args, no_subscription=>1, no_items=>1 })
3343 } @{ $self->obj->issuances }
3344 ) . " </issuances>\n";
3347 $xml .= " <issuances/>\n";
3352 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3353 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
3354 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
3355 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
3358 unless ($args->{no_record}) {
3359 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
3361 my $r_doc = $parser->parse_string($self->obj->record_entry->marc);
3362 $r_doc->documentElement->setAttribute( id => $rec_tag );
3363 $xml .= $U->entityize($r_doc->documentElement->toString);
3366 $xml .= " </subscription>\n";
3371 package OpenILS::Application::SuperCat::unAPI::ssum_base;
3372 use base qw/OpenILS::Application::SuperCat::unAPI/;
3378 (my $type = ref($self)) =~ s/^.+([^:]+)$/$1/;
3380 my $xml = " <serial_summary xmlns=\"http://open-ils.org/spec/holdings/v1\" type=\"$type\" ";
3382 $xml .= "id=\"tag:open-ils.org:serial-summary-$type/" . $self->obj->id . '" ';
3383 $xml .= 'generated_coverage="' . $self->escape( $self->obj->generated_coverage ) . '" ';
3384 $xml .= 'show_generated="' . $self->escape( $self->obj->show_generated ) . '" ';
3385 $xml .= 'textual_holdings="' . $self->escape( $self->obj->textual_holdings ) . '">';
3388 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->distribution )->as_xml({ %$args, no_summaries=>1 }) if (!$args->{no_distribution});
3390 $xml .= " </serial_summary>\n";
3396 package OpenILS::Application::SuperCat::unAPI::sssum;
3397 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3399 package OpenILS::Application::SuperCat::unAPI::sbsum;
3400 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3402 package OpenILS::Application::SuperCat::unAPI::sisum;
3403 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3405 package OpenILS::Application::SuperCat::unAPI::sdist;
3406 use base qw/OpenILS::Application::SuperCat::unAPI/;
3412 my $xml = ' <distribution xmlns="http://open-ils.org/spec/holdings/v1" ';
3414 $xml .= 'id="tag:open-ils.org:serial-distribution/' . $self->obj->id . '" ';
3415 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
3416 $xml .= 'unit_label_prefix="' . $self->escape( $self->obj->unit_label_prefix ) . '" ';
3417 $xml .= 'unit_label_suffix="' . $self->escape( $self->obj->unit_label_suffix ) . '">';
3420 if (!$args->{no_distributions}) {
3421 if (ref($self->obj->streams) && @{ $self->obj->streams }) {
3422 $xml .= " <streams>\n" . join(
3425 OpenILS::Application::SuperCat::unAPI
3427 ->as_xml({ %$args, no_distribution=>1 })
3428 } @{ $self->obj->streams }
3429 ) . " </streams>\n";
3432 $xml .= " <streams/>\n";
3436 if (!$args->{no_summaries}) {
3437 $xml .= " <summaries>\n";
3441 OpenILS::Application::SuperCat::unAPI
3443 ->as_xml({ %$args, no_distribution=>1 }) : ""
3444 } ($self->obj->basic_summary, $self->obj->supplement_summary, $self->obj->index_summary)
3447 $xml .= " </summaries>\n";
3451 $xml .= ' <holding_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3452 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->holding_lib->id . '" ';
3453 $xml .= 'shortname="'.$self->escape( $self->obj->holding_lib->shortname ) .'" ';
3454 $xml .= 'name="'.$self->escape( $self->obj->holding_lib->name ) .'"/>';
3457 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_distributions=>1 }) if (!$args->{no_subscription});
3459 if (!$args->{no_record} && $self->obj->record_entry) {
3460 my $rec_tag = "tag:open-ils.org:serial-record_entry/".$self->obj->record_entry->id ;
3462 my $r_doc = $parser->parse_string($self->obj->record_entry->marc);
3463 $r_doc->documentElement->setAttribute( id => $rec_tag );
3464 $xml .= $U->entityize($r_doc->documentElement->toString);
3467 $xml .= " </distribution>\n";
3472 package OpenILS::Application::SuperCat::unAPI::sstr;
3473 use base qw/OpenILS::Application::SuperCat::unAPI/;
3479 my $xml = ' <stream xmlns="http://open-ils.org/spec/holdings/v1" ';
3481 $xml .= 'id="tag:open-ils.org:serial-stream/' . $self->obj->id . '" ';
3482 $xml .= 'routing_label="' . $self->escape( $self->obj->routing_label ) . '">';
3485 if (!$args->{no_items}) {
3486 if (ref($self->obj->items) && @{ $self->obj->items }) {
3487 $xml .= " <items>\n" . join(
3490 OpenILS::Application::SuperCat::unAPI
3492 ->as_xml({ %$args, no_stream=>1 })
3493 } @{ $self->obj->items }
3497 $xml .= " <items/>\n";
3501 #XXX routing_list_user's?
3503 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->distribution )->as_xml({ %$args, no_streams=>1 }) if (!$args->{no_distribution});
3505 $xml .= " </stream>\n";
3510 package OpenILS::Application::SuperCat::unAPI::sitem;
3511 use base qw/OpenILS::Application::SuperCat::unAPI/;
3517 my $xml = ' <serial_item xmlns="http://open-ils.org/spec/holdings/v1" ';
3519 $xml .= 'id="tag:open-ils.org:serial-item/' . $self->obj->id . '" ';
3520 $xml .= 'date_expected="' . $self->escape( $self->obj->date_expected ) . '"';
3521 $xml .= ' date_received="' . $self->escape( $self->obj->date_received ) .'"'if ($self->obj->date_received);
3523 if ($args->{no_issuance}) {
3524 my $siss = ref($self->obj->issuance) ? $self->obj->issuance->id : $self->obj->issuance;
3525 $xml .= ' issuance="tag:open-ils.org:serial-issuance/' . $siss . '"';
3530 if (ref($self->obj->notes) && $self->obj->notes) {
3531 $xml .= " <notes>\n";
3532 for my $note ( @{$self->obj->notes} ) {
3533 next unless ( $note->pub eq 't' );
3534 $xml .= sprintf(' <note date="%s" title="%s">%s</note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3537 $xml .= " </notes>\n";
3539 $xml .= " <notes/>\n";
3542 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->issuance )->as_xml({ %$args, no_items=>1 }) if (!$args->{no_issuance});
3543 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->stream )->as_xml({ %$args, no_items=>1 }) if (!$args->{no_stream});
3544 $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});
3545 $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});
3547 $xml .= " </serial_item>\n";
3552 package OpenILS::Application::SuperCat::unAPI::sunit;
3553 use base qw/OpenILS::Application::SuperCat::unAPI/;
3559 my $xml = ' <serial_unit xmlns="http://open-ils.org/spec/holdings/v1" '.
3560 'id="tag:open-ils.org:serial-unit/' . $self->obj->id . '" ';
3562 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3563 create_date edit_date copy_number circulate deposit ref holdable deleted
3564 deposit_amount price barcode circ_modifier circ_as_type opac_visible cost
3565 status_changed_time floating mint_condition detailed_contents sort_key summary_contents
3570 $xml .= ' <status ident="' . $self->obj->status->id . '" opac_visible="' . $self->obj->status->opac_visible . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
3571 $xml .= ' <location ident="' . $self->obj->location->id . '">' . $self->escape( $self->obj->location->name ) . "</location>\n";
3572 $xml .= ' <circlib ident="' . $self->obj->circ_lib->id . '">' . $self->escape( $self->obj->circ_lib->name ) . "</circlib>\n";
3574 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3575 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
3576 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
3577 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'"/>';
3580 $xml .= " <copy_notes>\n";
3581 if (ref($self->obj->notes) && $self->obj->notes) {
3582 for my $note ( @{$self->obj->notes} ) {
3583 next unless ( $note->pub eq 't' );
3584 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3589 $xml .= " </copy_notes>\n";
3590 $xml .= " <statcats>\n";
3592 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
3593 for my $sce ( @{$self->obj->stat_cat_entries} ) {
3594 next unless ( $sce->stat_cat->opac_visible eq 't' );
3595 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
3599 $xml .= " </statcats>\n";
3601 unless ($args->{no_volume}) {
3602 if (ref($self->obj->call_number)) {
3603 $xml .= OpenILS::Application::SuperCat::unAPI
3604 ->new( $self->obj->call_number )
3605 ->as_xml({ %$args, no_copies=>1 });
3607 $xml .= " <volume/>\n";
3611 $xml .= " </serial_unit>\n";
3616 package OpenILS::Application::SuperCat::unAPI::scap;
3617 use base qw/OpenILS::Application::SuperCat::unAPI/;
3623 my $xml = ' <caption_and_pattern xmlns="http://open-ils.org/spec/holdings/v1" '.
3624 'id="tag:open-ils.org:serial-caption_and_pattern/' . $self->obj->id . '" ';
3626 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3627 create_date type active pattern_code enum_1 enum_2 enum_3 enum_4
3628 enum_5 enum_6 chron_1 chron_2 chron_3 chron_4 chron_5 start_date end_date
3631 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_captions_and_patterns=>1 }) if (!$args->{no_subscription});
3632 $xml .= " </caption_and_pattern>\n";
3637 package OpenILS::Application::SuperCat::unAPI::siss;
3638 use base qw/OpenILS::Application::SuperCat::unAPI/;
3644 my $xml = ' <issuance xmlns="http://open-ils.org/spec/holdings/v1" '.
3645 'id="tag:open-ils.org:serial-issuance/' . $self->obj->id . '" ';
3647 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" '
3648 for (qw/create_date edit_date label date_published holding_code holding_type holding_link_id/);
3652 if (!$args->{no_items}) {
3653 if (ref($self->obj->items) && @{ $self->obj->items }) {
3654 $xml .= " <items>\n" . join(
3657 OpenILS::Application::SuperCat::unAPI
3659 ->as_xml({ %$args, no_stream=>1 })
3660 } @{ $self->obj->items }
3664 $xml .= " <items/>\n";
3668 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_issuances=>1 }) if (!$args->{no_subscription});
3669 $xml .= " </issuance>\n";
3674 package OpenILS::Application::SuperCat::unAPI::acp;
3675 use base qw/OpenILS::Application::SuperCat::unAPI/;
3681 my $xml = ' <copy xmlns="http://open-ils.org/spec/holdings/v1" '.
3682 'id="tag:open-ils.org:asset-copy/' . $self->obj->id . '" ';
3684 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3685 create_date edit_date copy_number circulate deposit ref holdable deleted
3686 deposit_amount price barcode circ_modifier circ_as_type opac_visible
3691 $xml .= ' <status ident="' . $self->obj->status->id . '" opac_visible="' . $self->obj->status->opac_visible . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
3692 $xml .= ' <location ident="' . $self->obj->location->id . '" opac_visible="'.$self->obj->location->opac_visible.'">' . $self->escape( $self->obj->location->name ) . "</location>\n";
3693 $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";
3695 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3696 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
3697 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
3698 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'" opac_visible="'.$self->obj->circ_lib->opac_visible.'"/>';
3701 $xml .= " <monograph_parts>\n";
3702 if (ref($self->obj->parts) && $self->obj->parts) {
3703 for my $part ( @{$self->obj->parts} ) {
3704 $xml .= sprintf(' <monograph_part record="%s" sortkey="%s">%s</monograph_part>',$part->record, $self->escape($part->label_sortkey), $self->escape($part->label));
3709 $xml .= " </monograph_parts>\n";
3710 $xml .= " <copy_notes>\n";
3711 if (ref($self->obj->notes) && $self->obj->notes) {
3712 for my $note ( @{$self->obj->notes} ) {
3713 next unless ( $note->pub eq 't' );
3714 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3719 $xml .= " </copy_notes>\n";
3720 $xml .= " <statcats>\n";
3722 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
3723 for my $sce ( @{$self->obj->stat_cat_entries} ) {
3724 next unless ( $sce->stat_cat->opac_visible eq 't' );
3725 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
3729 $xml .= " </statcats>\n";
3731 unless ($args->{no_volume}) {
3732 if (ref($self->obj->call_number)) {
3733 $xml .= OpenILS::Application::SuperCat::unAPI
3734 ->new( $self->obj->call_number )
3735 ->as_xml({ %$args, no_copies=>1 });
3737 $xml .= " <volume/>\n";
3741 $xml .= " </copy>\n";