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