]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/SuperCat.pm
becceb3ff10438a0239ba1674d645d38609282b2
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / SuperCat.pm
1 package OpenILS::Application::SuperCat;
2
3 use strict;
4 use warnings;
5
6 # All OpenSRF applications must be based on OpenSRF::Application or
7 # a subclass thereof.  Makes sense, eh?
8 use OpenSRF::Application;
9 use base qw/OpenSRF::Application/;
10
11 # This is the client class, used for connecting to open-ils.storage
12 use OpenSRF::AppSession;
13
14 # This is an extention of Error.pm that supplies some error types to throw
15 use OpenSRF::EX qw(:try);
16
17 # This is a helper class for querying the OpenSRF Settings application ...
18 use OpenSRF::Utils::SettingsClient;
19
20 # ... and here we have the built in logging helper ...
21 use OpenSRF::Utils::Logger qw($logger);
22
23 # ... and this is our OpenILS object (en|de)coder and psuedo-ORM package.
24 use OpenILS::Utils::Fieldmapper;
25
26
27 # We'll be working with XML, so...
28 use XML::LibXML;
29 use XML::LibXSLT;
30 use Unicode::Normalize;
31
32 use JSON;
33
34 our (
35   $_parser,
36   $_xslt,
37   %record_xslt,
38   %metarecord_xslt,
39   %holdings_data_cache,
40 );
41
42 sub child_init {
43         # we need an XML parser
44         $_parser = new XML::LibXML;
45
46         # and an xslt parser
47         $_xslt = new XML::LibXSLT;
48         
49         # parse the MODS xslt ...
50         my $mods3_xslt = $_parser->parse_file(
51                 OpenSRF::Utils::SettingsClient
52                         ->new
53                         ->config_value( dirs => 'xsl' ).
54                 "/MARC21slim2MODS3.xsl"
55         );
56         # and stash a transformer
57         $record_xslt{mods3}{xslt} = $_xslt->parse_stylesheet( $mods3_xslt );
58         $record_xslt{mods3}{namespace_uri} = 'http://www.loc.gov/mods/v3';
59         $record_xslt{mods3}{docs} = 'http://www.loc.gov/mods/';
60         $record_xslt{mods3}{schema_location} = 'http://www.loc.gov/standards/mods/v3/mods-3-1.xsd';
61
62         # parse the MODS xslt ...
63         my $mods_xslt = $_parser->parse_file(
64                 OpenSRF::Utils::SettingsClient
65                         ->new
66                         ->config_value( dirs => 'xsl' ).
67                 "/MARC21slim2MODS.xsl"
68         );
69         # and stash a transformer
70         $record_xslt{mods}{xslt} = $_xslt->parse_stylesheet( $mods_xslt );
71         $record_xslt{mods}{namespace_uri} = 'http://www.loc.gov/mods/';
72         $record_xslt{mods}{docs} = 'http://www.loc.gov/mods/';
73         $record_xslt{mods}{schema_location} = 'http://www.loc.gov/standards/mods/mods.xsd';
74
75         # parse the ATOM entry xslt ...
76         my $atom_xslt = $_parser->parse_file(
77                 OpenSRF::Utils::SettingsClient
78                         ->new
79                         ->config_value( dirs => 'xsl' ).
80                 "/MARC21slim2ATOM.xsl"
81         );
82         # and stash a transformer
83         $record_xslt{atom}{xslt} = $_xslt->parse_stylesheet( $atom_xslt );
84         $record_xslt{atom}{namespace_uri} = 'http://www.w3.org/2005/Atom';
85         $record_xslt{atom}{docs} = 'http://www.ietf.org/rfc/rfc4287.txt';
86
87         # parse the RDFDC xslt ...
88         my $rdf_dc_xslt = $_parser->parse_file(
89                 OpenSRF::Utils::SettingsClient
90                         ->new
91                         ->config_value( dirs => 'xsl' ).
92                 "/MARC21slim2RDFDC.xsl"
93         );
94         # and stash a transformer
95         $record_xslt{rdf_dc}{xslt} = $_xslt->parse_stylesheet( $rdf_dc_xslt );
96         $record_xslt{rdf_dc}{namespace_uri} = 'http://purl.org/dc/elements/1.1/';
97         $record_xslt{rdf_dc}{schema_location} = 'http://purl.org/dc/elements/1.1/';
98
99         # parse the SRWDC xslt ...
100         my $srw_dc_xslt = $_parser->parse_file(
101                 OpenSRF::Utils::SettingsClient
102                         ->new
103                         ->config_value( dirs => 'xsl' ).
104                 "/MARC21slim2SRWDC.xsl"
105         );
106         # and stash a transformer
107         $record_xslt{srw_dc}{xslt} = $_xslt->parse_stylesheet( $srw_dc_xslt );
108         $record_xslt{srw_dc}{namespace_uri} = 'info:srw/schema/1/dc-schema';
109         $record_xslt{srw_dc}{schema_location} = 'http://www.loc.gov/z3950/agency/zing/srw/dc-schema.xsd';
110
111         # parse the OAIDC xslt ...
112         my $oai_dc_xslt = $_parser->parse_file(
113                 OpenSRF::Utils::SettingsClient
114                         ->new
115                         ->config_value( dirs => 'xsl' ).
116                 "/MARC21slim2OAIDC.xsl"
117         );
118         # and stash a transformer
119         $record_xslt{oai_dc}{xslt} = $_xslt->parse_stylesheet( $oai_dc_xslt );
120         $record_xslt{oai_dc}{namespace_uri} = 'http://www.openarchives.org/OAI/2.0/oai_dc/';
121         $record_xslt{oai_dc}{schema_location} = 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd';
122
123         # parse the RSS xslt ...
124         my $rss_xslt = $_parser->parse_file(
125                 OpenSRF::Utils::SettingsClient
126                         ->new
127                         ->config_value( dirs => 'xsl' ).
128                 "/MARC21slim2RSS2.xsl"
129         );
130         # and stash a transformer
131         $record_xslt{rss2}{xslt} = $_xslt->parse_stylesheet( $rss_xslt );
132
133         # and finally, a storage server session
134
135         register_record_transforms();
136
137         return 1;
138 }
139
140 sub register_record_transforms {
141         for my $type ( keys %record_xslt ) {
142                 __PACKAGE__->register_method(
143                         method    => 'retrieve_record_transform',
144                         api_name  => "open-ils.supercat.record.$type.retrieve",
145                         api_level => 1,
146                         argc      => 1,
147                         signature =>
148                                 { desc     => "Returns the \U$type\E representation ".
149                                               "of the requested bibliographic record",
150                                   params   =>
151                                         [
152                                                 { name => 'bibId',
153                                                   desc => 'An OpenILS biblio::record_entry id',
154                                                   type => 'number' },
155                                         ],
156                                 'return' =>
157                                         { desc => "The bib record in \U$type\E",
158                                           type => 'string' }
159                                 }
160                 );
161         }
162 }
163
164
165 sub entityize {
166         my $stuff = NFC(shift());
167         $stuff =~ s/([\x{0080}-\x{fffd}])/sprintf('&#x%X;',ord($1))/sgoe;
168         return $stuff;
169 }
170
171 sub record_holdings {
172         my $self = shift;
173         my $client = shift;
174         my $bib = shift;
175
176         my $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
177
178         if (!$holdings_data_cache{status}) {
179                 $holdings_data_cache{status} = { map { ($_->id => $_) } @{ $_storage->request( "open-ils.storage.direct.config.copy_status.retrieve.all.atomic" )->gather(1) } };
180                 $holdings_data_cache{location} = { map { ($_->id => $_) } @{ $_storage->request( "open-ils.storage.direct.asset.copy_location.retrieve.all.atomic" )->gather(1) } };
181                 $holdings_data_cache{ou} =
182                 {
183                         map {
184                                 ($_->id => $_)
185                         } @{$_storage->request( "open-ils.storage.direct.actor.org_unit.search_where.atomic" => { id => { '>' => 0 } } )->gather(1)}
186                 };
187                 $holdings_data_cache{statcat} =
188                 {
189                         map {
190                                 ($_->id => $_)
191                         } @{$_storage->request( "open-ils.storage.direct.asset.stat_cat_entry.search_where.atomic" => { id => { '>' => 0 } } )->gather(1)}
192                 };
193         }
194
195
196         my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
197         $year += 1900;
198         $month += 1;
199
200         my $xml = "<volumes xmlns='http://open-ils.org/spec/holdings/v1'>";
201         
202         for my $cn ( @{$_storage->request( "open-ils.storage.direct.asset.call_number.search.record.atomic" => $bib )->gather(1)} ) {
203                 (my $cn_class = $cn->class_name) =~ s/::/-/gso;
204                 $cn_class =~ s/Fieldmapper-//gso;
205                 my $cn_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cn_class/".$cn->id, $month, $day);
206
207                 my $cn_lib = $holdings_data_cache{ou}{$cn->owning_lib}->shortname;
208
209                 my $cn_label = $cn->label;
210
211                 $xml .= "<volume id='$cn_tag' lib='$cn_lib' label='$cn_label'><copies>";
212                 
213                 for my $cp ( @{$_storage->request( "open-ils.storage.direct.asset.copy.search.call_number.atomic" => $cn->id )->gather(1)} ) {
214                         (my $cp_class = $cn->class_name) =~ s/::/-/gso;
215                         $cp_class =~ s/Fieldmapper-//gso;
216                         my $cp_tag = sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:$cp_class/".$cp->id, $month, $day);
217
218                         my $cp_stat = $holdings_data_cache{status}{$cp->status}->name;
219
220                         my $cp_loc = $holdings_data_cache{location}{$cp->location}->name;
221
222                         my $cp_lib = $holdings_data_cache{ou}{$cp->circ_lib}->shortname;
223
224                         my $cp_bc = $cp->barcode;
225
226                         $xml .= "<copy id='$cp_tag' barcode='$cp_bc'><status>$cp_stat</status><location>$cp_loc</location><circlib>$cp_lib</circlib><notes>";
227
228                         for my $note ( @{$_storage->request( "open-ils.storage.direct.asset.copy_note.search.atomic" => {id => $cp->id, pub => "t" })->gather(1)} ) {
229                                 $xml .= sprintf('<note date="%s" title="%s">%s</note>',$note->create_date, escape($note->title), escape($note->value));
230                         }
231
232                         $xml .= "</notes><statcats>";
233
234                         for my $sce ( @{$_storage->request( "open-ils.storage.direct.asset.stat_cat_entry_copy_map.search.atomic" => { owning_copy => $cp->id })->gather(1)} ) {
235                                 my $sc = $holdings_data_cache{statcat}{$sce->stat_cat_entry};
236                                 $xml .= sprintf('<statcat>%s</statcat>',escape($sc->value));
237                         }
238
239                         $xml .= "</statcats></copy>";
240                 }
241                 
242                 $xml .= "</volume>";
243         }
244
245         $xml .= "</volumes>";
246
247         return $xml;
248 }
249 __PACKAGE__->register_method(
250         method    => 'record_holdings',
251         api_name  => 'open-ils.supercat.record.holdings_xml.retrieve',
252         api_level => 1,
253         argc      => 1,
254         signature =>
255                 { desc     => <<"                 DESC",
256 Returns the XML representation of the requested bibliographic record's holdings
257                   DESC
258                   params   =>
259                         [
260                                 { name => 'bibId',
261                                   desc => 'An OpenILS biblio::record_entry id',
262                                   type => 'number' },
263                         ],
264                   'return' =>
265                         { desc => 'The bib record holdings hierarchy in XML',
266                           type => 'string' }
267                 }
268 );
269
270
271 sub escape {
272         my $text = shift;
273         $text =~ s/&/&amp;/gsom;
274         $text =~ s/</&lt;/gsom;
275         $text =~ s/>/&gt;/gsom;
276         return $text;
277 }
278
279 sub recent_changes {
280         my $self = shift;
281         my $client = shift;
282         my $when = shift;
283         my $limit = shift;
284
285         if (!$when) {
286                 my ($d,$m,$y) = (localtime)[4,5,6];
287                 $when = sprintf('%4d-%02d-%02d', $y + 1900, $m + 1, $d);
288         }
289
290         my $type = 'biblio';
291         $type = 'authority' if ($self->api_name =~ /authority/o);
292
293         my $axis = 'create_date';
294         $axis = 'edit_date' if ($self->api_name =~ /edit/o);
295
296         my $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
297
298         return $_storage
299                 ->request(
300                         "open-ils.storage.id_list.$type.record_entry.search_where.atomic",
301                         { $axis => { ">" => $when } },
302                         { order_by => "$axis desc", limit => $limit } )
303                 ->gather(1);
304 }
305
306 for my $t ( qw/biblio authority/ ) {
307         for my $a ( qw/import edit/ ) {
308
309                 __PACKAGE__->register_method(
310                         method    => 'recent_changes',
311                         api_name  => "open-ils.supercat.$t.record.$a.recent",
312                         api_level => 1,
313                         argc      => 0,
314                         signature =>
315                                 { desc     => "Returns a list of recently ${a}ed $t records",
316                                   params   =>
317                                         [
318                                                 { name => 'when',
319                                                   desc => "Date to start looking for ${a}ed records",
320                                                   default => 'today',
321                                                   type => 'string' },
322
323                                                 { name => 'limit',
324                                                   desc => "Maximum count to retrieve",
325                                                   type => 'number' },
326                                         ],
327                                   'return' =>
328                                         { desc => "An id list of $t records",
329                                           type => 'array' }
330                                 },
331                 );
332         }
333 }
334
335
336 sub retrieve_record_marcxml {
337         my $self = shift;
338         my $client = shift;
339         my $rid = shift;
340
341         my $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
342
343         return
344         entityize(
345                 $_storage
346                         ->request( 'open-ils.storage.direct.biblio.record_entry.retrieve' => $rid )
347                         ->gather(1)
348                         ->marc
349         );
350 }
351
352 __PACKAGE__->register_method(
353         method    => 'retrieve_record_marcxml',
354         api_name  => 'open-ils.supercat.record.marcxml.retrieve',
355         api_level => 1,
356         argc      => 1,
357         signature =>
358                 { desc     => <<"                 DESC",
359 Returns the MARCXML representation of the requested bibliographic record
360                   DESC
361                   params   =>
362                         [
363                                 { name => 'bibId',
364                                   desc => 'An OpenILS biblio::record_entry id',
365                                   type => 'number' },
366                         ],
367                   'return' =>
368                         { desc => 'The bib record in MARCXML',
369                           type => 'string' }
370                 }
371 );
372
373 sub retrieve_record_transform {
374         my $self = shift;
375         my $client = shift;
376         my $rid = shift;
377
378         (my $transform = $self->api_name) =~ s/^.+record\.([^\.]+)\.retrieve$/$1/o;
379
380         my $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
381
382         warn "Fetching record entry $rid\n";
383         my $marc = $_storage->request(
384                 'open-ils.storage.direct.biblio.record_entry.retrieve',
385                 $rid
386         )->gather(1)->marc;
387         warn "Fetched record entry $rid\n";
388
389         return entityize($record_xslt{$transform}{xslt}->transform( $_parser->parse_string( $marc ) )->toString);
390 }
391
392
393 sub retrieve_metarecord_mods {
394         my $self = shift;
395         my $client = shift;
396         my $rid = shift;
397
398         my $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
399
400         # We want a session
401         $_storage->connect;
402
403         # Get the metarecord in question
404         my $mr =
405         $_storage->request(
406                 'open-ils.storage.direct.metabib.metarecord.retrieve' => $rid
407         )->gather(1);
408
409         # Now get the map of all bib records for the metarecord
410         my $recs =
411         $_storage->request(
412                 'open-ils.storage.direct.metabib.metarecord_source_map.search.metarecord.atomic',
413                 $rid
414         )->gather(1);
415
416         $logger->debug("Adding ".scalar(@$recs)." bib record to the MODS of the metarecord");
417
418         # and retrieve the lead (master) record as MODS
419         my ($master) =
420                 $self   ->method_lookup('open-ils.supercat.record.mods.retrieve')
421                         ->run($mr->master_record);
422         my $master_mods = $_parser->parse_string($master)->documentElement;
423         $master_mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
424
425         # ... and a MODS clone to populate, with guts removed.
426         my $mods = $_parser->parse_string($master)->documentElement;
427         $mods->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
428         ($mods) = $mods->findnodes('//mods:mods');
429         $mods->removeChildNodes;
430
431         # Add the metarecord ID as a (locally defined) info URI
432         my $recordInfo = $mods
433                 ->ownerDocument
434                 ->createElement("mods:recordInfo");
435
436         my $recordIdentifier = $mods
437                 ->ownerDocument
438                 ->createElement("mods:recordIdentifier");
439
440         my ($year,$month,$day) = reverse( (localtime)[3,4,5] );
441         $year += 1900;
442         $month += 1;
443
444         my $id = $mr->id;
445         $recordIdentifier->appendTextNode(
446                 sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:metabib-metarecord/$id", $month, $day)
447         );
448
449         $recordInfo->appendChild($recordIdentifier);
450         $mods->appendChild($recordInfo);
451
452         # Grab the title, author and ISBN for the master record and populate the metarecord
453         my ($title) = $master_mods->findnodes( './mods:titleInfo[not(@type)]' );
454         
455         if ($title) {
456                 $title->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
457                 $title = $mods->ownerDocument->importNode($title);
458                 $mods->appendChild($title);
459         }
460
461         my ($author) = $master_mods->findnodes( './mods:name[mods:role/mods:text[text()="creator"]]' );
462         if ($author) {
463                 $author->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
464                 $author = $mods->ownerDocument->importNode($author);
465                 $mods->appendChild($author);
466         }
467
468         my ($isbn) = $master_mods->findnodes( './mods:identifier[@type="isbn"]' );
469         if ($isbn) {
470                 $isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
471                 $isbn = $mods->ownerDocument->importNode($isbn);
472                 $mods->appendChild($isbn);
473         }
474
475         # ... and loop over the constituent records
476         for my $map ( @$recs ) {
477
478                 # get the MODS
479                 my ($rec) =
480                         $self   ->method_lookup('open-ils.supercat.record.mods.retrieve')
481                                 ->run($map->source);
482
483                 my $part_mods = $_parser->parse_string($rec);
484                 $part_mods->documentElement->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
485                 ($part_mods) = $part_mods->findnodes('//mods:mods');
486
487                 for my $node ( ($part_mods->findnodes( './mods:subject' )) ) {
488                         $node->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
489                         $node = $mods->ownerDocument->importNode($node);
490                         $mods->appendChild( $node );
491                 }
492
493                 my $relatedItem = $mods
494                         ->ownerDocument
495                         ->createElement("mods:relatedItem");
496
497                 $relatedItem->setAttribute( type => 'constituent' );
498
499                 my $identifier = $mods
500                         ->ownerDocument
501                         ->createElement("mods:identifier");
502
503                 $identifier->setAttribute( type => 'uri' );
504
505                 my $subRecordInfo = $mods
506                         ->ownerDocument
507                         ->createElement("mods:recordInfo");
508
509                 my $subRecordIdentifier = $mods
510                         ->ownerDocument
511                         ->createElement("mods:recordIdentifier");
512
513                 my $subid = $map->source;
514                 $subRecordIdentifier->appendTextNode(
515                         sprintf("tag:open-ils.org,$year-\%0.2d-\%0.2d:biblio-record_entry/$subid",
516                                 $month,
517                                 $day
518                         )
519                 );
520                 $subRecordInfo->appendChild($subRecordIdentifier);
521
522                 $relatedItem->appendChild( $subRecordInfo );
523
524                 my ($tor) = $part_mods->findnodes( './mods:typeOfResource' );
525                 $tor->setNamespace( "http://www.loc.gov/mods/", "mods", 1 ) if ($tor);
526                 $tor = $mods->ownerDocument->importNode($tor) if ($tor);
527                 $relatedItem->appendChild($tor) if ($tor);
528
529                 if ( my ($part_isbn) = $part_mods->findnodes( './mods:identifier[@type="isbn"]' ) ) {
530                         $part_isbn->setNamespace( "http://www.loc.gov/mods/", "mods", 1 );
531                         $part_isbn = $mods->ownerDocument->importNode($part_isbn);
532                         $relatedItem->appendChild( $part_isbn );
533
534                         if (!$isbn) {
535                                 $isbn = $mods->appendChild( $part_isbn->cloneNode(1) );
536                         }
537                 }
538
539                 $mods->appendChild( $relatedItem );
540
541         }
542
543         $_storage->disconnect;
544
545         return entityize($mods->toString);
546
547 }
548 __PACKAGE__->register_method(
549         method    => 'retrieve_metarecord_mods',
550         api_name  => 'open-ils.supercat.metarecord.mods.retrieve',
551         api_level => 1,
552         argc      => 1,
553         signature =>
554                 { desc     => <<"                 DESC",
555 Returns the MODS representation of the requested metarecord
556                   DESC
557                   params   =>
558                         [
559                                 { name => 'metarecordId',
560                                   desc => 'An OpenILS metabib::metarecord id',
561                                   type => 'number' },
562                         ],
563                   'return' =>
564                         { desc => 'The metarecord in MODS',
565                           type => 'string' }
566                 }
567 );
568
569 sub list_metarecord_formats {
570         my @list = (
571                 { mods =>
572                         { namespace_uri   => 'http://www.loc.gov/mods/',
573                           docs            => 'http://www.loc.gov/mods/',
574                           schema_location => 'http://www.loc.gov/standards/mods/mods.xsd',
575                         }
576                 }
577         );
578
579         for my $type ( keys %metarecord_xslt ) {
580                 push @list,
581                         { $type => 
582                                 { namespace_uri   => $metarecord_xslt{$type}{namespace_uri},
583                                   docs            => $metarecord_xslt{$type}{docs},
584                                   schema_location => $metarecord_xslt{$type}{schema_location},
585                                 }
586                         };
587         }
588
589         return \@list;
590 }
591 __PACKAGE__->register_method(
592         method    => 'list_metarecord_formats',
593         api_name  => 'open-ils.supercat.metarecord.formats',
594         api_level => 1,
595         argc      => 0,
596         signature =>
597                 { desc     => <<"                 DESC",
598 Returns the list of valid metarecord formats that supercat understands.
599                   DESC
600                   'return' =>
601                         { desc => 'The format list',
602                           type => 'array' }
603                 }
604 );
605
606
607 sub list_record_formats {
608         my @list = (
609                 { marcxml =>
610                         { namespace_uri   => 'http://www.loc.gov/MARC21/slim',
611                           docs            => 'http://www.loc.gov/marcxml/',
612                           schema_location => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
613                         }
614                 }
615         );
616
617         for my $type ( keys %record_xslt ) {
618                 push @list,
619                         { $type => 
620                                 { namespace_uri   => $record_xslt{$type}{namespace_uri},
621                                   docs            => $record_xslt{$type}{docs},
622                                   schema_location => $record_xslt{$type}{schema_location},
623                                 }
624                         };
625         }
626
627         return \@list;
628 }
629 __PACKAGE__->register_method(
630         method    => 'list_record_formats',
631         api_name  => 'open-ils.supercat.record.formats',
632         api_level => 1,
633         argc      => 0,
634         signature =>
635                 { desc     => <<"                 DESC",
636 Returns the list of valid record formats that supercat understands.
637                   DESC
638                   'return' =>
639                         { desc => 'The format list',
640                           type => 'array' }
641                 }
642 );
643
644
645 sub oISBN {
646         my $self = shift;
647         my $client = shift;
648         my $isbn = shift;
649
650         throw OpenSRF::EX::InvalidArg ('I need an ISBN please')
651                 unless (length($isbn) >= 10);
652
653         my $_storage = OpenSRF::AppSession->create( 'open-ils.storage' );
654
655         # Create a storage session, since we'll be making muliple requests.
656         $_storage->connect;
657
658         # Find the record that has that ISBN.
659         my $bibrec = $_storage->request(
660                 'open-ils.storage.direct.metabib.full_rec.search_where.atomic',
661                 { tag => '020', subfield => 'a', value => { ilike => $isbn.'%'} }
662         )->gather(1);
663
664         # Go away if we don't have one.
665         return {} unless (@$bibrec);
666
667         # Find the metarecord for that bib record.
668         my $mr = $_storage->request(
669                 'open-ils.storage.direct.metabib.metarecord_source_map.search.source.atomic',
670                 $bibrec->[0]->record
671         )->gather(1);
672
673         # Find the other records for that metarecord.
674         my $records = $_storage->request(
675                 'open-ils.storage.direct.metabib.metarecord_source_map.search.metarecord.atomic',
676                 $mr->[0]->metarecord
677         )->gather(1);
678
679         # Just to be safe.  There's currently no unique constraint on sources...
680         my %unique_recs = map { ($_->source, 1) } @$records;
681         my @rec_list = sort keys %unique_recs;
682
683         # And now fetch the ISBNs for thos records.
684         my $recs = $_storage->request(
685                 'open-ils.storage.direct.metabib.full_rec.search_where.atomic',
686                 { tag => '020', subfield => 'a', record => \@rec_list }
687         )->gather(1);
688
689         # We're done with the storage server session.
690         $_storage->disconnect;
691
692         # Return the oISBN data structure.  This will be XMLized at a higher layer.
693         return
694                 { metarecord => $mr->[0]->metarecord,
695                   record_list => { map { ($_->record, $_->value) } @$recs } };
696
697 }
698 __PACKAGE__->register_method(
699         method    => 'oISBN',
700         api_name  => 'open-ils.supercat.oisbn',
701         api_level => 1,
702         argc      => 1,
703         signature =>
704                 { desc     => <<"                 DESC",
705 Returns the ISBN list for the metarecord of the requested isbn
706                   DESC
707                   params   =>
708                         [
709                                 { name => 'isbn',
710                                   desc => 'An ISBN.  Duh.',
711                                   type => 'string' },
712                         ],
713                   'return' =>
714                         { desc => 'record to isbn map',
715                           type => 'object' }
716                 }
717 );
718
719 1;