mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
add stat picker to single stat
This commit is contained in:
parent
f8094a6e09
commit
658f419cab
@ -69,7 +69,7 @@ export class StatsPicker extends PureComponent<Props> {
|
||||
return {
|
||||
value: s.id,
|
||||
label: s.name,
|
||||
desctipiton: s.description,
|
||||
description: s.description,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -27,6 +27,7 @@ export { Switch } from './Switch/Switch';
|
||||
export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
|
||||
export { PieChart, PieChartDataPoint, PieChartType } from './PieChart/PieChart';
|
||||
export { UnitPicker } from './UnitPicker/UnitPicker';
|
||||
export { StatsPicker } from './StatsPicker/StatsPicker';
|
||||
export { Input, InputStatus } from './Input/Input';
|
||||
|
||||
// Visualizations
|
||||
|
@ -5,6 +5,7 @@ export * from './colors';
|
||||
export * from './namedColorsPalette';
|
||||
export * from './thresholds';
|
||||
export * from './string';
|
||||
export * from './statsCalculator';
|
||||
export * from './displayValue';
|
||||
export * from './deprecationWarning';
|
||||
export { getMappedValue } from './valueMappings';
|
||||
|
@ -41,7 +41,7 @@ describe('Stats Calculators', () => {
|
||||
|
||||
it('should calculate basic stats', () => {
|
||||
const stats = calculateStats({
|
||||
data: basicTable,
|
||||
table: basicTable,
|
||||
columnIndex: 0,
|
||||
stats: ['first', 'last', 'mean'],
|
||||
});
|
||||
@ -58,7 +58,7 @@ describe('Stats Calculators', () => {
|
||||
|
||||
it('should support a single stat also', () => {
|
||||
const stats = calculateStats({
|
||||
data: basicTable,
|
||||
table: basicTable,
|
||||
columnIndex: 0,
|
||||
stats: ['first'],
|
||||
});
|
||||
@ -70,7 +70,7 @@ describe('Stats Calculators', () => {
|
||||
|
||||
it('should get non standard stats', () => {
|
||||
const stats = calculateStats({
|
||||
data: basicTable,
|
||||
table: basicTable,
|
||||
columnIndex: 0,
|
||||
stats: [StatID.distinctCount, StatID.changeCount],
|
||||
});
|
||||
@ -78,4 +78,15 @@ describe('Stats Calculators', () => {
|
||||
expect(stats.distinctCount).toEqual(2);
|
||||
expect(stats.changeCount).toEqual(1);
|
||||
});
|
||||
|
||||
it('should calculate step', () => {
|
||||
const stats = calculateStats({
|
||||
table: { columns: [{ text: 'A' }], rows: [[100], [200], [300], [400]] },
|
||||
columnIndex: 0,
|
||||
stats: [StatID.step, StatID.delta],
|
||||
});
|
||||
|
||||
expect(stats.step).toEqual(100);
|
||||
expect(stats.delta).toEqual(300);
|
||||
});
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ export interface ColumnStats {
|
||||
}
|
||||
|
||||
// Internal function
|
||||
type StatCalculator = (data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => ColumnStats;
|
||||
type StatCalculator = (table: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean) => ColumnStats;
|
||||
|
||||
export interface StatCalculatorInfo {
|
||||
id: string;
|
||||
@ -64,7 +64,7 @@ export function getStatsCalculators(ids?: string[]): StatCalculatorInfo[] {
|
||||
}
|
||||
|
||||
export interface CalculateStatsOptions {
|
||||
data: TableData;
|
||||
table: TableData;
|
||||
columnIndex: number;
|
||||
stats: string[]; // The stats to calculate
|
||||
nullValueMode?: NullValueMode;
|
||||
@ -74,7 +74,7 @@ export interface CalculateStatsOptions {
|
||||
* @returns an object with a key for each selected stat
|
||||
*/
|
||||
export function calculateStats(options: CalculateStatsOptions): ColumnStats {
|
||||
const { data, columnIndex, stats, nullValueMode } = options;
|
||||
const { table, columnIndex, stats, nullValueMode } = options;
|
||||
|
||||
if (!stats || stats.length < 1) {
|
||||
return {};
|
||||
@ -84,7 +84,7 @@ export function calculateStats(options: CalculateStatsOptions): ColumnStats {
|
||||
|
||||
// Return early for empty tables
|
||||
// This lets the concrete implementations assume at least one row
|
||||
if (!data.rows || data.rows.length < 1) {
|
||||
if (!table.rows || table.rows.length < 1) {
|
||||
const stats = {} as ColumnStats;
|
||||
queue.forEach(stat => {
|
||||
stats[stat.id] = stat.emptyInputResult !== null ? stat.emptyInputResult : null;
|
||||
@ -97,19 +97,19 @@ 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(table, columnIndex, ignoreNulls, nullAsZero);
|
||||
}
|
||||
|
||||
// For now everything can use the standard stats
|
||||
let values = standardStatsStat(data, columnIndex, ignoreNulls, nullAsZero);
|
||||
queue.forEach(calc => {
|
||||
let values = standardStatsStat(table, columnIndex, ignoreNulls, nullAsZero);
|
||||
for (const calc of queue) {
|
||||
if (!values.hasOwnProperty(calc.id) && calc.calculator) {
|
||||
values = {
|
||||
...values,
|
||||
...calc.calculator(data, columnIndex, ignoreNulls, nullAsZero),
|
||||
...calc.calculator(table, columnIndex, ignoreNulls, nullAsZero),
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ function getById(id: string): StatCalculatorInfo | undefined {
|
||||
{ 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 },
|
||||
{ id: StatID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
|
||||
{
|
||||
id: StatID.sum,
|
||||
name: 'Total',
|
||||
@ -241,7 +241,7 @@ function standardStatsStat(
|
||||
range: null,
|
||||
diff: null,
|
||||
delta: 0,
|
||||
step: 0,
|
||||
step: Number.MAX_VALUE,
|
||||
|
||||
// Just used for calcutations -- not exposed as a stat
|
||||
previousDeltaUp: true,
|
||||
@ -260,8 +260,6 @@ function standardStatsStat(
|
||||
}
|
||||
|
||||
if (currentValue !== null) {
|
||||
stats.last = currentValue;
|
||||
|
||||
const isFirst = stats.first === null;
|
||||
if (isFirst) {
|
||||
stats.first = currentValue;
|
||||
@ -324,6 +322,10 @@ function standardStatsStat(
|
||||
stats.min = null;
|
||||
}
|
||||
|
||||
if (stats.step === Number.MAX_VALUE) {
|
||||
stats.step = null;
|
||||
}
|
||||
|
||||
if (stats.nonNullCount > 0) {
|
||||
stats.mean = stats.sum! / stats.nonNullCount;
|
||||
}
|
||||
@ -342,7 +344,6 @@ function standardStatsStat(
|
||||
}
|
||||
|
||||
function calculateFirst(data: TableData, columnIndex: number, ignoreNulls: boolean, nullAsZero: boolean): ColumnStats {
|
||||
console.log('FIRST', data);
|
||||
return { first: data.rows[0][columnIndex] };
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,12 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
||||
}
|
||||
panel.changeType(pluginId, hook);
|
||||
}
|
||||
} else if (plugin.exports && plugin.exports.reactPanel) {
|
||||
const hook = plugin.exports.reactPanel.panelTypeChangedHook;
|
||||
if (hook) {
|
||||
panel.options = hook(panel.options || {}, null, null);
|
||||
console.log('OPITONS', pluginId, panel);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ plugin, angularPanel: null });
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { VizOrientation, SelectOptionItem } from '@grafana/ui';
|
||||
import { VizOrientation, SelectOptionItem, StatID } from '@grafana/ui';
|
||||
import { SingleStatBaseOptions } from '../singlestat2/types';
|
||||
|
||||
export interface BarGaugeOptions extends SingleStatBaseOptions {
|
||||
@ -25,7 +25,7 @@ export const defaults: BarGaugeOptions = {
|
||||
orientation: VizOrientation.Horizontal,
|
||||
valueOptions: {
|
||||
unit: 'none',
|
||||
stat: 'avg',
|
||||
stat: StatID.mean,
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
decimals: null,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SingleStatBaseOptions } from '../singlestat2/types';
|
||||
import { VizOrientation } from '@grafana/ui';
|
||||
import { VizOrientation, StatID } from '@grafana/ui';
|
||||
|
||||
export interface GaugeOptions extends SingleStatBaseOptions {
|
||||
maxValue: number;
|
||||
@ -17,7 +17,7 @@ export const defaults: GaugeOptions = {
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
decimals: null,
|
||||
stat: 'avg',
|
||||
stat: StatID.mean,
|
||||
unit: 'none',
|
||||
},
|
||||
valueMappings: [],
|
||||
|
@ -4,7 +4,7 @@ import React, { PureComponent, CSSProperties } from 'react';
|
||||
// Types
|
||||
import { SingleStatOptions, SingleStatBaseOptions } from './types';
|
||||
|
||||
import { DisplayValue, PanelProps, processTimeSeries, NullValueMode } from '@grafana/ui';
|
||||
import { DisplayValue, PanelProps, NullValueMode, calculateStats } from '@grafana/ui';
|
||||
import { config } from 'app/core/config';
|
||||
import { getDisplayProcessor } from '@grafana/ui';
|
||||
import { ProcessedValuesRepeater } from './ProcessedValuesRepeater';
|
||||
@ -14,7 +14,7 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
|
||||
const { valueOptions, valueMappings } = options;
|
||||
const { unit, decimals, stat } = valueOptions;
|
||||
|
||||
const processor = getDisplayProcessor({
|
||||
const display = getDisplayProcessor({
|
||||
unit,
|
||||
decimals,
|
||||
mappings: valueMappings,
|
||||
@ -24,12 +24,20 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
|
||||
theme: config.theme,
|
||||
});
|
||||
|
||||
return processTimeSeries({
|
||||
data,
|
||||
nullValueMode: NullValueMode.Null,
|
||||
}).map((series, index) => {
|
||||
const value = stat !== 'name' ? series.stats[stat] : series.label;
|
||||
return processor(value);
|
||||
return data.map(table => {
|
||||
// Support last_time? Add that to the migration? last, but differnt column
|
||||
if (stat === 'name') {
|
||||
// Should not really be possible anymore?
|
||||
return display(table.name);
|
||||
}
|
||||
return display(
|
||||
calculateStats({
|
||||
table,
|
||||
columnIndex: 0, // Hardcoded for now!
|
||||
stats: [stat], // The stats to calculate
|
||||
nullValueMode: NullValueMode.Null,
|
||||
})[stat]
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -2,25 +2,11 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Components
|
||||
import { FormField, FormLabel, PanelOptionsGroup, Select, UnitPicker } from '@grafana/ui';
|
||||
import { FormField, FormLabel, PanelOptionsGroup, StatsPicker, UnitPicker, StatID } from '@grafana/ui';
|
||||
|
||||
// Types
|
||||
import { SingleStatValueOptions } from './types';
|
||||
|
||||
const statOptions = [
|
||||
{ value: 'min', label: 'Min' },
|
||||
{ value: 'max', label: 'Max' },
|
||||
{ value: 'avg', label: 'Average' },
|
||||
{ value: 'current', label: 'Current' },
|
||||
{ value: 'total', label: 'Total' },
|
||||
{ value: 'name', label: 'Name' },
|
||||
{ value: 'first', label: 'First' },
|
||||
{ value: 'delta', label: 'Delta' },
|
||||
{ value: 'diff', label: 'Difference' },
|
||||
{ value: 'range', label: 'Range' },
|
||||
{ value: 'last_time', label: 'Time of last point' },
|
||||
];
|
||||
|
||||
const labelWidth = 6;
|
||||
|
||||
export interface Props {
|
||||
@ -30,7 +16,12 @@ export interface Props {
|
||||
|
||||
export class SingleStatValueEditor extends PureComponent<Props> {
|
||||
onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
|
||||
onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });
|
||||
|
||||
onStatsChange = stats => {
|
||||
console.log('SELECTED', stats);
|
||||
const stat = stats[0] || StatID.mean;
|
||||
this.props.onChange({ ...this.props.options, stat });
|
||||
};
|
||||
|
||||
onDecimalChange = event => {
|
||||
if (!isNaN(event.target.value)) {
|
||||
@ -57,15 +48,19 @@ export class SingleStatValueEditor extends PureComponent<Props> {
|
||||
decimalsString = decimals.toString();
|
||||
}
|
||||
|
||||
console.log('xxx', stat);
|
||||
|
||||
return (
|
||||
<PanelOptionsGroup title="Value">
|
||||
<div className="gf-form">
|
||||
<FormLabel width={labelWidth}>Stat</FormLabel>
|
||||
<Select
|
||||
<StatsPicker
|
||||
width={12}
|
||||
options={statOptions}
|
||||
onChange={this.onStatChange}
|
||||
value={statOptions.find(option => option.value === stat)}
|
||||
placeholder="Choose Stat"
|
||||
defaultStat={StatID.mean}
|
||||
allowMultiple={false}
|
||||
stats={[stat]}
|
||||
onChange={this.onStatsChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ReactPanelPlugin } from '@grafana/ui';
|
||||
import { ReactPanelPlugin, getStatsCalculators } from '@grafana/ui';
|
||||
import { SingleStatOptions, defaults, SingleStatBaseOptions } from './types';
|
||||
import { SingleStatPanel } from './SingleStatPanel';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
@ -21,6 +21,13 @@ export const singleStatBaseOptionsCheck = (
|
||||
});
|
||||
}
|
||||
|
||||
// 6.1 renamed some stats, This makes sure they are up to date
|
||||
// avg -> mean, current -> last, total -> sum
|
||||
const { valueOptions } = options;
|
||||
if (valueOptions && valueOptions.stat) {
|
||||
valueOptions.stat = getStatsCalculators([valueOptions.stat]).map(s => s.id)[0];
|
||||
console.log('CHANGED', valueOptions);
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { VizOrientation, ValueMapping, Threshold } from '@grafana/ui';
|
||||
import { VizOrientation, ValueMapping, Threshold, StatID } from '@grafana/ui';
|
||||
|
||||
export interface SingleStatBaseOptions {
|
||||
valueMappings: ValueMapping[];
|
||||
@ -24,7 +24,7 @@ export const defaults: SingleStatOptions = {
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
decimals: null,
|
||||
stat: 'avg',
|
||||
stat: StatID.mean,
|
||||
unit: 'none',
|
||||
},
|
||||
valueMappings: [],
|
||||
|
Loading…
Reference in New Issue
Block a user