Ejegg has submitted this change and it was merged.
Change subject: Data retrieval/superclassing.
......................................................................
Data retrieval/superclassing.
Change-Id: I8c685154bf3da6659996a5104dfd30115f9e30d1
---
M src/app/require.config.js
A src/app/widgetBase.js
M src/components/app-content/app-content.js
M src/components/boards/generic-board/generic-board.html
M src/components/nav-bar/nav-bar.html
M src/components/widgets/amt-per-second-chart/amt-per-second-chart.html
M src/components/widgets/amt-per-second-chart/amt-per-second-chart.js
M src/components/widgets/distance-to-goal-chart/distance-to-goal-chart.js
M src/components/widgets/fraud-gauge/fraud-gauge.html
M src/components/widgets/fraud-gauge/fraud-gauge.js
M src/components/widgets/x-by-y/x-by-y.html
M src/components/widgets/x-by-y/x-by-y.js
M src/css/style.css
M widgets/x-by-y.js
14 files changed, 771 insertions(+), 235 deletions(-)
Approvals:
Ejegg: Looks good to me, approved
diff --git a/src/app/require.config.js b/src/app/require.config.js
index 68a15b5..4b15b77 100644
--- a/src/app/require.config.js
+++ b/src/app/require.config.js
@@ -24,7 +24,8 @@
'select2': 'bower_modules/select2//select2',
'c3': 'bower_modules/c3/c3',
'numeraljs': 'bower_modules/numeraljs/numeral',
- 'decemberData': 'bower_modules/fakeData/decemberData'
+ 'decemberData': 'bower_modules/fakeData/decemberData',
+ 'WidgetBase': 'app/widgetBase'
},
shim: {
'bootstrap': {
diff --git a/src/app/widgetBase.js b/src/app/widgetBase.js
new file mode 100644
index 0000000..f1dd56b
--- /dev/null
+++ b/src/app/widgetBase.js
@@ -0,0 +1,215 @@
+define([
+ 'jquery',
+ 'knockout',
+ 'momentjs'
+], function( $, ko, moment ){
+
+ function WidgetBase( params ){
+
+ var self = this;
+
+ self.retrievedResults = ko.observable();
+ self.queryStringSQL = ko.observable('This widget hasn\'t
been set up yet!');
+ self.config = params.configuration;
+ self.instanceID = params.widgetInstance;
+ self.widgetCode = params.widgetCode;
+ self.preDataLoading = ko.observable(true);
+ self.dataLoading = ko.observable(!!self.config);
+ self.chartSaved = ko.observable(!!self.config);
+ self.optionStateChanged = ko.observable(false);
+ self.chartWidth = ko.observable('900');
+ self.chartHeight = ko.observable('550');
+ self.chartLoaded = ko.observable(false);
+
+ self.getChartData = function( qs ){
+ self.dataLoading(true);
+ return $.ajax({
+ url: '/data/' + self.widgetCode + '?' + ( qs
).replace( /\+/g, '%20' ),
+ success: function ( dataget ) {
+ self.retrievedResults( dataget.results );
+ self.queryStringSQL( dataget.sqlQuery );
+ }
+ });
+ };
+
+ self.saveWidgetConfig = function(){
+
+ if( self.instanceID ){
+ $.ajax({
+ method: 'PUT',
+ url: '/widget-instance/' + self.instanceID,
+ contentType: 'application/json; charset=UTF-8',
+ data: JSON.stringify({
+ configuration: self.config,
+ isShared: false
+ }),
+ success: function( data ) {
+ self.chartSaved(true);
+ }
+ });
+ } else {
+ $.ajax({
+ method: 'POST',
+ url: '/widget-instance/',
+ contentType: 'application/json; charset=UTF-8',
+ data: JSON.stringify({
+ configuration: self.config,
+ isShared: false
+ }),
+ success: function( data ) {
+ self.instanceID = data.id;
+ self.chartSaved(true);
+ }
+ });
+ }
+
+ };
+
+ self.processData = function(rawdata, timescale){
+
+ var dailyDataArray =
['Daily Total'],
+ dailyCountArray =
['Daily Count'],
+ secondsByHourDonationData = ['Donations
Per Second'],
+ dayObj
= {}, returnObj;
+
+ switch(timescale){
+ case 'Year':
+ case 'Month':
+ var monthlyDataArray = ['Monthly
Total'],
+ monthlyCountArray = ['Monthly Count'],
+ months = rawdata;
+
+ $.each(months, function(i, el){
+
monthlyDataArray.push(el.usd_total);
+
monthlyCountArray.push(el.donations);
+ });
+
+ returnObj = {
+
timescale: timescale,
+
monthlyDataArray: monthlyDataArray,
+
monthlyCountArray: monthlyCountArray
+ };
+ return returnObj;
+ case 'Day':
+ case 'Hour':
+ for (var d = 1; d < 32; d++) {
+ dailyDataArray[d] = 0;
+ dailyCountArray[d] = 0;
+ if (!dayObj[d]) {
+ dayObj[d] = new
Array(25);
+ dayObj[d][0] = 'Hourly
Totals';
+ for (var h = 0; h < 24;
h++) {
+ dayObj[d][h +
1] = { total: 0, count: 0 };
+
secondsByHourDonationData[(d - 1) * 24 + h + 1] = 0;
+ }
+ }
+ }
+
+ var dataCount = rawdata.length;
+ for (var i = 0; i < dataCount; i++ ) {
+
+ var el = rawdata[i],
+ day = el.Day,
+ hour = el.hour,
+ total =
el.usd_total,
+ runningTotal =
0;
+
+ if(!hour){
+ dayObj[day+1] = {
total: total, count: el.donations };
+ } else {
+ dayObj[day][hour + 1] =
{ total: total, count: el.donations };
+ }
+
+ secondsByHourDonationData[(day
- 1) * 24 + hour + 1] = el.usd_per_second;
+ runningTotal += total;
+ dailyDataArray[day] += total;
+ dailyCountArray[day] +=
el.donations;
+ }
+
+ returnObj = {
+
timescale: timescale,
+
dailyDataArray: dailyDataArray,
+
dailyCountArray: dailyCountArray,
+
secondsByHourDonationData: secondsByHourDonationData,
+
dayObj: dayObj
+ };
+
+ return returnObj;
+ }
+
+ };
+
+ self.convertToQuery = function( userChoices ){
+
+ var timeBreakout = 'group=' + userChoices.timeBreakout;
+ //groupStr = timeBreakout + '&group=' +
userChoices.xSlice;
+
+ // if( userChoices.additionalFilters.length > 0 ){
+
+ // var filterStr = '$filter=', filterObj = {},
haveMultipleSubfilters = [];
+
+ // $.each( userChoices.additionalFilters, function( el,
subfilter ){
+ // var filter = subfilter.substr(0, subfilter.indexOf('
'));
+ // if(!filterObj[ filter ]){
+ // filterObj[ filter ] = subfilter;
+ // } else {
+ // filterObj[ filter ] += ' or ' + subfilter;
+ // haveMultipleSubfilters.push( filter );
+ // }
+ // });
+
+ // $.each( filterObj, function( el, s ){
+ // if( haveMultipleSubfilters.indexOf( el ) > -1){
+ // filterStr += '(' + filterObj[ el ] + ')';
+ // } else {
+ // filterStr += filterObj[ el ];
+ // }
+ // filterStr += ' and ';
+ // });
+
+ // if( filterStr !== '$filter=' ){
+ // return groupStr + '&' + ( filterStr.slice(0, -5) );
+ // } else {
+ // return groupStr;
+ // }
+ // } else {
+ // return groupStr;
+ // }
+ return timeBreakout;
+ };
+
+ // Generate chart label arrays for time increment types
+ self.chartLabels = function(type){
+ var chartLabels;
+ switch(type){
+ case 'Year':
+ chartLabels = ['Year'];
+ break;
+ case 'Month':
+ chartLabels = ['Jan', 'Feb', 'Mar', 'Apr', 'May',
'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ break;
+ case 'Day':
+ chartLabels = [ '01', '02', '03', '04', '05', '06',
'07', '08', '09', '10',
+ '11', '12', '13', '14', '15', '16',
'17', '18', '19', '20',
+ '21', '22', '23', '24', '25', '26',
'27', '28', '29', '30', '31'];
+ break;
+ case 'Hour':
+ chartLabels = [ '00:00', '01:00', '02:00', '03:00',
'04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00',
+ '11:00', '12:00', '13:00', '14:00',
'15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00',
+ '22:00', '23:00'];
+ break;
+ }
+ return chartLabels;
+ };
+
+ self.logStateChange = function(n){
+ self.optionStateChanged(n);
+ self.chartSaved(false);
+ };
+
+ return(this);
+ }
+
+ return( WidgetBase );
+
+});
diff --git a/src/components/app-content/app-content.js
b/src/components/app-content/app-content.js
index 894bd67..dce8604 100644
--- a/src/components/app-content/app-content.js
+++ b/src/components/app-content/app-content.js
@@ -42,6 +42,8 @@
var pages = ['Library', 'Profile', 'Home'], view = data.target.id;
if( pages.indexOf(data.target.id) > -1 ){
self.displayPage(view);
+ } else if( typeof view !== 'number' ) {
+ self.displayPage($.trim($(data.target).text()));
} else {
$.get('board/' + view, function( bdata ){
console.log('get board #', data.target.id);
diff --git a/src/components/boards/generic-board/generic-board.html
b/src/components/boards/generic-board/generic-board.html
index 9408094..dcdcc9e 100644
--- a/src/components/boards/generic-board/generic-board.html
+++ b/src/components/boards/generic-board/generic-board.html
@@ -20,7 +20,7 @@
</span>
<div class="row" data-bind="foreach: displayedBoard().widgets">
- <div data-bind="component: { name: widgetCode, params: { title:
displayName, description: description, configuration: configuration } }"><span
data-bind="text: display_name"></span></div>
+ <div data-bind="component: { name: widgetCode, params: { title:
displayName, description: description, configuration: configuration,
widgetInstance: id, widgetCode: widgetCode } }"><span data-bind="text:
display_name"></span></div>
</div>
</div>
\ No newline at end of file
diff --git a/src/components/nav-bar/nav-bar.html
b/src/components/nav-bar/nav-bar.html
index 7f356de..b1fb42a 100644
--- a/src/components/nav-bar/nav-bar.html
+++ b/src/components/nav-bar/nav-bar.html
@@ -43,7 +43,7 @@
</ul>
</div>
- <div class="mainNavButton" id="Library"
data-bind="click: $parent.setDisplayPage"><i class="fa fa-plug"></i><span>
Library</span>
+ <div class="mainNavButton" id="Library"
data-bind="click: $parent.setDisplayPage"><i class="fa fa-plug"></i><span
id="LibraryLink"> Library</span>
</div>
</span>
diff --git
a/src/components/widgets/amt-per-second-chart/amt-per-second-chart.html
b/src/components/widgets/amt-per-second-chart/amt-per-second-chart.html
index 51150ab..71185d3 100644
--- a/src/components/widgets/amt-per-second-chart/amt-per-second-chart.html
+++ b/src/components/widgets/amt-per-second-chart/amt-per-second-chart.html
@@ -1,7 +1,7 @@
<div class="col-md-6 lastWidget">
<div class="panel panel-purple">
<div class="panel-heading">
- <p data-bind="text: title">USD/Second Required
(Average)</p>
+ <p>USD/Second Required (Average)</p>
</div>
<div class="panel-body">
<div class="col-md-12">
diff --git
a/src/components/widgets/amt-per-second-chart/amt-per-second-chart.js
b/src/components/widgets/amt-per-second-chart/amt-per-second-chart.js
index 1647056..bf4824b 100644
--- a/src/components/widgets/amt-per-second-chart/amt-per-second-chart.js
+++ b/src/components/widgets/amt-per-second-chart/amt-per-second-chart.js
@@ -2,39 +2,82 @@
'knockout',
'text!components/widgets/amt-per-second-chart/amt-per-second-chart.html',
'c3',
- 'numeraljs'
-], function( ko, template, c3, numeral ){
+ 'numeraljs',
+ 'momentjs'
+], function( ko, template, c3, numeral, moment ){
function AmtPerSecondChartViewModel( params ){
var self = this;
- self.title = ko.observable(params.title);
+ //TODO: make dayObj (and other params) come from data
+ self.dayObj = [];
+
+ self.loadData = function ( decemberData, timestamp ) {
+ var runningTotal = 0,
+ currentDate = new Date(),
+ timeFormat = 'dddd, MMMM Do YYYY, h:mm:ss a';
+
+ currentDate.setTime( timestamp );
+ self.displayDate( moment( currentDate ).format(
timeFormat ) );
+ self.lastDataPoint.day = currentDate.getUTCDate();
+ self.lastDataPoint.hour = currentDate.getUTCHours();
+
+ for (var d = 1; d < 32; d++) {
+ self.dailyDataArray[d] = 0;
+ self.dailyCountArray[d] = 0;
+ if (!self.dayObj[d]) {
+ self.dayObj[d] = new Array(25);
+ self.dayObj[d][0] = 'Hourly Totals';
+ for (var h = 0; h < 24; h++) {
+ self.dayObj[d][h + 1] = {
total: 0, count: 0 };
+
self.secondsByHourDonationData[(d - 1) * 24 + h + 1] = 0;
+ }
+ }
+ }
+
+ var dataCount = decemberData.length;
+ for (var i = 0; i < dataCount; i++ ) {
+
+ var el = decemberData[i],
+ day = el.day,
+ hour = el.hour,
+ total = el.usd_total;
+ self.dayObj[day][hour + 1] = { total: total,
count: el.donations };
+
+ self.secondsByHourDonationData[(day - 1) * 24 +
hour + 1] = el.usd_per_second;
+ runningTotal += total;
+ self.dailyDataArray[day] += total;
+ self.dailyCountArray[day] += el.donations;
+ }
+
+ self.raised(runningTotal);
+ };
self.makeChart = function() {
- if ( params.dayObj.length < 2 ) {
+ if ( self.dayObj.length < 2 ) {
return;
}
- var numPoints = ( params.lastDataPoint.day - 1 ) * 24 +
params.lastDataPoint.hour + 1,
+ var numPoints = ( self.lastDataPoint.day - 1 ) * 24 +
self.lastDataPoint.hour + 1,
xs = new Array( numPoints + 2 ), // label, data
to date, final point
index = 0,
- remainingNeeded = params.goal();
+ remainingNeeded = self.goal();
xs[0] = 'x1';
self.needPerSecond = new Array( numPoints + 2 );
self.needPerSecond[0] = 'Needed Per Second';
// secondsByHourDonationData already has a label in [0]
- self.gotPerSecond =
params.secondsByHourDonationData.slice( 0, numPoints + 1 );
+ self.gotPerSecond =
self.secondsByHourDonationData.slice( 0, numPoints + 1 );
- for( var d = 1; d < params.dayObj.length; d++ ) {
+ for( var d = 1; d < self.dayObj.length; d++ ) {
for ( var h = 0; h < 24; h++ ) {
index = ( d - 1 ) * 24 + h + 1;
if ( index > numPoints + 1 ) {
break;
}
- remainingNeeded = remainingNeeded -
params.dayObj[d][h + 1].total;
+ remainingNeeded = remainingNeeded -
self.dayObj[d][h + 1].total;
if ( remainingNeeded < 0 ) {
remainingNeeded = 0;
}
@@ -98,7 +141,6 @@
}
} );
};
- params.dataChanged.subscribe(self.makeChart);
self.makeChart();
}
diff --git
a/src/components/widgets/distance-to-goal-chart/distance-to-goal-chart.js
b/src/components/widgets/distance-to-goal-chart/distance-to-goal-chart.js
index 68cc542..bd52553 100644
--- a/src/components/widgets/distance-to-goal-chart/distance-to-goal-chart.js
+++ b/src/components/widgets/distance-to-goal-chart/distance-to-goal-chart.js
@@ -1,25 +1,79 @@
define( [
'knockout',
'text!components/widgets/distance-to-goal-chart/distance-to-goal-chart.html',
- 'c3'
-], function( ko, template, c3 ){
+ 'c3',
+ 'momentjs'
+], function( ko, template, c3, moment ){
function DistanceToGoalChartViewModel( params ){
var self = this;
+ self.goal = ko.observable('20,000,000');
+ self.dailyDataArray = [ 'Daily Total' ];
+
+ self.loadData = function ( decemberData, timestamp ) {
+ var runningTotal = 0,
+ currentDate = new Date(),
+ timeFormat = 'dddd, MMMM Do YYYY, h:mm:ss a';
+ currentDate.setTime( timestamp );
+ self.displayDate( moment( currentDate ).format(
timeFormat ) );
+ self.lastDataPoint.day = currentDate.getUTCDate();
+ self.lastDataPoint.hour = currentDate.getUTCHours();
+
+ for (var d = 1; d < 32; d++) {
+ self.dailyDataArray[d] = 0;
+ self.dailyCountArray[d] = 0;
+ if (!self.dayObj[d]) {
+ self.dayObj[d] = new Array(25);
+ self.dayObj[d][0] = 'Hourly Totals';
+ for (var h = 0; h < 24; h++) {
+ self.dayObj[d][h + 1] = {
total: 0, count: 0 };
+
self.secondsByHourDonationData[(d - 1) * 24 + h + 1] = 0;
+ }
+ }
+ }
+
+ var dataCount = decemberData.length;
+ for (var i = 0; i < dataCount; i++ ) {
+
+ var el = decemberData[i],
+ day = el.day,
+ hour = el.hour,
+ total = el.usd_total;
+ self.dayObj[day][hour + 1] = { total: total,
count: el.donations };
+
+ self.secondsByHourDonationData[(day - 1) * 24 +
hour + 1] = el.usd_per_second;
+ runningTotal += total;
+ self.dailyDataArray[day] += total;
+ self.dailyCountArray[day] += el.donations;
+ }
+
+ //self.makeCharts();
+
+ self.raised(runningTotal);
+ };
+
+ // $.get('data/big-english', function(dtgData){
+
+ // console.log('dtgData', dtgData);
+
+ // self.dailyDataArray;
+
+
+ // });
self.title = ko.observable(params.title);
self.makeCharts = function() {
- if ( params.dailyDataArray.length < 2 ) {
+ if ( self.dailyDataArray.length < 2 ) {
return;
}
- self.goal = ko.observable(params.goal);
+ self.goal = ko.observable(self.goal);
- self.updatedGoal = params.goal();
+ self.updatedGoal = self.goal();
self.neededArray = ['Needed'];
- for(var d = 1; d < params.dailyDataArray.length; d++) {
- self.updatedGoal = self.updatedGoal -
params.dailyDataArray[d];
+ for(var d = 1; d < self.dailyDataArray.length; d++) {
+ self.updatedGoal = self.updatedGoal -
self.dailyDataArray[d];
self.neededArray[d] = self.updatedGoal >= 0 ?
self.updatedGoal : 0;
}
@@ -60,10 +114,8 @@
}
});
};
- params.dataChanged.subscribe(function() {
- self.makeCharts();
- });
- self.makeCharts();
+
+ //self.makeCharts();
}
return { viewModel: DistanceToGoalChartViewModel, template: template };
diff --git a/src/components/widgets/fraud-gauge/fraud-gauge.html
b/src/components/widgets/fraud-gauge/fraud-gauge.html
index ee4c49a..e3b9fed 100644
--- a/src/components/widgets/fraud-gauge/fraud-gauge.html
+++ b/src/components/widgets/fraud-gauge/fraud-gauge.html
@@ -1,4 +1,5 @@
<div class="row">
+ <div data-bind="attr: {class: columnSize}">
<div class="panel panel-purple" id="fraudGaugeWidget">
<div class="panel-heading">
<span data-bind='text: title'></span>
@@ -10,7 +11,7 @@
<div class="panel-body">
<div class="row">
- <div class="col-md-12 gauge">
+ <div class="gauge">
<div class="row">
<h3 class="text-center" data-bind="visible:
gaugeIsSetUp(), text: selectedTimePeriod"></h3>
@@ -126,6 +127,6 @@
</div>
</div>
-
+ </div>
</div>
</div>
diff --git a/src/components/widgets/fraud-gauge/fraud-gauge.js
b/src/components/widgets/fraud-gauge/fraud-gauge.js
index c3b7824..8fca0bd 100644
--- a/src/components/widgets/fraud-gauge/fraud-gauge.js
+++ b/src/components/widgets/fraud-gauge/fraud-gauge.js
@@ -33,6 +33,8 @@
var self = this;
self.filters = ko.observableArray();
self.title = ko.observable(params.title);
+ console.log('fraud widget params: ', params);
+ self.columnSize = 'col-md-' + params.configuration.width + ' fraudGauge';
$.get( 'metadata/fraud-gauge', function(reqData){
self.data = reqData;
diff --git a/src/components/widgets/x-by-y/x-by-y.html
b/src/components/widgets/x-by-y/x-by-y.html
index 0289139..3229239 100644
--- a/src/components/widgets/x-by-y/x-by-y.html
+++ b/src/components/widgets/x-by-y/x-by-y.html
@@ -1,16 +1,22 @@
<div class="row">
+
+ <span data-bind="if: preDataLoading">
+ <div class="loadingWidget">
+ <img src="/images/catloader.gif" />
+ <h3 class="text-center">Loading.....</h3>
+ </div>
+ </span>
+
<div class="panel panel-purple">
<div class="panel-heading">
<div class="btn-group btn-group-xs pull-right">
<button type="button" class="btn btn-default
dropdown-toggle" data-toggle="modal" data-target="#XYsqlModal"><i class="fa
fa-database"></i></button>
- <button type="button" id="savedCharts" class="btn
btn-default dropdown-toggle" data-toggle="dropdown"
data-target="#XYsavedChart"><i class="fa fa-area-chart"></i> Saved
charts...</button>
+ <!-- <button type="button" id="savedCharts" class="btn
btn-default dropdown-toggle" data-toggle="dropdown"
data-target="#XYsavedChart"><i class="fa fa-area-chart"></i> Saved
charts...</button>
<ul class="dropdown-menu" role="menu"
aria-labelledby="savedCharts" id="savedChartsMenu" data-bind="foreach:
presetTitles, style: { width: '300px'}">
<li data-bind="text: $data"></li>
- </ul>
+ </ul> -->
</div>
</div>
-
-
<div class="panel-body">
<div class="row-fluid">
@@ -24,19 +30,19 @@
<select
data-bind="options: ySlices, value: showSlice"></select>
<hr>
</div>
- <div class="row-fluid">
+ <!-- <div class="row-fluid">
<h4>By:</h4>
<select
data-bind="options: xSlices, value: bySlice"></select>
<hr>
- </div>
+ </div> -->
<div class="row-fluid">
<h4>Starting
time range:</h4><br>
- <select
id="startingTimeRange" placeholder="Range..." data-bind="options:
timeChoices"></select>
- <hr>
+ <select
id="startingTimeRange" placeholder="Range..." data-bind="options: timeChoices,
value: timeChoice"></select>
+ <!--
<hr> -->
</div>
- <div class="row-fluid">
+ <!-- <div
class="row-fluid">
<label
for="selectXYFilters">Additional filters:</label><br>
<span
data-bind="foreach: groupChoices">
<div
class="panel panel-default xyGroupOption">
@@ -55,7 +61,7 @@
</div>
</div>
</span>
- </div>
+ </div> -->
</form>
</div>
@@ -65,7 +71,7 @@
<button class="btn btn-block btn-col btn-info"
data-bind="click: submitXY"><i class="fa fa-area-chart"></i> Preview chart
</button>
</div>
<div class="row">
- <button class="btn btn-block btn-col
btn-danger" data-bind="visible: !chartSaved() && optionStateChanged, click:
saveXY"><i class="fa fa-save"></i> Save chart </button>
+ <button class="btn btn-block btn-col
btn-danger" data-bind="visible: !chartSaved() && optionStateChanged, click:
saveWidgetConfig"><i class="fa fa-save"></i> Save chart </button>
</div>
<div class="row">
<button class="btn btn-block btn-col
btn-success" data-bind="visible: chartSaved"><i class="fa
fa-check-circle-o"></i> Chart saved. </button>
@@ -76,8 +82,8 @@
<div class="col-md-9 col-sm-12">
<div class="row" id="specifiedXYchart">
- <div class="col-md-10">
- <span data-bind="if: !xyIsSetUp()">
+ <div class="col-md-12">
+ <span data-bind="if: !config">
<div class="row-fluid alert
alert-danger" data-bind="style: { width: '100%', overflow: 'hidden'}">
<div class="col-md-1">
<h1><i class="fa
fa-gears"></i></h1>
@@ -87,8 +93,10 @@
<p>Choose display
options to see the chart.</p>
</div>
</div>
- </span>
- <div class="row-fluid" data-bind="if:
xyIsSetUp">
+ </span>
+
+
+ <div class="row-fluid" data-bind="if:
chartLoaded">
<h1 data-bind="text: title"></h1>
<h4 data-bind="visible:
chosenFilters > 0">Narrowed by:
<span data-bind="foreach:
chosenFilters">
@@ -96,15 +104,15 @@
</span>
</h4>
</div>
- <canvas id='x-by-yChart' height="550"
width="900"></canvas>
+ <div id='x-by-yChart' data-bind="attr: {
width: chartWidth, height: chartHeight }"></div>
</div>
- <div class="col-md-2 pull-right">
+ <!-- <div class="col-md-2 pull-right">
<button class="btn-info"><i class="fa
fa-bar-chart"></i></button>
<button class="btn-info"><i class="fa
fa-line-chart"></i></button>
<button class="btn-info"><i class="fa
fa-pie-chart"></i></button>
<button class="btn-info"><i class="fa
fa-table"></i></button>
- </div>
+ </div> -->
</div>
</div>
@@ -146,7 +154,19 @@
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title">Fraud Gauge SQL:</h4>
</div>
- <div class="modal-body" data-bind="text: queryStringXYsql"></div>
+ <div class="modal-body" data-bind="text: queryStringSQL"></div>
+ </div><!-- /.modal-content -->
+ </div>
+</div>
+
+<div class="modal fade" id="loadingModal">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <h3 class="modal-body">Chart Loading....</h3>
+ <div class="progress">
+ <div class="progress-bar progress-bar-success
progress-bar-striped active" role="progressbar" aria-valuenow="100"
aria-valuemin="0" aria-valuemax="100" style="width: 100%">
+ </div>
+ </div>
</div><!-- /.modal-content -->
</div>
</div>
\ No newline at end of file
diff --git a/src/components/widgets/x-by-y/x-by-y.js
b/src/components/widgets/x-by-y/x-by-y.js
index 257c24a..44dcffe 100644
--- a/src/components/widgets/x-by-y/x-by-y.js
+++ b/src/components/widgets/x-by-y/x-by-y.js
@@ -3,194 +3,369 @@
'text!components/widgets/x-by-y/x-by-y.html',
'momentjs',
'numeraljs',
- 'chartjs',
- 'select2'
-], function( ko, template, moment, numeral, Chart, select2 ){
-
+ 'c3',
+ 'select2',
+ 'WidgetBase'
+], function( ko, template, moment, numeral, c3, select2, WidgetBase ){
function XByYChartViewModel( params ){
+ WidgetBase.call( this, params );
var self = this;
- self.xyIsSetUp = ko.observable(false);
- self.chartWidth = ko.observable('900');
- self.chartHeight = ko.observable('550');
- self.showSlice = ko.observable();
- self.bySlice = ko.observable();
- self.timeChoice = ko.observable();
+ var chartDataCall =
self.getChartData(params.configuration.queryString);
- self.queryStringXYsql = ko.observable('This widget hasn\'t been set up
yet!');
- self.queryRequest = {};
- self.chosenFilters = ko.observableArray();
- self.subChoices = ko.observableArray();
+ $.when( chartDataCall ).then( function( dataArray ){
+ self.retrievedResults(dataArray.results);
+ self.dataLoading(false);
+ self.preDataLoading(false);
- self.chartSaved = ko.observable(false);
- self.optionStateChanged = ko.observable(false);
- self.logStateChange = function(n){
- self.optionStateChanged(n);
- self.chartSaved(false);
- };
+ self.chartData = self.processData(self.retrievedResults(),
params.configuration.timeBreakout);
- self.title = ko.computed(function(){
- return self.showSlice() + ' by ' + self.bySlice();
+ self.makeChart(self.chartData);
});
- self.showPanelBody = function(area){
- $('#'+area+'body').toggleClass('hide');
- };
+ self.showSlice = ko.observable();
+ self.bySlice = ko.observable();
+ self.timeChoice = ko.observable();
+ self.queryRequest = {};
+ self.queryString = '';
+ self.chosenFilters = ko.observableArray();
+ self.subChoices = ko.observableArray();
+ self.chartWidth(950);
- //saved charts
- //TODO: these will trigger a saved set of parameters to draw the chart
with.
- self.presetTitles = ko.observableArray([
- 'Donations During Big English 2014',
- 'Donations for Fiscal Year 2014'
- ]);
- ///////
+ self.title = ko.computed(function(){
+ return self.showSlice(); //+ ' by ' + self.bySlice();
+ });
- self.ySlices = ko.observableArray([
- 'Donations',
- 'Failed Donations'
- ]);
+ self.makeChart = function(data){
- self.xSlices = ko.observableArray();
+ self.chartLoaded(true);
- self.timeChoices = ko.observableArray();
+ self.monthlyChart = function(d,i){
- self.groupChoices = ko.observableArray();
+ var monthNamesArray = ['Jan', 'Feb', 'Mar', 'Apr', 'May',
'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
- //populate user choices dynamically
- self.populateChoices = (function(){
- //populate y slices
- //TODO: this one will be user-defined, and developed in full later
- //since right now the specifics are a bit obscured.
- $.get( 'metadata/x-by-y', function(reqData){
- self.metadata = reqData;
+ return {
+ bindto: '#x-by-yChart',
+ size: {
+ height: 450,
+ width: window.width
+ },
+ zoom: { enabled: true },
+ data: {
+ columns: [ data.monthlyCountArray,
data.monthlyDataArray ],
+ type: 'bar',
+ colors: { 'Monthly Total': 'rgb(92,184,92)',
'Monthly Count': '#f0ad4e' },
+ axes: {
+ 'Monthly Total': 'y',
+ 'Monthly Count': 'y2'
+ }
+ },
+ grid: {
+ x: {
+ show: true
+ },
+ y: {
+ show: true
+ }
+ },
+ axis: {
+ x: {
+ tick: {
+ format: function(x){ return
monthNamesArray[x]; }
+ }
+ },
+ y: {
+ tick: {
+ format: function(x){ return
numeral(x).format('$0,0'); }
+ }
+ },
+ y2: {
+ tick: {
+ format: function(x){ return
numeral(x).format('0,0'); }
+ },
+ show: true
+ }
+ },
+ tooltip: {
+ format: {
+ title: function (d) {
+ return monthNamesArray[d];
+ },
+ value: function (value, ratio, id) {
+ var display;
+ if(id === 'Monthly Total'){
+ display =
numeral(value).format('$0,0');
+ } else {
+ display = numeral(value).format('0,0');
+ }
+ return display;
+ }
+ }
+ },
+ bar: {
+ width: {
+ ratio: 0.5
+ }
+ }
+ };
+ };
+
+ self.hourlyChart = function(d,i){
+ var hourlyData = data.dayObj[d.x + 1 ],
+ hourlyCountArray = ['Hourly Count'],
+ hourlyTotalArray = ['Hourly Total'];
+ for(var j=1; j<25; j++){
+ hourlyCountArray.push(hourlyData[j].count);
+ hourlyTotalArray.push(hourlyData[j].total);
+ }
+ return {
+ bindto: '#x-by-yChart',
+ size: {
+ height: 450,
+ width: window.width
+ },
+ zoom: { enabled: true },
+ data: {
+ columns: [ hourlyTotalArray, hourlyCountArray ],
+ type: 'bar',
+ colors: { 'Hourly Total': 'rgb(92,184,92)',
'Hourly Count': '#f0ad4e' },
+ onclick: function (d, i) {
c3.generate(self.dailyChart()); },
+ axes: {
+ 'Hourly Total': 'y',
+ 'Hourly Count': 'y2'
+ }
+ },
+ grid: {
+ x: {
+ show: true
+ },
+ y: {
+ show: true
+ }
+ },
+ axis: {
+ x: {
+ label: {
+ text: 'December ' + ( d.x + 1 ),
+ position: 'outer-left'
+ },
+ tick: {
+ format: function(x){ return x + ':00'; }
+ }
+ },
+ y: {
+ tick: {
+ format: function(x){ return
numeral(x).format('$0,0'); }
+ }
+ },
+ y2: {
+ tick: {
+ format: function(x){ return
numeral(x).format('0,0'); }
+ },
+ show: true
+ }
+ },
+ tooltip: {
+ format: {
+ title: function (d) { return 'Hour ' + d; },
+ value: function (value, ratio, id) {
+ var display;
+ if(id === 'Hourly Total'){
+ display =
numeral(value).format('$0,0');
+ } else {
+ display = numeral(value).format('0,0');
+ }
+ return display;
+ }
+ }
+ },
+ bar: {
+ width: {
+ ratio: 0.5
+ }
+ }
+ };
+ };
+
+ self.dailyChart = function(d,i){
+ return {
+ bindto: '#x-by-yChart',
+ size: {
+ height: 450,
+ width: window.width
+ },
+ zoom: { enabled: true },
+ data: {
+ columns: [ data.dailyDataArray,
data.dailyCountArray ],
+ type: 'bar',
+ colors: { 'Daily Total': 'rgb(49,176,213)', 'Daily
Count': '#f0ad4e' },
+ onclick: function (d, i) {
+ self.xByYChart =
c3.generate(self.hourlyChart(d,i));
+ },
+ axes: {
+ 'Daily Total': 'y',
+ 'Daily Count': 'y2'
+ }
+ },
+ grid: {
+ x: {
+ show: true
+ },
+ y: {
+ show: true
+ }
+ },
+ axis: {
+ x: {
+ tick: {
+ format: function(x){ return 'Dec ' +
(x+1); }
+ }
+ },
+ y: {
+ tick: {
+ format: function(x){ return
numeral(x).format('$0,0'); }
+ }
+ },
+ y2: {
+ tick: {
+ format: function(x){ return
numeral(x).format('0,0'); }
+ },
+ show: true
+ }
+ },
+ tooltip: {
+ format: {
+ title: function (d) { return 'Day ' + (d+1); },
+ value: function (value, ratio, id) {
+ var display;
+ if(id === 'Daily Total'){
+ display =
numeral(value).format('$0,0');
+ } else {
+ display = numeral(value).format('0,0');
+ }
+ return display;
+ }
+ }
+ },
+ bar: {
+ width: {
+ ratio: 0.5
+ }
+ }
+ };
+ };
- var xArray = [], timeArray = [], groupArray = [];
- $.each(self.metadata.filters, function(prop, obj){
+ switch(data.timescale){
+ case 'Year':
+ case 'Month':
+ self.xByYChart = c3.generate(self.monthlyChart());
+ break;
+ case 'Day':
+ self.xByYChart = c3.generate(self.dailyChart());
+ break;
+ case 'Hour':
+ self.xByYChart = c3.generate(self.hourlyChart());
+ break;
+ }
+ };
- if(obj.type !== 'number' || prop === 'Amount'){
+ if(params.configuration){
+ self.chartSaved(true);
+ //self.makeChart(self.retrievedResults());
- if(obj.canGroup){
- if(obj.values){
- groupArray.push({ 'name': prop, 'choices':
obj.values });
+ } else {
+ self.chartSaved(false);
+ }
+
+ self.showPanelBody = function(area){
+ $('#'+area+'body').toggleClass('hide');
+ };
+
+ //saved charts
+ //TODO: these will trigger a saved set of parameters to draw the
chart with.
+ self.presetTitles = ko.observableArray([
+ 'This does not work yet.',
+ 'Donations During Big English 2014',
+ 'Donations for Fiscal Year 2014'
+ ]);
+ ///////
+
+ self.ySlices = ko.observableArray([
+ 'Donations'
+ //'Failed Donations'
+ ]);
+
+ self.xSlices = ko.observableArray();
+ self.timeChoices = ko.observableArray();
+ self.groupChoices = ko.observableArray();
+
+ //populate user choices dynamically
+ self.populateChoices = (function(){
+ //populate y slices
+ $.get( 'metadata/x-by-y', function(reqData){
+ self.metadata = reqData;
+
+ var xArray = [], timeArray = ['Year', 'Month', 'Day'],
groupArray = [];
+
+ $.each(self.metadata.filters, function(prop, obj){
+
+ if(obj.type !== 'number' || prop === 'Amount'){
+
+ if(obj.canGroup){
+ if(obj.values){
+ groupArray.push({ 'name': prop, 'choices':
obj.values });
+ }
+
+ $('select #'+prop).select2();
+
+ //TODO: later this will do something
different/more specific.
+ xArray.push(prop);
}
- $('select #'+prop).select2();
-
- //TODO: later this will do something
different/more specific.
- xArray.push(prop);
}
+ });
+ self.xSlices(xArray);
+ self.timeChoices(timeArray);
+ self.groupChoices(groupArray);
-
- } else {
- timeArray.push(prop);
- }
- });
- self.xSlices(xArray);
- self.timeChoices(timeArray);
- self.groupChoices(groupArray);
-
- });
-
- })();
-
- self.convertToQuery = function( userChoices ){
- //y slice
- //right now this doesn't matter because it's always 'donations'
-
- var groupStr = 'group=' + userChoices.xSlice;
-
- //additional filters:
- if( userChoices.additionalFilters.length > 0 ){
-
- var filterStr = '$filter=';
-
- var filterObj = {}, haveMultipleSubfilters = [];
- $.each( userChoices.additionalFilters, function(el, subfilter){
- var filter = subfilter.substr(0, subfilter.indexOf(' '));
- if(!filterObj[filter]){
- filterObj[filter] = subfilter;
- } else {
- filterObj[filter] += ' or ' + subfilter;
- haveMultipleSubfilters.push(filter);
- }
});
- $.each( filterObj, function(el, s){
- if( haveMultipleSubfilters.indexOf(el) > -1){
- filterStr += '(' + filterObj[el] + ')';
- } else {
- filterStr += filterObj[el];
- }
- filterStr += ' and ';
+ })();
+
+ self.submitXY = function(){
+
+ $('#loadingModal').modal('show');
+ self.queryRequest.ySlice = self.showSlice();
+ //self.queryRequest.xSlice = self.bySlice();
+ //self.queryRequest.additionalFilters = self.chosenFilters();
+ self.queryRequest.timeBreakout = self.timeChoice();
+
+ self.queryString =
self.convertToQuery(self.queryRequest);
+ self.config.queryString = self.queryString;
+ self.config.timeBreakout = self.queryRequest.timeBreakout;
+ self.config.chartData = self.chartData;
+
+ var chartDataCall = self.getChartData(self.queryString);
+
+ $.when( chartDataCall ).then( function( dataArray ){
+ self.retrievedResults(dataArray.results);
+ self.dataLoading(false);
+
+ self.chartData = self.processData(self.retrievedResults(),
self.timeChoice());
+
+ self.makeChart(self.chartData);
+ $('#loadingModal').modal('hide');
+
+ self.chartSaved(false);
});
- //cut off last AND
- if( filterStr !== '$filter=' ){
- return groupStr + '&' + (filterStr.slice(0, -5));
- } else {
- return groupStr;
- }
- } else {
- return groupStr;
- }
- };
- self.saveXY = function(){
- //TODO: save it in the user profile
- self.chartSaved(true);
- };
+ };
- self.submitXY = function(){
-
- //here is an example query string for grabbing Big English
countries by day for Dec:
- //
http://localhost:8080/data/x-by-y?group=Day&group=Country&$filter=DT gt
- //'2014-12-01T00:00:00Z' and Country eq 'US' or Country eq 'CA' or
Country eq 'NZ'
- //or Country eq 'AU' or Country eq 'GB'
-
- //get all the choices into a queryRequest object
- self.queryRequest.ySlice = self.showSlice();
- self.queryRequest.xSlice = self.bySlice();
- self.queryRequest.additionalFilters = self.chosenFilters();
- var queryString = self.convertToQuery(self.queryRequest);
-
- $.get( '/data/x-by-y?' + (queryString).replace(
- /\+/g, '%20' ), function ( dataget ) {
- console.log('dataget: ', dataget);
- });
-
- self.fakeData = {
- labels: ['January', 'February', 'March', 'April', 'May',
'June', 'July'],
- datasets: [
- {
- label: 'My First dataset',
- fillColor: 'rgba(220,220,220,0.2)',
- strokeColor: 'rgba(220,220,220,1)',
- pointColor: 'rgba(220,220,220,1)',
- pointStrokeColor: '#fff',
- pointHighlightFill: '#fff',
- pointHighlightStroke: 'rgba(220,220,220,1)',
- data: [65, 59, 80, 81, 56, 55, 40]
- },
- {
- label: 'My Second dataset',
- fillColor: 'rgba(151,187,205,0.2)',
- strokeColor: 'rgba(151,187,205,1)',
- pointColor: 'rgba(151,187,205,1)',
- pointStrokeColor: '#fff',
- pointHighlightFill: '#fff',
- pointHighlightStroke: 'rgba(151,187,205,1)',
- data: [28, 48, 40, 19, 86, 27, 90]
- }
- ]
- };
- var ctx = $('#x-by-yChart').get(0).getContext('2d');
- self.fakeChart = new Chart(ctx).Line(self.fakeData);
-
- self.xyIsSetUp(true);
- };
+ return(this);
}
diff --git a/src/css/style.css b/src/css/style.css
index f11f16f..f0a0982 100644
--- a/src/css/style.css
+++ b/src/css/style.css
@@ -232,6 +232,10 @@
/* WIDGET BASE STYLINGS */
+.alert {
+ overflow: hidden;
+}
+
.factbox {
width: 100%;
padding: 7px;
@@ -276,6 +280,10 @@
}
/* Gauges */
+
+.fraudGauge {
+ padding: 0;
+}
#fraudGaugeWidget .panel-body {
padding: 0;
@@ -403,6 +411,10 @@
overflow: hidden;
}
+#loadingModal .modal-content {
+ padding: 10px;
+}
+
.navbar-fixed-top {
height: 70px;
}
@@ -513,21 +525,30 @@
padding: 0 9px 0 0;
}
+.loading, .loadingWidget {
+ background-color: #5bc0de;
+ opacity: .85;
+ border-radius: 25px;
+ padding: 37px;
+ color: #ffffff;
+ z-index: 99999;
+ font-size: 110%;
+ height: 192px;
+ width: 192px;
+}
+
.loading {
position: fixed;
top: 360px;
left: 50%;
margin-top: -96px;
margin-left: -96px;
- background-color: #5bc0de;
- opacity: .85;
- border-radius: 25px;
- width: 192px;
- height: 192px;
- padding: 37px;
- color: #ffffff;
- z-index: 99999;
- font-size: 110%;
+}
+
+.loadingWidget {
+ position: absolute;
+ display: block;
+ margin: 10% 43%;
}
.loading div {
diff --git a/widgets/x-by-y.js b/widgets/x-by-y.js
index cc1715d..98d8afb 100644
--- a/widgets/x-by-y.js
+++ b/widgets/x-by-y.js
@@ -145,28 +145,33 @@
values: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
labels: [ 'Completed', 'Pending', 'Cancelled',
'Failed', 'In Progress', 'Overdue', 'Settled', 'Paid', 'Refunded' ],
canGroup: true
- }
- },
- defaultFilter: {
- type: 'and',
- left: {
- type: 'eq',
- left: { type: 'property', name: 'Year' },
- right: { type: 'literal', value: '2014' }
},
- right: {
- type: 'and',
- left: {
- type: 'eq',
- left: { type: 'property', name: 'Month' },
- right: { type: 'literal', value: '12' }
- },
- right: {
- type: 'lt',
- left: { type: 'property', name: 'Amount' },
- right: { type: 'literal', value: '5000' }
- }
+ YearsAgo: {
+ table: 'cc',
+ column : 'receive_date',
+ func: 'timestampdiff(YEAR, [[COL]], utc_timestamp())',
+ display: 'Years ago',
+ type: 'number',
+ min: 0,
+ max: 12
+ },
+ MonthsAgo: {
+ table: 'cc',
+ column : 'receive_date',
+ func: 'timestampdiff(MONTH, [[COL]], utc_timestamp())',
+ display: 'Months ago',
+ type: 'number',
+ min: 0,
+ max: 10000
+ },
+ DaysAgo: {
+ table: 'cc',
+ column : 'receive_date',
+ func: 'timestampdiff(DAY, [[COL]], utc_timestamp())',
+ display: 'Days ago',
+ type: 'number',
+ min: 0,
+ max: 10000
}
- },
- defaultGroup: ['Day']
+ }
};
--
To view, visit https://gerrit.wikimedia.org/r/190343
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I8c685154bf3da6659996a5104dfd30115f9e30d1
Gerrit-PatchSet: 15
Gerrit-Project: wikimedia/fundraising/dash
Gerrit-Branch: master
Gerrit-Owner: Ssmith <[email protected]>
Gerrit-Reviewer: Ejegg <[email protected]>
Gerrit-Reviewer: Ssmith <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits