From 56fd7b72b5fc7b5dc26806734304d5c5a3f23c3c Mon Sep 17 00:00:00 2001 From: wajasu Date: Sun, 16 Mar 2014 21:00:02 -0500 Subject: [PATCH] Bug 11909 - Fix hidelostitems, OpacHiddenItems total count,prog theme facet display Fix hidden items handling to set a tag 999h=1 for the biblio in rebuild_zebra.pl thus moving relavant items querying to rebuild_zebra.pl (for exported biblios). Fix facets building counters to leverage looking at the tag in the marc record getRecords method which feeds into searchResults. Added the hiding of branch/library facet when OpacHiddenItems rules specify the holdingbranch hidden. Fix searchResults to hide biblio results accordingly. Hopefully a perfomance improvement for searches since excessive database item queries are done once at index build. This patch lays the groundwork for: correct facet counting, and hooks for patron category hiding rules, and opac supress without the need for and extra zebra index. This patch is a work in progress validation of this strategy before adding all the other features and fixes on deck. Signed-off-by: wajasu --- C4/Items.pm | 84 +++++++++++++++++++++++++++++++++ C4/Search.pm | 86 ++++++++++++++++++++++++++------- misc/migration_tools/rebuild_zebra.pl | 89 +++++++++++++++++++++++++++++++++-- opac/opac-search.pl | 2 +- 4 files changed, 239 insertions(+), 22 deletions(-) diff --git a/C4/Items.pm b/C4/Items.pm index 12dce85..ae28627 100644 --- a/C4/Items.pm +++ b/C4/Items.pm @@ -75,6 +75,8 @@ BEGIN { GetItemnumberFromBarcode GetBarcodeFromItemnumber GetHiddenItemnumbers + GetHiddenItemnumbersByItemnumbers + isHidingOverridden DelItemCheck MoveItemFromBiblio GetLatestAcquisitions @@ -1714,6 +1716,88 @@ sub GetHiddenItemnumbers { return @resultitems; } +=head2 GetHiddenItemnumbersByItemnumbers + + my @hiddenitemnumbers = GetHiddenItemnumbersByItemnumbers(@itemnumbers); + +Given a list of itemnumbers it checks which should be hidden from the OPAC given +the current configuration. Returns a list of itemnumbers that should be hidden. + +=cut + +sub GetHiddenItemnumbersByItemnumbers { + my (@itemnumbers) = @_; + my @resultitems = (); + my $hidingrules = GetHidingRules(); + if (! $hidingrules ) { + return (); + } + my $dbh = C4::Context->dbh; + + # For each item + foreach my $itemnumber (@itemnumbers) { + + # We check each rule + foreach my $field (keys %$hidingrules) { + my $query = "SELECT $field from items where itemnumber = ?"; + my $val = $dbh->selectrow_array($query, undef, $itemnumber); + $val = '' unless defined $val; + + # If the results matches the values in the yaml file + if (any { $val eq $_ } @{$hidingrules->{$field}}) { + + # We add the itemnumber to the list + push @resultitems, $itemnumber; + + # If at least one rule matched for an item, no need to test the others + last; + } + } + } + return @resultitems; +} + +=head2 GetHiddingRules + + returns hiding rules keyed by rule name (i.e. homebranch) + +=cut + +sub GetHidingRules { + my $hidingrules; + my $yaml = C4::Context->preference('OpacHiddenItems'); + return () if (! $yaml =~ /\S/ ); + $yaml = "$yaml\n\n"; # YAML is anal on ending \n. Surplus does not hurt + eval { + $hidingrules = YAML::Load($yaml); + }; + if ($@) { + warn "Unable to parse OpacHiddenItems syspref : $@"; + return (); + } + return $hidingrules; +} + + +=head2 isHidingOverridden + + returns 1 (true) if the OpacHiddenItems is overridden. + +In the context of a particular search, a patron might be configured +to SEEALL items. + +=cut + +sub isHidingOverridden { + my $override = 0; # false + +=pod +#TODO: add patron SEEL and patron category override logic here + return @resultitems; +=cut + return $override; +} + =head3 get_item_authorised_values find the types and values for all authorised values assigned to this item. diff --git a/C4/Search.pm b/C4/Search.pm index 09c1951..b5b0a26 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -31,6 +31,7 @@ use C4::Branch; use C4::Reserves; # GetReserveStatus use C4::Debug; use C4::Charset; +use List::MoreUtils qw/any/; use YAML; use URI::Escape; use Business::ISBN; @@ -67,6 +68,7 @@ This module provides searching functions for Koha's bibliographic databases &SimpleSearch &searchResults &getRecords + &getItemSubfieldsToSearch &buildQuery &AddSearchHistory &GetDistinctValues @@ -340,6 +342,10 @@ sub getRecords { my @results; my $results_hashref = (); + my $opachiddenitems = C4::Context->preference('OpacHiddenItems'); + my $hidingrules = C4::Items::GetHidingRules(); + my $ishidingoverridden = C4::Items::isHidingOverridden(); + # Initialize variables for the faceted results objects my $facets_counter = (); my $facets_info = (); @@ -506,6 +512,7 @@ sub getRecords { my $jmax = $size > $facets_maxrecs ? $facets_maxrecs : $size; for my $facet (@$facets) { +#warn "XXXfacet: ".$facet->{idx}." ".$facet->{label};; for ( my $j = 0 ; $j < $jmax ; $j++ ) { my $marc_record = new_record_from_zebra ( @@ -519,10 +526,23 @@ sub getRecords { next; } + if ($opac) { + if ($opachiddenitems && !$ishidingoverridden) { +# TODO: get hidden tag mapping + #my ($bibliotag,$bibliosubf)=GetMarcFromKohaField('biblio.hidden',''); + my ($bibliotag,$bibliosubf)=('999','h'); + my $bibliohidden = $marc_record->field($bibliotag)->subfield($bibliosubf); +#warn "$i $j bibliohidden:$bibliohidden"; + if ( $bibliohidden && $bibliohidden == 1 ) { + next; # this biblio is not part of facets + } + + } + } + my @used_datas = (); foreach my $tag ( @{ $facet->{tags} } ) { - # avoid first line my $tag_num = substr( $tag, 0, 3 ); my $subfield_letters = substr( $tag, 3 ); @@ -532,6 +552,23 @@ sub getRecords { my @fields = $marc_record->field($tag_num); foreach my $field (@fields) { my $data = $field->as_string( $subfield_letters, $facet->{sep} ); +#warn "tagnum:$tag_num tag: $tag data: $data"; +# ITEMLEVEL handling: +#NEED TO LOOK AT ITEM IN DB and don't count YAML MATCHING fields tag subfield -> mapping holdingbranch +# these fields are from marc_record. might need raw as in rebuild_zebra. + +#IDEA: +#TOD GET RID OF hidden library facets do these: (if not patron SEEALL, and patronCategory, and OpacHiddenItems) + # hide the facet if it's OpacHiddenItems rule hides its items. + # (because we don't hide biblios if any non-hidden items exists) + # Could be done for other facets such as authors, locations, itemtypes, series, topics + # TODO: possibly discern which to hide based on DisplayLibraryFacets syspref from bug 11334 if it gets pushed + # handle homebranch as well if needed + if ($opachiddenitems && !$ishidingoverridden) { + if (any { $data eq $_ } @{$hidingrules->{'holdingbranch'}}) { # see getFacets branch tag995b or 952b + next; # skip hidden + } + } unless ( $data ~~ @used_datas ) { push @used_datas, $data; @@ -591,7 +628,7 @@ sub getRecords { $facet_max_length; # if it's a branch, label by the name, not the code, - if ( $link_value =~ /branch/ ) { + if ( $link_value =~ /branch/ ) { # see getFacets tag for branch (holdingbranch) if ( defined $branches && ref($branches) eq "HASH" && defined $branches->{$one_facet} @@ -606,6 +643,7 @@ sub getRecords { $facet_label_value = "*"; } } +#warn "onefacet: $link_value $number_of_facets $one_facet ^ $facet_link_value ? $facet_label_value =$expanded_facet:$facets_info->{$link_value}->{'expanded'}"; # if it's a itemtype, label by the name, not the code, if ( $link_value =~ /itype/ ) { @@ -1682,16 +1720,8 @@ sub searchResults { my ($itemtag, undef) = &GetMarcFromKohaField( "items.itemnumber", "" ); ## find column names of items related to MARC - my $sth2 = $dbh->prepare("SHOW COLUMNS FROM items"); - $sth2->execute; - my %subfieldstosearch; - while ( ( my $column ) = $sth2->fetchrow ) { - my ( $tagfield, $tagsubfield ) = - &GetMarcFromKohaField( "items." . $column, "" ); - if ( defined $tagsubfield ) { - $subfieldstosearch{$column} = $tagsubfield; - } - } + my $subfieldstosearch_hashref = getItemSubfieldsToSearch(); + my %subfieldstosearch = %$subfieldstosearch_hashref; # handle which records to actually retrieve my $times; @@ -1844,7 +1874,6 @@ sub searchResults { my $other_count = 0; my $withdrawn_count = 0; my $itemlost_count = 0; - my $hideatopac_count = 0; my $itembinding_count = 0; my $itemdamaged_count = 0; my $item_in_transit_count = 0; @@ -1854,6 +1883,7 @@ sub searchResults { my $maxitems_pref = C4::Context->preference('maxItemsinSearchResults'); my $maxitems = $maxitems_pref ? $maxitems_pref - 1 : 1; my @hiddenitems; # hidden itemnumbers based on OpacHiddenItems syspref + my @itemnumbers; # itemnumbers for this biblio # loop through every item foreach my $field (@fields) { @@ -1864,19 +1894,19 @@ sub searchResults { $item->{$code} = $field->subfield( $subfieldstosearch{$code} ); } $item->{description} = $itemtypes{ $item->{itype} }{description}; + push @itemnumbers, $item->{itemnumber}; - # OPAC hidden items + # OPAC hidden items if ($is_opac) { # hidden because lost if ($hidelostitems && $item->{itemlost}) { - $hideatopac_count++; next; } # hidden based on OpacHiddenItems syspref - my @hi = C4::Items::GetHiddenItemnumbers($item); + my @itemnum = ($item->{itemnumber}); + my @hi = C4::Items::GetHiddenItemnumbersByItemnumbers(@itemnum); if (scalar @hi) { push @hiddenitems, @hi; - $hideatopac_count++; next; } } @@ -2007,7 +2037,8 @@ sub searchResults { } # notforloan, item level and biblioitem level # if all items are hidden, do not show the record - if ($items_count > 0 && $hideatopac_count == $items_count) { + if ( scalar(@itemnumbers) > 0 && scalar(@hiddenitems) == scalar(@itemnumbers) ) { + # TODO: patron category override next; } @@ -2094,6 +2125,25 @@ sub searchResults { return @newresults; } +=head2 getItemSubfieldsToSearch + Search for the item table related subfield tags +=cut +sub getItemSubfieldsToSearch { + my $dbh = C4::Context->dbh; + ## find column names of items related to MARC + my $sth2 = $dbh->prepare("SHOW COLUMNS FROM items"); + $sth2->execute; + my %subfieldstosearch; + while ( ( my $column ) = $sth2->fetchrow ) { + my ( $tagfield, $tagsubfield ) = + &GetMarcFromKohaField( "items." . $column, "" ); + if ( defined $tagsubfield ) { + $subfieldstosearch{$column} = $tagsubfield; + } + } + return \%subfieldstosearch; +} + =head2 SearchAcquisitions Search for acquisitions =cut diff --git a/misc/migration_tools/rebuild_zebra.pl b/misc/migration_tools/rebuild_zebra.pl index 682e4be..930d060 100755 --- a/misc/migration_tools/rebuild_zebra.pl +++ b/misc/migration_tools/rebuild_zebra.pl @@ -638,9 +638,15 @@ sub get_corrected_marc_record { fix_leader($marc); if ($record_type eq 'authority') { fix_authority_id($marc, $record_number); - } elsif ($record_type eq 'biblio' && C4::Context->preference('IncludeSeeFromInSearches')) { - my $normalizer = Koha::RecordProcessor->new( { filters => 'EmbedSeeFromHeadings' } ); - $marc = $normalizer->process($marc); + } elsif ($record_type eq 'biblio') { + if (C4::Context->preference('IncludeSeeFromInSearches')) { + my $normalizer = Koha::RecordProcessor->new( { filters => 'EmbedSeeFromHeadings' } ); + $marc = $normalizer->process($marc); + } + my $enriched_marc = enrich_marc($marc); + if (defined $enriched_marc) { + $marc = $enriched_marc; + } } if (C4::Context->preference("marcflavour") eq "UNIMARC") { fix_unimarc_100($marc); @@ -773,6 +779,83 @@ sub fix_unimarc_100 { } } + +=pod + +Enriches marc record to facilitate adding local tags. + +Used to enable upfront processing when generating indexes (zebra, etc) +as tags can be added on the fly without affecting the original marc records +in the database. Aggregated results based on items are done once during index +build time, which don't need to be done on each and every search. + +Currently supports: +- updating biblio's $biblionumbertagfield (i.e. 999h=1) when all associated + items are "hidden" as specified by the OpacHiddenItems syspref rules. +- updating biblio's $biblionumbertagfield (i.e. 999s=1) when all associated + items are "suppressed" as specified by the items.suppressed mapping. + +$marc = marc record + +=cut +sub enrich_marc { + my $marc = $_[0]; + my $frameworkcode = ''; + my $results_marc = $marc; + + my $biblionumber = $marc->field($biblionumbertagfield)->subfield($biblionumbertagsubfield); + die qq{No biblionumber tag for framework "$frameworkcode"} unless $biblionumber; + if ($biblionumber && + (C4::Context->preference('OpacHiddenItems') || + C4::Context->preference('OpacSuppression') ) ) { + + my $old_biblio_field = $marc->field($biblionumbertagfield); + my $current_biblio_field = $old_biblio_field->clone; + my $new_biblio_field; + my @itemnumbers = @{ GetItemnumbersForBiblio($biblionumber) }; + + if (C4::Context->preference('OpacHiddenItems')) { + my ($bibliohiddentagfield,$bibliohiddentagsubfield) = &GetMarcFromKohaField("biblio.hidden",$frameworkcode); + if ( !defined $bibliohiddentagfield) { $bibliohiddentagfield = '999'; } # default + if ( !defined $bibliohiddentagsubfield) { $bibliohiddentagsubfield = 'h'; } # default + + + my @hiddenitems = GetHiddenItemnumbersByItemnumbers(@itemnumbers); +#warn "itemcount:".@itemnumbers." hiddenitemcount:".@hiddenitems; + if ( @itemnumbers > 0 && (scalar(@itemnumbers) == scalar(@hiddenitems)) ) { + # all items are hidden, so set the marc record biblionumber subfield h to 1 + $current_biblio_field->update( $bibliohiddentagsubfield => 1); # hidden + $new_biblio_field = $current_biblio_field; + } + } +=pod + if (C4::Context->preference('OpacSuppression')) { + my ($bibliosuppresstagfield,$bibliosuppresstagsubfield) = &GetMarcFromKohaField("biblio.suppress",$frameworkcode); + if ( !defined $bibliosuppresstagfield) { $bibliosuppresstagfield = '942'; } # default + if ( !defined $bibliosuppresstagsubfield) { $bibliodsuppresstagsubfield = 'n'; } # default + + my @suppresseditems = GetSuppressedItemnumbers(@itemnumbers); +#warn "itemcount:".@itemnumbers." suppresseditemcount:".@suppresseditems; + if ( @itemnumbers > 0 && (scalar(@itemnumbers) == scalar(@suppresseditems)) ) { + # all items are suppressed, so set the marc record biblionumber subfield s to 1 +#TODO: SuppressMarcTags syspref with list of marc tags 942n=1|... so folks with other/multiple suppress scenarios can work + $current_biblio_field->update( $bibliosuppresstagsubfield => 1); # suppressed + $new_biblio_field = $current_biblio_field; + } + } +=cut + + if (defined $new_biblio_field && defined $old_biblio_field) { + # drop old field and replace with new one... + $marc->delete_field($old_biblio_field); + $marc->insert_fields_ordered($new_biblio_field); + } + } + + return $results_marc; +} + + sub do_indexing { my ($record_type, $op, $record_dir, $reset_index, $noshadow, $record_format, $zebraidx_log_opt) = @_; diff --git a/opac/opac-search.pl b/opac/opac-search.pl index 617d67e..058630e 100755 --- a/opac/opac-search.pl +++ b/opac/opac-search.pl @@ -610,7 +610,7 @@ for (my $i=0;$i<@servers;$i++) { } if ($results_hashref->{$server}->{"hits"}){ - $total = $total + $results_hashref->{$server}->{"hits"}; + $total = $total + @newresults; } # Opac search history -- 1.8.5.3