From d1e58bbe6dd32d65f2f20d52607eafa36e01e20e Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 21 Feb 2019 14:21:47 -0500 Subject: [PATCH 1/1] LP1806087 Synchronize browse code for staff cat / tpac Teach the TPAC to call the use browse API, which was originally built from the TPAC browse code. This way we avoid duplicating the code. Signed-off-by: Bill Erickson Signed-off-by: Dan Wells --- .../lib/OpenILS/WWW/EGCatLoader/Browse.pm | 323 +----------------- 1 file changed, 12 insertions(+), 311 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Browse.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Browse.pm index 8f537c51a5..bd8681b074 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Browse.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Browse.pm @@ -23,32 +23,7 @@ my $U = 'OpenILS::Application::AppUtils'; my $browse_cache; my $browse_timeout; -# Plain procedural functions start here. -# -sub _init_browse_cache { - if (not defined $browse_cache) { - my $conf = new OpenSRF::Utils::SettingsClient; - - $browse_timeout = $conf->config_value( - "apps", "open-ils.search", "app_settings", "cache_timeout" - ) || 300; - $browse_cache = new OpenSRF::Utils::Cache("global"); - } -} - -sub _get_authority_heading { - my ($field, $sf_lookup, $joiner) = @_; - - $joiner ||= ' '; - - return join( - $joiner, - map { $_->[1] } grep { $sf_lookup->{$_->[0]} } $field->subfields - ); -} - # Object methods start here. -# # Returns cache key and a list of parameters for DB proc metabib.browse(). sub prepare_browse_parameters { @@ -58,251 +33,18 @@ sub prepare_browse_parameters { # XXX TODO add config.global_flag rows for browse limit-limit ? - my @params = ( - scalar($self->cgi->param('qtype')), - scalar($self->cgi->param('bterm')), - $self->ctx->{copy_location_group_org} || + return { + browse_class => scalar($self->cgi->param('qtype')), + term => scalar($self->cgi->param('bterm')), + org_unit => $self->ctx->{copy_location_group_org} || $self->ctx->{aou_tree}->()->id, - $self->ctx->{copy_location_group}, - $self->ctx->{is_staff} ? 't' : 'f', - scalar($self->cgi->param('bpivot')), - int( + copy_location_group => $self->ctx->{copy_location_group}, + pivot => scalar($self->cgi->param('bpivot')), + limit => int( $self->cgi->param('blimit') || $self->ctx->{opac_hits_per_page} || 10 ) - ); - - return ( - "oils_browse_" . md5_hex(OpenSRF::Utils::JSON->perl2JSON(\@params)), - @params - ); -} - -# Break out any Public General Notes (field 680) for display. These are -# sometimes (erroneously?) called "scope notes." I say erroneously, -# tentatively, because LoC doesn't seem to document a "scope notes" -# field for authority records, while it does so for classification -# records, which are something else. But I am not a librarian. -sub extract_public_general_notes { - my ($self, $record, $row) = @_; - - # Make a list of strings, each string being a concatentation of any - # subfields 'i', '5', or 'a' from one field 680, in order of appearance. - $row->{notes} = [ - map { - join( - " ", - map { $_->[1] } grep { $_->[0] =~ /[i5a]/ } $_->subfields - ) - } $record->field('680') - ]; -} - -sub find_authority_headings_and_notes { - my ($self, $row) = @_; - - my $acsaf_table = - $self->ctx->{get_authority_fields}->($row->{control_set}); - - $row->{headings} = []; - - my $record; - eval { - $record = new_from_xml MARC::Record($row->{marc}); }; - if ($@) { - $logger->warn("Problem with MARC from authority record #" . - $row->{id} . ": $@"); - return $row; # We're called in map(), so we must move on without - # a fuss. - } - - $self->extract_public_general_notes($record, $row); - - # extract headings from the main authority record along with their - # types - my $parsed_headings = $self->editor->json_query({ - from => [ "authority.extract_headings", $row->{marc} ] - }); - my %heading_type_map = (); - if ($parsed_headings) { - foreach my $h (@$parsed_headings) { - $heading_type_map{$h->{normalized_heading}} = - $h->{purpose} eq 'variant' ? 'variant' : - $h->{purpose} eq 'related' ? $h->{related_type} : - ''; - } - } - - # By applying grep in this way, we get acsaf objects that *have* and - # therefore *aren't* main entries, which is what we want. - foreach my $acsaf (values(%$acsaf_table)) { - my @fields = $record->field($acsaf->tag); - my %sf_lookup = map { $_ => 1 } split("", $acsaf->display_sf_list); - my @headings; - - foreach my $field (@fields) { - my $h = { main_entry => ( $acsaf->main_entry ? 0 : 1 ), - heading => _get_authority_heading($field, \%sf_lookup, $acsaf->joiner) }; - - my $norm = search_normalize($h->{heading}); - if (exists $heading_type_map{$norm}) { - $h->{type} = $heading_type_map{$norm}; - } - # XXX I was getting "target" from authority.authority_linking, but - # that makes no sense: that table can only tell you that one - # authority record as a whole points at another record. It does - # not record when a specific *field* in one authority record - # points to another record (not that it makes much sense for - # one authority record to have links to multiple others, but I can't - # say there definitely aren't cases for that). - $h->{target} = $2 - if ($field->subfield('0') || "") =~ /(^|\))(\d+)$/; - - # The target is the row id if this is a main entry... - $h->{target} = $row->{id} if $h->{main_entry}; - - push @headings, $h; - } - - push @{$row->{headings}}, {$acsaf->id => \@headings} if @headings; - } - - return $row; -} - -sub map_authority_headings_to_results { - my ($self, $linked, $results, $auth_ids, $authority_field_name) = @_; - - # Use the linked authority records' control sets to find and pick - # out non-main-entry headings. Build the headings and make a - # combined data structure for the template's use. - my %linked_headings_by_auth_id = map { - $_->{id} => $self->find_authority_headings_and_notes($_) - } @$linked; - - # Graft this authority heading data onto our main result set at the - # named column, either "authorities" or "sees". - foreach my $row (@$results) { - $row->{$authority_field_name} = [ - map { $linked_headings_by_auth_id{$_} } @{$row->{$authority_field_name}} - ]; - } - - # Get linked-bib counts for each of those authorities, and put THAT - # information into place in the data structure. - my $counts = $self->editor->json_query({ - select => { - abl => [ - {column => "id", transform => "count", - alias => "count", aggregate => 1}, - "authority" - ] - }, - from => {abl => {}}, - where => { - "+abl" => { - authority => [ - @$auth_ids, - $U->unique_unnested_numbers(map { $_->{target} } @$linked) - ] - } - } - }) or return; - - my %auth_counts = map { $_->{authority} => $_->{count} } @$counts; - - # Soooo nesty! We look for places where we'll need a count of bibs - # linked to an authority record, and put it there for the template to find. - for my $row (@$results) { - for my $auth (@{$row->{$authority_field_name}}) { - if ($auth->{headings}) { - for my $outer_heading (@{$auth->{headings}}) { - for my $heading_blob (@{(values %$outer_heading)[0]}) { - if ($heading_blob->{target}) { - $heading_blob->{target_count} = - $auth_counts{$heading_blob->{target}}; - } - } - } - } - } - } -} - -# flesh_browse_results() attaches data from authority records. It -# changes $results and returns 1 for success, undef for failure (in which -# case $self->editor->event should always point to the reason for failure). -# $results must be an arrayref of result rows from the DB's metabib.browse() -sub flesh_browse_results { - my ($self, $results) = @_; - - for my $authority_field_name ( qw/authorities sees/ ) { - for my $r (@$results) { - # Turn comma-seprated strings of numbers in "authorities" and "sees" - # columns into arrays. - if ($r->{$authority_field_name}) { - $r->{$authority_field_name} = [split /,/, $r->{$authority_field_name}]; - } else { - $r->{$authority_field_name} = []; - } - $r->{"list_$authority_field_name"} = [ @{$r->{$authority_field_name} } ]; - } - - # Group them in one arrray, not worrying about dupes because we're about - # to use them in an IN () comparison in a SQL query. - my @auth_ids = map { @{$_->{$authority_field_name}} } @$results; - - if (@auth_ids) { - # Get all linked authority records themselves - my $linked = $self->editor->json_query({ - select => { - are => [qw/id marc control_set/], - aalink => [{column => "target", transform => "array_agg", - aggregate => 1}] - }, - from => { - are => { - aalink => { - type => "left", - fkey => "id", field => "source" - } - } - }, - where => {"+are" => {id => \@auth_ids}} - }) or return; - - $self->map_authority_headings_to_results($linked, $results, \@auth_ids, $authority_field_name); - } - } - - return 1; -} - -sub load_browse_impl { - my ($self, @params) = @_; - - my $results = $self->editor->json_query({ - from => [ "metabib.browse", @params ] - }); - - if (not $results) { # DB error, not empty result set. - $logger->warn( - "error in browse (direct): " . $self->editor->event->{textcode} - ); - $self->ctx->{browse_error} = 1; - - return; - } elsif (not $self->flesh_browse_results($results)) { - $logger->warn( - "error in browse (flesh): " . $self->editor->event->{textcode} - ); - $self->ctx->{browse_error} = 1; - - return; - } - - return $results; } # Find paging information, put it into $self->ctx, and return "real" @@ -324,42 +66,9 @@ sub infer_browse_paging { return [ grep { not defined $_->{pivot_point} } @$results ]; } -sub leading_article_test { - my ($self, $qtype, $bterm) = @_; - - my $flag_name = "opac.browse.warnable_regexp_per_class"; - my $flag = $self->ctx->{get_cgf}->($flag_name); - - return unless $flag->enabled eq 't'; - - my $map; - - eval { $map = OpenSRF::Utils::JSON->JSON2perl($flag->value); }; - if ($@) { - $logger->warn("cgf '$flag_name' enabled but value is invalid JSON? $@"); - return; - } - - # Don't crash over any of the things that could go wrong in here: - eval { - if ($map->{$qtype}) { - if ($bterm =~ qr/$map->{$qtype}/i) { - $self->ctx->{browse_leading_article_warning} = 1; - ($self->ctx->{browse_leading_article_alternative} = $bterm) =~ - s/$map->{$qtype}//; - } - } - }; - if ($@) { - $logger->warn("cgf '$flag_name' has valid JSON in value, but: $@"); - } -} - sub load_browse { my ($self) = @_; - _init_browse_cache(); - # If there's a user logged in, flesh extended user info so we can get # her opac.hits_per_page setting, if any. if ($self->ctx->{user}) { @@ -387,20 +96,12 @@ sub load_browse { if ($self->cgi->param('qtype') and defined $self->cgi->param('bterm')) { - $self->leading_article_test( - $self->cgi->param('qtype'), - $self->cgi->param('bterm') - ); - - my ($cache_key, @params) = $self->prepare_browse_parameters; + my $method = 'open-ils.search.browse'; + $method .= '.staff' if $self->ctx->{is_staff}; + $method .= '.atomic'; - my $results = $browse_cache->get_cache($cache_key); - if (not $results) { - $results = $self->load_browse_impl(@params); - if ($results) { - $browse_cache->put_cache($cache_key, $results, $browse_timeout); - } - } + my $results = $U->simplereq('open-ils.search', + $method, $self->prepare_browse_parameters); if ($results) { $self->ctx->{browse_results} = $self->infer_browse_paging($results); -- 2.43.2