| /* |
| * 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, |
| }; |
| } |
| } |