Ejegg has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/327401 )
Change subject: WIP top 10 days / hours
......................................................................
WIP top 10 days / hours
TODO: wire up filters
Bug: T152028
Change-Id: I183615c9cb077f078cafa9602fe36aa8fcfbe31a
---
M routes/data.js
A schema/0005_top10.sql
M src/app/startup.js
A src/components/widgets/top10/top10.html
A src/components/widgets/top10/top10.js
A widgets/top10.js
6 files changed, 159 insertions(+), 4 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/dash
refs/changes/01/327401/1
diff --git a/routes/data.js b/routes/data.js
index 599b3f1..3319b30 100644
--- a/routes/data.js
+++ b/routes/data.js
@@ -329,13 +329,13 @@
return;
}
}
- sqlQuery = sqlQuery.replace( '[[WHERE]]', whereClause );
+ sqlQuery = sqlQuery.replace( /\[\[WHERE\]\]/g, whereClause );
for ( i = 0; i < joins.length; i++ ) {
joinClause += widget.optionalJoins[joins[i]].text + ' ';
}
- sqlQuery = sqlQuery.replace( '[[JOINS]]', joinClause );
- sqlQuery = sqlQuery.replace( '[[GROUP]]', groupClause );
- sqlQuery = sqlQuery.replace( '[[SELECTGROUP]]', selectGroup );
+ sqlQuery = sqlQuery.replace( /\[\[JOINS\]\]/g, joinClause );
+ sqlQuery = sqlQuery.replace( /\[\[GROUP\]\]/g, groupClause );
+ sqlQuery = sqlQuery.replace( /\[\[SELECTGROUP\]\]/g, selectGroup );
connection = mysql.createConnection({
host: config.dbserver,
diff --git a/schema/0005_top10.sql b/schema/0005_top10.sql
new file mode 100644
index 0000000..2f68aa6
--- /dev/null
+++ b/schema/0005_top10.sql
@@ -0,0 +1,2 @@
+INSERT IGNORE INTO dash_widget (code, display_name, description, preview_path)
+ VALUES ('top10', 'Top 10 days and hours', 'Best 10 days and hours in
the history of fundraising.', 'images/top10.png');
diff --git a/src/app/startup.js b/src/app/startup.js
index 775e4bd..42cd8eb 100644
--- a/src/app/startup.js
+++ b/src/app/startup.js
@@ -29,6 +29,7 @@
ko.components.register( 'distance-to-goal-chart', {
require: 'components/widgets/distance-to-goal-chart/distance-to-goal-chart' });
ko.components.register( 'donation-age', {
require: 'components/widgets/donation-age/donation-age' });
ko.components.register( 'fraud-gauge', {
require: 'components/widgets/fraud-gauge/fraud-gauge' });
+ ko.components.register( 'top10', {
require: 'components/widgets/top10/top10' });
ko.components.register( 'totals-earned-chart', {
require: 'components/widgets/totals-earned-chart/totals-earned-chart' });
ko.components.register( 'x-by-y', {
require: 'components/widgets/x-by-y/x-by-y' });
diff --git a/src/components/widgets/top10/top10.html
b/src/components/widgets/top10/top10.html
new file mode 100644
index 0000000..3947765
--- /dev/null
+++ b/src/components/widgets/top10/top10.html
@@ -0,0 +1,70 @@
+<div id="top10Widget" class="panel panel-purple panel-short widget-6">
+ <div class="panel-heading">
+ <span data-bind='text: title'></span>
+ <div class="btn-group btn-group-xs pull-right">
+ <button type="button" class="btn btn-default
dropdown-toggle" data-toggle="modal" data-target="#top10SqlModal"><i class="fa
fa-database"></i></button>
+ <button type="button" class="btn btn-default"
data-toggle="modal" data-target="#top10FilterModal" ><i class="fa
fa-edit"></i></button>
+ <button type="button" class="btn btn-danger"
data-bind="visible: !chartSaved() && optionStateChanged, click:
saveWidgetConfig"><i class="fa fa-save"></i></button>
+ <button type="button" class="btn btn-success"
data-bind="visible: chartSaved && !optionStateChanged()"><i class="fa
fa-save"></i> Saved</button>
+
+ <button type="button" class="btn btn-default
dropdown-toggle" data-toggle="modal" data-bind="attr: { 'data-target':
'#remove'+instanceID }"><i class="fa fa-times"></i></button>
+ </div>
+ </div>
+ <div class="panel-body">
+ <div class="column text-center">
+ <h4>Top 10 days</h4>
+ <ul data-bind='foreach:days'>
+ <li><span data-bind='text: date'></span>:
$<span data-bind='text: total'></span></li>
+ </ul>
+ </div>
+ <div class="column text-center">
+ <h4>Top 10 hours</h4>
+ <ul data-bind='foreach: hours'>
+ <li><span data-bind='text: hour'></span>:
$<span data-bind='text: total'></span></li>
+ </ul>
+ </div>
+ </div>
+ <div class="modal fade" id="top10FilterModal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close"
data-dismiss="modal"><span aria-hidden="true">×</span><span
class="sr-only">Close</span></button>
+ <h4 class="modal-title">Filters</h4>
+ </div>
+ <div class="modal-body">
+ <filters params="change:
logStateChange, userChoices: userChoices, metadataRequest: metadataRequest,
queryString: filterQueryString" />
+ </div>
+ </div>
+ </div>
+ </div>
+
+
+ <div class="modal fade" id="top10SqlModal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close"
data-dismiss="modal"><span aria-hidden="true">×</span><span
class="sr-only">Close</span></button>
+ <h4 class="modal-title">Top 10 SQL:</h4>
+ </div>
+ <div class="modal-body">
+ <p data-bind="text:queryStringSQL"></p>
+ </div>
+ </div><!-- /.modal-content -->
+ </div><!-- /.modal-dialog -->
+ </div><!-- /.modal -->
+
+ <div class="modal fade" data-bind="attr: { id: 'remove'+instanceID } ">
+ <div class="modal-dialog modal-sm">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h2>Remove this widget from the
board?</h2>
+ </div>
+ <div class="modal-body">
+ <button class="btn btn-lg btn-success
pull-right" data-dismiss="modal" data-bind="click:
$parents[2].removeWidgetFromBoard"> OK</button>
+ <button class="btn btn-lg btn-danger
pull-right" data-dismiss="modal"> Cancel</button>
+ </div>
+ </div><!-- /.modal-content -->
+ </div>
+ </div>
+
+</div>
diff --git a/src/components/widgets/top10/top10.js
b/src/components/widgets/top10/top10.js
new file mode 100644
index 0000000..53097a1
--- /dev/null
+++ b/src/components/widgets/top10/top10.js
@@ -0,0 +1,58 @@
+define([
+ 'knockout',
+ 'text!components/widgets/top10/top10.html',
+ 'momentjs',
+ 'WidgetBase'
+ ],
+function( ko, template, moment, WidgetBase ){
+
+ function Top10ViewModel( params ){
+
+ WidgetBase.call( this, params );
+
+ var self = this;
+
+ self.title = ko.observable(params.title);
+ self.queryStringSQL = ko.observable('This widget hasn\'t been
set up yet!');
+ self.days = ko.observableArray();
+ self.hours = ko.observableArray();
+
+ // Reload the data. For the automatic reload, we're fine
getting
+ // something from the cache.
+ self.reloadData = function( automatic ){
+ self.dataLoading(true);
+ var url = '/data/top10';
+ if ( automatic !== true ) {
+ url += '?cache=false';
+ }
+ $.get( url , function ( dataget ) {
+ var r, i, hours = [], days = [];
+ for ( i = 0; i < dataget.results.length; i++ ) {
+ r = dataget.results[i];
+ if ( r.hour === -1 ) {
+ days.push( {
+ date: r.year + '-' +
r.month + '-' + r.day,
+ total: r.usd_total
+ } );
+ } else {
+ hours.push( {
+ hour: r.year + '-' +
r.month + '-' + r.day + ' ' + r.hour + ':00',
+ total: r.usd_total
+ } );
+ }
+ }
+ self.days( days );
+ self.hours( hours );
+ self.dataLoading( false );
+ self.queryStringSQL( dataget.sqlQuery );
+ });
+ // Do it every 5 minutes as well
+ setTimeout( function () {
+ self.reloadData( true );
+ }, 300000 );
+ };
+ self.reloadData( true );
+ }
+
+ return { viewModel: Top10ViewModel, template: template };
+});
diff --git a/widgets/top10.js b/widgets/top10.js
new file mode 100644
index 0000000..2a82c3b
--- /dev/null
+++ b/widgets/top10.js
@@ -0,0 +1,24 @@
+var extend = require( 'node.extend' ),
+ commonFilters = require( './common-filters.js' ),
+ config = require( '../config.js' ),
+ filters = {};
+
+extend( filters, commonFilters.paymentsInitial );
+extend( filters, commonFilters.contributionTracking );
+extend( filters, commonFilters.civicrmContribution );
+
+module.exports = {
+ name: 'top10',
+ query: 'SELECT * FROM (SELECT YEAR(receive_date) AS year,
MONTH(receive_date) AS month, DAY(receive_date) AS day, -1 AS hour, count(*) AS
donations, SUM(total_amount) AS usd_total FROM ' + config.civicrmDb +
'.civicrm_contribution cc [[JOINS]] [[WHERE]] GROUP BY YEAR(receive_date),
MONTH(receive_date), DAY(receive_date) ORDER BY usd_total DESC LIMIT 10) AS
top_days UNION (SELECT YEAR(receive_date) AS year, MONTH(receive_date) AS
month, DAY(receive_date) AS day, HOUR(receive_date) AS hour, count(*) AS
donations, SUM(total_amount) AS usd_total FROM ' + config.civicrmDb +
'.civicrm_contribution cc [[JOINS]] [[WHERE]] GROUP BY YEAR(receive_date),
MONTH(receive_date), DAY(receive_date), HOUR(receive_date) ORDER BY usd_total
DESC LIMIT 10);',
+ mainTableAlias: 'cc',
+ optionalJoins: {
+ ct: {
+ text: 'LEFT JOIN drupal.contribution_tracking ct ON
ct.contribution_id = cc.id'
+ },
+ pi: {
+ text: 'LEFT JOIN payments_initial pi ON
pi.contribution_tracking_id = ct.id',
+ requires: [ 'ct' ]
+ }
+ },
+ filters: filters
+};
--
To view, visit https://gerrit.wikimedia.org/r/327401
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I183615c9cb077f078cafa9602fe36aa8fcfbe31a
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/fundraising/dash
Gerrit-Branch: master
Gerrit-Owner: Ejegg <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits