| Filename | /usr/share/koha/lib/C4/Tags.pm |
| Statements | Executed 1787 statements in 50.0ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 25 | 1 | 1 | 6.73ms | 68.7ms | C4::Tags::get_tags |
| 600 | 1 | 1 | 1.35ms | 1.35ms | C4::Tags::CORE:regcomp (opcode) |
| 700 | 3 | 1 | 853µs | 853µs | C4::Tags::CORE:match (opcode) |
| 100 | 1 | 1 | 172µs | 172µs | C4::Tags::CORE:subst (opcode) |
| 1 | 1 | 1 | 35µs | 1.96ms | C4::Tags::BEGIN@35 |
| 1 | 1 | 1 | 24µs | 31µs | C4::Tags::BEGIN@21 |
| 1 | 1 | 1 | 22µs | 88µs | C4::Tags::BEGIN@30 |
| 1 | 1 | 1 | 21µs | 143µs | C4::Tags::BEGIN@27 |
| 1 | 1 | 1 | 18µs | 44µs | C4::Tags::BEGIN@22 |
| 1 | 1 | 1 | 17µs | 73µs | C4::Tags::BEGIN@23 |
| 1 | 1 | 1 | 17µs | 17µs | C4::Tags::INIT |
| 1 | 1 | 1 | 16µs | 21µs | C4::Tags::BEGIN@26 |
| 1 | 1 | 1 | 14µs | 66µs | C4::Tags::BEGIN@29 |
| 1 | 1 | 1 | 14µs | 34µs | C4::Tags::BEGIN@24 |
| 1 | 1 | 1 | 13µs | 96µs | C4::Tags::BEGIN@32 |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::_set_weight |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::_set_weight_total |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::add_filter |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::add_tag |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::add_tag_approval |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::add_tag_index |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::approval_counts |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::blacklist |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::decrement_weight |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::decrement_weight_total |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::decrement_weights |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::delete_tag_approval |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::delete_tag_index |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::delete_tag_row_by_id |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::delete_tag_rows_by_ids |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::get_approval_rows |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::get_count_by_tag_status |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::get_filters |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::get_tag |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::get_tag_index |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::get_tag_rows |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::increment_weight |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::increment_weight_total |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::increment_weights |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::is_approved |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::mod_tag_approval |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::remove_filter |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::remove_tag |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::stratify_tags |
| 0 | 0 | 0 | 0s | 0s | C4::Tags::whitelist |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package C4::Tags; | ||||
| 2 | |||||
| 3 | # Copyright Liblime 2008 | ||||
| 4 | # Parts Copyright ACPL 2011 | ||||
| 5 | # | ||||
| 6 | # This file is part of Koha. | ||||
| 7 | # | ||||
| 8 | # Koha is free software; you can redistribute it and/or modify it under the | ||||
| 9 | # terms of the GNU General Public License as published by the Free Software | ||||
| 10 | # Foundation; either version 2 of the License, or (at your option) any later | ||||
| 11 | # version. | ||||
| 12 | # | ||||
| 13 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | ||||
| 14 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||||
| 15 | # A PARTICULAR PURPOSE. See the GNU General Public License for more details. | ||||
| 16 | # | ||||
| 17 | # You should have received a copy of the GNU General Public License along | ||||
| 18 | # with Koha; if not, write to the Free Software Foundation, Inc., | ||||
| 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| 20 | |||||
| 21 | 3 | 34µs | 2 | 38µs | # spent 31µs (24+7) within C4::Tags::BEGIN@21 which was called:
# once (24µs+7µs) by main::BEGIN@51 at line 21 # spent 31µs making 1 call to C4::Tags::BEGIN@21
# spent 7µs making 1 call to strict::import |
| 22 | 3 | 88µs | 2 | 69µs | # spent 44µs (18+26) within C4::Tags::BEGIN@22 which was called:
# once (18µs+26µs) by main::BEGIN@51 at line 22 # spent 44µs making 1 call to C4::Tags::BEGIN@22
# spent 26µs making 1 call to warnings::import |
| 23 | 3 | 35µs | 2 | 129µs | # spent 73µs (17+56) within C4::Tags::BEGIN@23 which was called:
# once (17µs+56µs) by main::BEGIN@51 at line 23 # spent 73µs making 1 call to C4::Tags::BEGIN@23
# spent 56µs making 1 call to Exporter::import |
| 24 | 3 | 29µs | 2 | 54µs | # spent 34µs (14+20) within C4::Tags::BEGIN@24 which was called:
# once (14µs+20µs) by main::BEGIN@51 at line 24 # spent 34µs making 1 call to C4::Tags::BEGIN@24
# spent 20µs making 1 call to Exporter::import |
| 25 | |||||
| 26 | 3 | 90µs | 2 | 25µs | # spent 21µs (16+4) within C4::Tags::BEGIN@26 which was called:
# once (16µs+4µs) by main::BEGIN@51 at line 26 # spent 21µs making 1 call to C4::Tags::BEGIN@26
# spent 4µs making 1 call to C4::Context::import |
| 27 | 3 | 62µs | 2 | 265µs | # spent 143µs (21+122) within C4::Tags::BEGIN@27 which was called:
# once (21µs+122µs) by main::BEGIN@51 at line 27 # spent 143µs making 1 call to C4::Tags::BEGIN@27
# spent 122µs making 1 call to Exporter::import |
| 28 | #use Data::Dumper; | ||||
| 29 | 3 | 54µs | 2 | 118µs | # spent 66µs (14+52) within C4::Tags::BEGIN@29 which was called:
# once (14µs+52µs) by main::BEGIN@51 at line 29 # spent 66µs making 1 call to C4::Tags::BEGIN@29
# spent 52µs making 1 call to constant::import |
| 30 | 3 | 51µs | 3 | 154µs | # spent 88µs (22+66) within C4::Tags::BEGIN@30 which was called:
# once (22µs+66µs) by main::BEGIN@51 at line 30 # spent 88µs making 1 call to C4::Tags::BEGIN@30
# spent 63µs making 1 call to constant::import
# spent 3µs making 1 call to constant::__ANON__[constant.pm:119] |
| 31 | |||||
| 32 | 3 | 169µs | 2 | 179µs | # spent 96µs (13+83) within C4::Tags::BEGIN@32 which was called:
# once (13µs+83µs) by main::BEGIN@51 at line 32 # spent 96µs making 1 call to C4::Tags::BEGIN@32
# spent 83µs making 1 call to vars::import |
| 33 | 1 | 300ns | our $ext_dict; | ||
| 34 | |||||
| 35 | # spent 1.96ms (35µs+1.92) within C4::Tags::BEGIN@35 which was called:
# once (35µs+1.92ms) by main::BEGIN@51 at line 64 | ||||
| 36 | 1 | 1µs | $VERSION = 3.07.00.049; | ||
| 37 | 1 | 8µs | @ISA = qw(Exporter); | ||
| 38 | 1 | 3µs | @EXPORT_OK = qw( | ||
| 39 | &get_tag &get_tags &get_tag_rows | ||||
| 40 | &add_tags &add_tag | ||||
| 41 | &delete_tag_row_by_id | ||||
| 42 | &remove_tag | ||||
| 43 | &delete_tag_rows_by_ids | ||||
| 44 | &get_approval_rows | ||||
| 45 | &blacklist | ||||
| 46 | &whitelist | ||||
| 47 | &is_approved | ||||
| 48 | &approval_counts | ||||
| 49 | &get_count_by_tag_status | ||||
| 50 | &get_filters | ||||
| 51 | stratify_tags | ||||
| 52 | ); | ||||
| 53 | # %EXPORT_TAGS = (); | ||||
| 54 | 1 | 5µs | 1 | 1.92ms | $ext_dict = C4::Context->preference('TagsExternalDictionary'); # spent 1.92ms making 1 call to C4::Context::preference |
| 55 | 1 | 1µs | if ($debug) { | ||
| 56 | require Data::Dumper; | ||||
| 57 | import Data::Dumper qw(:DEFAULT); | ||||
| 58 | print STDERR __PACKAGE__ . " external dictionary = " . ($ext_dict||'none') . "\n"; | ||||
| 59 | } | ||||
| 60 | 1 | 8µs | if ($ext_dict) { | ||
| 61 | require Lingua::Ispell; | ||||
| 62 | import Lingua::Ispell qw(spellcheck add_word_lc save_dictionary); | ||||
| 63 | } | ||||
| 64 | 1 | 6.14ms | 1 | 1.96ms | } # spent 1.96ms making 1 call to C4::Tags::BEGIN@35 |
| 65 | |||||
| 66 | =head1 C4::Tags.pm - Support for user tagging of biblios. | ||||
| 67 | |||||
| - - | |||||
| 72 | # spent 17µs within C4::Tags::INIT which was called:
# once (17µs+0s) by main::RUNTIME at line 0 of /usr/share/koha/opac/cgi-bin/opac/opac-search.pl | ||||
| 73 | 1 | 2µs | $ext_dict and $Lingua::Ispell::path = $ext_dict; | ||
| 74 | 1 | 23µs | $debug and print STDERR "\$Lingua::Ispell::path = $Lingua::Ispell::path\n"; | ||
| 75 | } | ||||
| 76 | |||||
| 77 | sub get_filters { | ||||
| 78 | my $query = "SELECT * FROM tags_filters "; | ||||
| 79 | my ($sth); | ||||
| 80 | if (@_) { | ||||
| 81 | $sth = C4::Context->dbh->prepare($query . " WHERE filter_id = ? "); | ||||
| 82 | $sth->execute(shift); | ||||
| 83 | } else { | ||||
| 84 | $sth = C4::Context->dbh->prepare($query); | ||||
| 85 | $sth->execute; | ||||
| 86 | } | ||||
| 87 | return $sth->fetchall_arrayref({}); | ||||
| 88 | } | ||||
| 89 | |||||
| 90 | # (SELECT count(*) FROM tags_all ) as tags_all, | ||||
| 91 | # (SELECT count(*) FROM tags_index ) as tags_index, | ||||
| 92 | |||||
| 93 | sub approval_counts { | ||||
| 94 | my $query = "SELECT | ||||
| 95 | (SELECT count(*) FROM tags_approval WHERE approved= 1) as approved_count, | ||||
| 96 | (SELECT count(*) FROM tags_approval WHERE approved=-1) as rejected_count, | ||||
| 97 | (SELECT count(*) FROM tags_approval WHERE approved= 0) as unapproved_count | ||||
| 98 | "; | ||||
| 99 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 100 | $sth->execute; | ||||
| 101 | my $result = $sth->fetchrow_hashref(); | ||||
| 102 | $result->{approved_total} = $result->{approved_count} + $result->{rejected_count} + $result->{unapproved_count}; | ||||
| 103 | $debug and warn "counts returned: " . Dumper $result; | ||||
| 104 | return $result; | ||||
| 105 | } | ||||
| 106 | |||||
| 107 | =head2 get_count_by_tag_status | ||||
| 108 | |||||
| - - | |||||
| 115 | sub get_count_by_tag_status { | ||||
| 116 | my ($status) = @_; | ||||
| 117 | my $dbh = C4::Context->dbh; | ||||
| 118 | my $query = | ||||
| 119 | "SELECT count(*) FROM tags_approval WHERE approved=?"; | ||||
| 120 | my $sth = $dbh->prepare($query); | ||||
| 121 | $sth->execute( $status ); | ||||
| 122 | return $sth->fetchrow; | ||||
| 123 | } | ||||
| 124 | |||||
| 125 | sub remove_tag { | ||||
| 126 | my $tag_id = shift or return; | ||||
| 127 | my $user_id = (@_) ? shift : undef; | ||||
| 128 | my $rows = (defined $user_id) ? | ||||
| 129 | get_tag_rows({tag_id=>$tag_id, borrowernumber=>$user_id}) : | ||||
| 130 | get_tag_rows({tag_id=>$tag_id}) ; | ||||
| 131 | $rows or return 0; | ||||
| 132 | (scalar(@$rows) == 1) or return; # should never happen (duplicate ids) | ||||
| 133 | my $row = shift(@$rows); | ||||
| 134 | ($tag_id == $row->{tag_id}) or return 0; | ||||
| 135 | my $tags = get_tags({term=>$row->{term}, biblionumber=>$row->{biblionumber}}); | ||||
| 136 | my $index = shift(@$tags); | ||||
| 137 | $debug and print STDERR | ||||
| 138 | sprintf "remove_tag: tag_id=>%s, biblionumber=>%s, weight=>%s, weight_total=>%s\n", | ||||
| 139 | $row->{tag_id}, $row->{biblionumber}, $index->{weight}, $index->{weight_total}; | ||||
| 140 | if ($index->{weight} <= 1) { | ||||
| 141 | delete_tag_index($row->{term},$row->{biblionumber}); | ||||
| 142 | } else { | ||||
| 143 | decrement_weight($row->{term},$row->{biblionumber}); | ||||
| 144 | } | ||||
| 145 | if ($index->{weight_total} <= 1) { | ||||
| 146 | delete_tag_approval($row->{term}); | ||||
| 147 | } else { | ||||
| 148 | decrement_weight_total($row->{term}); | ||||
| 149 | } | ||||
| 150 | delete_tag_row_by_id($tag_id); | ||||
| 151 | } | ||||
| 152 | |||||
| 153 | sub delete_tag_index { | ||||
| 154 | (@_) or return; | ||||
| 155 | my $sth = C4::Context->dbh->prepare("DELETE FROM tags_index WHERE term = ? AND biblionumber = ? LIMIT 1"); | ||||
| 156 | $sth->execute(@_); | ||||
| 157 | return $sth->rows || 0; | ||||
| 158 | } | ||||
| 159 | sub delete_tag_approval { | ||||
| 160 | (@_) or return; | ||||
| 161 | my $sth = C4::Context->dbh->prepare("DELETE FROM tags_approval WHERE term = ? LIMIT 1"); | ||||
| 162 | $sth->execute(shift); | ||||
| 163 | return $sth->rows || 0; | ||||
| 164 | } | ||||
| 165 | sub delete_tag_row_by_id { | ||||
| 166 | (@_) or return; | ||||
| 167 | my $sth = C4::Context->dbh->prepare("DELETE FROM tags_all WHERE tag_id = ? LIMIT 1"); | ||||
| 168 | $sth->execute(shift); | ||||
| 169 | return $sth->rows || 0; | ||||
| 170 | } | ||||
| 171 | sub delete_tag_rows_by_ids { | ||||
| 172 | (@_) or return; | ||||
| 173 | my $i=0; | ||||
| 174 | foreach(@_) { | ||||
| 175 | $i += delete_tag_row_by_id($_); | ||||
| 176 | } | ||||
| 177 | ($i == scalar(@_)) or | ||||
| 178 | warn sprintf "delete_tag_rows_by_ids tried %s tag_ids, only succeeded on $i", scalar(@_); | ||||
| 179 | return $i; | ||||
| 180 | } | ||||
| 181 | |||||
| 182 | sub get_tag_rows { | ||||
| 183 | my $hash = shift || {}; | ||||
| 184 | my @ok_fields = TAG_FIELDS; | ||||
| 185 | push @ok_fields, 'limit'; # push the limit! :) | ||||
| 186 | my $wheres; | ||||
| 187 | my $limit = ""; | ||||
| 188 | my @exe_args = (); | ||||
| 189 | foreach my $key (keys %$hash) { | ||||
| 190 | $debug and print STDERR "get_tag_rows arg. '$key' = ", $hash->{$key}, "\n"; | ||||
| 191 | unless (length $key) { | ||||
| 192 | carp "Empty argument key to get_tag_rows: ignoring!"; | ||||
| 193 | next; | ||||
| 194 | } | ||||
| 195 | unless (1 == scalar grep {/^ $key $/x} @ok_fields) { | ||||
| 196 | carp "get_tag_rows received unreconized argument key '$key'."; | ||||
| 197 | next; | ||||
| 198 | } | ||||
| 199 | if ($key eq 'limit') { | ||||
| 200 | my $val = $hash->{$key}; | ||||
| 201 | unless ($val =~ /^(\d+,)?\d+$/) { | ||||
| 202 | carp "Non-nuerical limit value '$val' ignored!"; | ||||
| 203 | next; | ||||
| 204 | } | ||||
| 205 | $limit = " LIMIT $val\n"; | ||||
| 206 | } else { | ||||
| 207 | $wheres .= ($wheres) ? " AND $key = ?\n" : " WHERE $key = ?\n"; | ||||
| 208 | push @exe_args, $hash->{$key}; | ||||
| 209 | } | ||||
| 210 | } | ||||
| 211 | my $query = TAG_SELECT . ($wheres||'') . $limit; | ||||
| 212 | $debug and print STDERR "get_tag_rows query:\n $query\n", | ||||
| 213 | "get_tag_rows query args: ", join(',', @exe_args), "\n"; | ||||
| 214 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 215 | if (@exe_args) { | ||||
| 216 | $sth->execute(@exe_args); | ||||
| 217 | } else { | ||||
| 218 | $sth->execute; | ||||
| 219 | } | ||||
| 220 | return $sth->fetchall_arrayref({}); | ||||
| 221 | } | ||||
| 222 | |||||
| 223 | # spent 68.7ms (6.73+62.0) within C4::Tags::get_tags which was called 25 times, avg 2.75ms/call:
# 25 times (6.73ms+62.0ms) by main::RUNTIME at line 581 of /usr/share/koha/opac/cgi-bin/opac/opac-search.pl, avg 2.75ms/call | ||||
| 224 | 25 | 42µs | my $hash = shift || {}; | ||
| 225 | 25 | 101µs | my @ok_fields = qw(term biblionumber weight limit sort approved); | ||
| 226 | 25 | 12µs | my $wheres; | ||
| 227 | 25 | 20µs | my $limit = ""; | ||
| 228 | 25 | 13µs | my $order = ""; | ||
| 229 | 25 | 14µs | my @exe_args = (); | ||
| 230 | 25 | 144µs | foreach my $key (keys %$hash) { | ||
| 231 | 100 | 54µs | $debug and print STDERR "get_tags arg. '$key' = ", $hash->{$key}, "\n"; | ||
| 232 | 100 | 55µs | unless (length $key) { | ||
| 233 | carp "Empty argument key to get_tags: ignoring!"; | ||||
| 234 | next; | ||||
| 235 | } | ||||
| 236 | 700 | 4.76ms | 1200 | 1.92ms | unless (1 == scalar grep {/^ $key $/x} @ok_fields) { # spent 1.35ms making 600 calls to C4::Tags::CORE:regcomp, avg 2µs/call
# spent 566µs making 600 calls to C4::Tags::CORE:match, avg 943ns/call |
| 237 | carp "get_tags received unreconized argument key '$key'."; | ||||
| 238 | next; | ||||
| 239 | } | ||||
| 240 | 100 | 204µs | if ($key eq 'limit') { | ||
| 241 | 25 | 29µs | my $val = $hash->{$key}; | ||
| 242 | 25 | 210µs | 25 | 95µs | unless ($val =~ /^(\d+,)?\d+$/) { # spent 95µs making 25 calls to C4::Tags::CORE:match, avg 4µs/call |
| 243 | carp "Non-nuerical limit value '$val' ignored!"; | ||||
| 244 | next; | ||||
| 245 | } | ||||
| 246 | 25 | 40µs | $limit = " LIMIT $val\n"; | ||
| 247 | } elsif ($key eq 'sort') { | ||||
| 248 | 25 | 120µs | foreach my $by (split /\,/, $hash->{$key}) { | ||
| 249 | 25 | 372µs | 75 | 192µs | unless ( # spent 192µs making 75 calls to C4::Tags::CORE:match, avg 3µs/call |
| 250 | $by =~ /^([-+])?(term)/ or | ||||
| 251 | $by =~ /^([-+])?(biblionumber)/ or | ||||
| 252 | $by =~ /^([-+])?(weight)/ | ||||
| 253 | ) { | ||||
| 254 | carp "get_tags received illegal sort order '$by'"; | ||||
| 255 | next; | ||||
| 256 | } | ||||
| 257 | 25 | 25µs | if ($order) { | ||
| 258 | $order .= ", "; | ||||
| 259 | } else { | ||||
| 260 | 25 | 28µs | $order = " ORDER BY "; | ||
| 261 | } | ||||
| 262 | 25 | 237µs | $order .= $2 . " " . ((!$1) ? '' : $1 eq '-' ? 'DESC' : $1 eq '+' ? 'ASC' : '') . "\n"; | ||
| 263 | } | ||||
| 264 | |||||
| 265 | } else { | ||||
| 266 | 50 | 61µs | my $whereval = $hash->{$key}; | ||
| 267 | 50 | 77µs | my $longkey = ($key eq 'term' ) ? 'tags_index.term' : | ||
| 268 | ($key eq 'approved') ? 'tags_approval.approved' : $key; | ||||
| 269 | 50 | 431µs | 100 | 172µs | my $op = ($whereval =~ s/^(>=|<=)// or # spent 172µs making 100 calls to C4::Tags::CORE:subst, avg 2µs/call |
| 270 | $whereval =~ s/^(>|=|<)// ) ? $1 : '='; | ||||
| 271 | 50 | 176µs | $wheres .= ($wheres) ? " AND $longkey $op ?\n" : " WHERE $longkey $op ?\n"; | ||
| 272 | 50 | 124µs | push @exe_args, $whereval; | ||
| 273 | } | ||||
| 274 | } | ||||
| 275 | 25 | 132µs | my $query = " | ||
| 276 | SELECT tags_index.term as term,biblionumber,weight,weight_total | ||||
| 277 | FROM tags_index | ||||
| 278 | LEFT JOIN tags_approval | ||||
| 279 | ON tags_index.term = tags_approval.term | ||||
| 280 | " . ($wheres||'') . $order . $limit; | ||||
| 281 | 25 | 12µs | $debug and print STDERR "get_tags query:\n $query\n", | ||
| 282 | "get_tags query args: ", join(',', @exe_args), "\n"; | ||||
| 283 | 25 | 620µs | 75 | 26.0ms | my $sth = C4::Context->dbh->prepare($query); # spent 21.6ms making 25 calls to C4::Context::dbh, avg 866µs/call
# spent 2.35ms making 25 calls to DBI::db::prepare, avg 94µs/call
# spent 1.97ms making 25 calls to DBD::mysql::db::prepare, avg 79µs/call |
| 284 | 25 | 33.3ms | 25 | 32.9ms | if (@exe_args) { # spent 32.9ms making 25 calls to DBI::st::execute, avg 1.32ms/call |
| 285 | $sth->execute(@exe_args); | ||||
| 286 | } else { | ||||
| 287 | $sth->execute; | ||||
| 288 | } | ||||
| 289 | 25 | 1.80ms | 50 | 4.14ms | return $sth->fetchall_arrayref({}); # spent 2.35ms making 25 calls to DBI::st::fetchall_arrayref, avg 94µs/call
# spent 1.79ms making 25 calls to DBD::_::st::fetchall_arrayref, avg 71µs/call |
| 290 | } | ||||
| 291 | |||||
| 292 | sub get_approval_rows { # i.e., from tags_approval | ||||
| 293 | my $hash = shift || {}; | ||||
| 294 | my @ok_fields = qw(term approved date_approved approved_by weight_total limit sort borrowernumber); | ||||
| 295 | my $wheres; | ||||
| 296 | my $limit = ""; | ||||
| 297 | my $order = ""; | ||||
| 298 | my @exe_args = (); | ||||
| 299 | foreach my $key (keys %$hash) { | ||||
| 300 | $debug and print STDERR "get_approval_rows arg. '$key' = ", $hash->{$key}, "\n"; | ||||
| 301 | unless (length $key) { | ||||
| 302 | carp "Empty argument key to get_approval_rows: ignoring!"; | ||||
| 303 | next; | ||||
| 304 | } | ||||
| 305 | unless (1 == scalar grep {/^ $key $/x} @ok_fields) { | ||||
| 306 | carp "get_approval_rows received unreconized argument key '$key'."; | ||||
| 307 | next; | ||||
| 308 | } | ||||
| 309 | if ($key eq 'limit') { | ||||
| 310 | my $val = $hash->{$key}; | ||||
| 311 | unless ($val =~ /^(\d+,)?\d+$/) { | ||||
| 312 | carp "Non-numerical limit value '$val' ignored!"; | ||||
| 313 | next; | ||||
| 314 | } | ||||
| 315 | $limit = " LIMIT $val\n"; | ||||
| 316 | } elsif ($key eq 'sort') { | ||||
| 317 | foreach my $by (split /\,/, $hash->{$key}) { | ||||
| 318 | unless ( | ||||
| 319 | $by =~ /^([-+])?(term)/ or | ||||
| 320 | $by =~ /^([-+])?(biblionumber)/ or | ||||
| 321 | $by =~ /^([-+])?(borrowernumber)/ or | ||||
| 322 | $by =~ /^([-+])?(weight_total)/ or | ||||
| 323 | $by =~ /^([-+])?(approved(_by)?)/ or | ||||
| 324 | $by =~ /^([-+])?(date_approved)/ | ||||
| 325 | ) { | ||||
| 326 | carp "get_approval_rows received illegal sort order '$by'"; | ||||
| 327 | next; | ||||
| 328 | } | ||||
| 329 | if ($order) { | ||||
| 330 | $order .= ", "; | ||||
| 331 | } else { | ||||
| 332 | $order = " ORDER BY " unless $order; | ||||
| 333 | } | ||||
| 334 | $order .= $2 . " " . ((!$1) ? '' : $1 eq '-' ? 'DESC' : $1 eq '+' ? 'ASC' : '') . "\n"; | ||||
| 335 | } | ||||
| 336 | |||||
| 337 | } else { | ||||
| 338 | my $whereval = $hash->{$key}; | ||||
| 339 | my $op = ($whereval =~ s/^(>=|<=)// or | ||||
| 340 | $whereval =~ s/^(>|=|<)// ) ? $1 : '='; | ||||
| 341 | $wheres .= ($wheres) ? " AND $key $op ?\n" : " WHERE $key $op ?\n"; | ||||
| 342 | push @exe_args, $whereval; | ||||
| 343 | } | ||||
| 344 | } | ||||
| 345 | my $query = " | ||||
| 346 | SELECT tags_approval.term AS term, | ||||
| 347 | tags_approval.approved AS approved, | ||||
| 348 | tags_approval.date_approved AS date_approved, | ||||
| 349 | tags_approval.approved_by AS approved_by, | ||||
| 350 | tags_approval.weight_total AS weight_total, | ||||
| 351 | CONCAT(borrowers.surname, ', ', borrowers.firstname) AS approved_by_name | ||||
| 352 | FROM tags_approval | ||||
| 353 | LEFT JOIN borrowers | ||||
| 354 | ON tags_approval.approved_by = borrowers.borrowernumber "; | ||||
| 355 | $query .= ($wheres||'') . $order . $limit; | ||||
| 356 | $debug and print STDERR "get_approval_rows query:\n $query\n", | ||||
| 357 | "get_approval_rows query args: ", join(',', @exe_args), "\n"; | ||||
| 358 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 359 | if (@exe_args) { | ||||
| 360 | $sth->execute(@exe_args); | ||||
| 361 | } else { | ||||
| 362 | $sth->execute; | ||||
| 363 | } | ||||
| 364 | return $sth->fetchall_arrayref({}); | ||||
| 365 | } | ||||
| 366 | |||||
| 367 | sub is_approved { | ||||
| 368 | my $term = shift or return; | ||||
| 369 | my $sth = C4::Context->dbh->prepare("SELECT approved FROM tags_approval WHERE term = ?"); | ||||
| 370 | $sth->execute($term); | ||||
| 371 | unless ($sth->rows) { | ||||
| 372 | $ext_dict and return (spellcheck($term) ? 0 : 1); # spellcheck returns empty on OK word | ||||
| 373 | return 0; | ||||
| 374 | } | ||||
| 375 | return $sth->fetchrow; | ||||
| 376 | } | ||||
| 377 | |||||
| 378 | sub get_tag_index { | ||||
| 379 | my $term = shift or return; | ||||
| 380 | my $sth; | ||||
| 381 | if (@_) { | ||||
| 382 | $sth = C4::Context->dbh->prepare("SELECT * FROM tags_index WHERE term = ? AND biblionumber = ?"); | ||||
| 383 | $sth->execute($term,shift); | ||||
| 384 | } else { | ||||
| 385 | $sth = C4::Context->dbh->prepare("SELECT * FROM tags_index WHERE term = ?"); | ||||
| 386 | $sth->execute($term); | ||||
| 387 | } | ||||
| 388 | return $sth->fetchrow_hashref; | ||||
| 389 | } | ||||
| 390 | |||||
| 391 | sub whitelist { | ||||
| 392 | my $operator = shift; | ||||
| 393 | defined $operator or return; # have to test defined to allow =0 (kohaadmin) | ||||
| 394 | if ($ext_dict) { | ||||
| 395 | foreach (@_) { | ||||
| 396 | spellcheck($_) or next; | ||||
| 397 | add_word_lc($_); | ||||
| 398 | } | ||||
| 399 | } | ||||
| 400 | foreach (@_) { | ||||
| 401 | my $aref = get_approval_rows({term=>$_}); | ||||
| 402 | if ($aref and scalar @$aref) { | ||||
| 403 | mod_tag_approval($operator,$_,1); | ||||
| 404 | } else { | ||||
| 405 | add_tag_approval($_,$operator); | ||||
| 406 | } | ||||
| 407 | } | ||||
| 408 | return scalar @_; | ||||
| 409 | } | ||||
| 410 | # note: there is no "unwhitelist" operation because there is no remove for Ispell. | ||||
| 411 | # The blacklist regexps should operate "in front of" the whitelist, so if you approve | ||||
| 412 | # a term mistakenly, you can still reverse it. But there is no going back to "neutral". | ||||
| 413 | sub blacklist { | ||||
| 414 | my $operator = shift; | ||||
| 415 | defined $operator or return; # have to test defined to allow =0 (kohaadmin) | ||||
| 416 | foreach (@_) { | ||||
| 417 | my $aref = get_approval_rows({term=>$_}); | ||||
| 418 | if ($aref and scalar @$aref) { | ||||
| 419 | mod_tag_approval($operator,$_,-1); | ||||
| 420 | } else { | ||||
| 421 | add_tag_approval($_,$operator,-1); | ||||
| 422 | } | ||||
| 423 | } | ||||
| 424 | return scalar @_; | ||||
| 425 | } | ||||
| 426 | sub add_filter { | ||||
| 427 | my $operator = shift; | ||||
| 428 | defined $operator or return; # have to test defined to allow =0 (kohaadmin) | ||||
| 429 | my $query = "INSERT INTO tags_blacklist (regexp,y,z) VALUES (?,?,?)"; | ||||
| 430 | # my $sth = C4::Context->dbh->prepare($query); | ||||
| 431 | return scalar @_; | ||||
| 432 | } | ||||
| 433 | sub remove_filter { | ||||
| 434 | my $operator = shift; | ||||
| 435 | defined $operator or return; # have to test defined to allow =0 (kohaadmin) | ||||
| 436 | my $query = "REMOVE FROM tags_blacklist WHERE blacklist_id = ?"; | ||||
| 437 | # my $sth = C4::Context->dbh->prepare($query); | ||||
| 438 | # $sth->execute($term); | ||||
| 439 | return scalar @_; | ||||
| 440 | } | ||||
| 441 | |||||
| 442 | sub add_tag_approval { # or disapproval | ||||
| 443 | $debug and warn "add_tag_approval(" . join(", ",map {defined($_) ? $_ : 'UNDEF'} @_) . ")"; | ||||
| 444 | my $term = shift or return; | ||||
| 445 | my $query = "SELECT * FROM tags_approval WHERE term = ?"; | ||||
| 446 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 447 | $sth->execute($term); | ||||
| 448 | ($sth->rows) and return increment_weight_total($term); | ||||
| 449 | my $operator = shift || 0; | ||||
| 450 | my $approval = (@_ ? shift : 0); # default is unapproved | ||||
| 451 | my @exe_args = ($term); # all 3 queries will use this argument | ||||
| 452 | if ($operator) { | ||||
| 453 | $query = "INSERT INTO tags_approval (term,approved_by,approved,date_approved) VALUES (?,?,?,NOW())"; | ||||
| 454 | push @exe_args, $operator, $approval; | ||||
| 455 | } elsif ($approval) { | ||||
| 456 | $query = "INSERT INTO tags_approval (term,approved,date_approved) VALUES (?,?,NOW())"; | ||||
| 457 | push @exe_args, $approval; | ||||
| 458 | } else { | ||||
| 459 | $query = "INSERT INTO tags_approval (term,date_approved) VALUES (?,NOW())"; | ||||
| 460 | } | ||||
| 461 | $debug and print STDERR "add_tag_approval query: $query\nadd_tag_approval args: (" . join(", ", @exe_args) . ")\n"; | ||||
| 462 | $sth = C4::Context->dbh->prepare($query); | ||||
| 463 | $sth->execute(@exe_args); | ||||
| 464 | return $sth->rows; | ||||
| 465 | } | ||||
| 466 | |||||
| 467 | sub mod_tag_approval { | ||||
| 468 | my $operator = shift; | ||||
| 469 | defined $operator or return; # have to test defined to allow =0 (kohaadmin) | ||||
| 470 | my $term = shift or return; | ||||
| 471 | my $approval = (scalar @_ ? shift : 1); # default is to approve | ||||
| 472 | my $query = "UPDATE tags_approval SET approved_by=?, approved=?, date_approved=NOW() WHERE term = ?"; | ||||
| 473 | $debug and print STDERR "mod_tag_approval query: $query\nmod_tag_approval args: ($operator,$approval,$term)\n"; | ||||
| 474 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 475 | $sth->execute($operator,$approval,$term); | ||||
| 476 | } | ||||
| 477 | |||||
| 478 | sub add_tag_index { | ||||
| 479 | my $term = shift or return; | ||||
| 480 | my $biblionumber = shift or return; | ||||
| 481 | my $query = "SELECT * FROM tags_index WHERE term = ? AND biblionumber = ?"; | ||||
| 482 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 483 | $sth->execute($term,$biblionumber); | ||||
| 484 | ($sth->rows) and return increment_weight($term,$biblionumber); | ||||
| 485 | $query = "INSERT INTO tags_index (term,biblionumber) VALUES (?,?)"; | ||||
| 486 | $debug and print STDERR "add_tag_index query: $query\nadd_tag_index args: ($term,$biblionumber)\n"; | ||||
| 487 | $sth = C4::Context->dbh->prepare($query); | ||||
| 488 | $sth->execute($term,$biblionumber); | ||||
| 489 | return $sth->rows; | ||||
| 490 | } | ||||
| 491 | |||||
| 492 | sub get_tag { # by tag_id | ||||
| 493 | (@_) or return; | ||||
| 494 | my $sth = C4::Context->dbh->prepare(TAG_SELECT . "WHERE tag_id = ?"); | ||||
| 495 | $sth->execute(shift); | ||||
| 496 | return $sth->fetchrow_hashref; | ||||
| 497 | } | ||||
| 498 | |||||
| 499 | sub increment_weights { | ||||
| 500 | increment_weight(@_); | ||||
| 501 | increment_weight_total(shift); | ||||
| 502 | } | ||||
| 503 | sub decrement_weights { | ||||
| 504 | decrement_weight(@_); | ||||
| 505 | decrement_weight_total(shift); | ||||
| 506 | } | ||||
| 507 | sub increment_weight_total { | ||||
| 508 | _set_weight_total('weight_total+1',shift); | ||||
| 509 | } | ||||
| 510 | sub increment_weight { | ||||
| 511 | _set_weight('weight+1',shift,shift); | ||||
| 512 | } | ||||
| 513 | sub decrement_weight_total { | ||||
| 514 | _set_weight_total('weight_total-1',shift); | ||||
| 515 | } | ||||
| 516 | sub decrement_weight { | ||||
| 517 | _set_weight('weight-1',shift,shift); | ||||
| 518 | } | ||||
| 519 | sub _set_weight_total { | ||||
| 520 | my $sth = C4::Context->dbh->prepare(" | ||||
| 521 | UPDATE tags_approval | ||||
| 522 | SET weight_total=" . (shift) . " | ||||
| 523 | WHERE term=? | ||||
| 524 | "); # note: CANNOT use "?" for weight_total (see the args above). | ||||
| 525 | $sth->execute(shift); # just the term | ||||
| 526 | } | ||||
| 527 | sub _set_weight { | ||||
| 528 | my $dbh = C4::Context->dbh; | ||||
| 529 | my $sth = $dbh->prepare(" | ||||
| 530 | UPDATE tags_index | ||||
| 531 | SET weight=" . (shift) . " | ||||
| 532 | WHERE term=? | ||||
| 533 | AND biblionumber=? | ||||
| 534 | "); | ||||
| 535 | $sth->execute(@_); | ||||
| 536 | } | ||||
| 537 | |||||
| 538 | sub add_tag { # biblionumber,term,[borrowernumber,approvernumber] | ||||
| 539 | my $biblionumber = shift or return; | ||||
| 540 | my $term = shift or return; | ||||
| 541 | my $borrowernumber = (@_) ? shift : 0; # the user, default to kohaadmin | ||||
| 542 | $term =~ s/^\s+//; | ||||
| 543 | $term =~ s/\s+$//; | ||||
| 544 | ($term) or return; # must be more than whitespace | ||||
| 545 | my $rows = get_tag_rows({biblionumber=>$biblionumber, borrowernumber=>$borrowernumber, term=>$term, limit=>1}); | ||||
| 546 | my $query = "INSERT INTO tags_all | ||||
| 547 | (borrowernumber,biblionumber,term,date_created) | ||||
| 548 | VALUES (?,?,?,NOW())"; | ||||
| 549 | $debug and print STDERR "add_tag query: $query\n", | ||||
| 550 | "add_tag query args: ($borrowernumber,$biblionumber,$term)\n"; | ||||
| 551 | if (scalar @$rows) { | ||||
| 552 | $debug and carp "Duplicate tag detected. Tag not added."; | ||||
| 553 | return; | ||||
| 554 | } | ||||
| 555 | # add to tags_all regardless of approaval | ||||
| 556 | my $sth = C4::Context->dbh->prepare($query); | ||||
| 557 | $sth->execute($borrowernumber,$biblionumber,$term); | ||||
| 558 | |||||
| 559 | # then | ||||
| 560 | if (scalar @_) { # if arg remains, it is the borrowernumber of the approver: tag is pre-approved. | ||||
| 561 | my $approver = shift; | ||||
| 562 | $debug and print STDERR "term '$term' pre-approved by borrower #$approver\n"; | ||||
| 563 | add_tag_approval($term,$approver,1); | ||||
| 564 | add_tag_index($term,$biblionumber,$approver); | ||||
| 565 | } elsif (is_approved($term) >= 1) { | ||||
| 566 | $debug and print STDERR "term '$term' approved by whitelist\n"; | ||||
| 567 | add_tag_approval($term,0,1); | ||||
| 568 | add_tag_index($term,$biblionumber,1); | ||||
| 569 | } else { | ||||
| 570 | $debug and print STDERR "term '$term' NOT approved (yet)\n"; | ||||
| 571 | add_tag_approval($term); | ||||
| 572 | add_tag_index($term,$biblionumber); | ||||
| 573 | } | ||||
| 574 | } | ||||
| 575 | |||||
| 576 | # This takes a set of tags, as returned by C<get_approval_rows> and divides | ||||
| 577 | # them up into a number of "strata" based on their weight. This is useful | ||||
| 578 | # to display them in a number of different sizes. | ||||
| 579 | # | ||||
| 580 | # Usage: | ||||
| 581 | # ($min, $max) = stratify_tags($strata, $tags); | ||||
| 582 | # $stratum: the number of divisions you want | ||||
| 583 | # $tags: the tags, as provided by get_approval_rows | ||||
| 584 | # $min: the minumum stratum value | ||||
| 585 | # $max: the maximum stratum value. This may be the same as $min if there | ||||
| 586 | # is only one weight. Beware of divide by zeros. | ||||
| 587 | # This will add a field to the tag called "stratum" containing the calculated | ||||
| 588 | # value. | ||||
| 589 | sub stratify_tags { | ||||
| 590 | my ( $strata, $tags ) = @_; | ||||
| 591 | |||||
| 592 | my ( $min, $max ); | ||||
| 593 | foreach (@$tags) { | ||||
| 594 | my $w = $_->{weight_total}; | ||||
| 595 | $min = $w if ( !defined($min) || $min > $w ); | ||||
| 596 | $max = $w if ( !defined($max) || $max < $w ); | ||||
| 597 | } | ||||
| 598 | |||||
| 599 | # normalise min to zero | ||||
| 600 | $max = $max - $min; | ||||
| 601 | my $orig_min = $min; | ||||
| 602 | $min = 0; | ||||
| 603 | |||||
| 604 | # if min and max are the same, just make it 1 | ||||
| 605 | my $span = ( $strata - 1 ) / ( $max || 1 ); | ||||
| 606 | foreach (@$tags) { | ||||
| 607 | my $w = $_->{weight_total}; | ||||
| 608 | $_->{stratum} = int( ( $w - $orig_min ) * $span ); | ||||
| 609 | } | ||||
| 610 | return ( $min, $max ); | ||||
| 611 | } | ||||
| 612 | |||||
| 613 | 1 | 8µs | 1; | ||
| 614 | __END__ | ||||
sub C4::Tags::CORE:match; # opcode | |||||
# spent 1.35ms within C4::Tags::CORE:regcomp which was called 600 times, avg 2µs/call:
# 600 times (1.35ms+0s) by C4::Tags::get_tags at line 236, avg 2µs/call | |||||
# spent 172µs within C4::Tags::CORE:subst which was called 100 times, avg 2µs/call:
# 100 times (172µs+0s) by C4::Tags::get_tags at line 269, avg 2µs/call |