mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-11-25 05:15 am

[dw-free] Business statistics

[commit: http://hg.dwscoalition.org/dw-free/rev/c557c814bf14]

http://bugs.dwscoalition.org/show_bug.cgi?id=124

Add statistics gathering to break down activity by paid account type.

Patch by [personal profile] pauamma.

Files modified:
  • cgi-bin/DW/Pay.pm
  • cgi-bin/DW/StatData/ActiveAccounts.pm
  • cgi-bin/DW/StatData/PaidAccounts.pm
--------------------------------------------------------------------------------
diff -r f1b881e72e8d -r c557c814bf14 cgi-bin/DW/Pay.pm
--- a/cgi-bin/DW/Pay.pm	Wed Nov 25 04:18:51 2009 +0000
+++ b/cgi-bin/DW/Pay.pm	Wed Nov 25 05:14:57 2009 +0000
@@ -38,7 +38,8 @@ use constant ERR_TEMP => 2;
 # RETURN: 1/0 if the type is a valid type
 #
 sub type_is_valid {
-    return 1 if $LJ::CAP{$_[0]} && $LJ::CAP{$_[0]}->{_account_type};
+    return 1 if $LJ::CAP{$_[0]} && $LJ::CAP{$_[0]}->{_account_type}
+        && $LJ::CAP{$_[0]}->{_visible_name};
     return 0;
 }
 
@@ -52,9 +53,41 @@ sub type_is_valid {
 # RETURN: string name of type, else undef
 #
 sub type_name {
-    confess 'invalid typeid'
+    confess "invalid typeid $_[0]"
         unless DW::Pay::type_is_valid( $_[0] );
     return $LJ::CAP{$_[0]}->{_visible_name};
+}
+
+################################################################################
+# DW::Pay::type_shortname
+#
+# ARGUMENTS: typeid
+#
+#   typeid      required    the id of the type we're checking
+#
+# RETURN: string short name of type, else undef
+#
+sub type_shortname {
+    confess "invalid typeid $_[0]"
+        unless DW::Pay::type_is_valid( $_[0] );
+    return $LJ::CAP{$_[0]}->{_account_type};
+}
+
+################################################################################
+# DW::Pay::all_shortnames
+#
+# ARGUMENTS: (none)
+#
+# RETURN: { typeid => shortname } hashref
+#
+sub all_shortnames {
+    my %names;
+    while ( my ( $typeid, $data ) = each %LJ::CAP ) {
+        # Avoid calling DW::Pay::type_is_valid a zillion times for the same typeid
+        $names{$typeid} = $data->{_account_type}
+            if DW::Pay::type_is_valid( $typeid );
+    }
+    return \%names;
 }
 
 ################################################################################
@@ -173,7 +206,7 @@ sub get_account_type {
     confess 'account has no valid typeid'
         unless $typeid && $typeid > 0;
     confess "typeid $typeid not a valid account level"
-        unless $LJ::CAP{$typeid} && $LJ::CAP{$typeid}->{_account_type};
+        unless DW::Pay::type_is_valid( $typeid );
     return $LJ::CAP{$typeid}->{_account_type};
 }
 
@@ -191,7 +224,7 @@ sub get_account_type_name {
     confess 'account has no valid typeid'
         unless $typeid && $typeid > 0;
     confess "typeid $typeid not a valid account level"
-        unless $LJ::CAP{$typeid} && $LJ::CAP{$typeid}->{_visible_name};
+        unless DW::Pay::type_is_valid( $typeid );
     return $LJ::CAP{$typeid}->{_visible_name};
 }
 
@@ -679,3 +712,4 @@ sub get_db_writer {
 }
 
 1;
+
diff -r f1b881e72e8d -r c557c814bf14 cgi-bin/DW/StatData/ActiveAccounts.pm
--- a/cgi-bin/DW/StatData/ActiveAccounts.pm	Wed Nov 25 04:18:51 2009 +0000
+++ b/cgi-bin/DW/StatData/ActiveAccounts.pm	Wed Nov 25 05:14:57 2009 +0000
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 #
 # DW::StatData::ActiveAccounts - Active accounts, by #days since last active
+# and account level
 #
 # Authors:
 #      Pau Amma <pauamma@cpan.org>
@@ -38,7 +39,16 @@ use base 'DW::StatData';
 
 sub category { "active" }
 sub name     { "Active Accounts" }
-sub keylist  { [ qw( active_1d active_7d active_30d ) ] }
+
+my %key_to_days = ( active_1d => 1, active_7d => 7, active_30d => 30 );
+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;
+    }
+    return \@keys;
+}
 
 =head1 API
 
@@ -48,33 +58,45 @@ Collects data for the following keys:
 
 =over
 
-=item active_1d
+=item active_1d, active_1d-I<< level name >>
 
-Number of accounts active in the last 24 hours
+Number of accounts active in the last 24 hours (total and for each account
+level)
 
-=item active_7d
+=item active_7d, active_7d-I<< level name >>
 
-Number of accounts active in the last 168 (7*24) hours
+Number of accounts active in the last 168 (7*24) hours (total and for each
+account level)
 
-=item active_30d
+=item active_30d, active_30d-I<< level name >>
 
-Number of accounts active in the last 720 (30*24) hours
+Number of accounts active in the last 720 (30*24) hours (total and for each
+account level)
 
 =back
 
+In the above, I<< level name >> is any of the account level names returned by
+C<< DW::Pay::all_shortnames >> or "unknown".
+
 =cut
 
-my %key_to_days = ( active_1d => 1, active_7d => 7, active_30d => 30 );
 sub collect {
     my ( $class, @keys ) = @_;
     my $max_days = 0;
     my %data;
+    my $shortnames = DW::Pay::all_shortnames();
+    my @levels = ( '', 'unknown', values %$shortnames );
 
     foreach my $k ( @keys ) {
+        my ( $keyprefix, $keylevel ) = split( '-', $k );
+        $keylevel ||= '';
+
         die "Unknown statkey $k for $class"
-            unless exists $key_to_days{$k};
-        $max_days = $key_to_days{$k}
-            if $max_days < $key_to_days{$k};
+            unless exists $key_to_days{$keyprefix}
+                   and grep { $_ eq $keylevel } @levels;
+
+        $max_days = $key_to_days{$keyprefix}
+            if $max_days < $key_to_days{$keyprefix};
         $data{$k} = 0;
     }
 
@@ -82,17 +104,25 @@ sub collect {
         my ( $cid, $dbr ) = @_; # $cid isn't used
 
         my $sth = $dbr->prepare( qq{
-            SELECT FLOOR((UNIX_TIMESTAMP()-timeactive)/86400) as days, COUNT(*)
+            SELECT FLOOR((UNIX_TIMESTAMP()-timeactive)/86400) as days,
+                   accountlevel, COUNT(*)
             FROM clustertrack2
-            WHERE timeactive > UNIX_TIMESTAMP()-? GROUP BY days } );
+            WHERE timeactive > UNIX_TIMESTAMP()-?
+            GROUP BY days, accountlevel } );
         $sth->execute( $max_days*86400 );
 
-        while ( my ( $days, $active ) = $sth->fetchrow_array ) {
+        while ( my ( $days, $level, $active ) = $sth->fetchrow_array ) {
+            $level = ( defined $level ) ? $shortnames->{$level} : 'unknown';
 
             # which day interval(s) does this fall in?
             # -- in last day, in last 7, in last 30?
             foreach my $k ( @keys ) {
-                $data{$k} += $active if $days < $key_to_days{$k};
+                my ( $keyprefix, $keylevel ) = split( '-', $k );
+                $keylevel ||= '';
+                if ( $days < $key_to_days{$keyprefix}
+                     && ( $keylevel eq $level || $keylevel eq '' ) ) {
+                    $data{$k} += $active;
+                }
             }
         }
     } );
diff -r f1b881e72e8d -r c557c814bf14 cgi-bin/DW/StatData/PaidAccounts.pm
--- a/cgi-bin/DW/StatData/PaidAccounts.pm	Wed Nov 25 04:18:51 2009 +0000
+++ b/cgi-bin/DW/StatData/PaidAccounts.pm	Wed Nov 25 05:14:57 2009 +0000
@@ -35,11 +35,12 @@ sub keylist  {
 sub keylist  {
     my @account_type_keys;
     my $default_typeid = DW::Pay::default_typeid();
+    my $shortnames = DW::Pay::all_shortnames();
 
-    foreach my $typeid ( keys %LJ::CAP ) {
+    while ( my ( $typeid, $name ) = each %$shortnames ) {
         next if $typeid == $default_typeid;
 
-        push @account_type_keys, $LJ::CAP{$typeid}->{_account_type} if $LJ::CAP{$typeid}->{_account_type};
+        push @account_type_keys, $name;
     }
 
     return \@account_type_keys;
@@ -71,19 +72,16 @@ sub collect {
 
     my $dbslow = LJ::get_dbh( 'slow' ) or die "Can't get slow role";
 
+    my $default_typeid = DW::Pay::default_typeid();
     my $sth = $dbslow->prepare( qq{
-        SELECT typeid, count(*) FROM dw_paidstatus GROUP BY typeid
+        SELECT typeid, count(*) FROM dw_paidstatus WHERE typeid != ? GROUP BY typeid
     } );
-    $sth->execute;
+    $sth->execute( $default_typeid );
 
-    my $default_typeid = DW::Pay::default_typeid();
     while ( my ( $typeid, $active ) = $sth->fetchrow_array ) {
-        next if $typeid == $default_typeid;
-
-        my $account_type = $LJ::CAP{$typeid}->{_account_type};
-        next unless defined $account_type and exists $data{$account_type};
-
-        $data{$account_type} = $active;
+        next unless DW::Pay::type_is_valid( $typeid );
+        my $account_type = DW::Pay::type_shortname( $typeid );
+        $data{$account_type} = $active if exists $data{$account_type};
     }
 
     return \%data;
@@ -92,8 +90,6 @@ sub collect {
 =head1 BUGS
 
 Trying to get the number of free accounts from dw_paidstatus will return an inaccurate number, because that only counts accounts which were paid at some point. So we do not collect stats for the default_typeid, which are free accounts for Dreamwidth. This makes assumptions, but I think not too out of line. 
-
-Needs to refactor more of the logic into DW::Pay (or some kind of BusinessRule or hook, to take care of site-specific logic)
 
 =head1 AUTHORS
 
--------------------------------------------------------------------------------