diff --git a/cpanfile b/cpanfile
index 3062244eb..b1ddd0b3e 100644
--- a/cpanfile
+++ b/cpanfile
@@ -147,6 +147,9 @@ requires 'Text::LineFold', '>= 2018.012';
# Used to get time with sub-second precision
requires 'Time::HiRes', '>= 1.29';
+# Used to get Unix time from local time
+requires 'Time::Local', '>= 1.23';
+
# Used to create URI containing non URI-canonical characters.
# Note: '3.28' is the version included in URI-1.35.
requires 'URI::Escape', '>= 3.28';
diff --git a/default/web_tt2/nav.tt2 b/default/web_tt2/nav.tt2
index 6678e3f08..0562373ce 100644
--- a/default/web_tt2/nav.tt2
+++ b/default/web_tt2/nav.tt2
@@ -129,6 +129,18 @@
[%|loc%]Families[%END%]
[% END %]
+
+ [% IF subaction == 'stats' %]
+ [% SET class = 'active' %]
+ [% ELSE %]
+ [% SET class = '' %]
+ [% END %]
+
+
+ [%|loc%]Statistics[%END%]
+
+
+
[% IF subaction == 'translation' %]
[% SET class = 'active' %]
[% ELSE %]
diff --git a/default/web_tt2/serveradmin.tt2 b/default/web_tt2/serveradmin.tt2
index 90ea177d3..e12039b67 100644
--- a/default/web_tt2/serveradmin.tt2
+++ b/default/web_tt2/serveradmin.tt2
@@ -255,6 +255,15 @@
[% close_table %]
[% END %]
+
+[% IF subaction == 'stats' ~%]
+ [%|loc(domain)%]Virtual domain %1 statistics[%END%]
+ [%|loc%]This page displays overall information regarding the activity on this virtual domain.[%END%]
+
+ [% PROCESS stats.tt2 %]
+
+[%~ END %]
+
[% IF subaction == 'translation' %]
[%|loc%]Translating Sympa[%END%]
[%|loc%]Sympa is designed to allow easy internationalization of its user interface (service email messages and web interface). All translations for a language are gathered into a single PO file that can be manipulated by the standard GNU gettext tools.[%END%]
diff --git a/default/web_tt2/stats.tt2 b/default/web_tt2/stats.tt2
index 1fbbd2a36..d2db5496a 100644
--- a/default/web_tt2/stats.tt2
+++ b/default/web_tt2/stats.tt2
@@ -1,12 +1,14 @@
-[%|loc(listname)%]List %1 statistics[%END%]
+[% IF listconf ~%]
+ [%|loc(listname)%]List %1 statistics[%END%]
-[%|loc%]This page displays overall information regarding the list activity[%END%]
+ [%|loc%]This page displays overall information regarding the list activity[%END%]
[%|loc(shared_size)%]Shared document directory size: %1 kB[%END%]
[%|loc(arc_size)%]Web archives size: %1 kB[%END%]
+[%~ END %]
[% FOREACH stat_id IN [ 'send_mail', 'add_or_subscribe', 'signoff', 'del', 'auto_del' ] ~%]
[% SET o = stats.$stat_id ~%]
@@ -37,6 +39,9 @@
}
},
yaxis: {
+ [% IF o.max_value && o.max_value < 10 ~%]
+ ticks: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ [% END ~%]
tickOptions: {
angle: 0,
formatString: '%d'
diff --git a/src/cgi/wwsympa.fcgi.in b/src/cgi/wwsympa.fcgi.in
index c2db69033..f8c8f3c7c 100644
--- a/src/cgi/wwsympa.fcgi.in
+++ b/src/cgi/wwsympa.fcgi.in
@@ -5964,6 +5964,9 @@ sub do_serveradmin {
## Checking families and other virtual hosts.
get_server_details();
+ if ($in{'subaction'} eq 'stats') {
+ _get_stats();
+ }
## Server files
foreach my $f (
@@ -15108,6 +15111,12 @@ sub do_stats {
$param->{'arc_size'} =
int((Sympa::Archive->new(context => $list)->get_size + 512) / 1024);
+ return _get_stats();
+}
+
+sub _get_stats {
+ my $that = (ref $list eq 'Sympa::List') ? $list : $robot;
+
my $stats = {
send_mail => {title => $language->gettext("Mail sending")},
add_or_subscribe =>
@@ -15122,7 +15131,7 @@ sub do_stats {
};
foreach my $operation (keys %$stats) {
- my $data = $log->aggregate_daily_data($list, $operation);
+ my $data = $log->aggregate_daily_data($that, $operation);
if (%{$data || {}}) {
$stats->{$operation}{'stats_values'} = '[' . join(
',',
@@ -15133,6 +15142,8 @@ sub do_stats {
sprintf "['%s',%d]", $formatted_date, $data->{$_}
} sort keys %$data
) . ']';
+ $stats->{$operation}{'max_value'} =
+ [sort { $b <=> $a } values %$data]->[0];
}
}
$param->{'stats'} = $stats;
diff --git a/src/lib/Sympa/Log.pm b/src/lib/Sympa/Log.pm
index 0c53d421e..664340ebf 100644
--- a/src/lib/Sympa/Log.pm
+++ b/src/lib/Sympa/Log.pm
@@ -625,11 +625,11 @@ sub aggregate_stat {
unless (@res) {
return 0;
}
- my $date_deb = $res[0] - ($res[0] % 3600);
+ my $date_deb = $res[0] - ($res[0] % 900);
- # Hour to hour
+ # By each 15 minutes (some time zones have this time difference).
my @slots;
- for (my $i = $date_deb; $i <= $date_end; $i = $i + 3600) {
+ for (my $i = $date_deb; $i <= $date_end; $i += 900) {
push @slots, $i;
}
@@ -759,7 +759,7 @@ sub _aggregate_data {
sub aggregate_daily_data {
my $self = shift;
$self->syslog('debug2', '(%s, %s)', @_);
- my $list = shift;
+ my $that = shift;
my $operation = shift;
my $sdm;
@@ -769,42 +769,77 @@ sub aggregate_daily_data {
return;
}
- my $result;
+ my $result = {};
+
+ my $cond;
+ my @vars;
+ if (ref $that eq 'Sympa::List') {
+ $cond = q{robot_counter = ? AND list_counter = ?};
+ @vars = ($that->{'domain'}, $that->{'name'});
+ } else {
+ $cond = q{robot_counter = ?};
+ @vars = ($that);
+ }
my $sth;
- my $row;
+
unless (
$sth = $sdm->do_prepared_query(
- q{SELECT beginning_date_counter AS "date",
- count_counter AS "count"
- FROM stat_counter_table
- WHERE data_counter = ? AND
- robot_counter = ? AND list_counter = ?},
+ sprintf(
+ q{SELECT beginning_date_counter AS "date",
+ count_counter AS "count"
+ FROM stat_counter_table
+ WHERE data_counter = ? AND %s}, $cond
+ ),
$operation,
- $list->{'domain'}, $list->{'name'}
+ @vars
)
) {
$self->syslog('err', 'Unable to get stat data %s for list %s',
- $operation, $list);
+ $operation, $that);
return;
}
- while ($row = $sth->fetchrow_hashref('NAME_lc')) {
- my $midnight = Sympa::Tools::Time::get_midnight_time($row->{'date'});
- $result->{$midnight} = 0 unless defined $result->{$midnight};
- $result->{$midnight} += $row->{'count'};
+ while (my $row = $sth->fetchrow_hashref('NAME_lc')) {
+ _add_count($result, $row->{date}, $row->{count});
}
$sth->finish;
- my @dates = sort { $a <=> $b } keys %$result;
- return {} unless @dates;
-
- for (my $date = $dates[0]; $date < $dates[-1]; $date += 86400) {
- my $midnight = Sympa::Tools::Time::get_midnight_time($date);
- $result->{$midnight} = 0 unless defined $result->{$midnight};
+ if (%{$result || {}}) {
+ # Fill in zeroes for missing days.
+ unless (
+ $sth = $sdm->do_prepared_query(
+ sprintf(
+ q{SELECT MIN(beginning_date_counter),
+ MAX(beginning_date_counter)
+ FROM stat_counter_table
+ WHERE %s}, $cond
+ ),
+ @vars
+ )
+ ) {
+ $self->syslog('err', 'Unable to get stat data %s for list %s',
+ $operation, $that);
+ return;
+ }
+ my ($min, $max) = $sth->fetchrow_array;
+ for (my $d = $min; $d <= $max; $d += 900) {
+ _add_count($result, $d, 0);
+ }
+ $sth->finish;
}
+
return $result;
}
+sub _add_count {
+ my $result = shift;
+ my $date = shift;
+ my $count = shift;
+
+ my $midnight = Sympa::Tools::Time::get_midnight_time($date);
+ $result->{$midnight} //= 0;
+ $result->{$midnight} += $count;
+}
1;
__END__
diff --git a/src/lib/Sympa/Tools/Time.pm b/src/lib/Sympa/Tools/Time.pm
index a9a36904c..25696ee15 100644
--- a/src/lib/Sympa/Tools/Time.pm
+++ b/src/lib/Sympa/Tools/Time.pm
@@ -8,8 +8,8 @@
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER
-# Copyright 2018 The Sympa Community. See the AUTHORS.md file at the
-# top-level directory of this distribution and at
+# Copyright 2018, 2023 The Sympa Community. See the
+# AUTHORS.md file at the top-level directory of this distribution and at
# .
#
# This program is free software; you can redistribute it and/or modify
@@ -41,9 +41,9 @@ use constant has_gettimeofday => defined eval { Time::HiRes::gettimeofday() };
# Note: This is used only once.
sub get_midnight_time {
- my $epoch = $_[0];
- my @date = localtime($epoch);
- return $epoch - $date[0] - $date[1] * 60 - $date[2] * 3600;
+ my $epoch = shift;
+ my @date = localtime $epoch;
+ return Time::Local::timelocal(0, 0, 0, @date[3 .. 5]);
}
sub epoch_conv {