| Index: trunk/extensions/ContributionReporting/ContributionReporting.php |
| — | — | @@ -115,6 +115,10 @@ |
| 116 | 116 | ), |
| 117 | 117 | ); |
| 118 | 118 | |
| | 119 | +// The first year of statistics to make visible by default. |
| | 120 | +// We normally don't show all of them by default, since it makes the chart extremely wide. |
| | 121 | +$egFundraiserStatisticsFirstYearDefault = 2009; |
| | 122 | + |
| 119 | 123 | // Thesholds for fundraiser statistics |
| 120 | 124 | $egFundraiserStatisticsMinimum = 1; |
| 121 | 125 | $egFundraiserStatisticsMaximum = 10000; |
| Index: trunk/extensions/ContributionReporting/FundraiserStatistics_body.php |
| — | — | @@ -1,6 +1,7 @@ |
| 2 | 2 | <?php |
| 3 | 3 | /** |
| 4 | 4 | * Special Page for Contribution statistics extension |
| | 5 | + * This page displays charts and tables related to donation statistics. |
| 5 | 6 | * |
| 6 | 7 | * @file |
| 7 | 8 | * @ingroup Extensions |
| — | — | @@ -15,15 +16,22 @@ |
| 16 | 17 | } |
| 17 | 18 | |
| 18 | 19 | public function execute( $sub ) { |
| 19 | | - global $wgRequest, $wgOut, $wgLang, $wgScriptPath, $egFundraiserStatisticsFundraisers; |
| | 20 | + global $wgRequest, $wgOut, $wgLang, $wgScriptPath, $egFundraiserStatisticsFundraisers, |
| | 21 | + $egFundraiserStatisticsFirstYearDefault; |
| 20 | 22 | |
| | 23 | + // Figure out what years to display initially |
| 21 | 24 | $showYear = array(); |
| 22 | 25 | foreach ( $egFundraiserStatisticsFundraisers as $fundraiser ) { |
| 23 | | - if ( $wgRequest->wasPosted() ) { |
| 24 | | - $showYear[$fundraiser['id']] = $wgRequest->getCheck( 'toogle'.$fundraiser['id'] ); |
| | 26 | + // You can override the years to display by default via the query string. |
| | 27 | + // For example, Special:FundraiserStatistics?2007=show&2010=hide |
| | 28 | + $yearOverride = $wgRequest->getVal( $fundraiser['id'] ); |
| | 29 | + if ( $yearOverride === "hide" ) { |
| | 30 | + $showYear[$fundraiser['id']] = false; |
| | 31 | + } else if ( $yearOverride === "show" ) { |
| | 32 | + $showYear[$fundraiser['id']] = true; |
| 25 | 33 | } else { |
| 26 | | - // By default, show only the fundraising years after 2008 |
| 27 | | - if ( intval( $fundraiser['id'] ) > 2008 ) { |
| | 34 | + // By default, show only the recent fundraising years |
| | 35 | + if ( intval( $fundraiser['id'] ) >= $egFundraiserStatisticsFirstYearDefault ) { |
| 28 | 36 | $showYear[$fundraiser['id']] = true; |
| 29 | 37 | } else { |
| 30 | 38 | $showYear[$fundraiser['id']] = false; |
| — | — | @@ -37,7 +45,6 @@ |
| 38 | 46 | 'totals' => array( |
| 39 | 47 | 'data' => array(), |
| 40 | 48 | 'index' => 1, |
| 41 | | - 'query' => 'dailyTotalMax', |
| 42 | 49 | 'precision' => 2, |
| 43 | 50 | 'label' => 'fundraiserstats-total', |
| 44 | 51 | 'max' => 1, |
| — | — | @@ -45,7 +52,6 @@ |
| 46 | 53 | 'contributions' => array( |
| 47 | 54 | 'data' => array(), |
| 48 | 55 | 'index' => 2, |
| 49 | | - 'query' => 'contributionsMax', |
| 50 | 56 | 'precision' => 0, |
| 51 | 57 | 'label' => 'fundraiserstats-contributions', |
| 52 | 58 | 'max' => 1, |
| — | — | @@ -53,7 +59,6 @@ |
| 54 | 60 | 'averages' => array( |
| 55 | 61 | 'data' => array(), |
| 56 | 62 | 'index' => 3, |
| 57 | | - 'query' => 'averagesMax', |
| 58 | 63 | 'precision' => 2, |
| 59 | 64 | 'label' => 'fundraiserstats-avg', |
| 60 | 65 | 'max' => 1, |
| — | — | @@ -61,7 +66,6 @@ |
| 62 | 67 | 'maximums' => array( |
| 63 | 68 | 'data' => array(), |
| 64 | 69 | 'index' => 4, |
| 65 | | - 'query' => 'maximumsMax', |
| 66 | 70 | 'precision' => 2, |
| 67 | 71 | 'label' => 'fundraiserstats-max', |
| 68 | 72 | 'max' => 1, |
| — | — | @@ -69,7 +73,6 @@ |
| 70 | 74 | 'ytd' => array( |
| 71 | 75 | 'data' => array(), |
| 72 | 76 | 'index' => 5, |
| 73 | | - 'query' => 'yearlyTotalMax', |
| 74 | 77 | 'precision' => 2, |
| 75 | 78 | 'label' => 'fundraiserstats-ytd', |
| 76 | 79 | 'max' => 1, |
| — | — | @@ -85,10 +88,28 @@ |
| 86 | 89 | |
| 87 | 90 | $wgOut->addWikiMsg( 'contribstats-header' ); // Header (typically empty) |
| 88 | 91 | |
| | 92 | + // The $fundraisingData array contains all of the fundraising data in the following scheme: |
| | 93 | + // $fundraisingData[<fundraiserId>][<dayIndex>][<dataTypeIndex>] |
| | 94 | + $fundraisingData = array(); |
| | 95 | + |
| | 96 | + foreach ( $egFundraiserStatisticsFundraisers as $fundraiserIndex => $fundraiser ) { |
| | 97 | + |
| | 98 | + // See if this is the most recent fundraiser or not |
| | 99 | + if ( $fundraiserIndex == count( $egFundraiserStatisticsFundraisers ) - 1 ) { |
| | 100 | + $mostRecent = true; |
| | 101 | + } else { |
| | 102 | + $mostRecent = false; |
| | 103 | + } |
| | 104 | + |
| | 105 | + // Collect all the data |
| | 106 | + $fundraisingData[$fundraiser['id']] = $this->query( $mostRecent, $fundraiser['start'], $fundraiser['end'] ); |
| | 107 | + } |
| | 108 | + |
| 89 | 109 | // Chart maximums |
| 90 | | - foreach ( $egFundraiserStatisticsFundraisers as $fundraiser ) { |
| | 110 | + // We cycle through all the fundraisers with all the different chart types and collect the maximums. |
| | 111 | + foreach ( $fundraisingData as $fundraiser ) { |
| 91 | 112 | foreach ( $charts as $name => $chart ) { |
| 92 | | - $chartMax = $this->query( $charts[$name]['query'], $fundraiser['start'], $fundraiser['end'] ); |
| | 113 | + $chartMax = $this->getMaximum( $fundraiser, $chart['index'] ); |
| 93 | 114 | if ( $chartMax > $charts[$name]['max'] ) { |
| 94 | 115 | $charts[$name]['max'] = $chartMax; |
| 95 | 116 | } |
| — | — | @@ -108,7 +129,7 @@ |
| 109 | 130 | foreach ( $egFundraiserStatisticsFundraisers as $fundraiserIndex => $fundraiser ) { |
| 110 | 131 | |
| 111 | 132 | // Get all the daily data for a particular fundraiser |
| 112 | | - $days = $this->query( 'dailyTotals', $fundraiser['start'], $fundraiser['end'] ); |
| | 133 | + $days = $fundraisingData[$fundraiser['id']]; |
| 113 | 134 | |
| 114 | 135 | // See if this is the most recent fundraiser or not |
| 115 | 136 | if ( $fundraiserIndex == count( $egFundraiserStatisticsFundraisers ) - 1 ) { |
| — | — | @@ -137,7 +158,7 @@ |
| 138 | 159 | |
| 139 | 160 | $height = $chart['factor'] * $day[$chart['index']]; |
| 140 | 161 | $style = "height:{$height}px;"; |
| 141 | | - if ( $showYear[$fundraiser['id']] !== true ) { |
| | 162 | + if ( !$showYear[$fundraiser['id']] ) { |
| 142 | 163 | $style .= "display:none;"; |
| 143 | 164 | } |
| 144 | 165 | $attributes = array( |
| — | — | @@ -212,8 +233,8 @@ |
| 213 | 234 | |
| 214 | 235 | $years = wfMsg( 'fundraiserstats-show-years' ).'<br/>'; |
| 215 | 236 | foreach ( $egFundraiserStatisticsFundraisers as $fundraiser ) { |
| 216 | | - $years .= Xml::check( 'toogle'.$fundraiser['id'], $showYear[$fundraiser['id']], array( 'id' => 'bar-'.$fundraiser['id'], 'class' => 'yeartoggle' ) ); |
| 217 | | - $years .= Xml::label( $fundraiser['id'], 'toogle'.$fundraiser['id'] ); |
| | 237 | + $years .= Xml::check( 'toggle'.$fundraiser['id'], $showYear[$fundraiser['id']], array( 'id' => 'bar-'.$fundraiser['id'], 'class' => 'yeartoggle' ) ); |
| | 238 | + $years .= Xml::label( $fundraiser['id'], 'toggle'.$fundraiser['id'] ); |
| 218 | 239 | $years .= "<br/>"; |
| 219 | 240 | } |
| 220 | 241 | $wgOut->addHTML( Xml::openElement( 'div', array( 'id' => 'configholder' ) ) ); |
| — | — | @@ -285,16 +306,16 @@ |
| 286 | 307 | /** |
| 287 | 308 | * Retrieve the donation data from the database |
| 288 | 309 | * |
| 289 | | - * @param string $type Which type of query to do |
| | 310 | + * @param boolean $mostRecent Is this query for the most recent fundraiser? |
| 290 | 311 | * @param string $start The start date for a fundraiser |
| 291 | 312 | * @param string $end The end date for a fundraiser |
| 292 | 313 | * @return an array of results or null |
| 293 | 314 | */ |
| 294 | | - private function query( $type, $start, $end ) { |
| | 315 | + private function query( $mostRecent, $start, $end ) { |
| 295 | 316 | global $wgMemc, $egFundraiserStatisticsMinimum, $egFundraiserStatisticsMaximum, $egFundraiserStatisticsCacheTimeout; |
| 296 | 317 | |
| 297 | 318 | // Conctruct the key for memcached |
| 298 | | - $key = wfMemcKey( 'fundraiserstatistics', $type, $start, $end ); |
| | 319 | + $key = wfMemcKey( 'fundraiserstatistics', $start, $end ); |
| 299 | 320 | |
| 300 | 321 | // If result exists in memcached, use that |
| 301 | 322 | $cache = $wgMemc->get( $key ); |
| — | — | @@ -310,88 +331,60 @@ |
| 311 | 332 | 'converted_amount >= ' . $egFundraiserStatisticsMinimum, |
| 312 | 333 | 'converted_amount <= ' . $egFundraiserStatisticsMaximum |
| 313 | 334 | ); |
| 314 | | - switch ( $type ) { |
| 315 | | - case 'dailyTotals': |
| 316 | | - $select = $dbr->select( 'public_reporting', |
| 317 | | - array( |
| 318 | | - "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')", |
| 319 | | - 'sum(converted_amount)', |
| 320 | | - 'count(*)', |
| 321 | | - 'avg(converted_amount)', |
| 322 | | - 'max(converted_amount)', |
| 323 | | - ), |
| 324 | | - $conditions, |
| 325 | | - __METHOD__ . '-dailyTotals', |
| 326 | | - array( |
| 327 | | - 'ORDER BY' => 'received', |
| 328 | | - 'GROUP BY' => "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')" |
| 329 | | - ) |
| 330 | | - ); |
| 331 | | - $result = array(); |
| 332 | | - $ytd = 0; |
| 333 | | - while ( $row = $dbr->fetchRow( $select ) ) { |
| 334 | | - // Insert the year-to-date amount as a record in the row (existing $ytd + sum) |
| 335 | | - $row[5] = $ytd += $row[1]; |
| 336 | | - $result[] = $row; |
| 337 | | - } |
| 338 | | - break; |
| 339 | | - case 'dailyTotalMax': |
| 340 | | - $result = $dbr->selectField( 'public_reporting', |
| 341 | | - array( 'sum(converted_amount) as sum' ), |
| 342 | | - $conditions, |
| 343 | | - __METHOD__ . '-dailyTotalMax', |
| 344 | | - array( |
| 345 | | - 'ORDER BY' => 'sum DESC', |
| 346 | | - 'GROUP BY' => "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')" |
| 347 | | - ) |
| 348 | | - ); |
| 349 | | - break; |
| 350 | | - case 'yearlyTotalMax': |
| 351 | | - $result = $dbr->selectField( 'public_reporting', |
| 352 | | - array( 'sum(converted_amount) as sum' ), |
| 353 | | - $conditions, |
| 354 | | - __METHOD__ . '-yearlyTotalMax' |
| 355 | | - ); |
| 356 | | - break; |
| 357 | | - case 'contributionsMax': |
| 358 | | - $result = $dbr->selectField( 'public_reporting', |
| 359 | | - array( 'count(converted_amount) as sum' ), |
| 360 | | - $conditions, |
| 361 | | - __METHOD__ . '-contributionsMax', |
| 362 | | - array( |
| 363 | | - 'ORDER BY' => 'sum DESC', |
| 364 | | - 'GROUP BY' => "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')" |
| 365 | | - ) |
| 366 | | - ); |
| 367 | | - break; |
| 368 | | - case 'averagesMax': |
| 369 | | - $result = $dbr->selectField( 'public_reporting', |
| 370 | | - array( 'avg(converted_amount) as sum' ), |
| 371 | | - $conditions, |
| 372 | | - __METHOD__ . '-averagesMax', |
| 373 | | - array( |
| 374 | | - 'ORDER BY' => 'sum DESC', |
| 375 | | - 'GROUP BY' => "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')" |
| 376 | | - ) |
| 377 | | - ); |
| 378 | | - break; |
| 379 | | - case 'maximumsMax': |
| 380 | | - $result = $dbr->selectField( 'public_reporting', |
| 381 | | - array( 'max(converted_amount) as sum' ), |
| 382 | | - $conditions, |
| 383 | | - __METHOD__ . '-maximumsMax', |
| 384 | | - array( |
| 385 | | - 'ORDER BY' => 'sum DESC', |
| 386 | | - 'GROUP BY' => "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')" |
| 387 | | - ) |
| 388 | | - ); |
| 389 | | - break; |
| | 335 | + |
| | 336 | + // Get the data for a fundraiser |
| | 337 | + $select = $dbr->select( 'public_reporting', |
| | 338 | + array( |
| | 339 | + "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')", |
| | 340 | + 'sum(converted_amount)', |
| | 341 | + 'count(*)', |
| | 342 | + 'avg(converted_amount)', |
| | 343 | + 'max(converted_amount)', |
| | 344 | + ), |
| | 345 | + $conditions, |
| | 346 | + __METHOD__, |
| | 347 | + array( |
| | 348 | + 'ORDER BY' => 'received', |
| | 349 | + 'GROUP BY' => "DATE_FORMAT(FROM_UNIXTIME(received),'%Y-%m-%d')" |
| | 350 | + ) |
| | 351 | + ); |
| | 352 | + $result = array(); |
| | 353 | + $ytd = 0; |
| | 354 | + while ( $row = $dbr->fetchRow( $select ) ) { |
| | 355 | + // Insert the year-to-date amount as a record in the row (existing $ytd + sum) |
| | 356 | + $row[5] = $ytd += $row[1]; |
| | 357 | + $result[] = $row; |
| 390 | 358 | } |
| | 359 | + |
| 391 | 360 | if ( isset( $result ) ) { |
| 392 | | - // Store the result in memcached |
| 393 | | - $wgMemc->set( $key, $result, $egFundraiserStatisticsCacheTimeout ); |
| | 361 | + // Store the result in memcached. |
| | 362 | + // If it's the most recent fundraiser, cache for a short period of time, otherwise |
| | 363 | + // cache for 24 hours (since the query is expensive). |
| | 364 | + if ( $mostRecent ) { |
| | 365 | + $wgMemc->set( $key, $result, $egFundraiserStatisticsCacheTimeout ); |
| | 366 | + } else { |
| | 367 | + #$wgMemc->set( $key, $result, 86400 ); |
| | 368 | + } |
| 394 | 369 | return $result; |
| 395 | 370 | } |
| 396 | 371 | return null; |
| 397 | 372 | } |
| | 373 | + |
| | 374 | + /** |
| | 375 | + * Given a particular index, find the maximum for all values in a 2D array with that particular |
| | 376 | + * index as the 2nd key. |
| | 377 | + * |
| | 378 | + * @param array $fundraiserDays A 2D array of daily data for a particular fundraiser |
| | 379 | + * @param integer $comparisonIndex The index to find the maximum for |
| | 380 | + * @return an integer |
| | 381 | + */ |
| | 382 | + private function getMaximum( $fundraiserDays, $comparisonIndex ) { |
| | 383 | + $max = 0; |
| | 384 | + foreach( $fundraiserDays as $day ) { |
| | 385 | + if ( $day[$comparisonIndex] > $max ) { |
| | 386 | + $max = $day[$comparisonIndex]; |
| | 387 | + } |
| | 388 | + } |
| | 389 | + return $max; |
| | 390 | + } |
| 398 | 391 | } |
| Index: trunk/extensions/ContributionReporting/modules/ext.fundraiserstatistics.js |
| — | — | @@ -33,9 +33,11 @@ |
| 34 | 34 | $j( '.fundraiserstats-current' ).each( function() { |
| 35 | 35 | replaceView( $j(this).attr( 'rel' ) ) |
| 36 | 36 | } ); |
| | 37 | + // When someone clicks on a year, hide or show that year in the chart |
| 37 | 38 | $j( '#configholder .yeartoggle' ).click( function() { |
| 38 | 39 | $j('.fundraiserstats-'+$j(this).attr( 'id' )).toggle(); |
| 39 | 40 | } ); |
| | 41 | + // When someone clicks on Customize, display pop-up menu and change arrow icon. |
| 40 | 42 | $j( '#configtoggle' ).click( function() { |
| 41 | 43 | $j('#configholder').toggle(); |
| 42 | 44 | if ($j( '#configtoggle a' ).css( 'background-position' ) == '0px -18px') { |
| — | — | @@ -44,8 +46,5 @@ |
| 45 | 47 | $j( '#configtoggle a' ).css( 'background-position','0px -18px' ); |
| 46 | 48 | } |
| 47 | 49 | } ); |
| 48 | | - $j( '#timezone' ).change( function() { |
| 49 | | - $j('#configform').submit(); |
| 50 | | - } ); |
| 51 | 50 | |
| 52 | 51 | } ); |