X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=C4%2FTags.pm;h=7f94da4d0eaa1b2b37285e7690c0cad64b592da0;hb=d00d07cae751986a220d114ff165cbf5206a0283;hp=06c1e1db6de4153d94e4ff80359f1eaae87f55db;hpb=0b0212cae3497f7746eb58a5397453c6e0173ba0;p=koha-ffzg.git diff --git a/C4/Tags.pm b/C4/Tags.pm index 06c1e1db6d..7f94da4d0e 100644 --- a/C4/Tags.pm +++ b/C4/Tags.pm @@ -1,69 +1,72 @@ package C4::Tags; + +# Copyright Liblime 2008 +# Parts Copyright ACPL 2011 +# # 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., 59 Temple Place, -# Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . use strict; use warnings; -use Carp; +use Carp qw( carp ); use Exporter; use C4::Context; -use C4::Debug; - -use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); -use vars qw($ext_dict $select_all @fields); - +use Module::Load::Conditional qw( check_install ); +use Koha::Tags; +use Koha::Tags::Approvals; +use Koha::Tags::Indexes; +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"; + +our (@ISA, @EXPORT_OK); BEGIN { - $VERSION = 0.03; - @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_tags get_tag_rows + add_tags + add_tag + add_tag_approval + add_tag_index + remove_tag + 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 (;$) { +sub get_filters { my $query = "SELECT * FROM tags_filters "; my ($sth); if (@_) { @@ -79,7 +82,7 @@ sub get_filters (;$) { # (SELECT count(*) FROM tags_all ) as tags_all, # (SELECT count(*) FROM tags_index ) as tags_index, -sub approval_counts () { +sub approval_counts { my $query = "SELECT (SELECT count(*) FROM tags_approval WHERE approved= 1) as approved_count, (SELECT count(*) FROM tags_approval WHERE approved=-1) as rejected_count, @@ -89,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; } @@ -111,77 +113,44 @@ sub get_count_by_tag_status { return $sth->fetchrow; } -sub remove_tag ($;$) { - my $tag_id = shift or return undef; - 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) - 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 { - decrement_weight($row->{term},$row->{biblionumber}); - } - if ($index->{weight_total} <= 1) { - delete_tag_approval($row->{term}); - } else { - decrement_weight_total($row->{term}); - } - delete_tag_row_by_id($tag_id); -} - -sub delete_tag_index ($$) { - (@_) or return undef; - 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; - 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; - 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; - my $i=0; - foreach(@_) { - $i += delete_tag_row_by_id($_); - } - ($i == scalar(@_)) or - warn sprintf "delete_tag_rows_by_ids tried %s tag_ids, only succeeded on $i", scalar(@_); - return $i; -} - -sub get_tag_rows ($) { +sub remove_tag { + 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; # 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); + if ($index->{weight} <= 1) { + Koha::Tags::Indexes->search({ term => $row->{term}, biblionumber => $row->{biblionumber} })->delete; + } else { + decrement_weight($row->{term},$row->{biblionumber}); + } + if ($index->{weight_total} <= 1) { + Koha::Tags::Approvals->search({ term => $row->{term} })->delete; + } else { + decrement_weight_total($row->{term}); + } + Koha::Tags->search({ tag_id => $tag_id })->delete; +} + +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; } @@ -197,9 +166,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); @@ -209,7 +176,7 @@ sub get_tag_rows ($) { return $sth->fetchall_arrayref({}); } -sub get_tags (;$) { # i.e., from tags_index +sub get_tags { # i.e., from tags_index my $hash = shift || {}; my @ok_fields = qw(term biblionumber weight limit sort approved); my $wheres; @@ -217,12 +184,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; } @@ -267,8 +233,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); @@ -278,7 +242,7 @@ sub get_tags (;$) { # i.e., from tags_index return $sth->fetchall_arrayref({}); } -sub get_approval_rows (;$) { # i.e., from tags_approval +sub get_approval_rows { # i.e., from tags_approval my $hash = shift || {}; my @ok_fields = qw(term approved date_approved approved_by weight_total limit sort borrowernumber); my $wheres; @@ -286,12 +250,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; } @@ -342,8 +305,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); @@ -353,10 +314,11 @@ sub get_approval_rows (;$) { # i.e., from tags_approval return $sth->fetchall_arrayref({}); } -sub is_approved ($) { - my $term = shift or return undef; +sub is_approved { + 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; @@ -364,8 +326,8 @@ sub is_approved ($) { return $sth->fetchrow; } -sub get_tag_index ($;$) { - my $term = shift or return undef; +sub get_tag_index { + my $term = shift or return; my $sth; if (@_) { $sth = C4::Context->dbh->prepare("SELECT * FROM tags_index WHERE term = ? AND biblionumber = ?"); @@ -379,7 +341,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; @@ -401,7 +364,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) { @@ -414,23 +377,22 @@ 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); return scalar @_; } -sub add_tag_approval ($;$$) { # or disapproval - $debug and warn "add_tag_approval(" . join(", ",map {defined($_) ? $_ : 'UNDEF'} @_) . ")"; - my $term = shift or return undef; +sub add_tag_approval { # or disapproval + my $term = shift or return; my $query = "SELECT * FROM tags_approval WHERE term = ?"; my $sth = C4::Context->dbh->prepare($query); $sth->execute($term); @@ -447,92 +409,55 @@ 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; } -sub mod_tag_approval ($$$) { +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; +sub add_tag_index { + 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 = ?"); - $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 ($$) { +sub increment_weights { increment_weight(@_); increment_weight_total(shift); } -sub decrement_weights ($$) { +sub decrement_weights { decrement_weight(@_); decrement_weight_total(shift); } -sub increment_weight_total ($) { +sub increment_weight_total { _set_weight_total('weight_total+1',shift); } -sub increment_weight ($$) { +sub increment_weight { _set_weight('weight+1',shift,shift); } -sub decrement_weight_total ($) { +sub decrement_weight_total { _set_weight_total('weight_total-1',shift); } -sub decrement_weight ($$) { +sub decrement_weight { _set_weight('weight-1',shift,shift); } -sub _set_weight_total ($$) { +sub _set_weight_total { my $sth = C4::Context->dbh->prepare(" UPDATE tags_approval SET weight_total=" . (shift) . " @@ -540,7 +465,7 @@ sub _set_weight_total ($$) { "); # note: CANNOT use "?" for weight_total (see the args above). $sth->execute(shift); # just the term } -sub _set_weight ($$$) { +sub _set_weight { my $dbh = C4::Context->dbh; my $sth = $dbh->prepare(" UPDATE tags_index @@ -551,22 +476,19 @@ sub _set_weight ($$$) { $sth->execute(@_); } -sub add_tag ($$;$$) { # biblionumber,term,[borrowernumber,approvernumber] - my $biblionumber = shift or return undef; - my $term = shift or return undef; +sub add_tag { # biblionumber,term,[borrowernumber,approvernumber] + 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); @@ -575,27 +497,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 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 @@ -649,7 +601,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