huhu/MOD/DBIUtilsPublic.pm

479 lines
13 KiB
Perl
Raw Normal View History

######################################################################
#
# $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:MariaDB: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;
######################################################################