diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ee7904..a8cba2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 -------------------- diff --git a/provisioning/dashboardData/stringData.yaml b/provisioning/dashboardData/stringData.yaml index 0d0a5f0..4f68222 100644 --- a/provisioning/dashboardData/stringData.yaml +++ b/provisioning/dashboardData/stringData.yaml @@ -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: @@ -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 diff --git a/provisioning/dashboardData/stringDataSparse.yaml b/provisioning/dashboardData/stringDataSparse.yaml index 425a09a..472dbb4 100644 --- a/provisioning/dashboardData/stringDataSparse.yaml +++ b/provisioning/dashboardData/stringDataSparse.yaml @@ -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} diff --git a/provisioning/dashboardData/stringDataThresholdPatterns.yaml b/provisioning/dashboardData/stringDataThresholdPatterns.yaml new file mode 100644 index 0000000..accb3ba --- /dev/null +++ b/provisioning/dashboardData/stringDataThresholdPatterns.yaml @@ -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 diff --git a/provisioning/dashboardData/stringDataThresholdPatternsSite.yaml b/provisioning/dashboardData/stringDataThresholdPatternsSite.yaml new file mode 100644 index 0000000..d060581 --- /dev/null +++ b/provisioning/dashboardData/stringDataThresholdPatternsSite.yaml @@ -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 diff --git a/provisioning/dashboards/stringData.json b/provisioning/dashboards/stringData.json index 723f279..1214270 100644 --- a/provisioning/dashboards/stringData.json +++ b/provisioning/dashboards/stringData.json @@ -40,7 +40,7 @@ "showLineNumbers": false, "showMiniMap": false }, - "content": "# String Data \n\nThis shows labels being driven off of string timeseries in all the potential ways:\n- 'Inbox Depth' cell-label has dateRef override for string data so color drive\n also possible.\n- 'Active Workers' cell level dataRef is string data so color drive not possible\neven though defined\n- Value mappings: 'Inbox Depth' has a zero mapping and a 29 mapping defined.\nIt also has a range mapping that can't work because source data is a string.\n- Sparse data: The string data has a different timestamp cadence to the number\ndata and so comes and goes at a different frequency. This results in a a complete\nintermix of no-data / color-data with no label-data / label-data with no color-data / all-data", + "content": "# String Data \n\nThis shows labels being driven off of string timeseries:\n- 'Inbox Depth' cell-label uses a siteConfig thresholdPatternsRef to drive colors in the 100->300 range.\n- 'Active Workers' does the same with panelConfig thresholdPatterns.\n- Value mappings: 'Inbox Depth' has a zero mapping and a 29 mapping defined.\nIt also has a range mapping that can't work because source data is a string.\n- Sparse data: The string data has a different timestamp cadence to the number\ndata and so comes and goes at a different frequency. This results in a a complete\nintermix of no-data / color-data with no label-data / label-data with no color-data / all-data. In these examples\nnumber thresholds are used rather than pattern thresholds.", "mode": "markdown" }, "pluginVersion": "10.0.0", @@ -69,8 +69,8 @@ }, "highlighterEnabled": true, "panZoomEnabled": true, - "panelConfig": "---\n\n#------------------------------------------------------------------------------\n# YAML Aliases to simplify maintenance\n\nanchorLinks:\n - link: &grafana-home\n url: \"https://grafana.com/\"\n params: \"time\"\n\n#------------------------------------------------------------------------------\n# Panel Config\n\ntest:\n testDataExtendedZero: true\n testDataStringData: true\n\ncellIdPreamble: \"cell-\"\ngradientMode: \"hue\"\ntagConfig:\n condensed: false\ncells: \n drawio:\n link:\n url: \"https://app.diagrams.net/?p=svgdata\"\n docs:\n link:\n url: \"https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md\"\n inbox_depth:\n tags: ['depth']\n dataRef: \"test-data-large-sin\"\n label:\n dataRef: \"test-data-string\"\n valueMappings:\n - {value: \"*0*\", text: \"*mapped to 0*\"}\n - {value: \"*29*\", text: \"*mapped to 29*\"}\n - {valueMin: 300, valueMax: 600, text: \"will never get here as data not a number\"}\n separator: \"cr\"\n units: \"none\"\n labelColor:\n gradientMode: \"hue\"\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 500}\n - {color: \"red\", level: 1000}\n link: *grafana-home\n db_transactions:\n dataRef: \"test-data-large-cos\"\n label:\n separator: \"cr\"\n units: \"ops\"\n fillColor:\n thresholds:\n - {color: \"semi-dark-green\", level: 0}\n - {color: \"orange\", level: 400}\n - {color: \"red\", level: 800}\n link: *grafana-home\n inbox_transactions:\n tags: ['db']\n dataRef: \"test-data-large-cos\"\n inbox_workers:\n tags: ['worker']\n dataRef: \"test-data-small-sin\"\n workers_reads:\n dataRef: \"test-data-small-sin\"\n start_rate:\n dataRef: \"test-data-small-sin\"\n label:\n separator: \"colon\"\n units: \"pps\"\n labelColor:\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 100}\n link: *grafana-home\n active_workers:\n dataRef: \"test-data-string\"\n label:\n separator: \"cr\"\n units: \"none\"\n labelColor:\n thresholds:\n - {color: \"#888888\", level: 0}\n - {color: \"light-blue\", level: 100}", - "siteConfig": "", + "panelConfig": "---\n\n#------------------------------------------------------------------------------\n# YAML Aliases to simplify maintenance\n\nanchorLinks:\n - link: &grafana-home\n url: \"https://grafana.com/\"\n params: \"time\"\n\n#------------------------------------------------------------------------------\n# Panel Config\n\ntest:\n testDataExtendedZero: true\n testDataStringData: true\n\ncellIdPreamble: \"cell-\"\ngradientMode: \"hue\"\ntagConfig:\n condensed: false\ncells: \n drawio:\n link:\n url: \"https://app.diagrams.net/?p=svgdata\"\n docs:\n link:\n url: \"https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md\"\n inbox_depth:\n tags: ['depth']\n dataRef: \"test-data-string\"\n label:\n valueMappings:\n - {value: \"*0*\", text: \"*mapped to 0*\"}\n - {value: \"*29*\", text: \"*mapped to 29*\"}\n - {valueMin: 300, valueMax: 600, text: \"will never get here as data not a number\"}\n separator: \"cr\"\n units: \"none\"\n labelColor:\n thresholdPatternsRef: \"example\"\n link: *grafana-home\n db_transactions:\n dataRef: \"test-data-large-cos\"\n label:\n separator: \"cr\"\n units: \"ops\"\n fillColor:\n thresholds:\n - {color: \"semi-dark-green\", level: 0}\n - {color: \"orange\", level: 400}\n - {color: \"red\", level: 800}\n link: *grafana-home\n inbox_transactions:\n tags: ['db']\n dataRef: \"test-data-large-cos\"\n inbox_workers:\n tags: ['worker']\n dataRef: \"test-data-small-sin\"\n workers_reads:\n dataRef: \"test-data-small-sin\"\n start_rate:\n dataRef: \"test-data-small-sin\"\n label:\n separator: \"colon\"\n units: \"pps\"\n labelColor:\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 100}\n link: *grafana-home\n active_workers:\n dataRef: \"test-data-string\"\n label:\n separator: \"cr\"\n units: \"none\"\n labelColor:\n thresholdPatterns:\n - {color: \"#888888\", pattern: '\\*1[0-9][0-9]\\*'} # 100-199\n - {color: \"light-blue\", pattern: '\\*2[0-9][0-9]\\*'} # 200-299\n - {color: \"green\", pattern: '.*'} # base", + "siteConfig": "---\n\n#------------------------------------------------------------------------------\n# Site Config\n\nthresholdPatterns:\n example:\n - {color: \"#888888\", pattern: '\\*1[0-9][0-9]\\*'} # 100-199\n - {color: \"light-blue\", pattern: '\\*2[0-9][0-9]\\*'} # 200-299\n - {color: \"green\", pattern: '.*'} # base", "svg": "\n\n\n
start rate
start rate
Example tooltip for the Inbox Depth. These are defined inside the SVG. From draw.io select the widget -> Edit -> Edit TooltipExample tooltip for the Inbox Depth. These are defined inside the SVG. From draw.io select the widget -> Edit -> Edit Tooltip
Inbox Depth
Inbox Depth
Active Workers
Active Workers
Transactions
Transactions
Docs
Docs
Text is not SVG - cannot display
", "testDataEnabled": true, "timeSliderEnabled": true @@ -101,7 +101,7 @@ }, "highlighterEnabled": true, "panZoomEnabled": true, - "panelConfig": "---\n\n#------------------------------------------------------------------------------\n# YAML Aliases to simplify maintenance\n\nanchorLinks:\n - link: &grafana-home\n url: \"https://grafana.com/\"\n params: \"time\"\n\n#------------------------------------------------------------------------------\n# Panel Config\n\ntest:\n testDataExtendedZero: true\n testDataStringData: true\n testDataSparse: true\n\ncellIdPreamble: \"cell-\"\ngradientMode: \"hue\"\ntagConfig:\n condensed: false\ncells: \n drawio:\n link:\n url: \"https://app.diagrams.net/?p=svgdata\"\n docs:\n link:\n url: \"https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md\"\n inbox_depth:\n tags: ['depth']\n dataRef: \"test-data-large-sin\"\n label:\n dataRef: \"test-data-string\"\n valueMappings:\n - {value: \"*0*\", text: \"*mapped to 0*\"}\n - {value: \"*29*\", text: \"*mapped to 29*\"}\n - {valueMin: 300, valueMax: 600, text: \"will never get here as data not a number\"}\n separator: \"cr\"\n units: \"none\"\n labelColor:\n gradientMode: \"hue\"\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 500}\n - {color: \"red\", level: 1000}\n link: *grafana-home\n db_transactions:\n dataRef: \"test-data-large-cos\"\n label:\n separator: \"cr\"\n units: \"ops\"\n fillColor:\n thresholds:\n - {color: \"semi-dark-green\", level: 0}\n - {color: \"orange\", level: 400}\n - {color: \"red\", level: 800}\n link: *grafana-home\n inbox_transactions:\n tags: ['db']\n dataRef: \"test-data-large-cos\"\n inbox_workers:\n tags: ['worker']\n dataRef: \"test-data-small-sin\"\n workers_reads:\n dataRef: \"test-data-small-sin\"\n start_rate:\n dataRef: \"test-data-small-sin\"\n label:\n separator: \"colon\"\n units: \"pps\"\n labelColor:\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 100}\n link: *grafana-home\n active_workers:\n dataRef: \"test-data-string\"\n label:\n separator: \"cr\"\n units: \"none\"\n labelColor:\n thresholds:\n - {color: \"#888888\", level: 0}\n - {color: \"light-blue\", level: 100}", + "panelConfig": "---\n\n#------------------------------------------------------------------------------\n# YAML Aliases to simplify maintenance\n\nanchorLinks:\n - link: &grafana-home\n url: \"https://grafana.com/\"\n params: \"time\"\n\n#------------------------------------------------------------------------------\n# Panel Config\n\ntest:\n testDataExtendedZero: true\n testDataStringData: true\n testDataSparse: true\n\ncellIdPreamble: \"cell-\"\ngradientMode: \"hue\"\ntagConfig:\n condensed: false\ncells: \n drawio:\n link:\n url: \"https://app.diagrams.net/?p=svgdata\"\n docs:\n link:\n url: \"https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md\"\n inbox_depth:\n tags: ['depth']\n dataRef: \"test-data-large-sin\"\n label:\n dataRef: \"test-data-string\"\n valueMappings:\n - {value: \"*0*\", text: \"*mapped to 0*\"}\n - {value: \"*29*\", text: \"*mapped to 29*\"}\n - {valueMin: 300, valueMax: 600, text: \"will never get here as data not a number\"}\n separator: \"cr\"\n units: \"none\"\n labelColor:\n gradientMode: \"hue\"\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 500}\n - {color: \"red\", level: 1000}\n link: *grafana-home\n db_transactions:\n dataRef: \"test-data-large-cos\"\n label:\n separator: \"cr\"\n units: \"ops\"\n fillColor:\n thresholds:\n - {color: \"semi-dark-green\", level: 0}\n - {color: \"orange\", level: 400}\n - {color: \"red\", level: 800}\n link: *grafana-home\n inbox_transactions:\n tags: ['db']\n dataRef: \"test-data-large-cos\"\n inbox_workers:\n tags: ['worker']\n dataRef: \"test-data-small-sin\"\n workers_reads:\n dataRef: \"test-data-small-sin\"\n start_rate:\n dataRef: \"test-data-small-sin\"\n label:\n separator: \"colon\"\n units: \"pps\"\n labelColor:\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 100}\n link: *grafana-home\n active_workers:\n dataRef: \"test-data-string\"\n label:\n separator: \"cr\"\n units: \"none\"\n labelColor:\n dataRef: \"test-data-large-cos\"\n thresholds:\n - {color: \"#888888\", level: 0}\n - {color: \"light-blue\", level: 100}", "siteConfig": "", "svg": "\n\n\n
start rate
start rate
Example tooltip for the Inbox Depth. These are defined inside the SVG. From draw.io select the widget -> Edit -> Edit TooltipExample tooltip for the Inbox Depth. These are defined inside the SVG. From draw.io select the widget -> Edit -> Edit Tooltip
Inbox Depth
Inbox Depth
Active Workers
Active Workers
Transactions
Transactions
Docs
Docs
Text is not SVG - cannot display
", "testDataEnabled": true, diff --git a/src/components/Config.tsx b/src/components/Config.tsx index b2c6a05..8d1440f 100644 --- a/src/components/Config.tsx +++ b/src/components/Config.tsx @@ -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; @@ -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 = { @@ -140,7 +150,8 @@ export type SiteConfig = { linkVariables: Map; links: Map; colors: Map; - thresholds: Map; + thresholds: Map; + thresholdPatterns: Map; valueMappings: Map; }; @@ -213,14 +224,15 @@ export function siteConfigFactory(config: any) { links: new Map(Object.entries(config.links || {})), colors: new Map(Object.entries(config.colors || {})), variableThresholdScalars: new Map(Object.entries(config.variableThresholdScalars || {})), - thresholds: new Map(Object.entries(config.thresholds || {})), + thresholds: new Map(Object.entries(config.thresholds || {})), + thresholdPatterns: new Map(Object.entries(config.thresholdPatterns || {})), valueMappings: new Map(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; }); }); @@ -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; diff --git a/src/components/SvgUpdater.tsx b/src/components/SvgUpdater.tsx index ae0c21c..cecee41 100644 --- a/src/components/SvgUpdater.tsx +++ b/src/components/SvgUpdater.tsx @@ -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; } diff --git a/src/components/Utils.tsx b/src/components/Utils.tsx index d60aa69..bf425ed 100644 --- a/src/components/Utils.tsx +++ b/src/components/Utils.tsx @@ -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'; @@ -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); @@ -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) { @@ -261,37 +265,56 @@ export function variableThresholdScaleValue(variableValues: Map, 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; } diff --git a/yaml_defs/panelConfig.yaml b/yaml_defs/panelConfig.yaml index 330db01..47a3f3f 100644 --- a/yaml_defs/panelConfig.yaml +++ b/yaml_defs/panelConfig.yaml @@ -267,6 +267,29 @@ cells: # checked. thresholdsRef: "depth" + # Version 1.14.0 onwards: ThresholdPatterns are the same concept as thresholds but are used with + # text data. They have the same fields apart from 'level' being substituted for 'pattern'. + # Whereas thresholds start with a base value and then progress through the numerical levels, + # thresholdPatterns define a regex that must be explicitely matched for the color to be applied. + # As a match has to occur there is no 'gradient' possible. If no match occurs, no color is + # applied, so if a default color is wanted it should be in the last 'catch-all' pattern. Pattern + # match breaks out on first match so patterns should be defined in most-specific->least specific + # order. + # If both 'thresholds' and 'thresholdPatterns' are defined, 'thresholdPatterns' take precedence + # and the 'thresholds' definitions are ignored. + thresholdPatterns: + queuecount: + - {color: "green", pattern: '.*INFO.*', order: null} + - {color: "amber", pattern: '.*WARNING.*', order: null} + - {color: "red", pattern: '.*ERROR.*', order: null} + - {color: "gray", pattern: '.*', order: null} + + # Version 1.14.0 onwards: This defines an ID to a thresholdPatterns set defined in the siteConfig. + # If you want to share a thresholdPatterns set just within the panelConfig you should instead use a + # yaml anchor/alias. The above 'thresholdPatterns' field takes precedence. Only if undefined will + # thresholdPatternsRef be checked. + thresholdPatternsRef: "depth" + # Version 1.14.0 onwards: This defines an array of 'labelColor' alongside an # aggregation function on how the color should be chosen from the array. If 'labelColor' # is also defined it is prepended to this array and color is just driven from this array. @@ -298,6 +321,16 @@ cells: # See labelColor thresholdsRef: "depth" + # Version 1.14.0 onwards: See labelColor + thresholdPatterns: + - {color: "green", pattern: '.*INFO.*'} + - {color: "amber", pattern: '.*WARNING.*'} + - {color: "red", pattern: '.*ERROR.*'} + - {color: "gray", pattern: '.*'} + + # Version 1.14.0 onwards: See labelColor + thresholdPatternsRef: "depth" + # Version 1.14.0 onwards: This defines an array of 'strokeColor' alongside an # aggregation function on how the color should be chosen from the array. If 'strokeColor' # is also defined it is prepended to this array and color is just driven from this array. @@ -329,6 +362,16 @@ cells: # See labelColor thresholdsRef: "depth" + # Version 1.14.0 onwards: See labelColor + thresholdPatterns: + - {color: "green", pattern: '.*INFO.*'} + - {color: "amber", pattern: '.*WARNING.*'} + - {color: "red", pattern: '.*ERROR.*'} + - {color: "gray", pattern: '.*'} + + # Version 1.14.0 onwards: See labelColor + thresholdPatternsRef: "depth" + # Version 1.14.0 onwards: This defines an array of 'fillColor' alongside an # aggregation function on how the color should be chosen from the array. If 'fillColor' # is also defined it is prepended to this array and color is just driven from this array. diff --git a/yaml_defs/siteConfig.yaml b/yaml_defs/siteConfig.yaml index 08a1d98..cce59d2 100644 --- a/yaml_defs/siteConfig.yaml +++ b/yaml_defs/siteConfig.yaml @@ -58,7 +58,7 @@ colors: green: "#00AA00" # These are refenced from panelConfig via the 'thresholdsRef' field. -# i.e. in panelConfig you see: thresholdsRef: "queueCount". +# i.e. in panelConfig you see: thresholdsRef: "queuecount". # The fields inside are explained in the panelConfig documentation. thresholds: queuecount: @@ -70,6 +70,21 @@ thresholds: - {color: "amber", level: 300} - {color: "red", level: 1500} +# Version 1.14.0 onwards: These are refenced from panelConfig via the 'thresholdPatternsRef' +# field. i.e. in panelConfig you see: thresholdPatternsRef: "queuecount". +# The fields inside are explained in the panelConfig documentation. +thresholdPatterns: + queuecount: + - {color: "green", pattern: '.*INFO.*'} + - {color: "amber", pattern: '.*WARNING.*'} + - {color: "red", pattern: '.*ERROR.*'} + - {color: "gray", pattern: '.*'} + queuedepth: + - {color: "green", level: 200} + - {color: "amber", level: 300} + - {color: "red", level: 1500} + - {color: "gray", pattern: '.*'} + # Version 1.11.0 onwards: These are refenced from panelConfig via the 'valueMappingsRef' field. # i.e. in panelConfig you see: valueMappingsRef: "dbTrans". # The fields inside are explained in the panelConfig documentation.