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:') %]
[% 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" %]
+
+