478 lines
13 KiB
Perl
478 lines
13 KiB
Perl
######################################################################
|
|
#
|
|
# $Id: DBIUtilsPublic.pm 267 2010-05-27 19:46:57Z alba $
|
|
#
|
|
# Copyright 2007 - 2009 Roman Racine
|
|
# Copyright 2009 Alexander Bartolich
|
|
#
|
|
# 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 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
######################################################################
|
|
package MOD::DBIUtilsPublic;
|
|
|
|
use warnings;
|
|
use strict;
|
|
use DBI;
|
|
use News::Article;
|
|
use Carp qw(confess);
|
|
use Time::Local;
|
|
|
|
sub display_single;
|
|
sub set_status;
|
|
sub set_status_by_moderator;
|
|
sub displayrange;
|
|
sub get_working_by_id;
|
|
sub get_reason;
|
|
sub set_rejected;
|
|
sub get_statistics;
|
|
|
|
# 'spam' can be put back to 'pending' queue
|
|
# 'moderated' tells poster.pl to send the message via NNTP (there is
|
|
# no safe way to undo this)
|
|
# 'rejected' means that a mail was sent the poster - cannot be undone
|
|
# 'deleted' can be put back to 'pending' queue
|
|
# 'posted' means that message was sent to server - cannot be undone
|
|
|
|
use constant NOT_FINAL =>
|
|
"status <> 'rejected' AND status <> 'posted' AND status <> 'sending'";
|
|
|
|
######################################################################
|
|
# Constructor, open a new connection to the database
|
|
######################################################################
|
|
sub new($$)
|
|
######################################################################
|
|
{
|
|
my ($class,$configref) = @_;
|
|
my $self = {};
|
|
$self->{'config'} = $configref;
|
|
$self->{'dsn'} = "DBI:mysql:database=$self->{'config'}->{'mysql_db'};host=$self->{'config'}->{'mysql_host'};port=$self->{'config'}->{'mysql_port'}";
|
|
$self->{'dbh'} = DBI->connect($self->{'dsn'},$self->{'config'}->{'mysql_username'},$self->{'config'}->{'mysql_password'},
|
|
{ RaiseError => 1})
|
|
or die($DBI::errstr);
|
|
|
|
bless $self,$class;
|
|
return $self;
|
|
}
|
|
|
|
|
|
##Die nachfolgenden Methoden sind fuer den Gebrauch im Webinterface bestimmt, alle Methoden, die den Zustand der DB
|
|
##aendern, muessen idempotent sein!
|
|
|
|
######################################################################
|
|
# Update the Status of a posting
|
|
######################################################################
|
|
sub set_status($$$$)
|
|
######################################################################
|
|
{
|
|
my ($self, $id, $newstatus, $prevstatus) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
my $query =
|
|
"UPDATE $table" .
|
|
"\nSET status=(?)" .
|
|
"\nWHERE id=(?)";
|
|
if ($prevstatus)
|
|
{
|
|
$query .= "\nAND (\n status = '";
|
|
$query .= join("'\n OR status = '", @$prevstatus);
|
|
$query .= "'\n)";
|
|
}
|
|
else
|
|
{
|
|
$query .= "\nAND " . NOT_FINAL;
|
|
}
|
|
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
return $stmt->execute($newstatus, $id);
|
|
}
|
|
|
|
######################################################################
|
|
# Update the Status of a posting
|
|
######################################################################
|
|
sub set_status_by_moderator()
|
|
######################################################################
|
|
{
|
|
my ($self, $newstatus, $id, $moderator) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
$self->{'dbh'}->do(
|
|
"UPDATE $table" .
|
|
"\nSET status=(?), Moderator=(?), Moddatum=NOW()" .
|
|
"\nWHERE id=(?)" .
|
|
"\nAND " . NOT_FINAL,
|
|
undef, $newstatus, $moderator, $id
|
|
);
|
|
}
|
|
|
|
######################################################################
|
|
sub set_rejected($$$$$)
|
|
######################################################################
|
|
{
|
|
my ($self, $newstatus, $article_id, $moderator, $reply) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
$self->set_status_by_moderator($newstatus, $article_id, $moderator);
|
|
|
|
my $query = sprintf(
|
|
"INSERT INTO %s_reply\n" .
|
|
"(article_id, reply_date, reply_message)\n" .
|
|
"VALUES (?, NOW(), ?)\n" .
|
|
"ON DUPLICATE KEY UPDATE\n" .
|
|
" reply_date = NOW(),\n" .
|
|
" reply_message=(?)",
|
|
$table
|
|
);
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
$stmt->execute($article_id, $reply, $reply);
|
|
}
|
|
|
|
######################################################################
|
|
sub set_reply($$$)
|
|
######################################################################
|
|
{
|
|
my ($self, $article_id, $reply) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
my $query = sprintf(
|
|
"INSERT INTO %s_reply\n" .
|
|
"(article_id, reply_date, reply_message)\n" .
|
|
"VALUES (?, NOW(), ?)\n" .
|
|
"ON DUPLICATE KEY UPDATE\n" .
|
|
" reply_date = NOW(),\n" .
|
|
" reply_message=(?)",
|
|
$table
|
|
);
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
$stmt->execute($article_id, $reply, $reply);
|
|
}
|
|
|
|
######################################################################
|
|
# Display a range out of a table
|
|
######################################################################
|
|
sub displayrange($$$$$)
|
|
######################################################################
|
|
{
|
|
my ($self,$status,$start,$number_of_results,$overviewref) = @_;
|
|
$start = 0 if ($start !~ /^\d+$/);
|
|
$number_of_results = 1 if ($number_of_results !~ /^\d+$/);
|
|
|
|
my $table = $self->{'config'}->{'mysql_table'}
|
|
|| confess 'No "mysql_table" in config';
|
|
|
|
my $query = sprintf(
|
|
"SELECT %s" .
|
|
"\nFROM %s" .
|
|
"\nWHERE status=(?)" .
|
|
"\nORDER BY ID DESC" .
|
|
"\nLIMIT %d,%d",
|
|
join(',', @$overviewref),
|
|
$table,
|
|
$start, $number_of_results
|
|
);
|
|
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
$stmt->execute($status);
|
|
return $stmt;
|
|
}
|
|
|
|
######################################################################
|
|
# display a single entry of a table
|
|
######################################################################
|
|
sub display_single($$$)
|
|
######################################################################
|
|
{
|
|
my ($self,$status,$id) = @_;
|
|
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
my $query =
|
|
"SELECT CONCAT(header, \"\\n\\n\", body)," .
|
|
"\n Status," .
|
|
"\n Spamcount," .
|
|
"\n Moderator," .
|
|
"\n Moddatum AS 'Decision Date'," .
|
|
"\n Flag" .
|
|
"\nFROM $table" .
|
|
"\nWHERE id=(?)";
|
|
my @param = ( $id );
|
|
|
|
if ($status)
|
|
{
|
|
$query .= "\nAND Status=(?)";
|
|
push @param, $status;
|
|
}
|
|
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
$stmt->execute(@param);
|
|
return $stmt;
|
|
}
|
|
|
|
######################################################################
|
|
sub set_status_posted($$$)
|
|
######################################################################
|
|
{
|
|
my ($self,$id,$user) = @_;
|
|
return $self->set_status_by_moderator('moderated', $id, $user);
|
|
}
|
|
|
|
######################################################################
|
|
sub get_working_by_id($$)
|
|
######################################################################
|
|
{
|
|
my ($self,$id) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
my $query = $self->{'dbh'}->prepare(
|
|
"SELECT" .
|
|
"\n CONCAT(header,\"\\n\\n\",body) AS Posting," .
|
|
"\n if (length(replyto) > 1,replyto,sender) AS Adresse" .
|
|
"\nFROM $table" .
|
|
"\nWHERE ID=(?)" .
|
|
"\nAND " . NOT_FINAL
|
|
);
|
|
$query->execute($id);
|
|
return $query;
|
|
}
|
|
|
|
######################################################################
|
|
sub get_reason($$)
|
|
######################################################################
|
|
{
|
|
my ($self,$id) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
my $query = $self->{'dbh'}->prepare(
|
|
"SELECT reply_message" .
|
|
"\nFROM ${table}, ${table}_reply" .
|
|
"\nWHERE ${table}.id = ${table}_reply.article_id" .
|
|
"\nAND article_id=(?)" .
|
|
"\nAND (status='rejected' OR status='deleted')
|
|
");
|
|
$query->execute($id);
|
|
return $query;
|
|
}
|
|
|
|
######################################################################
|
|
sub display_errors($$$$$)
|
|
######################################################################
|
|
{
|
|
my ($self,$status,$start,$number_of_results,$overviewref) = @_;
|
|
$start = 0 if ($start !~ /^\d+$/);
|
|
$number_of_results = 1 if ($number_of_results !~ /^\d+$/);
|
|
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
my $query = sprintf(
|
|
"SELECT %s" .
|
|
"\nFROM %s_error_view" .
|
|
"\nORDER BY error_date DESC" .
|
|
"\nLIMIT %d, %d",
|
|
join(',', @$overviewref),
|
|
$table,
|
|
$start, $number_of_results
|
|
);
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
$stmt->execute();
|
|
return $stmt;
|
|
}
|
|
|
|
######################################################################
|
|
sub get_errormessage($$)
|
|
######################################################################
|
|
{
|
|
my ($self,$id) = @_;
|
|
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
my $query = sprintf(
|
|
"SELECT error_message" .
|
|
"\nFROM %s_error" .
|
|
"\nWHERE error_id=(?)",
|
|
$table
|
|
);
|
|
my $stmt = $self->{'dbh'}->prepare($query);
|
|
$stmt->execute($id);
|
|
return $stmt;
|
|
}
|
|
|
|
######################################################################
|
|
sub invert_flag($$)
|
|
######################################################################
|
|
{
|
|
my ($self,$id) = @_;
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
$self->{'dbh'}->do(
|
|
"UPDATE $table" .
|
|
"\nSET flag = !flag" .
|
|
"\nWHERE ID=(?)" .
|
|
"\nAND " . NOT_FINAL,
|
|
undef, $id
|
|
);
|
|
}
|
|
|
|
######################################################################
|
|
sub calc_item_stats($$)
|
|
######################################################################
|
|
{
|
|
my ($r_result, $r_items) = @_;
|
|
|
|
my @items = sort { $a <=> $b }( @$r_items );
|
|
return undef unless($#items >= 0);
|
|
|
|
my $sum = 0;
|
|
for my $i( @items ) { $sum += $i; }
|
|
|
|
my $nr_items = 1 + $#items;
|
|
my $pivot = int($nr_items / 2);
|
|
my $median = $items[$pivot];
|
|
if (($nr_items % 2) == 0)
|
|
{
|
|
$median = ( $median + $items[$pivot - 1] ) / 2;
|
|
}
|
|
|
|
$r_result->{'count'} = $nr_items;
|
|
$r_result->{'sum'} = $sum;
|
|
$r_result->{'avg'} = $sum / $nr_items;
|
|
$r_result->{'median'} = $median;
|
|
$r_result->{'min'} = $items[0];
|
|
$r_result->{'max'} = $items[ $#items ];
|
|
|
|
return $r_items;
|
|
}
|
|
|
|
######################################################################
|
|
sub get_reaction_time($;$$$)
|
|
######################################################################
|
|
{
|
|
my ( $self, $from, $to, $status ) = @_;
|
|
|
|
# Warning: Plain "Moddatum - Datum" returns strange values when
|
|
# crossing day boundaries. Using unix_timestamp instead.
|
|
|
|
my $query =
|
|
"select unix_timestamp(Datum), timestampdiff(SECOND, Datum, Moddatum)" .
|
|
"\nfrom " . $self->{'config'}->{'mysql_table'} .
|
|
"\nwhere datum is not null" .
|
|
"\nand Moddatum is not null" .
|
|
"\nand Datum is not null";
|
|
|
|
if ($from)
|
|
{ $query .= "\nand datum >= from_unixtime($from)"; }
|
|
if ($to)
|
|
{ $query .= "\nand datum < from_unixtime($to)"; }
|
|
if ($status)
|
|
{ $query .= "\nand Status = '$status'"; }
|
|
|
|
my $sth = $self->{'dbh'}->prepare($query);
|
|
$sth->execute();
|
|
|
|
my %result;
|
|
while(my $row = $sth->fetchrow_arrayref )
|
|
{
|
|
my $datum = 0 + $row->[0];
|
|
my $reaction_time = 0 + $row->[1];
|
|
my ($sec, $minute, $hour, $mday, $month, $year, $wday, $yday, $isdst) =
|
|
localtime($datum);
|
|
|
|
my $y = sprintf("%04d", $year + 1900);
|
|
my $m = sprintf("%02d", $month + 1);
|
|
my $d = sprintf("%02d", $mday);
|
|
|
|
my $items = $result{$y}->{$m}->{$d}->{'items'};
|
|
if (defined( $items ))
|
|
{
|
|
push @$items, $reaction_time;
|
|
}
|
|
else
|
|
{
|
|
$result{$y}->{$m}->{$d}->{'items'} = [ $reaction_time ];
|
|
}
|
|
}
|
|
|
|
# we are going to modify the hash so we need robust iteration
|
|
my @year = keys(%result);
|
|
my @all_items;
|
|
|
|
for my $year(@year)
|
|
{
|
|
my $r_month = $result{$year};
|
|
my @month = keys(%$r_month);
|
|
my @year_items;
|
|
|
|
for my $month(@month)
|
|
{
|
|
my $r_mday = $r_month->{$month};
|
|
my @mday = keys(%$r_mday);
|
|
my @month_items;
|
|
|
|
for my $mday(@mday)
|
|
{
|
|
my $r = $r_mday->{$mday};
|
|
my $r_items = $r->{'items'};
|
|
push @month_items, @$r_items;
|
|
calc_item_stats($r, $r_items);
|
|
# delete $r->{'items'};
|
|
}
|
|
|
|
push @year_items, @month_items;
|
|
calc_item_stats($r_mday->{'total'} = {}, \@month_items);
|
|
}
|
|
push @all_items, @year_items;
|
|
calc_item_stats($r_month->{'total'} = {}, \@year_items);
|
|
}
|
|
calc_item_stats($result{'total'} = {}, \@all_items);
|
|
|
|
return \%result;
|
|
}
|
|
|
|
######################################################################
|
|
sub get_statistics($)
|
|
######################################################################
|
|
{
|
|
my ($self) = @_;
|
|
my $dbh = $self->{'dbh'}
|
|
|| confess 'No "dbh" in self';
|
|
my $table = $self->{'config'}->{'mysql_table'} || confess;
|
|
|
|
#
|
|
# Warning: The combination of union and selectall_arrayref does not
|
|
# like null values. They are just ommitted from the result.
|
|
#
|
|
my $arrayref = $dbh->selectall_arrayref(
|
|
"select unix_timestamp(min(datum)) from $table" .
|
|
"\nunion" .
|
|
"\nselect unix_timestamp(max(datum)) from $table"
|
|
);
|
|
|
|
if (!$arrayref) { return undef; }
|
|
if (!$arrayref->[1]) { return undef; }
|
|
if (!$arrayref->[0]) { return undef; }
|
|
|
|
# add 1 because query is (datum >= min and datum < max)
|
|
my $to = 1 + $arrayref->[1]->[0];
|
|
my $from = $arrayref->[0]->[0];
|
|
undef $arrayref;
|
|
|
|
my $result = {
|
|
'all' => $self->get_reaction_time($from, $to)
|
|
};
|
|
for my $status(
|
|
'pending',
|
|
'moderated',
|
|
'spam',
|
|
'rejected',
|
|
'deleted',
|
|
'posted',
|
|
'sending')
|
|
{
|
|
$result->{$status} = $self->get_reaction_time($from, $to, $status);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
######################################################################
|
|
1;
|
|
######################################################################
|