Browse Source

path icons options

merge-requests/7/head
vargburz 3 years ago
parent
commit
9a3315d1a7
  1. 27
      src/components/editors/IconsEditor.tsx
  2. 83
      src/models/options.ts
  3. 2
      src/models/series.ts
  4. 20
      src/types.ts
  5. 6
      src/utils.ts

27
src/components/editors/IconsEditor.tsx

@ -1,4 +1,4 @@
import { IconPosition } from 'types'; import { IconPosition, Icon, Condition } from 'types';
import { GrafanaTheme, SelectableValue, StandardEditorProps } from '@grafana/data'; import { GrafanaTheme, SelectableValue, StandardEditorProps } from '@grafana/data';
import { import {
@ -18,23 +18,6 @@ import React from 'react';
import * as _ from 'lodash'; import * as _ from 'lodash';
type IconConfig = {
position: IconPosition;
url: string;
metrics: string[];
conditions: Condition[];
values: number[];
size: number;
};
enum Condition {
EQUAL = '=',
GREATER = '>',
LESS = '<',
GREATER_OR_EQUAL = '>=',
LESS_OR_EQUAL = '<=',
}
const positionOptions: Array<SelectableValue<IconPosition>> = [ const positionOptions: Array<SelectableValue<IconPosition>> = [
{ {
label: 'Upper-Left', label: 'Upper-Left',
@ -73,7 +56,7 @@ const conditionOptions: Array<SelectableValue<Condition>> = [
}, },
]; ];
const DEFAULT_ICON: IconConfig = { const DEFAULT_ICON: Icon = {
position: IconPosition.UPPER_LEFT, position: IconPosition.UPPER_LEFT,
url: '', url: '',
size: 40, size: 40,
@ -86,7 +69,7 @@ const fieldNamePickerSettings = {
settings: { width: 24 }, settings: { width: 24 },
} as any; } as any;
export function IconsEditor({ onChange, value, context }: StandardEditorProps<IconConfig[]>) { export function IconsEditor({ onChange, value, context }: StandardEditorProps<Icon[]>) {
const icons = value; const icons = value;
const addIcon = () => { const addIcon = () => {
@ -113,14 +96,14 @@ export function IconsEditor({ onChange, value, context }: StandardEditorProps<Ic
onChange(icons); onChange(icons);
}; };
const onIconFieldChange = (iconIdx: number, field: keyof IconConfig, value: any) => { const onIconFieldChange = (iconIdx: number, field: keyof Icon, value: any) => {
// @ts-ignore // @ts-ignore
icons[iconIdx][field] = value; icons[iconIdx][field] = value;
onChange(icons); onChange(icons);
}; };
const onConditionChange = (iconIdx: number, conditionIdx: number, field: keyof IconConfig, value: any) => { const onConditionChange = (iconIdx: number, conditionIdx: number, field: keyof Icon, value: any) => {
// @ts-ignore // @ts-ignore
icons[iconIdx][field][conditionIdx] = value; icons[iconIdx][field][conditionIdx] = value;

83
src/models/options.ts

@ -1,4 +1,4 @@
import { PanelOptions, Aggregation, Threshold } from 'types'; import { PanelOptions, Aggregation, Threshold, Icon, IconPosition, Condition } from 'types';
import { filterMetricListByAlias, getAggregatedValueFromSerie } from '../utils'; import { filterMetricListByAlias, getAggregatedValueFromSerie } from '../utils';
@ -13,11 +13,13 @@ export class Options {
private minValue: number | undefined; private minValue: number | undefined;
private maxValue: number | undefined; private maxValue: number | undefined;
private thresholds: { value: number, color: string }[] = []; private thresholds: { value: number, color: string }[] = [];
private icons: { src: string, position: string, size: number}[] = [];
constructor(private grafanaSeriesList: any[], private grafanaOptions: PanelOptions) { constructor(private grafanaSeriesList: any[], private grafanaOptions: PanelOptions) {
this._setMin(); this._setMin();
this._setMax(); this._setMax();
this._setThresholds(); this._setThresholds();
this._setIcons();
} }
private _setMin(): void { private _setMin(): void {
@ -65,8 +67,81 @@ export class Options {
return `${value.toFixed(decimals)} ${suffix}`; return `${value.toFixed(decimals)} ${suffix}`;
} }
private _setIcons(): void {
if (_.isEmpty(this.grafanaOptions.gauge.icons)) {
return;
}
for (let [idx, icon] of this.grafanaOptions.gauge.icons.entries()) {
this._setIcon(icon, idx);
}
}
private _setIcon(icon: Icon, idx: number): void {
if (!this._areIconConditionsFulfilled(icon, idx)) {
return;
}
this.icons.push({
src: icon.url,
size: icon.size,
position: this._getChartwerkIconPosition(icon.position),
});
}
private _areIconConditionsFulfilled(icon: Icon, iconIdx: number): boolean {
if(_.isEmpty(icon.metrics)) {
return true;
}
// check each condition and return false if something goes wrong
for (let [conditionIdx, metric] of icon.metrics.entries()) {
const value = this.getLastValueFromMetrics(metric, `Icon ${iconIdx + 1}, Condition ${conditionIdx}`);
if(value === null || value === undefined) {
// TODO: may be throw an error
return false;
}
if (!this.checkIconCondition(value, icon.values[conditionIdx], icon.conditions[conditionIdx])) {
return false;
}
}
return true;
}
private checkIconCondition(metricValue: number, inputValue: number, condition: Condition): boolean {
if (inputValue === undefined || inputValue === null) {
return true;
}
switch (condition) {
case Condition.EQUAL:
return metricValue === inputValue;
case Condition.GREATER:
return metricValue > inputValue;
case Condition.GREATER_OR_EQUAL:
return metricValue >= inputValue;
case Condition.LESS:
return metricValue < inputValue;
case Condition.LESS_OR_EQUAL:
return metricValue <= inputValue;
default:
throw new Error(`Unknown condition: ${condition}`);
}
}
private _getChartwerkIconPosition(position: IconPosition): string {
// TODO: use chartwerk types
switch (position) {
case IconPosition.MIDDLE:
return 'middle';
case IconPosition.UPPER_LEFT:
return 'left';
case IconPosition.UPPER_RIGHT:
return 'right';
default:
throw new Error(`Unknown Icon Position ${position}`);
}
}
getChartwerkOptions(): any { getChartwerkOptions(): any {
console.log('opt', this.maxValue, this.minValue);
return { return {
maxValue: this.maxValue, maxValue: this.maxValue,
minValue: this.minValue, minValue: this.minValue,
@ -76,12 +151,12 @@ export class Options {
reversed: this.grafanaOptions.gauge.reversed, reversed: this.grafanaOptions.gauge.reversed,
stops: this.thresholds, stops: this.thresholds,
valueFontSize: this.grafanaOptions.gauge.valueSize, valueFontSize: this.grafanaOptions.gauge.valueSize,
// @ts-ignore icons: this.icons,
icons: [{ src: 'https://cityhost.ua/upload_img/blog5ef308ea5529c_trash2-01.jpg', position: 'middle', size: 30 }],
}; };
} }
getLastValueFromMetrics(metricName: string | undefined, optionName: string): number | null { getLastValueFromMetrics(metricName: string | undefined, optionName: string): number | null {
// optionName -> helper in Error, mb use option path instead
const filteredSeries = filterMetricListByAlias( const filteredSeries = filterMetricListByAlias(
this.grafanaSeriesList, this.grafanaSeriesList,
metricName, metricName,

2
src/models/series.ts

@ -29,7 +29,7 @@ export class Series {
return { return {
target: serie.alias, target: serie.alias,
color: serie.color, color: serie.color,
datapoints: _.map(serie.datapoints, (row) => _.reverse(row)), datapoints: _.map(serie.datapoints, (row) => _.reverse(_.clone(row))),
alias: serie.label, alias: serie.label,
}; };
}); });

20
src/types.ts

@ -12,7 +12,8 @@ export interface PanelOptions {
arcBackground: string; arcBackground: string;
defaultColor: string; defaultColor: string;
thresholds: Threshold[]; thresholds: Threshold[];
} };
icons: Icon[];
}; };
} }
@ -50,3 +51,20 @@ export type Threshold = {
metricName: string; metricName: string;
color: string; color: string;
} }
export type Icon = {
conditions: Condition[];
metrics: string[];
values: number[];
position: IconPosition;
size: number;
url: string;
};
export enum Condition {
EQUAL = '=',
GREATER = '>',
LESS = '<',
GREATER_OR_EQUAL = '>=',
LESS_OR_EQUAL = '<=',
}

6
src/utils.ts

@ -13,8 +13,9 @@ export function filterMetricListByAlias(list: any[], alias: string | undefined,
return filteredSeries; return filteredSeries;
} }
export function getAggregatedValueFromSerie(serie: any, aggregation = Aggregation.LAST): number | null { export function getAggregatedValueFromSerie(serie: any, aggregation = Aggregation.LAST, valueIdx: 0 | 1 = 0): number | null {
// series types { datapoints: [number, number][]} // series types { datapoints: [number, number][]}
// valueIdx === 0 for Grafana series, valueIdx === 1 for Chartwerk series
if (serie === undefined) { if (serie === undefined) {
return null; return null;
} }
@ -24,9 +25,8 @@ export function getAggregatedValueFromSerie(serie: any, aggregation = Aggregatio
switch (aggregation) { switch (aggregation) {
case Aggregation.LAST: case Aggregation.LAST:
const lastRow = _.last(serie.datapoints as Array<[number, number]>); const lastRow = _.last(serie.datapoints as Array<[number, number]>);
// [0] because it is Grafan series. So 0 idx for values, 1 idx for timestamps
// @ts-ignore // @ts-ignore
return !_.isEmpty(lastRow) ? lastRow[0] : null; return !_.isEmpty(lastRow) ? lastRow[valueIdx] : null;
default: default:
throw new Error(`Unknown aggregation type: ${aggregation}`); throw new Error(`Unknown aggregation type: ${aggregation}`);
} }

Loading…
Cancel
Save