From af6d659774ddd923afba428db7615b4ce7ec831f Mon Sep 17 00:00:00 2001 From: vargburz Date: Fri, 6 May 2022 21:30:26 +0300 Subject: [PATCH 1/2] use max and min from metrics --- src/components/Panel.tsx | 16 +++++++++++++-- src/models/options.ts | 43 +++++++++++++++++++++++++++++++++++----- src/models/series.ts | 23 +++++---------------- src/types.ts | 6 ++++++ src/utils.ts | 33 ++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 25 deletions(-) create mode 100644 src/utils.ts diff --git a/src/components/Panel.tsx b/src/components/Panel.tsx index 9363cdf..fcb68dc 100644 --- a/src/components/Panel.tsx +++ b/src/components/Panel.tsx @@ -3,9 +3,12 @@ import { Series } from '../models/series'; import { PanelOptions } from '../types'; +import { DataProcessor } from '../grafana/data_processor'; + import { ChartwerkGaugePod } from '@chartwerk/gauge-pod'; import { PanelProps } from '@grafana/data'; +import { PanelData, TimeRange } from '@grafana/data'; import React, { useRef } from 'react'; import { css } from 'emotion'; @@ -15,9 +18,10 @@ interface Props extends PanelProps {} export function Panel({ options, data, width, height, timeZone, timeRange, onChangeTimeRange }: Props) { console.log('options', options); - const series = new Series(data, timeRange, options.gauge.value).getChartwerkSeries(); + const grafanaSeriesList = getGrafanaSeriesList(data, timeRange); + const series = new Series(grafanaSeriesList, options.gauge.value).getChartwerkSeries(); console.log('series', series); - const chartwerkOptions = new Options(options).getChartwerkOptions(); + const chartwerkOptions = new Options(grafanaSeriesList, options).getChartwerkOptions(); let chartContainer = useRef(null); // we request animation frame here because we need an existing DOM-element at the moment we render the pod @@ -40,3 +44,11 @@ export function Panel({ options, data, width, height, timeZone, timeRange, onCha > ); } + +function getGrafanaSeriesList(grafanaData: PanelData, timeRange: TimeRange): any[] { + const processor = new DataProcessor({}); + return processor.getSeriesList({ + dataList: grafanaData.series, + range: timeRange, + }); +} diff --git a/src/models/options.ts b/src/models/options.ts index 01d32d3..2608597 100644 --- a/src/models/options.ts +++ b/src/models/options.ts @@ -1,13 +1,46 @@ -import { PanelOptions } from 'types'; +import { PanelOptions, Aggregation } from 'types'; -// Convert Grafana options into Chartwerk options +import { filterMetricListByAlias, getAggregatedValueFromSerie } from '../utils'; + +// Convert Grafana options into Chartwerk Gauge options export class Options { - constructor(private grafanaOptions: PanelOptions) {} + private minValue: number | undefined; + private maxValue: number | undefined; + + constructor(private grafanaSeriesList: any[], private grafanaOptions: PanelOptions) { + this._setMin(); + this._setMax(); + } + + private _setMin(): any { + if(!this.grafanaOptions.gauge.min.metricName) { + this.minValue = this.grafanaOptions.gauge.min.value; + return; + } + const filteredSeries = filterMetricListByAlias(this.grafanaSeriesList, this.grafanaOptions.gauge.min.metricName, 'Min'); + const serie = filteredSeries[0]; + // Last value for now + const aggregatedValue = getAggregatedValueFromSerie(serie, Aggregation.LAST); + this.minValue = aggregatedValue ? aggregatedValue : undefined; + } + + private _setMax(): any { + if(!this.grafanaOptions.gauge.max.metricName) { + this.maxValue = this.grafanaOptions.gauge.max.value; + return; + } + const filteredSeries = filterMetricListByAlias(this.grafanaSeriesList, this.grafanaOptions.gauge.max.metricName, 'Max'); + const serie = filteredSeries[0]; + // Last value for now + const aggregatedValue = getAggregatedValueFromSerie(serie, Aggregation.LAST); + this.maxValue = aggregatedValue ? aggregatedValue : undefined; + } public getChartwerkOptions(): any { + console.log('opt', this.maxValue, this.minValue); return { - maxValue: this.grafanaOptions.gauge.max.value || 0, - minValue: this.grafanaOptions.gauge.min.value || 0, + maxValue: this.maxValue, + minValue: this.minValue, valueFormatter: (val: any) => val.toFixed(2), defaultColor: 'green', stops: [ diff --git a/src/models/series.ts b/src/models/series.ts index 7949228..95cd99a 100644 --- a/src/models/series.ts +++ b/src/models/series.ts @@ -1,36 +1,23 @@ -import { PanelData, TimeRange } from '@grafana/data'; -import { DataProcessor } from '../grafana/data_processor'; import { ValueOptions } from 'types'; +import { filterMetricListByAlias } from '../utils'; + import * as _ from 'lodash'; // Convert Grafana series into Chartwerk series export class Series { - private processor; private _seriesList; private _selectedSerieName; - constructor(grafanaData: PanelData, timeRange: TimeRange, private gaugeValueOptions: ValueOptions) { + constructor(grafanaSeriesList: any, private gaugeValueOptions: ValueOptions) { if(_.isEmpty(this.gaugeValueOptions.metricName)) { - throw new Error(`Value or metric is not selected.`); + throw new Error(`Value: metric is not selected. [See options: Extremum -> Value]`); } - this._selectedSerieName = this.gaugeValueOptions.metricName; - this.processor = new DataProcessor({}); - const seriesList = this.processor.getSeriesList({ - dataList: grafanaData.series, - range: timeRange, - }); - const filteredSeries = _.filter(seriesList, serie => serie.alias === this._selectedSerieName); - if(filteredSeries.length === 0) { - throw new Error(`Can't find metric for ${this._selectedSerieName} name`); - } - if(filteredSeries.length > 1) { - throw new Error(`Get ${filteredSeries.length} metrics for ${this._selectedSerieName} name. Please choose one`); - } + const filteredSeries = filterMetricListByAlias(grafanaSeriesList, this._selectedSerieName, 'Value'); this._seriesList = this._updateSeriesListWithChartwerkParams(filteredSeries); } diff --git a/src/types.ts b/src/types.ts index aed200c..3b62bab 100644 --- a/src/types.ts +++ b/src/types.ts @@ -28,3 +28,9 @@ export enum Pod { BAR = 'Bar', GAUGE = 'Gauge', } + +export enum Aggregation { + MIN = 'min', + MAX = 'max', + LAST = 'last' +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..2075909 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,33 @@ +import { Aggregation } from './types'; + +import * as _ from 'lodash'; + +export function filterMetricListByAlias(list: any[], alias: string | undefined, option: string): any[] { + const filteredSeries = _.filter(list, serie => serie.alias === alias); + if(filteredSeries.length === 0) { + throw new Error(`${option}: Can't find metric for ${alias} name.`); + } + if(filteredSeries.length > 1) { + throw new Error(`${option}: Get ${filteredSeries.length} metrics for ${alias} name. Please choose one.`); + } + return filteredSeries; +} + +export function getAggregatedValueFromSerie(serie: any, aggregation = Aggregation.LAST): number | null { + // series types { datapoints: [number, number][]} + if(serie === undefined) { + return null; + } + if(serie.datapoints.length === 0) { + return null; + } + switch(aggregation) { + case Aggregation.LAST: + const lastRow = _.last((serie.datapoints as [number, number][])); + // [0] because it is Grafan series. So 0 idx for values, 1 idx for timestamps + // @ts-ignore + return !_.isEmpty(lastRow) ? lastRow[0] : null; + default: + throw new Error(`Unknown aggregation type: ${aggregation}`) + } +} From 5135e3f47d16f19e7dd0f09f6f5dc24208896c3d Mon Sep 17 00:00:00 2001 From: vargburz Date: Fri, 6 May 2022 21:37:36 +0300 Subject: [PATCH 2/2] rversed options --- src/models/options.ts | 1 + src/module.ts | 8 +++++++- src/types.ts | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/models/options.ts b/src/models/options.ts index 2608597..bf572f9 100644 --- a/src/models/options.ts +++ b/src/models/options.ts @@ -43,6 +43,7 @@ export class Options { minValue: this.minValue, valueFormatter: (val: any) => val.toFixed(2), defaultColor: 'green', + reversed: this.grafanaOptions.gauge.reversed, stops: [ { color: 'green', diff --git a/src/module.ts b/src/module.ts index 4dd7a1b..86de3c7 100644 --- a/src/module.ts +++ b/src/module.ts @@ -79,7 +79,13 @@ export const plugin = new PanelPlugin(Panel).setPanelOptions((buil category: ['Extremum'], showIf: (config) => config.visualizationType === Pod.GAUGE, }) - + .addBooleanSwitch({ + path: 'gauge.reversed', + name: 'Reversed', + defaultValue: false, + category: ['Direction'], + showIf: (config) => config.visualizationType === Pod.GAUGE, + }) .addCustomEditor({ id: 'icons', path: 'gauge.icons', diff --git a/src/types.ts b/src/types.ts index 3b62bab..f0427d2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,7 @@ export interface PanelOptions { min: ExtremumOptions; max: ExtremumOptions; value: ExtremumOptions; + reversed: boolean; }; }