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-04-25 08:50 pm

[dw-free] Business statistics

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

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

More business statistics.

Patch by [personal profile] afuna.

Files modified:
  • cgi-bin/DW/StatData.pm
  • cgi-bin/DW/StatData/AccountsByType.pm
  • cgi-bin/DW/StatStore.pm
  • htdocs/admin/index.bml
  • htdocs/admin/index.bml.text
--------------------------------------------------------------------------------
diff -r 7d8ff1c41e7c -r a9f42c02a0b5 cgi-bin/DW/StatData.pm
--- a/cgi-bin/DW/StatData.pm	Sat Apr 25 20:45:19 2009 +0000
+++ b/cgi-bin/DW/StatData.pm	Sat Apr 25 20:50:34 2009 +0000
@@ -19,13 +19,29 @@ DW::StatData - Abstract superclass for s
 
 =head1 SYNOPSIS
 
-  # examples of function usage
+    use DW::StatStore;  # to retrieve stored statistics from the database
+    use DW::StatData;   # to serve as an API for gathering the data
+    # load all the available DW::StatData::* submodules
+    LJ::ModuleLoader::autouse_subclasses( 'DW::StatData' );
+
+    # get the latest set of pony statistics
+    my $ponies = DW::StatData::Ponies->load_latest( DW::StatStore->get( "ponies" ) );
+
+    # how many ponies are currently sparkly?
+    $ret .= $ponies->value( "sparkly" );
+
+    # load statistics for ponies over the past 30 days
+    my $ponies_history = DW::StatData::Ponies->load( DW::StatStore->get( "ponies", 30 ) );
+    
+    # get the number of sparkly ponies 15 days ago
+    $ret .= $ponies_history->{15}->value( "sparkly" );
 
 =cut
 
 use strict;
 use warnings;
 use Carp qw( confess );
+use POSIX qw( floor );
 
 use fields qw( data );
 
@@ -99,17 +115,66 @@ sub collect {
     confess "'collect' should be implemented by subclass"; 
 }
 
-=head2 C<< $class->new( $category, $name, $key1 => $value, ... ) >>
+=head2 C<< $class->new( $key1 => $value, ... ) >>
 
-Initialize
+Initialize this row of stat data, given a hash of statkey-value pairs
 
 =cut
 
 sub new {
-    return fields::new( $_[0] );
+    my ( $self, %data ) = @_;
+    
+    unless ( ref $self ) {
+        $self = fields::new( $self );
+    }
+    while ( my ( $k, $v ) = each %data ) {
+        $self->{$k} = $v;
+    }
+
+    return $self;
+}
+
+=head2 C<< $class->load( { $timestampA => { $key1 => $value1, ... }, $timestampB => ... } ) >>
+
+Given a hashref of timestamps mapped to data rows, returns a hashref of DW::StatData::* objects. Input timestamps are time that row of statistics was collected; returned hash keys are how many days ago this data was collected.
+
+=cut
+
+sub load {
+    my ( $class, $rows ) = @_;
+    my $days_ago = sub {
+        my $timestamp = $_[0];
+        return floor( ( time() - $timestamp ) / ( 24 * 60 * 60 ) );
+    };
+
+    my $ret;
+    while ( my ( $timestamp, $data )  = each %$rows ) {
+        # does not protect against multiple versions of the data collected on the same day?
+        $ret->{$days_ago->( $timestamp )} = $class->new( data => $data );
+    }
+    return $ret;
+}
+
+=head2 C<< $class->load_latest( ... ) >>
+
+Accepts the same arguments as $class->load, but returns only the latest row
+
+=cut
+sub load_latest {
+    my $self = shift;
+    my $rows = $self->load( @_ );
+    my @sorted;
+    if ( %$rows ) {
+        @sorted = sort { $a <=> $b } keys %$rows;
+        return $rows->{$sorted[0]};
+    }
+
+    return undef;
 }
 
 =head1 BUGS
+
+Multiple versions of the data collected on the same day will be collapsed into one day.
 
 =head1 AUTHORS
 
diff -r 7d8ff1c41e7c -r a9f42c02a0b5 cgi-bin/DW/StatData/AccountsByType.pm
--- a/cgi-bin/DW/StatData/AccountsByType.pm	Sat Apr 25 20:45:19 2009 +0000
+++ b/cgi-bin/DW/StatData/AccountsByType.pm	Sat Apr 25 20:50:34 2009 +0000
@@ -61,14 +61,15 @@ sub collect {
     return \%data;
 }
 
-=head2 C<< $class->data >>
+=head2 C<< $self->data >>
  
 =cut
 
 sub data {
     my $self = shift;
-    my $data = $self->data;
-    $data{total} += $data{$_} foreach keys $data;
+    my $data = $self->{data};
+    $data->{total} = 0;
+    $data->{total} += $data->{$_} foreach keys %$data;
     return $data;
 }
 
diff -r 7d8ff1c41e7c -r a9f42c02a0b5 cgi-bin/DW/StatStore.pm
--- a/cgi-bin/DW/StatStore.pm	Sat Apr 25 20:45:19 2009 +0000
+++ b/cgi-bin/DW/StatStore.pm	Sat Apr 25 20:50:34 2009 +0000
@@ -7,6 +7,7 @@
 # Authors:
 #      Mark Smith <mark@dreamwidth.org>
 #      Pau Amma <pauamma@cpan.org>
+#      Afuna <coder.dw@afunamatata.com>
 #
 # Copyright (c) 2009 by Dreamwidth Studios, LLC.
 #
@@ -23,7 +24,12 @@ DW::StatStore -- Statistics store update
   # Add timestamped line to pony stats
   DW::StatStore->add( 'ponies', total => 34738, sparkly => 45 )
       or die "Some error happened";
-  # FIXME: define retrieval method(s)
+
+  # get pony stats from one day ago
+  DW::StatStore->get( 'ponies' );
+  
+  # get pony stats over the last 30 days
+  DW::StatStore->get( 'ponies', 30 );
 
 =cut
 
@@ -67,6 +73,39 @@ sub add {
     return 1;
 }
 
+=head2 C<< $class->get( $catkey, $statkeys, $howmany ) >>
+
+Get statistics data over the past $numdays for all keys under this category. Catkey is a string. $numdays defaults to 1.
+
+=cut
+
+sub get {
+    my ( $class, $catkey, $numdays ) = @_;
+
+    my $catkey_id = $class->to_id( $catkey );
+    return undef unless $catkey_id;
+
+    $numdays ||= 1;
+    my $timestamp = time() - $numdays * 24 * 60 * 60;
+
+    my $dbr = LJ::get_db_reader()
+        or return undef;
+
+    my $sth = $dbr->prepare( "SELECT category_id, key_id, insert_time, value " .
+                    "FROM site_stats " .
+                    "WHERE category_id = ? AND insert_time >= ? ");
+    $sth->execute( $catkey_id, $timestamp );
+
+    my %ret;
+    while ( my $data = $sth->fetchrow_hashref ) {
+        my $key = $class->to_key( $data->{key_id} )
+            or next;
+
+        $ret{$data->{insert_time}}->{$key} = $data->{value};
+    }
+    return \%ret;
+}
+
 =head2 C<< $class->to_id( $key ) >>
 
 Internal: converts key to an id. Key can be either a cat key or a stat key.
@@ -76,6 +115,16 @@ Autocreated on first reference.
 
 sub to_id {
     return $_[0]->typemap->class_to_typeid( $_[1] );
+}
+
+=head2 C<< $class->to_key( $id ) >>
+
+Internal: converts id to a key. Errors hard if you give an invalid id.
+
+=cut
+
+sub to_key {
+    return $_[0]->typemap->typeid_to_class( $_[1] );
 }
 
 =head2 C<< $class->typemap >>
@@ -96,13 +145,13 @@ 1;
 
 =head1 BUGS
 
-There's no API for retrieving stat data.
-
 =head1 AUTHORS
 
 Mark Smith <mark@dreamwidth.org>
 
 Pau Amma <pauamma@cpan.org>
+
+Afuna <coder.dw@afunamatata.com>
 
 =head1 COPYRIGHT AND LICENSE
 
diff -r 7d8ff1c41e7c -r a9f42c02a0b5 htdocs/admin/index.bml
--- a/htdocs/admin/index.bml	Sat Apr 25 20:45:19 2009 +0000
+++ b/htdocs/admin/index.bml	Sat Apr 25 20:50:34 2009 +0000
@@ -91,6 +91,8 @@ body<=
             } ] ],
         [ 'spamreports.bml',
             '<?_ml .admin.spamreports.link _ml?>', '<?_ml .admin.spamreports.text _ml?>', [ 'siteadmin:spamreports', 'siteadmin:*' ] ],
+        [ 'stats.bml',
+            '<?_ml .admin.stats.link _ml?>', '<?_ml .admin.stats.text _ml?>', [ 'payments' ] ],
         [ 'statushistory.bml',
             '<?_ml .admin.statushistory.link _ml?>', '<?_ml .admin.statushistory.text _ml?>', [ 'historyview', sub {
                 return ( $LJ::IS_DEV_SERVER, "<?_ml .devserver _ml?>" );
diff -r 7d8ff1c41e7c -r a9f42c02a0b5 htdocs/admin/index.bml.text
--- a/htdocs/admin/index.bml.text	Sat Apr 25 20:45:19 2009 +0000
+++ b/htdocs/admin/index.bml.text	Sat Apr 25 20:50:34 2009 +0000
@@ -74,6 +74,9 @@
 .admin.spamreports.link=Spam Reports
 .admin.spamreports.text=View and handle reports of spam.
 
+.admin.stats.link=Business Statistics
+.admin.stats.text=Detailed breakdown of business statistics
+
 .admin.statushistory.link=Statushistory
 .admin.statushistory.text=Shows you a user's statushistory.
 
--------------------------------------------------------------------------------

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
No Subject Icon Selected
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org