1 # We'll be working with XML, so...
4 use Unicode::Normalize;
6 # ... and this has some handy common methods
7 use OpenILS::Application::AppUtils;
9 my $parser = new XML::LibXML;
10 my $U = 'OpenILS::Application::AppUtils';
13 package OpenILS::Application::SuperCat;
18 # All OpenSRF applications must be based on OpenSRF::Application or
19 # a subclass thereof. Makes sense, eh?
20 use OpenILS::Application;
21 use base qw/OpenILS::Application/;
23 # This is the client class, used for connecting to open-ils.storage
24 use OpenSRF::AppSession;
26 # This is an extension of Error.pm that supplies some error types to throw
27 use OpenSRF::EX qw(:try);
29 # This is a helper class for querying the OpenSRF Settings application ...
30 use OpenSRF::Utils::SettingsClient;
32 # ... and here we have the built in logging helper ...
33 use OpenSRF::Utils::Logger qw($logger);
35 # ... and this is our OpenILS object (en|de)coder and psuedo-ORM package.
36 use OpenILS::Utils::Fieldmapper;
47 # we need an XML parser
48 $_parser = new XML::LibXML;
51 $_xslt = new XML::LibXSLT;
53 # parse the MODS xslt ...
54 my $mods33_xslt = $_parser->parse_file(
55 OpenSRF::Utils::SettingsClient
57 ->config_value( dirs => 'xsl' ).
58 "/MARC21slim2MODS33.xsl"
60 # and stash a transformer
61 $record_xslt{mods33}{xslt} = $_xslt->parse_stylesheet( $mods33_xslt );
62 $record_xslt{mods33}{namespace_uri} = 'http://www.loc.gov/mods/v3';
63 $record_xslt{mods33}{docs} = 'http://www.loc.gov/mods/';
64 $record_xslt{mods33}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-3.xsd';
66 # parse the MODS xslt ...
67 my $mods32_xslt = $_parser->parse_file(
68 OpenSRF::Utils::SettingsClient
70 ->config_value( dirs => 'xsl' ).
71 "/MARC21slim2MODS32.xsl"
73 # and stash a transformer
74 $record_xslt{mods32}{xslt} = $_xslt->parse_stylesheet( $mods32_xslt );
75 $record_xslt{mods32}{namespace_uri} = 'http://www.loc.gov/mods/v3';
76 $record_xslt{mods32}{docs} = 'http://www.loc.gov/mods/';
77 $record_xslt{mods32}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-2.xsd';
79 # parse the MODS xslt ...
80 my $mods3_xslt = $_parser->parse_file(
81 OpenSRF::Utils::SettingsClient
83 ->config_value( dirs => 'xsl' ).
84 "/MARC21slim2MODS3.xsl"
86 # and stash a transformer
87 $record_xslt{mods3}{xslt} = $_xslt->parse_stylesheet( $mods3_xslt );
88 $record_xslt{mods3}{namespace_uri} = 'http://www.loc.gov/mods/v3';
89 $record_xslt{mods3}{docs} = 'http://www.loc.gov/mods/';
90 $record_xslt{mods3}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-1.xsd';
92 # parse the MODS xslt ...
93 my $mods_xslt = $_parser->parse_file(
94 OpenSRF::Utils::SettingsClient
96 ->config_value( dirs => 'xsl' ).
97 "/MARC21slim2MODS.xsl"
99 # and stash a transformer
100 $record_xslt{mods}{xslt} = $_xslt->parse_stylesheet( $mods_xslt );
101 $record_xslt{mods}{namespace_uri} = 'http://www.loc.gov/mods/';
102 $record_xslt{mods}{docs} = 'http://www.loc.gov/mods/';
103 $record_xslt{mods}{schema_location} = 'http://www.loc.gov/standards/mods/mods.xsd';
105 # parse the ATOM entry xslt ...
106 my $atom_xslt = $_parser->parse_file(
107 OpenSRF::Utils::SettingsClient
109 ->config_value( dirs => 'xsl' ).
110 "/MARC21slim2ATOM.xsl"
112 # and stash a transformer
113 $record_xslt{atom}{xslt} = $_xslt->parse_stylesheet( $atom_xslt );
114 $record_xslt{atom}{namespace_uri} = 'http://www.w3.org/2005/Atom';
115 $record_xslt{atom}{docs} = 'http://www.ietf.org/rfc/rfc4287.txt';
117 # parse the RDFDC xslt ...
118 my $rdf_dc_xslt = $_parser->parse_file(
119 OpenSRF::Utils::SettingsClient
121 ->config_value( dirs => 'xsl' ).
122 "/MARC21slim2RDFDC.xsl"
124 # and stash a transformer
125 $record_xslt{rdf_dc}{xslt} = $_xslt->parse_stylesheet( $rdf_dc_xslt );
126 $record_xslt{rdf_dc}{namespace_uri} = 'http://purl.org/dc/elements/1.1/';
127 $record_xslt{rdf_dc}{schema_location} = 'http://purl.org/dc/elements/1.1/';
129 # parse the SRWDC xslt ...
130 my $srw_dc_xslt = $_parser->parse_file(
131 OpenSRF::Utils::SettingsClient
133 ->config_value( dirs => 'xsl' ).
134 "/MARC21slim2SRWDC.xsl"
136 # and stash a transformer
137 $record_xslt{srw_dc}{xslt} = $_xslt->parse_stylesheet( $srw_dc_xslt );
138 $record_xslt{srw_dc}{namespace_uri} = 'info:srw/schema/1/dc-schema';
139 $record_xslt{srw_dc}{schema_location} = 'http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd';
141 # parse the OAIDC xslt ...
142 my $oai_dc_xslt = $_parser->parse_file(
143 OpenSRF::Utils::SettingsClient
145 ->config_value( dirs => 'xsl' ).
146 "/MARC21slim2OAIDC.xsl"
148 # and stash a transformer
149 $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $oai_dc_xslt );
150 $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
151 $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
153 # parse the RSS xslt ...
154 my $rss_xslt = $_parser->parse_file(
155 OpenSRF::Utils::SettingsClient
157 ->config_value( dirs => 'xsl' ).
158 "/MARC21slim2RSS2.xsl"
160 # and stash a transformer
161 $record_xslt{rss2}{xslt} = $_xslt->parse_stylesheet( $rss_xslt );
163 # parse the FGDC xslt ...
164 my $fgdc_xslt = $_parser->parse_file(
165 OpenSRF::Utils::SettingsClient
167 ->config_value( dirs => 'xsl' ).
168 "/MARC21slim2FGDC.xsl"
170 # and stash a transformer
171 $record_xslt{fgdc}{xslt} = $_xslt->parse_stylesheet( $fgdc_xslt );
172 $record_xslt{fgdc}{docs} = 'http://www.fgdc.gov/metadata/csdgm/index_html';
173 $record_xslt{fgdc}{schema_location} = 'http://www.fgdc.gov/metadata/fgdc-std-001-1998.xsd';
175 register_record_transforms();
180 sub register_record_transforms {
181 for my $type ( keys %record_xslt ) {
182 __PACKAGE__->register_method(
183 method => 'retrieve_record_transform',
184 api_name => "open-ils.supercat.record.$type.retrieve",
188 { desc => "Returns the \U$type\E representation ".
189 "of the requested bibliographic record",
193 desc => 'An OpenILS biblio::record_entry id',
197 { desc => "The bib record in \U$type\E",
202 __PACKAGE__->register_method(
203 method => 'retrieve_isbn_transform',
204 api_name => "open-ils.supercat.isbn.$type.retrieve",
208 { desc => "Returns the \U$type\E representation ".
209 "of the requested bibliographic record",
217 { desc => "The bib record in \U$type\E",
229 return unless ($tree && ref($tree->$field));
231 my @things = $filter->($tree);
232 for my $v ( @{$tree->$field} ){
233 push @things, $filter->($v);
234 push @things, tree_walker($v, $field, $filter);
245 my $page_size = shift || 9;
246 my $page = shift || 0;
247 my $statuses = shift || [];
248 my $copy_locations = shift || [];
250 my ($before_limit,$after_limit) = (0,0);
251 my ($before_offset,$after_offset) = (0,0);
254 $before_limit = $after_limit = int($page_size / 2);
255 $after_limit += 1 if ($page_size % 2);
257 $before_offset = $after_offset = int($page_size / 2);
258 $before_offset += 1 if ($page_size % 2);
259 $before_limit = $after_limit = $page_size;
262 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
264 my $o_search = { shortname => $ou };
265 if (!$ou || $ou eq '-') {
266 $o_search = { parent_ou => undef };
269 my $orgs = $_storage->request(
270 "open-ils.cstore.direct.actor.org_unit.search",
273 flesh_fields => { aou => [qw/children/] }
277 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
279 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
284 if (@$statuses || @$copy_locations) {
289 call_number => { '=' => { '+acn' => 'id' } },
291 ((@$statuses) ? ( status => $statuses) : ()),
292 ((@$copy_locations) ? ( location => $copy_locations) : ())
299 my $before = $_storage->request(
300 "open-ils.cstore.direct.asset.call_number.search.atomic",
301 { label => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
302 owning_lib => \@ou_ids,
307 flesh_fields => { acn => [qw/record owning_lib/] },
308 order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" },
309 limit => $before_limit,
310 offset => abs($page) * $page_size - $before_offset,
313 push @list, reverse(@$before);
317 my $after = $_storage->request(
318 "open-ils.cstore.direct.asset.call_number.search.atomic",
319 { label => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
320 owning_lib => \@ou_ids,
325 flesh_fields => { acn => [qw/record owning_lib/] },
326 order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" },
327 limit => $after_limit,
328 offset => abs($page) * $page_size - $after_offset,
336 __PACKAGE__->register_method(
337 method => 'cn_browse',
338 api_name => 'open-ils.supercat.call_number.browse',
343 Returns the XML representation of the requested bibliographic record's holdings
348 desc => 'The target call number lable',
350 { name => 'org_unit',
351 desc => 'The org unit shortname (or "-" or undef for global) to browse',
353 { name => 'page_size',
354 desc => 'Count of call numbers to retrieve, default is 9',
357 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
359 { name => 'statuses',
360 desc => 'Array of statuses to filter copies by, optional and can be undef.',
362 { name => 'locations',
363 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
367 { desc => 'Call numbers with owning_lib and record fleshed',
378 my $limit = shift || 10;
379 my $page = shift || 0;
380 my $statuses = shift || [];
381 my $copy_locations = shift || [];
384 my $offset = abs($page) * $limit;
385 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
387 my $o_search = { shortname => $ou };
388 if (!$ou || $ou eq '-') {
389 $o_search = { parent_ou => undef };
392 my $orgs = $_storage->request(
393 "open-ils.cstore.direct.actor.org_unit.search",
396 flesh_fields => { aou => [qw/children/] }
400 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
402 $logger->debug("Searching for CNs at orgs [".join(',',@ou_ids)."], based on $ou");
407 if (@$statuses || @$copy_locations) {
412 call_number => { '=' => { '+acn' => 'id' } },
414 ((@$statuses) ? ( status => $statuses) : ()),
415 ((@$copy_locations) ? ( location => $copy_locations) : ())
422 my $before = $_storage->request(
423 "open-ils.cstore.direct.asset.call_number.search.atomic",
424 { label => { "<" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
425 owning_lib => \@ou_ids,
430 flesh_fields => { acn => [qw/record owning_lib/] },
431 order_by => { acn => "oils_text_as_bytea(label_sortkey) desc, oils_text_as_bytea(label) desc, id desc, owning_lib desc" },
436 push @list, reverse(@$before);
440 my $after = $_storage->request(
441 "open-ils.cstore.direct.asset.call_number.search.atomic",
442 { label => { ">=" => { transform => "oils_text_as_bytea", value => ["oils_text_as_bytea", $label] } },
443 owning_lib => \@ou_ids,
448 flesh_fields => { acn => [qw/record owning_lib/] },
449 order_by => { acn => "oils_text_as_bytea(label_sortkey), oils_text_as_bytea(label), id, owning_lib" },
459 __PACKAGE__->register_method(
460 method => 'cn_startwith',
461 api_name => 'open-ils.supercat.call_number.startwith',
466 Returns the XML representation of the requested bibliographic record's holdings
471 desc => 'The target call number lable',
473 { name => 'org_unit',
474 desc => 'The org unit shortname (or "-" or undef for global) to browse',
476 { name => 'page_size',
477 desc => 'Count of call numbers to retrieve, default is 9',
480 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
482 { name => 'statuses',
483 desc => 'Array of statuses to filter copies by, optional and can be undef.',
485 { name => 'locations',
486 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
490 { desc => 'Call numbers with owning_lib and record fleshed',
496 sub new_books_by_item {
501 my $page_size = shift || 10;
502 my $page = shift || 1;
503 my $statuses = shift || [];
504 my $copy_locations = shift || [];
506 my $offset = $page_size * ($page - 1);
508 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
511 if ($ou && $ou ne '-') {
512 my $orgs = $_storage->request(
513 "open-ils.cstore.direct.actor.org_unit.search",
514 { shortname => $ou },
516 flesh_fields => { aou => [qw/children/] }
519 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
522 $logger->debug("Searching for records with new copies at orgs [".join(',',@ou_ids)."], based on $ou");
523 my $cns = $_storage->request(
524 "open-ils.cstore.json_query.atomic",
525 { select => { acn => ['record'],
526 acp => [{ aggregate => 1 => transform => max => column => create_date => alias => 'create_date'}]
528 from => { 'acn' => { 'acp' => { field => call_number => fkey => 'id' } } },
532 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
533 ((@$statuses) ? ( status => $statuses) : ()),
534 ((@$copy_locations) ? ( location => $copy_locations) : ())
536 '+acn' => { record => { '>' => 0 } },
538 order_by => { acp => { create_date => { transform => 'max', direction => 'desc' } } },
544 return [ map { $_->{record} } @$cns ];
546 __PACKAGE__->register_method(
547 method => 'new_books_by_item',
548 api_name => 'open-ils.supercat.new_book_list',
553 Returns the XML representation of the requested bibliographic record's holdings
557 { name => 'org_unit',
558 desc => 'The org unit shortname (or "-" or undef for global) to list',
560 { name => 'page_size',
561 desc => 'Count of records to retrieve, default is 10',
564 desc => 'The page of records to retrieve, calculated based on page_size. Starts at 1.',
566 { name => 'statuses',
567 desc => 'Array of statuses to filter copies by, optional and can be undef.',
569 { name => 'locations',
570 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
574 { desc => 'Record IDs',
583 return tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
585 __PACKAGE__->register_method(
586 method => 'general_browse',
587 api_name => 'open-ils.supercat.title.browse',
588 tag => 'tnf', subfield => 'a',
592 { desc => "Returns a list of the requested org-scoped record IDs held",
594 [ { name => 'value', desc => 'The target title', type => 'string' },
595 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
596 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
597 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
598 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
599 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
600 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
603 __PACKAGE__->register_method(
604 method => 'general_browse',
605 api_name => 'open-ils.supercat.author.browse',
606 tag => [qw/100 110 111/], subfield => 'a',
610 { desc => "Returns a list of the requested org-scoped record IDs held",
612 [ { name => 'value', desc => 'The target author', type => 'string' },
613 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
614 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
615 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
616 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
617 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
618 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
621 __PACKAGE__->register_method(
622 method => 'general_browse',
623 api_name => 'open-ils.supercat.subject.browse',
624 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
628 { desc => "Returns a list of the requested org-scoped record IDs held",
630 [ { name => 'value', desc => 'The target subject', type => 'string' },
631 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
632 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
633 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
634 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
635 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
636 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
639 __PACKAGE__->register_method(
640 method => 'general_browse',
641 api_name => 'open-ils.supercat.topic.browse',
642 tag => [qw/650 690/], subfield => 'a',
646 { desc => "Returns a list of the requested org-scoped record IDs held",
648 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
649 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
650 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
651 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
652 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
653 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
654 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
657 __PACKAGE__->register_method(
658 method => 'general_browse',
659 api_name => 'open-ils.supercat.series.browse',
660 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
664 { desc => "Returns a list of the requested org-scoped record IDs held",
666 [ { name => 'value', desc => 'The target series', type => 'string' },
667 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
668 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
669 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
670 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
671 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
672 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
682 my $subfield = shift;
685 my $page_size = shift || 9;
686 my $page = shift || 0;
687 my $statuses = shift || [];
688 my $copy_locations = shift || [];
690 my ($before_limit,$after_limit) = (0,0);
691 my ($before_offset,$after_offset) = (0,0);
694 $before_limit = $after_limit = int($page_size / 2);
695 $after_limit += 1 if ($page_size % 2);
697 $before_offset = $after_offset = int($page_size / 2);
698 $before_offset += 1 if ($page_size % 2);
699 $before_limit = $after_limit = $page_size;
702 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
705 if ($ou && $ou ne '-') {
706 my $orgs = $_storage->request(
707 "open-ils.cstore.direct.actor.org_unit.search",
708 { shortname => $ou },
710 flesh_fields => { aou => [qw/children/] }
713 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
716 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
721 my $before = $_storage->request(
722 "open-ils.cstore.json_query.atomic",
723 { select => { mfr => [qw/record value/] },
728 subfield => $subfield,
729 value => { '<' => lc($value) }
733 { select=> { acp => [ 'id' ] },
734 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
736 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
739 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
740 ((@$statuses) ? ( status => $statuses) : ()),
741 ((@$copy_locations) ? ( location => $copy_locations) : ())
748 { select=> { auri => [ 'id' ] },
749 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
751 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
752 '+auri' => { active => 't' }
759 order_by => { mfr => { value => 'desc' } },
760 limit => $before_limit,
761 offset => abs($page) * $page_size - $before_offset,
764 push @list, map { $_->{record} } reverse(@$before);
768 my $after = $_storage->request(
769 "open-ils.cstore.json_query.atomic",
770 { select => { mfr => [qw/record value/] },
775 subfield => $subfield,
776 value => { '>=' => lc($value) }
780 { select=> { acp => [ 'id' ] },
781 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
783 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
786 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
787 ((@$statuses) ? ( status => $statuses) : ()),
788 ((@$copy_locations) ? ( location => $copy_locations) : ())
795 { select=> { auri => [ 'id' ] },
796 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
798 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
799 '+auri' => { active => 't' }
806 order_by => { mfr => { value => 'asc' } },
807 limit => $after_limit,
808 offset => abs($page) * $page_size - $after_offset,
811 push @list, map { $_->{record} } @$after;
816 __PACKAGE__->register_method(
817 method => 'tag_sf_browse',
818 api_name => 'open-ils.supercat.tag.browse',
823 Returns a list of the requested org-scoped record IDs held
828 desc => 'The target MARC tag',
830 { name => 'subfield',
831 desc => 'The target MARC subfield',
834 desc => 'The target string',
836 { name => 'org_unit',
837 desc => 'The org unit shortname (or "-" or undef for global) to browse',
839 { name => 'page_size',
840 desc => 'Count of call numbers to retrieve, default is 9',
843 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
845 { name => 'statuses',
846 desc => 'Array of statuses to filter copies by, optional and can be undef.',
848 { name => 'locations',
849 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
853 { desc => 'Record IDs that have copies at the relevant org units',
858 sub general_authority_browse {
861 return authority_tag_sf_browse($self, $client, $self->{tag}, $self->{subfield}, @_);
863 __PACKAGE__->register_method(
864 method => 'general_authority_browse',
865 api_name => 'open-ils.supercat.authority.title.browse',
866 tag => '130', subfield => 'a',
870 { desc => "Returns a list of the requested authority record IDs held",
872 [ { name => 'value', desc => 'The target title', type => 'string' },
873 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
874 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
875 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
878 __PACKAGE__->register_method(
879 method => 'general_authority_browse',
880 api_name => 'open-ils.supercat.authority.author.browse',
881 tag => [qw/100 110 111/], subfield => 'a',
885 { desc => "Returns a list of the requested authority record IDs held",
887 [ { name => 'value', desc => 'The target author', type => 'string' },
888 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
889 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
890 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
893 __PACKAGE__->register_method(
894 method => 'general_authority_browse',
895 api_name => 'open-ils.supercat.authority.subject.browse',
896 tag => [qw/148 150 151 155/], subfield => 'a',
900 { desc => "Returns a list of the requested authority record IDs held",
902 [ { name => 'value', desc => 'The target subject', type => 'string' },
903 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
904 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
905 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
908 __PACKAGE__->register_method(
909 method => 'general_authority_browse',
910 api_name => 'open-ils.supercat.authority.topic.browse',
911 tag => '150', subfield => 'a',
915 { desc => "Returns a list of the requested authority record IDs held",
917 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
918 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
919 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
920 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
923 __PACKAGE__->register_method(
924 method => 'general_authority_browse',
925 api_name => 'open-ils.supercat.authority.title.refs.browse',
926 tag => '130', subfield => 'a',
930 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
932 [ { name => 'value', desc => 'The target title', type => 'string' },
933 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
934 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
935 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
938 __PACKAGE__->register_method(
939 method => 'general_authority_browse',
940 api_name => 'open-ils.supercat.authority.author.refs.browse',
941 tag => [qw/100 110 111/], subfield => 'a',
945 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
947 [ { name => 'value', desc => 'The target author', type => 'string' },
948 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
949 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
950 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
953 __PACKAGE__->register_method(
954 method => 'general_authority_browse',
955 api_name => 'open-ils.supercat.authority.subject.refs.browse',
956 tag => [qw/148 150 151 155/], subfield => 'a',
960 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
962 [ { name => 'value', desc => 'The target subject', type => 'string' },
963 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
964 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
965 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
968 __PACKAGE__->register_method(
969 method => 'general_authority_browse',
970 api_name => 'open-ils.supercat.authority.topic.refs.browse',
971 tag => '150', subfield => 'a',
975 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
977 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
978 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
979 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
980 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
984 sub authority_tag_sf_browse {
989 my $subfield = shift;
991 my $page_size = shift || 9;
992 my $page = shift || 0;
994 my ($before_limit,$after_limit) = (0,0);
995 my ($before_offset,$after_offset) = (0,0);
998 $before_limit = $after_limit = int($page_size / 2);
999 $after_limit += 1 if ($page_size % 2);
1001 $before_offset = $after_offset = int($page_size / 2);
1002 $before_offset += 1 if ($page_size % 2);
1003 $before_limit = $after_limit = $page_size;
1006 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1008 # .refs variant includes 4xx and 5xx variants for see / see also
1010 foreach my $tagname (@$tag) {
1011 push(@ref_tags, $tagname);
1012 if ($self->api_name =~ /\.refs\./) {
1013 push(@ref_tags, '4' . substr($tagname, 1, 2));
1014 push(@ref_tags, '5' . substr($tagname, 1, 2));
1020 my $before = $_storage->request(
1021 "open-ils.cstore.json_query.atomic",
1022 { select => { afr => [qw/record value/] },
1023 from => { 'are', 'afr' },
1025 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '<' => lc($value) } },
1026 '+are' => { 'deleted' => 'f' }
1028 order_by => { afr => { value => 'desc' } },
1029 limit => $before_limit,
1030 offset => abs($page) * $page_size - $before_offset,
1033 push @list, map { $_->{record} } reverse(@$before);
1037 my $after = $_storage->request(
1038 "open-ils.cstore.json_query.atomic",
1039 { select => { afr => [qw/record value/] },
1040 from => { 'are', 'afr' },
1042 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => lc($value) } },
1043 '+are' => { 'deleted' => 'f' }
1045 order_by => { afr => { value => 'asc' } },
1046 limit => $after_limit,
1047 offset => abs($page) * $page_size - $after_offset,
1050 push @list, map { $_->{record} } @$after;
1053 # If we're not pulling in see/see also references, just return the raw list
1054 if ($self->api_name !~ /\.refs\./) {
1058 # Remove dupe record IDs that turn up due to 4xx and 5xx matches
1061 foreach my $record (@list) {
1062 next if exists $seen{$record};
1063 push @retlist, int($record);
1069 __PACKAGE__->register_method(
1070 method => 'authority_tag_sf_browse',
1071 api_name => 'open-ils.supercat.authority.tag.browse',
1075 { desc => <<" DESC",
1076 Returns a list of the requested authority record IDs held
1081 desc => 'The target Authority MARC tag',
1083 { name => 'subfield',
1084 desc => 'The target Authority MARC subfield',
1087 desc => 'The target string',
1089 { name => 'page_size',
1090 desc => 'Count of call numbers to retrieve, default is 9',
1093 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1097 { desc => 'Authority Record IDs that are near the target string',
1102 sub general_startwith {
1105 return tag_sf_startwith($self, $client, $self->{tag}, $self->{subfield}, @_);
1107 __PACKAGE__->register_method(
1108 method => 'general_startwith',
1109 api_name => 'open-ils.supercat.title.startwith',
1110 tag => 'tnf', subfield => 'a',
1114 { desc => "Returns a list of the requested org-scoped record IDs held",
1116 [ { name => 'value', desc => 'The target title', type => 'string' },
1117 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1118 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1119 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1120 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1121 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1122 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1125 __PACKAGE__->register_method(
1126 method => 'general_startwith',
1127 api_name => 'open-ils.supercat.author.startwith',
1128 tag => [qw/100 110 111/], subfield => 'a',
1132 { desc => "Returns a list of the requested org-scoped record IDs held",
1134 [ { name => 'value', desc => 'The target author', type => 'string' },
1135 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1136 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1137 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1138 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1139 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1140 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1143 __PACKAGE__->register_method(
1144 method => 'general_startwith',
1145 api_name => 'open-ils.supercat.subject.startwith',
1146 tag => [qw/600 610 611 630 648 650 651 653 655 656 662 690 691 696 697 698 699/], subfield => 'a',
1150 { desc => "Returns a list of the requested org-scoped record IDs held",
1152 [ { name => 'value', desc => 'The target subject', type => 'string' },
1153 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1154 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1155 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1156 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1157 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1158 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1161 __PACKAGE__->register_method(
1162 method => 'general_startwith',
1163 api_name => 'open-ils.supercat.topic.startwith',
1164 tag => [qw/650 690/], subfield => 'a',
1168 { desc => "Returns a list of the requested org-scoped record IDs held",
1170 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1171 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1172 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1173 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1174 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1175 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1176 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1179 __PACKAGE__->register_method(
1180 method => 'general_startwith',
1181 api_name => 'open-ils.supercat.series.startwith',
1182 tag => [qw/440 490 800 810 811 830/], subfield => 'a',
1186 { desc => "Returns a list of the requested org-scoped record IDs held",
1188 [ { name => 'value', desc => 'The target series', type => 'string' },
1189 { name => 'org_unit', desc => 'The org unit shortname (or "-" or undef for global) to browse', type => 'string' },
1190 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1191 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' },
1192 { name => 'statuses', desc => 'Array of statuses to filter copies by, optional and can be undef.', type => 'array' },
1193 { name => 'locations', desc => 'Array of copy locations to filter copies by, optional and can be undef.', type => 'array' }, ],
1194 'return' => { desc => 'Record IDs that have copies at the relevant org units', type => 'array' }
1199 sub tag_sf_startwith {
1204 my $subfield = shift;
1207 my $limit = shift || 10;
1208 my $page = shift || 0;
1209 my $statuses = shift || [];
1210 my $copy_locations = shift || [];
1212 my $offset = $limit * abs($page);
1213 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1216 if ($ou && $ou ne '-') {
1217 my $orgs = $_storage->request(
1218 "open-ils.cstore.direct.actor.org_unit.search",
1219 { shortname => $ou },
1221 flesh_fields => { aou => [qw/children/] }
1224 @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1227 $logger->debug("Searching for records at orgs [".join(',',@ou_ids)."], based on $ou");
1232 my $before = $_storage->request(
1233 "open-ils.cstore.json_query.atomic",
1234 { select => { mfr => [qw/record value/] },
1239 subfield => $subfield,
1240 value => { '<' => lc($value) }
1244 { select=> { acp => [ 'id' ] },
1245 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
1247 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
1250 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
1251 ((@$statuses) ? ( status => $statuses) : ()),
1252 ((@$copy_locations) ? ( location => $copy_locations) : ())
1259 { select=> { auri => [ 'id' ] },
1260 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
1262 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
1263 '+auri' => { active => 't' }
1270 order_by => { mfr => { value => 'desc' } },
1275 push @list, map { $_->{record} } reverse(@$before);
1279 my $after = $_storage->request(
1280 "open-ils.cstore.json_query.atomic",
1281 { select => { mfr => [qw/record value/] },
1286 subfield => $subfield,
1287 value => { '>=' => lc($value) }
1291 { select=> { acp => [ 'id' ] },
1292 from => { acn => { acp => { field => 'call_number', fkey => 'id' } } },
1294 { '+acn' => { record => { '=' => { '+mfr' => 'record' } } },
1297 ((@ou_ids) ? ( circ_lib => \@ou_ids) : ()),
1298 ((@$statuses) ? ( status => $statuses) : ()),
1299 ((@$copy_locations) ? ( location => $copy_locations) : ())
1306 { select=> { auri => [ 'id' ] },
1307 from => { acn => { auricnm => { field => 'call_number', fkey => 'id', join => { auri => { field => 'id', fkey => 'uri' } } } } },
1309 { '+acn' => { record => { '=' => { '+mfr' => 'record' } }, (@ou_ids) ? ( owning_lib => \@ou_ids) : () },
1310 '+auri' => { active => 't' }
1317 order_by => { mfr => { value => 'asc' } },
1322 push @list, map { $_->{record} } @$after;
1327 __PACKAGE__->register_method(
1328 method => 'tag_sf_startwith',
1329 api_name => 'open-ils.supercat.tag.startwith',
1333 { desc => <<" DESC",
1334 Returns a list of the requested org-scoped record IDs held
1339 desc => 'The target MARC tag',
1341 { name => 'subfield',
1342 desc => 'The target MARC subfield',
1345 desc => 'The target string',
1347 { name => 'org_unit',
1348 desc => 'The org unit shortname (or "-" or undef for global) to browse',
1350 { name => 'page_size',
1351 desc => 'Count of call numbers to retrieve, default is 9',
1354 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1356 { name => 'statuses',
1357 desc => 'Array of statuses to filter copies by, optional and can be undef.',
1359 { name => 'locations',
1360 desc => 'Array of copy locations to filter copies by, optional and can be undef.',
1364 { desc => 'Record IDs that have copies at the relevant org units',
1369 sub general_authority_startwith {
1372 return authority_tag_sf_startwith($self, $client, $self->{tag}, $self->{subfield}, @_);
1374 __PACKAGE__->register_method(
1375 method => 'general_authority_startwith',
1376 api_name => 'open-ils.supercat.authority.title.startwith',
1377 tag => '130', subfield => 'a',
1381 { desc => "Returns a list of the requested authority record IDs held",
1383 [ { name => 'value', desc => 'The target title', type => 'string' },
1384 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1385 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1386 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1389 __PACKAGE__->register_method(
1390 method => 'general_authority_startwith',
1391 api_name => 'open-ils.supercat.authority.author.startwith',
1392 tag => [qw/100 110 111/], subfield => 'a',
1396 { desc => "Returns a list of the requested authority record IDs held",
1398 [ { name => 'value', desc => 'The target author', type => 'string' },
1399 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1400 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1401 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1404 __PACKAGE__->register_method(
1405 method => 'general_authority_startwith',
1406 api_name => 'open-ils.supercat.authority.subject.startwith',
1407 tag => [qw/148 150 151 155/], subfield => 'a',
1411 { desc => "Returns a list of the requested authority record IDs held",
1413 [ { name => 'value', desc => 'The target subject', type => 'string' },
1414 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1415 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1416 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1419 __PACKAGE__->register_method(
1420 method => 'general_authority_startwith',
1421 api_name => 'open-ils.supercat.authority.topic.startwith',
1422 tag => '150', subfield => 'a',
1426 { desc => "Returns a list of the requested authority record IDs held",
1428 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1429 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1430 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1431 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1434 __PACKAGE__->register_method(
1435 method => 'general_authority_startwith',
1436 api_name => 'open-ils.supercat.authority.title.refs.startwith',
1437 tag => '130', subfield => 'a',
1441 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1443 [ { name => 'value', desc => 'The target title', type => 'string' },
1444 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1445 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1446 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1449 __PACKAGE__->register_method(
1450 method => 'general_authority_startwith',
1451 api_name => 'open-ils.supercat.authority.author.refs.startwith',
1452 tag => [qw/100 110 111/], subfield => 'a',
1456 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1458 [ { name => 'value', desc => 'The target author', type => 'string' },
1459 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1460 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1461 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1464 __PACKAGE__->register_method(
1465 method => 'general_authority_startwith',
1466 api_name => 'open-ils.supercat.authority.subject.refs.startwith',
1467 tag => [qw/148 150 151 155/], subfield => 'a',
1471 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1473 [ { name => 'value', desc => 'The target subject', type => 'string' },
1474 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1475 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1476 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1479 __PACKAGE__->register_method(
1480 method => 'general_authority_startwith',
1481 api_name => 'open-ils.supercat.authority.topic.refs.startwith',
1482 tag => '150', subfield => 'a',
1486 { desc => "Returns a list of the requested authority record IDs held, including see (4xx) and see also (5xx) references",
1488 [ { name => 'value', desc => 'The target topical subject', type => 'string' },
1489 { name => 'page_size', desc => 'Count of records to retrieve, default is 9', type => 'number' },
1490 { name => 'page', desc => 'The page of records retrieved, calculated based on page_size. Can be positive, negative or 0.', type => 'number' }, ],
1491 'return' => { desc => 'Authority Record IDs that are near the target string', type => 'array' }
1495 sub authority_tag_sf_startwith {
1500 my $subfield = shift;
1502 my $limit = shift || 10;
1503 my $page = shift || 0;
1505 my $ref_limit = $limit;
1506 my $offset = $limit * abs($page);
1507 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1510 # .refs variant includes 4xx and 5xx variants for see / see also
1511 foreach my $tagname (@$tag) {
1512 push(@ref_tags, $tagname);
1513 if ($self->api_name =~ /\.refs\./) {
1514 push(@ref_tags, '4' . substr($tagname, 1, 2));
1515 push(@ref_tags, '5' . substr($tagname, 1, 2));
1522 # Don't skip the first actual page of results in descending order
1523 $offset = $offset - $limit;
1525 my $before = $_storage->request(
1526 "open-ils.cstore.json_query.atomic",
1527 { select => { afr => [qw/record value/] },
1528 from => { 'afr', 'are' },
1530 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '<' => lc($value) } },
1531 '+are' => { deleted => 'f' }
1533 order_by => { afr => { value => 'desc' } },
1534 limit => $ref_limit,
1538 push @list, map { $_->{record} } reverse(@$before);
1542 my $after = $_storage->request(
1543 "open-ils.cstore.json_query.atomic",
1544 { select => { afr => [qw/record value/] },
1545 from => { 'afr', 'are' },
1547 '+afr' => { tag => \@ref_tags, subfield => $subfield, value => { '>=' => lc($value) } },
1548 '+are' => { deleted => 'f' }
1550 order_by => { afr => { value => 'asc' } },
1551 limit => $ref_limit,
1555 push @list, map { $_->{record} } @$after;
1558 # If we're not pulling in see/see also references, just return the raw list
1559 if ($self->api_name !~ /\.refs\./) {
1563 # Remove dupe record IDs that turn up due to 4xx and 5xx matches
1566 foreach my $record (@list) {
1567 next if exists $seen{$record};
1568 push @retlist, int($record);
1574 __PACKAGE__->register_method(
1575 method => 'authority_tag_sf_startwith',
1576 api_name => 'open-ils.supercat.authority.tag.startwith',
1580 { desc => <<" DESC",
1581 Returns a list of the requested authority record IDs held
1586 desc => 'The target Authority MARC tag',
1588 { name => 'subfield',
1589 desc => 'The target Authority MARC subfield',
1592 desc => 'The target string',
1594 { name => 'page_size',
1595 desc => 'Count of call numbers to retrieve, default is 9',
1598 desc => 'The page of call numbers to retrieve, calculated based on page_size. Can be positive, negative or 0.',
1602 { desc => 'Authority Record IDs that are near the target string',
1608 sub holding_data_formats {
1611 namespace_uri => 'http://www.loc.gov/MARC21/slim',
1612 docs => 'http://www.loc.gov/marcxml/',
1613 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
1617 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acn.formats', api_level => 1 );
1618 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.acp.formats', api_level => 1 );
1619 __PACKAGE__->register_method( method => 'holding_data_formats', api_name => 'open-ils.supercat.auri.formats', api_level => 1 );
1622 __PACKAGE__->register_method(
1623 method => 'retrieve_uri',
1624 api_name => 'open-ils.supercat.auri.marcxml.retrieve',
1628 { desc => <<" DESC",
1629 Returns a fleshed call number object
1634 desc => 'An OpenILS asset::uri id',
1638 { desc => 'fleshed uri',
1646 my $args = shift || {};
1648 return OpenILS::Application::SuperCat::unAPI
1649 ->new(OpenSRF::AppSession
1650 ->create( 'open-ils.cstore' )
1652 "open-ils.cstore.direct.asset.uri.retrieve",
1656 auri => [qw/call_number_maps/],
1657 auricnm => [qw/call_number/],
1658 acn => [qw/owning_lib record/],
1665 __PACKAGE__->register_method(
1666 method => 'retrieve_copy',
1667 api_name => 'open-ils.supercat.acp.marcxml.retrieve',
1671 { desc => <<" DESC",
1672 Returns a fleshed call number object
1677 desc => 'An OpenILS asset::copy id',
1681 { desc => 'fleshed copy',
1689 my $args = shift || {};
1691 return OpenILS::Application::SuperCat::unAPI
1692 ->new(OpenSRF::AppSession
1693 ->create( 'open-ils.cstore' )
1695 "open-ils.cstore.direct.asset.copy.retrieve",
1699 acn => [qw/owning_lib record/],
1700 acp => [qw/call_number location status circ_lib stat_cat_entries notes/],
1707 __PACKAGE__->register_method(
1708 method => 'retrieve_callnumber',
1709 api_name => 'open-ils.supercat.acn.marcxml.retrieve',
1714 { desc => <<" DESC",
1715 Returns a fleshed call number object
1720 desc => 'An OpenILS asset::call_number id',
1724 { desc => 'call number with copies',
1728 sub retrieve_callnumber {
1732 my $args = shift || {};
1734 return OpenILS::Application::SuperCat::unAPI
1735 ->new(OpenSRF::AppSession
1736 ->create( 'open-ils.cstore' )
1738 "open-ils.cstore.direct.asset.call_number.retrieve",
1742 acn => [qw/owning_lib record copies uri_maps/],
1743 auricnm => [qw/uri/],
1744 acp => [qw/location status circ_lib stat_cat_entries notes/],
1752 __PACKAGE__->register_method(
1753 method => 'basic_record_holdings',
1754 api_name => 'open-ils.supercat.record.basic_holdings.retrieve',
1759 { desc => <<" DESC",
1760 Returns a basic hash representation of the requested bibliographic record's holdings
1765 desc => 'An OpenILS biblio::record_entry id',
1769 { desc => 'Hash of bib record holdings hierarchy (call numbers and copies)',
1773 sub basic_record_holdings {
1779 # holdings hold an array of call numbers, which hold an array of copies
1780 # holdings => [ label: { library, [ copies: { barcode, location, status, circ_lib } ] } ]
1783 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1785 my $tree = $_storage->request(
1786 "open-ils.cstore.direct.biblio.record_entry.retrieve",
1790 bre => [qw/call_numbers/],
1791 acn => [qw/copies owning_lib/],
1792 acp => [qw/location status circ_lib/],
1797 my $o_search = { shortname => uc($ou) };
1798 if (!$ou || $ou eq '-') {
1799 $o_search = { parent_ou => undef };
1802 my $orgs = $_storage->request(
1803 "open-ils.cstore.direct.actor.org_unit.search",
1806 flesh_fields => { aou => [qw/children/] }
1810 my @ou_ids = tree_walker($orgs, 'children', sub {shift->id}) if $orgs;
1812 $logger->debug("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
1814 for my $cn (@{$tree->call_numbers}) {
1815 next unless ( $cn->deleted eq 'f' || $cn->deleted == 0 );
1818 for my $c (@{$cn->copies}) {
1819 next unless grep {$c->circ_lib->id == $_} @ou_ids;
1820 next unless ( $c->deleted eq 'f' || $c->deleted == 0 );
1826 $holdings{$cn->label}{'owning_lib'} = $cn->owning_lib->shortname;
1828 for my $cp (@{$cn->copies}) {
1830 next unless grep { $cp->circ_lib->id == $_ } @ou_ids;
1831 next unless ( $cp->deleted eq 'f' || $cp->deleted == 0 );
1833 push @{$holdings{$cn->label}{'copies'}}, {
1834 barcode => $cp->barcode,
1835 status => $cp->status->name,
1836 location => $cp->location->name,
1837 circlib => $cp->circ_lib->shortname
1846 #__PACKAGE__->register_method(
1847 # method => 'new_record_holdings',
1848 # api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
1853 # { desc => <<" DESC",
1854 #Returns the XML representation of the requested bibliographic record's holdings
1858 # { name => 'bibId',
1859 # desc => 'An OpenILS biblio::record_entry id',
1860 # type => 'number' },
1863 # { desc => 'Stream of bib record holdings hierarchy in XML',
1864 # type => 'string' }
1869 sub new_record_holdings {
1878 $paging = [-1,0] if (!$paging or !ref($paging) or @$paging == 0);
1879 my $limit = $$paging[0];
1880 my $offset = $$paging[1] || 0;
1882 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
1883 my $_search = OpenSRF::AppSession->create( 'open-ils.search' );
1885 my $o_search = { shortname => uc($ou) };
1886 if (!$ou || $ou eq '-') {
1887 $o_search = { parent_ou => undef };
1890 my $one_org = $_storage->request(
1891 "open-ils.cstore.direct.actor.org_unit.search",
1895 my $count_req = $_search->request('open-ils.search.biblio.record.copy_count' => $one_org->id => $bib);
1896 my $staff_count_req = $_search->request('open-ils.search.biblio.record.copy_count.staff' => $one_org->id => $bib);
1898 my $orgs = $_storage->request(
1899 'open-ils.cstore.json_query.atomic',
1900 { from => [ 'actor.org_unit_descendants', defined($depth) ? ( $one_org->id, $depth ) : ( $one_org->id ) ] }
1904 my @ou_ids = map { $_->{id} } @$orgs;
1906 $logger->info("Searching for holdings at orgs [".join(',',@ou_ids)."], based on $ou");
1908 my %subselect = ( '-or' => [
1909 { owning_lib => \@ou_ids },
1913 call_number => { '=' => {'+acn'=>'id'} },
1915 circ_lib => \@ou_ids
1921 if ($flesh and $flesh eq 'uris') {
1923 owning_lib => \@ou_ids,
1925 from => { auricnm => 'auri' },
1927 call_number => { '=' => {'+acn'=>'id'} },
1928 '+auri' => { active => 't' }
1935 my $cns = $_storage->request(
1936 "open-ils.cstore.direct.asset.call_number.search.atomic",
1943 acn => [qw/copies owning_lib uri_maps/],
1944 auricnm => [qw/uri/],
1945 acp => [qw/circ_lib location status stat_cat_entries notes/],
1946 asce => [qw/stat_cat/],
1948 ( $limit > -1 ? ( limit => $limit ) : () ),
1949 ( $offset ? ( offset => $offset ) : () ),
1950 order_by => { acn => { label_sortkey => {} } }
1954 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
1958 $client->respond("<holdings xmlns='http://open-ils.org/spec/holdings/v1'><counts>\n");
1960 my $copy_counts = $count_req->gather(1);
1961 my $staff_copy_counts = $staff_count_req->gather(1);
1963 for my $c (@$copy_counts) {
1964 $$c{transcendant} ||= 0;
1965 my $out = "<count type='public'";
1966 $out .= " $_='$$c{$_}'" for (qw/count available unshadow transcendant org_unit depth/);
1967 $client->respond("$out/>\n")
1970 for my $c (@$staff_copy_counts) {
1971 $$c{transcendant} ||= 0;
1972 my $out = "<count type='staff'";
1973 $out .= " $_='$$c{$_}'" for (qw/count available unshadow transcendant org_unit depth/);
1974 $client->respond("$out/>\n")
1977 $client->respond("</counts><volumes>\n");
1979 for my $cn (@$cns) {
1980 next unless (@{$cn->copies} > 0 or (ref($cn->uri_maps) and @{$cn->uri_maps}));
1982 # We don't want O:A:S:unAPI::acn to return the record, we've got that already
1983 # In the context of BibTemplate, copies aren't necessary because we pull those
1984 # in a separate call
1986 OpenILS::Application::SuperCat::unAPI::acn
1988 ->as_xml( {no_record => 1, no_copies => ($flesh ? 0 : 1)} )
1992 $client->respond("</volumes><subscriptions>\n");
1994 $logger->info("Searching for serial holdings at orgs [".join(',',@ou_ids)."], based on $ou");
1996 %subselect = ( '-or' => [
1997 { owning_lib => \@ou_ids },
2000 where => { holding_lib => \@ou_ids }
2005 my $ssubs = $_storage->request(
2006 "open-ils.cstore.direct.serial.subscription.search.atomic",
2007 { record_entry => $bib,
2012 ssub => [qw/distributions issuances scaps owning_lib/],
2013 sdist => [qw/basic_summary supplement_summary index_summary streams holding_lib/],
2014 sstr => [qw/items/],
2015 sitem => [qw/notes unit/],
2016 sunit => [qw/notes location status circ_lib stat_cat_entries call_number/],
2017 acn => [qw/owning_lib/],
2019 ( $limit > -1 ? ( limit => $limit ) : () ),
2020 ( $offset ? ( offset => $offset ) : () ),
2032 date_expected => {},
2039 for my $ssub (@$ssubs) {
2040 next unless (@{$ssub->distributions} or @{$ssub->issuances} or @{$ssub->scaps});
2042 # We don't want O:A:S:unAPI::ssub to return the record, we've got that already
2043 # In the context of BibTemplate, copies aren't necessary because we pull those
2044 # in a separate call
2046 OpenILS::Application::SuperCat::unAPI::ssub
2048 ->as_xml( {no_record => 1, no_items => ($flesh ? 0 : 1)} )
2053 return "</subscriptions></holdings>\n";
2055 __PACKAGE__->register_method(
2056 method => 'new_record_holdings',
2057 api_name => 'open-ils.supercat.record.holdings_xml.retrieve',
2062 { desc => <<" DESC",
2063 Returns the XML representation of the requested bibliographic record's holdings
2068 desc => 'An OpenILS biblio::record_entry ID',
2070 { name => 'orgUnit',
2071 desc => 'An OpenILS actor::org_unit short name that limits the scope of returned holdings',
2074 desc => 'An OpenILS actor::org_unit_type depththat limits the scope of returned holdings',
2076 { name => 'hideCopies',
2077 desc => 'Flag that prevents the inclusion of copies in the returned holdings',
2078 type => 'boolean' },
2080 desc => 'Arry of limit and offset for holdings paging',
2084 { desc => 'Stream of bib record holdings hierarchy in XML',
2094 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2096 my $recs = $_storage->request(
2097 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2098 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2101 return undef unless (@$recs);
2103 return ($self->method_lookup( 'open-ils.supercat.record.holdings_xml.retrieve')->run( $recs->[0]->record ))[0];
2105 __PACKAGE__->register_method(
2106 method => 'isbn_holdings',
2107 api_name => 'open-ils.supercat.isbn.holdings_xml.retrieve',
2111 { desc => <<" DESC",
2112 Returns the XML representation of the requested bibliographic record's holdings
2121 { desc => 'The bib record holdings hierarchy in XML',
2129 return '' unless $text;
2130 $text =~ s/&/&/gsom;
2131 $text =~ s/</</gsom;
2132 $text =~ s/>/>/gsom;
2133 $text =~ s/"/"/gsom;
2134 $text =~ s/'/'/gsom;
2138 sub recent_changes {
2141 my $when = shift || '1-01-01';
2144 my $type = 'biblio';
2147 if ($self->api_name =~ /authority/o) {
2148 $type = 'authority';
2152 my $axis = 'create_date';
2153 $axis = 'edit_date' if ($self->api_name =~ /edit/o);
2155 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2157 return $_storage->request(
2158 "open-ils.cstore.direct.$type.record_entry.id_list.atomic",
2159 { $axis => { ">" => $when }, id => { '>' => 0 }, deleted => 'f', active => 't' },
2160 { order_by => { $hint => "$axis desc" }, limit => $limit }
2164 for my $t ( qw/biblio authority/ ) {
2165 for my $a ( qw/import edit/ ) {
2167 __PACKAGE__->register_method(
2168 method => 'recent_changes',
2169 api_name => "open-ils.supercat.$t.record.$a.recent",
2173 { desc => "Returns a list of recently ${a}ed $t records",
2177 desc => "Date to start looking for ${a}ed records",
2178 default => '1-01-01',
2182 desc => "Maximum count to retrieve",
2186 { desc => "An id list of $t records",
2194 sub retrieve_authority_marcxml {
2199 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2201 my $record = $_storage->request( 'open-ils.cstore.direct.authority.record_entry.retrieve' => $rid )->gather(1);
2202 return $U->entityize( $record->marc ) if ($record);
2206 __PACKAGE__->register_method(
2207 method => 'retrieve_authority_marcxml',
2208 api_name => 'open-ils.supercat.authority.marcxml.retrieve',
2212 { desc => <<" DESC",
2213 Returns the MARCXML representation of the requested authority record
2217 { name => 'authorityId',
2218 desc => 'An OpenILS authority::record_entry id',
2222 { desc => 'The authority record in MARCXML',
2227 sub retrieve_record_marcxml {
2232 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2234 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $rid )->gather(1);
2235 return $U->entityize( $record->marc ) if ($record);
2239 __PACKAGE__->register_method(
2240 method => 'retrieve_record_marcxml',
2241 api_name => 'open-ils.supercat.record.marcxml.retrieve',
2245 { desc => <<" DESC",
2246 Returns the MARCXML representation of the requested bibliographic record
2251 desc => 'An OpenILS biblio::record_entry id',
2255 { desc => 'The bib record in MARCXML',
2260 sub retrieve_isbn_marcxml {
2265 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2267 my $recs = $_storage->request(
2268 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2269 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2272 return undef unless (@$recs);
2274 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
2275 return $U->entityize( $record->marc ) if ($record);
2279 __PACKAGE__->register_method(
2280 method => 'retrieve_isbn_marcxml',
2281 api_name => 'open-ils.supercat.isbn.marcxml.retrieve',
2285 { desc => <<" DESC",
2286 Returns the MARCXML representation of the requested ISBN
2291 desc => 'An ... um ... ISBN',
2295 { desc => 'The bib record in MARCXML',
2300 sub retrieve_record_transform {
2305 (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
2307 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2308 #$_storage->connect;
2310 my $record = $_storage->request(
2311 'open-ils.cstore.direct.biblio.record_entry.retrieve',
2315 return undef unless ($record);
2317 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
2320 sub retrieve_isbn_transform {
2325 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2327 my $recs = $_storage->request(
2328 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2329 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2332 return undef unless (@$recs);
2334 (my $transform = $self->api_name) =~ s/^.+isbn\.([^\.]+)\.retrieve$/$1/o;
2336 my $record = $_storage->request( 'open-ils.cstore.direct.biblio.record_entry.retrieve' => $recs->[0]->record )->gather(1);
2338 return undef unless ($record);
2340 return $U->entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $record->marc ) )->toString);
2343 sub retrieve_record_objects {
2348 $ids = [$ids] unless (ref $ids);
2349 $ids = [grep {$_} @$ids];
2351 return [] unless (@$ids);
2353 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2354 return $_storage->request('open-ils.cstore.direct.biblio.record_entry.search.atomic' => { id => [grep {$_} @$ids] })->gather(1);
2356 __PACKAGE__->register_method(
2357 method => 'retrieve_record_objects',
2358 api_name => 'open-ils.supercat.record.object.retrieve',
2362 { desc => <<" DESC",
2363 Returns the Fieldmapper object representation of the requested bibliographic records
2368 desc => 'OpenILS biblio::record_entry ids',
2372 { desc => 'The bib records',
2378 sub retrieve_isbn_object {
2383 return undef unless ($isbn);
2385 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2386 my $recs = $_storage->request(
2387 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2388 { tag => { like => '02%'}, value => {like => "$isbn\%"}}
2391 return undef unless (@$recs);
2393 return $_storage->request(
2394 'open-ils.cstore.direct.biblio.record_entry.search.atomic',
2395 { id => $recs->[0]->record }
2398 __PACKAGE__->register_method(
2399 method => 'retrieve_isbn_object',
2400 api_name => 'open-ils.supercat.isbn.object.retrieve',
2404 { desc => <<" DESC",
2405 Returns the Fieldmapper object representation of the requested bibliographic record
2414 { desc => 'The bib record',
2421 sub retrieve_metarecord_mods {
2426 my $_storage = OpenSRF::AppSession->connect( 'open-ils.cstore' );
2428 # Get the metarecord in question
2431 'open-ils.cstore.direct.metabib.metarecord.retrieve' => $rid
2434 # Now get the map of all bib records for the metarecord
2437 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2438 {metarecord => $rid}
2441 $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
2443 # and retrieve the lead (master) record as MODS
2445 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
2446 ->run($mr->master_record);
2447 my $master_mods = $_parser->parse_string($master)->documentElement;
2448 $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods" );
2449 $master_mods->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2451 # ... and a MODS clone to populate, with guts removed.
2452 my $mods = $_parser->parse_string($master)->documentElement;
2453 $mods->setNamespace( "http://www.loc.gov/mods/", "mods" ); # modsCollection element
2454 $mods->setNamespace('http://www.loc.gov/mods/', undef, 1);
2455 ($mods) = $mods->findnodes('//mods:mods');
2456 #$mods->setNamespace( "http://www.loc.gov/mods/", "mods" ); # mods element
2457 $mods->removeChildNodes;
2458 $mods->setNamespace('http://www.loc.gov/mods/', undef, 1);
2460 # Add the metarecord ID as a (locally defined) info URI
2461 my $recordInfo = $mods
2463 ->createElement("recordInfo");
2465 my $recordIdentifier = $mods
2467 ->createElement("recordIdentifier");
2469 my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
2474 $recordIdentifier->appendTextNode(
2475 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
2478 $recordInfo->appendChild($recordIdentifier);
2479 $mods->appendChild($recordInfo);
2481 # Grab the title, author and ISBN for the master record and populate the metarecord
2482 my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
2485 $title->setNamespace( "http://www.loc.gov/mods/", "mods" );
2486 $title->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2487 $title = $mods->ownerDocument->importNode($title);
2488 $mods->appendChild($title);
2491 my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
2493 $author->setNamespace( "http://www.loc.gov/mods/", "mods" );
2494 $author->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2495 $author = $mods->ownerDocument->importNode($author);
2496 $mods->appendChild($author);
2499 my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
2501 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods" );
2502 $isbn->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2503 $isbn = $mods->ownerDocument->importNode($isbn);
2504 $mods->appendChild($isbn);
2507 # ... and loop over the constituent records
2508 for my $map ( @$recs ) {
2512 $self ->method_lookup('open-ils.supercat.record.mods.retrieve')
2513 ->run($map->source);
2515 my $part_mods = $_parser->parse_string($rec);
2516 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods" );
2517 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2518 ($part_mods) = $part_mods->findnodes('//mods:mods');
2520 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
2521 $node->setNamespace( "http://www.loc.gov/mods/", "mods" );
2522 $node->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2523 $node = $mods->ownerDocument->importNode($node);
2524 $mods->appendChild( $node );
2527 my $relatedItem = $mods
2529 ->createElement("relatedItem");
2531 $relatedItem->setAttribute( type => 'constituent' );
2533 my $identifier = $mods
2535 ->createElement("identifier");
2537 $identifier->setAttribute( type => 'uri' );
2539 my $subRecordInfo = $mods
2541 ->createElement("recordInfo");
2543 my $subRecordIdentifier = $mods
2545 ->createElement("recordIdentifier");
2547 my $subid = $map->source;
2548 $subRecordIdentifier->appendTextNode(
2549 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
2554 $subRecordInfo->appendChild($subRecordIdentifier);
2556 $relatedItem->appendChild( $subRecordInfo );
2558 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
2559 $tor->setNamespace( "http://www.loc.gov/mods/", "mods" );
2560 $tor->setNamespace( "http://www.loc.gov/mods/", undef, 1 ) if ($tor);
2561 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
2562 $relatedItem->appendChild($tor) if ($tor);
2564 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
2565 $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods" );
2566 $part_isbn->setNamespace( "http://www.loc.gov/mods/", undef, 1 );
2567 $part_isbn = $mods->ownerDocument->importNode($part_isbn);
2568 $relatedItem->appendChild( $part_isbn );
2571 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
2575 $mods->appendChild( $relatedItem );
2579 $_storage->disconnect;
2581 return $U->entityize($mods->toString);
2584 __PACKAGE__->register_method(
2585 method => 'retrieve_metarecord_mods',
2586 api_name => 'open-ils.supercat.metarecord.mods.retrieve',
2590 { desc => <<" DESC",
2591 Returns the MODS representation of the requested metarecord
2595 { name => 'metarecordId',
2596 desc => 'An OpenILS metabib::metarecord id',
2600 { desc => 'The metarecord in MODS',
2605 sub list_metarecord_formats {
2608 { namespace_uri => 'http://www.loc.gov/mods/',
2609 docs => 'http://www.loc.gov/mods/',
2610 schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
2615 for my $type ( keys %metarecord_xslt ) {
2618 { namespace_uri => $metarecord_xslt{$type}{namespace_uri},
2619 docs => $metarecord_xslt{$type}{docs},
2620 schema_location => $metarecord_xslt{$type}{schema_location},
2627 __PACKAGE__->register_method(
2628 method => 'list_metarecord_formats',
2629 api_name => 'open-ils.supercat.metarecord.formats',
2633 { desc => <<" DESC",
2634 Returns the list of valid metarecord formats that supercat understands.
2637 { desc => 'The format list',
2643 sub list_authority_formats {
2646 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
2647 docs => 'http://www.loc.gov/marcxml/',
2648 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
2653 # for my $type ( keys %record_xslt ) {
2656 # { namespace_uri => $record_xslt{$type}{namespace_uri},
2657 # docs => $record_xslt{$type}{docs},
2658 # schema_location => $record_xslt{$type}{schema_location},
2665 __PACKAGE__->register_method(
2666 method => 'list_authority_formats',
2667 api_name => 'open-ils.supercat.authority.formats',
2671 { desc => <<" DESC",
2672 Returns the list of valid authority formats that supercat understands.
2675 { desc => 'The format list',
2680 sub list_record_formats {
2683 { namespace_uri => 'http://www.loc.gov/MARC21/slim',
2684 docs => 'http://www.loc.gov/marcxml/',
2685 schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
2690 for my $type ( keys %record_xslt ) {
2693 { namespace_uri => $record_xslt{$type}{namespace_uri},
2694 docs => $record_xslt{$type}{docs},
2695 schema_location => $record_xslt{$type}{schema_location},
2702 __PACKAGE__->register_method(
2703 method => 'list_record_formats',
2704 api_name => 'open-ils.supercat.record.formats',
2708 { desc => <<" DESC",
2709 Returns the list of valid record formats that supercat understands.
2712 { desc => 'The format list',
2716 __PACKAGE__->register_method(
2717 method => 'list_record_formats',
2718 api_name => 'open-ils.supercat.isbn.formats',
2722 { desc => <<" DESC",
2723 Returns the list of valid record formats that supercat understands.
2726 { desc => 'The format list',
2739 throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
2740 unless (length($isbn) >= 10);
2742 my $_storage = OpenSRF::AppSession->create( 'open-ils.cstore' );
2744 # Create a storage session, since we'll be making muliple requests.
2747 # Find the record that has that ISBN.
2748 my $bibrec = $_storage->request(
2749 'open-ils.cstore.direct.metabib.full_rec.search.atomic',
2750 { tag => '020', subfield => 'a', value => { like => lc($isbn).'%'} }
2753 # Go away if we don't have one.
2754 return {} unless (@$bibrec);
2756 # Find the metarecord for that bib record.
2757 my $mr = $_storage->request(
2758 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2759 {source => $bibrec->[0]->record}
2762 # Find the other records for that metarecord.
2763 my $records = $_storage->request(
2764 'open-ils.cstore.direct.metabib.metarecord_source_map.search.atomic',
2765 {metarecord => $mr->[0]->metarecord}
2768 # Just to be safe. There's currently no unique constraint on sources...
2769 my %unique_recs = map { ($_->source, 1) } @$records;
2770 my @rec_list = sort keys %unique_recs;
2772 # And now fetch the ISBNs for thos records.
2776 'open-ils.cstore.direct.metabib.full_rec.search',
2777 { tag => '020', subfield => 'a', record => $_ }
2778 )->gather(1) for (@rec_list);
2780 # We're done with the storage server session.
2781 $_storage->disconnect;
2783 # Return the oISBN data structure. This will be XMLized at a higher layer.
2785 { metarecord => $mr->[0]->metarecord,
2786 record_list => { map { $_ ? ($_->record, $_->value) : () } @$recs } };
2789 __PACKAGE__->register_method(
2791 api_name => 'open-ils.supercat.oisbn',
2795 { desc => <<" DESC",
2796 Returns the ISBN list for the metarecord of the requested isbn
2801 desc => 'An ISBN. Duh.',
2805 { desc => 'record to isbn map',
2810 package OpenILS::Application::SuperCat::unAPI;
2811 use base qw/OpenILS::Application::SuperCat/;
2814 die "dummy superclass, use a real class";
2820 return unless ($obj);
2822 $class = ref($class) || $class;
2824 if ($class eq __PACKAGE__) {
2825 return unless (ref($obj));
2826 $class .= '::' . $obj->json_hint;
2829 return bless { obj => $obj } => $class;
2834 return $self->{obj};
2837 package OpenILS::Application::SuperCat::unAPI::auri;
2838 use base qw/OpenILS::Application::SuperCat::unAPI/;
2844 my $xml = ' <uri xmlns="http://open-ils.org/spec/holdings/v1" ';
2845 $xml .= 'id="tag:open-ils.org:asset-uri/' . $self->obj->id . '" ';
2846 $xml .= 'use_restriction="' . $self->escape( $self->obj->use_restriction ) . '" ';
2847 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
2848 $xml .= 'href="' . $self->escape( $self->obj->href ) . '">';
2850 if (!$args->{no_volumes}) {
2851 if (ref($self->obj->call_number_maps) && @{ $self->obj->call_number_maps }) {
2852 $xml .= " <volumes>\n" . join(
2855 OpenILS::Application::SuperCat::unAPI
2856 ->new( $_->call_number )
2857 ->as_xml({ %$args, no_uris=>1, no_copies=>1 })
2858 } @{ $self->obj->call_number_maps }
2859 ) . " </volumes>\n";
2862 $xml .= " <volumes/>\n";
2866 $xml .= " </uri>\n";
2871 package OpenILS::Application::SuperCat::unAPI::acn;
2872 use base qw/OpenILS::Application::SuperCat::unAPI/;
2878 my $xml = ' <volume xmlns="http://open-ils.org/spec/holdings/v1" ';
2880 $xml .= 'id="tag:open-ils.org:asset-call_number/' . $self->obj->id . '" ';
2881 $xml .= 'lib="' . $self->escape( $self->obj->owning_lib->shortname ) . '" ';
2882 $xml .= 'opac_visible="' . $self->obj->owning_lib->opac_visible . '" ';
2883 $xml .= 'deleted="' . $self->obj->deleted . '" ';
2884 $xml .= 'label="' . $self->escape( $self->obj->label ) . '">';
2887 if (!$args->{no_copies}) {
2888 if (ref($self->obj->copies) && @{ $self->obj->copies }) {
2889 $xml .= " <copies>\n" . join(
2892 OpenILS::Application::SuperCat::unAPI
2894 ->as_xml({ %$args, no_volume=>1 })
2895 } @{ $self->obj->copies }
2899 $xml .= " <copies/>\n";
2903 if (!$args->{no_uris}) {
2904 if (ref($self->obj->uri_maps) && @{ $self->obj->uri_maps }) {
2905 $xml .= " <uris>\n" . join(
2908 OpenILS::Application::SuperCat::unAPI
2910 ->as_xml({ %$args, no_volumes=>1 })
2911 } @{ $self->obj->uri_maps }
2915 $xml .= " <uris/>\n";
2920 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
2921 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
2922 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
2923 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
2926 unless ($args->{no_record}) {
2927 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
2929 my $r_doc = $parser->parse_string($self->obj->record->marc);
2930 $r_doc->documentElement->setAttribute( id => $rec_tag );
2931 $xml .= $U->entityize($r_doc->documentElement->toString);
2934 $xml .= " </volume>\n";
2939 package OpenILS::Application::SuperCat::unAPI::ssub;
2940 use base qw/OpenILS::Application::SuperCat::unAPI/;
2946 my $xml = ' <subscription xmlns="http://open-ils.org/spec/holdings/v1" ';
2948 $xml .= 'id="tag:open-ils.org:serial-subscription/' . $self->obj->id . '" ';
2949 $xml .= 'start="' . $self->escape( $self->obj->start_date ) . '" ';
2950 $xml .= 'end="' . $self->escape( $self->obj->end_date ) . '" ';
2951 $xml .= 'expected_date_offset="' . $self->escape( $self->obj->expected_date_offset ) . '">';
2954 if (!$args->{no_distributions}) {
2955 if (ref($self->obj->distributions) && @{ $self->obj->distributions }) {
2956 $xml .= " <distributions>\n" . join(
2959 OpenILS::Application::SuperCat::unAPI
2961 ->as_xml({ %$args, no_subscription=>1, no_issuance=>1 })
2962 } @{ $self->obj->distributions }
2963 ) . " </distributions>\n";
2966 $xml .= " <distributions/>\n";
2970 if (!$args->{no_captions_and_patterns}) {
2971 if (ref($self->obj->scaps) && @{ $self->obj->scaps }) {
2972 $xml .= " <captions_and_patterns>\n" . join(
2975 OpenILS::Application::SuperCat::unAPI
2977 ->as_xml({ %$args, no_subscription=>1 })
2978 } @{ $self->obj->scaps }
2979 ) . " </captions_and_patterns>\n";
2982 $xml .= " <captions_and_patterns/>\n";
2986 if (!$args->{no_issuances}) {
2987 if (ref($self->obj->issuances) && @{ $self->obj->issuances }) {
2988 $xml .= " <issuances>\n" . join(
2991 OpenILS::Application::SuperCat::unAPI
2993 ->as_xml({ %$args, no_subscription=>1, no_items=>1 })
2994 } @{ $self->obj->issuances }
2995 ) . " </issuances>\n";
2998 $xml .= " <issuances/>\n";
3003 $xml .= ' <owning_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3004 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->owning_lib->id . '" ';
3005 $xml .= 'shortname="'.$self->escape( $self->obj->owning_lib->shortname ) .'" ';
3006 $xml .= 'name="'.$self->escape( $self->obj->owning_lib->name ) .'"/>';
3009 unless ($args->{no_record}) {
3010 my $rec_tag = "tag:open-ils.org:biblio-record_entry/".$self->obj->record->id.'/'.$self->escape( $self->obj->owning_lib->shortname ) ;
3012 my $r_doc = $parser->parse_string($self->obj->record_entry->marc);
3013 $r_doc->documentElement->setAttribute( id => $rec_tag );
3014 $xml .= $U->entityize($r_doc->documentElement->toString);
3017 $xml .= " </subscription>\n";
3022 package OpenILS::Application::SuperCat::unAPI::ssum_base;
3023 use base qw/OpenILS::Application::SuperCat::unAPI/;
3029 (my $type = ref($self)) =~ s/^.+([^:]+)$/$1/;
3031 my $xml = " <serial_summary xmlns=\"http://open-ils.org/spec/holdings/v1\" type=\"$type\" ";
3033 $xml .= "id=\"tag:open-ils.org:serial-summary-$type/" . $self->obj->id . '" ';
3034 $xml .= 'generated_coverage="' . $self->escape( $self->obj->generated_coverage ) . '" ';
3035 $xml .= 'show_generated="' . $self->escape( $self->obj->show_generated ) . '" ';
3036 $xml .= 'textual_holdings="' . $self->escape( $self->obj->textual_holdings ) . '">';
3039 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->distribution )->as_xml({ %$args, no_summaries=>1 }) if (!$args->{no_distribution});
3041 $xml .= " </serial_summary>\n";
3047 package OpenILS::Application::SuperCat::unAPI::sssum;
3048 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3050 package OpenILS::Application::SuperCat::unAPI::sbsum;
3051 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3053 package OpenILS::Application::SuperCat::unAPI::sisum;
3054 use base qw/OpenILS::Application::SuperCat::unAPI::ssum_base/;
3056 package OpenILS::Application::SuperCat::unAPI::sdist;
3057 use base qw/OpenILS::Application::SuperCat::unAPI/;
3063 my $xml = ' <distribution xmlns="http://open-ils.org/spec/holdings/v1" ';
3065 $xml .= 'id="tag:open-ils.org:serial-distribution/' . $self->obj->id . '" ';
3066 $xml .= 'label="' . $self->escape( $self->obj->label ) . '" ';
3067 $xml .= 'unit_label_prefix="' . $self->escape( $self->obj->unit_label_prefix ) . '" ';
3068 $xml .= 'unit_label_suffix="' . $self->escape( $self->obj->unit_label_suffix ) . '">';
3071 if (!$args->{no_distributions}) {
3072 if (ref($self->obj->streams) && @{ $self->obj->streams }) {
3073 $xml .= " <streams>\n" . join(
3076 OpenILS::Application::SuperCat::unAPI
3078 ->as_xml({ %$args, no_distribution=>1 })
3079 } @{ $self->obj->streams }
3080 ) . " </streams>\n";
3083 $xml .= " <streams/>\n";
3087 if (!$args->{no_summaries}) {
3088 $xml .= " <summaries>\n";
3092 OpenILS::Application::SuperCat::unAPI
3094 ->as_xml({ %$args, no_distribution=>1 }) : ""
3095 } ($self->obj->basic_summary, $self->obj->supplement_summary, $self->obj->index_summary)
3098 $xml .= " </summaries>\n";
3102 $xml .= ' <holding_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3103 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->holding_lib->id . '" ';
3104 $xml .= 'shortname="'.$self->escape( $self->obj->holding_lib->shortname ) .'" ';
3105 $xml .= 'name="'.$self->escape( $self->obj->holding_lib->name ) .'"/>';
3108 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_distributions=>1 }) if (!$args->{no_subscription});
3110 if (!$args->{no_record} && $self->obj->record_entry) {
3111 my $rec_tag = "tag:open-ils.org:serial-record_entry/".$self->obj->record_entry->id ;
3113 my $r_doc = $parser->parse_string($self->obj->record_entry->marc);
3114 $r_doc->documentElement->setAttribute( id => $rec_tag );
3115 $xml .= $U->entityize($r_doc->documentElement->toString);
3118 $xml .= " </distribution>\n";
3123 package OpenILS::Application::SuperCat::unAPI::sstr;
3124 use base qw/OpenILS::Application::SuperCat::unAPI/;
3130 my $xml = ' <stream xmlns="http://open-ils.org/spec/holdings/v1" ';
3132 $xml .= 'id="tag:open-ils.org:serial-stream/' . $self->obj->id . '" ';
3133 $xml .= 'routing_label="' . $self->escape( $self->obj->routing_label ) . '">';
3136 if (!$args->{no_items}) {
3137 if (ref($self->obj->items) && @{ $self->obj->items }) {
3138 $xml .= " <items>\n" . join(
3141 OpenILS::Application::SuperCat::unAPI
3143 ->as_xml({ %$args, no_stream=>1 })
3144 } @{ $self->obj->items }
3148 $xml .= " <items/>\n";
3152 #XXX routing_list_user's?
3154 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->distribution )->as_xml({ %$args, no_streams=>1 }) if (!$args->{no_distribution});
3156 $xml .= " </stream>\n";
3161 package OpenILS::Application::SuperCat::unAPI::sitem;
3162 use base qw/OpenILS::Application::SuperCat::unAPI/;
3168 my $xml = ' <serial_item xmlns="http://open-ils.org/spec/holdings/v1" ';
3170 $xml .= 'id="tag:open-ils.org:serial-item/' . $self->obj->id . '" ';
3171 $xml .= 'date_expected="' . $self->escape( $self->obj->date_expected ) . '"';
3172 $xml .= ' date_received="' . $self->escape( $self->obj->date_received ) .'"'if ($self->obj->date_received);
3174 if ($args->{no_issuance}) {
3175 my $siss = ref($self->obj->issuance) ? $self->obj->issuance->id : $self->obj->issuance;
3176 $xml .= ' issuance="tag:open-ils.org:serial-issuance/' . $siss . '"';
3181 if (ref($self->obj->notes) && $self->obj->notes) {
3182 $xml .= " <notes>\n";
3183 for my $note ( @{$self->obj->notes} ) {
3184 next unless ( $note->pub eq 't' );
3185 $xml .= sprintf(' <note date="%s" title="%s">%s</note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3188 $xml .= " </notes>\n";
3190 $xml .= " <notes/>\n";
3193 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->issuance )->as_xml({ %$args, no_items=>1 }) if (!$args->{no_issuance});
3194 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->stream )->as_xml({ %$args, no_items=>1 }) if (!$args->{no_stream});
3195 $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});
3196 $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});
3198 $xml .= " </serial_item>\n";
3203 package OpenILS::Application::SuperCat::unAPI::sunit;
3204 use base qw/OpenILS::Application::SuperCat::unAPI/;
3210 my $xml = ' <serial_unit xmlns="http://open-ils.org/spec/holdings/v1" '.
3211 'id="tag:open-ils.org:serial-unit/' . $self->obj->id . '" ';
3213 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3214 create_date edit_date copy_number circulate deposit ref holdable deleted
3215 deposit_amount price barcode circ_modifier circ_as_type opac_visible cost
3216 status_changed_time floating mint_condition detailed_contents sort_key summary_contents
3221 $xml .= ' <status ident="' . $self->obj->status->id . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
3222 $xml .= ' <location ident="' . $self->obj->location->id . '">' . $self->escape( $self->obj->location->name ) . "</location>\n";
3223 $xml .= ' <circlib ident="' . $self->obj->circ_lib->id . '">' . $self->escape( $self->obj->circ_lib->name ) . "</circlib>\n";
3225 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3226 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
3227 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
3228 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'"/>';
3231 $xml .= " <copy_notes>\n";
3232 if (ref($self->obj->notes) && $self->obj->notes) {
3233 for my $note ( @{$self->obj->notes} ) {
3234 next unless ( $note->pub eq 't' );
3235 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3240 $xml .= " </copy_notes>\n";
3241 $xml .= " <statcats>\n";
3243 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
3244 for my $sce ( @{$self->obj->stat_cat_entries} ) {
3245 next unless ( $sce->stat_cat->opac_visible eq 't' );
3246 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
3250 $xml .= " </statcats>\n";
3252 unless ($args->{no_volume}) {
3253 if (ref($self->obj->call_number)) {
3254 $xml .= OpenILS::Application::SuperCat::unAPI
3255 ->new( $self->obj->call_number )
3256 ->as_xml({ %$args, no_copies=>1 });
3258 $xml .= " <volume/>\n";
3262 $xml .= " </serial_unit>\n";
3267 package OpenILS::Application::SuperCat::unAPI::scap;
3268 use base qw/OpenILS::Application::SuperCat::unAPI/;
3274 my $xml = ' <caption_and_pattern xmlns="http://open-ils.org/spec/holdings/v1" '.
3275 'id="tag:open-ils.org:serial-caption_and_pattern/' . $self->obj->id . '" ';
3277 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3278 create_date type active pattern_code enum_1 enum_2 enum_3 enum_4
3279 enum_5 enum_6 chron_1 chron_2 chron_3 chron_4 chron_5 start_date end_date
3282 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_captions_and_patterns=>1 }) if (!$args->{no_subscription});
3283 $xml .= " </caption_and_pattern>\n";
3288 package OpenILS::Application::SuperCat::unAPI::siss;
3289 use base qw/OpenILS::Application::SuperCat::unAPI/;
3295 my $xml = ' <issuance xmlns="http://open-ils.org/spec/holdings/v1" '.
3296 'id="tag:open-ils.org:serial-issuance/' . $self->obj->id . '" ';
3298 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" '
3299 for (qw/create_date edit_date label date_published holding_code holding_type holding_link_id/);
3303 if (!$args->{no_items}) {
3304 if (ref($self->obj->items) && @{ $self->obj->items }) {
3305 $xml .= " <items>\n" . join(
3308 OpenILS::Application::SuperCat::unAPI
3310 ->as_xml({ %$args, no_stream=>1 })
3311 } @{ $self->obj->items }
3315 $xml .= " <items/>\n";
3319 $xml .= OpenILS::Application::SuperCat::unAPI->new( $self->obj->subscription )->as_xml({ %$args, no_issuances=>1 }) if (!$args->{no_subscription});
3320 $xml .= " </issuance>\n";
3325 package OpenILS::Application::SuperCat::unAPI::acp;
3326 use base qw/OpenILS::Application::SuperCat::unAPI/;
3332 my $xml = ' <copy xmlns="http://open-ils.org/spec/holdings/v1" '.
3333 'id="tag:open-ils.org:asset-copy/' . $self->obj->id . '" ';
3335 $xml .= $_ . '="' . $self->escape( $self->obj->$_ ) . '" ' for (qw/
3336 create_date edit_date copy_number circulate deposit ref holdable deleted
3337 deposit_amount price barcode circ_modifier circ_as_type opac_visible
3342 $xml .= ' <status ident="' . $self->obj->status->id . '">' . $self->escape( $self->obj->status->name ) . "</status>\n";
3343 $xml .= ' <location ident="' . $self->obj->location->id . '" opac_visible="'.$self->obj->location->opac_visible.'">' . $self->escape( $self->obj->location->name ) . "</location>\n";
3344 $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";
3346 $xml .= ' <circ_lib xmlns="http://open-ils.org/spec/actors/v1" ';
3347 $xml .= 'id="tag:open-ils.org:actor-org_unit/' . $self->obj->circ_lib->id . '" ';
3348 $xml .= 'shortname="'.$self->escape( $self->obj->circ_lib->shortname ) .'" ';
3349 $xml .= 'name="'.$self->escape( $self->obj->circ_lib->name ) .'" opac_visible="'.$self->obj->circ_lib->opac_visible.'"/>';
3352 $xml .= " <copy_notes>\n";
3353 if (ref($self->obj->notes) && $self->obj->notes) {
3354 for my $note ( @{$self->obj->notes} ) {
3355 next unless ( $note->pub eq 't' );
3356 $xml .= sprintf(' <copy_note date="%s" title="%s">%s</copy_note>',$note->create_date, $self->escape($note->title), $self->escape($note->value));
3361 $xml .= " </copy_notes>\n";
3362 $xml .= " <statcats>\n";
3364 if (ref($self->obj->stat_cat_entries) && $self->obj->stat_cat_entries) {
3365 for my $sce ( @{$self->obj->stat_cat_entries} ) {
3366 next unless ( $sce->stat_cat->opac_visible eq 't' );
3367 $xml .= sprintf(' <statcat name="%s">%s</statcat>',$self->escape($sce->stat_cat->name) ,$self->escape($sce->value));
3371 $xml .= " </statcats>\n";
3373 unless ($args->{no_volume}) {
3374 if (ref($self->obj->call_number)) {
3375 $xml .= OpenILS::Application::SuperCat::unAPI
3376 ->new( $self->obj->call_number )
3377 ->as_xml({ %$args, no_copies=>1 });
3379 $xml .= " <volume/>\n";
3383 $xml .= " </copy>\n";
3390 # vim: noet:ts=4:sw=4