| 1 | (=PAGE |
|---|
| 2 | TITLE=>Statistics |
|---|
| 3 | BODY<= |
|---|
| 4 | |
|---|
| 5 | (=H1 (=SITENAME=) Statistics H1=) |
|---|
| 6 | (=P |
|---|
| 7 | The following statistics may be interesting for some of you. Note that for speed, most of this page is only updated every 24 hours. However, certain parts are live. Raw data can be picked up <A HREF="stats/">here</A>. |
|---|
| 8 | P=) |
|---|
| 9 | (=HR=) |
|---|
| 10 | |
|---|
| 11 | (=_CODE |
|---|
| 12 | |
|---|
| 13 | my $ret = ""; |
|---|
| 14 | |
|---|
| 15 | my $dbs = LJ::get_dbs(); |
|---|
| 16 | my $dbh = $dbs->{'dbh'}; |
|---|
| 17 | my $dbr = $dbs->{'reader'}; |
|---|
| 18 | |
|---|
| 19 | $now = time(); |
|---|
| 20 | %stat = (); |
|---|
| 21 | |
|---|
| 22 | $sth = $dbr->prepare("SELECT statcat, statkey, statval FROM stats WHERE statcat IN ('userinfo', 'client', 'age', 'gender', 'account')"); |
|---|
| 23 | $sth->execute; |
|---|
| 24 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 25 | $stat{$_->{'statcat'}}->{$_->{'statkey'}} = $_->{'statval'}; |
|---|
| 26 | } |
|---|
| 27 | |
|---|
| 28 | unless (%stat) { |
|---|
| 29 | return "(=H1 Sorry... H1=)(=P No statistics are available. If you're the administrator for this site, run <b>ljmaint.pl genstats</b>, or ideally, put it in cron to run nightly. P=)"; |
|---|
| 30 | } |
|---|
| 31 | |
|---|
| 32 | $sth = $dbr->prepare("SELECT c.item, s.statval FROM stats s, codes c WHERE c.type='country' AND s.statcat='country' AND s.statkey=c.code ORDER BY s.statval DESC LIMIT 15"); |
|---|
| 33 | $sth->execute; |
|---|
| 34 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 35 | $stat{'country'}->{$_->{'item'}} = $_->{'statval'}; |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | $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"); |
|---|
| 39 | $sth->execute; |
|---|
| 40 | while ($_ = $sth->fetchrow_hashref) { |
|---|
| 41 | $stat{'state'}->{$_->{'item'}} = $_->{'statval'}; |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | $total = $stat{'userinfo'}->{'total'}+0; |
|---|
| 45 | $usedever = $stat{'userinfo'}->{'updated'}+0; |
|---|
| 46 | $used30 = $stat{'userinfo'}->{'updated_last30'}+0; |
|---|
| 47 | $used7 = $stat{'userinfo'}->{'updated_last7'}+0; |
|---|
| 48 | $usedlastday = $stat{'userinfo'}->{'updated_last1'}+0; |
|---|
| 49 | $allow_getljnews = $stat{'userinfo'}->{'allow_getljnews'}+0; |
|---|
| 50 | $allow_getpromos = $stat{'userinfo'}->{'allow_getpromos'}+0; |
|---|
| 51 | |
|---|
| 52 | $ret .= "(=H1 Users H1=)\n"; |
|---|
| 53 | $ret .= "(=P How many users, and how many of those are active? <UL>"; |
|---|
| 54 | $ret .= "<LI><B>Total users: </B> $total\n"; |
|---|
| 55 | $ret .= "<LI><B>Users that have ever updated: </B> $usedever\n"; |
|---|
| 56 | $ret .= "<LI><B>Users updating in last 30 days: </B> $used30\n"; |
|---|
| 57 | $ret .= "<LI><B>Users updating in last 7 days: </B> $used7\n"; |
|---|
| 58 | $ret .= "<LI><B>Users updating in past 24 hours: </B> $usedlastday\n"; |
|---|
| 59 | $ret .= "</UL> P=)\n"; |
|---|
| 60 | |
|---|
| 61 | $ret .= "(=H1 Gender H1=)\n"; |
|---|
| 62 | $ret .= "(=P Are males or females more likely to maintain journals? <UL>"; |
|---|
| 63 | { |
|---|
| 64 | my $male = $stat{'gender'}->{'M'}+0; |
|---|
| 65 | my $female = $stat{'gender'}->{'F'}+0; |
|---|
| 66 | my $tot = $male+$female; |
|---|
| 67 | $tot ||= 1; |
|---|
| 68 | $ret .= "<LI><B>Male: </B> $male (" . sprintf("%0.1f", $male*100/($tot||1)) . "%)"; |
|---|
| 69 | $ret .= "<LI><B>Female: </B> $female (" . sprintf("%0.1f", $female*100/($tot||1)) . "%)"; |
|---|
| 70 | } |
|---|
| 71 | $ret .= "<LI><B>Unspecified: </B> " . ($stat{'gender'}->{'U'}+0); |
|---|
| 72 | $ret .= "</UL> P=)\n"; |
|---|
| 73 | |
|---|
| 74 | $ret .= "(=H1 Account Types H1=)\n"; |
|---|
| 75 | $ret .= "(=P What type of <a href=\"/support/faqbrowse.bml?faqid=38\">account</a> do people have? <ul>"; |
|---|
| 76 | { |
|---|
| 77 | # FIXME: ljcom specific, make into a hook. |
|---|
| 78 | my %acct_name = ("paid" => "Paid Account", |
|---|
| 79 | "off" => "Free Account", |
|---|
| 80 | "early" => "Early Adopter", |
|---|
| 81 | "on" => "Permanent Account"); |
|---|
| 82 | foreach my $act (qw(off early paid on)) { |
|---|
| 83 | my $num = $stat{'account'}->{$act}+0; |
|---|
| 84 | $ret .= "<LI><B>$acct_name{$act}: </B> $num (" . sprintf("%0.1f", $num*100/($stat{'userinfo'}->{'total'}||1)) . "%)"; |
|---|
| 85 | } |
|---|
| 86 | } |
|---|
| 87 | $ret .= "</ul> P=)\n"; |
|---|
| 88 | |
|---|
| 89 | unless ($LJ::DISABLED{'stats-newsadv'}) { |
|---|
| 90 | $ret .= "(=H1 Receiving News/Advertising H1=)\n"; |
|---|
| 91 | $ret .= "(=P This information says how many users of the ones with validated email addresses subscribe to the LiveJournal news that we send out, and how many people say they wouldn't mind getting advertisement/promotions sent to them by email. <UL>"; |
|---|
| 92 | $ret .= "<LI><B>Users getting LiveJournal news: </B> $allow_getljnews\n"; |
|---|
| 93 | $ret .= "<LI><B>Users getting promotions by mail: </B> $allow_getpromos\n"; |
|---|
| 94 | $ret .= "</UL> P=)\n"; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | unless ($LJ::DISABLED{'stats-recentupdates'}) |
|---|
| 98 | { |
|---|
| 99 | $ret .= "(=H1 Recent Updates H1=)\n"; |
|---|
| 100 | $ret .= "(=P The following are the 10 most recently updated journals: <ul>"; |
|---|
| 101 | $sth = $dbr->prepare("SELECT u.user, u.name, uu.timeupdate FROM user u, userusage uu WHERE u.userid=uu.userid AND uu.timeupdate > DATE_SUB(NOW(), INTERVAL 30 DAY) ORDER BY uu.timeupdate DESC LIMIT 10"); |
|---|
| 102 | $sth->execute; |
|---|
| 103 | while (my ($iuser, $iname, $itime) = $sth->fetchrow_array) { |
|---|
| 104 | $ret .= "<li><a href='/users/$iuser/'>(=_EH $iname _EH=)</a>, $itime</li>\n"; |
|---|
| 105 | } |
|---|
| 106 | $ret .= "</ul> P=)\n"; |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | unless ($LJ::DISABLED{'stats-newjournals'}) |
|---|
| 110 | { |
|---|
| 111 | $ret .= "(=H1 New Journals H1=)\n"; |
|---|
| 112 | $ret .= "(=P The following are the 10 most recently created journals. It's very likely these users haven't modified their journals much, and probably haven't wrote much in them yet either... <ul>"; |
|---|
| 113 | $sth = $dbr->prepare("SELECT u.user, u.name, uu.timeupdate FROM user u, userusage uu WHERE u.userid=uu.userid AND uu.timeupdate IS NOT NULL ORDER BY uu.timecreate DESC LIMIT 10"); |
|---|
| 114 | $sth->execute; |
|---|
| 115 | while (my ($iuser, $iname, $itime) = $sth->fetchrow_array) { |
|---|
| 116 | $ret .= "<li><a href='/users/$iuser/'>(=_EH $iname _EH=)</a>, $itime</li>\n"; |
|---|
| 117 | } |
|---|
| 118 | $ret .= "</ul> P=)\n"; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | $ret .= "(=H1 Demographics H1=)\n"; |
|---|
| 122 | $ret .= "(=P The following are the 15 most popular countries LiveJournal is used in: <UL>"; |
|---|
| 123 | foreach my $key (sort { $stat{'country'}->{$b} <=> $stat{'country'}->{$a} } keys %{$stat{'country'}}) |
|---|
| 124 | { |
|---|
| 125 | $ret .= "<LI><B>$key</B> - $stat{'country'}->{$key}\n"; |
|---|
| 126 | } |
|---|
| 127 | $ret .= "</UL>\n"; |
|---|
| 128 | $ret .= "The following are the 15 most popular U.S. states LiveJournal is used in: <UL>"; |
|---|
| 129 | foreach my $key (sort { $stat{'state'}->{$b} <=> $stat{'state'}->{$a} } keys %{$stat{'state'}}) |
|---|
| 130 | { |
|---|
| 131 | $ret .= "<LI><B>$key</B> - $stat{'state'}->{$key}\n"; |
|---|
| 132 | } |
|---|
| 133 | $ret .= "</UL> P=)\n"; |
|---|
| 134 | |
|---|
| 135 | # ages |
|---|
| 136 | $ret .= "(=H1 Age Distribution H1=)(=P The following shows the age distribution of LiveJournal users: P=)\n"; |
|---|
| 137 | |
|---|
| 138 | my %age = (); |
|---|
| 139 | my $maxage = 1; |
|---|
| 140 | foreach my $key (keys %{$stat{'age'}}) { |
|---|
| 141 | $age{$key} = $stat{'age'}->{$key}; |
|---|
| 142 | if ($stat{'age'}->{$key} > $maxage) { $maxage = $stat{'age'}->{$key}; } |
|---|
| 143 | } |
|---|
| 144 | $ret .= "<P><TABLE>\n"; |
|---|
| 145 | my $lastage = 0; |
|---|
| 146 | foreach my $age (grep { $_ >= 13 && $_ <= 55 } sort { $a <=> $b } sort keys %age) |
|---|
| 147 | { |
|---|
| 148 | $width = int(400 * $age{$age}/$maxage); |
|---|
| 149 | $ret .= "<TR><TD ALIGN=RIGHT><B>$age</B></TD><TD>$age{$age}</TD><TD><IMG SRC=\"/img/bluedot.gif\" HEIGHT=10 WIDTH=$width></TD></TR>\n"; |
|---|
| 150 | $lastage = $_; |
|---|
| 151 | } |
|---|
| 152 | $ret .= "</TABLE>\n"; |
|---|
| 153 | |
|---|
| 154 | |
|---|
| 155 | # clients |
|---|
| 156 | $ret .= "(=H1 Client Usage H1=)(=P How people update their journals (over the last 30 days): P=)\n"; |
|---|
| 157 | $ret .= "<P><TABLE CELLPADDING=3>\n"; |
|---|
| 158 | |
|---|
| 159 | ### sum up clients over different versions |
|---|
| 160 | foreach my $c (keys %{$stat{'client'}}) { |
|---|
| 161 | next unless ($c =~ /^(.+?)\//); |
|---|
| 162 | $stat{'clientname'}->{$1} += $stat{'client'}->{$c}; |
|---|
| 163 | } |
|---|
| 164 | foreach my $cn (sort { $stat{'clientname'}->{$b} <=> $stat{'clientname'}->{$a} } keys %{$stat{'clientname'}}) |
|---|
| 165 | { |
|---|
| 166 | $ret .= "<TR VALIGN=TOP><TD><FONT SIZE=+1><B>$stat{'clientname'}->{$cn}</B></FONT></TD><TD><B>$cn</B><BR>\n"; |
|---|
| 167 | $ret .= "<FONT SIZE=-1>\n"; |
|---|
| 168 | foreach my $c (sort grep { /^\Q$cn\E\// } keys %{$stat{'client'}}) { |
|---|
| 169 | my $count = $stat{'client'}->{$c}; |
|---|
| 170 | $c =~ s/^\Q$cn\E\///; |
|---|
| 171 | $ret .= "$c ($count), "; |
|---|
| 172 | } |
|---|
| 173 | chop $ret; chop $ret; # remove trailing ", " |
|---|
| 174 | $ret .= "</FONT>\n"; |
|---|
| 175 | $ret .= "</TD></TR>\n"; |
|---|
| 176 | } |
|---|
| 177 | $ret .= "</TABLE>\n"; |
|---|
| 178 | |
|---|
| 179 | ### graphs! |
|---|
| 180 | |
|---|
| 181 | $ret .= "(=H1 Pretty Graphs! H1=)(=P These are the most fun, aren't they? P=)"; |
|---|
| 182 | |
|---|
| 183 | $ret .= "(=H2 Journal entries -- last 60 days H2=)(=P How often do people post over the last 60 days? P=)"; |
|---|
| 184 | $ret .= "<P><CENTER><IMG SRC=\"stats/postsbyday.png\" WIDTH=520 HEIGHT=350></CENTER>"; |
|---|
| 185 | |
|---|
| 186 | $ret .= "(=H2 Journal entries -- by week, ever H2=)(=P What's the week-to-week trend? P=)"; |
|---|
| 187 | $ret .= "<P><CENTER><IMG SRC=\"stats/postsbyweek.png\" WIDTH=520 HEIGHT=350></CENTER>"; |
|---|
| 188 | |
|---|
| 189 | $ret .= "(=H2 New accounts -- last 60 days H2=)(=P How fast are we growing? P=)"; |
|---|
| 190 | $ret .= "<P><CENTER><IMG SRC=\"stats/newbyday.png\" WIDTH=520 HEIGHT=350></CENTER>"; |
|---|
| 191 | |
|---|
| 192 | return $ret; |
|---|
| 193 | |
|---|
| 194 | _CODE=) |
|---|
| 195 | |
|---|
| 196 | <=BODY |
|---|
| 197 | PAGE=)(=_C <LJDEP> |
|---|
| 198 | link: htdocs/stats/, htdocs/support/faqbrowse.bml |
|---|
| 199 | img: htdocs/img/bluedot.gif, htdocs/stats/postsbyday.png, htdocs/stats/postsbyweek.png, htdocs/stats/newbyday.png |
|---|
| 200 | </LJDEP> _C=) |
|---|