Bug 17600: Standardize our EXPORT_OK
[srvgit] / C4 / Tags.pm
index 4786b2c..e01244f 100644 (file)
@@ -5,68 +5,66 @@ package C4::Tags;
 #
 # This file is part of Koha.
 #
-# Koha 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.
+# Koha 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 3 of the License, or
+# (at your option) any later version.
 #
-# Koha 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.
+# Koha 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.
 #
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use strict;
 use warnings;
-use Carp;
+use Carp qw( carp );
 use Exporter;
 
 use C4::Context;
-use C4::Debug;
+use Module::Load::Conditional qw( check_install );
 #use Data::Dumper;
+use constant TAG_FIELDS => qw(tag_id borrowernumber biblionumber term language date_created);
+use constant TAG_SELECT => "SELECT " . join(',', TAG_FIELDS) . "\n FROM   tags_all\n";
 
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
-use vars qw($ext_dict $select_all @fields);
-
+our (@ISA, @EXPORT_OK);
 BEGIN {
-    $VERSION = 3.07.00.049;
-       @ISA = qw(Exporter);
-       @EXPORT_OK = qw(
-               &get_tag &get_tags &get_tag_rows
-               &add_tags &add_tag
-               &delete_tag_row_by_id
-               &remove_tag
-               &delete_tag_rows_by_ids
-               &rectify_weights
-               &get_approval_rows
-               &blacklist
-               &whitelist
-               &is_approved
-               &approval_counts
-               &get_count_by_tag_status
-               &get_filters
-       );
-       # %EXPORT_TAGS = ();
-       $ext_dict = C4::Context->preference('TagsExternalDictionary');
-       if ($debug) {
-               require Data::Dumper;
-               import Data::Dumper qw(:DEFAULT);
-               print STDERR __PACKAGE__ . " external dictionary = " . ($ext_dict||'none') . "\n";
-       }
+    @ISA       = qw(Exporter);
+    @EXPORT_OK = qw(
+      get_tag get_tags get_tag_rows
+      add_tags
+      add_tag
+      add_tag_approval
+      add_tag_index
+      delete_tag_row_by_id
+      remove_tag
+      delete_tag_rows_by_ids
+      get_approval_rows
+      blacklist
+      whitelist
+      is_approved
+      approval_counts
+      get_count_by_tag_status
+      get_filters
+      stratify_tags
+    );
+    my $ext_dict = C4::Context->preference('TagsExternalDictionary');
+    if ( $ext_dict && ! check_install( module => 'Lingua::Ispell' ) ) {
+        warn "Ignoring TagsExternalDictionary, because Lingua::Ispell is not installed.";
+        $ext_dict = q{};
+    }
        if ($ext_dict) {
                require Lingua::Ispell;
-               import Lingua::Ispell qw(spellcheck add_word_lc save_dictionary);
+        import Lingua::Ispell qw(spellcheck add_word_lc);
+        $Lingua::Ispell::path = $ext_dict;
        }
 }
 
-INIT {
-    $ext_dict and $Lingua::Ispell::path = $ext_dict;
-    $debug and print STDERR "\$Lingua::Ispell::path = $Lingua::Ispell::path\n";
-       @fields = qw(tag_id borrowernumber biblionumber term language date_created);
-       $select_all = "SELECT " . join(',',@fields) . "\n FROM   tags_all\n";
-}
+=head1 C4::Tags.pm - Support for user tagging of biblios.
+
+=cut
 
 sub get_filters {
        my $query = "SELECT * FROM tags_filters ";
@@ -94,7 +92,6 @@ sub approval_counts {
        $sth->execute;
        my $result = $sth->fetchrow_hashref();
        $result->{approved_total} = $result->{approved_count} + $result->{rejected_count} + $result->{unapproved_count};
-       $debug and warn "counts returned: " . Dumper $result;
        return $result;
 }
 
@@ -117,20 +114,17 @@ sub get_count_by_tag_status  {
 }
 
 sub remove_tag {
-       my $tag_id  = shift or return undef;
+       my $tag_id  = shift or return;
        my $user_id = (@_) ? shift : undef;
        my $rows = (defined $user_id) ?
                        get_tag_rows({tag_id=>$tag_id, borrowernumber=>$user_id}) :
                        get_tag_rows({tag_id=>$tag_id}) ;
        $rows or return 0;
-       (scalar(@$rows) == 1) or return undef;  # should never happen (duplicate ids)
+       (scalar(@$rows) == 1) or return;        # should never happen (duplicate ids)
        my $row = shift(@$rows);
        ($tag_id == $row->{tag_id}) or return 0;
        my $tags = get_tags({term=>$row->{term}, biblionumber=>$row->{biblionumber}});
        my $index = shift(@$tags);
-       $debug and print STDERR
-               sprintf "remove_tag: tag_id=>%s, biblionumber=>%s, weight=>%s, weight_total=>%s\n",
-                       $row->{tag_id}, $row->{biblionumber}, $index->{weight}, $index->{weight_total};
        if ($index->{weight} <= 1) {
                delete_tag_index($row->{term},$row->{biblionumber});
        } else {
@@ -145,25 +139,25 @@ sub remove_tag {
 }
 
 sub delete_tag_index {
-       (@_) or return undef;
+       (@_) or return;
        my $sth = C4::Context->dbh->prepare("DELETE FROM tags_index WHERE term = ? AND biblionumber = ? LIMIT 1");
        $sth->execute(@_);
        return $sth->rows || 0;
 }
 sub delete_tag_approval {
-       (@_) or return undef;
+       (@_) or return;
        my $sth = C4::Context->dbh->prepare("DELETE FROM tags_approval WHERE term = ? LIMIT 1");
        $sth->execute(shift);
        return $sth->rows || 0;
 }
 sub delete_tag_row_by_id {
-       (@_) or return undef;
+       (@_) or return;
        my $sth = C4::Context->dbh->prepare("DELETE FROM tags_all WHERE tag_id = ? LIMIT 1");
        $sth->execute(shift);
        return $sth->rows || 0;
 }
 sub delete_tag_rows_by_ids {
-       (@_) or return undef;
+       (@_) or return;
        my $i=0;
        foreach(@_) {
                $i += delete_tag_row_by_id($_);
@@ -175,18 +169,17 @@ sub delete_tag_rows_by_ids {
 
 sub get_tag_rows {
        my $hash = shift || {};
-       my @ok_fields = @fields;
+    my @ok_fields = TAG_FIELDS;
        push @ok_fields, 'limit';       # push the limit! :)
        my $wheres;
        my $limit  = "";
        my @exe_args = ();
        foreach my $key (keys %$hash) {
-               $debug and print STDERR "get_tag_rows arg. '$key' = ", $hash->{$key}, "\n";
                unless (length $key) {
                        carp "Empty argument key to get_tag_rows: ignoring!";
                        next;
                }
-               unless (1 == scalar grep {/^ $key $/x} @ok_fields) {
+               unless (1 == scalar grep { $_ eq $key } @ok_fields) {
                        carp "get_tag_rows received unreconized argument key '$key'.";
                        next;
                }
@@ -202,9 +195,7 @@ sub get_tag_rows {
                        push @exe_args, $hash->{$key};
                }
        }
-       my $query = $select_all . ($wheres||'') . $limit;
-       $debug and print STDERR "get_tag_rows query:\n $query\n",
-                                                       "get_tag_rows query args: ", join(',', @exe_args), "\n";
+    my $query = TAG_SELECT . ($wheres||'') . $limit;
        my $sth = C4::Context->dbh->prepare($query);
        if (@exe_args) {
                $sth->execute(@exe_args);
@@ -222,12 +213,11 @@ sub get_tags {            # i.e., from tags_index
        my $order  = "";
        my @exe_args = ();
        foreach my $key (keys %$hash) {
-               $debug and print STDERR "get_tags arg. '$key' = ", $hash->{$key}, "\n";
                unless (length $key) {
                        carp "Empty argument key to get_tags: ignoring!";
                        next;
                }
-               unless (1 == scalar grep {/^ $key $/x} @ok_fields) {
+               unless (1 == scalar grep { $_ eq $key } @ok_fields) {
                        carp "get_tags received unreconized argument key '$key'.";
                        next;
                }
@@ -272,8 +262,6 @@ sub get_tags {              # i.e., from tags_index
        LEFT JOIN tags_approval 
        ON        tags_index.term = tags_approval.term
        " . ($wheres||'') . $order . $limit;
-       $debug and print STDERR "get_tags query:\n $query\n",
-                                                       "get_tags query args: ", join(',', @exe_args), "\n";
        my $sth = C4::Context->dbh->prepare($query);
        if (@exe_args) {
                $sth->execute(@exe_args);
@@ -291,12 +279,11 @@ sub get_approval_rows {           # i.e., from tags_approval
        my $order  = "";
        my @exe_args = ();
        foreach my $key (keys %$hash) {
-               $debug and print STDERR "get_approval_rows arg. '$key' = ", $hash->{$key}, "\n";
                unless (length $key) {
                        carp "Empty argument key to get_approval_rows: ignoring!";
                        next;
                }
-               unless (1 == scalar grep {/^ $key $/x} @ok_fields) {
+               unless (1 == scalar grep { $_ eq $key } @ok_fields) {
                        carp "get_approval_rows received unreconized argument key '$key'.";
                        next;
                }
@@ -347,8 +334,6 @@ sub get_approval_rows {             # i.e., from tags_approval
        LEFT JOIN borrowers
        ON      tags_approval.approved_by = borrowers.borrowernumber ";
        $query .= ($wheres||'') . $order . $limit;
-       $debug and print STDERR "get_approval_rows query:\n $query\n",
-                                                       "get_approval_rows query args: ", join(',', @exe_args), "\n";
        my $sth = C4::Context->dbh->prepare($query);
        if (@exe_args) {
                $sth->execute(@exe_args);
@@ -359,9 +344,10 @@ sub get_approval_rows {            # i.e., from tags_approval
 }
 
 sub is_approved {
-       my $term = shift or return undef;
+       my $term = shift or return;
        my $sth = C4::Context->dbh->prepare("SELECT approved FROM tags_approval WHERE term = ?");
        $sth->execute($term);
+    my $ext_dict = C4::Context->preference('TagsExternalDictionary');
        unless ($sth->rows) {
                $ext_dict and return (spellcheck($term) ? 0 : 1);       # spellcheck returns empty on OK word
                return 0;
@@ -370,7 +356,7 @@ sub is_approved {
 }
 
 sub get_tag_index {
-       my $term = shift or return undef;
+       my $term = shift or return;
        my $sth;
        if (@_) {
                $sth = C4::Context->dbh->prepare("SELECT * FROM tags_index WHERE term = ? AND biblionumber = ?");
@@ -384,7 +370,8 @@ sub get_tag_index {
 
 sub whitelist {
        my $operator = shift;
-       defined $operator or return undef; # have to test defined to allow =0 (kohaadmin)
+       defined $operator or return; # have to test defined to allow =0 (kohaadmin)
+    my $ext_dict = C4::Context->preference('TagsExternalDictionary');
        if ($ext_dict) {
                foreach (@_) {
                        spellcheck($_) or next;
@@ -406,7 +393,7 @@ sub whitelist {
 # a term mistakenly, you can still reverse it. But there is no going back to "neutral".
 sub blacklist {
        my $operator = shift;
-       defined $operator or return undef; # have to test defined to allow =0 (kohaadmin)
+       defined $operator or return; # have to test defined to allow =0 (kohaadmin)
        foreach (@_) {
                my $aref = get_approval_rows({term=>$_});
                if ($aref and scalar @$aref) {
@@ -419,14 +406,14 @@ sub blacklist {
 }
 sub add_filter {
        my $operator = shift;
-       defined $operator or return undef; # have to test defined to allow =0 (kohaadmin)
+       defined $operator or return; # have to test defined to allow =0 (kohaadmin)
        my $query = "INSERT INTO tags_blacklist (regexp,y,z) VALUES (?,?,?)";
        # my $sth = C4::Context->dbh->prepare($query);
        return scalar @_;
 }
 sub remove_filter {
        my $operator = shift;
-       defined $operator or return undef; # have to test defined to allow =0 (kohaadmin)
+       defined $operator or return; # have to test defined to allow =0 (kohaadmin)
        my $query = "REMOVE FROM tags_blacklist WHERE blacklist_id = ?";
        # my $sth = C4::Context->dbh->prepare($query);
        # $sth->execute($term);
@@ -434,8 +421,7 @@ sub remove_filter {
 }
 
 sub add_tag_approval { # or disapproval
-       $debug and warn "add_tag_approval(" . join(", ",map {defined($_) ? $_ : 'UNDEF'} @_) . ")";
-       my $term = shift or return undef;
+       my $term = shift or return;
        my $query = "SELECT * FROM tags_approval WHERE term = ?";
        my $sth = C4::Context->dbh->prepare($query);
        $sth->execute($term);
@@ -452,7 +438,6 @@ sub add_tag_approval {      # or disapproval
        } else {
                $query = "INSERT INTO tags_approval (term,date_approved) VALUES (?,NOW())";
        }
-       $debug and print STDERR "add_tag_approval query: $query\nadd_tag_approval args: (" . join(", ", @exe_args) . ")\n";
        $sth = C4::Context->dbh->prepare($query);
        $sth->execute(@exe_args);
        return $sth->rows;
@@ -460,63 +445,34 @@ sub add_tag_approval {    # or disapproval
 
 sub mod_tag_approval {
        my $operator = shift;
-       defined $operator or return undef; # have to test defined to allow =0 (kohaadmin)
-       my $term     = shift or return undef;
+       defined $operator or return; # have to test defined to allow =0 (kohaadmin)
+       my $term     = shift or return;
        my $approval = (scalar @_ ? shift : 1); # default is to approve
        my $query = "UPDATE tags_approval SET approved_by=?, approved=?, date_approved=NOW() WHERE term = ?";
-       $debug and print STDERR "mod_tag_approval query: $query\nmod_tag_approval args: ($operator,$approval,$term)\n";
        my $sth = C4::Context->dbh->prepare($query);
        $sth->execute($operator,$approval,$term);
 }
 
 sub add_tag_index {
-       my $term         = shift or return undef;
-       my $biblionumber = shift or return undef;
+       my $term         = shift or return;
+       my $biblionumber = shift or return;
        my $query = "SELECT * FROM tags_index WHERE term = ? AND biblionumber = ?";
        my $sth = C4::Context->dbh->prepare($query);
        $sth->execute($term,$biblionumber);
        ($sth->rows) and return increment_weight($term,$biblionumber);
        $query = "INSERT INTO tags_index (term,biblionumber) VALUES (?,?)";
-       $debug and print STDERR "add_tag_index query: $query\nadd_tag_index args: ($term,$biblionumber)\n";
        $sth = C4::Context->dbh->prepare($query);
        $sth->execute($term,$biblionumber);
        return $sth->rows;
 }
 
 sub get_tag {          # by tag_id
-       (@_) or return undef;
-       my $sth = C4::Context->dbh->prepare("$select_all WHERE tag_id = ?");
+       (@_) or return;
+    my $sth = C4::Context->dbh->prepare(TAG_SELECT . "WHERE tag_id = ?");
        $sth->execute(shift);
        return $sth->fetchrow_hashref;
 }
 
-sub rectify_weights {
-       my $dbh = C4::Context->dbh;
-       my $sth;
-       my $query = "
-       SELECT term,biblionumber,count(*) as count
-       FROM   tags_all
-       ";
-       (@_) and $query .= " WHERE term =? ";
-       $query .= " GROUP BY term,biblionumber ";
-       $sth = $dbh->prepare($query);
-       if (@_) {
-               $sth->execute(shift);
-       } else {
-               $sth->execute();
-       }
-       my $results = $sth->fetchall_arrayref({}) or return undef;
-       my %tally = ();
-       foreach (@$results) {
-               _set_weight($_->{count},$_->{term},$_->{biblionumber});
-               $tally{$_->{term}} += $_->{count};
-       }
-       foreach (keys %tally) {
-               _set_weight_total($tally{$_},$_);
-       }
-       return ($results,\%tally);
-}
-
 sub increment_weights {
        increment_weight(@_);
        increment_weight_total(shift);
@@ -557,21 +513,18 @@ sub _set_weight {
 }
 
 sub add_tag {  # biblionumber,term,[borrowernumber,approvernumber]
-       my $biblionumber = shift or return undef;
-       my $term         = shift or return undef;
+       my $biblionumber = shift or return;
+       my $term         = shift or return;
        my $borrowernumber = (@_) ? shift : 0;          # the user, default to kohaadmin
        $term =~ s/^\s+//;
        $term =~ s/\s+$//;
-       ($term) or return undef;        # must be more than whitespace
+       ($term) or return;      # must be more than whitespace
        my $rows = get_tag_rows({biblionumber=>$biblionumber, borrowernumber=>$borrowernumber, term=>$term, limit=>1});
        my $query = "INSERT INTO tags_all
        (borrowernumber,biblionumber,term,date_created)
        VALUES (?,?,?,NOW())";
-       $debug and print STDERR "add_tag query: $query\n",
-                                                       "add_tag query args: ($borrowernumber,$biblionumber,$term)\n";
        if (scalar @$rows) {
-               $debug and carp "Duplicate tag detected.  Tag not added.";      
-               return undef;
+               return;
        }
        # add to tags_all regardless of approaval
        my $sth = C4::Context->dbh->prepare($query);
@@ -580,27 +533,57 @@ sub add_tag {     # biblionumber,term,[borrowernumber,approvernumber]
        # then 
        if (scalar @_) {        # if arg remains, it is the borrowernumber of the approver: tag is pre-approved.
                my $approver = shift;
-               $debug and print STDERR "term '$term' pre-approved by borrower #$approver\n";
                add_tag_approval($term,$approver,1);
                add_tag_index($term,$biblionumber,$approver);
        } elsif (is_approved($term) >= 1) {
-               $debug and print STDERR "term '$term' approved by whitelist\n";
                add_tag_approval($term,0,1);
                add_tag_index($term,$biblionumber,1);
        } else {
-               $debug and print STDERR "term '$term' NOT approved (yet)\n";
                add_tag_approval($term);
                add_tag_index($term,$biblionumber);
        }
 }
 
+# This takes a set of tags, as returned by C<get_approval_rows> and divides
+# them up into a number of "strata" based on their weight. This is useful
+# to display them in a number of different sizes.
+#
+# Usage:
+#   ($min, $max) = stratify_tags($strata, $tags);
+# $stratum: the number of divisions you want
+# $tags: the tags, as provided by get_approval_rows
+# $min: the minimum stratum value
+# $max: the maximum stratum value. This may be the same as $min if there
+# is only one weight. Beware of divide by zeros.
+# This will add a field to the tag called "stratum" containing the calculated
+# value.
+sub stratify_tags {
+    my ( $strata, $tags ) = @_;
+    return (0,0) if !@$tags;
+    my ( $min, $max );
+    foreach (@$tags) {
+        my $w = $_->{weight_total};
+        $min = $w if ( !defined($min) || $min > $w );
+        $max = $w if ( !defined($max) || $max < $w );
+    }
+
+    # normalise min to zero
+    $max = $max - $min;
+    my $orig_min = $min;
+    $min = 0;
+
+    # if min and max are the same, just make it 1
+    my $span = ( $strata - 1 ) / ( $max || 1 );
+    foreach (@$tags) {
+        my $w = $_->{weight_total};
+        $_->{stratum} = int( ( $w - $orig_min ) * $span );
+    }
+    return ( $min, $max );
+}
+
 1;
 __END__
 
-=head1 C4::Tags.pm - Support for user tagging of biblios.
-
-More verose debugging messages are sent in the presence of non-zero $ENV{"DEBUG"}.
-
 =head2 add_tag(biblionumber,term[,borrowernumber])
 
 =head3 TO DO: Add real perldoc
@@ -654,7 +637,7 @@ This could be called an "approved terms" table.  See above regarding the Externa
        approved       - Negative, 0 or positive if tag is rejected, pending or approved.
        date_approved  - date of last action
        approved_by    - staffer performing the last action
-       weight_total   - total occurance of term in any biblio by any users
+    weight_total   - total occurrence of term in any biblio by any users
 
 tags_index - This table is for performance, because by far the most common operation will 
 be fetching tags for a list of search results.  We will have a set of biblios, and we will