Merge branch 'master' of github.com:okfn/recline

This commit is contained in:
Rufus Pollock
2012-08-05 17:04:00 +01:00
3 changed files with 87 additions and 82 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.DS_Store .DS_Store
sandbox/* sandbox/*
.*.swp .*.swp
.*.swo
_site/* _site/*

View File

@@ -31,8 +31,9 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
my.fetch = function(dataset) { my.fetch = function(dataset) {
var dfd = $.Deferred(); var dfd = $.Deferred();
var url = my.getSpreadsheetAPIUrl(dataset.url); var url = my.getSpreadsheetAPIUrl(dataset.url);
$.getJSON(url, function(d) { $.getJSON(url, function(d) {
result = my.parseData(d); var result = my.parseData(d);
var fields = _.map(result.fields, function(fieldId) { var fields = _.map(result.fields, function(fieldId) {
return {id: fieldId}; return {id: fieldId};
}); });
@@ -42,6 +43,7 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
useMemoryStore: true useMemoryStore: true
}); });
}); });
return dfd.promise(); return dfd.promise();
}; };
@@ -56,67 +58,66 @@ this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
// //
// Issues: seems google docs return columns in rows in random order and not even sure whether consistent across rows. // Issues: seems google docs return columns in rows in random order and not even sure whether consistent across rows.
my.parseData = function(gdocsSpreadsheet) { my.parseData = function(gdocsSpreadsheet) {
var options = {}; var options = arguments[1] || {};
if (arguments.length > 1) { var colTypes = options.colTypes || {};
options = arguments[1];
}
var results = { var results = {
fields: [], fields : [],
records: [] records: []
}; };
// default is no special info on type of columns var entries = gdocsSpreadsheet.feed.entry || [];
var colTypes = {}; var key;
if (options.colTypes) { var colName;
colTypes = options.colTypes; // percentage values (e.g. 23.3%)
} var rep = /^([\d\.\-]+)\%$/;
if (gdocsSpreadsheet.feed.entry.length > 0) {
for (var k in gdocsSpreadsheet.feed.entry[0]) { for(key in entries[0]) {
if (k.substr(0, 3) == 'gsx') { // it's barely possible it has inherited keys starting with 'gsx$'
var col = k.substr(4); if(/^gsx/.test(key)) {
results.fields.push(col); colName = key.substr(4);
} results.fields.push(colName);
} }
} }
// converts non numberical values that should be numerical (22.3%[string] -> 0.223[float]) // converts non numberical values that should be numerical (22.3%[string] -> 0.223[float])
var rep = /^([\d\.\-]+)\%$/; results.records = _.map(entries, function(entry) {
results.records = _.map(gdocsSpreadsheet.feed.entry, function(entry) {
var row = {}; var row = {};
_.each(results.fields, function(col) { _.each(results.fields, function(col) {
var _keyname = 'gsx$' + col; var _keyname = 'gsx$' + col;
var value = entry[_keyname]['$t']; var value = entry[_keyname].$t;
var num;
// TODO decide the entry format of percentage data to be parsed
// TODO cover this part of code with test
// TODO use the regexp only once
// if labelled as % and value contains %, convert // if labelled as % and value contains %, convert
if (colTypes[col] == 'percent') { if(colTypes[col] === 'percent' && rep.test(value)) {
if (rep.test(value)) { num = rep.exec(value)[1];
var value2 = rep.exec(value); value = parseFloat(num) / 100;
var value3 = parseFloat(value2);
value = value3 / 100;
}
} }
row[col] = value; row[col] = value;
}); });
return row; return row;
}); });
return results; return results;
}; };
// Convenience function to get GDocs JSON API Url from standard URL // Convenience function to get GDocs JSON API Url from standard URL
my.getSpreadsheetAPIUrl = function(url) { my.getSpreadsheetAPIUrl = function(url) {
if (url.indexOf('feeds/list') != -1) {
return url;
} else {
// https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=0 // https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=0
var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+).*/; var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+).*/;
var matches = url.match(regex); var matches = url.match(regex);
if (matches) { var key;
var key = matches[1]; // TODO check possible worksheet options
var worksheet = 1; var worksheet = 1;
var out = 'https://spreadsheets.google.com/feeds/list/' + key + '/' + worksheet + '/public/values?alt=json';
return out; if(!!matches) {
} else { key = matches[1];
alert('Failed to extract gdocs key from ' + url); url = 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json';
}
} }
return url;
}; };
}(jQuery, this.recline.Backend.GDocs)); }(jQuery, this.recline.Backend.GDocs));

View File

@@ -115,22 +115,9 @@ my.Graph = Backbone.View.extend({
// @param typeId graphType id (lines, lines-and-points etc) // @param typeId graphType id (lines, lines-and-points etc)
getGraphOptions: function(typeId) { getGraphOptions: function(typeId) {
var self = this; var self = this;
// special tickformatter to show labels rather than numbers
// TODO: we should really use tickFormatter and 1 interval ticks if (and var tickFormatter = function (x) {
// only if) x-axis values are non-numeric return getFormattedX(x);
// However, that is non-trivial to work out from a dataset (datasets may
// have no field type info). Thus at present we only do this for bars.
var tickFormatter = function (val) {
if (self.model.records.models[val]) {
var out = self.model.records.models[val].get(self.state.attributes.group);
// if the value was in fact a number we want that not the
if (typeof(out) == 'number') {
return val;
} else {
return out;
}
}
return val;
}; };
var trackFormatter = function (obj) { var trackFormatter = function (obj) {
@@ -142,17 +129,8 @@ my.Graph = Backbone.View.extend({
x = y; x = y;
y = _tmp; y = _tmp;
} }
// convert back from 'index' value on x-axis (e.g. in cases where non-number values)
//if (self.model.records.models[x]) {
// x = self.model.records.models[x].get(self.state.attributes.group);
//};
// is it time series x = getFormattedX(x);
var xfield = self.model.fields.get(self.state.attributes.group);
var isDateTime = xfield.get('type') === 'date';
if (isDateTime) {
x = x.toLocaleDateString();
}
var content = _.template('<%= group %> = <%= x %>, <%= series %> = <%= y %>', { var content = _.template('<%= group %> = <%= x %>, <%= series %> = <%= y %>', {
group: self.state.attributes.group, group: self.state.attributes.group,
@@ -164,14 +142,26 @@ my.Graph = Backbone.View.extend({
return content; return content;
}; };
var getFormattedX = function (x) {
var xfield = self.model.fields.get(self.state.attributes.group);
// time series
var isDateTime = xfield.get('type') === 'date';
if (self.model.records.models[parseInt(x)]) {
x = self.model.records.models[parseInt(x)].get(self.state.attributes.group);
if (isDateTime) {
x = new Date(x).toLocaleDateString();
}
} else if (isDateTime) {
x = new Date(parseInt(x)).toLocaleDateString();
}
return x;
}
var xaxis = {}; var xaxis = {};
// check for time series on x-axis xaxis.tickFormatter = tickFormatter;
if (this.model.fields.get(this.state.get('group')).get('type') === 'date') {
xaxis.mode = 'time';
xaxis.timeformat = '%y-%b';
xaxis.autoscale = true;
xaxis.autoscaleMargin = 0.02;
};
var yaxis = {}; var yaxis = {};
yaxis.autoscale = true; yaxis.autoscale = true;
yaxis.autoscaleMargin = 0.02; yaxis.autoscaleMargin = 0.02;
@@ -217,7 +207,8 @@ my.Graph = Backbone.View.extend({
legend: legend, legend: legend,
colors: this.graphColors, colors: this.graphColors,
lines: { show: false }, lines: { show: false },
yaxis: yaxis, xaxis: yaxis,
yaxis: xaxis,
mouse: { mouse: {
track: true, track: true,
relative: true, relative: true,
@@ -237,6 +228,7 @@ my.Graph = Backbone.View.extend({
legend: legend, legend: legend,
colors: this.graphColors, colors: this.graphColors,
lines: { show: false }, lines: { show: false },
xaxis: xaxis,
yaxis: yaxis, yaxis: yaxis,
mouse: { mouse: {
track: true, track: true,
@@ -258,7 +250,7 @@ my.Graph = Backbone.View.extend({
return optionsPerGraphType[typeId]; return optionsPerGraphType[typeId];
}, },
createSeries: function () { createSeries: function() {
var self = this; var self = this;
var series = []; var series = [];
_.each(this.state.attributes.series, function(field) { _.each(this.state.attributes.series, function(field) {
@@ -266,19 +258,30 @@ my.Graph = Backbone.View.extend({
_.each(self.model.records.models, function(doc, index) { _.each(self.model.records.models, function(doc, index) {
var xfield = self.model.fields.get(self.state.attributes.group); var xfield = self.model.fields.get(self.state.attributes.group);
var x = doc.getFieldValue(xfield); var x = doc.getFieldValue(xfield);
// time series // time series
var isDateTime = xfield.get('type') === 'date'; var isDateTime = xfield.get('type') === 'date';
if (isDateTime) { if (isDateTime) {
x = moment(x).toDate(); // datetime
if (self.state.attributes.graphType != 'bars' && self.state.attributes.graphType != 'columns') {
// not bar or column
x = new Date(x).getTime();
} else {
// bar or column
x = index;
} }
var yfield = self.model.fields.get(field); } else if (typeof x === 'string') {
var y = doc.getFieldValue(yfield); // string
if (typeof x === 'string') {
x = parseFloat(x); x = parseFloat(x);
if (isNaN(x)) { if (isNaN(x)) {
x = index; x = index;
} }
} }
var yfield = self.model.fields.get(field);
var y = doc.getFieldValue(yfield);
// horizontal bar chart // horizontal bar chart
if (self.state.attributes.graphType == 'bars') { if (self.state.attributes.graphType == 'bars') {
points.push([y, x]); points.push([y, x]);