From c47acb65ddddf82e4ae87dae7bc8f3e7974751b8 Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Sat, 3 Jan 2026 08:49:41 -0800 Subject: [PATCH 1/3] QC plot y-scale option to show delta from mean --- webapp/TargetedMS/js/QCPlotHelperBase.js | 4 ++-- webapp/TargetedMS/js/QCPlotHoverPanel.js | 4 ++++ webapp/TargetedMS/js/QCTrendPlotPanel.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/webapp/TargetedMS/js/QCPlotHelperBase.js b/webapp/TargetedMS/js/QCPlotHelperBase.js index 53e6dbf16..88cddda86 100644 --- a/webapp/TargetedMS/js/QCPlotHelperBase.js +++ b/webapp/TargetedMS/js/QCPlotHelperBase.js @@ -730,7 +730,7 @@ Ext4.define("LABKEY.targetedms.QCPlotHelperBase", { shape: shapeProp, combined: true, yAxisScale: (showLogInvalid ? 'linear' : (this.yAxisScale !== 'log' ? 'linear' : 'log')), - valueConversion: (this.yAxisScale === 'percentDeviation' || this.yAxisScale === 'standardDeviation' ? this.yAxisScale : undefined), + valueConversion: (this.yAxisScale === 'percentDeviation' || this.yAxisScale === 'standardDeviation' || this.yAxisScale === 'deltaFromMean' ? this.yAxisScale : undefined), groupBy: 'fragment', color: 'fragment', defaultGuideSetLabel: 'fragment', @@ -873,7 +873,7 @@ Ext4.define("LABKEY.targetedms.QCPlotHelperBase", { xTick: this.groupedX ? 'groupedXTick' : 'fullDate', xTickLabel: 'date', yAxisScale: (precursorInfo.showLogInvalid ? 'linear' : (this.yAxisScale !== 'log' ? 'linear' : 'log')), - valueConversion: (this.yAxisScale === 'percentDeviation' || this.yAxisScale === 'standardDeviation' ? this.yAxisScale : undefined), + valueConversion: (this.yAxisScale === 'percentDeviation' || this.yAxisScale === 'standardDeviation' || this.yAxisScale === 'deltaFromMean' ? this.yAxisScale : undefined), shape: shapeProp, combined: false, pointSize: 2, diff --git a/webapp/TargetedMS/js/QCPlotHoverPanel.js b/webapp/TargetedMS/js/QCPlotHoverPanel.js index 0374de1cc..660f2fc57 100644 --- a/webapp/TargetedMS/js/QCPlotHoverPanel.js +++ b/webapp/TargetedMS/js/QCPlotHoverPanel.js @@ -95,6 +95,10 @@ Ext4.define('LABKEY.targetedms.QCPlotHoverPanel', { this.add(this.getPlotPointDetailField('Value', LABKEY.targetedms.PlotSettingsUtil.formatNumeric(this.pointData.rawValue))); this.add(this.getPlotPointDetailField('Std Devs', this.valueName ? this.pointData[this.valueName] : this.pointData['value'])) } + else if (this.pointData.conversion === 'deltaFromMean') { + this.add(this.getPlotPointDetailField('Value', LABKEY.targetedms.PlotSettingsUtil.formatNumeric(this.pointData.rawValue))); + this.add(this.getPlotPointDetailField('Delta From Mean', LABKEY.targetedms.PlotSettingsUtil.formatNumeric(this.valueName ? this.pointData[this.valueName] : this.pointData['value']))) + } else { this.add(this.getPlotPointDetailField('Value', LABKEY.targetedms.PlotSettingsUtil.formatNumeric(this.valueName ? this.pointData[this.valueName] : this.pointData['value']))); } diff --git a/webapp/TargetedMS/js/QCTrendPlotPanel.js b/webapp/TargetedMS/js/QCTrendPlotPanel.js index 51e41b383..987ac3d77 100644 --- a/webapp/TargetedMS/js/QCTrendPlotPanel.js +++ b/webapp/TargetedMS/js/QCTrendPlotPanel.js @@ -696,7 +696,7 @@ Ext4.define('LABKEY.targetedms.QCTrendPlotPanel', { getYAxisOptions: function () { return { fields: ['value', 'display'], - data: [['linear', 'Linear'], ['log', 'Log'], ['percentDeviation', 'Percent of Mean'], ['standardDeviation', 'Standard Deviations']] + data: [['linear', 'Linear'], ['log', 'Log'], ['percentDeviation', 'Percent of Mean'], ['standardDeviation', 'Standard Deviations'], ['deltaFromMean', 'Delta from Mean']] } }, From 472c7dabcec8a2c005f17dc93a5de1328fcdab8c Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Thu, 15 Jan 2026 11:43:08 -0800 Subject: [PATCH 2/3] add automation testing support --- .../test/components/targetedms/QCPlotsWebPart.java | 5 +++-- .../test/tests/targetedms/TargetedMSQCTest.java | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/src/org/labkey/test/components/targetedms/QCPlotsWebPart.java b/test/src/org/labkey/test/components/targetedms/QCPlotsWebPart.java index 3660dd5a7..20b4c2434 100644 --- a/test/src/org/labkey/test/components/targetedms/QCPlotsWebPart.java +++ b/test/src/org/labkey/test/components/targetedms/QCPlotsWebPart.java @@ -752,7 +752,8 @@ public enum Scale LINEAR("Linear"), LOG("Log"), PERCENT_OF_MEAN("Percent of Mean"), - STANDARD_DEVIATIONS("Standard Deviations"); + STANDARD_DEVIATIONS("Standard Deviations"), + DELTA_FROM_MEAN("Delta from Mean"); private final String _text; @@ -773,7 +774,7 @@ public String toString() { return _text; } - } + } public enum DateRangeOffset { diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSQCTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSQCTest.java index 6960810ee..f9016ad3d 100644 --- a/test/src/org/labkey/test/tests/targetedms/TargetedMSQCTest.java +++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSQCTest.java @@ -317,6 +317,8 @@ public void testQCPlotInputs() assertTrue(initialSVGText.equals(qcPlotsWebPart.getSVGPlotText("precursorPlot0_plotType_1"))); qcPlotsWebPart.setScale(QCPlotsWebPart.Scale.STANDARD_DEVIATIONS); assertTrue(initialSVGText.equals(qcPlotsWebPart.getSVGPlotText("precursorPlot0_plotType_1"))); + qcPlotsWebPart.setScale(QCPlotsWebPart.Scale.DELTA_FROM_MEAN); + assertTrue(initialSVGText.equals(qcPlotsWebPart.getSVGPlotText("precursorPlot0_plotType_1"))); qcPlotsWebPart.setScale(QCPlotsWebPart.Scale.LINEAR); @@ -927,6 +929,15 @@ public void testYAxisOptionsMovingRangePlot() //Expected y axis values are 90 95 100 105 110 115 log("SVG text " + svgPlotText); assertTrue("New plot is not as expected for percent of mean (y-axis) values", svgPlotText.contains("9095100105110115")); + + log("Verifying delta from mean plots"); + qcPlotsWebPart.setScale(QCPlotsWebPart.Scale.DELTA_FROM_MEAN); + svgPlotText = qcPlotsWebPart.getSVGPlotText("precursorPlot0"); + assertFalse("Plot with delta from mean option is blank", svgPlotText.isEmpty()); + //Expected y axis values are -3 -2.5 -2 -1.5 -1 -0.5 0 0.5 1 1.5 2 2.5 + log("SVG text " + svgPlotText); + assertTrue("New plot is not as expected for percent of mean (y-axis) values", svgPlotText.contains("-3-2.5-2-1.5-1-0.500.511.522.5")); + } /* From 45456af0b8bab42ea9575517acd72031d062adc7 Mon Sep 17 00:00:00 2001 From: ankurjuneja Date: Fri, 16 Jan 2026 11:51:59 -0800 Subject: [PATCH 3/3] use constants --- webapp/TargetedMS/js/QCPlotHelperBase.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webapp/TargetedMS/js/QCPlotHelperBase.js b/webapp/TargetedMS/js/QCPlotHelperBase.js index 88cddda86..c4b7c7dc3 100644 --- a/webapp/TargetedMS/js/QCPlotHelperBase.js +++ b/webapp/TargetedMS/js/QCPlotHelperBase.js @@ -730,7 +730,9 @@ Ext4.define("LABKEY.targetedms.QCPlotHelperBase", { shape: shapeProp, combined: true, yAxisScale: (showLogInvalid ? 'linear' : (this.yAxisScale !== 'log' ? 'linear' : 'log')), - valueConversion: (this.yAxisScale === 'percentDeviation' || this.yAxisScale === 'standardDeviation' || this.yAxisScale === 'deltaFromMean' ? this.yAxisScale : undefined), + valueConversion: (this.yAxisScale === LABKEY.vis.PlotProperties.ValueConversion.PercentDeviation || + this.yAxisScale === LABKEY.vis.PlotProperties.ValueConversion.StandardDeviation || + this.yAxisScale === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean ? this.yAxisScale : undefined), groupBy: 'fragment', color: 'fragment', defaultGuideSetLabel: 'fragment', @@ -873,7 +875,9 @@ Ext4.define("LABKEY.targetedms.QCPlotHelperBase", { xTick: this.groupedX ? 'groupedXTick' : 'fullDate', xTickLabel: 'date', yAxisScale: (precursorInfo.showLogInvalid ? 'linear' : (this.yAxisScale !== 'log' ? 'linear' : 'log')), - valueConversion: (this.yAxisScale === 'percentDeviation' || this.yAxisScale === 'standardDeviation' || this.yAxisScale === 'deltaFromMean' ? this.yAxisScale : undefined), + valueConversion: (this.yAxisScale === LABKEY.vis.PlotProperties.ValueConversion.PercentDeviation || + this.yAxisScale === LABKEY.vis.PlotProperties.ValueConversion.StandardDeviation || + this.yAxisScale === LABKEY.vis.PlotProperties.ValueConversion.DeltaFromMean ? this.yAxisScale : undefined), shape: shapeProp, combined: false, pointSize: 2,