Skip to content

Commit

Permalink
Merge pull request #75 from andymchugh/textthresh
Browse files Browse the repository at this point in the history
thresholds for text
  • Loading branch information
andymchugh authored Jun 16, 2024
2 parents 754e3e3 + 7c3d996 commit e3fe4e4
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 49 deletions.
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# Changelog

## 1.x.0
## 1.14.0

Threshold Support for Text Data
-------------------------------
This adds the ability to configure thresholds using regex patterns for text data.
New config terms in siteConfig and panelConfig:
- siteConfig:thresholdPatterns
- panelConfig:cells.cell-name.labelColor.thresholdPatterns
- panelConfig:cells.cell-name.labelColor.thresholdPatternsRef
- panelConfig:cells.cell-name.strokeColor.thresholdPatterns
- panelConfig:cells.cell-name.strokeColor.thresholdPatternsRef
- panelConfig:cells.cell-name.fillColor.thresholdPatterns
- panelConfig:cells.cell-name.fillColor.thresholdPatternsRef

Link in the Same Tab
--------------------
Expand Down
9 changes: 5 additions & 4 deletions provisioning/dashboardData/stringData.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ cells:
url: "https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md"
inbox_depth:
tags: ['depth']
dataRef: "test-data-large-sin"
dataRef: "test-data-large-cos"
label:
dataRef: "test-data-string"
valueMappings:
Expand Down Expand Up @@ -79,6 +79,7 @@ cells:
separator: "cr"
units: "none"
labelColor:
thresholds:
- {color: "#888888", level: 0}
- {color: "light-blue", level: 100}
thresholdPatterns:
- {color: "#888888", pattern: '\*1[0-9][0-9]\*'} # 100-199
- {color: "light-blue", pattern: '\*2[0-9][0-9]\*'} # 200-299
- {color: "green", pattern: '.*'} # base
1 change: 1 addition & 0 deletions provisioning/dashboardData/stringDataSparse.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ cells:
separator: "cr"
units: "none"
labelColor:
dataRef: "test-data-large-cos"
thresholds:
- {color: "#888888", level: 0}
- {color: "light-blue", level: 100}
80 changes: 80 additions & 0 deletions provisioning/dashboardData/stringDataThresholdPatterns.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---

#------------------------------------------------------------------------------
# YAML Aliases to simplify maintenance

anchorLinks:
- link: &grafana-home
url: "https://grafana.com/"
params: "time"

#------------------------------------------------------------------------------
# Panel Config

test:
testDataExtendedZero: true
testDataStringData: true

cellIdPreamble: "cell-"
gradientMode: "hue"
tagConfig:
condensed: false
cells:
drawio:
link:
url: "https://app.diagrams.net/?p=svgdata"
docs:
link:
url: "https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md"
inbox_depth:
tags: ['depth']
dataRef: "test-data-string"
label:
valueMappings:
- {value: "*0*", text: "*mapped to 0*"}
- {value: "*29*", text: "*mapped to 29*"}
- {valueMin: 300, valueMax: 600, text: "will never get here as data not a number"}
separator: "cr"
units: "none"
labelColor:
thresholdPatternsRef: "example"
link: *grafana-home
db_transactions:
dataRef: "test-data-large-cos"
label:
separator: "cr"
units: "ops"
fillColor:
thresholds:
- {color: "semi-dark-green", level: 0}
- {color: "orange", level: 400}
- {color: "red", level: 800}
link: *grafana-home
inbox_transactions:
tags: ['db']
dataRef: "test-data-large-cos"
inbox_workers:
tags: ['worker']
dataRef: "test-data-small-sin"
workers_reads:
dataRef: "test-data-small-sin"
start_rate:
dataRef: "test-data-small-sin"
label:
separator: "colon"
units: "pps"
labelColor:
thresholds:
- {color: "green", level: 0}
- {color: "orange", level: 100}
link: *grafana-home
active_workers:
dataRef: "test-data-string"
label:
separator: "cr"
units: "none"
labelColor:
thresholdPatterns:
- {color: "#888888", pattern: '\*1[0-9][0-9]\*'} # 100-199
- {color: "light-blue", pattern: '\*2[0-9][0-9]\*'} # 200-299
- {color: "green", pattern: '.*'} # base
10 changes: 10 additions & 0 deletions provisioning/dashboardData/stringDataThresholdPatternsSite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---

#------------------------------------------------------------------------------
# Site Config

thresholdPatterns:
example:
- {color: "#888888", pattern: '\*1[0-9][0-9]\*'} # 100-199
- {color: "light-blue", pattern: '\*2[0-9][0-9]\*'} # 200-299
- {color: "green", pattern: '.*'} # base
8 changes: 4 additions & 4 deletions provisioning/dashboards/stringData.json

Large diffs are not rendered by default.

36 changes: 29 additions & 7 deletions src/components/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ export type VariableThresholdScalars = {
cellIdPatternScope: string[];
};

export type Threshold = {
export type ThresholdNumber = {
color: string;
level: number;
order: number;
};

export type ThresholdPattern = {
color: string;
pattern: string;
regexp: RegExp;
order: number;
};


export type Link = {
url: string;
params: LinkUrlParams;
Expand Down Expand Up @@ -63,7 +71,9 @@ export type PanelConfigCellColor = {
datapoint: DatapointMode | undefined;
gradientMode: ColorGradientMode | undefined;
thresholdsRef: string | undefined;
thresholds: Threshold[] | undefined;
thresholds: ThresholdNumber[] |undefined;
thresholdPatternsRef: string | undefined;
thresholdPatterns: ThresholdPattern[] | undefined;
};

export type PanelConfigCellColorCompound = {
Expand Down Expand Up @@ -140,7 +150,8 @@ export type SiteConfig = {
linkVariables: Map<string, string>;
links: Map<string, Link>;
colors: Map<string, string>;
thresholds: Map<string, Threshold[]>;
thresholds: Map<string, ThresholdNumber[]>;
thresholdPatterns: Map<string, ThresholdPattern[]>;
valueMappings: Map<string, FlowValueMapping[]>;
};

Expand Down Expand Up @@ -213,14 +224,15 @@ export function siteConfigFactory(config: any) {
links: new Map<string, Link>(Object.entries(config.links || {})),
colors: new Map<string, string>(Object.entries(config.colors || {})),
variableThresholdScalars: new Map<string, VariableThresholdScalars[]>(Object.entries(config.variableThresholdScalars || {})),
thresholds: new Map<string, Threshold[]>(Object.entries(config.thresholds || {})),
thresholds: new Map<string, ThresholdNumber[]>(Object.entries(config.thresholds || {})),
thresholdPatterns: new Map<string, ThresholdPattern[]>(Object.entries(config.thresholdPatterns || {})),
valueMappings: new Map<string, FlowValueMapping[]>(Object.entries(config.valueMappings || {})),
} as SiteConfig;
}

function siteConfigDereference(siteConfig: SiteConfig) {
siteConfig.thresholds.forEach((thresholds) => {
thresholds.forEach(function(threshold: Threshold) {
thresholds.forEach(function(threshold: ThresholdNumber | ThresholdPattern) {
threshold.color = siteConfig.colors.get(threshold.color) || threshold.color;
});
});
Expand All @@ -230,14 +242,24 @@ function panelConfigDereference(siteConfig: SiteConfig, panelConfig: PanelConfig
function colorDeref(cell: PanelConfigCell, color: PanelConfigCellColor | undefined) {
if (color) {
color.gradientMode = color.gradientMode || panelConfig.gradientMode;
if (!color.thresholds && color.thresholdsRef) {
color.thresholds = siteConfig.thresholds.get(color.thresholdsRef);
}
if (color.thresholds) {
color.thresholds.forEach(function(threshold, index) {
threshold.color = siteConfig.colors.get(threshold.color) || threshold.color;
threshold.order = typeof threshold.order === 'number' ? threshold.order : index;
});
}
if (!color.thresholds && color.thresholdsRef) {
color.thresholds = siteConfig.thresholds.get(color.thresholdsRef);
if (!color.thresholdPatterns && color.thresholdPatternsRef) {
color.thresholdPatterns = siteConfig.thresholdPatterns.get(color.thresholdPatternsRef);
}
if (color.thresholdPatterns) {
color.thresholdPatterns.forEach(function(threshold, index) {
threshold.color = siteConfig.colors.get(threshold.color) || threshold.color;
threshold.order = typeof threshold.order === 'number' ? threshold.order : index;
threshold.regexp = typeof threshold.pattern === 'object' ? threshold.regexp : new RegExp(threshold.pattern);
});
}
if (typeof color.datapoint === 'undefined') {
color.datapoint = cell.datapoint || panelConfig.datapoint;
Expand Down
2 changes: 1 addition & 1 deletion src/components/SvgUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ function getThresholdColor(sdb: SvgDriveBase,
configCellColor: PanelConfigCellColor | undefined) {
const datapoint = configCellColor?.datapoint;
const colorSeed = thresholdSeed(sdb, datapoint, configCellColor, cellValueSeed);
const thresholdColor = configCellColor && (typeof colorSeed === 'number') ? getColor(configCellColor, colorSeed, sdb.highlight, sdb.highlightFactors) : null;
const thresholdColor = configCellColor && (colorSeed !== null) ? getColor(configCellColor, colorSeed, sdb.highlight, sdb.highlightFactors) : null;
return thresholdColor;
}

Expand Down
85 changes: 54 additions & 31 deletions src/components/Utils.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
import { SvgAttribs, SvgCell, SvgElementAttribs } from 'components/SvgUpdater'
import { Background, HighlightFactors, Link, PanelConfigCellColor, Threshold, VariableThresholdScalars } from 'components/Config';
import { Background, ColorGradientMode, HighlightFactors, Link, PanelConfigCellColor, ThresholdNumber, ThresholdPattern, VariableThresholdScalars } from 'components/Config';
import { HighlightState } from './Highlighter';


Expand Down Expand Up @@ -126,7 +126,7 @@ function rgbToString(rgb: number[], highlight: HighlightState, highlightFactors:
}

export function primeColorCache(theme: GrafanaTheme2, svgAttribs: SvgAttribs, background: Background) {
function initCache(thresholds: Threshold[] | undefined) {
function initCache(thresholds: ThresholdNumber[] | ThresholdPattern[] | undefined) {
if (thresholds) {
thresholds.forEach(function(threshold) {
colorStringToRgb(theme, threshold.color);
Expand All @@ -135,8 +135,12 @@ export function primeColorCache(theme: GrafanaTheme2, svgAttribs: SvgAttribs, ba
}

svgAttribs.cells.forEach((cellData) => {
initCache(cellData.cellProps.fillColor && cellData.cellProps.fillColor.thresholds);
initCache(cellData.cellProps.labelColor && cellData.cellProps.labelColor.thresholds);
initCache(cellData.cellProps.strokeColor?.thresholds);
initCache(cellData.cellProps.strokeColor?.thresholdPatterns);
initCache(cellData.cellProps.fillColor?.thresholds);
initCache(cellData.cellProps.fillColor?.thresholdPatterns);
initCache(cellData.cellProps.labelColor?.thresholds);
initCache(cellData.cellProps.labelColor?.thresholdPatterns);
});

if (background.darkThemeColor) {
Expand Down Expand Up @@ -261,37 +265,56 @@ export function variableThresholdScaleValue(variableValues: Map<string, string>,
return value / scalar;
}

export function getColor(cellColorData: PanelConfigCellColor, value: number, highlight: HighlightState, highlightFactors: HighlightFactors) {
if (cellColorData.thresholds && cellColorData.thresholds.length > 0) {
const thresholds = cellColorData.thresholds;
let threshold = thresholds[0];
for (let i = 1; i < thresholds.length; i++) {
threshold = thresholds[i];
if (value < threshold.level) {
const thresholdLwr = thresholds[i - 1];
if (cellColorData.gradientMode === 'hue') {
const scalar = (value - thresholdLwr.level) / (threshold.level - thresholdLwr.level);
const scalarBounded = isFinite(scalar) ? Math.min(1, Math.max(0, scalar)) : 1;

return {
color: colorGradient(thresholdLwr.color, threshold.color, scalarBounded, highlight, highlightFactors),
order: thresholdLwr.order + scalarBounded,
};
}
else {
// The only other mode is 'none'
return {
color: colorLookup(thresholdLwr.color, highlight, highlightFactors),
order: thresholdLwr.order,
}
}
export function getColorFromPattern(thresholds: ThresholdPattern[], value: string, highlight: HighlightState, highlightFactors: HighlightFactors) {
for (let i = 0; i < thresholds.length; i++) {
const threshold = thresholds[i];
if (value.match(threshold.regexp)) {
return {
color: colorLookup(threshold.color, highlight, highlightFactors),
order: threshold.order,
}
}
return {
color: colorLookup(threshold.color, highlight, highlightFactors),
order: threshold.order,
}
return null;
}

export function getColorFromNumber(gradientMode: ColorGradientMode | undefined, thresholds: ThresholdNumber[], value: number, highlight: HighlightState, highlightFactors: HighlightFactors) {
let threshold = thresholds[0];
for (let i = 1; i < thresholds.length; i++) {
threshold = thresholds[i];
if (value < threshold.level) {
const thresholdLwr = thresholds[i - 1];
if (gradientMode === 'hue') {
const scalar = (value - thresholdLwr.level) / (threshold.level - thresholdLwr.level);
const scalarBounded = isFinite(scalar) ? Math.min(1, Math.max(0, scalar)) : 1;

return {
color: colorGradient(thresholdLwr.color, threshold.color, scalarBounded, highlight, highlightFactors),
order: thresholdLwr.order + scalarBounded,
};
}
else {
// The only other mode is 'none'
return {
color: colorLookup(thresholdLwr.color, highlight, highlightFactors),
order: thresholdLwr.order,
}
}
}
}
return {
color: colorLookup(threshold.color, highlight, highlightFactors),
order: threshold.order,
}
}

export function getColor(cellColorData: PanelConfigCellColor, value: number | string, highlight: HighlightState, highlightFactors: HighlightFactors) {
if (cellColorData.thresholdPatterns && cellColorData.thresholdPatterns.length > 0) {
return getColorFromPattern(cellColorData.thresholdPatterns, value.toString(), highlight, highlightFactors);
}
else if ((typeof value === 'number') && cellColorData.thresholds && (cellColorData.thresholds.length > 0)) {
return getColorFromNumber(cellColorData.gradientMode, cellColorData.thresholds, value, highlight, highlightFactors);
}
return null;
}

Expand Down
Loading

0 comments on commit e3fe4e4

Please sign in to comment.