e4cfd283091487377460fa141bffbd3b57b708e5
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Search / Biblio.pm
1 package OpenILS::Application::Search::Biblio;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
4
5 use OpenILS::EX;
6
7 use JSON;
8 use OpenILS::Utils::Fieldmapper;
9 use OpenILS::Utils::ModsParser;
10 use OpenSRF::Utils::SettingsClient;
11
12 use OpenILS::Application::AppUtils;
13 my $U = "OpenILS::Application::AppUtils";
14
15 use JSON;
16
17 use Time::HiRes qw(time);
18 use OpenSRF::EX qw(:try);
19 use Digest::MD5 qw(md5_hex);
20
21 use XML::LibXML;
22 use XML::LibXSLT;
23 use Data::Dumper;
24 $Data::Dumper::Indent = 0;
25 use OpenSRF::Utils::Logger qw/:logger/;
26
27 my $apputils = "OpenILS::Application::AppUtils";
28
29 # Houses biblio search utilites 
30
31
32 __PACKAGE__->register_method(
33         method  => "test",
34         api_name        => "open-ils.search.test");
35
36 sub test { return "test"; }
37
38
39
40 =head comment
41 __PACKAGE__->register_method(
42         method  => "biblio_search_marc",
43         api_name        => "open-ils.search.biblio.marc",
44         argc            => 1, 
45         note            => "Searches biblio information by marc tag",
46 );
47
48 sub biblio_search_marc {
49
50         my( $self, $client, $search_hash, $string ) = @_;
51
52         warn "Building biblio marc session\n";
53         my $session = OpenSRF::AppSession->create("open-ils.storage");
54
55         warn "Sending biblio marc request. String $string\nSearch hash: " . Dumper($search_hash);
56         my $request = $session->request( 
57                         "open-ils.storage.direct.metabib.full_rec.search_fts.index_vector.atomic", 
58                         restrict => $search_hash, 
59                         term            => $string );
60         my $data = $request->gather(1);
61
62         warn Dumper $data;
63
64         $session->finish();
65         $session->disconnect();
66
67         return $data;
68
69 }
70 =cut
71
72
73
74 # ---------------------------------------------------------------------------
75 # takes a list of record id's and turns the docs into friendly 
76 # mods structures. Creates one MODS structure for each doc id.
77 # ---------------------------------------------------------------------------
78 sub _records_to_mods {
79         my @ids = @_;
80         
81         my @results;
82         my @marcxml_objs;
83
84         my $session = OpenSRF::AppSession->create("open-ils.storage");
85         my $request = $session->request(
86                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve",  @ids );
87
88         my $last_content = undef;
89
90         while( my $response = $request->recv() ) {
91
92                 if( $last_content ) {
93                         my $u = OpenILS::Utils::ModsParser->new();
94                         $u->start_mods_batch( $last_content->marc );
95                         my $mods = $u->finish_mods_batch();
96                         $mods->doc_id($last_content->id());
97                         $mods->tcn($last_content->tcn_value);
98                         warn "Turning doc " . $mods->doc_id() . " into MODS\n";
99                         $last_content = undef;
100                         push @results, $mods;
101                 }
102
103                 next unless $response;
104
105                 if($response->isa("OpenSRF::EX")) {
106                         throw $response ($response->stringify);
107                 }
108
109                 $last_content = $response->content;
110
111         }
112
113         if( $last_content ) {
114                 my $u = OpenILS::Utils::ModsParser->new();
115                 $u->start_mods_batch( $last_content->marc );
116                 my $mods = $u->finish_mods_batch();
117                 $mods->doc_id($last_content->id());
118                 $mods->tcn($last_content->tcn_value);
119                 push @results, $mods;
120         }
121
122         $request->finish();
123         $session->finish();
124         $session->disconnect();
125
126         return \@results;
127
128 }
129
130 __PACKAGE__->register_method(
131         method  => "record_id_to_mods",
132         api_name        => "open-ils.search.biblio.record.mods.retrieve",
133         argc            => 1, 
134         note            => "Provide ID, we provide the mods"
135 );
136
137 # converts a record into a mods object with copy counts attached
138 sub record_id_to_mods {
139
140         my( $self, $client, $org_id, $id ) = @_;
141
142         my $mods_list = _records_to_mods( $id );
143         my $mods_obj = $mods_list->[0];
144         my $cmethod = $self->method_lookup(
145                         "open-ils.search.biblio.record.copy_count");
146         my ($count) = $cmethod->run($org_id, $id);
147         $mods_obj->copy_count($count);
148
149         return $mods_obj;
150 }
151
152
153
154 __PACKAGE__->register_method(
155         method  => "record_id_to_mods_slim",
156         api_name        => "open-ils.search.biblio.record.mods_slim.retrieve",
157         argc            => 1, 
158         note            => "Provide ID, we provide the mods"
159 );
160
161 # converts a record into a mods object with NO copy counts attached
162 sub record_id_to_mods_slim {
163         my( $self, $client, $id ) = @_;
164         return undef unless defined $id;
165
166         if(ref($id) and ref($id) == 'ARRAY') {
167                 return _records_to_mods( @$id );
168         }
169         my $mods_list = _records_to_mods( $id );
170         my $mods_obj = $mods_list->[0];
171         return $mods_obj;
172 }
173
174
175 # Returns the number of copies attached to a record based on org location
176 __PACKAGE__->register_method(
177         method  => "record_id_to_copy_count",
178         api_name        => "open-ils.search.biblio.record.copy_count",
179 );
180
181 __PACKAGE__->register_method(
182         method  => "record_id_to_copy_count",
183         api_name        => "open-ils.search.biblio.metarecord.copy_count",
184 );
185
186 __PACKAGE__->register_method(
187         method  => "record_id_to_copy_count",
188         api_name        => "open-ils.search.biblio.metarecord.copy_count.staff",
189 );
190 sub record_id_to_copy_count {
191         my( $self, $client, $org_id, $record_id, $format ) = @_;
192
193         $format = undef if ($format eq 'all');
194
195         my $method = "open-ils.storage.biblio.record_entry.copy_count.atomic";
196         my $key = "record";
197         if($self->api_name =~ /metarecord/) {
198                 $method = "open-ils.storage.metabib.metarecord.copy_count.atomic";
199                 $key = "metarecord";
200         }
201
202         if($self->api_name =~ /staff/ ) {
203                 $method =~ s/atomic/staff\.atomic/og;
204                 warn "Doing staff search $method\n";
205         }
206
207
208         my $session = OpenSRF::AppSession->create("open-ils.storage");
209         warn "copy_count retrieve $record_id\n";
210         return undef unless(defined $record_id);
211
212         my $request = $session->request(
213                 $method, org_unit => $org_id => $key => $record_id, format => $format );
214
215
216         my $count = $request->gather(1);
217         $session->disconnect();
218         return [ sort { $a->{depth} <=> $b->{depth} } @$count ];
219
220 }
221
222
223 # used for cat search classes
224 my $cat_search_hash =  {
225
226         author => [ 
227                 { tag => "100", subfield => "a"} ,
228                 { tag => "700", subfield => "a"}, 
229         ],
230
231         title => [ 
232                 { tag => "245", subfield => "a"},
233                 { tag => "242", subfield => "a"}, 
234                 { tag => "240", subfield => "a"},
235                 { tag => "210", subfield => "a"},
236         ],
237
238         subject => [ 
239                 { tag => "650", subfield => "_" }, 
240         ],
241
242         tcn     => [
243                 { tag => "035", subfield => "_" },
244         ],
245
246         isbn    => [
247                 { tag => "020", subfield => "a" },
248         ],
249
250         issn    => [
251                 { tag => '022', subfield => 'a' },
252         ],
253
254 };
255
256
257 __PACKAGE__->register_method(
258         method  => "biblio_search_tcn",
259         api_name        => "open-ils.search.biblio.tcn",
260         argc            => 3, 
261         note            => "Retrieve a record by TCN",
262 );
263
264 sub biblio_search_tcn {
265
266         my( $self, $client, $tcn ) = @_;
267
268         $tcn =~ s/.*?(\w+)\s*$/$1/o;
269         warn "Searching TCN $tcn\n";
270
271         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
272         my $request = $session->request( 
273                         "open-ils.storage.direct.biblio.record_entry.search.tcn_value.atomic", $tcn );
274         my $record_entry = $request->gather(1);
275
276         my @ids;
277         for my $record (@$record_entry) {
278                 push @ids, $record->id;
279         }
280
281         $session->disconnect();
282
283         warn "received ID's for tcn search @ids\n";
284         my $size = @ids;
285
286         return { count => $size, ids => \@ids };
287
288 }
289
290
291 # --------------------------------------------------------------------------------
292 # ISBN
293
294
295
296
297
298 # --------------------------------------------------------------------------------
299
300 __PACKAGE__->register_method(
301         method  => "biblio_barcode_to_copy",
302         api_name        => "open-ils.search.asset.copy.find_by_barcode",
303 );
304
305 # turns a barcode into a copy object
306 sub biblio_barcode_to_copy { 
307         my( $self, $client, $barcode ) = @_;
308
309         throw OpenSRF::EX::InvalidArg 
310                 ("search.biblio.barcode needs a barcode to search")
311                         unless defined $barcode;
312
313         warn "copy search for barcode $barcode\n";
314         my $record = OpenILS::Application::AppUtils->simple_scalar_request(
315                         "open-ils.storage", 
316                         "open-ils.storage.direct.asset.copy.search.barcode.atomic",
317                         $barcode );
318
319         return undef unless($record);
320         return $record->[0];
321
322 }
323
324 __PACKAGE__->register_method(
325         method  => "biblio_id_to_copy",
326         api_name        => "open-ils.search.asset.copy.batch.retrieve",
327 );
328
329 # turns a barcode into a copy object
330 sub biblio_id_to_copy { 
331         my( $self, $client, $ids ) = @_;
332
333         throw OpenSRF::EX::InvalidArg 
334                 ("search.biblio.batch.retrieve needs a id to search")
335                         unless defined $ids;
336
337         warn "copy search for ids @$ids\n";
338         my $record = OpenILS::Application::AppUtils->simple_scalar_request(
339                         "open-ils.storage", 
340                         "open-ils.storage.direct.asset.copy.batch.retrieve.atomic",
341                         @$ids );
342
343         return $record;
344
345 }
346
347
348 __PACKAGE__->register_method(
349         method  => "copy_retrieve", 
350         api_name        => "open-ils.search.asset.copy.retrieve",
351 );
352
353 sub copy_retrieve {
354         my( $self, $client, $cid ) = @_;
355         my( $copy, $evt ) = $U->fetch_copy($cid);
356         return $evt if $evt;
357         return $copy;
358 }
359
360
361 __PACKAGE__->register_method(
362         method  => "fleshed_copy_retrieve_batch",
363         api_name        => "open-ils.search.asset.copy.fleshed.batch.retrieve",
364 );
365
366 sub fleshed_copy_retrieve_batch { 
367         my( $self, $client, $ids ) = @_;
368
369         throw OpenSRF::EX::InvalidArg 
370                 ("search.biblio.batch.retrieve needs a id to search")
371                         unless defined $ids;
372
373         warn "fleshed copy search for id @$ids\n";
374         my $copy = OpenILS::Application::AppUtils->simple_scalar_request(
375                         "open-ils.storage", 
376                         "open-ils.storage.fleshed.asset.copy.batch.retrieve.atomic",
377                         @$ids );
378
379         return $copy;
380 }
381
382 __PACKAGE__->register_method(
383         method  => "fleshed_copy_retrieve",
384         api_name        => "open-ils.search.asset.copy.fleshed.retrieve",
385 );
386
387 sub fleshed_copy_retrieve { 
388         my( $self, $client, $id ) = @_;
389
390         return undef unless defined $id;
391         warn "copy retrieve for id $id\n";
392         return OpenILS::Application::AppUtils->simple_scalar_request(
393                         "open-ils.storage", 
394                         "open-ils.storage.fleshed.asset.copy.retrieve",
395                         $id );
396 }
397
398
399
400 __PACKAGE__->register_method(
401         method  => "biblio_barcode_to_title",
402         api_name        => "open-ils.search.biblio.find_by_barcode",
403 );
404
405 sub biblio_barcode_to_title {
406         my( $self, $client, $barcode ) = @_;
407
408         if(!$barcode) {
409                 throw OpenSRF::EX::ERROR 
410                         ("Not enough args to find_by_barcode");
411         }
412
413         my $title = $apputils->simple_scalar_request(
414                 "open-ils.storage",
415                 "open-ils.storage.biblio.record_entry.retrieve_by_barcode",
416                 $barcode );
417
418         if($title) {
419                 return { ids => [ $title->id ], count => 1 };
420         } else {
421                 return { count => 0 };
422         }
423
424 }
425
426
427 __PACKAGE__->register_method(
428         method  => "biblio_copy_to_mods",
429         api_name        => "open-ils.search.biblio.copy.mods.retrieve",
430 );
431
432 # takes a copy object and returns it fleshed mods object
433 sub biblio_copy_to_mods {
434         my( $self, $client, $copy ) = @_;
435
436         throw OpenSRF::EX::InvalidArgs 
437                 ("copy.mods.retrieve needs a copy") unless( $copy );
438
439         new Fieldmapper::asset::copy($copy);
440
441         my $volume = OpenILS::Application::AppUtils->simple_scalar_request(
442                 "open-ils.storage",
443                 "open-ils.storage.direct.asset.call_number.retrieve",
444                 $copy->call_number() );
445
446         my $mods = _records_to_mods($volume->record());
447         $mods = shift @$mods;
448         $volume->copies([$copy]);
449         push @{$mods->call_numbers()}, $volume;
450
451         return $mods;
452 }
453
454
455 sub barcode_to_mods {
456
457 }
458
459
460 # --------------------------------------------------------------------------------
461
462
463
464 =head
465 __PACKAGE__->register_method(
466         method  => "biblio_search_class_count",
467         api_name        => "open-ils.search.biblio.class.count",
468 );
469
470 __PACKAGE__->register_method(
471         method  => "biblio_search_class_count",
472         api_name        => "open-ils.search.biblio.class.count.staff",
473 );
474
475 sub biblio_search_class_count {
476
477         my( $self, $client, $class, $string, $org_id, $org_type, $format ) = @_;
478
479         warn "org: $org_id : depth: $org_type\n";
480
481         $org_id         = "1" unless defined($org_id); # xxx
482         $org_type       = 0     unless defined($org_type);
483
484         warn "Searching biblio.class.id\n" . 
485                 "string: $string "              . 
486                 "org_id: $org_id\n"             .
487                 "depth: $org_type\n"            .
488                 "format: $format\n";
489
490         if( !defined($org_id) or !$class or !$string ) {
491                 warn "not enbough args to metarecord search\n";
492                 throw OpenSRF::EX::InvalidArg 
493                         ("Not enough args to open-ils.search.cat.biblio.class")
494         }
495
496         $class =~ s/\s+//g;
497
498         if( ($class ne "title") and ($class ne "author") and 
499                 ($class ne "subject") and ($class ne "keyword") 
500                 and ($class ne "series"  )) {
501                 warn "Invalid search class: $class\n";
502                 throw OpenSRF::EX::InvalidArg ("Not a valid search class: $class")
503         }
504
505         # grab the mr id's from storage
506
507         my $method = "open-ils.storage.cachable.metabib.$class.search_fts.metarecord_count";
508         if($self->api_name =~ /staff/) { 
509                 $method = "$method.staff"; 
510                 $method =~ s/\.cachable//o;
511         }
512         warn "Performing count method $method\n";
513         warn "API name " . $self->api_name() . "\n";
514
515         my $session = OpenSRF::AppSession->create('open-ils.storage');
516
517         my $request = $session->request( $method, 
518                         term                                    => $string, 
519                         org_unit                                => $org_id, 
520                         cache_page_size => 1,
521                         depth                                   => $org_type,
522                         format                          => $format );
523
524         my $count = $request->gather(1);
525         warn "Received count $count\n";
526
527         return $count;
528 }
529
530 =cut
531
532
533 __PACKAGE__->register_method(
534         method          => "record_search_class",
535         api_name                => "open-ils.search.biblio.record.class.search",
536         signature       => q/
537                 Performs a class search for biblio records (not metarecords)
538                 @param class The search class to use
539                 @param args A hash of named parameters including:
540                         term            : The search string,
541                         org_unit : The org id to focus the search at
542                         depth           : The org depth
543                         limit           : The search limit
544                         offset  : The search offset
545                         format  : The MARC format
546                         sort            : What field to sort the results on [ author | title | pubdate ]
547                         sort_dir        : What direction do we sort? [ asc | desc ]
548                 The only required argument is the term.
549                 @return 
550         /);
551
552 __PACKAGE__->register_method(
553         method          => "record_search_class",
554         api_name                => "open-ils.search.biblio.record.class.search.staff",
555         signature       => q/@see open-ils.search.biblio.record.class.search/);
556 __PACKAGE__->register_method(
557         method  => "record_search_class",
558         api_name        => "open-ils.search.biblio.metabib.class.search",
559         signature       => q/@see open-ils.search.biblio.record.class.search/);
560 __PACKAGE__->register_method(
561         method  => "record_search_class",
562         api_name        => "open-ils.search.biblio.metabib.class.search.staff",
563         signature       => q/@see open-ils.search.biblio.record.class.search/);
564
565 sub record_search_class {
566         my( $self, $client, $class, $args ) = @_;
567
568         my $type                = ($self->api_name =~ /metabib/) ? 1 : 0;
569         my $method      = "open-ils.storage.metabib.$class.post_filter.search_fts.metarecord.atomic";
570         $method         = "open-ils.storage.biblio.$class.search_fts.record.atomic" unless $type;
571
572         if(!$$args{'sort'}) {
573                 delete $$args{'sort'}; 
574                 delete $$args{'sort_dir'};
575         }
576         $$args{limit}   = 200 if (!$$args{limit} || $$args{limit} > 1000);
577         $$args{offset} ||= 0;
578
579         $logger->info("Class search with args: ".Dumper($args));
580
581         $method =~ s/\.atomic/.staff.atomic/o if $self->api_name =~ /staff/o;
582         my $result = $U->storagereq( $method, %$args );
583
584         if($result and $result->[0]) {
585                 my $recs = [];
586                 for my $r (@$result) { push(@$recs, $r) if ($r and $r->[0]); }
587                 return { ids => $recs, 
588                         count => ($type) ? $result->[0]->[3] : $result->[0]->[2] };
589         }
590         return { count => 0 };
591 }
592
593
594 =head comment 
595
596 __PACKAGE__->register_method(
597         method  => "biblio_search_class",
598         api_name        => "open-ils.search.biblio.class",
599 );
600
601 __PACKAGE__->register_method(
602         method  => "biblio_search_class",
603         api_name        => "open-ils.search.biblio.class.full",
604 );
605
606 __PACKAGE__->register_method(
607         method  => "biblio_search_class",
608         api_name        => "open-ils.search.biblio.class.full.staff",
609 );
610
611 __PACKAGE__->register_method(
612         method  => "biblio_search_class",
613         api_name        => "open-ils.search.biblio.class.staff",
614 );
615
616 sub biblio_search_class {
617
618         my( $self, $client, $class, $string, 
619                         $org_id, $org_type, $limit, $offset, $format ) = @_;
620
621         warn "org: $org_id : depth: $org_type : limit: $limit :  offset: $offset\n";
622
623
624         $offset         = 0     unless (defined($offset) and $offset > 0);
625         $limit          = 100 unless (defined($limit) and $limit > 0);
626         $org_id         = "1" unless (defined($org_id)); # xxx
627         $org_type       = 0     unless (defined($org_type));
628
629         warn "Searching biblio.class.id\n" . 
630                 "string: $string "              . 
631                 "\noffset: $offset\n"   .
632                 "limit: $limit\n"                       .
633                 "org_id: $org_id\n"             .
634                 "depth: $org_type\n"            .
635                 "format: $format\n";
636
637         warn "Search filtering string " . time() . "\n";
638         $string = OpenILS::Application::Search->filter_search($string);
639         if(!$string) { return undef; }
640
641         if( !defined($org_id) or !$class or !$string ) {
642                 warn "not enbough args to metarecord search\n";
643                 throw OpenSRF::EX::InvalidArg 
644                         ("Not enough args to open-ils.search.biblio.class")
645         }
646
647         $class =~ s/\s+//g;
648
649         if( ($class ne "title") and ($class ne "author") and 
650                 ($class ne "subject") and ($class ne "keyword") 
651                 and ($class ne "series") ) {
652                 warn "Invalid search class: $class\n";
653                 throw OpenSRF::EX::InvalidArg ("Not a valid search class: $class")
654         }
655
656         #my $method = "open-ils.storage.cachable.metabib.$class.post_filter.search_fts.metarecord.atomic";
657         my $method = "open-ils.storage.metabib.$class.post_filter.search_fts.metarecord.atomic";
658
659         if($self->api_name =~ /staff/) { 
660                 $method =~ s/atomic/staff\.atomic/og;
661                 $method =~ s/\.cachable//o;
662         }
663
664         if($self->api_name =~ /full/) { 
665                 $method =~ s/\.cachable//o; #XXX testing..
666         }
667
668         warn "Performing search method $method\n";
669         warn "MR search method is $method\n";
670
671         my $session = OpenSRF::AppSession->create('open-ils.storage');
672
673         warn "Search making request " . time() . "\n";
674         my $request = $session->request(        
675                 $method,
676                 term            => $string, 
677                 org_unit => $org_id, 
678                 depth           => $org_type, 
679                 limit           => $limit,
680                 offset  => $offset,
681                 format  => $format,
682                 cache_page_size => 200,
683                 );
684
685         my $records = $request->gather(1);
686         if(!$records) {return { ids => [] }};
687
688         #my @all_ids;
689
690         warn "Received " . scalar(@$records) . " id's from class search\n";
691
692 #       for my $i (@$records) { if(defined($i)) { push @all_ids, $i; } }
693 #       my @ids = @all_ids;
694 #       @ids = grep { defined($_->[0]) } @ids;
695
696         $session->finish();
697         $session->disconnect();
698
699         my $count = undef;
700         if( $records->[0] && defined($records->[0]->[3])) { $count = $records->[0]->[3];}
701         my $recs = [];
702         for my $r (@$records) { push( @$recs, $r ) if ($r and $r->[0]); }
703
704         # records has the form: [ mrid, rank, singleRecord / 0, hitCount ];
705         return { ids => $recs, count => $count };
706
707 }
708 =cut
709
710
711
712
713 __PACKAGE__->register_method(
714         method  => "biblio_mrid_to_modsbatch_batch",
715         api_name        => "open-ils.search.biblio.metarecord.mods_slim.batch.retrieve");
716
717 sub biblio_mrid_to_modsbatch_batch {
718         my( $self, $client, $mrids) = @_;
719         warn "Performing mrid_to_modsbatch_batch...";
720         my @mods;
721         my $method = $self->method_lookup("open-ils.search.biblio.metarecord.mods_slim.retrieve");
722         warn "Grabbing mods for " . Dumper($mrids) . "\n";
723
724         for my $id (@$mrids) {
725                 next unless defined $id;
726                 my ($m) = $method->run($id);
727                 push @mods, $m;
728         }
729         return \@mods;
730 }
731
732
733 __PACKAGE__->register_method(
734         method  => "biblio_mrid_to_modsbatch",
735         api_name        => "open-ils.search.biblio.metarecord.mods_slim.retrieve",
736         notes           => <<"  NOTES");
737         Returns the mvr associated with a given metarecod. If none exists, 
738         it is created.
739         NOTES
740
741 __PACKAGE__->register_method(
742         method  => "biblio_mrid_to_modsbatch",
743         api_name        => "open-ils.search.biblio.metarecord.mods_slim.retrieve.staff",
744         notes           => <<"  NOTES");
745         Returns the mvr associated with a given metarecod. If none exists, 
746         it is created.
747         NOTES
748
749 sub biblio_mrid_to_modsbatch {
750         my( $self, $client, $mrid ) = @_;
751
752         warn "Grabbing mvr for $mrid\n";
753
754         my $mr = _grab_metarecord($mrid);
755         return undef unless $mr;
756
757         if( my $m = $self->biblio_mrid_check_mvr($client, $mr)) {
758                 return $m;
759         }
760
761         return $self->biblio_mrid_make_modsbatch( $client, $mr ); 
762 }
763
764 # converts a metarecord to an mvr
765 sub _mr_to_mvr {
766         my $mr = shift;
767         my $perl = JSON->JSON2perl($mr->mods());
768         return Fieldmapper::metabib::virtual_record->new($perl);
769 }
770
771 # checks to see if a metarecord has mods, if so returns true;
772
773 __PACKAGE__->register_method(
774         method  => "biblio_mrid_check_mvr",
775         api_name        => "open-ils.search.biblio.metarecord.mods_slim.check",
776         notes           => <<"  NOTES");
777         Takes a metarecord ID or a metarecord object and returns true
778         if the metarecord already has an mvr associated with it.
779         NOTES
780
781 sub biblio_mrid_check_mvr {
782         my( $self, $client, $mrid ) = @_;
783         my $mr; 
784
785         if(ref($mrid)) { $mr = $mrid; } 
786         else { $mr = _grab_metarecord($mrid); }
787
788         warn "Checking mvr for mr " . $mr->id . "\n";
789
790         return _mr_to_mvr($mr) if $mr->mods();
791         return undef;
792 }
793
794 sub _grab_metarecord {
795
796         my $mrid = shift;
797         warn "Grabbing MR $mrid\n";
798
799         my $mr = OpenILS::Application::AppUtils->simple_scalar_request( 
800                 "open-ils.storage", 
801                 "open-ils.storage.direct.metabib.metarecord.retrieve", $mrid );
802
803         if(!$mr) {
804                 throw OpenSRF::EX::ERROR 
805                         ("No metarecord exists with the given id: $mrid");
806         }
807
808         return $mr;
809 }
810
811 __PACKAGE__->register_method(
812         method  => "biblio_mrid_make_modsbatch",
813         api_name        => "open-ils.search.biblio.metarecord.mods_slim.create",
814         notes           => <<"  NOTES");
815         Takes either a metarecord ID or a metarecord object.
816         Forces the creations of an mvr for the given metarecord.
817         The created mvr is returned.
818         NOTES
819
820 sub biblio_mrid_make_modsbatch {
821
822         my( $self, $client, $mrid ) = @_;
823
824         my $mr; 
825         if(ref($mrid)) { $mr = $mrid; }
826         else { $mr = _grab_metarecord($mrid); }
827         $mrid = $mr->id;
828
829         warn "Forcing mvr creation for mr " . $mr->id . "\n";
830         my $master_id = $mr->master_record;
831
832         my $session = OpenSRF::AppSession->create("open-ils.storage");
833
834         # grab the records attached to this metarecod 
835         warn "Creating mods batch for metarecord $mrid\n";
836         my $meth = "open-ils.search.biblio.metarecord_to_records.staff";
837         $meth = $self->method_lookup($meth);
838         my ($id_hash) = $meth->run($mrid);
839         my @ids = @{$id_hash->{ids}};
840         if(@ids < 1) { return undef; }
841
842         warn "Master ID is $master_id\n";
843         # grab the master record to start the mods batch 
844
845         $meth = "open-ils.storage.direct.biblio.record_entry.retrieve";
846
847         my $record = $session->request(
848                         "open-ils.storage.direct.biblio.record_entry.retrieve", $master_id );
849         $record = $record->gather(1);
850
851         #my $record = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
852
853         if(!$record) {
854                 warn "No record returned with id $master_id";
855                 throw OpenSRF::EX::ERROR 
856         }
857
858         my $u = OpenILS::Utils::ModsParser->new();
859         $u->start_mods_batch( $record->marc );
860         my $main_doc_id = $record->id();
861
862         @ids = grep { $_ ne $master_id } @ids;
863
864         # now we have to collect all of the marc objects and push them into a mods batch
865         my $request = $session->request(
866                 "open-ils.storage.direct.biblio.record_entry.batch.retrieve",  @ids );
867
868         while( my $response = $request->recv() ) {
869
870                 next unless $response;
871                 if(UNIVERSAL::isa( $response,"OpenSRF::EX")) {
872                         throw $response ($response->stringify);
873                 }
874
875                 my $content = $response->content;
876
877                 if( $content ) {
878                         $u->push_mods_batch( $content->marc );
879                 }
880         }
881
882         my $mods = $u->finish_mods_batch();
883         $mods->doc_id($mrid);
884         $request->finish();
885
886         $client->respond_complete($mods);
887
888         my $mods_string = JSON->perl2JSON($mods->decast);
889
890         $mr->mods($mods_string);
891
892         my $req = $session->request( 
893                 "open-ils.storage.direct.metabib.metarecord.update", $mr );
894
895
896         $req->gather(1);
897         $session->finish();
898         $session->disconnect();
899
900         return undef;
901 }
902
903
904
905 # converts a mr id into a list of record ids
906
907 __PACKAGE__->register_method(
908         method  => "biblio_mrid_to_record_ids",
909         api_name        => "open-ils.search.biblio.metarecord_to_records",
910 );
911
912 __PACKAGE__->register_method(
913         method  => "biblio_mrid_to_record_ids",
914         api_name        => "open-ils.search.biblio.metarecord_to_records.staff",
915 );
916
917 sub biblio_mrid_to_record_ids {
918         my( $self, $client, $mrid, $format ) = @_;
919
920         throw OpenSRF::EX::InvalidArg 
921                 ("search.biblio.metarecord_to_record_ids requires mr id")
922                         unless defined( $mrid );
923
924         warn "Searching for record for MR $mrid and format $format\n";
925
926         my $method = "open-ils.storage.ordered.metabib.metarecord.records.atomic";
927         if($self and $self->api_name =~ /staff/) { $method =~ s/atomic/staff\.atomic/; }
928         warn "Performing record retrieval with method $method\n";
929
930
931         my $mrmaps = OpenILS::Application::AppUtils->simple_scalar_request( 
932                         "open-ils.storage", $method, $mrid, $format );
933
934         warn Dumper $mrmaps;
935
936         my $size = @$mrmaps;    
937
938         return { count => $size, ids => $mrmaps };
939
940 }
941
942
943 __PACKAGE__->register_method(
944         method  => "biblio_record_to_marc_html",
945         api_name        => "open-ils.search.biblio.record.html" );
946
947 my $parser              = XML::LibXML->new();
948 my $xslt                        = XML::LibXSLT->new();
949 my $marc_sheet;
950
951 my $settings_client = OpenSRF::Utils::SettingsClient->new();
952 sub biblio_record_to_marc_html {
953         my( $self, $client, $recordid ) = @_;
954
955         if( !$marc_sheet ) {
956                 my $dir = $settings_client->config_value( "dirs", "xsl" );
957                 my $xsl = $settings_client->config_value(
958                         "apps", "open-ils.search", "app_settings", "marc_html_xsl" );
959
960                 $xsl = $parser->parse_file("$dir/$xsl");
961                 $marc_sheet = $xslt->parse_stylesheet( $xsl );
962         }
963
964
965         my $record = $apputils->simple_scalar_request(
966                 "open-ils.storage", 
967                 "open-ils.storage.direct.biblio.record_entry.retrieve",
968                 $recordid );
969
970         my $xmldoc = $parser->parse_string($record->marc);
971         my $html = $marc_sheet->transform($xmldoc);
972         $html = $html->toString();
973         return $html;
974
975 }
976
977
978
979 __PACKAGE__->register_method(
980         method  => "retrieve_all_copy_locations",
981         api_name        => "open-ils.search.config.copy_location.retrieve.all" );
982
983 my $shelving_locations;
984 sub retrieve_all_copy_locations {
985         my( $self, $client ) = @_;
986         if(!$shelving_locations) {
987                 $shelving_locations = $apputils->simple_scalar_request(
988                         "open-ils.storage", 
989                         "open-ils.storage.direct.asset.copy_location.retrieve.all.atomic");
990         }
991         return $shelving_locations;
992 }
993
994
995
996 __PACKAGE__->register_method(
997         method  => "retrieve_all_copy_statuses",
998         api_name        => "open-ils.search.config.copy_status.retrieve.all" );
999
1000 my $copy_statuses;
1001 sub retrieve_all_copy_statuses {
1002         my( $self, $client ) = @_;
1003         if(!$copy_statuses) {
1004                 $copy_statuses = $apputils->simple_scalar_request(
1005                         "open-ils.storage",
1006                         "open-ils.storage.direct.config.copy_status.retrieve.all.atomic" );
1007         }
1008         return $copy_statuses;
1009 }
1010
1011
1012 __PACKAGE__->register_method(
1013         method  => "copy_counts_per_org",
1014         api_name        => "open-ils.search.biblio.copy_counts.retrieve");
1015
1016 __PACKAGE__->register_method(
1017         method  => "copy_counts_per_org",
1018         api_name        => "open-ils.search.biblio.copy_counts.retrieve.staff");
1019
1020 sub copy_counts_per_org {
1021         my( $self, $client, $record_id ) = @_;
1022
1023         warn "Retreiveing copy copy counts for record $record_id and method " . $self->api_name . "\n";
1024
1025         my $method = "open-ils.storage.biblio.record_entry.global_copy_count.atomic";
1026         if($self->api_name =~ /staff/) { $method =~ s/atomic/staff\.atomic/; }
1027
1028         my $counts = $apputils->simple_scalar_request(
1029                 "open-ils.storage", $method, $record_id );
1030
1031         $counts = [ sort {$a->[0] <=> $b->[0]} @$counts ];
1032         return $counts;
1033 }
1034
1035
1036 __PACKAGE__->register_method(
1037         method          => "copy_count_summary",
1038         api_name        => "open-ils.search.biblio.copy_counts.summary.retrieve",
1039         notes           => <<"  NOTES");
1040         returns an array of these:
1041                 [ org_id, callnumber_label, <status1_count>, <status2_cout>,...]
1042                 where statusx is a copy status name.  the statuses are sorted
1043                 by id.
1044         NOTES
1045
1046 sub copy_count_summary {
1047         my( $self, $client, $rid ) = @_;
1048         my $method = "open-ils.storage.biblio.record_entry.status_copy_count.atomic";
1049         return $apputils->simple_scalar_request( "open-ils.storage", $method, $rid );
1050 }
1051
1052
1053 __PACKAGE__->register_method(
1054         method          => "multiclass_search",
1055         api_name        => "open-ils.search.biblio.multiclass",
1056         notes           => <<"  NOTES");
1057                 Performs a multiclass search
1058                 PARAMS( searchBlob, org_unit, format, limit ) 
1059                 where searchBlob is defined like this:
1060                         { 
1061                                 "title" : { "term" : "water" }, 
1062                                 "author" : { "term" : "smith" }, 
1063                                 ... 
1064                         }
1065         NOTES
1066
1067 __PACKAGE__->register_method(
1068         method          => "multiclass_search",
1069         api_name        => "open-ils.search.biblio.multiclass.staff",
1070         notes           => "see open-ils.search.biblio.multiclass" );
1071
1072 sub multiclass_search {
1073         my( $self, $client, $searchBlob, $orgid, $format, $limit ) = @_;
1074
1075         $logger->debug("Performing multiclass search with org => $orgid, " .
1076                 "format => $format, limit => $limit, and search blob " . Dumper($searchBlob));
1077
1078         my $meth = 'open-ils.storage.metabib.post_filter.multiclass.search_fts.metarecord.atomic';
1079         if($self->api_name =~ /staff/) { $meth =~ s/metarecord\.atomic/metarecord.staff.atomic/; }
1080
1081
1082         my $records = $apputils->simplereq(
1083                 'open-ils.storage', $meth, 
1084                  org_unit => $orgid, searches => $searchBlob, format => $format, limit => $limit );
1085
1086         my $count = 0;
1087         my $recs = [];
1088
1089         if( ref($records) and $records->[0] and 
1090                 defined($records->[0]->[3])) { $count = $records->[0]->[3];}
1091
1092         for my $r (@$records) { push( @$recs, $r ) if ($r and $r->[0]); }
1093
1094         # records has the form: [ mrid, rank, singleRecord / 0, hitCount ];
1095         return { ids => $recs, count => $count };
1096
1097
1098 }
1099
1100 __PACKAGE__->register_method(
1101         method          => "marc_search",
1102         api_name        => "open-ils.search.biblio.marc",
1103         notes           => <<"  NOTES");
1104                 Performs a multiclass search
1105                 PARAMS( searchBlob, org_unit, format ) 
1106                 where searchBlob is defined like this:
1107                 [ 
1108                         {
1109                                 "term":"shakespeare",
1110                                 "restrict":[{"tag":"245","subfield":"a"}] 
1111                         }, 
1112                         {
1113                                 "term":"bloom",
1114                                 "restrict":[{"tag":"100","subfield":"a"}] 
1115                         } 
1116
1117                 ]
1118         NOTES
1119
1120
1121 sub marc_search {
1122         my( $self, $client, $searchBlob, $orgid, $format ) = @_;
1123
1124         $logger->debug("Performing MARC search with org => $orgid, " .
1125                 "format => $format and search blob " . Dumper($searchBlob) );
1126                 
1127         my $records =  $apputils->simplereq(
1128                 'open-ils.storage',
1129                 'open-ils.storage.metabib.full_rec.multi_search.atomic',
1130                 searches => $searchBlob, org_unit => $orgid, format => $format );
1131
1132         my $count = 0;
1133         my $recs = [];
1134
1135         if( ref($records) and $records->[0] and 
1136                 defined($records->[0]->[3])) { $count = $records->[0]->[3];}
1137
1138         for my $r (@$records) { push( @$recs, $r ) if ($r and $r->[0]); }
1139
1140         # records has the form: [ mrid, rank, singleRecord / 0, hitCount ];
1141         return { ids => $recs, count => $count };
1142
1143 }
1144
1145
1146
1147 __PACKAGE__->register_method(
1148         method  => "biblio_search_isbn",
1149         api_name        => "open-ils.search.biblio.isbn",
1150 );
1151
1152 sub biblio_search_isbn { 
1153         my( $self, $client, $isbn ) = @_;
1154
1155         $logger->debug("Searching ISBN $isbn");
1156
1157         my $method = $self->method_lookup("open-ils.search.biblio.marc");
1158
1159         my ($records) = $method->run(  
1160                 [ {     term => $isbn,
1161                                 restrict => $cat_search_hash->{isbn} } ], 1);
1162
1163         return $records;
1164 }
1165
1166
1167 __PACKAGE__->register_method(
1168         method  => "biblio_search_issn",
1169         api_name        => "open-ils.search.biblio.issn",
1170 );
1171
1172 sub biblio_search_issn { 
1173         my( $self, $client, $issn ) = @_;
1174
1175         $logger->debug("Searching ISSN $issn");
1176
1177         my $method = $self->method_lookup("open-ils.search.biblio.marc");
1178
1179         my ($records) = $method->run(  
1180                 [ {     term => $issn,
1181                                 restrict => $cat_search_hash->{issn} } ], 1);
1182
1183         return $records;
1184 }
1185
1186
1187
1188
1189 __PACKAGE__->register_method(
1190         method  => "fetch_mods_by_copy",
1191         api_name        => "open-ils.search.biblio.mods_from_copy",
1192 );
1193
1194 sub fetch_mods_by_copy {
1195         my( $self, $client, $copyid ) = @_;
1196         my ($record, $evt) = $apputils->fetch_record_by_copy( $copyid );
1197         return $evt if $evt;
1198         return OpenILS::Event->new('ITEM_NOT_CATALOGED') unless $record->marc;
1199         return $apputils->record_to_mvr($record);
1200 }
1201
1202
1203
1204
1205
1206 # -------------------------------------------------------------------------------------
1207
1208 __PACKAGE__->register_method(
1209         method  => "cn_browse",
1210         api_name        => "open-ils.search.callnumber.browse.target",
1211         notes           => "Starts a callnumber browse"
1212         );
1213
1214 __PACKAGE__->register_method(
1215         method  => "cn_browse",
1216         api_name        => "open-ils.search.callnumber.browse.page_up",
1217         notes           => "Returns the previous page of callnumbers", 
1218         );
1219
1220 __PACKAGE__->register_method(
1221         method  => "cn_browse",
1222         api_name        => "open-ils.search.callnumber.browse.page_down",
1223         notes           => "Returns the next page of callnumbers", 
1224         );
1225
1226
1227 # RETURNS array of arrays like so: label, owning_lib, record, id
1228 sub cn_browse {
1229         my( $self, $client, @params ) = @_;
1230         my $method;
1231
1232         $method = 'open-ils.storage.asset.call_number.browse.target.atomic' 
1233                 if( $self->api_name =~ /target/ );
1234         $method = 'open-ils.storage.asset.call_number.browse.page_up.atomic'
1235                 if( $self->api_name =~ /page_up/ );
1236         $method = 'open-ils.storage.asset.call_number.browse.page_down.atomic'
1237                 if( $self->api_name =~ /page_down/ );
1238
1239         return $apputils->simplereq( 'open-ils.storage', $method, @params );
1240 }
1241 # -------------------------------------------------------------------------------------
1242
1243 __PACKAGE__->register_method(
1244         method => "fetch_cn",
1245         api_name => "open-ils.search.callnumber.retrieve",
1246         notes           => "retrieves a callnumber based on ID",
1247         );
1248
1249 sub fetch_cn {
1250         my( $self, $client, $id ) = @_;
1251         my( $cn, $evt ) = $apputils->fetch_callnumber( $id );
1252         return $evt if $evt;
1253         return $cn;
1254 }
1255
1256
1257
1258
1259
1260
1261
1262
1263 1;