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 {