]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Search/Biblio.pm
adding methods daily
[working/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 JSON;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::ModsParser;
8 use OpenSRF::Utils::SettingsClient;
9
10 use OpenILS::Application::AppUtils;
11
12 use JSON;
13
14 use Time::HiRes qw(time);
15 use OpenSRF::EX qw(:try);
16 use Digest::MD5 qw(md5_hex);
17
18 # Houses biblio search utilites 
19
20 __PACKAGE__->register_method(
21         method  => "biblio_search_marc",
22         api_name        => "open-ils.search.biblio.marc",
23         argc            => 1, 
24         note            => "Searches biblio information by marc tag",
25 );
26
27 sub biblio_search_marc {
28
29         my( $self, $client, $search_hash, $string ) = @_;
30
31         warn "Building biblio marc session\n";
32         my $session = OpenSRF::AppSession->create("open-ils.storage");
33
34         warn "Sending biblio marc request\n";
35         my $request = $session->request( 
36                         #"open-ils.storage.cachable.direct.metabib.full_rec.search_fts.index_vector", 
37                         "open-ils.storage.direct.metabib.full_rec.search_fts.index_vector", 
38                         restrict => $search_hash, 
39                         term            => $string );
40
41         warn "Waiting complete\n";
42         $request->wait_complete();
43
44         warn "Calling recv\n";
45         my $response = $request->recv(20);
46
47         warn "out of recv\n";
48         if($response and UNIVERSAL::isa($response,"OpenSRF::EX")) {
49                 throw $response ($response->stringify);
50         }
51
52
53         my $data = [];
54         if($response and UNIVERSAL::can($response,"content")) {
55                 $data = $response->content;
56         }
57         warn "finishing request\n";
58
59         $request->finish();
60         $session->finish();
61         $session->disconnect();
62
63         return $data;
64
65 }
66
67
68
69 # ---------------------------------------------------------------------------
70 # takes a list of record id's and turns the docs into friendly 
71 # mods structures. Creates one MODS structure for each doc id.
72 # ---------------------------------------------------------------------------
73 sub _records_to_mods {
74         my @ids = @_;
75         
76         my @results;
77         my @marcxml_objs;
78
79         my $session = OpenSRF::AppSession->create("open-ils.storage");
80         my $request = $session->request(
81                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve",  @ids );
82
83         my $last_content = undef;
84
85         while( my $response = $request->recv() ) {
86
87                 if( $last_content ) {
88                         my $u = OpenILS::Utils::ModsParser->new();
89                         $u->start_mods_batch( $last_content->marc );
90                         my $mods = $u->finish_mods_batch();
91                         $mods->doc_id($last_content->id());
92                         warn "Turning doc " . $mods->doc_id() . " into MODS\n";
93                         $last_content = undef;
94                         push @results, $mods;
95                 }
96
97                 next unless $response;
98
99                 if($response->isa("OpenSRF::EX")) {
100                         throw $response ($response->stringify);
101                 }
102
103                 $last_content = $response->content;
104
105         }
106
107         if( $last_content ) {
108                 my $u = OpenILS::Utils::ModsParser->new();
109                 $u->start_mods_batch( $last_content->marc );
110                 my $mods = $u->finish_mods_batch();
111                 $mods->doc_id($last_content->id());
112                 push @results, $mods;
113         }
114
115         $request->finish();
116         $session->finish();
117         $session->disconnect();
118
119         return \@results;
120
121 }
122
123 __PACKAGE__->register_method(
124         method  => "record_id_to_mods",
125         api_name        => "open-ils.search.biblio.record.mods.retrieve",
126         argc            => 1, 
127         note            => "Provide ID, we provide the mods"
128 );
129
130 # converts a record into a mods object with copy counts attached
131 sub record_id_to_mods {
132
133         my( $self, $client, $org_id, $id ) = @_;
134
135         my $mods_list = _records_to_mods( $id );
136         my $mods_obj = $mods_list->[0];
137         my $cmethod = $self->method_lookup(
138                         "open-ils.search.biblio.record.copy_count");
139         my ($count) = $cmethod->run($org_id, $id);
140         $mods_obj->copy_count($count);
141
142         return $mods_obj;
143 }
144
145
146 __PACKAGE__->register_method(
147         method  => "record_id_to_mods_slim",
148         api_name        => "open-ils.search.biblio.record.mods_slim.retrieve",
149         argc            => 1, 
150         note            => "Provide ID, we provide the mods"
151 );
152
153 # converts a record into a mods object with NO copy counts attached
154 sub record_id_to_mods_slim {
155
156         my( $self, $client, $id ) = @_;
157         warn "Retrieving MODS object for record $id\n";
158         return undef unless(defined $id);
159
160         my $mods_list = _records_to_mods( $id );
161         my $mods_obj = $mods_list->[0];
162         return $mods_obj;
163 }
164
165
166 # Returns the number of copies attached to a record based on org location
167 __PACKAGE__->register_method(
168         method  => "record_id_to_copy_count",
169         api_name        => "open-ils.search.biblio.record.copy_count",
170 );
171
172 __PACKAGE__->register_method(
173         method  => "record_id_to_copy_count",
174         api_name        => "open-ils.search.biblio.metarecord.copy_count",
175 );
176 sub record_id_to_copy_count {
177         my( $self, $client, $org_id, $record_id ) = @_;
178
179         my $method = "open-ils.storage.biblio.record_entry.copy_count.atomic";
180         my $key = "record";
181         if($self->api_name =~ /metarecord/) {
182                 $method = "open-ils.storage.metabib.metarecord.copy_count.atomic";
183                 $key = "metarecord";
184         }
185
186         my $session = OpenSRF::AppSession->create("open-ils.storage");
187         warn "copy_count retrieve $record_id\n";
188         return undef unless(defined $record_id);
189
190         my $request = $session->request(
191                 $method, org_unit => $org_id => $key => $record_id );
192
193         warn "copy_count wait $record_id\n";
194
195         my $count = $request->gather(1);
196         $session->disconnect();
197         return [ sort { $a->{depth} <=> $b->{depth} } @$count ];
198
199 }
200
201
202 # used for cat search classes
203 my $cat_search_hash =  {
204
205         author => [ 
206                 { tag => "100", subfield => "a"} ,
207                 { tag => "700", subfield => "a"}, 
208         ],
209
210         title => [ 
211                 { tag => "245", subfield => "a"},
212                 { tag => "242", subfield => "a"}, 
213                 { tag => "240", subfield => "a"},
214                 { tag => "210", subfield => "a"},
215         ],
216
217         subject => [ 
218                 { tag => "650", subfield => "_" }, 
219         ],
220
221         tcn     => [
222                 { tag => "035", subfield => "_" },
223         ],
224
225         isbn    => [
226                 { tag => "020", subfield => "a" },
227         ],
228
229 };
230
231
232 __PACKAGE__->register_method(
233         method  => "biblio_search_tcn",
234         api_name        => "open-ils.search.biblio.tcn",
235         argc            => 3, 
236         note            => "Retrieve a record by TCN",
237 );
238
239 sub biblio_search_tcn {
240
241         my( $self, $client, $tcn ) = @_;
242
243         $tcn =~ s/.*?(\w+)\s*$/$1/o;
244         warn "Searching TCN $tcn\n";
245
246         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
247         my $request = $session->request( 
248                         "open-ils.storage.direct.biblio.record_entry.search.tcn_value", $tcn );
249         warn "tcn going into recv\n";
250         my $response = $request->recv();
251
252
253         unless ($response) { return []; }
254
255         if(UNIVERSAL::isa($response,"OpenSRF::EX")) {
256                 warn "Received exception for tcn search\n";
257                 throw $response ($response->stringify);
258         }
259
260         my $record_entry = $response->content;
261         my @ids;
262         for my $record (@$record_entry) {
263                 push @ids, $record->id;
264         }
265
266         warn "received ID's for tcn search @ids\n";
267
268         my $size = @ids;
269         return { count => $size, ids => \@ids };
270
271 }
272
273
274 # --------------------------------------------------------------------------------
275 # ISBN
276
277 __PACKAGE__->register_method(
278         method  => "biblio_search_isbn",
279         api_name        => "open-ils.search.biblio.isbn",
280 );
281
282 sub biblio_search_isbn { 
283         my( $self, $client, $isbn ) = @_;
284         throw OpenSRF::EX::InvalidArg 
285
286                 ("biblio_search_isbn needs an ISBN to search")
287                         unless defined $isbn;
288
289         warn "biblio search for ISBN $isbn\n";
290         my $method = $self->method_lookup("open-ils.search.biblio.marc");
291         my ($records) = $method->run( $cat_search_hash->{isbn}, $isbn );
292         my @ids;
293         for my $i (@$records) { 
294                 if( ref($i) and defined($i->[0])) { 
295                         push @ids, $i->[0]; 
296                 }
297         }
298
299         my $size = @ids;
300         return { count => $size, ids => \@ids };
301 }
302
303
304
305 # --------------------------------------------------------------------------------
306
307 __PACKAGE__->register_method(
308         method  => "biblio_barcode_to_copy",
309         api_name        => "open-ils.search.biblio.barcode",
310 );
311
312 # turns a barcode into a copy object
313 sub biblio_barcode_to_copy { 
314         my( $self, $client, $barcode ) = @_;
315
316         throw OpenSRF::EX::InvalidArg 
317                 ("search.biblio.barcode needs an ISBN to search")
318                         unless defined $barcode;
319
320         warn "copy search for ISBN $barcode\n";
321         my $record = OpenILS::Application::AppUtils->simple_scalar_request(
322                         "open-ils.storage", 
323                         "open-ils.storage.direct.asset.copy.search.barcode",
324                         $barcode );
325
326         return undef unless($record);
327         return $record->[0];
328
329 }
330
331
332 __PACKAGE__->register_method(
333         method  => "biblio_copy_to_mods",
334         api_name        => "open-ils.search.biblio.copy.mods.retrieve",
335 );
336
337 # takes a copy object and returns it fleshed mods object
338 sub biblio_copy_to_mods {
339         my( $self, $client, $copy ) = @_;
340
341         throw OpenSRF::EX::InvalidArgs 
342                 ("copy.mods.retrieve needs a copy") unless( $copy );
343
344         new Fieldmapper::asset::copy($copy);
345
346         my $volume = OpenILS::Application::AppUtils->simple_scalar_request(
347                 "open-ils.storage",
348                 "open-ils.storage.direct.asset.call_number.retrieve",
349                 $copy->call_number() );
350
351         my $mods = _records_to_mods($volume->record());
352         $mods = shift @$mods;
353         $volume->copies([$copy]);
354         push @{$mods->call_numbers()}, $volume;
355
356         return $mods;
357 }
358
359
360 sub barcode_to_mods {
361
362 }
363
364
365 # --------------------------------------------------------------------------------
366
367 __PACKAGE__->register_method(
368         method  => "cat_biblio_search_class",
369         api_name        => "open-ils.search.cat.biblio.class",
370         argc            => 3, 
371         note            => "Searches biblio information by search class",
372 );
373
374 sub cat_biblio_search_class {
375
376         my( $self, $client, $org_id, $class, $sort, $string ) = @_;
377
378         throw OpenSRF::EX::InvalidArg 
379                 ("Not enough args to open-ils.search.cat.biblio.class")
380                         unless( defined($org_id) and $class and $sort and $string );
381
382
383         my $search_hash;
384
385         my $method = $self->method_lookup("open-ils.search.biblio.marc");
386         if(!$method) {
387                 throw OpenSRF::EX::PANIC 
388                         ("Can't lookup method 'open-ils.search.biblio.marc'");
389         }
390
391         my ($records) = $method->run( $cat_search_hash->{$class}, $string );
392
393         my @ids;
394         for my $i (@$records) { push @ids, $i->[0]; }
395
396         my $mods_list = _records_to_mods( @ids );
397         return undef unless (ref($mods_list) eq "ARRAY");
398
399         # ---------------------------------------------------------------
400         # append copy count information to the mods objects
401         my $session = OpenSRF::AppSession->create("open-ils.storage");
402
403         my $request = $session->request(
404                 "open-ils.storage.direct.biblio.record_copy_count.batch",  $org_id, @ids );
405
406         for my $id (@ids) {
407
408                 warn "receiving copy counts for doc $id\n";
409
410                 my $response = $request->recv();
411                 next unless $response;
412
413                 if( $response and UNIVERSAL::isa($response, "Error")) {
414                         throw $response ($response->stringify);
415                 }
416
417                 my $count = $response->content;
418                 my $mods_obj = undef;
419                 for my $m (@$mods_list) {
420                         $mods_obj = $m if ($m->doc_id() == $id)
421                 }
422                 if($mods_obj) {
423                         $mods_obj->copy_count($count);
424                 }
425
426                 $client->respond( $mods_obj );
427
428         }       
429         $request->finish();
430
431         $session->finish();
432         $session->disconnect();
433         $session->kill_me();
434         # ---------------------------------------------------------------
435
436         return undef;
437 }
438
439
440
441 __PACKAGE__->register_method(
442         method  => "cat_biblio_search_class_id",
443         api_name        => "open-ils.search.cat.biblio.class.id",
444         argc            => 3, 
445         note            => "Searches biblio information by search class and returns the IDs",
446 );
447
448 sub cat_biblio_search_class_id {
449
450         my( $self, $client, $org_id, $class, $string, $limit, $offset ) = @_;
451
452         $offset ||= 0;
453         $limit  ||= 100;
454         $limit -= 1;
455
456
457         $string = OpenILS::Application::Search->filter_search($string);
458         if(!$string) { return undef; }
459
460         warn "Searching cat.biblio.class.id string: $string offset: $offset limit: $limit\n";
461
462         throw OpenSRF::EX::InvalidArg 
463                 ("Not enough args to open-ils.search.cat.biblio.class")
464                         unless( defined($org_id) and $class and $string );
465
466
467         my $search_hash;
468
469         my $cache_key = md5_hex( $org_id . $class . $string );
470         my $id_array = OpenILS::Application::SearchCache->get_cache($cache_key);
471
472         if(ref($id_array)) {
473                 warn "Return search from cache\n";
474                 my $size = @$id_array;
475                 my @ids = @$id_array[ $offset..($offset+$limit) ];
476                 warn "Returning cat.biblio.class.id $string\n";
477                 return { count => $size, ids => \@ids };
478         }
479
480         my $method = $self->method_lookup("open-ils.search.biblio.marc");
481         if(!$method) {
482                 throw OpenSRF::EX::PANIC 
483                         ("Can't lookup method 'open-ils.search.biblio.marc'");
484         }
485
486         my ($records) = $method->run( $cat_search_hash->{$class}, $string );
487
488         my @cache_ids;
489
490         for my $i (@$records) { 
491                 if(defined($i->[0])) {
492                         push @cache_ids, $i->[0]; 
493                 }
494         }
495
496         my @ids = @cache_ids[ $offset..($offset+$limit) ];
497         my $size = @$records;
498
499         OpenILS::Application::SearchCache->put_cache( 
500                         $cache_key, \@cache_ids, $size );
501
502         warn "Returning cat.biblio.class.id $string\n";
503         return { count =>$size, ids => \@ids };
504
505 }
506
507
508 __PACKAGE__->register_method(
509         method  => "biblio_search_class",
510         api_name        => "open-ils.search.biblio.class",
511         argc            => 3, 
512         note            => "Searches biblio information by search class and returns the IDs",
513 );
514
515 sub biblio_search_class {
516
517         my( $self, $client, $class, $string, 
518                         $org_id, $org_type, $limit, $offset ) = @_;
519
520         warn "$org_id : $org_type : $limit :  $offset\n";
521
522         $offset         ||= 0;
523         $limit          = 100 unless defined($limit and $limit > 0 );
524         $org_id         = "1" unless defined($org_id); # xxx
525         $org_type       = 0     unless defined($org_type);
526
527         warn "$org_id : $org_type : $limit :  $offset\n";
528         warn "Searching biblio.class.id\n" . 
529                 "string: $string "              . 
530                 "\noffset: $offset\n"   .
531                 "limit: $limit\n"                       .
532                 "org_id: $org_id\n"             .
533                 "depth: $org_type\n" ;
534
535         $string = OpenILS::Application::Search->filter_search($string);
536         if(!$string) { return undef; }
537
538         if( !defined($org_id) or !$class or !$string ) {
539                 warn "not enbough args to metarecord search\n";
540                 throw OpenSRF::EX::InvalidArg 
541                         ("Not enough args to open-ils.search.cat.biblio.class")
542         }
543
544         $class =~ s/\s+//g;
545
546         if( ($class ne "title") and ($class ne "author") and 
547                 ($class ne "subject") and ($class ne "keyword") ) {
548                 warn "Invalid search class: $class\n";
549                 throw OpenSRF::EX::InvalidArg ("Not a valid search class: $class")
550         }
551
552         # grab the mr id's from storage
553
554         my $method = "open-ils.storage.metabib.$class.search_fts.metarecord_count";
555         warn "Performing count method $method\n";
556         my $session = OpenSRF::AppSession->create('open-ils.storage');
557
558         my $request = $session->request( $method, 
559                         term => $string, 
560                         org_unit => $org_id, 
561                         depth =>$org_type );
562
563         my $response = $request->recv();
564
565         if(UNIVERSAL::isa($response, "OpenSRF::EX")) {
566                 throw $response ($response->stringify);
567         }
568
569         my $count = $response->content;
570         warn "Received count $count\n";
571         # XXX check count size and respond accordingly
572
573         $request->finish();
574         warn "performing mr search\n";
575         $request = $session->request(   
576                 #"open-ils.storage.cachable.metabib.$class.search_fts.metarecord.atomic",
577                 "open-ils.storage.cachable.metabib.$class.search_fts.metarecord.atomic",
578                 term            => $string, 
579                 org_unit => $org_id, 
580                 depth           => $org_type, 
581                 limit           => $limit,
582                 offset  => $offset,
583                 );
584
585         warn "a\n";
586         $response = $request->recv();
587
588         if(UNIVERSAL::isa($response, "OpenSRF::EX")) {
589                 warn "Recieved Exception from storage: " . $response->stringify . "\n";
590                 $response->{'msg'} = $response->stringify();
591                 throw $response ($response->stringify);
592         }
593
594         warn "b\n";
595
596         my $records = $response->content;
597         use Data::Dumper;
598         warn Dumper $records;
599
600         my @all_ids;
601
602         # if we just get one, it won't be wrapped in an array
603         if(!ref($records->[0])) {
604                 $records = [$records];
605         }
606
607         for my $i (@$records) { 
608                 if(defined($i->[0])) {
609                         push @all_ids, $i->[0]; 
610                 }
611         }
612
613         #my @ids = @all_ids[ $offset..($offset+$limit) ];
614         my @ids = @all_ids;
615         @ids = grep { defined($_) } @ids;
616
617         $request->finish();
618         $session->finish();
619         $session->disconnect();
620
621         warn "Returning biblio.class $string\n";
622         return { count =>$count, ids => \@ids };
623
624 }
625
626
627
628
629 __PACKAGE__->register_method(
630         method  => "biblio_mrid_to_modsbatch",
631         api_name        => "open-ils.search.biblio.metarecord.mods_slim.retrieve",
632 );
633
634 sub biblio_mrid_to_modsbatch {
635         my( $self, $client, $mrid ) = @_;
636
637         throw OpenSRF::EX::InvalidArg 
638                 ("search.biblio.metarecord_to_mods requires mr id")
639                         unless defined( $mrid );
640
641
642         my $metarecord = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
643                         "open-ils.storage.direct.metabib.metarecord.retrieve", $mrid );
644
645         if(!$metarecord) {
646                 throw OpenSRF::EX::ERROR ("No metarecord exists with the given id: $mrid");
647         }
648
649         my $master_id = $metarecord->master_record();
650
651
652         # check for existing mods
653         if($metarecord->mods()){
654                 warn "We already have mods for " . $metarecord->id . "\n";
655                 my $perl = JSON->JSON2perl($metarecord->mods());
656                 use Data::Dumper;
657                 warn Dumper $perl;
658                 return Fieldmapper::metabib::virtual_record->new($perl);
659         }
660
661
662
663         warn "Creating mods batch for metarecord $mrid\n";
664         my $id_hash = biblio_mrid_to_record_ids( undef, undef,  $mrid );
665         my @ids = @{$id_hash->{ids}};
666
667         if(@ids < 1) { return undef; }
668
669         warn "Master ID is $master_id\n";
670         # grab the master record to start the mods batch 
671
672         my $record = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
673                         "open-ils.storage.direct.biblio.record_entry.retrieve", $master_id );
674
675         if(!$record) {
676                 throw OpenSRF::EX::ERROR 
677                         ("No record returned with id $master_id");
678         }
679
680         my $u = OpenILS::Utils::ModsParser->new();
681         warn "marc:\n" . $record->marc;
682         $u->start_mods_batch( $record->marc );
683         my $main_doc_id = $record->id();
684
685         @ids = grep { $_ ne $master_id } @ids;
686
687         warn "NON-Master IDs are @ids\n";
688
689         # now we have to collect all of the marc objects and push them into a mods batch
690         my $session = OpenSRF::AppSession->create("open-ils.storage");
691         my $request = $session->request(
692                 "open-ils.storage.direct.biblio.record_entry.batch.retrieve",  @ids );
693
694         while( my $response = $request->recv() ) {
695
696                 next unless $response;
697                 if(UNIVERSAL::isa( $response,"OpenSRF::EX")) {
698                         throw $response ($response->stringify);
699                 }
700
701                 my $content = $response->content;
702
703                 if( $content ) {
704                         $u->push_mods_batch( $content->marc );
705                 }
706         }
707
708         my $mods = $u->finish_mods_batch();
709         $mods->doc_id($mrid);
710         $request->finish();
711
712         $client->respond_complete($mods);
713
714         my $mods_string = JSON->perl2JSON($mods->decast);
715         warn "Mods String to DB:\n $mods_string\n";
716
717         $metarecord->mods($mods_string);
718
719         my $req = $session->request( 
720                         "open-ils.storage.direct.metabib.metarecord.update", 
721                         $metarecord );
722
723
724         $req->gather(1);
725
726         $session->finish();
727         $session->disconnect();
728
729         return undef;
730
731 }
732
733
734
735 # converts a mr id into a list of record ids
736
737 __PACKAGE__->register_method(
738         method  => "biblio_mrid_to_record_ids",
739         api_name        => "open-ils.search.biblio.metarecord_to_records",
740 );
741
742 sub biblio_mrid_to_record_ids {
743         my( $self, $client, $mrid ) = @_;
744
745         throw OpenSRF::EX::InvalidArg 
746                 ("search.biblio.metarecord_to_record_ids requires mr id")
747                         unless defined( $mrid );
748
749         warn "Searching for record for MR $mrid\n";
750
751         my $mrmaps = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
752                         "open-ils.storage.direct.metabib.metarecord_source_map.search.metarecord", $mrid );
753
754         my @ids;
755         for my $map (@$mrmaps) { push @ids, $map->source(); }
756
757         warn "Recovered id's [@ids] for mr $mrid\n";
758
759         my $size = @ids;
760         warn "Size: $size\n";
761
762         return { count => $size, ids => \@ids };
763
764 }
765
766
767
768 1;