From 8180b60d040d6b1025de21bbd587839076037493 Mon Sep 17 00:00:00 2001 From: Mike Rylander Date: Fri, 22 Jan 2016 17:13:25 -0500 Subject: [PATCH] LP#1549505: Teach QP and its caller stack how to use badges Signed-off-by: Mike Rylander Signed-off-by: Galen Charlton Signed-off-by: Kathy Lussier --- .../lib/OpenILS/Application/Search/Biblio.pm | 4 +- .../Storage/Driver/Pg/QueryParser.pm | 61 ++- .../Application/Storage/Publisher/metabib.pm | 60 ++- .../lib/OpenILS/WWW/EGCatLoader/Record.pm | 15 + .../lib/OpenILS/WWW/EGCatLoader/Search.pm | 12 +- .../Pg/upgrade/YYYY.function.qp_search.sql | 396 ++++++++++++++++++ .../templates/opac/parts/record/summary.tt2 | 10 + .../src/templates/opac/parts/result/table.tt2 | 9 +- 8 files changed, 550 insertions(+), 17 deletions(-) create mode 100644 Open-ILS/src/sql/Pg/upgrade/YYYY.function.qp_search.sql diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm index a3376193e2..5c077903f4 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm @@ -1364,9 +1364,9 @@ sub staged_search { # Create backwards-compatible result structures if($IAmMetabib) { - $results = [map {[$_->{id}, $_->{rel}, $_->{record}]} @$results]; + $results = [map {[$_->{id}, $_->{badges}, $_->{popularity}, $_->{rel}, $_->{record}]} @$results]; } else { - $results = [map {[$_->{id}]} @$results]; + $results = [map {[$_->{id}, $_->{badges}, $_->{popularity}]} @$results]; } push @$new_ids, grep {defined($_)} map {$_->[0]} @$results; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm index ea557699a6..f06d097f62 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm @@ -651,6 +651,8 @@ __PACKAGE__->add_search_filter( 'statuses' ); __PACKAGE__->add_search_filter( 'locations' ); __PACKAGE__->add_search_filter( 'location_groups' ); __PACKAGE__->add_search_filter( 'bib_source' ); +__PACKAGE__->add_search_filter( 'badge_orgs' ); +__PACKAGE__->add_search_filter( 'badges' ); __PACKAGE__->add_search_filter( 'site' ); __PACKAGE__->add_search_filter( 'pref_ou' ); __PACKAGE__->add_search_filter( 'lasso' ); @@ -808,7 +810,6 @@ sub toSQL { $rel = "($rel * COALESCE( NULLIF( FIRST(mrv.vlist \@> ARRAY[lang_with.id]), FALSE )::INT * $plw, 1))"; $$flat_plan{uses_mrv} = 1; } - $rel = "1.0/($rel)::NUMERIC"; my $mrv_join = ''; if ($$flat_plan{uses_mrv}) { @@ -831,23 +832,66 @@ sub toSQL { $bre_join = 'INNER JOIN biblio.record_entry bre ON m.source = bre.id'; } - my $rank = $rel; - my $desc = 'ASC'; $desc = 'DESC' if ($self->find_modifier('descending')); my $nullpos = 'NULLS LAST'; $nullpos = 'NULLS FIRST' if ($self->find_modifier('nullsfirst')); + # Do we have a badges() filter? + my $badges = ''; + my ($badge_filter) = $self->find_filter('badges'); + if ($badge_filter && @{$badge_filter->args}) { + $badges = join (',', grep /^\d+$/, @{$badge_filter->args}); + } + + # Do we have a badge_orgs() filter? (used for calculating popularity) + my $borgs = ''; + my ($bo_filter) = $self->find_filter('badge_orgs'); + if ($bo_filter && @{$bo_filter->args}) { + $borgs = join (',', grep /^\d+$/, @{$bo_filter->args}); + } + + # Build the badge-ish WITH query + my $pop_with = <<' WITH'; + pop_with AS ( + SELECT record, + ARRAY_AGG(badge) AS badges, + SUM(s.score::NUMERIC*b.weight::NUMERIC)/SUM(b.weight::NUMERIC) AS total_score + FROM rating.record_badge_score s + JOIN rating.badge b ON ( + b.id = s.badge + WITH + + $pop_with .= " AND b.id = ANY ('{$badges}')" if ($badges); + $pop_with .= " AND b.scope = ANY ('{$borgs}')" if ($borgs); + $pop_with .= ') GROUP BY 1)'; + + my $pop_join = $badges ? # inner join if we are restricting via badges() + 'INNER JOIN pop_with ON ( m.source = pop_with.record )' : + 'LEFT JOIN pop_with ON ( m.source = pop_with.record )'; + + $$flat_plan{with} .= ',' if $$flat_plan{with}; + $$flat_plan{with} .= $pop_with; + + + my $rank; + my $pop_extra_sort = ''; if (grep {$_ eq $sort_filter} @{$self->QueryParser->dynamic_sorters}) { $rank = "FIRST((SELECT value FROM metabib.record_sorter rbr WHERE rbr.source = m.source and attr = '$sort_filter'))" } elsif ($sort_filter eq 'create_date') { $rank = "FIRST((SELECT create_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))"; } elsif ($sort_filter eq 'edit_date') { $rank = "FIRST((SELECT edit_date FROM biblio.record_entry rbr WHERE rbr.id = m.source))"; + } elsif ($sort_filter eq 'poprel') { + $rank = '1.0/((' . $rel . ') * (1.0 + AVG(COALESCE(pop_with.total_score::NUMERIC,0.0)) / 5.0))::NUMERIC'; + } elsif ($sort_filter =~ /^pop/) { + $rank = '1.0/(AVG(COALESCE(pop_with.total_score::NUMERIC,0.0)) + 5.0)::NUMERIC'; + my $pop_desc = $desc eq 'ASC' ? 'DESC' : 'ASC'; + $pop_extra_sort = "3 $pop_desc $nullpos,"; } else { # default to rel ranking - $rank = $rel; + $rank = "1.0/($rel)::NUMERIC"; } my $key = 'm.source'; @@ -877,18 +921,21 @@ SELECT $key AS id, $agg_records, $rel AS rel, $rank AS rank, - FIRST(pubdate_t.value) AS tie_break + FIRST(pubdate_t.value) AS tie_break, + STRING_AGG(ARRAY_TO_STRING(pop_with.badges,','),',') AS badges, + AVG(COALESCE(pop_with.total_score::NUMERIC,0.0))::NUMERIC(2,1) AS popularity FROM metabib.metarecord_source_map m $$flat_plan{from} - $pubdate_join $mra_join $mrv_join $bre_join + $pop_join + $pubdate_join $lang_join WHERE 1=1 $flat_where GROUP BY 1 - ORDER BY 4 $desc $nullpos, 5 DESC $nullpos, 3 DESC + ORDER BY 4 $desc $nullpos, $pop_extra_sort 5 DESC $nullpos, 3 DESC LIMIT $core_limit SQL diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm index fad6ef306a..aebd348284 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm @@ -3067,6 +3067,8 @@ sub query_parser_fts { delete $$summary_row{id}; delete $$summary_row{rel}; delete $$summary_row{record}; + delete $$summary_row{badges}; + delete $$summary_row{popularity}; if (defined($simple_plan)) { $$summary_row{complex_query} = $simple_plan ? 0 : 1; @@ -3098,6 +3100,8 @@ __PACKAGE__->register_method( cachable => 1, ); +my $top_org; + sub query_parser_fts_wrapper { my $self = shift; my $client = shift; @@ -3114,6 +3118,8 @@ sub query_parser_fts_wrapper { die "No search arguments were passed to ".$self->api_name; } + $top_org ||= actor::org_unit->search( { parent_ou => undef } )->next; + $log->debug("Constructing QueryParser query from staged search hash ...", DEBUG); my $base_query = ''; for my $sclass ( keys %{$args{searches}} ) { @@ -3144,7 +3150,59 @@ sub query_parser_fts_wrapper { if ($args{preferred_language_weight} and !$base_plan->parse_tree->find_filter('preferred_language_weight') and !$base_plan->parse_tree->find_filter('preferred_language_multiplier')); + my $borgs = undef; + if (!$base_plan->parse_tree->find_filter('badge_orgs')) { + # supply a suitable badge_orgs filter unless user has + # explicitly supplied one + my $site = undef; + + my @lg_id_list = @{$args{location_groups}} if (ref $args{location_groups}); + + my ($lg_filter) = $base_plan->parse_tree->find_filter('location_groups'); + @lg_id_list = @{$lg_filter->args} if ($lg_filter && @{$lg_filter->args}); + + if (@lg_id_list) { + my @borg_list; + for my $lg ( grep { /^\d+$/ } @lg_id_list ) { + my $lg_obj = asset::copy_location_group->retrieve($lg); + next unless $lg_obj; + + push(@borg_list, ''.$lg_obj->owner); + } + $borgs = join(',', @borg_list) if @borg_list; + } + + if (!$borgs) { + my ($site_filter) = $base_plan->parse_tree->find_filter('site'); + if ($site_filter && @{$site_filter->args}) { + $site = $top_org if ($site_filter->args->[0] eq '-'); + $site = $top_org if ($site_filter->args->[0] eq $top_org->shortname); + $site = actor::org_unit->search( { shortname => $site_filter->args->[0] })->next unless ($site); + } elsif ($args{org_unit}) { + $site = $top_org if ($args{org_unit} eq '-'); + $site = $top_org if ($args{org_unit} eq $top_org->shortname); + $site = actor::org_unit->search( { shortname => $args{org_unit} })->next unless ($site); + } else { + $site = $top_org; + } + + if ($site) { + $borgs = OpenSRF::AppSession->create( 'open-ils.cstore' )->request( + 'open-ils.cstore.json_query.atomic', + { from => [ 'actor.org_unit_ancestors', $site->id ] } + )->gather(1); + + if (ref $borgs && @$borgs) { + $borgs = join(',', map { $_->{'id'} } @$borgs); + } else { + $borgs = undef; + } + } + } + } + $query = "estimation_strategy($args{estimation_strategy}) $query" if ($args{estimation_strategy}); + $query = "badge_orgs($borgs) $query" if ($borgs); $query = "site($args{org_unit}) $query" if ($args{org_unit}); $query = "depth($args{depth}) $query" if (defined($args{depth})); $query = "sort($args{sort}) $query" if ($args{sort}); @@ -3173,7 +3231,7 @@ sub query_parser_fts_wrapper { $args{item_form} = [ split '', $f ]; } - for my $filter ( qw/locations location_groups statuses between audience language lit_form item_form item_type bib_level vr_format/ ) { + for my $filter ( qw/locations location_groups statuses between audience language lit_form item_form item_type bib_level vr_format badges/ ) { if (my $s = $args{$filter}) { $s = [$s] if (!ref($s)); 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 5f3d51c626..0253478bae 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Record.pm @@ -66,6 +66,21 @@ sub load_record { $self->mk_copy_query($rec_id, $org, $copy_depth, $copy_limit, $copy_offset, $pref_ou) ); + if ($self->cgi->param('badges')) { + my $badges = $self->cgi->param('badges'); + $badges = $badges ? [split(',', $badges)] : []; + $badges = [grep { /^\d+$/ }, @$badges]; + if (@$badges) { + $self->ctx->{badge_scores} = $cstore->request( + 'open-ils.cstore.direct.rating.record_badge_score.search.atomic', + { record => $rec_id, badge => $badges }, + { flesh => 1, flesh_fields => { rrbs => ['badge'] } } + )->gather(1); + } + } else { + $self->ctx->{badge_scores} = []; + } + # find foreign copy data my $peer_rec = $U->simplereq( 'open-ils.search', 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 51bdf238e2..7ee9cfae20 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Search.pm @@ -522,13 +522,15 @@ sub load_rresults { } } - if ($tag_circs) { - for my $rec (@{$ctx->{records}}) { - my ($res_rec) = grep { $_->[0] == $rec->{$id_key} } @{$results->{ids}}; - # index 1 in the per-record result array is a boolean which + for my $rec (@{$ctx->{records}}) { + my ($res_rec) = grep { $_->[0] == $rec->{$id_key} } @{$results->{ids}}; + $rec->{badges} = [split(',', $res_rec->[1])] if $res_rec->[1]; + $rec->{popularity} = $res_rec->[2]; + if ($tag_circs) { + # index 3 (5 for MR) in the per-record result array is a boolean which # indicates whether the record in question is in the users # accessible circ history list - my $index = $is_meta ? 3 : 1; + my $index = $is_meta ? 5 : 3; $rec->{user_circulated} = 1 if $res_rec->[$index]; } } diff --git a/Open-ILS/src/sql/Pg/upgrade/YYYY.function.qp_search.sql b/Open-ILS/src/sql/Pg/upgrade/YYYY.function.qp_search.sql new file mode 100644 index 0000000000..9dbbfe52b3 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/YYYY.function.qp_search.sql @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2016 Equinox Software, Inc. + * Mike Rylander + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +BEGIN; + +ALTER TYPE search.search_result ADD ATTRIBUTE badges TEXT, ADD ATTRIBUTE popularity NUMERIC; + +CREATE OR REPLACE FUNCTION search.query_parser_fts ( + + param_search_ou INT, + param_depth INT, + param_query TEXT, + param_statuses INT[], + param_locations INT[], + param_offset INT, + param_check INT, + param_limit INT, + metarecord BOOL, + staff BOOL, + deleted_search BOOL, + param_pref_ou INT DEFAULT NULL +) RETURNS SETOF search.search_result AS $func$ +DECLARE + + current_res search.search_result%ROWTYPE; + search_org_list INT[]; + luri_org_list INT[]; + tmp_int_list INT[]; + + check_limit INT; + core_limit INT; + core_offset INT; + tmp_int INT; + + core_result RECORD; + core_cursor REFCURSOR; + core_rel_query TEXT; + + total_count INT := 0; + check_count INT := 0; + deleted_count INT := 0; + visible_count INT := 0; + excluded_count INT := 0; + + luri_as_copy BOOL; +BEGIN + + check_limit := COALESCE( param_check, 1000 ); + core_limit := COALESCE( param_limit, 25000 ); + core_offset := COALESCE( param_offset, 0 ); + + SELECT COALESCE( enabled, FALSE ) INTO luri_as_copy FROM config.global_flag WHERE name = 'opac.located_uri.act_as_copy'; + + -- core_skip_chk := COALESCE( param_skip_chk, 1 ); + + IF param_search_ou > 0 THEN + IF param_depth IS NOT NULL THEN + SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou, param_depth ); + ELSE + SELECT ARRAY_AGG(distinct id) INTO search_org_list FROM actor.org_unit_descendants( param_search_ou ); + END IF; + + IF luri_as_copy THEN + SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_full_path( param_search_ou ); + ELSE + SELECT ARRAY_AGG(distinct id) INTO luri_org_list FROM actor.org_unit_ancestors( param_search_ou ); + END IF; + + ELSIF param_search_ou < 0 THEN + SELECT ARRAY_AGG(distinct org_unit) INTO search_org_list FROM actor.org_lasso_map WHERE lasso = -param_search_ou; + + FOR tmp_int IN SELECT * FROM UNNEST(search_org_list) LOOP + + IF luri_as_copy THEN + SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( tmp_int ); + ELSE + SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( tmp_int ); + END IF; + + luri_org_list := luri_org_list || tmp_int_list; + END LOOP; + + SELECT ARRAY_AGG(DISTINCT x.id) INTO luri_org_list FROM UNNEST(luri_org_list) x(id); + + ELSIF param_search_ou = 0 THEN + -- reserved for user lassos (ou_buckets/type='lasso') with ID passed in depth ... hack? sure. + END IF; + + IF param_pref_ou IS NOT NULL THEN + IF luri_as_copy THEN + SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_full_path( param_pref_ou ); + ELSE + SELECT ARRAY_AGG(distinct id) INTO tmp_int_list FROM actor.org_unit_ancestors( param_pref_ou ); + END IF; + + luri_org_list := luri_org_list || tmp_int_list; + END IF; + + OPEN core_cursor FOR EXECUTE param_query; + + LOOP + + FETCH core_cursor INTO core_result; + EXIT WHEN NOT FOUND; + EXIT WHEN total_count >= core_limit; + + total_count := total_count + 1; + + CONTINUE WHEN total_count NOT BETWEEN core_offset + 1 AND check_limit + core_offset; + + check_count := check_count + 1; + + IF NOT deleted_search THEN + + PERFORM 1 FROM biblio.record_entry b WHERE NOT b.deleted AND b.id IN ( SELECT * FROM unnest( core_result.records ) ); + IF NOT FOUND THEN + -- RAISE NOTICE ' % were all deleted ... ', core_result.records; + deleted_count := deleted_count + 1; + CONTINUE; + END IF; + + PERFORM 1 + FROM biblio.record_entry b + JOIN config.bib_source s ON (b.source = s.id) + WHERE s.transcendant + AND b.id IN ( SELECT * FROM unnest( core_result.records ) ); + + IF FOUND THEN + -- RAISE NOTICE ' % were all transcendant ... ', core_result.records; + visible_count := visible_count + 1; + + current_res.id = core_result.id; + current_res.rel = core_result.rel; + current_res.badges = core_result.badges; + current_res.popularity = core_result.popularity; + + tmp_int := 1; + IF metarecord THEN + SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id; + END IF; + + IF tmp_int = 1 THEN + current_res.record = core_result.records[1]; + ELSE + current_res.record = NULL; + END IF; + + RETURN NEXT current_res; + + CONTINUE; + END IF; + + PERFORM 1 + FROM asset.call_number cn + JOIN asset.uri_call_number_map map ON (map.call_number = cn.id) + JOIN asset.uri uri ON (map.uri = uri.id) + WHERE NOT cn.deleted + AND cn.label = '##URI##' + AND uri.active + AND ( param_locations IS NULL OR array_upper(param_locations, 1) IS NULL ) + AND cn.record IN ( SELECT * FROM unnest( core_result.records ) ) + AND cn.owning_lib IN ( SELECT * FROM unnest( luri_org_list ) ) + LIMIT 1; + + IF FOUND THEN + -- RAISE NOTICE ' % have at least one URI ... ', core_result.records; + visible_count := visible_count + 1; + + current_res.id = core_result.id; + current_res.rel = core_result.rel; + current_res.badges = core_result.badges; + current_res.popularity = core_result.popularity; + + tmp_int := 1; + IF metarecord THEN + SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id; + END IF; + + IF tmp_int = 1 THEN + current_res.record = core_result.records[1]; + ELSE + current_res.record = NULL; + END IF; + + RETURN NEXT current_res; + + CONTINUE; + END IF; + + IF param_statuses IS NOT NULL AND array_upper(param_statuses, 1) > 0 THEN + + PERFORM 1 + FROM asset.call_number cn + JOIN asset.copy cp ON (cp.call_number = cn.id) + WHERE NOT cn.deleted + AND NOT cp.deleted + AND cp.status IN ( SELECT * FROM unnest( param_statuses ) ) + AND cn.record IN ( SELECT * FROM unnest( core_result.records ) ) + AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + LIMIT 1; + + IF NOT FOUND THEN + PERFORM 1 + FROM biblio.peer_bib_copy_map pr + JOIN asset.copy cp ON (cp.id = pr.target_copy) + WHERE NOT cp.deleted + AND cp.status IN ( SELECT * FROM unnest( param_statuses ) ) + AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) ) + AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + LIMIT 1; + + IF NOT FOUND THEN + -- RAISE NOTICE ' % and multi-home linked records were all status-excluded ... ', core_result.records; + excluded_count := excluded_count + 1; + CONTINUE; + END IF; + END IF; + + END IF; + + IF param_locations IS NOT NULL AND array_upper(param_locations, 1) > 0 THEN + + PERFORM 1 + FROM asset.call_number cn + JOIN asset.copy cp ON (cp.call_number = cn.id) + WHERE NOT cn.deleted + AND NOT cp.deleted + AND cp.location IN ( SELECT * FROM unnest( param_locations ) ) + AND cn.record IN ( SELECT * FROM unnest( core_result.records ) ) + AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + LIMIT 1; + + IF NOT FOUND THEN + PERFORM 1 + FROM biblio.peer_bib_copy_map pr + JOIN asset.copy cp ON (cp.id = pr.target_copy) + WHERE NOT cp.deleted + AND cp.location IN ( SELECT * FROM unnest( param_locations ) ) + AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) ) + AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + LIMIT 1; + + IF NOT FOUND THEN + -- RAISE NOTICE ' % and multi-home linked records were all copy_location-excluded ... ', core_result.records; + excluded_count := excluded_count + 1; + CONTINUE; + END IF; + END IF; + + END IF; + + IF staff IS NULL OR NOT staff THEN + + PERFORM 1 + FROM asset.opac_visible_copies + WHERE circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + AND record IN ( SELECT * FROM unnest( core_result.records ) ) + LIMIT 1; + + IF NOT FOUND THEN + PERFORM 1 + FROM biblio.peer_bib_copy_map pr + JOIN asset.opac_visible_copies cp ON (cp.copy_id = pr.target_copy) + WHERE cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) ) + LIMIT 1; + + IF NOT FOUND THEN + + -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records; + excluded_count := excluded_count + 1; + CONTINUE; + END IF; + END IF; + + ELSE + + PERFORM 1 + FROM asset.call_number cn + JOIN asset.copy cp ON (cp.call_number = cn.id) + WHERE NOT cn.deleted + AND NOT cp.deleted + AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + AND cn.record IN ( SELECT * FROM unnest( core_result.records ) ) + LIMIT 1; + + IF NOT FOUND THEN + + PERFORM 1 + FROM biblio.peer_bib_copy_map pr + JOIN asset.copy cp ON (cp.id = pr.target_copy) + WHERE NOT cp.deleted + AND cp.circ_lib IN ( SELECT * FROM unnest( search_org_list ) ) + AND pr.peer_record IN ( SELECT * FROM unnest( core_result.records ) ) + LIMIT 1; + + IF NOT FOUND THEN + + PERFORM 1 + FROM asset.call_number cn + JOIN asset.copy cp ON (cp.call_number = cn.id) + WHERE cn.record IN ( SELECT * FROM unnest( core_result.records ) ) + AND NOT cp.deleted + LIMIT 1; + + IF NOT FOUND THEN + -- Recheck Located URI visibility in the case of no "foreign" copies + PERFORM 1 + FROM asset.call_number cn + JOIN asset.uri_call_number_map map ON (map.call_number = cn.id) + JOIN asset.uri uri ON (map.uri = uri.id) + WHERE NOT cn.deleted + AND cn.label = '##URI##' + AND uri.active + AND cn.record IN ( SELECT * FROM unnest( core_result.records ) ) + AND cn.owning_lib NOT IN ( SELECT * FROM unnest( luri_org_list ) ) + LIMIT 1; + + IF FOUND THEN + -- RAISE NOTICE ' % were excluded for foreign located URIs... ', core_result.records; + excluded_count := excluded_count + 1; + CONTINUE; + END IF; + ELSE + -- RAISE NOTICE ' % and multi-home linked records were all visibility-excluded ... ', core_result.records; + excluded_count := excluded_count + 1; + CONTINUE; + END IF; + END IF; + + END IF; + + END IF; + + END IF; + + visible_count := visible_count + 1; + + current_res.id = core_result.id; + current_res.rel = core_result.rel; + current_res.badges = core_result.badges; + current_res.popularity = core_result.popularity; + + tmp_int := 1; + IF metarecord THEN + SELECT COUNT(DISTINCT s.source) INTO tmp_int FROM metabib.metarecord_source_map s WHERE s.metarecord = core_result.id; + END IF; + + IF tmp_int = 1 THEN + current_res.record = core_result.records[1]; + ELSE + current_res.record = NULL; + END IF; + + RETURN NEXT current_res; + + IF visible_count % 1000 = 0 THEN + -- RAISE NOTICE ' % visible so far ... ', visible_count; + END IF; + + END LOOP; + + current_res.id = NULL; + current_res.rel = NULL; + current_res.record = NULL; + current_res.badges = NULL; + current_res.popularity = NULL; + current_res.total = total_count; + current_res.checked = check_count; + current_res.deleted = deleted_count; + current_res.visible = visible_count; + current_res.excluded = excluded_count; + + CLOSE core_cursor; + + RETURN NEXT current_res; + +END; +$func$ LANGUAGE PLPGSQL; + +COMMIT; + diff --git a/Open-ILS/src/templates/opac/parts/record/summary.tt2 b/Open-ILS/src/templates/opac/parts/record/summary.tt2 index 76f45ffebd..b2bd5e5849 100644 --- a/Open-ILS/src/templates/opac/parts/record/summary.tt2 +++ b/Open-ILS/src/templates/opac/parts/record/summary.tt2 @@ -314,6 +314,16 @@ IF num_uris > 0; [%- END %] + [%- IF (ctx.badge_scores.size > 0) %] +
  • + [% l("Badges:") %] +
      + [% FOR bscore IN ctx.badge_scores; %] +
    • [% bscore.badge.name | html %]: [% bscore.score %] / 5.0
    • + [%- END -%] + + + [%- END %]
    [%- INCLUDE "opac/parts/record/contents.tt2" %] diff --git a/Open-ILS/src/templates/opac/parts/result/table.tt2 b/Open-ILS/src/templates/opac/parts/result/table.tt2 index 960d8c652f..02fe684ec4 100644 --- a/Open-ILS/src/templates/opac/parts/result/table.tt2 +++ b/Open-ILS/src/templates/opac/parts/result/table.tt2 @@ -56,11 +56,11 @@ ELSE; # for MR, bre_id refers to the master and in # this case, only, record - record_url = mkurl(ctx.opac_root _ '/record/' _ rec.bre_id); + record_url = mkurl(ctx.opac_root _ '/record/' _ rec.bre_id, { badges => rec.badges.join(',') }); END; hold_type = 'M'; ELSE; - record_url = mkurl(ctx.opac_root _ '/record/' _ rec.bre_id); + record_url = mkurl(ctx.opac_root _ '/record/' _ rec.bre_id, { badges => rec.badges.join(',') }); hold_type = 'T'; END; -%] @@ -155,6 +155,11 @@ END; END -%] + [% IF rec.popularity > 0.0 %] +
    + [% l('Popularity:') %] [% rec.popularity %] / 5.0 +
    + [% END %]