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