LP#1053397 initial metarecord detail page
authorBill Erickson <berick@esilibrary.com>
Mon, 20 Jan 2014 17:15:31 +0000 (12:15 -0500)
committerBill Erickson <berick@esilibrary.com>
Mon, 20 Jan 2014 17:15:31 +0000 (12:15 -0500)
* support for MR-focused copy query
* MR-focused hold / copy summary counts

Still much to do

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Serial/OPAC.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Util.pm
Open-ILS/src/templates/opac/metarecord.tt2 [new file with mode: 0644]

index b5900ed..dd72130 100644 (file)
@@ -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'},
index 6422edc..93fcdef 100644 (file)
@@ -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
         )
index 0af9399..cee1eaf 100644 (file)
@@ -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|;
 
index e372267..d633c6f 100644 (file)
@@ -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;
index ce009dd..db2ad9a 100644 (file)
@@ -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 (file)
index 0000000..935ebdd
--- /dev/null
@@ -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" %]
+    <br class="clear-both" />
+    <div id="content-wrapper" class="content-wrapper-record-page">
+        [% IF ctx.staff_saved_search_size %]
+        <div id="results-side-bar">
+            <div id="staff-saved-search">
+                [% INCLUDE "opac/parts/staff_saved_searches.tt2" %]
+            </div>
+        </div>
+        [% END %]
+        <div id="[% ctx.staff_saved_search_size ? 'main-content-after-bar' : 'main-content' %]">
+            [% INCLUDE "opac/parts/record/body.tt2" %]
+            <div class="common-full-pad"></div>
+        </div>
+        <br class="clear-both" />
+    </div>
+[%- END %]