mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
add more functions and tests
This commit is contained in:
@@ -36,10 +36,10 @@ export class StatsPicker extends PureComponent<Props> {
|
||||
|
||||
const current = getStatsCalculators(stats);
|
||||
if (current.length !== stats.length) {
|
||||
const found = current.map(v => v.value);
|
||||
const found = current.map(v => v.id);
|
||||
const notFound = difference(stats, found);
|
||||
console.warn('Unknown stats', notFound, stats);
|
||||
onChange(current.map(stat => stat.value));
|
||||
onChange(current.map(stat => stat.id));
|
||||
}
|
||||
|
||||
// Make sure there is only one
|
||||
@@ -65,15 +65,31 @@ export class StatsPicker extends PureComponent<Props> {
|
||||
|
||||
render() {
|
||||
const { width, stats, allowMultiple, defaultStat, placeholder } = this.props;
|
||||
const current = getStatsCalculators(stats);
|
||||
const options = getStatsCalculators().map(s => {
|
||||
return {
|
||||
value: s.id,
|
||||
label: s.name,
|
||||
desctipiton: s.description,
|
||||
};
|
||||
});
|
||||
|
||||
const value: SelectOptionItem[] = [];
|
||||
stats.forEach(s => {
|
||||
const o = options.find(v => v.value === s);
|
||||
if (o) {
|
||||
value.push(o);
|
||||
}
|
||||
});
|
||||
|
||||
//getStatsCalculators(stats);
|
||||
return (
|
||||
<Select
|
||||
width={width}
|
||||
value={current}
|
||||
value={value}
|
||||
isClearable={!defaultStat}
|
||||
isMulti={allowMultiple}
|
||||
isSearchable={true}
|
||||
options={getStatsCalculators()}
|
||||
options={options}
|
||||
placeholder={placeholder}
|
||||
onChange={this.onSelectionChange}
|
||||
/>
|
||||
|
||||
@@ -32,14 +32,14 @@ describe('Stats Calculators', () => {
|
||||
const stats = getStatsCalculators(names);
|
||||
expect(stats.length).toBe(2);
|
||||
|
||||
const found = stats.map(v => v.value);
|
||||
const found = stats.map(v => v.id);
|
||||
const notFound = _.difference(names, found);
|
||||
expect(notFound.length).toBe(2);
|
||||
|
||||
expect(notFound[0]).toBe('not a stat');
|
||||
});
|
||||
|
||||
it('should calculate stats', () => {
|
||||
it('should calculate basic stats', () => {
|
||||
const stats = calculateStats({
|
||||
data: basicTable,
|
||||
columnIndex: 0,
|
||||
@@ -60,16 +60,22 @@ describe('Stats Calculators', () => {
|
||||
const stats = calculateStats({
|
||||
data: basicTable,
|
||||
columnIndex: 0,
|
||||
stats: ['first', 'last', 'mean'],
|
||||
stats: ['first'],
|
||||
});
|
||||
|
||||
// First
|
||||
// Should do the simple version that just looks up value
|
||||
expect(Object.keys(stats).length).toEqual(1);
|
||||
expect(stats.first).toEqual(10);
|
||||
});
|
||||
|
||||
// Last
|
||||
expect(stats.last).toEqual(20);
|
||||
it('should get non standard stats', () => {
|
||||
const stats = calculateStats({
|
||||
data: basicTable,
|
||||
columnIndex: 0,
|
||||
stats: [StatID.distinctCount, StatID.changeCount],
|
||||
});
|
||||
|
||||
// Mean
|
||||
expect(stats.mean).toEqual(15);
|
||||
expect(stats.distinctCount).toEqual(2);
|
||||
expect(stats.changeCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,9 @@ export enum StatID {
|
||||
delta = 'delta',
|
||||
step = 'step',
|
||||
|
||||
changeCount = 'changeCount',
|
||||
distinctCount = 'distinctCount',
|
||||
|
||||
allIsZero = 'allIsZero',
|
||||
allIsNull = 'allIsNull',
|
||||
}
|
||||
@@ -29,9 +32,10 @@ export interface ColumnStats {
|
||||
type StatCalculator = (data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => ColumnStats;
|
||||
|
||||
export interface StatCalculatorInfo {
|
||||
value: string; // The ID - value maps directly to select component
|
||||
label: string; // The name - label for Select component
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
alias?: string; // optional secondary key. 'avg' vs 'mean', 'total' vs 'sum'
|
||||
|
||||
// Internal details
|
||||
@@ -83,7 +87,7 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats {
|
||||
if (!data.rows || data.rows.length < 1) {
|
||||
const stats = {} as ColumnStats;
|
||||
queue.forEach(stat => {
|
||||
stats[stat.value] = stat.emptyInputResult !== null ? stat.emptyInputResult : null;
|
||||
stats[stat.id] = stat.emptyInputResult !== null ? stat.emptyInputResult : null;
|
||||
});
|
||||
return stats;
|
||||
}
|
||||
@@ -93,13 +97,13 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats {
|
||||
|
||||
// Avoid calculating all the standard stats if possible
|
||||
if (queue.length === 1 && queue[0].calculator) {
|
||||
return [queue[0].calculator(data, columnIndex, ignoreNulls, nullAsZero)];
|
||||
return queue[0].calculator(data, columnIndex, ignoreNulls, nullAsZero);
|
||||
}
|
||||
|
||||
// For now everything can use the standard stats
|
||||
let values = standardStatsStat(data, columnIndex, ignoreNulls, nullAsZero);
|
||||
queue.forEach(calc => {
|
||||
if (!values.hasOwnProperty(calc.value) && calc.calculator) {
|
||||
if (!values.hasOwnProperty(calc.id) && calc.calculator) {
|
||||
values = {
|
||||
...values,
|
||||
...calc.calculator(data, columnIndex, ignoreNulls, nullAsZero),
|
||||
@@ -127,75 +131,89 @@ function getById(id: string): StatCalculatorInfo | undefined {
|
||||
if (!hasBuiltIndex) {
|
||||
[
|
||||
{
|
||||
value: StatID.last,
|
||||
label: 'Last',
|
||||
id: StatID.last,
|
||||
name: 'Last',
|
||||
description: 'Last Value (current)',
|
||||
standard: true,
|
||||
alias: 'current',
|
||||
stat: calculateLast,
|
||||
calculator: calculateLast,
|
||||
},
|
||||
{ value: StatID.first, label: 'First', description: 'First Value', standard: true, stat: calculateFirst },
|
||||
{ value: StatID.min, label: 'Min', description: 'Minimum Value', standard: true },
|
||||
{ value: StatID.max, label: 'Max', description: 'Maximum Value', standard: true },
|
||||
{ value: StatID.mean, label: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
|
||||
{ id: StatID.first, name: 'First', description: 'First Value', standard: true, calculator: calculateFirst },
|
||||
{ id: StatID.min, name: 'Min', description: 'Minimum Value', standard: true },
|
||||
{ id: StatID.max, name: 'Max', description: 'Maximum Value', standard: true },
|
||||
{ id: StatID.mean, name: 'Mean', description: 'Average Value', standard: true },
|
||||
{
|
||||
value: StatID.sum,
|
||||
label: 'Total',
|
||||
id: StatID.sum,
|
||||
name: 'Total',
|
||||
description: 'The sum of all values',
|
||||
emptyInputResult: 0,
|
||||
standard: true,
|
||||
alias: 'total',
|
||||
},
|
||||
{
|
||||
value: StatID.count,
|
||||
label: 'Count',
|
||||
id: StatID.count,
|
||||
name: 'Count',
|
||||
description: 'Number of values in response',
|
||||
emptyInputResult: 0,
|
||||
standard: true,
|
||||
},
|
||||
{
|
||||
value: StatID.range,
|
||||
label: 'Range',
|
||||
id: StatID.range,
|
||||
name: 'Range',
|
||||
description: 'Difference between minimum and maximum values',
|
||||
standard: true,
|
||||
},
|
||||
{
|
||||
value: StatID.delta,
|
||||
label: 'Delta',
|
||||
id: StatID.delta,
|
||||
name: 'Delta',
|
||||
description: 'Cumulative change in value (??? help not really sure ???)',
|
||||
standard: true,
|
||||
},
|
||||
{
|
||||
value: StatID.step,
|
||||
label: 'Step',
|
||||
id: StatID.step,
|
||||
name: 'Step',
|
||||
description: 'Minimum interval between values',
|
||||
standard: true,
|
||||
},
|
||||
{
|
||||
value: StatID.diff,
|
||||
label: 'Difference',
|
||||
id: StatID.diff,
|
||||
name: 'Difference',
|
||||
description: 'Difference between first and last values',
|
||||
standard: true,
|
||||
},
|
||||
{
|
||||
value: StatID.logmin,
|
||||
label: 'Min (above zero)',
|
||||
id: StatID.logmin,
|
||||
name: 'Min (above zero)',
|
||||
description: 'Used for log min scale',
|
||||
standard: true,
|
||||
},
|
||||
].forEach(calc => {
|
||||
const { value, alias } = calc;
|
||||
if (index.hasOwnProperty(value)) {
|
||||
console.warn('Duplicate Stat', value, calc, index);
|
||||
{
|
||||
id: StatID.changeCount,
|
||||
name: 'Change Count',
|
||||
description: 'Number of times the value changes',
|
||||
standard: false,
|
||||
calculator: calculateChangeCount,
|
||||
},
|
||||
{
|
||||
id: StatID.distinctCount,
|
||||
name: 'Distinct Count',
|
||||
description: 'Number of distinct values',
|
||||
standard: false,
|
||||
calculator: calculateDistinctCount,
|
||||
},
|
||||
].forEach(info => {
|
||||
const { id, alias } = info;
|
||||
if (index.hasOwnProperty(id)) {
|
||||
console.warn('Duplicate Stat', id, info, index);
|
||||
}
|
||||
index[value] = calc;
|
||||
index[id] = info;
|
||||
if (alias) {
|
||||
if (index.hasOwnProperty(alias)) {
|
||||
console.warn('Duplicate Stat (alias)', alias, calc, index);
|
||||
console.warn('Duplicate Stat (alias)', alias, info, index);
|
||||
}
|
||||
index[alias] = calc;
|
||||
index[alias] = info;
|
||||
}
|
||||
listOfStats.push(calc);
|
||||
listOfStats.push(info);
|
||||
});
|
||||
hasBuiltIndex = true;
|
||||
}
|
||||
@@ -324,9 +342,61 @@ function standardStatsStat(
|
||||
}
|
||||
|
||||
function calculateFirst(data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean): ColumnStats {
|
||||
console.log('FIRST', data);
|
||||
return { first: data.rows[0][columnIndex] };
|
||||
}
|
||||
|
||||
function calculateLast(data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean): ColumnStats {
|
||||
return { last: data.rows[data.rows.length - 1][columnIndex] };
|
||||
}
|
||||
|
||||
function calculateChangeCount(
|
||||
data: TableData,
|
||||
columnIndex: number,
|
||||
ignoreNulls: boolean,
|
||||
nullAsZero: boolean
|
||||
): ColumnStats {
|
||||
let count = 0;
|
||||
let first = true;
|
||||
let last: any = null;
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
let currentValue = data.rows[i][columnIndex];
|
||||
if (currentValue === null) {
|
||||
if (ignoreNulls) {
|
||||
continue;
|
||||
}
|
||||
if (nullAsZero) {
|
||||
currentValue = 0;
|
||||
}
|
||||
}
|
||||
if (!first && last !== currentValue) {
|
||||
count++;
|
||||
}
|
||||
first = false;
|
||||
last = currentValue;
|
||||
}
|
||||
|
||||
return { changeCount: count };
|
||||
}
|
||||
|
||||
function calculateDistinctCount(
|
||||
data: TableData,
|
||||
columnIndex: number,
|
||||
ignoreNulls: boolean,
|
||||
nullAsZero: boolean
|
||||
): ColumnStats {
|
||||
const distinct = new Set<any>();
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
let currentValue = data.rows[i][columnIndex];
|
||||
if (currentValue === null) {
|
||||
if (ignoreNulls) {
|
||||
continue;
|
||||
}
|
||||
if (nullAsZero) {
|
||||
currentValue = 0;
|
||||
}
|
||||
}
|
||||
distinct.add(currentValue);
|
||||
}
|
||||
return { distinctCount: distinct.size };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user