'MonthlyStats (@'.$version.')', 'url' => 'http://public.perforce.com/wiki/Monthly_Stats', 'author' => 'Sam Stafford', 'description' => 'Special page with monthly activity statistics' ); class MonthlyStats extends SpecialPage { function MonthlyStats() { SpecialPage::SpecialPage('MonthlyStats'); } function execute( $param ) { global $wgOut; global $wgScriptPath; $this->setHeaders(); $wgOut->setPagetitle( 'Monthly Stats' ); $args = explode( '@@', $param ); $par = $args[0]; if ( $par == "" ) $par = 1; if ( $par < 1 ) $par = 1; if ( $par > 10 ) $par = 10; $path = $args[1]; $maxUsers = 18; # number of users that can fit in the chart $dates = array(); # n => date $d_cur = getdate(); $d_end = $d_cur; $d_cur["year"] = $d_cur["year"] - $par; $t_cur = mktime( 0, 0, 0, $d_cur["mon"], 1, $d_cur["year"] ); $t_end = mktime( 0, 0, 0, $d_end["mon"], 1, $d_end["year"] ); $t_begin = $t_cur; $s_begin = date( 'Ym00000000', $t_begin ); $i = 0; while ( $t_cur < $t_end ) { $dates[$i] = $t_cur; $i++; $d_cur["mon"] = $d_cur["mon"] + 1; if ( $d_cur["mon"] > 12 ) { $d_cur["year"] = $d_cur["year"] + 1; $d_cur["mon"] = 1; } $t_cur = mktime( 0, 0, 0, $d_cur["mon"], 1, $d_cur["year"] ); } $dates[$i] = $t_cur; $i_max = $i; # This is the current month; $i_max-1 is last month. $db = wfGetDB( DB_SLAVE ); $sql = "SELECT rev_user_text,rev_timestamp FROM ". $db->tableName('revision'). " WHERE rev_timestamp>$s_begin". " ORDER BY rev_timestamp"; $res = $db->query($sql, __METHOD__); $userPages = array(); # user => ( n => count ) $totalPages = array(); # n => counts $i = 0; $maxPages = 0; for ($j=0; $j<$db->numRows($res); $j++) { $row = $db->fetchRow($res); $u = $row[0]; # Figure out which date bucket this row falls into. while ( $i < $i_max && date( 'Ym00000000', $dates[$i+1] ) < $row[1] ) { $i++; } if ( !isset( $userPages[$u] ) ) $userPages[$u] = array(); if ( !isset( $userPages[$u][$i] ) ) $userPages[$u][$i] = 0; if ( !isset( $totalPages[$i] ) ) $totalPages[$i] = 0; $userPages[$u][$i]++; $totalPages[$i]++; if ( $totalPages[$i] > $maxPages ) $maxPages = $totalPages[$i]; } ksort( $userPages ); # Figure out which users are worth showing. $tempUsers = array(); # temp user list $tempCounts = array(); # corresponding edit counts foreach( $userPages as $u => $a ) { $tempUsers[] = $u; $tempCounts[] = max($a); } array_multisort( $tempCounts, $tempUsers ); foreach( $tempUsers as $i => $u ) { if ( count( $tempUsers ) <= $maxUsers ) break; if ( $userPages[$u][$i_max] || $userPages[$u][$i_max-1] ) continue; unset( $tempUsers[$i] ); } $tempUsers = array_slice( $tempUsers, -$maxUsers ); # If it's in tempUsers it's worth showing. foreach( $userPages as $u => $a ) { if ( !in_array( $u, $tempUsers ) ) { if ( !isset( $userPages['(other)'] ) ) $userPages['(other)'] = array_fill( 0, $i_max + 1, 0 ); $userPages['(other)'] = array_sum_values( $userPages['(other)'], $a ); unset( $userPages[$u] ); } } global $wgP4EXEC; global $wgP4PORT; global $wgP4USER; global $wgP4PASSWD; global $wgP4WEBURL; if ( $wgP4EXEC != "" ) { $cmdline = $wgP4EXEC . " -u " . $wgP4USER . " -p " . $wgP4PORT ; if ( $wgP4PASSWD != "" ) $cmdline .= " -P " . $wgP4PASSWD ; $cmdline .= ' changes '; if ( $path != '' ) $cmdline .= '-i '; $cmdline .= $path.'@'.date( 'Y/m/01', $dates[0] ).',@now'; $cmdline .= ' 2>&1'; $userChanges = array(); # user => ( n => count ) $totalChanges = array(); # n => counts $i = 0; $maxChanges = 0; $changes = array(); exec( $cmdline, $changes ); $changes = array_reverse( $changes ); foreach( $changes as $c ) { $cmatch = array(); if ( !preg_match( '/Change \d+ on (\d+)\/(\d+)\/(\d+) by (\S+)@\S+ /', $c, $cmatch ) ) { continue; } $u = $cmatch[4]; #Find the date bucket. $dc = $cmatch[1].$cmatch[2].$cmatch[3]; if ( $dc < date( 'Ym00', $dates[0] ) ) continue; while( $i < $i_max && date( 'Ym00', $dates[$i+1] ) < $dc ) { $i++; } if ( !isset( $userChanges[$u] ) ) $userChanges[$u] = array(); if ( !isset( $userChanges[$u][$i] ) ) $userChanges[$u][$i] = 0; if ( !isset( $totalChanges[$i] ) ) $totalChanges[$i] = 0; $userChanges[$u][$i]++; $totalChanges[$i]++; if ( $totalChanges[$i] > $maxChanges ) $maxChanges = $totalChanges[$i]; } ksort( $userChanges ); # Figure out which users are worth showing. $tempUsers = array(); # temp user list $tempCounts = array(); # corresponding edit counts foreach( $userChanges as $u => $a ) { $tempUsers[] = $u; $tempCounts[] = max($a); } array_multisort( $tempCounts, $tempUsers ); foreach( $tempUsers as $i => $u ) { if ( count( $tempUsers ) <= $maxUsers ) break; if ( $userChanges[$u][$i_max] || $userChanges[$u][$i_max-1] ) continue; unset( $tempUsers[$i] ); } $tempUsers = array_slice( $tempUsers, -$maxUsers ); # If it's in tempUsers it's worth showing. foreach( $userChanges as $u => $a ) { if ( !in_array( $u, $tempUsers ) ) { if ( !isset( $userChanges['(other)'] ) ) $userChanges['(other)'] = array_fill( 0, $i_max + 1, 0 ); $userChanges['(other)'] = array_sum_values( $userChanges['(other)'], $a ); unset( $userChanges[$u] ); } } } #end wgP4EXEC block # Generate colors for users. $userColors = array(); foreach ( $userPages as $u => $a ) $userColors[unorm($u)] = '000000'; if ( isset( $wgP4EXEC ) ) foreach ( $userChanges as $u => $a ) $userColors[unorm($u)] = '000000'; if ( isset( $userColors['(other)'] ) ) { $other = 'DDDDDD'; unset($userColors['(other)']); } ksort( $userColors ); $i = 0; $colorCount = count( $userColors ); foreach( $userColors as $u => $c ) { $userColors[$u] = HSV_TO_RGB ( 0.5 + ( $i * 1.0 / count( $userColors ) ), 0.4, 0.9 ); $i++; } if ( isset( $other ) ) $userColors['(other)'] = $other; # Wiki stats chart. $height = 200; if ( count( $userPages ) * 20 > $height ) $height = count( $userPages ) * 20; if ( $height > 360 ) $height = 360; $url = 'http://chart.apis.google.com/chart?'; $url .= 'chs=800x'.$height.'&'; $url .= 'cht=bvs&chbh=a&'; $url .= 'chds=0,'.$maxPages.'&'; $url .= 'chxt=y,x&'; $url .= 'chma=9,150,9,9&chldp=r&'; $url .= 'chxr=0,0,'.$maxPages.'&'; $url .= 'chxl=1:|'; $interval = $i_max / 12; if ( $interval == 0 ) $interval = 1; for( $i = 0 ; $i < $i_max ; $i++ ) { if ( ( $i + 1 ) % ( $interval ) == 0 ) $url .= date( 'M y', $dates[$i] ); $url .= '|'; } $url = substr( $url, 0, -1 ); $url .= '&'; $data = 'chd=t:'; foreach ( $userPages as $u => $a ) { for ( $i = 0 ; $i < $i_max ; $i++ ) { if ( !isset( $a[$i] ) ) $a[$i] = 0; $data .= "$a[$i],"; } $data = substr( $data, 0, -1 ); $data .= '|'; } $data = substr( $data, 0, -1 ); $data .= '&'; $data .= 'chco='; foreach ( $userPages as $u => $a ) { $data .= $userColors[unorm($u)]; $data .= ','; } $data = substr( $data, 0, -1 ); $data .= '&'; $data .= 'chdl='; foreach( $userPages as $u => $a ) { $data .= $u; $data .= '|'; } $data = substr( $data, 0, -1 ); $data = str_replace( ' ', '%20', $data ); if ( strlen( $url ) + strlen( $data ) > 2048 ) { $data = 'chd=t:'; for( $i = 0 ; $i < $i_max ; $i++ ) { if ( !isset( $totalPages[$i] ) ) $totalPages[$i] = 0; $data .= "$totalPages[$i],"; } $data = substr( $data, 0, -1 ); $data .= '&'; $data .= 'chdl=All Users'; $url = str_replace( 'chs=800x'.$height.'&', 'chs=800x200&', $url ); } $mwa = $url.$data; #Last month's wiki edits (pie) $i = $i_max - 1; $mwl = 'http://chart.apis.google.com/chart?'; $mwl .= 'chs=390x100&'; $mwl .= 'cht=p3&'; $mwl .= 'chtt='.date('M y',$dates[$i]).'&'; $dat = ""; $col = ""; $lab = ""; foreach ( $userPages as $u => $a ) { if ( !isset( $a[$i] ) || !$a[$i] ) continue; $dat .= "$a[$i],"; $col .= $userColors[unorm($u)].","; $lab .= "$u ($a[$i])|"; } if ( strlen( $dat ) ) { $dat = substr( $dat, 0, -1 ); $col = substr( $col, 0, -1 ); $lab = substr( $lab, 0, -1 ); } else { $dat = '1'; $col = 'EEEEEE'; $lab = 'No pages edited'; } $mwl .= 'chd=t:'.$dat.'&'; $mwl .= 'chco='.$col.'&'; $mwl .= 'chl='.$lab; $mwlu = $wgScriptPath.'/index.php?title=Special:Recentchanges'; $mwlu .= '&from='.date( 'Ym01000000', $dates[$i] ); $mwlu .= '&days=60&limit=500'; #This month's wiki edits to date (pie) $i = $i_max; $mwt = 'http://chart.apis.google.com/chart?'; $mwt .= 'chs=390x100&'; $mwt .= 'cht=p3&'; $mwt .= 'chtt='.date('M y',$dates[$i]).' to date&'; $dat = ""; $col = ""; $lab = ""; foreach ( $userPages as $u => $a ) { if ( !isset( $a[$i] ) || !$a[$i] ) continue; $dat .= "$a[$i],"; $col .= $userColors[unorm($u)].","; $lab .= "$u ($a[$i])|"; } if ( strlen( $dat ) ) { $dat = substr( $dat, 0, -1 ); $col = substr( $col, 0, -1 ); $lab = substr( $lab, 0, -1 ); } else { $dat = '1'; $col = 'EEEEEE'; $lab = 'No pages edited yet'; } $mwt .= 'chd=t:'.$dat.'&'; $mwt .= 'chco='.$col.'&'; $mwt .= 'chl='.$lab; $mwtu = $wgScriptPath.'/index.php?title=Special:Recentchanges'; $mwtu .= '&from='.date( 'Ym01000000', $dates[$i] ); $mwtu .= '&days=30&limit=500'; if ( $wgP4EXEC != "" ) { # Perforce stats chart. $height = 200; if ( count( $userChanges ) * 20 > $height ) $height = count( $userChanges ) * 20; if ( $height > 360 ) $height = 360; $url = 'http://chart.apis.google.com/chart?'; $url .= 'chs=800x'.$height.'&'; $url .= 'cht=bvs&chbh=a&'; $url .= 'chds=0,'.$maxChanges.'&'; $url .= 'chxt=y,x&'; $url .= 'chma=9,150,9,9&chldp=r&'; $url .= 'chxr=0,0,'.$maxChanges.'&'; $url .= 'chxl=1:|'; $interval = $i_max / 12; if ( $interval == 0 ) $interval = 1; for( $i = 0 ; $i < $i_max ; $i++ ) { if ( ( $i + 1 ) % ( $interval ) == 0 ) $url .= date( 'M y', $dates[$i] ); $url .= '|'; } $url = substr( $url, 0, -1 ); $url .= '&'; $data = 'chd=t:'; foreach ( $userChanges as $u => $a ) { for ( $i = 0 ; $i < $i_max ; $i++ ) { if ( !isset( $a[$i] ) ) $a[$i] = 0; $data .= "$a[$i],"; } $data = substr( $data, 0, -1 ); $data .= '|'; } $data = substr( $data, 0, -1 ); $data .= '&'; $data .= 'chco='; foreach ( $userChanges as $u => $a ) { $data .= $userColors[unorm($u)]; $data .= ','; } $data = substr( $data, 0, -1 ); $data .= '&'; $data .= 'chdl='; foreach( $userChanges as $u => $a ) { $data .= $u; $data .= '|'; } $data = substr( $data, 0, -1 ); $data = str_replace( ' ', '%20', $data ); if ( strlen( $url ) + strlen( $data ) > 2048 ) { $data = 'chd=t:'; for( $i = 0 ; $i < $i_max ; $i++ ) { if ( !isset( $totalChanges[$i] ) ) $totalChanges[$i] = 0; $data .= "$totalChanges[$i],"; } $data = substr( $data, 0, -1 ); $data .= '&'; $data .= 'chdl=All Users'; $url = str_replace( 'chs=800x'.$height.'&', 'chs=800x200&', $url ); } $p4a = $url.$data; #Last month's Perforce changes (pie) $i = $i_max - 1; $p4l = 'http://chart.apis.google.com/chart?'; $p4l .= 'chs=390x100&'; $p4l .= 'cht=p3&'; $p4l .= 'chtt='.date('M y',$dates[$i]).'&'; $dat = ""; $col = ""; $lab = ""; foreach ( $userChanges as $u => $a ) { if ( !isset( $a[$i] ) || !$a[$i] ) continue; $dat .= "$a[$i],"; $col .= $userColors[unorm($u)].","; $lab .= "$u ($a[$i])|"; } if ( strlen( $dat ) ) { $dat = substr( $dat, 0, -1 ); $col = substr( $col, 0, -1 ); $lab = substr( $lab, 0, -1 ); } else { $dat = '1'; $col = 'EEEEEE'; $lab = 'No changes submitted'; } $p4l .= 'chd=t:'.$dat.'&'; $p4l .= 'chco='.$col.'&'; $p4l .= 'chl='.$lab; $p4lu = $wgP4WEBURL . '?ac=43&sr='; $p4lu .= date( 'Y/m/01', $dates[$i] ) . '&sr2='; $p4lu .= date( 'Y/m/00', $dates[$i+1] ); $p4lu .= '&al=y'; #This month's Perforce changes (pie) $i = $i_max; $p4t = 'http://chart.apis.google.com/chart?'; $p4t .= 'chs=390x100&'; $p4t .= 'cht=p3&'; $p4t .= 'chtt='.date('M y',$dates[$i]).' to date&'; $dat = ""; $col = ""; $lab = ""; foreach ( $userChanges as $u => $a ) { if ( !isset( $a[$i] ) || !$a[$i] ) continue; $dat .= "$a[$i],"; $col .= $userColors[unorm($u)].","; $lab .= "$u ($a[$i])|"; } if ( strlen( $dat ) ) { $dat = substr( $dat, 0, -1 ); $col = substr( $col, 0, -1 ); $lab = substr( $lab, 0, -1 ); } else { $dat = '1'; $col = 'EEEEEE'; $lab = 'No changes submitted yet'; } $p4t .= 'chd=t:'.$dat.'&'; $p4t .= 'chco='.$col.'&'; $p4t .= 'chl='.$lab; $p4tu = $wgP4WEBURL . '?ac=43&sr='; $p4tu .= date( 'Y/m/01', $dates[$i] ) . '&sr2=now'; $p4tu .= '&al=y'; } #end P4EXEC check $wgOut->addHTML('

Wiki Page Edits by All Users

'."\n"); $wgOut->addHTML('

'); $wgOut->addHTML('

'."\n"); $wgOut->addHTML('

'."\n"); if ( $wgP4EXEC != "" ) { $wgOut->addHTML('



'."\n"); $wgOut->addHTML('

Perforce Changelist Submissions by All Users

'."\n"); $wgOut->addHTML('

'); $wgOut->addHTML('

'."\n"); $wgOut->addHTML('

'."\n"); } } } function unorm( $u ) # Normalization function for folding wiki and Perforce usernames together. # This works nicely for the Perforce Public Depot; your mileage may vary. { return strtr( strtolower( $u ), '_', ' ' ); } # Slightly tweaked function from actionscript.org forum poster petefs. function HSV_TO_RGB ($H, $S, $V) // HSV Values:Number 0-1 { while ( $H < 0 ) { $H++; } while ( $H > 1 ) { $H--; } if($S == 0) { $R = $G = $B = $V * 255; } else { $var_H = $H * 6; $var_i = floor( $var_H ); $var_1 = $V * ( 1 - $S ); $var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) ); $var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) ); if ($var_i == 0) { $var_R = $V ; $var_G = $var_3 ; $var_B = $var_1 ; } else if ($var_i == 1) { $var_R = $var_2 ; $var_G = $V ; $var_B = $var_1 ; } else if ($var_i == 2) { $var_R = $var_1 ; $var_G = $V ; $var_B = $var_3 ; } else if ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2 ; $var_B = $V ; } else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $V ; } else { $var_R = $V ; $var_G = $var_1 ; $var_B = $var_2 ; } $R = $var_R * 255; $G = $var_G * 255; $B = $var_B * 255; } return strtoupper( dechex($R).dechex($G).dechex($B) ); } #From Tobias Schlemmer at php.net. /** * Sums the values of the arrays be there keys (PHP 4, PHP 5) * array array_sum_values ( array array1 [, array array2 [, array ...]] ) */ function array_sum_values() { $return = array(); $intArgs = func_num_args(); $arrArgs = func_get_args(); if($intArgs < 1) trigger_error('Warning: Wrong parameter count for array_sum_values()', E_USER_WARNING); foreach($arrArgs as $arrItem) { if(!is_array($arrItem)) trigger_error('Warning: Wrong parameter values for array_sum_values()', E_USER_WARNING); foreach($arrItem as $k => $v) { $return[$k] += $v; } } return $return; }