LP#1208875: follow-up to standardize extract fields
authorGalen Charlton <gmc@equinoxinitiative.org>
Fri, 2 Jun 2017 17:13:50 +0000 (13:13 -0400)
committerJason Stephenson <jason@sigio.com>
Tue, 27 Jun 2017 13:52:42 +0000 (09:52 -0400)
This patch removes the proposed custom methods for extracting
title, author, and record format in favor of tweaking
->fetch_user_circ_history to invoke unapi.bre and adjusting
the template to use get_marc_attrs. Also, nowadays
->fetch_user_circ_history can flesh what we need it to without
having to rely on the existance of an action.circulation row,
which won't be present if the circ was aged but was otherwise
retained in the user circ history.

The result is slower than the previous approach, but still
retains the core idea of getting A/T out of the equation, and remains
much faster than the A/T approach.

Dropping use of unapi.bre would speed things up a bit more, as it
was added only to match the addition of the record format column
in the CSV output. Drop the column, and we no longer need to worry
about MVFs.

There would also be opportunities to improve caching further.  Bib
display fields, when it comes, will likely help even more, as it
would mean being able to drop a lot of the XML parsing currently used.

This patch also adjusts misc_util.tt2 so that including it doesn't
result in an unwanted blank line.

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Signed-off-by: Jason Etheridge <jason@equinoxinitiative.org>
Signed-off-by: Jason Stephenson <jason@sigio.com>
Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm
Open-ILS/src/templates/opac/myopac/circ_history/export.tt2
Open-ILS/src/templates/opac/parts/misc_util.tt2

index 947f183..9f96bfd 100644 (file)
@@ -1647,16 +1647,44 @@ sub fetch_user_circ_history {
 
     return $circs unless $flesh;
 
+    $e->xact_begin;
     my @circs;
+    my %unapi_cache = ();
     for my $circ (@$circs) {
-        push(@circs, {
-            circ => $circ,
-            marc_xml => ($circ->target_copy->call_number->id != -1) ?
-                XML::LibXML->new->parse_string(
-                    $circ->target_copy->call_number->record->marc) :
-                undef  # pre-cat copy, use the dummy title/author instead
-        });
+        if ($circ->target_copy->call_number->id == -1) {
+            push(@circs, {
+                circ => $circ,
+                marc_xml => undef # pre-cat copy, use the dummy title/author instead
+            });
+            next;
+        }
+        my $bre_id = $circ->target_copy->call_number->record->id;
+        my $unapi;
+        if (exists $unapi_cache{$bre_id}) {
+            $unapi = $unapi_cache{$bre_id};
+        } else {
+            my $result = $e->json_query({
+                from => [
+                    'unapi.bre', $bre_id, 'marcxml','record','{mra}', undef, undef, undef
+                ]
+            });
+            if ($result) {
+                $unapi_cache{$bre_id} = $unapi = XML::LibXML->new->parse_string($result->[0]->{'unapi.bre'});
+            }
+        }
+        if ($unapi) {
+            push(@circs, {
+                circ => $circ,
+                marc_xml => $unapi
+            });
+        } else {
+            push(@circs, {
+                circ => $circ,
+                marc_xml => undef # failed, but try to go on
+            });
+        }
     }
+    $e->rollback;
 
     return \@circs;
 }
@@ -2716,13 +2744,11 @@ sub load_myopac_circ_history_export {
     my $e = $self->editor;
     my $filename = $self->cgi->param('filename') || 'circ_history.csv';
 
-    my $circs = $self->fetch_user_circ_history;
-
-    $self->ctx->{csv}->{rows} = $self->_get_circ_history_csv(
-        [map {$_->source_circ} @$circs]
-    );
+    my $circs = $self->fetch_user_circ_history(1);
 
+    $self->ctx->{csv}->{circs} = $circs;
     return $self->set_file_download_headers($filename, 'text/csv; encoding=UTF-8');
+
 }
 
 sub load_password_reset {
@@ -2794,182 +2820,4 @@ sub load_password_reset {
     return Apache2::Const::OK;
 }
 
-# fetch_user_circs is too slow for our purposes.  (Yes, I tested it.)
-# It also does a lot more than we need in order to produce our CSV
-# output of a patron's circ history.  So, _get_circ_history_csv was
-# concocted to get only the information that we need in a more timely
-# manner, though it is still not as speedy as one would like.
-#
-# Given an array ref of circulation ids, the function returns an array
-# ref of hash ref objects with fields, xact_start, due_date,
-# checkin_time, title, author, call_number, barcode, and format.
-# There is one array entry for each circulation.  This is the
-# information presently required for the circ_history.csv file.
-sub _get_circ_history_csv {
-    my $self = shift;
-    my $ids = shift;
-    my $rows = [];
-    my $e = $self->editor;
-
-    if ($ids && @$ids) {
-        my $results = $e->json_query(
-            {
-                'select' => {
-                    'circ' => ['xact_start','due_date','checkin_time'],
-                    'acp' => ['barcode'],
-                    'acn' => ['label','record']
-                },
-                'from' => {'circ' => {'acp' => {'join' => 'acn'}}},
-                'where' => {'+circ' => {'id' => $ids}},
-                'order_by' => {'circ' => ['xact_start']}
-            }
-        );
-        foreach my $r (@$results) {
-            my $row = {};
-            my $record = $self->_get_record_data($r->{record});
-            $row->{title} = $record->{title};
-            $row->{author} = $record->{author};
-            $row->{format} = join('+', @{$record->{formats}});
-            $row->{xact_start} = $r->{xact_start};
-            $row->{due_date} = $r->{due_date};
-            $row->{checkin_time} = $r->{checkin_time};
-            $row->{barcode} = $r->{barcode};
-            $row->{call_number} = $r->{label};
-            push(@$rows, $row);
-        }
-    }
-
-    return $rows;
-}
-
-# Utility functions used by _get_circ_history_csv:
-
-# This funtion retrieves the information that we need from a bre for
-# our circ_history.csv output.  It gathers, the title, author, and
-# icon formats into a hashref using the _get_title_proper,
-# _get_author, and _get_icon_formats functions defined below.  Once
-# the data are retrieved, it is cached for 90 seconds in the
-# global cache.  Testing has shown that it takes about 82 seconds to
-# retrieve the information for a patron with 4,196 retained
-# circulations on a slow development system.  Using the cache with a
-# timeout of 90 seconds, dropped 4 seconds from the retrieval time,
-# and led to 371 cache hits.  The cached information is, of course,
-# used if available rather than doing a database lookup.
-sub _get_record_data {
-    my $self = shift;
-    my $record = shift;
-    my $e = $self->editor;
-    my $obj;
-    my $key = 'TPAC_csv_info_' . $record;
-
-    # First, we try the global cache.
-    my $cache = OpenSRF::Utils::Cache->new('global');
-    $obj = $cache->get_cache($key);
-
-    unless ($obj) {
-        $obj = {};
-
-        $obj->{author} = $self->_get_author($record);
-        $obj->{formats} = $self->_get_icon_formats($record);
-        $obj->{title} = $self->_get_title_proper($record);
-
-        $cache->put_cache($key, $obj, 90);
-    }
-
-    return $obj;
-}
-
-# This function looks up the text for the icon formats of a given
-# bre.id.  It does so using a JSON query and a cached hash map of
-# coded value map code => value.  The hash map is cached for 24 hours
-# because we have no idea how often people ask for their circ history
-# in a csv, nor how often they might want to retrieve their lists/book
-# bags in a csv.
-sub _get_icon_formats {
-    my $self = shift;
-    my $record = shift;
-    my $e = $self->editor;
-    my $formats = [];
-
-    # First, let's try using a cache:
-    my $cache = OpenSRF::Utils::Cache->new('global');
-    my $icon_formats = $cache->get_cache('CCVM_icon_format_code_map');
-    unless ($icon_formats) {
-        # Build a map of item_type coded value map:
-        my $ccvms = $e->search_config_coded_value_map({ctype => 'icon_format'});
-        if ($ccvms && @$ccvms) {
-            foreach my $ccvm (@$ccvms) {
-                $icon_formats->{$ccvm->code} = $ccvm->value;
-            }
-        }
-        $cache->put_cache('CCVM_icon_format_code_map', $icon_formats);
-    }
-
-
-    my $query = {
-        'select' => {'mraf' => ['value']},
-        'from' => 'mraf',
-        'where' => {'id' => $record, 'attr' => 'icon_format'},
-        'distinct' => 'true'
-    };
-
-    my $result = $e->json_query($query);
-    foreach (@$result) {
-        push(@$formats, $$icon_formats{$_->{value}});
-    }
-
-    return $formats;
-}
-
-# This function retrieves the "first" author for a bre using a JSON
-# query on metabib.author_field_entry.  It deterimines "first" by
-# ordering by "field" and then "id" and using a limit of 1 on the
-# result set.
-sub _get_author {
-    my $self = shift;
-    my $record = shift;
-    my $e = $self->editor;
-    my $author;
-
-    my $query = {
-        'select' => {'mafe' => ['value']},
-        'from' => 'mafe',
-        'where' => {'source' => $record},
-        'order_by' => {'mafe' => ['field','id']},
-        'limit' => 1
-    };
-
-    my $result = $e->json_query($query);
-    if ($result && @$result) {
-        $author = $result->[0]->{value};
-    }
-
-    return $author;
-}
-
-# This function looks up a "title proper" for a bre using a JSON query
-# on metabib.author_field_entry where the "field" is equal to 6, the
-# code for "title proper."
-sub _get_title_proper {
-    my $self = shift;
-    my $record = shift;
-    my $e = $self->editor;
-    my $title;
-
-    my $query = {
-        'select' => {'mtfe' => ['value']},
-        'from' => 'mtfe',
-        'where' => {'source' => $record, 'field' => 6},
-        'limit' => 1
-    };
-
-    my $result = $e->json_query($query);
-    if ($result && @$result) {
-        $title = $result->[0]->{value};
-    }
-
-    return $title;
-}
-
-
 1;
index 9bffe35..45aca55 100644 (file)
@@ -1,4 +1,5 @@
-[%- USE CSVFilter 'csv';
+[%- PROCESS "opac/parts/misc_util.tt2";
+    USE CSVFilter 'csv';
     USE date;
     SET DATE_FORMAT = l('%m/%d/%Y'); -%]
 [%- l('Title') | csv -%]
 [%- l('Barcode') | csv -%]
 [%- l('Call Number') | csv -%]
 [%- l('Format') | csv 'last' %]
-[%  FOREACH row IN ctx.csv.rows; -%]
-[%- row.title | csv -%]
-[%- row.author | csv -%]
-[%- date.format(ctx.parse_datetime(row.xact_start), DATE_FORMAT) | csv-%]
-[%- date.format(ctx.parse_datetime(row.due_date), DATE_FORMAT) | csv -%]
-[%- IF row.checkin_time;
-       date.format(ctx.parse_datetime(row.checkin_time), DATE_FORMAT) | csv;
+[%  FOREACH circ IN ctx.csv.circs;
+    attrs = { marc_xml => circ.marc_xml };
+    PROCESS get_marc_attrs args=attrs;
+    formats = [];
+    FOR format IN attrs.all_formats;
+        formats.push(format.label);
+    END;
+-%]
+[%- IF circ.circ.target_copy.call_number.id == -1 -%]
+    [%- circ.circ.target_copy.dummy_title | csv -%]
+    [%- circ.circ.target_copy.dummy_author | csv -%]
+[%- ELSIF attrs.title -%]
+    [%- attrs.title | csv -%]
+    [%- attrs.author | csv -%]
+[%- END -%]
+[%- date.format(ctx.parse_datetime(circ.circ.xact_start), DATE_FORMAT) | csv-%]
+[%- date.format(ctx.parse_datetime(circ.circ.due_date), DATE_FORMAT) | csv -%]
+[%- IF circ.circ.checkin_time;
+       date.format(ctx.parse_datetime(circ.checkin_time), DATE_FORMAT) | csv;
     ELSE; -%]
 ,
 [%- END -%]
-[%- row.barcode | csv -%]
-[%- row.call_number | csv -%]
-[%- row.format | csv 'last' %]
+[%- circ.circ.target_copy.barcode | csv -%]
+[%- circ.circ.target_copy.call_number.label | csv -%]
+[%- formats.join('+') | csv 'last' %]
 [%  END -%]
index 69ca1b4..add3c64 100644 (file)
@@ -1,4 +1,4 @@
-[% 
+[%- 
     # Support multiscript records via alternate graphic 880 fields
     # get_graphic_880s(target_field='100')
     # See "Model A" in http://www.loc.gov/marc/bibliographic/ecbdmulti.html
                     || CGI.param(loc_name) || CGI.param('loc') || ctx.search_ou;
     END;
 
-%]
+-%]