blob: 2baa6b5c8d8c22c374bd7def6fc462597f1126e8 [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Transformation from './transformation';
/**
* pivot table data and return d3 chart data
*/
export default class PivotTransformation extends Transformation {
// eslint-disable-next-line no-useless-constructor
constructor(config) {
super(config);
}
getSetting() {
let self = this;
let configObj = self.config;
console.log('getSetting', configObj);
return {
template: 'app/tabledata/pivot_settings.html',
scope: {
config: configObj.common.pivot,
tableDataColumns: self.tableDataColumns,
save: function() {
self.emitConfig(configObj);
},
removeKey: function(idx) {
configObj.common.pivot.keys.splice(idx, 1);
self.emitConfig(configObj);
},
removeGroup: function(idx) {
configObj.common.pivot.groups.splice(idx, 1);
self.emitConfig(configObj);
},
removeValue: function(idx) {
configObj.common.pivot.values.splice(idx, 1);
self.emitConfig(configObj);
},
setValueAggr: function(idx, aggr) {
configObj.common.pivot.values[idx].aggr = aggr;
self.emitConfig(configObj);
},
},
};
}
/**
* Method will be invoked when tableData or config changes
*/
transform(tableData) {
this.tableDataColumns = tableData.columns;
this.config.common = this.config.common || {};
this.config.common.pivot = this.config.common.pivot || {};
let config = this.config.common.pivot;
let firstTime = (!config.keys && !config.groups && !config.values);
config.keys = config.keys || [];
config.groups = config.groups || [];
config.values = config.values || [];
this.removeUnknown();
if (firstTime) {
this.selectDefault();
}
return this.pivot(
tableData,
config.keys,
config.groups,
config.values);
}
removeUnknown() {
let config = this.config.common.pivot;
let tableDataColumns = this.tableDataColumns;
let unique = function(list) {
for (let i = 0; i < list.length; i++) {
for (let j = i + 1; j < list.length; j++) {
if (angular.equals(list[i], list[j])) {
list.splice(j, 1);
j--;
}
}
}
};
let removeUnknown = function(list) {
for (let i = 0; i < list.length; i++) {
// remove non existing column
let found = false;
for (let j = 0; j < tableDataColumns.length; j++) {
let a = list[i];
let b = tableDataColumns[j];
if (a.index === b.index && a.name === b.name) {
found = true;
break;
}
}
if (!found) {
list.splice(i, 1);
}
}
};
unique(config.keys);
removeUnknown(config.keys);
unique(config.groups);
removeUnknown(config.groups);
removeUnknown(config.values);
}
selectDefault() {
let config = this.config.common.pivot;
if (config.keys.length === 0 &&
config.groups.length === 0 &&
config.values.length === 0) {
if (config.keys.length === 0 && this.tableDataColumns.length > 0) {
config.keys.push(this.tableDataColumns[0]);
}
if (config.values.length === 0 && this.tableDataColumns.length > 1) {
config.values.push(this.tableDataColumns[1]);
}
}
}
pivot(data, keys, groups, values) {
let aggrFunc = {
sum: function(a, b) {
let varA = (a !== undefined) ? (isNaN(a) ? 0 : parseFloat(a)) : 0;
let varB = (b !== undefined) ? (isNaN(b) ? 0 : parseFloat(b)) : 0;
return varA + varB;
},
count: function(a, b) {
let varA = (a !== undefined) ? parseInt(a) : 0;
let varB = (b !== undefined) ? 1 : 0;
return varA + varB;
},
min: function(a, b) {
let aIsValid = isValidNumber(a);
let bIsValid = isValidNumber(b);
if (!aIsValid) {
return parseFloat(b);
} else if (!bIsValid) {
return parseFloat(a);
} else {
return Math.min(parseFloat(a), parseFloat(b));
}
},
max: function(a, b) {
let aIsValid = isValidNumber(a);
let bIsValid = isValidNumber(b);
if (!aIsValid) {
return parseFloat(b);
} else if (!bIsValid) {
return parseFloat(a);
} else {
return Math.max(parseFloat(a), parseFloat(b));
}
},
avg: function(a, b, c) {
let varA = (a !== undefined) ? (isNaN(a) ? 0 : parseFloat(a)) : 0;
let varB = (b !== undefined) ? (isNaN(b) ? 0 : parseFloat(b)) : 0;
return varA + varB;
},
};
let isValidNumber = function(num) {
return num !== undefined && !isNaN(num);
};
let aggrFuncDiv = {
sum: false,
count: false,
min: false,
max: false,
avg: true,
};
let schema = {};
let rows = {};
for (let i = 0; i < data.rows.length; i++) {
let row = data.rows[i];
let s = schema;
let p = rows;
for (let k = 0; k < keys.length; k++) {
let key = keys[k];
// add key to schema
if (!s[key.name]) {
s[key.name] = {
order: k,
index: key.index,
type: 'key',
children: {},
};
}
s = s[key.name].children;
// add key to row
let keyKey = row[key.index];
if (!p[keyKey]) {
p[keyKey] = {};
}
p = p[keyKey];
}
for (let g = 0; g < groups.length; g++) {
let group = groups[g];
let groupKey = row[group.index];
// add group to schema
if (!s[groupKey]) {
s[groupKey] = {
order: g,
index: group.index,
type: 'group',
children: {},
};
}
s = s[groupKey].children;
// add key to row
if (!p[groupKey]) {
p[groupKey] = {};
}
p = p[groupKey];
}
for (let v = 0; v < values.length; v++) {
let value = values[v];
let valueKey = value.name + '(' + value.aggr + ')';
// add value to schema
if (!s[valueKey]) {
s[valueKey] = {
type: 'value',
order: v,
index: value.index,
};
}
// add value to row
if (!p[valueKey]) {
p[valueKey] = {
value: (value.aggr !== 'count') ? row[value.index] : 1,
count: 1,
};
} else {
p[valueKey] = {
value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1),
count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count,
};
}
}
}
// console.log('schema=%o, rows=%o', schema, rows);
return {
keys: keys,
groups: groups,
values: values,
schema: schema,
rows: rows,
};
}
}