mourningdove/cgi-bin/DW/Controller/Stats.pm

207 lines
6.3 KiB
Perl
Raw Permalink Normal View History

2026-05-24 01:03:05 +00:00
#!/usr/bin/perl
#
# DW::Controller::Stats
#
# This controller concerns basic site account statistics.
# The newer, more business-related stats are in SiteStats.pm.
#
# Authors:
# Jen Griffin <kareila@livejournal.com>
#
# Copyright (c) 2020 by Dreamwidth Studios, LLC.
#
# This program is free software; you may redistribute it and/or modify it under
# the same terms as Perl itself. For a copy of the license, please reference
# 'perldoc perlartistic' or 'perldoc perlgpl'.
#
package DW::Controller::Stats;
use strict;
use DW::Routing;
use DW::Controller;
use DW::Template;
use DW::Countries;
DW::Routing->register_string( '/stats', \&main_handler, app => 1 );
sub main_handler {
my ( $ok, $rv ) = controller( anonymous => 1 );
return $rv unless $ok;
my $scope = "/stats/main.tt";
my $dbr = LJ::get_db_reader();
my $sth;
my %stat;
{ # start with basic stat categories, bail out if we don't have these
$sth = $dbr->prepare(
"SELECT statcat, statkey, statval FROM stats WHERE statcat IN
('userinfo', 'client', 'age', 'gender', 'account', 'size')"
);
$sth->execute;
while ( $_ = $sth->fetchrow_hashref ) {
$stat{ $_->{'statcat'} }->{ $_->{'statkey'} } = $_->{'statval'};
}
return error_ml("$scope.error.nostats") unless %stat;
}
my @countries;
my @states;
{ # load country and state stats
my %countries;
DW::Countries->load_legacy( \%countries );
$sth = $dbr->prepare(
"SELECT statkey, statval FROM stats WHERE statcat='country'
ORDER BY statval DESC LIMIT 15"
);
$sth->execute;
while ( my $row = $sth->fetchrow_hashref ) {
$stat{'country'}->{ $countries{ $row->{statkey} } } = $row->{statval};
}
@countries = sort { $stat{'country'}->{$b} <=> $stat{'country'}->{$a} }
keys %{ $stat{'country'} };
$sth = $dbr->prepare(
"SELECT c.item, s.statval FROM stats s, codes c
WHERE c.type='state' AND s.statcat='stateus' AND s.statkey=c.code
ORDER BY s.statval DESC LIMIT 15"
);
$sth->execute;
while ( $_ = $sth->fetchrow_hashref ) {
$stat{'state'}->{ $_->{'item'} } = $_->{'statval'};
}
@states = sort { $stat{'state'}->{$b} <=> $stat{'state'}->{$a} }
keys %{ $stat{'state'} };
}
my %accounts_updated = ( P => [], C => [], Y => [] );
my %accounts_created = ( P => [], C => [], Y => [] );
{ # load recent usage stats for various account types
if ( LJ::is_enabled('stats-recentupdates') ) {
$sth = $dbr->prepare(
"SELECT u.userid, uu.timeupdate_public AS 'timeupdate' FROM user u, userusage uu
WHERE u.userid=uu.userid AND uu.timeupdate_public > DATE_SUB(NOW(), INTERVAL 30 DAY)
AND u.journaltype = ? ORDER BY uu.timeupdate_public DESC LIMIT 20"
);
$sth->execute('P');
$accounts_updated{P} = $sth->fetchall_arrayref( {} );
$sth->execute('C');
$accounts_updated{C} = $sth->fetchall_arrayref( {} );
$sth->execute('Y');
$accounts_updated{Y} = $sth->fetchall_arrayref( {} );
}
if ( LJ::is_enabled('stats-newjournals') ) {
$sth = $dbr->prepare(
"SELECT u.userid, uu.timeupdate FROM user u, userusage uu WHERE
u.userid=uu.userid AND uu.timeupdate IS NOT NULL AND u.journaltype = ?
AND u.statusvis != 'S' ORDER BY uu.timecreate DESC LIMIT 20"
);
$sth->execute('P');
$accounts_created{P} = $sth->fetchall_arrayref( {} );
$sth->execute('C');
$accounts_created{C} = $sth->fetchall_arrayref( {} );
$sth->execute('Y');
$accounts_created{Y} = $sth->fetchall_arrayref( {} );
}
}
my @uids;
foreach my $a ( values(%accounts_created), values(%accounts_updated) ) {
push @uids, map { $_->{userid} } @$a;
}
my %age;
my $maxage = 1;
{ # do math for age-related bar graphs
my $lowage = 13;
my $highage = 119; # given db floor of 1890 (as of 2009)
foreach my $key ( keys %{ $stat{'age'} } ) {
next if $key < $lowage;
next if $key > $highage;
$age{$key} = $stat{'age'}->{$key};
$maxage = $age{$key} if $age{$key} > $maxage;
}
}
my @client_list;
my %client_details;
{ # format client data (if enabled)
if ( LJ::is_enabled('clientversionlog') ) {
### sum up clients over different versions
foreach my $c ( keys %{ $stat{'client'} } ) {
next unless $c =~ /^(.+?)\//;
$stat{'clientname'}->{$1} += $stat{'client'}->{$c};
}
foreach my $cn (
sort { $stat{'clientname'}->{$b} <=> $stat{'clientname'}->{$a} }
keys %{ $stat{'clientname'} }
)
{
last unless $stat{'clientname'}->{$cn} >= 50;
push @client_list, $cn;
my @client_versions;
foreach my $c ( sort grep { /^\Q$cn\E\// } keys %{ $stat{'client'} } ) {
my $count = $stat{'client'}->{$c};
$c =~ s/^\Q$cn\E\///;
push @client_versions, LJ::ehtml($c) . " ($count)";
}
$client_details{$cn} = join ", ", @client_versions;
}
}
}
my %graphs = ( newbyday => 'stats/newbyday.png' );
my $vars = {
stat => \%stat,
countries => \@countries,
states => \@states,
accounts_updated => \%accounts_updated,
accounts_created => \%accounts_created,
userobj_for => LJ::load_userids(@uids),
age => \%age,
client_list => \@client_list,
client_details => \%client_details,
graphs => \%graphs,
default_zero => sub { $_[0] && $_[0] ne '' ? $_[0] + 0 : 0 },
percentage => sub { sprintf( "%0.1f", $_[0] * 100 / $_[1] ) },
scale_bar => sub { int( 400 * $_[0] / $maxage ) },
};
return DW::Template->render_template( 'stats/main.tt', $vars );
}
1;