From a594b9062d512a4309f48caa8c9a1012e67258c1 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 20 Jan 2014 12:15:31 -0500 Subject: [PATCH] LP#1053397 initial metarecord detail page * support for MR-focused copy query * MR-focused hold / copy summary counts Still much to do Signed-off-by: Bill Erickson --- .../perlmods/lib/OpenILS/Application/AppUtils.pm | 17 +- .../lib/OpenILS/Application/Serial/OPAC.pm | 2 +- .../src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm | 1 + .../perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm | 217 ++++++++++++++++++++- .../perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm | 30 ++- Open-ILS/src/templates/opac/metarecord.tt2 | 23 +++ 6 files changed, 271 insertions(+), 19 deletions(-) create mode 100644 Open-ILS/src/templates/opac/metarecord.tt2 diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm index b5900eda1c..dd721304e0 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm @@ -1943,10 +1943,10 @@ sub log_user_activity { sub basic_opac_copy_query { ###################################################################### - # Pass a defined value for either $rec_id OR ($iss_id AND $dist_id), # - # not both. # + # Pass a defined value for only one of $rec_id OR $mmr_id OR + # ($iss_id AND $dist_id). ###################################################################### - my ($self,$rec_id,$iss_id,$dist_id,$copy_limit,$copy_offset,$staff) = @_; + my ($self,$rec_id,$mmr_id,$iss_id,$dist_id,$copy_limit,$copy_offset,$staff) = @_; return { select => { @@ -1995,7 +1995,16 @@ sub basic_opac_copy_query { acn => { join => { acnp => { fkey => 'prefix' }, - acns => { fkey => 'suffix' } + acns => { fkey => 'suffix' }, + ($mmr_id ? (bre => { + join => { + mmrsm => { + field => 'source', + fkey => 'id', + filter => {metarecord => $mmr_id} + } + } + }) : ()) }, filter => [ {deleted => 'f'}, diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial/OPAC.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial/OPAC.pm index 6422edc248..93fcdef719 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial/OPAC.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Serial/OPAC.pm @@ -272,7 +272,7 @@ sub _opac_visible_unit_data { my $rows = $e->json_query( $U->basic_opac_copy_query( - undef, $issuance_id_list, $dist_id, + undef, undef, $issuance_id_list, $dist_id, 1000, 0, # XXX no mechanism for users to page at this level yet $staff ) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index 0af9399168..cee1eaf918 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -124,6 +124,7 @@ sub load { return $self->load_rresults if $path =~ m|opac/results|; return $self->load_print_record if $path =~ m|opac/record/print|; return $self->load_record if $path =~ m|opac/record/\d|; + return $self->load_metarecord if $path =~ m|opac/metarecord/\d|; return $self->load_cnbrowse if $path =~ m|opac/cnbrowse|; return $self->load_browse if $path =~ m|opac/browse|; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm index e3722678be..d633c6f893 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm @@ -63,7 +63,7 @@ sub load_record { my $cstore = OpenSRF::AppSession->create('open-ils.cstore'); my $copy_rec = $cstore->request( 'open-ils.cstore.json_query.atomic', - $self->mk_copy_query($rec_id, $org, $copy_depth, $copy_limit, $copy_offset, $pref_ou) + $self->mk_copy_query($rec_id, undef, $org, $copy_depth, $copy_limit, $copy_offset, $pref_ou) ); # find foreign copy data @@ -127,7 +127,7 @@ sub load_record { $ctx->{have_holdings_to_show} = 0; $ctx->{have_mfhd_to_show} = 0; - $self->get_hold_copy_summary($rec_id, $org); + $self->get_hold_copy_summary($rec_id, undef, $org); $self->timelog("past get_hold_copy_summary()"); $self->ctx->{bib_is_dead} = OpenILS::Application::AppUtils->is_true( @@ -223,6 +223,7 @@ sub fetch_related_search_info { sub mk_copy_query { my $self = shift; my $rec_id = shift; + my $mmr_id = shift; my $org = shift; my $depth = shift; my $copy_limit = shift; @@ -230,7 +231,8 @@ sub mk_copy_query { my $pref_ou = shift; my $query = $U->basic_opac_copy_query( - $rec_id, undef, undef, $copy_limit, $copy_offset, $self->ctx->{is_staff} + $rec_id, $mmr_id, undef, undef, + $copy_limit, $copy_offset, $self->ctx->{is_staff} ); if($org != $self->ctx->{aou_tree}->()->id) { @@ -419,11 +421,16 @@ sub prepare_browse_call_numbers { } sub get_hold_copy_summary { - my ($self, $rec_id, $org) = @_; + my ($self, $rec_id, $mmr_id, $org) = @_; my $ctx = $self->ctx; + + $rec_id = $mmr_id; my $search = OpenSRF::AppSession->create('open-ils.search'); - my $copy_count_meth = 'open-ils.search.biblio.record.copy_count'; + my $copy_count_meth = $mmr_id ? + 'open-ils.search.biblio.metarecord.copy_count' : + 'open-ils.search.biblio.record.copy_count'; + # We want to include OPAC-invisible copies in a staff context if ($ctx->{is_staff}) { $copy_count_meth .= '.staff'; @@ -438,9 +445,12 @@ sub get_hold_copy_summary { $org = $ctx->{get_aou}->($org)->parent_ou; } + my $hmeth = $mmr_id ? + 'open-ils.circ.mmr.holds.count' : + 'open-ils.circ.bre.holds.count'; + $self->ctx->{record_hold_count} = $U->simplereq( - 'open-ils.circ', 'open-ils.circ.bre.holds.count', - $rec_id, $count_args); + 'open-ils.circ', $hmeth, $rec_id, $count_args); $self->ctx->{copy_summary} = $req1->recv->content; @@ -608,4 +618,197 @@ sub get_ac_key { )[0]; } +sub load_metarecord { + my $self = shift; + my %kwargs = @_; + my $ctx = $self->ctx; + $ctx->{page} = 'metrecord'; + my $e = OpenILS::Utils::CStoreEditor->new; + + $self->timelog("load_metarecord() began"); + + my $mmr_id = $ctx->{page_args}->[0]; + return Apache2::Const::HTTP_BAD_REQUEST + unless $mmr_id and $mmr_id =~ /^\d+$/; + + my $mmr = $e->retrieve_metabib_metarecord($mmr_id) + or return Apache2::Const::HTTP_BAD_REQUEST; # bad ID + + # some data can be fetched directly via the master record + my $rec_id = $mmr->master_record; + + $self->added_content_stage1($rec_id); + $self->timelog("past added content stage 1"); + + my $org = $self->_get_search_lib(); + my $org_name = $ctx->{get_aou}->($org)->shortname; + my $pref_ou = $self->_get_pref_lib(); + my $depth = $self->cgi->param('depth'); + $depth = $ctx->{get_aou}->($org)->ou_type->depth + unless defined $depth; # can be 0 + + my $copy_depth = $self->cgi->param('copy_depth'); + $copy_depth = $depth unless defined $copy_depth; # can be 0 + $self->ctx->{copy_depth} = $copy_depth; + + my $copy_limit = int($self->cgi->param('copy_limit') || 10); + my $copy_offset = int($self->cgi->param('copy_offset') || 0); + + $self->get_staff_search_settings; + if ($ctx->{staff_saved_search_size}) { + $ctx->{saved_searches} = ($self->staff_load_searches)[1]; + } + $self->timelog("past staff saved searches"); + +# TODO +# $self->fetch_related_search_info($rec_id) unless $kwargs{no_search}; +# $self->timelog("past related search info"); + + # Check for user and load lists and prefs + if ($self->ctx->{user}) { + $self->_load_lists_and_settings; + $self->timelog("load user lists and settings"); + } + + # run copy retrieval in parallel to bib retrieval + my $cstore = OpenSRF::AppSession->create('open-ils.cstore'); + my $copy_rec = $cstore->request( + 'open-ils.cstore.json_query.atomic', + $self->mk_copy_query(undef, $mmr_id, $org, + $copy_depth, $copy_limit, $copy_offset, $pref_ou) + ); + +# TODO + # find foreign copy data +# my $peer_rec = $U->simplereq( +# 'open-ils.search', +# 'open-ils.search.peer_bibs', $rec_id ); +# +# $ctx->{foreign_copies} = $peer_rec; + + my (undef, @rec_data) = $self->get_records_and_facets([$mmr_id], undef, { + flesh => '{holdings_xml,bmp,mra,acp,acnp,acns}', + metarecord => 1, + site => $org_name, + depth => $depth, + pref_lib => $pref_ou + }); + + $self->timelog("past get_records_and_facets()"); + $ctx->{bre_id} = $mmr_id; + $ctx->{marc_xml} = $rec_data[0]->{marc_xml}; + $ctx->{copies} = $copy_rec->gather(1); + + # TODO move me to a standalone function + + # Add public copy notes to each copy - and while we're in there, grab peer bib records +# my %cached_bibs = (); +# foreach my $copy (@{$ctx->{copies}}) { +# $copy->{notes} = $U->simplereq( +# 'open-ils.circ', +# 'open-ils.circ.copy_note.retrieve.all', +# {itemid => $copy->{id}, pub => 1 } +# ); +# $self->timelog("past copy note retrieval call"); +# $copy->{peer_bibs} = $U->simplereq( +# 'open-ils.search', +# 'open-ils.search.multi_home.bib_ids.by_barcode', +# $copy->{barcode} +# ); +# $self->timelog("past peer bib id retrieval"); +# my @peer_marc; +# foreach my $bib (@{$copy->{peer_bibs}}) { +# next if $bib eq $ctx->{bre_id}; +# next if $cached_bibs{$bib}; +# my (undef, @peer_data) = $self->get_records_and_facets( +# [$bib], undef, { +# flesh => '{}', +# site => $org_name, +# depth => $depth, +# pref_lib => $pref_ou +# }); +# $cached_bibs{$bib} = 1; +# #$copy->{peer_bib_marc} = $peer_data[0]->{marc_xml}; +# push @peer_marc, $peer_data[0]->{marc_xml}; +# $self->timelog("fetched peer bib record $bib"); +# } +# $copy->{peer_bib_marc} = \@peer_marc; +# } + + $self->timelog("past store copy retrieval call"); + $ctx->{copy_limit} = $copy_limit; + $ctx->{copy_offset} = $copy_offset; + + $ctx->{have_holdings_to_show} = 0; + $ctx->{have_mfhd_to_show} = 0; + +# TODO + $self->get_hold_copy_summary(undef, $mmr_id, $org); + + $self->timelog("past get_hold_copy_summary()"); + $self->ctx->{bib_is_dead} = OpenILS::Application::AppUtils->is_true( + OpenILS::Utils::CStoreEditor->new->json_query({ + select => { bre => [ 'deleted' ] }, + from => 'bre', + where => { 'id' => $rec_id } + })->[0]->{deleted} + ); + + $cstore->kill_me; + +# if ( +# $ctx->{get_org_setting}-> +# ($org, "opac.fully_compressed_serial_holdings") +# ) { +# # We're loading this data here? Are we therefore assuming that we +# # *are* going to display something in the "issues" expandy? +# $self->load_serial_holding_summaries($rec_id, $org, $copy_depth); +# } else { +# $ctx->{mfhd_summaries} = +# $self->get_mfhd_summaries($rec_id, $org, $copy_depth); +# +# if ($ctx->{mfhd_summaries} && scalar(@{$ctx->{mfhd_summaries}}) +# ) { +# $ctx->{have_mfhd_to_show} = 1; +# }; +# } + + $self->timelog("past serials holding stuff"); + + my %expandies = ( + marchtml => sub { + $ctx->{marchtml} = $self->mk_marc_html($rec_id); + }, + issues => sub { + return; + # XXX this needed? + }, + cnbrowse => sub { + $self->prepare_browse_call_numbers(); + } + ); + + my @expand = $self->cgi->param('expand'); + if (grep {$_ eq 'all'} @expand) { + $ctx->{expand_all} = 1; + $expandies{$_}->() for keys %expandies; + + } else { + for my $exp (@expand) { + $ctx->{"expand_$exp"} = 1; + $expandies{$exp}->() if exists $expandies{$exp}; + } + } + + $self->timelog("past expandies"); + + $self->added_content_stage2($rec_id); + + $self->timelog("past added content stage 2"); + + return Apache2::Const::OK; +} + + + 1; 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..db2ad9aba0 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,9 +303,16 @@ 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_type})->documentElement; - my $xml = XML::LibXML->new->parse_string($data->{'unapi.bre'})->documentElement; + # NOTE: in metarecord mode, the bre_id will be that of the master + # record, in which case the bre_id acts as a secondary identifier + # for the metarecord. Caching via $bre_id (w/ is_meta key_suffix + # adjustments from above) should still produce unique cache + # values for each MR. IOW, there' no particular need to extract + # the MR id. $outer_self->timelog("get_records_and_facets(): parsed xml"); # Protect against legacy invalid MARCXML that might not have a 901c @@ -319,7 +330,7 @@ sub get_records_and_facets { my $key = 'TPAC_unapi_cache_'.$bre_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, { id => $bre_id, marc_xml => $data->{$unapi_type} }, 10); } } @@ -328,7 +339,8 @@ sub get_records_and_facets { } ); - $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 +371,23 @@ 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; + $ses->request( 'open-ils.cstore.json_query', {from => [ - 'unapi.bre', $bid, 'marcxml','record', + $unapi_type, $bid, 'marcxml','record', $unapi_args->{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/metarecord.tt2 b/Open-ILS/src/templates/opac/metarecord.tt2 new file mode 100644 index 0000000000..935ebdd5b2 --- /dev/null +++ b/Open-ILS/src/templates/opac/metarecord.tt2 @@ -0,0 +1,23 @@ +[%- PROCESS "opac/parts/header.tt2"; + WRAPPER "opac/parts/base.tt2"; + INCLUDE "opac/parts/topnav.tt2"; + ctx.page_title = l("Record Detail"); + IF CGI.param("expand"); basic_search = "f"; END; +-%] + [% INCLUDE "opac/parts/searchbar.tt2" %] +
+
+ [% IF ctx.staff_saved_search_size %] +
+ +
+ [% END %] +
+ [% INCLUDE "opac/parts/record/body.tt2" %] +
+
+
+
+[%- END %] -- 2.11.0