From: Bill Erickson Date: Mon, 20 Jan 2014 20:54:35 +0000 (-0500) Subject: LP#1053397 MR search, results display, and holds X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=8369ba8a85902bedb020c2623b9b1cab452d20ea;p=working%2FEvergreen.git LP#1053397 MR search, results display, and holds Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm index 8ba48ab600..00a0da10bd 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm @@ -92,8 +92,15 @@ sub test_and_create_hold_batch { elsif ($$params{'hold_type'} eq 'P') { $target_field = 'partid'; } else { return undef; } + my $formats_map = delete $$params{holdable_formats_map}; + foreach (@$target_list) { $$params{$target_field} = $_; + + # copy the requested formats from the target->formats map + # into the top-level formats attr for each hold + $$params{holdable_formats} = $formats_map->{$_}; + my $res; ($res) = $self->method_lookup( 'open-ils.circ.title_hold.is_possible')->run($auth, $params, $override ? $oargs : {}); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index 61b1028a84..82f6013a99 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -575,6 +575,9 @@ sub fetch_user_holds { [$blob->{hold}->{bre_id}], undef, {flesh => '{mra}'} ); $blob->{marc_xml} = $data[0]->{marc_xml}; + my $hold = $blob->{hold}->{hold}; + $blob->{metarecord_ccvms} = $self->get_mr_ccvms($hold->target) + if $hold->hold_type eq 'M'; push(@holds, $blob); } } @@ -777,6 +780,46 @@ sub load_place_hold { }; my $type_dispatch = { + M => sub { + + # target metarecords + my $mrecs = $e->batch_retrieve_metabib_metarecord([ + \@targets, + {flesh => 1, flesh_fields => {mmr => ['master_record']}}], + {substream => 1} + ); + + for my $id (@targets) { + my ($mr) = grep {$_->id eq $id} @$mrecs; + + my $avail_attrs = $self->get_mr_ccvms($mr->id); + my $format_attr = $avail_attrs->{formats}{attr}; + + # during hold placement submission, the user selects + # which of the available formats/langs are acceptiable. + # Capture those here as the holdable_formats for the MR hold. + my @selected_formats = $cgi->param('metarecord_formats_' . $mr->id); + my @selected_langs = $cgi->param('metarecord_langs_' . $mr->id); + + # map the selected attrs into the JSON holdable_formats structure + @selected_formats = map { + {_attr => $format_attr, _val => $_} } @selected_formats; + @selected_langs = map { + {_attr => 'item_lang', _val => $_} } @selected_langs; + + my $holdable_formats = OpenSRF::Utils::JSON->perl2JSON({ + 0 => \@selected_formats, + 1 => \@selected_langs + }); + + push(@hold_data, $data_filler->({ + target => $mr, + record => $mr->master_record, + metarecord_ccvms => $avail_attrs, + holdable_formats => $holdable_formats + })); + } + }, T => sub { my $recs = $e->batch_retrieve_biblio_record_entry(\@targets, {substream => 1}); @@ -984,13 +1027,25 @@ sub attempt_hold_placement { if(@create_targets) { + # holdable formats may be different for each MR hold. + # map each set to the ID of the target. + my $holdable_formats = {}; + if ($hold_type eq 'M') { + $holdable_formats->{$_->{target_id}} = + $_->{holdable_formats} for @hold_data; + # TODO: cleanup + warn "MR Holdable formats " . Dumper($holdable_formats) . "\n"; + } + my $bses = OpenSRF::AppSession->create('open-ils.circ'); my $breq = $bses->request( $method, $e->authtoken, - $data_filler->({ patronid => $usr, + $data_filler->({ + patronid => $usr, pickup_lib => $pickup_lib, - hold_type => $hold_type + hold_type => $hold_type, + holdable_formats_map => $holdable_formats }), \@create_targets ); @@ -1053,6 +1108,52 @@ sub attempt_hold_placement { } } +# fetches the CCVMs IDs representing available languages and formats +# for the constituent records of the selected metarecord. +sub get_mr_ccvms { + my ($self, $mr_id) = @_; + my $e = $self->editor; + + # crad.name which defines the set of metarecord formats + my $format_attr = $self->ctx->{search_cgf}->( # leverage caching + name => 'opac.metarecord.holds.format_attr')->[0]->value; + + my $bre_ids = + $e->search_metabib_metarecord_source_map({metarecord => $mr_id}); + $bre_ids = [map {$_->source} @$bre_ids]; + + my $query = { + select => {ccvm => [{column => 'id', transform => 'distinct'}]}, + from => {mraf => { + ccvm => { + fkey => 'attr', + field => 'ctype', + filter => {code => {'=' => {'+mraf' => 'value'}}} + } + }}, + where => {'+mraf' => {id => $bre_ids, attr => ''}} + }; + + $query->{where}{'+mraf'}{attr} = 'item_lang'; + my $langs = $e->json_query($query); + + $query->{where}{'+mraf'}{attr} = $format_attr; + my $formats = $e->json_query($query); + + return { + formats => { + attr => $format_attr, + values => [map { $_->{id} } @$formats], + }, + langs => { + attr => 'item_lang', + values => [map { $_->{id} } @$langs] + } + }; +} + + + sub fetch_user_circs { my $self = shift; my $flesh = shift; # flesh bib data, etc. diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm index 1a8740c206..b02b596744 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm @@ -67,7 +67,8 @@ sub _prepare_biblio_search { foreach ($cgi->param('modifier')) { # The unless bit is to avoid stacking modifiers. - $query = ('#' . $_ . ' ' . $query) unless $query =~ qr/\#\Q$_/; + $query = ('#' . $_ . ' ' . $query) unless + $query =~ qr/\#\Q$_/ or $_ eq 'metabib'; } # filters @@ -307,6 +308,13 @@ sub load_rresults { my $ctx = $self->ctx; my $e = $self->editor; + # 1. param->metarecord : view constituent bib records for a metarecord + # 2. param->modifier=metabib : perform a metarecord search + my $metarecord = $ctx->{metarecord} = $cgi->param('metarecord'); + my @mods = $cgi->param('modifier'); + my $is_meta = (@mods and grep {$_ eq 'metabib'} @mods and !$metarecord); + my $id_key = $is_meta ? 'mmr_id' : 'bre_id'; + # find the last record in the set, then redirect my $find_last = $cgi->param('find_last'); @@ -316,7 +324,9 @@ sub load_rresults { return $bbag_err; } - $ctx->{page} = 'rresult' unless $internal; + if (!$internal) { + $ctx->{page} = $is_meta ? 'mresult' : 'rresult'; + } $ctx->{ids} = []; $ctx->{records} = []; $ctx->{search_facets} = {}; @@ -343,7 +353,6 @@ sub load_rresults { $ctx->{search_ou} = $self->_get_search_lib(); $ctx->{pref_ou} = $self->_get_pref_lib() || $ctx->{search_ou}; my $offset = $page * $limit; - my $metarecord = $cgi->param('metarecord'); my $results; my $tag_circs = $self->tag_circed_items; $self->timelog("Got search parameters"); @@ -411,12 +420,14 @@ sub load_rresults { $query = "$_ $query" for @facets; - $logger->activity("EGWeb: [search] $query"); + my $ltag = $is_meta ? '[mmr search]' : '[bre search]'; + $logger->activity("EGWeb: $ltag $query"); try { my $method = 'open-ils.search.biblio.multiclass.query'; $method .= '.staff' if $ctx->{is_staff}; + $method =~ s/biblio/metabib/ if $is_meta; my $ses = OpenSRF::AppSession->create('open-ils.search'); @@ -443,6 +454,7 @@ sub load_rresults { my $rec_id = pop @$rec_ids; $cgi->delete('find_last'); my $url = $cgi->url(-full => 1, -path => 1, -query => 1); + # TODO: metarecord => /rresults?metarecord=$mmr_id $url =~ s|/results|/record/$rec_id|; return $self->generic_redirect($url); } @@ -451,12 +463,27 @@ sub load_rresults { $self->load_rresults_bookbag_item_notes($rec_ids) if $ctx->{bookbag}; + my $fetch_recs = $rec_ids; + + my $metarecord_master; + if ($metarecord) { + # when listing the contents of a metarecord, be sure to fetch + # the lead record for summary display. Adding the ID to + # $fetch_recs lets us grab the record (if necessary) w/o it + # unintentially becoming a member of the result set. + my $mr = $e->retrieve_metabib_metarecord($metarecord); + push(@$fetch_recs, $mr->master_record) + unless grep {$_ eq $mr->master_record} @$fetch_recs; + $metarecord_master = $mr->master_record; + } + $self->timelog("Calling get_records_and_facets()"); my ($facets, @data) = $self->get_records_and_facets( - $rec_ids, $results->{facet_key}, + $fetch_recs, $results->{facet_key}, { flesh => '{holdings_xml,mra,acp,acnp,acns,bmp}', site => $site, + metarecord => $is_meta, depth => $depth, pref_lib => $ctx->{pref_ou}, } @@ -464,6 +491,7 @@ sub load_rresults { $self->timelog("Returned from get_records_and_facets()"); if ($page == 0) { + # TODO: handle metarecords my $stat = $self->check_1hit_redirect($rec_ids); return $stat if $stat; } @@ -473,15 +501,16 @@ sub load_rresults { # shove recs into context in search results order for my $rec_id (@$rec_ids) { - push( - @{$ctx->{records}}, - grep { $_->{id} == $rec_id } @data - ); + my ($rec) = grep { $_->{$id_key} == $rec_id } @data; + push(@{$ctx->{records}}, $rec); + + $ctx->{metarecord_master} = $rec + if $metarecord_master and $metarecord_master eq $rec_id; } if ($tag_circs) { for my $rec (@{$ctx->{records}}) { - my ($res_rec) = grep { $_->[0] == $rec->{id} } @{$results->{ids}}; + my ($res_rec) = grep { $_->[0] == $rec->{$id_key} } @{$results->{ids}}; # index 1 in the per-record result array is a boolean which # indicates whether the record in question is in the users # accessible circ history list diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm index ce009dd4ca..2bc2c16b4b 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm @@ -277,9 +277,13 @@ sub get_records_and_facets { $unapi_args->{depth} ||= $self->ctx->{aou_tree}->()->ou_type->depth; $unapi_args->{flesh_depth} ||= 5; + my $is_meta = delete $unapi_args->{metarecord}; + my $unapi_type = $is_meta ? 'unapi.mmr' : 'unapi.bre'; + $unapi_cache ||= OpenSRF::Utils::Cache->new('global'); my $unapi_cache_key_suffix = join( '_', + $is_meta || 0, $unapi_args->{site}, $unapi_args->{depth}, $unapi_args->{flesh_depth}, @@ -299,36 +303,59 @@ sub get_records_and_facets { $outer_self->timelog("get_records_and_facets(): got response content"); # Protect against requests for non-existent records - return unless $data->{'unapi.bre'}; + return unless $data->{$unapi_type}; - my $xml = XML::LibXML->new->parse_string($data->{'unapi.bre'})->documentElement; + my $xml = XML::LibXML->new->parse_string($data->{$unapi_type})->documentElement; $outer_self->timelog("get_records_and_facets(): parsed xml"); # Protect against legacy invalid MARCXML that might not have a 901c my $bre_id; + my $mmr_id; my $bre_id_nodes = $xml->find('*[@tag="901"]/*[@code="c"]'); if ($bre_id_nodes) { $bre_id = $bre_id_nodes->[0]->textContent; } else { $logger->warn("Missing 901 subfield 'c' in " . $xml->toString()); } - $tmp_data{$bre_id} = {id => $bre_id, marc_xml => $xml}; - if ($bre_id) { + if ($is_meta) { + # extract metarecord ID from mmr.unapi tag + for my $node ($xml->getElementsByTagName('abbr')) { + my $title = $node->getAttribute('title'); + ($mmr_id = $title) =~ + s/tag:open-ils.org:U2\@mmr\/(\d+)\/.*/$1/g; + last if $mmr_id; + } + } + + my $rec_id = $mmr_id ? $mmr_id : $bre_id; + $tmp_data{$rec_id} = { + id => $rec_id, + bre_id => $bre_id, + mmr_id => $mmr_id, + marc_xml => $xml + }; + + if ($rec_id) { # Let other backends grab our data now that we're done. - my $key = 'TPAC_unapi_cache_'.$bre_id.'_'.$unapi_cache_key_suffix; + my $key = 'TPAC_unapi_cache_'.$rec_id.'_'.$unapi_cache_key_suffix; my $cache_data = $unapi_cache->get_cache($key); if ($$cache_data{running}) { - $unapi_cache->put_cache($key, { id => $bre_id, marc_xml => $data->{'unapi.bre'} }, 10); + $unapi_cache->put_cache($key, { + bre_id => $bre_id, + mmr_id => $mmr_id, + id => $rec_id, + marc_xml => $data->{$unapi_type} + }, 10); } } - $outer_self->timelog("get_records_and_facets(): end of success handler"); } ); - $self->timelog("get_records_and_facets(): about to call unapi.bre via json_query (rec_ids has " . scalar(@$rec_ids)); + $self->timelog("get_records_and_facets(): about to call ". + "$unapi_type via json_query (rec_ids has " . scalar(@$rec_ids)); my @loop_recs = @$rec_ids; my %rec_timeout; @@ -359,19 +386,26 @@ sub get_records_and_facets { $tmp_data{$unapi_data->{id}} = $unapi_data; } else { # we're the first or we timed out. success_handler will populate the real value $unapi_cache->put_cache($unapi_cache_key, { running => $$ }, 10); + + my $sdepth = $unapi_args->{flesh_depth}; + my $slimit = "acn=>$sdepth,acp=>$sdepth"; + $slimit .= ",bre=>$sdepth" if $is_meta; + my $flesh = $unapi_args->{flesh} || ''; + + # tag the record with the MR id + $flesh =~ s/}$/,mmr.unapi}/g if $is_meta; + $ses->request( 'open-ils.cstore.json_query', {from => [ - 'unapi.bre', $bid, 'marcxml','record', - $unapi_args->{flesh}, + $unapi_type, $bid, 'marcxml','record', $flesh, $unapi_args->{site}, $unapi_args->{depth}, - 'acn=>' . $unapi_args->{flesh_depth} . ',acp=>' . $unapi_args->{flesh_depth}, + $slimit, undef, undef, $unapi_args->{pref_lib} ]} ); } - } diff --git a/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 b/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 index 271cdad18a..8e0a81d3e4 100644 --- a/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 +++ b/Open-ILS/src/templates/opac/myopac/holds/edit.tt2 @@ -2,6 +2,7 @@ PROCESS "opac/parts/misc_util.tt2"; PROCESS "opac/parts/hold_status.tt2"; PROCESS "opac/parts/org_selector.tt2"; + PROCESS "opac/parts/metarecord_hold_filters.tt2"; WRAPPER "opac/parts/myopac/base.tt2"; myopac_page = "holds"; # in this case, just for tab coloring. @@ -34,6 +35,11 @@ [% l('Format:') %] [% attrs.format_label | html %]

[% END %] + [% IF hold.metarecord_ccvms.formats.values.size OR # should this be size > 1 + hold.metarecord_ccvms.langs.values.size > 1; + PROCESS metarecord_hold_filters_selector hold_data=hold; + END %] +

[% l('Status') %]: [% hold.human_status %]

diff --git a/Open-ILS/src/templates/opac/parts/advanced/search.tt2 b/Open-ILS/src/templates/opac/parts/advanced/search.tt2 index 42e5cfcebd..1fbd037f93 100644 --- a/Open-ILS/src/templates/opac/parts/advanced/search.tt2 +++ b/Open-ILS/src/templates/opac/parts/advanced/search.tt2 @@ -98,8 +98,19 @@ CASE "sort_selector"; INCLUDE "opac/parts/filtersort.tt2" value=CGI.param('sort') class='results_header_sel'; + %] - CASE "copy_location" %] + +
+ + +
+ + [% CASE "copy_location" %] + [% FOR ccvm IN format_ccvms.sort('search_label') %] + + [% END %] + + + +
+
+ [% l('Select your desired language(s)') %] +
+ +
+
 
+ +[% END # metarecord_hold_filters_selector %] diff --git a/Open-ILS/src/templates/opac/parts/place_hold.tt2 b/Open-ILS/src/templates/opac/parts/place_hold.tt2 index f12cd56b13..83668436de 100644 --- a/Open-ILS/src/templates/opac/parts/place_hold.tt2 +++ b/Open-ILS/src/templates/opac/parts/place_hold.tt2 @@ -1,5 +1,6 @@ [% PROCESS "opac/parts/misc_util.tt2"; PROCESS "opac/parts/hold_error_messages.tt2"; + PROCESS "opac/parts/metarecord_hold_filters.tt2"; %]
@@ -63,6 +64,10 @@ [% END %] [% END %] + [% IF hdata.metarecord_ccvms.formats.values.size OR # should this be size > 1 + hdata.metarecord_ccvms.langs.values.size > 1; + PROCESS metarecord_hold_filters_selector hold_data=hdata; + END %] [% END %] diff --git a/Open-ILS/src/templates/opac/parts/result/table.tt2 b/Open-ILS/src/templates/opac/parts/result/table.tt2 index e960a63cd4..b72e63850f 100644 --- a/Open-ILS/src/templates/opac/parts/result/table.tt2 +++ b/Open-ILS/src/templates/opac/parts/result/table.tt2 @@ -34,21 +34,40 @@ IF CGI.param('detail_record_view'); attrs.title = attrs.title_extended; END; + # note: rec.id refers to the record identifier, regardless + # of the type of record. i.e. rec.id = mmr_id ? mmr_id : bre_id + IF rec.mmr_id; + # metarecords link to record list page + record_url = mkurl(ctx.opac_root _ '/results', + {metarecord => rec.mmr_id}, ['page']); + hold_type = 'M'; + ELSE; + record_url = mkurl(ctx.opac_root _ '/record/' _ rec.bre_id); + hold_type = 'T'; + END; -%] [% result_count; result_count = result_count + 1 %]. - [% l('Image of item') %][% l('Image of item') %]
+ src='[% ctx.media_prefix %]/opac/extras/ac/jacket/small/r/[% rec.bre_id | uri %]' />