]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm
TPAC: Sort copies from preferred library first
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / WWW / EGCatLoader / Record.pm
1 package OpenILS::WWW::EGCatLoader;
2 use strict; use warnings;
3 use Apache2::Const -compile => qw(OK DECLINED FORBIDDEN HTTP_INTERNAL_SERVER_ERROR REDIRECT HTTP_BAD_REQUEST);
4 use OpenSRF::Utils::Logger qw/$logger/;
5 use OpenILS::Utils::CStoreEditor qw/:funcs/;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Application::AppUtils;
8 my $U = 'OpenILS::Application::AppUtils';
9
10 # context additions: 
11 #   record : bre object
12 sub load_record {
13     my $self = shift;
14     my $ctx = $self->ctx;
15     $ctx->{page} = 'record';  
16
17     my $org = $self->_get_search_lib();
18     my $org_name = $ctx->{get_aou}->($org)->shortname;
19     my $pref_ou = $self->_get_pref_lib();
20     my $depth = $self->cgi->param('depth');
21     $depth = $ctx->{get_aou}->($org)->ou_type->depth 
22         unless defined $depth; # can be 0
23
24     my $copy_depth = $self->cgi->param('copy_depth');
25     $copy_depth = $depth unless defined $copy_depth; # can be 0
26     $self->ctx->{copy_depth} = $copy_depth;
27
28     my $copy_limit = int($self->cgi->param('copy_limit') || 10);
29     my $copy_offset = int($self->cgi->param('copy_offset') || 0);
30
31     my $rec_id = $ctx->{page_args}->[0]
32         or return Apache2::Const::HTTP_BAD_REQUEST;
33
34     $self->get_staff_search_settings;
35     if ($ctx->{staff_saved_search_size}) {
36         $ctx->{saved_searches} = ($self->staff_load_searches)[1];
37     }
38
39     $self->fetch_related_search_info($rec_id);
40
41     # run copy retrieval in parallel to bib retrieval
42     # XXX unapi
43     my $cstore = OpenSRF::AppSession->create('open-ils.cstore');
44     my $copy_rec = $cstore->request(
45         'open-ils.cstore.json_query.atomic', 
46         $self->mk_copy_query($rec_id, $org, $copy_depth, $copy_limit, $copy_offset, $pref_ou)
47     );
48
49     my (undef, @rec_data) = $self->get_records_and_facets([$rec_id], undef, {
50         flesh => '{holdings_xml,bmp,mra,acp,acnp,acns}',
51         site => $org_name,
52         depth => $depth,
53         pref_lib => $pref_ou
54     });
55     $ctx->{bre_id} = $rec_data[0]->{id};
56     $ctx->{marc_xml} = $rec_data[0]->{marc_xml};
57
58     $ctx->{copies} = $copy_rec->gather(1);
59     $ctx->{copy_limit} = $copy_limit;
60     $ctx->{copy_offset} = $copy_offset;
61
62     $ctx->{have_holdings_to_show} = 0;
63     $ctx->{have_mfhd_to_show} = 0;
64
65     $self->get_hold_copy_summary($rec_id, $org);
66
67     $cstore->kill_me;
68
69     if (
70         $ctx->{get_org_setting}->
71             ($org, "opac.fully_compressed_serial_holdings")
72     ) {
73         $ctx->{holding_summaries} =
74             $self->get_holding_summaries($rec_id, $org, $copy_depth);
75
76         $ctx->{have_holdings_to_show} =
77             scalar(@{$ctx->{holding_summaries}->{basic}}) ||
78             scalar(@{$ctx->{holding_summaries}->{index}}) ||
79             scalar(@{$ctx->{holding_summaries}->{supplement}});
80     } else {
81         $ctx->{mfhd_summaries} =
82             $self->get_mfhd_summaries($rec_id, $org, $copy_depth);
83
84         if ($ctx->{mfhd_summaries} && scalar(@{$ctx->{mfhd_summaries}})
85         ) {
86             $ctx->{have_mfhd_to_show} = 1;
87         };
88     }
89
90     my %expandies = (
91         marchtml => sub {
92             $ctx->{marchtml} = $self->mk_marc_html($rec_id);
93         },
94         issues => sub {
95             $ctx->{expanded_holdings} =
96                 $self->get_expanded_holdings($rec_id, $org, $copy_depth)
97                 if $ctx->{have_holdings_to_show};
98         },
99         cnbrowse => sub {
100             $self->prepare_browse_call_numbers();
101         }
102     );
103
104     my @expand = $self->cgi->param('expand');
105     if (grep {$_ eq 'all'} @expand) {
106         $ctx->{expand_all} = 1;
107         $expandies{$_}->() for keys %expandies;
108
109     } else {
110         for my $exp (@expand) {
111             $ctx->{"expand_$exp"} = 1;
112             $expandies{$exp}->() if exists $expandies{$exp};
113         }
114     }
115
116     return Apache2::Const::OK;
117 }
118
119 # collect IDs and info on the search that lead to this details page
120 # If no search query, etc is present, we leave ctx.search_result_index == -1
121 sub fetch_related_search_info {
122     my $self = shift;
123     my $rec_id = shift;
124     my $ctx = $self->ctx;
125     $ctx->{search_result_index} = -1;
126
127     $self->load_rresults(internal => 1);
128
129     my @search_ids = @{$ctx->{ids}};
130     return unless @search_ids;
131
132     for my $idx (0..$#search_ids) {
133         if ($search_ids[$idx] == $rec_id) {
134             $ctx->{prev_search_record} = $search_ids[$idx - 1] if $idx > 0;
135             $ctx->{next_search_record} = $search_ids[$idx + 1];
136             $ctx->{search_result_index} = $idx;
137             last;
138         }
139     }
140
141     $ctx->{first_search_record} = $search_ids[0];
142     $ctx->{last_search_record} = $search_ids[-1];
143 }
144
145
146 sub mk_copy_query {
147     my $self = shift;
148     my $rec_id = shift;
149     my $org = shift;
150     my $depth = shift;
151     my $copy_limit = shift;
152     my $copy_offset = shift;
153     my $pref_ou = shift;
154
155     my $query = {
156         select => {
157             acp => ['id', 'barcode', 'circ_lib', 'create_date', 'age_protect', 'holdable'],
158             acpl => [
159                 {column => 'name', alias => 'copy_location'},
160                 {column => 'holdable', alias => 'location_holdable'}
161             ],
162             ccs => [
163                 {column => 'name', alias => 'copy_status'},
164                 {column => 'holdable', alias => 'status_holdable'}
165             ],
166             acn => [
167                 {column => 'label', alias => 'call_number_label'},
168                 {column => 'id', alias => 'call_number'}
169             ],
170             circ => ['due_date'],
171             acnp => [
172                 {column => 'label', alias => 'call_number_prefix_label'},
173                 {column => 'id', alias => 'call_number_prefix'}
174             ],
175             acns => [
176                 {column => 'label', alias => 'call_number_suffix_label'},
177                 {column => 'id', alias => 'call_number_suffix'}
178             ],
179             bmp => [
180                 {column => 'label', alias => 'part_label'},
181             ]
182         },
183
184         from => {
185             acp => {
186                 acn => { 
187                     join => { 
188                         acnp => { fkey => 'prefix' },
189                         acns => { fkey => 'suffix' }
190                     },
191                     filter => [{deleted => 'f'}, {record => $rec_id}],
192                 },
193                 circ => { # If the copy is circulating, retrieve the open circ
194                     type => 'left',
195                     filter => {checkin_time => undef}
196                 },
197                 acpl => {},
198                 ccs => {},
199                 aou => {},
200                 acpm => {
201                     type => 'left',
202                     join => {
203                         bmp => { type => 'left' }
204                     }
205                 }
206             }
207         },
208
209         where => {
210             '+acp' => {deleted => 'f' }
211         },
212
213         order_by => [
214             { class => "aou", field => 'id', 
215               transform => 'evergreen.rank_ou', params => [$org, $pref_ou]
216             },
217             {class => 'aou', field => 'name'}, 
218             {class => 'acn', field => 'label'},
219             { class => "acp", field => 'status',
220               transform => 'evergreen.rank_cp_status'
221             }
222         ],
223
224         limit => $copy_limit,
225         offset => $copy_offset
226     };
227
228     if($org != $self->ctx->{aou_tree}->()->id) { 
229         # no need to add the org join filter if we're not actually filtering
230         $query->{from}->{acp}->{aou} = {
231             fkey => 'circ_lib',
232             field => 'id',
233             filter => {
234                 id => {
235                     in => {
236                         select => {aou => [{
237                             column => 'id', 
238                             transform => 'actor.org_unit_descendants',
239                             result_field => 'id', 
240                             params => [$depth]
241                         }]},
242                         from => 'aou',
243                         where => {id => $org}
244                     }
245                 }
246             }
247         };
248         if ($org != $pref_ou) {
249             $query->{from}->{acp}->{aou}->{filter}->{id}->{in}->{select}->{aou} = [{
250                 column => 'id',
251                 transform => 'actor.org_unit_descendants_pref_lib',
252                 result_field => 'id',
253                 params => [$depth, $pref_ou]
254             }];
255         }
256     };
257
258     # Filter hidden items if this is the public catalog
259     unless($self->ctx->{is_staff}) { 
260         $query->{where}->{'+acp'}->{opac_visible} = 't';
261         $query->{from}->{'acp'}->{'acpl'}->{filter} = {opac_visible => 't'};
262         $query->{from}->{'acp'}->{'ccs'}->{filter} = {opac_visible => 't'};
263         $query->{where}->{'+aou'}->{opac_visible} = 't';
264     }
265
266     return $query;
267 }
268
269 sub mk_marc_html {
270     my($self, $rec_id) = @_;
271
272     # could be optimized considerably by performing the xslt on the already fetched record
273     return $U->simplereq(
274         'open-ils.search', 
275         'open-ils.search.biblio.record.html', $rec_id, 1);
276 }
277
278 sub get_holding_summaries {
279     my ($self, $rec_id, $org, $depth) = @_;
280
281     my $serial = create OpenSRF::AppSession("open-ils.serial");
282     my $result = $serial->request(
283         "open-ils.serial.bib.summary_statements",
284         $rec_id, {"org_id" => $org, "depth" => $depth}
285     )->gather(1);
286
287     $serial->kill_me;
288     return $result;
289 }
290
291 sub get_mfhd_summaries {
292     my ($self, $rec_id, $org, $depth) = @_;
293
294     my $serial = create OpenSRF::AppSession("open-ils.search");
295     my $result = $serial->request(
296         "open-ils.search.serial.record.bib.retrieve",
297         $rec_id, $org, $depth
298     )->gather(1);
299
300     $serial->kill_me;
301     return $result;
302 }
303
304 sub get_expanded_holdings {
305     my ($self, $rec_id, $org, $depth) = @_;
306
307     my $holding_limit = int($self->cgi->param("holding_limit") || 10);
308     my $holding_offset = int($self->cgi->param("holding_offset") || 0);
309     my $type = $self->cgi->param("expand_holding_type");
310
311     my $serial =  create OpenSRF::AppSession("open-ils.serial");
312     my $result = $serial->request(
313         "open-ils.serial.received_siss.retrieve.by_bib.atomic",
314         $rec_id, {
315             "ou" => $org, "depth" => $depth,
316             "limit" => $holding_limit, "offset" => $holding_offset,
317             "type" => $type
318         }
319     )->gather(1);
320
321     $serial->kill_me;
322     return $result;
323 }
324
325 sub any_call_number_label {
326     my ($self) = @_;
327
328     if ($self->ctx->{copies} and @{$self->ctx->{copies}}) {
329         return $self->ctx->{copies}->[0]->{call_number_label};
330     } else {
331         return;
332     }
333 }
334
335 sub prepare_browse_call_numbers {
336     my ($self) = @_;
337
338     my $cn = ($self->cgi->param("cn") || $self->any_call_number_label) or
339         return [];
340
341     my $org_unit = $self->ctx->{get_aou}->($self->cgi->param('loc')) ||
342         $self->ctx->{aou_tree}->();
343
344     my $supercat = create OpenSRF::AppSession("open-ils.supercat");
345     my $results = $supercat->request(
346         "open-ils.supercat.call_number.browse", 
347         $cn, $org_unit->shortname, 9, $self->cgi->param("cnoffset")
348     )->gather(1) || [];
349
350     $supercat->kill_me;
351
352     $self->ctx->{browsed_call_numbers} = [
353         map {
354             $_->record->marc(
355                 (new XML::LibXML)->parse_string($_->record->marc)
356             );
357             $_;
358         } @$results
359     ];
360     $self->ctx->{browsing_ou} = $org_unit;
361 }
362
363 sub get_hold_copy_summary {
364     my ($self, $rec_id, $org) = @_;
365     
366     my $search = OpenSRF::AppSession->create('open-ils.search');
367     my $req1 = $search->request(
368         'open-ils.search.biblio.record.copy_count', $org, $rec_id); 
369
370     $self->ctx->{record_hold_count} = $U->simplereq(
371         'open-ils.circ', 'open-ils.circ.bre.holds.count', $rec_id);
372
373     $self->ctx->{copy_summary} = $req1->recv->content;
374
375     $search->kill_me;
376 }
377
378 sub load_print_record {
379     my $self = shift;
380
381     my $rec_id = $self->ctx->{page_args}->[0] 
382         or return Apache2::Const::HTTP_BAD_REQUEST;
383
384     $self->{ctx}->{bre_id} = $rec_id;
385     $self->{ctx}->{printable_record} = $U->simplereq(
386         'open-ils.search',
387         'open-ils.search.biblio.record.print', $rec_id);
388
389     return Apache2::Const::OK;
390 }
391
392 sub load_email_record {
393     my $self = shift;
394
395     my $rec_id = $self->ctx->{page_args}->[0] 
396         or return Apache2::Const::HTTP_BAD_REQUEST;
397
398     $self->{ctx}->{bre_id} = $rec_id;
399     $U->simplereq(
400         'open-ils.search',
401         'open-ils.search.biblio.record.email', 
402         $self->ctx->{authtoken}, $rec_id);
403
404     return Apache2::Const::OK;
405 }
406
407 1;