Convert the old BML pages to use Template Toolkit. Change rounding to
the nearest tenth, rather than nearest whole, and add a message about the
rounding. Change calculation of percentages so they're only done over
personal/community accounts. Some whitespace tweaks.
Patch by pauamma.
diffs (1065 lines):
diff -r 9f08948db033 -r 4c2d441485d3 bin/upgrading/update-db-general.pl
--- a/bin/upgrading/update-db-general.pl Mon Jul 05 22:26:30 2010 +0800
+++ b/bin/upgrading/update-db-general.pl Mon Jul 05 22:57:18 2010 +0800
@@ -341,7 +341,7 @@ CREATE TABLE site_stats (
-- FIXME: This is good for retrieving data for a single category+key, but
-- maybe not as good if we want all keys for the category, with a limit on
-- time (ie, last 5 entries, or last 2 weeks). Do we need an extra index?
- INDEX (category_id, key_id, insert_time)
+ INDEX (category_id, key_id, insert_time)
)
EOC
@@ -1534,6 +1534,7 @@ CREATE TABLE clustertrack2 (
timeactive INT UNSIGNED NOT NULL,
clusterid SMALLINT UNSIGNED,
accountlevel SMALLINT UNSIGNED,
+ journaltype char(1),
INDEX (timeactive, clusterid)
)
@@ -2985,7 +2986,7 @@ CREATE TABLE content_filter_data (
data mediumblob NOT NULL,
PRIMARY KEY (userid,filterid)
-)
+)
EOC
register_tablecreate('sitekeywords', <<'EOC');
@@ -3846,6 +3847,11 @@ EOF
q{ALTER TABLE clustertrack2 ADD COLUMN accountlevel SMALLINT
UNSIGNED AFTER clusterid} );
}
+ unless ( column_type( 'clustertrack2', 'journaltype' ) ) {
+ do_alter( 'clustertrack2',
+ q{ALTER TABLE clustertrack2 ADD COLUMN journaltype char(1)
AFTER accountlevel} );
+ }
+
unless ( column_type( 'acctcode_promo', 'suggest_journalid' ) ) {
do_alter( 'acctcode_promo',
q{ALTER TABLE acctcode_promo ADD COLUMN suggest_journalid
INT UNSIGNED} );
diff -r 9f08948db033 -r 4c2d441485d3 cgi-bin/DW/Controller/SiteStats.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Controller/SiteStats.pm Mon Jul 05 22:57:18 2010 +0800
@@ -0,0 +1,188 @@
+#!/usr/bin/perl
+#
+# DW::Controller::SiteStats
+#
+# Controller module for new DW stats (public and restricted)
+#
+# Authors:
+# Afuna <[email protected]>
+# Pau Amma <[email protected]>
+#
+# Copyright (c) 2009-2010 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'.
+
+=head1 NAME
+
+DW::Controller::SiteStats -- Controller for new DW stats (public and
restricted)
+
+=head1 SYNOPSIS
+
+ use DW::Controller::SiteStats; # That's all there is to it.
+
+=cut
+
+use strict;
+use warnings;
+
+package DW::Controller::Sitestats;
+
+use DW::Controller;
+use DW::Routing;
+use DW::Template;
+use DW::StatStore;
+use DW::StatData;
+
+LJ::ModuleLoader::autouse_subclasses( 'DW::StatData' );
+
+DW::Routing->register_string( '/stats/site', \&stats_page, app => 1,
+ args => [ 'stats/site.tt', \&public_data, 1 ] );
+
+DW::Routing->register_string( '/admin/stats', \&stats_page, app => 1,
+ args => [ 'admin/stats.tt', \&admin_data, 0,
+ 'payments' ] );
+
+=head1 Internals
+
+=head2 C<< DW::Controller::SiteStats::stats_page( $opts ) >>
+
+C<< $opts->args >> is C<< [ $template, $source, $anon_ok, $privs ] >>, where
+
+=over
+
+=item $template -- filename of template, relative to views/
+
+=item $source -- subref to retrieve stat data passed to template
+
+=item $anon_ok -- true if anonymous not-logged-in access to the page is allowed
+
+=item $privs -- (optional) privs needed, in the privcheck => controller format
+
+=back
+
+=cut
+
+sub stats_page {
+ my ( $template, $source, $anon_ok, $privs ) = @{ $_[0]->args };
+
+ my ( $ok, $rv ) = controller( anonymous => $anon_ok, privcheck => $privs );
+
+ return $rv unless $ok;
+
+ my %vars = ( %$rv, %{ $source->() } );
+
+ return DW::Template->render_template( $template, \%vars );
+}
+
+=head2 C<< _make_a_number( $value ) >>
+
+Internal - return $value forced to a number
+
+=cut
+
+sub _make_a_number {
+ return defined( $_[0] ) ? $_[0] + 0 : 0;
+}
+
+=head2 C<< _dashes_to_underlines( $string ) >>
+
+Internal - returns $string with - characters changed to _
+
+=cut
+
+sub _dashes_to_underlines {
+ my $undashed = $_[0];
+ $undashed =~ tr/\-/_/;
+ return $undashed;
+}
+
+=head2 C<< DW::Controller::SiteStats::public_data( ) >>
+
+Public stats data
+
+Returns hashref of variables to pass to the template. Note: doesn't check
+or care whether user should have access. That's for the caller to do.
+
+=cut
+
+sub public_data {
+ my $vars = {};
+
+ # Accounts by type
+ my $accounts_by_type = DW::StatData::AccountsByType->load_latest(
DW::StatStore->get( "accounts" ) );
+ $vars->{accounts_by_type}
+ = { map { _dashes_to_underlines( $_ )
+ => _make_a_number( $accounts_by_type->value( $_ ) ) }
+ @{$accounts_by_type->keylist} }
+ if defined $accounts_by_type;
+
+ # Active accounts by time since last active, level, and type
+ my $active_accounts = DW::StatData::ActiveAccounts->load_latest(
DW::StatStore->get( "active" ) );
+ $vars->{active_accounts}
+ = { map { _dashes_to_underlines( $_ )
+ => _make_a_number( $active_accounts->value( $_ ) ) }
+ @{$active_accounts->keylist} }
+ if defined $active_accounts;
+
+ # Paid accounts by level
+ my $paid = DW::StatData::PaidAccounts->load_latest( DW::StatStore->get(
"paid" ) );
+ $vars->{paid}
+ = { map { _dashes_to_underlines( $_ )
+ => _make_a_number( $paid->value( $_ ) ) }
+ @{$paid->keylist} }
+ if defined $paid;
+
+ return $vars;
+}
+
+=head2 C<< DW::Controller::SiteStats::admin_data( ) >>
+
+Admin stats data
+
+Returns hashref of variables to pass to the template. Note: doesn't check or
+or care whether user should have access. That's for the caller to do.
+
+=cut
+
+sub admin_data {
+ my $vars = public_data( @_ ); # Just in case it gets arguments someday.
+
+<<COMMENT;
+
+FIXME: remove this when you have implemented them all
+
+* Number of accounts, total (done)
+* Number of accounts active (done)
+* Number of paid accounts (by payment level) (done)
+ -- as a percentage of total accounts (done)
+ -- as a percentage of active accounts (done)
+ -- number of active paid accounts (done)
+ -- number of inactive paid accounts (done)
+* Number of payments in last 1d/2d/5d/7d/1m/3m/1y
+ -- broken down by which payment level/payment item chosen
+ -- and divided into new payments vs. renewals
+ -- and expressed as a dollar amount taken in during that time
+* Number of lapsed paid accounts in last 1d/2d/5d/7d/1m/3m/1y
+ -- and renewed within 7d/14d/1m
+ -- and not renewed within 7d/14d/1m
+ -- and as a percentage of total paid accounts
+* Percent churn over last 7d/1m/3m/1y
+ -- (churn formula: total lapsed paid accounts that don't renew within 7d/total
+paid accounts * 100)
+* Number of paid accounts that were created via payment (no code)
+* Number of paid accounts that were created via code, then paid
+ -- within 1d/2d/5d/7d/1m/3m/1y of creation
+* Total refunds issued within last 7d/1m/3m/1y
+ -- with dollar amount refunded
+ -- with fees added to dollar amount refunded
+* Total chargebacks/PayPal refunds within last 7d/1m/3m/1y
+ -- with dollar amount charged back
+ -- with fees added to dollar amount charged back
+COMMENT
+
+ return $vars;
+}
+
+1;
diff -r 9f08948db033 -r 4c2d441485d3 cgi-bin/DW/StatData/ActiveAccounts.pm
--- a/cgi-bin/DW/StatData/ActiveAccounts.pm Mon Jul 05 22:26:30 2010 +0800
+++ b/cgi-bin/DW/StatData/ActiveAccounts.pm Mon Jul 05 22:57:18 2010 +0800
@@ -7,7 +7,7 @@
# Pau Amma <[email protected]>
# Some code based off bin/maint/stats.pl
#
-# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+# Copyright (c) 2009-2010 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
@@ -29,13 +29,14 @@ An account is counted as active when it
An account is counted as active when it logs in, when it posts an entry (when
posting to a community, both the poster and the community are marked active),
or when it posts or edits a comment.
-
+
=cut
use strict;
use warnings;
use base 'DW::StatData';
+use DW::Pay;
sub category { "active" }
sub name { "Active Accounts" }
@@ -45,7 +46,8 @@ sub keylist {
my @levels = ( 'unknown', values %{DW::Pay::all_shortnames()} );
my @keys = ();
foreach my $k ( keys %key_to_days ) {
- push @keys, $k, map "$k-$_", @levels;
+ push @keys, $k, map { +"$k-$_", "$k-$_-P", "$k-$_-C", "$k-$_-I" }
+ @levels;
}
return \...@keys;
}
@@ -58,25 +60,29 @@ Collects data for the following keys:
=over
-=item active_1d, active_1d-I<< level name >>
+=item active_1d, active_1d-I<< level name >>, active_1d-I<< level name >>-I<<
type letter >>
-Number of accounts active in the last 24 hours (total and for each account
-level)
+Number of accounts active in the last 24 hours (total, for each account
+level, and for each account level and type - for personal, community, and
+identity accounts only)
-=item active_7d, active_7d-I<< level name >>
+=item active_7d, active_7d-I<< level name >>, active_7d-I<< level name >>-I<<
type letter >>
-Number of accounts active in the last 168 (7*24) hours (total and for each
-account level)
+Number of accounts active in the last 168 (7*24) hours (total, for each
+account level, and for each account level and type - for personal, community,
+and identity accounts only)
-=item active_30d, active_30d-I<< level name >>
+=item active_30d, active_30d-I<< level name >>, active_30d-I<< level name
>>-I<< type letter >>
-Number of accounts active in the last 720 (30*24) hours (total and for each
-account level)
+Number of accounts active in the last 720 (30*24) hours (total, for each
+account level, and for each account level and type - for personal, community,
+and identity accounts only)
=back
In the above, I<< level name >> is any of the account level names returned by
-C<< DW::Pay::all_shortnames >> or "unknown".
+C<< DW::Pay::all_shortnames >> or "unknown", and I<< type letter >> is P for
+personal, C for community, or I for identity (OpenID, etc).
=cut
@@ -88,12 +94,14 @@ sub collect {
my @levels = ( '', 'unknown', values %$shortnames );
foreach my $k ( @keys ) {
- my ( $keyprefix, $keylevel ) = split( '-', $k );
+ my ( $keyprefix, $keylevel, $keytype ) = split( '-', $k );
$keylevel ||= '';
+ $keytype ||= '';
die "Unknown statkey $k for $class"
unless exists $key_to_days{$keyprefix}
- and grep { $_ eq $keylevel } @levels;
+ and grep { $_ eq $keylevel } @levels
+ and $keytype =~ /^[PCI]?$/;
$max_days = $key_to_days{$keyprefix}
if $max_days < $key_to_days{$keyprefix};
@@ -105,22 +113,25 @@ sub collect {
my $sth = $dbr->prepare( qq{
SELECT FLOOR((UNIX_TIMESTAMP()-timeactive)/86400) as days,
- accountlevel, COUNT(*)
+ accountlevel, journaltype, COUNT(*)
FROM clustertrack2
WHERE timeactive > UNIX_TIMESTAMP()-?
- GROUP BY days, accountlevel } );
+ GROUP BY days, accountlevel, journaltype } );
$sth->execute( $max_days*86400 );
- while ( my ( $days, $level, $active ) = $sth->fetchrow_array ) {
+ while ( my ( $days, $level, $type, $active ) = $sth->fetchrow_array ) {
$level = ( defined $level ) ? $shortnames->{$level} : 'unknown';
+ $type ||= '';
# which day interval(s) does this fall in?
# -- in last day, in last 7, in last 30?
foreach my $k ( @keys ) {
- my ( $keyprefix, $keylevel ) = split( '-', $k );
+ my ( $keyprefix, $keylevel, $keytype ) = split( '-', $k );
$keylevel ||= '';
+ $keytype ||= '';
if ( $days < $key_to_days{$keyprefix}
- && ( $keylevel eq $level || $keylevel eq '' ) ) {
+ && ( $keylevel eq $level || $keylevel eq '' )
+ && ( $keytype eq $type || $keytype eq '' ) ) {
$data{$k} += $active;
}
}
@@ -132,7 +143,9 @@ sub collect {
=head1 BUGS
-Bound to be some.
+Because not all account types are collected separately, only P/C/I, but the
+per-level stats count all types, the numbers don't add up. This is arguably
+a bug in the design.
=head1 AUTHORS
@@ -140,7 +153,7 @@ Pau Amma <[email protected]>, with some c
=head1 COPYRIGHT AND LICENSE
-Copyright (c) 2009 by Dreamwidth Studios, LLC.
+Copyright (c) 2009-2010 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
diff -r 9f08948db033 -r 4c2d441485d3 cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm Mon Jul 05 22:26:30 2010 +0800
+++ b/cgi-bin/LJ/User.pm Mon Jul 05 22:57:18 2010 +0800
@@ -238,7 +238,7 @@ sub create_personal {
}
}
# if we have initial friends for new accounts, add them.
- # TODO(mark): INITIAL_FRIENDS should be moved/renamed.
+ # FIXME(mark): INITIAL_FRIENDS should be moved/renamed.
foreach my $friend ( @LJ::INITIAL_FRIENDS ) {
my $friendid = LJ::get_userid( $friend )
or next;
@@ -271,8 +271,8 @@ sub create_personal {
}
# populate some default friends groups
- # TODO(mark): this should probably be removed or refactored, especially
since
- # editfriendgroups is dying/dead
+ # FIXME(mark): this should probably be removed or refactored, especially
+ # since editfriendgroups is dying/dead
# LJ::do_request(
# {
# 'mode' => 'editfriendgroups',
@@ -331,8 +331,8 @@ sub create_syndicated {
sub delete_and_purge_completely {
my $u = shift;
- # TODO: delete from user tables
- # TODO: delete from global tables
+ # FIXME: delete from user tables
+ # FIXME: delete from global tables
my $dbh = LJ::get_db_writer();
my @tables = qw(user useridmap reluser priv_map infohistory email
password);
@@ -388,7 +388,7 @@ sub who_invited {
sub get_previous_statusvis {
my $u = shift;
-
+
my $extra = $u->selectcol_arrayref(
"SELECT extra FROM userlog WHERE userid=? AND action='accountstatus'
ORDER BY logtime DESC",
undef, $u->userid );
@@ -723,7 +723,7 @@ sub last_updated {
my $ago_text = LJ::ago_text( $secondsold );
if ( $u->timeupdate ) {
- return LJ::Lang::ml( 'lastupdated.ago',
+ return LJ::Lang::ml( 'lastupdated.ago',
{ timestamp => $lastupdated, agotext => $ago_text });
} else {
return LJ::Lang::ml ( 'lastupdated.never' );
@@ -1037,7 +1037,7 @@ sub get_timeactive {
my $memkey = [$u->userid, "timeactive:" . $u->userid];
my $active;
unless (defined($active = LJ::MemCache::get($memkey))) {
- # TODO: die if unable to get handle? This was left verbatim from
+ # FIXME: die if unable to get handle? This was left verbatim from
# refactored code.
my $dbcr = LJ::get_cluster_def_reader($u) or return 0;
$active = $dbcr->selectrow_array("SELECT timeactive FROM clustertrack2
".
@@ -1164,7 +1164,7 @@ sub make_login_session {
unless ( $fake_login ) {
# activity for cluster usage tracking
LJ::mark_user_active($u, 'login');
-
+
# activity for global account number tracking
$u->note_activity('A');
}
@@ -2807,7 +2807,7 @@ sub sticky_entry {
unless ( $input ) {
$u->set_prop( sticky_entry => '' );
return 1;
- }
+ }
#also takes URL
my $ditemid;
if ( $input =~ m!/(\d+)\.html! ) {
@@ -2817,11 +2817,11 @@ sub sticky_entry {
} else {
return 0;
}
-
+
# Validate the entry
my $item = LJ::Entry->new( $u, ditemid => $ditemid );
return 0 unless $item && $item->valid;
-
+
$u->set_prop( sticky_entry => $ditemid );
return 1;
}
@@ -4137,7 +4137,7 @@ sub emails_visible {
push @emails, $u->email_raw;
} elsif ( $u->prop( 'opt_whatemailshow' ) eq "D" || $u->prop(
'opt_whatemailshow' ) eq "V" ) {
push @emails, $u->prop( 'opt_profileemail' );
- }
+ }
if ($LJ::USER_EMAIL && $useremail_cap) {
if ($whatemail eq "B" || $whatemail eq "V" || $whatemail eq "L") {
@@ -4292,7 +4292,7 @@ sub draft_text {
# 'start_date' - UTC date after which to look for match
# 'end_date' - UTC date before which to look for match
# 'return' - if 'count' just return the count
-# TODO: Add caching?
+# FIXME: Add caching?
# </LJFUNC>
sub get_post_ids {
my ($u, %opts) = @_;
@@ -4488,8 +4488,8 @@ sub set_draft_text {
push @methods, [ "append", $appending, 40 + length $new ];
}
- # TODO: prepending/middle insertion (the former being just the latter), as
well
- # appending, wihch we could then get rid of
+ # FIXME: prepending/middle insertion (the former being just the latter), as
+ # well as appending, wihch we could then get rid of
# try the methods in increasing order
foreach my $m (sort { $a->[2] <=> $b->[2] } @methods) {
@@ -6505,6 +6505,9 @@ sub mark_user_active {
return 0 unless $u; # do not auto-vivify $u
my $uid = $u->userid;
return 0 unless $uid && $u->clusterid;
+ # FIXME: return 1 instead? Callers don't use the return value, so I'm not
+ # sure whether 0 means "some error happened" or just "nothing done"
+ return 0 unless $u->is_personal || $u->is_community || $u->is_identity;
# Update the clustertrack2 table, but not if we've done it for this
# user in the last hour. if no memcache servers are configured
@@ -6514,9 +6517,12 @@ sub mark_user_active {
return 0 unless $u->writer;
my $active = time();
- $u->do( "REPLACE INTO clustertrack2 SET ".
- "userid=?, timeactive=?, clusterid=?, accountlevel=?", undef,
- $uid, $active, $u->clusterid,
DW::Pay::get_current_account_status( $uid ) ) or return 0;
+ $u->do( qq{ REPLACE INTO clustertrack2
+ SET userid=?, timeactive=?, clusterid=?,
+ accountlevel=?, journaltype=? },
+ undef, $uid, $active, $u->clusterid,
+ DW::Pay::get_current_account_status( $uid ), $u->journaltype )
+ or return 0;
my $memkey = [$u->userid, "timeactive:" . $u->userid];
LJ::MemCache::set($memkey, $active, 86400);
@@ -7586,7 +7592,7 @@ sub user_search_display {
# when the site is overloaded we don't always load the users
# we request.
next unless LJ::isu($u);
-
+
$ret .= "<div class='user-search-display'>";
$ret .= "<table style='height: 105px'><tr>";
@@ -7735,8 +7741,8 @@ sub rate_check {
# would this transaction go over the limit?
if ($sum + $count > $opp) {
- # TODO: optionally log to rateabuse, unless caller is doing it
themselves
- # somehow, like with the "loginstall" table.
+ # FIXME: optionally log to rateabuse, unless caller is doing it
+ # themselves somehow, like with the "loginstall" table.
return 0;
}
@@ -8275,7 +8281,7 @@ sub journal_base
}
}
}
-
+
if ( $LJ::ONLY_USER_VHOSTS ) {
my $rule = $u ? $LJ::SUBDOMAIN_RULES->{$u->journaltype} : undef;
$rule ||= $LJ::SUBDOMAIN_RULES->{P};
diff -r 9f08948db033 -r 4c2d441485d3 htdocs/admin/stats.bml
--- a/htdocs/admin/stats.bml Mon Jul 05 22:26:30 2010 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-<?_c
-#
-# admin/stats.bml
-#
-# Admin-level statistics
-#
-# Authors:
-# Afuna <[email protected]>
-#
-# Copyright (c) 2009 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'.
-#
-_c?><?page
-body<=
-<?_code
-{
- use strict;
- use warnings;
- use vars qw/ $title /;
-
- # translated/custom page title can go here
- $title = $ML{'/admin/index.bml.admin.stats.link'};
-
- my $remote = LJ::get_remote();
-
- return BML::ml( "admin.noprivserror", { numprivs => "1", needprivs =>
"<b>payments</b>"} )
- unless $LJ::IS_DEV_SERVER || ( $remote && $remote->has_priv(
"payments" ) );
-
- my $ret;
-
- use DW::StatStore;
- use DW::StatData;
- LJ::ModuleLoader::autouse_subclasses( 'DW::StatData' );
-
- # FIXME: refactor stuff common with stats/site.bml into... something. (A
LJ::Widget?)
- # FIXME: finish stripping
-
- # number of accounts, total
- my $accounts_by_type = DW::StatData::AccountsByType->load_latest(
DW::StatStore->get( "accounts" ) );
- my $total; # Used in paid account stats below
- if ( defined $accounts_by_type ) {
- $ret .= "<h2>Number of accounts</h2>";
- $ret .= "<ul>";
- $ret .= "<li><label>" . $ML{"/stats/site.bml.accounts.bytype.$_"} .
"</label> "
- . $accounts_by_type->value( $_ ) . "</li>"
- foreach qw/ total personal identity /;
- $ret .= "</ul>";
- $total = $accounts_by_type->value( 'total' );
- }
-
- # number of accounts, active
- my $active_accounts = DW::StatData::ActiveAccounts->load_latest(
DW::StatStore->get( "active" ) );
- my ( $active, $active_allpaid ); # Used in paid account stats below
- $ret .=
"<h2>$ML{'/stats/site.bml.active.title'}</h2><p>$ML{'/stats/site.bml.active.desc'}</p>";
-
- if ( defined $active_accounts ) {
- $ret .= "<ul>";
- $ret .= "<li>" . $ML{"/stats/site.bml.active.bytime.$_"} . " "
- . $active_accounts->value( $_ ) . "</li>"
- foreach qw/ active_1d active_7d active_30d /;
- $ret .= "</ul>";
- $active = $active_accounts->value( 'active_30d' );
- $active_allpaid = $active_accounts->value( 'active_30d-paid' )
- + $active_accounts->value( 'active_30d-premium' )
- + $active_accounts->value( 'active_30d-seed' );
- } else {
- $ret .= $ML{'/stats/site.bml.error.notavailable'};
- }
-
- # Paid accounts (by level), with % of total and active
- my $paid = DW::StatData::PaidAccounts->load_latest( DW::StatStore->get(
"paid" ) );
-
- $ret .= "<h2>$ML{'/stats/site.bml.paid.title'}</h2>";
- if ( defined $paid ) {
- $ret .= "<table><tr>";
- $ret .= "<th>" . $ML{"/stats/site.bml.paid.colhdr.$_"} . "</th>"
- foreach qw/ level number pct_total pct_active /;
- $ret .= "</tr>\n";
-
- foreach my $level ( qw( paid premium seed ) ) {
- $ret .= "<tr><th>" . $ML{"/stats/site.bml.paid.rowhdr.$level"} .
"</th>";
- my $n = $paid->value( $level ) || 0;
- $ret .= "<td class='stats'>$n</td>";
- $ret .= "<td class='stats'>"
- . ( defined $total ? int( 100 * $n / $total ) : "" )
- . "</td>";
- $ret .= "<td class='stats'>"
- . ( defined $active ? int( 100 * $n / $active ) : "" )
- . "</td></tr>\n";
- }
- $ret .= "<tr><th>$ML{'/stats/site.bml.paid.rowhdr.activepaid'}</th><td
class='stats'>";
- $ret .= $active_allpaid
- if defined $active_allpaid;
- $ret .=
"</td></tr><tr><th>$ML{'/stats/site.bml.paid.rowhdr.inactivepaid'}</th><td
class='stats'>";
-
- $ret .= $paid->value( 'total' ) - $active_allpaid
- if defined $active_allpaid;
- $ret .= "</td></tr></table>";
- } else {
- $ret .= $ML{'/stats/site.bml.error.notavailable'};
- }
-<<COMMENT;
-
-FIXME: remove this when you have implemented them all
-
-* Number of accounts, total (done)
-* Number of accounts active (done)
-* Number of paid accounts (by payment level) (done)
- -- as a percentage of total accounts (done)
- -- as a percentage of active accounts (done)
- -- number of active paid accounts (done)
- -- number of inactive paid accounts (done)
-* Number of payments in last 1d/2d/5d/7d/1m/3m/1y
- -- broken down by which payment level/payment item chosen
- -- and divided into new payments vs. renewals
- -- and expressed as a dollar amount taken in during that time
-* Number of lapsed paid accounts in last 1d/2d/5d/7d/1m/3m/1y
- -- and renewed within 7d/14d/1m
- -- and not renewed within 7d/14d/1m
- -- and as a percentage of total paid accounts
-* Percent churn over last 7d/1m/3m/1y
- -- (churn formula: total lapsed paid accounts that don't renew within 7d/total
-paid accounts * 100)
-* Number of paid accounts that were created via payment (no code)
-* Number of paid accounts that were created via code, then paid
- -- within 1d/2d/5d/7d/1m/3m/1y of creation
-* Total refunds issued within last 7d/1m/3m/1y
- -- with dollar amount refunded
- -- with fees added to dollar amount refunded
-* Total chargebacks/PayPal refunds within last 7d/1m/3m/1y
- -- with dollar amount charged back
- -- with fees added to dollar amount charged back
-COMMENT
- return $ret;
-}
-_code?>
-<=body
-title=><?_code return $title; _code?>
-page?>
diff -r 9f08948db033 -r 4c2d441485d3 htdocs/stats/site.bml
--- a/htdocs/stats/site.bml Mon Jul 05 22:26:30 2010 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-<?_c
-#
-# stats/site.bml
-#
-# New public statistics
-#
-# Authors:
-# Afuna <[email protected]>
-# Pau Amma <[email protected]>
-#
-# Copyright (c) 2009 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'.
-#
-_c?><?page
-body<=
-
-<?_code
-{
- use strict;
- use warnings;
- use vars qw/ $title /;
-
- use DW::StatStore;
- use DW::StatData;
-
- LJ::ModuleLoader::autouse_subclasses( 'DW::StatData' );
-
- LJ::need_res( 'stc/sitestats.css' );
- LJ::set_active_crumb( 'sitestats' );
-
- $title = BML::ml( '.title', { sitenameshort => $LJ::SITENAMESHORT } );
-
- # number of accounts (total+by type)
- my $accounts_by_type = DW::StatData::AccountsByType->load_latest(
DW::StatStore->get( "accounts" ) );
- my $total; # Used in paid account stats below
- my $ret = "<h2>$ML{'.accounts.title'}</h2>";
-
- if ( defined $accounts_by_type ) {
- $ret .= "<ul>";
- $ret .= "<li>" . $ML{".accounts.bytype.$_"} . " "
- . $accounts_by_type->value( $_ ) . "</li>"
- foreach qw/ total personal identity /;
- $ret .= "</ul>";
- $total = $accounts_by_type->value( 'total' );
- } else {
- $ret .= $ML{'.error.notavailable'};
- }
-
- # number of active accounts (by time since last active)
- my $active_accounts = DW::StatData::ActiveAccounts->load_latest(
DW::StatStore->get( "active" ) );
- my ( $active, $active_allpaid ); # Used in paid account stats below
- $ret .= "<h2>$ML{'.active.title'}</h2><p>$ML{'.active.desc'}</p>";
-
- if ( defined $active_accounts ) {
- $ret .= "<ul>";
- $ret .= "<li>" . $ML{".active.bytime.$_"} . " "
- . $active_accounts->value( $_ ) . "</li>"
- foreach qw/ active_1d active_7d active_30d /;
- $ret .= "</ul>";
- $active = $active_accounts->value( 'active_30d' );
- $active_allpaid = $active_accounts->value( 'active_30d-paid' )
- + $active_accounts->value( 'active_30d-premium' )
- + $active_accounts->value( 'active_30d-seed' );
- } else {
- $ret .= $ML{'.error.notavailable'};
- }
-
- # Paid accounts (by level), with % of total and active
- my $paid = DW::StatData::PaidAccounts->load_latest( DW::StatStore->get(
"paid" ) );
- $ret .= "<h2>$ML{'.paid.title'}</h2>";
-
- if ( defined $paid ) {
- $ret .= "<table class='stats-matrix'><tr>";
- $ret .= "<th>" . $ML{".paid.colhdr.$_"} . "</th>"
- foreach qw/ level number pct_total pct_active /;
- $ret .= "</tr>\n";
- foreach my $level ( qw( paid premium seed ) ) {
- $ret .= "<tr><th>" . $ML{".paid.rowhdr.$level"} . "</th>";
- my $n = $paid->value( $level ) || 0;
- $ret .= "<td class='stats'>$n</td>";
- $ret .= "<td class='stats'>"
- . ( defined $total ? int( 100 * $n / $total ) : "" )
- . "</td>";
- $ret .= "<td class='stats'>"
- . ( defined $active ? int( 100 * $n / $active ) : "" )
- . "</td></tr>\n";
- }
- $ret .= "<tr><th>$ML{'.paid.rowhdr.activepaid'}</th><td
class='stats'>";
- $ret .= $active_allpaid
- if defined $active_allpaid;
- $ret .= "</td></tr><tr><th>$ML{'.paid.rowhdr.inactivepaid'}</th><td
class='stats'>";
- $ret .= $paid->value( 'total' ) - $active_allpaid
- if defined $active_allpaid;
- $ret .= "</td></tr></table>";
- } else {
- $ret .= $ML{'.error.notavailable'};
- }
-
- return $ret;
-}
-_code?>
-
-<=body
-title=><?_code return $title; _code?>
-page?>
diff -r 9f08948db033 -r 4c2d441485d3 htdocs/stats/site.bml.text
--- a/htdocs/stats/site.bml.text Mon Jul 05 22:26:30 2010 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-;; -*- coding: utf-8 -*-
-
-.accounts.bytype.total=Total:
-
-.accounts.bytype.personal=Personal (users):
-
-.accounts.bytype.identity=External identity (OpenID, etc.):
-
-.accounts.title=Number of accounts
-
-.active.bytime.active_1d=Last 24 hours:
-
-.active.bytime.active_7d=Last 7 days:
-
-.active.bytime.active_30d=Last 30 days:
-
-.active.desc=These are accounts who logged in, posted an entry, commented, or
edited a comment during the period indicated. For entries posted to
communities, both the poster and the community are counted.
-
-.active.title=Active accounts
-
-.error.notavailable=(Sorry, those statistics aren't available right now.)
-
-.paid.colhdr.level=Level
-
-.paid.colhdr.number=Number
-
-.paid.colhdr.pct_total=% of total accounts
-
-.paid.colhdr.pct_active=% of active accounts
-
-.paid.rowhdr.activepaid=Active paid/premium/seed
-
-.paid.rowhdr.inactivepaid=Inactive paid/premium/seed
-
-.paid.rowhdr.paid=Paid
-
-.paid.rowhdr.premium=Premium
-
-.paid.rowhdr.seed=Seed
-
-.paid.title=Paid accounts
-
-.title=[[sitenameshort]] Site Statistics
diff -r 9f08948db033 -r 4c2d441485d3 views/admin/stats.tt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/views/admin/stats.tt Mon Jul 05 22:57:18 2010 +0800
@@ -0,0 +1,53 @@
+[%#
+admin/stats.tt
+
+Admin-level statistics
+
+Authors:
+ Afuna <[email protected]>
+ Pau Amma <[email protected]>
+
+Copyright (c) 2009-2010 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'.
+%]
+
+[% dw.need_res( 'stc/sitestats.css' ) %]
+[% scope = dw.ml_scope( ); CALL dw.ml_scope( '/stats/site.tt' );
+ INCLUDE stats/site.tt; CALL dw.ml_scope( scope ); %]
+[% sections.title = '.title' | ml( sitenameshort => site.nameshort ) %]
+
+[%#
+
+FIXME: remove this when you have implemented them all
+
+* Number of accounts, total (done)
+* Number of accounts active (done)
+* Number of paid accounts (by payment level) (done)
+ -- as a percentage of total accounts (done)
+ -- as a percentage of active accounts (done)
+ -- number of active paid accounts (done)
+ -- number of inactive paid accounts (done)
+* Number of payments in last 1d/2d/5d/7d/1m/3m/1y
+ -- broken down by which payment level/payment item chosen
+ -- and divided into new payments vs. renewals
+ -- and expressed as a dollar amount taken in during that time
+* Number of lapsed paid accounts in last 1d/2d/5d/7d/1m/3m/1y
+ -- and renewed within 7d/14d/1m
+ -- and not renewed within 7d/14d/1m
+ -- and as a percentage of total paid accounts
+* Percent churn over last 7d/1m/3m/1y
+ -- (churn formula: total lapsed paid accounts that don't renew within 7d/total
+paid accounts * 100)
+* Number of paid accounts that were created via payment (no code)
+* Number of paid accounts that were created via code, then paid
+ -- within 1d/2d/5d/7d/1m/3m/1y of creation
+* Total refunds issued within last 7d/1m/3m/1y
+ -- with dollar amount refunded
+ -- with fees added to dollar amount refunded
+* Total chargebacks/PayPal refunds within last 7d/1m/3m/1y
+ -- with dollar amount charged back
+ -- with fees added to dollar amount charged back
+%]
diff -r 9f08948db033 -r 4c2d441485d3 views/admin/stats.tt.text
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/views/admin/stats.tt.text Mon Jul 05 22:57:18 2010 +0800
@@ -0,0 +1,3 @@
+;; -*- coding: utf-8 -*-
+
+.title=Business Statistics
diff -r 9f08948db033 -r 4c2d441485d3 views/stats/site.tt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/views/stats/site.tt Mon Jul 05 22:57:18 2010 +0800
@@ -0,0 +1,87 @@
+[%#
+stats/site.tt
+
+New public statistics
+
+Authors:
+ Afuna <[email protected]>
+ Pau Amma <[email protected]>
+
+Copyright (c) 2009-2010 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'.
+%]
+
+[% dw.need_res( 'stc/sitestats.css' ) %]
+[% sections.title = '.title' | ml( sitenameshort => site.nameshort ) %]
+
+<p>[% '.note.roundedtonearesttenthofpct' | ml %]</p>
+
+<h2>[% '.accounts.title' | ml %]</h2>
+
+[%# number of accounts (total+by type) %]
+[% IF accounts_by_type.defined %]
+ <ul>
+ [% FOREACH t = [ 'total' 'personal' 'identity' ] %]
+ <li>[% ".accounts.bytype.$t" | ml %] [% accounts_by_type.$t %]</li>
+ [% END %]
+ </ul>
+ [% total_pc = accounts_by_type.personal + accounts_by_type.community %]
+[% ELSE %]
+ [% '.error.notavailable' | ml %]
+[% END %]
+
+[%# number of active accounts (by time since last active) %]
+<h2>[% '.active.title' | ml %]</h2><p>[% '.active.desc' | ml %]</p>
+[% IF active_accounts.defined %]
+ <ul>
+ [% FOREACH t = [ 'active_1d' 'active_7d' 'active_30d' ] %]
+ <li>[% ".active.bytime.$t" | ml %] [% active_accounts.$t %]</li>
+ [% END %]
+ </ul>
+ [% active_pc = active_accounts.active_30d_free_P
+ + active_accounts.active_30d_paid_P
+ + active_accounts.active_30d_premium_P
+ + active_accounts.active_30d_seed_P
+ + active_accounts.active_30d_free_C
+ + active_accounts.active_30d_paid_C
+ + active_accounts.active_30d_premium_C
+ + active_accounts.active_30d_seed_C %]
+ [% active_allpaid = active_accounts.active_30d_paid
+ + active_accounts.active_30d_premium
+ + active_accounts.active_30d_seed %]
+[% ELSE %]
+ [% '.error.notavailable' | ml %]
+[% END %]
+
+[%# Paid accounts (by level), with % of total (P+C) and active %]
+<h2>[% '.paid.title' | ml %]</h2>
+[% IF paid.defined %]
+ <table class='stats-matrix'><tr>
+ [% FOREACH h = [ 'level' 'number' 'pct_total' 'pct_active' ] %]
+ <th>[% ".paid.colhdr.$h" | ml %]</th>
+ [% END %]
+ </tr>
+ [% allpaid = 0 %]
+ [% FOREACH level = [ 'paid' 'premium' 'seed' ] %]
+ <tr><th>[% ".paid.rowhdr.$level" | ml %]</th>
+ [% n = paid.$level.defined ? paid.$level : 0 %]
+ [% allpaid = allpaid + n %]
+ <td class='stats'>[% n %]</td>
+ <td class='stats'>
+ [%- 100 * n / total_pc | format "%.1f" IF total_pc != 0 %]</td>
+ <td class='stats'>
+ [%- 100 * n / active_pc | format "%.1f" IF active_pc != 0 %]</td>
+ </tr>
+ [% END %]
+ <tr><th>[% '.paid.rowhdr.activepaid' | ml %]</th><td class='stats'>
+ [% active_allpaid IF active_allpaid.defined %]
+ </td></tr><tr><th>[% '.paid.rowhdr.inactivepaid' | ml %]</th>
+ <td class='stats'>
+ [% allpaid - active_allpaid IF active_allpaid.defined %]
+ </td></tr></table>
+[% ELSE %]
+ [% '.error.notavailable' | ml %]
+[% END %]
diff -r 9f08948db033 -r 4c2d441485d3 views/stats/site.tt.text
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/views/stats/site.tt.text Mon Jul 05 22:57:18 2010 +0800
@@ -0,0 +1,45 @@
+;; -*- coding: utf-8 -*-
+
+.accounts.bytype.total=Total:
+
+.accounts.bytype.personal=Personal (users):
+
+.accounts.bytype.identity=External identity (OpenID, etc.):
+
+.accounts.title=Number of accounts
+
+.active.bytime.active_1d=Last 24 hours:
+
+.active.bytime.active_7d=Last 7 days:
+
+.active.bytime.active_30d=Last 30 days:
+
+.active.desc=These are accounts who logged in, posted an entry, commented, or
edited a comment during the period indicated. For entries posted to
communities, both the poster and the community are counted.
+
+.active.title=Active accounts
+
+.error.notavailable=(Sorry, those statistics aren't available right now.)
+
+.note.roundedtonearesttenthofpct=Note: all percentages are rounded (up or
down) to the nearest 0.1%.
+
+.paid.colhdr.level=Level
+
+.paid.colhdr.number=Number
+
+.paid.colhdr.pct_total=% of total accounts
+
+.paid.colhdr.pct_active=% of active accounts
+
+.paid.rowhdr.activepaid=Active paid/premium/seed
+
+.paid.rowhdr.inactivepaid=Inactive paid/premium/seed
+
+.paid.rowhdr.paid=Paid
+
+.paid.rowhdr.premium=Premium
+
+.paid.rowhdr.seed=Seed
+
+.paid.title=Paid accounts
+
+.title=[[sitenameshort]] Site Statistics
_______________________________________________
dw-changelog mailing list
[email protected]
http://lists.dwscoalition.org/mailman/listinfo/dw-changelog