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