From e995b9e4e945c42efdd588cba92f709e119f39f2 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 6 Feb 2023 16:03:37 -0600 Subject: [PATCH 01/15] Remove Ruminex package usage and calculations from labkey_luminex_transform.R --- .../labkey_luminex_transform.R | 350 +----------------- 1 file changed, 8 insertions(+), 342 deletions(-) diff --git a/luminex/resources/transformscripts/labkey_luminex_transform.R b/luminex/resources/transformscripts/labkey_luminex_transform.R index 6bc3a4b0f3..7bd715d81d 100644 --- a/luminex/resources/transformscripts/labkey_luminex_transform.R +++ b/luminex/resources/transformscripts/labkey_luminex_transform.R @@ -5,17 +5,10 @@ # # Transform script for Luminex Assay. # -# First, the script subtracts the FI-Bkgd value for the negative bead from the FI-Bkgd value +# The script subtracts the FI-Bkgd value for the negative bead from the FI-Bkgd value # for the other analytes within a given run data file. It also converts FI-Bkgd and FI-Bkgd-Neg # values that are <= 0 to 1 (as per the lab's request). # -# Next, the script calculates curve fit parameters for each titration/analyte combination using both -# 4PL and 5PL curve fits (from the fit.drc function in the Ruminex package (developed by Youyi at SCHARP). -# -# Then, the script calculates new estimated concentration values for unknown samples using the -# rumi function. The rumi function takes a dataframe as input and uses the given Standard curve data to -# calculate est.log.conc an se for the unknowns. -# # CHANGES : # - 2.1.20111216 : Issue 13696: Luminex transform script should use excel file titration "Type" for EC50 and Conc calculations # - 2.2.20120217 : Issue 14070: Value out of range error when importing curve fit parameters for titrated unknown with flat dilution curve @@ -37,17 +30,16 @@ # - 9.2.20141103 : Issue 21268: Add OtherControl titrations to PDF output of curves from transform script # - 10.0.20150910 : Changes for LabKey server 15.2. Issue 23230: Luminex transform script error when standard or QC control name has a slash in it # - 10.1.20180903 : Use transform script helper functions from Rlabkey package +# - 11.0.20230206 : Remove Ruminex package usage and calculations # # Author: Cory Nathe, LabKey -transformVersion = "10.1.20180903"; +transformVersion = "11.0.20230206"; # print the starting time for the transform script writeLines(paste("Processing start time:",Sys.time(),"\n",sep=" ")); source("${srcDirectory}/youtil.R"); -# Ruminex package available from https://www.labkey.org/Documentation/wiki-page.view?name=configureLuminexScript -suppressMessages(library(Ruminex)); -ruminexVersion = installed.packages()["Ruminex","Version"]; +suppressMessages(library(drc)); suppressMessages(library(Rlabkey)); @@ -247,7 +239,6 @@ bothRawAndSummary = any(run.data$summary == "true") & any(run.data$summary == "f runprop.output.file = labkey.transform.getRunPropertyValue(run.props, "transformedRunPropertiesFile"); fileConn<-file(runprop.output.file); writeLines(c(paste("TransformVersion",transformVersion,sep="\t"), - paste("RuminexVersion",ruminexVersion,sep="\t"), paste("RVersion",rVersion,sep="\t")), fileConn); close(fileConn); @@ -264,31 +255,10 @@ run.data$Upper_4pl = NA; run.data$Inflection_4pl = NA; run.data$EC50_4pl = NA; run.data$Flag_4pl = NA; -run.data$Slope_5pl = NA; -run.data$Lower_5pl = NA; -run.data$Upper_5pl = NA; -run.data$Inflection_5pl = NA; -run.data$Asymmetry_5pl = NA; -run.data$EC50_5pl = NA; -run.data$Flag_5pl = NA; # get the unique analyte values analytes = unique(run.data$name); -# determine if the curve fits should be done with or without log transform -curveFitLogTransform = TRUE; -if (any(run.props$name == "CurveFitLogTransform")) { - propVal = labkey.transform.getRunPropertyValue(run.props, "CurveFitLogTransform"); - if (!is.na(propVal) & propVal != "1") curveFitLogTransform = FALSE; -} - -# set the weighting variance variable for use in the non-log tranform curve fits -drm.weights.var.power = -1.8; -if (any(run.props$name == "WeightingPower")) { - propVal = labkey.transform.getRunPropertyValue(run.props, "WeightingPower"); - if (!is.na(propVal) & propVal != "") drm.weights.var.power = as.numeric(propVal); -} - # loop through the possible titrations and to see if it is a standard, qc control, or titrated unknown if (nrow(titration.data) > 0) { @@ -303,8 +273,8 @@ if (nrow(titration.data) > 0) { titrationName = as.character(titrationDataRow$Name); - # 2 types of curve fits for the EC50 calculations, with separate PDFs for the QC Curves - fitTypes = c("4pl", "5pl"); + # use 4pl curve fit for the EC50 calculations, with separate PDFs for the QC Curves + fitTypes = c("4pl"); for (typeIndex in 1:length(fitTypes)) { # we want to create PDF plots of the curves for QC Controls @@ -413,75 +383,11 @@ if (nrow(titration.data) > 0) if (all(is.na(run.data[runDataIndex,]$EC50_4pl))) { run.data[runDataIndex,]$Flag_4pl = TRUE; } - } else if (fitTypes[typeIndex] == "5pl") - { - tryCatch({ - if (curveFitLogTransform) { - formula = log(fi)~dose - weighting = FALSE - } else { - formula = fi~dose - weighting = TRUE - dat.avg=aggregate(dat$fi, by = list(dat$name, dat$dose, dat$dataId), mean) - names(dat.avg) = c("name", "dose","dataId", "fi.avg") - dat = merge(dat, dat.avg, by=c("name", "dose", "dataId"), all.x=T, all.y=T) - } - fit = fit.drc(formula, data=dat, weighting=weighting, force.fit=TRUE, fit.4pl=FALSE); - run.data[runDataIndex,]$Slope_5pl = maxValueConversion(as.numeric(coef(fit))[1]); - run.data[runDataIndex,]$Lower_5pl = maxValueConversion(as.numeric(coef(fit))[2]); - run.data[runDataIndex,]$Upper_5pl = maxValueConversion(as.numeric(coef(fit))[3]); - run.data[runDataIndex,]$Inflection_5pl = maxValueConversion(as.numeric(coef(fit))[4]); - run.data[runDataIndex,]$Asymmetry_5pl = maxValueConversion(as.numeric(coef(fit))[5]); - - if (curveFitLogTransform) { - yLabel = paste("log(",yLabel,")", sep=""); - y = log((exp(run.data[runDataIndex,]$Lower_5pl) + exp(run.data[runDataIndex,]$Upper_5pl)) / 2) - } else { - y = (run.data[runDataIndex,]$Lower_5pl + run.data[runDataIndex,]$Upper_5pl) / 2; - } - ec50 = unname(getConc(fit, y))[3]; - if (is.nan(ec50) | ec50 > 10e6) { - writeErrorOrWarning("warn", paste("Warning: EC50 5pl value out of acceptable range (either outside standards MFI or greater than 10e6) for ", titrationName, " ", analyteName, ".", sep="")); - } else { - run.data[runDataIndex,]$EC50_5pl = ec50; - } - - # plot the curve fit for the QC Controls - if (titrationDataRow$QCControl == "true" | titrationDataRow$OtherControl == "true") { - plot(fit, type="all", main=analyteName, cex=.5, ylab=yLabel, xlab=xLabel); - } - }, - error = function(e) { - print(e); - - # plot the individual data points for the QC Controls - if (curveFitLogTransform) { - yLabel = paste("log(",yLabel,")", sep=""); - logAxes = "xy" - } else { - logAxes = "x"; - } - if (titrationDataRow$QCControl == "true" | titrationDataRow$OtherControl == "true") { - plot(fi ~ dose, data = dat, log=logAxes, cex=.5, las=1, main=paste("FAILED:", analyteName, sep=" "), ylab=yLabel, xlab=xLabel); - } - } - ); - - # set the failure flag if there is no EC50 value at this point - if (all(is.na(run.data[runDataIndex,]$EC50_5pl))) { - run.data[runDataIndex,]$Flag_5pl = TRUE; - } } } else { # create an empty plot indicating that there is no data available if (titrationDataRow$QCControl == "true" | titrationDataRow$OtherControl == "true") { - if (curveFitLogTransform) { - yLabel = paste("log(",yLabel,")", sep=""); - logAxes = "xy" - } else { - logAxes = "x"; - } - plot(NA, NA, log=logAxes, cex=.5, las=1, main=paste("FAILED:", analyteName, sep=" "), ylab=yLabel, xlab=xLabel, xlim=c(1,1), ylim=c(0,1)); + plot(NA, NA, log="x", cex=.5, las=1, main=paste("FAILED:", analyteName, sep=" "), ylab=yLabel, xlab=xLabel, xlim=c(1,1), ylim=c(0,1)); text(1, 0.5, "Data Not Available"); } } @@ -496,247 +402,7 @@ if (nrow(titration.data) > 0) } } -################################## STEP 4: CALCULATE EST CONC ################################# - -# initialize the columns to be calculated -run.data$EstLogConc_5pl = NA; -run.data$EstConc_5pl = NA; -run.data$SE_5pl = NA; - -run.data$EstLogConc_4pl = NA; -run.data$EstConc_4pl = NA; -run.data$SE_4pl = NA; - -run.data$Standard = NA; -run.data$well_role = ""; # initialize to empty string and set to Standard accordingly, well_role used by Rumi function - -# determine if the Ruminex curve fits should be run -runRumiCalculation = TRUE; -if (any(run.props$name == "SkipRumiCalculation")) { - propVal = labkey.transform.getRunPropertyValue(run.props, "SkipRumiCalculation"); - if (!is.na(propVal) & propVal == "1") runRumiCalculation = FALSE; -} - -if (runRumiCalculation) -{ - # get the analyte associated standard/titration information from the analyte data file and put it into the run.data object - for (index in 1:nrow(analyte.data)) - { - # hold on to the run data for the given analyte - run.analyte.data = subset(run.data, as.character(name) == as.character(analyte.data$Name[index])); - - # some analytes may have > 1 standard selected - stndSet = unlist(strsplit(as.character(analyte.data$titrations[index]), ",")); - - # if there are more than 1 standard for this analyte, duplicate run.data records for that analyte and set standard accordingly - if (length(stndSet) > 0) - { - for (stndIndex in 1:length(stndSet)) - { - if (stndIndex == 1) - { - run.data$Standard[as.character(run.data$name) == as.character(analyte.data$Name[index])] = stndSet[stndIndex]; - run.data$well_role[as.character(run.data$name) == as.character(analyte.data$Name[index]) & run.data$description == stndSet[stndIndex]] = "Standard"; - } else - { - temp.data = run.analyte.data; - temp.data$Standard = stndSet[stndIndex]; - temp.data$well_role[temp.data$description == stndSet[stndIndex]] = "Standard"; - temp.data$lsid = NA; # lsid will be set by the server - run.data = rbind(run.data, temp.data); - } - } - } - } - - # get the unique standards (not including NA or empty string) - standards = setdiff(unique(run.data$Standard), c(NA, "")); - - # setup the dataframe needed for the call to rumi - dat = subset(run.data, select=c("dataFile", "Standard", "lsid", "well", "description", "name", "expConc", "fi", "fiBackground", "FIBackgroundNegative", "dilution", "well_role", "summary", "FlaggedAsExcluded", "isStandard", "isQCControl", "isOtherControl", "isUnknown")); - - # if both raw and summary data are available, just use the raw data for the calc - if (bothRawAndSummary) { - dat = subset(dat, summary == "false"); - } - - # remove any excluded standard replicate groups - dat = subset(dat, (isStandard & tolower(FlaggedAsExcluded) == "false") | !isStandard); - - if (any(dat$isStandard) & length(standards) > 0) - { - # change column name from "name" to "analyte" - colnames(dat)[colnames(dat) == "name"] = "analyte"; - - # change column name from expConc to expected_conc - colnames(dat)[colnames(dat) == "expConc"] = "expected_conc"; - - # set the sample_id to be description||dilution or description||expected_conc - dat$sample_id[!is.na(dat$expected_conc)] = paste(dat$description[!is.na(dat$expected_conc)], "||", dat$expected_conc[!is.na(dat$expected_conc)], sep=""); - dat$sample_id[is.na(dat$expected_conc)] = paste(dat$description[is.na(dat$expected_conc)], "||", dat$dilution[is.na(dat$expected_conc)], sep=""); - - # choose the FI column for standards and qc controls based on the run property provided by the user, default to the original FI value - if (any(run.props$name == "StndCurveFitInput")) - { - fiCol = getCurveFitInputCol(run.props, "StndCurveFitInput", "fi") - dat$fi[dat$isStandard] = dat[dat$isStandard, fiCol] - dat$fi[dat$isQCControl] = dat[dat$isQCControl, fiCol] - dat$fi[dat$isOtherControl] = dat[dat$isOtherControl, fiCol] - } - - # choose the FI column for unknowns based on the run property provided by the user, default to the original FI value - if (any(dat$isUnknown)) - { - if (any(run.props$name == "UnkCurveFitInput")) - { - fiCol = getCurveFitInputCol(run.props, "UnkCurveFitInput", "fi") - dat$fi[dat$isUnknown] = dat[dat$isUnknown, fiCol] - } - } - - # subset the dat object to just those records that have an FI - dat = subset(dat, !is.na(fi)); - - # loop through the selected standards in the data.frame and call the rumi function once for each - # this will also create one pdf for each standard - for (s in 1:length(standards)) - { - stndVal = as.character(standards[s]); - - # subset the data for those analytes set to use the given standard curve - # note: also need to subset the standard records for only those where description matches the given standard - standard.dat = subset(dat, Standard == stndVal & (!isStandard | (isStandard & description == stndVal))); - - # LabKey Issue 13034: replicate standard records as unknowns so that Rumi will calculated estimated concentrations - tempStnd.dat = subset(standard.dat, well_role=="Standard"); - if (nrow(tempStnd.dat) > 0) - { - tempStnd.dat$well_role = ""; - tempStnd.dat$sample_id = paste(tempStnd.dat$description, "||", tempStnd.dat$expected_conc, sep=""); - standard.dat=rbind(standard.dat, tempStnd.dat); - } - - # LabKey Issue 13033: check if we need to "add" standard data for any analytes if this is a subclass assay - # (i.e. standard data from "Anti-Human" analyte to be used for other analytes) - selectedAnalytes = unique(standard.dat$analyte); - subclass.dat = subset(dat, well_role == "Standard" & description == stndVal & !is.na(lsid)); - subclass.dat = subset(subclass.dat, regexpr("^blank", analyte, ignore.case=TRUE) == -1); - # if we only have standard data for one analyte, it is the subclass standard data to be used - if (length(unique(subclass.dat$analyte)) == 1) - { - subclassAnalyte = subclass.dat$analyte[1]; - for (a in 1:length(selectedAnalytes)) - { - analyteStnd.dat = subset(standard.dat, well_role == "Standard" & analyte == selectedAnalytes[a]); - # if there is no standard data for this analyte/standard, "use" the subclass analyte standard data - if (nrow(analyteStnd.dat) == 0) - { - print(paste("Using ", subclassAnalyte, " standard data for analyte ", selectedAnalytes[a], sep="")); - subclass.dat$sample_id = NA; - subclass.dat$analyte = selectedAnalytes[a]; - standard.dat = rbind(standard.dat, subclass.dat); - } - } - } - - # set the assay_id (this value will be used in the PDF plot header) - standard.dat$assay_id = stndVal; - - # check to make sure there are expected_conc values in the standard data frame that will be passed to Rumi - if (any(!is.na(standard.dat$expected_conc))) - { - # use the decided upon conversion function for handling of negative values - standard.dat$fi = sapply(standard.dat$fi, fiConversion); - - # LabKey issue 13445: Don't calculate estimated concentrations for analytes where max(FI) is < 1000 - agg.dat = subset(standard.dat, well_role == "Standard"); - if (nrow(agg.dat) > 0) - { - agg.dat = aggregate(agg.dat$fi, by = list(Standard=agg.dat$Standard,Analyte=agg.dat$analyte), FUN = max); - for (aggIndex in 1:nrow(agg.dat)) - { - # remove the rows from the standard.dat object where the max FI < 1000 - if (agg.dat$x[aggIndex] < 1000) - { - writeErrorOrWarning("warn", paste("Warning: Max(FI) is < 1000 for ", agg.dat$Standard[aggIndex], " ", agg.dat$Analyte[aggIndex], ", not calculating estimated concentrations for this standard/analyte.", sep="")); - standard.dat = subset(standard.dat, !(Standard == agg.dat$Standard[aggIndex] & analyte == agg.dat$Analyte[aggIndex])); - } - } - } - - # check to make sure that we still have some standard data to pass to the rumi function calculations - if (nrow(standard.dat) == 0 | !any(standard.dat$isStandard)) - { - next(); - } - - # call the rumi function to calculate new estimated log concentrations using 5PL for the unknowns - mypdf(file=paste(convertToFileName(stndVal), "5PL", sep="_"), mfrow=c(2,2)); - fits = rumi(standard.dat, force.fit=TRUE, log.transform=curveFitLogTransform, plot.se.profile=curveFitLogTransform, verbose=TRUE); - fits$"est.conc" = 2.71828183 ^ fits$"est.log.conc"; - dev.off(); - - # put the calculated values back into the run.data dataframe by matching on analyte, description, expConc OR dilution, and standard - if (nrow(fits) > 0) - { - for (index in 1:nrow(fits)) - { - a = fits$analyte[index]; - dil = fits$dilution[index]; - desc = fits$description[index]; - exp = fits$expected_conc[index]; - - elc = fits$"est.log.conc"[index]; - ec = fits$"est.conc"[index]; - se = fits$"se"[index]; - - if (!is.na(exp)) { - runDataIndex = run.data$name == a & run.data$expConc == exp & run.data$description == desc & run.data$Standard == stndVal - } else { - runDataIndex = run.data$name == a & run.data$dilution == dil & run.data$description == desc & run.data$Standard == stndVal - } - run.data$EstLogConc_5pl[runDataIndex] = elc; - run.data$EstConc_5pl[runDataIndex] = ec; - run.data$SE_5pl[runDataIndex] = se; - } - } - - # call the rumi function to calculate new estimated log concentrations using 4PL for the unknowns - mypdf(file=paste(convertToFileName(stndVal), "4PL", sep="_"), mfrow=c(2,2)); - fits = rumi(standard.dat, fit.4pl=TRUE, force.fit=TRUE, log.transform=curveFitLogTransform, plot.se.profile=curveFitLogTransform, verbose=TRUE); - fits$"est.conc" = 2.71828183 ^ fits$"est.log.conc"; - dev.off(); - - # put the calculated values back into the run.data dataframe by matching on analyte, description, dilution, and standard - if (nrow(fits) > 0) - { - for (index in 1:nrow(fits)) - { - a = fits$analyte[index]; - dil = fits$dilution[index]; - desc = fits$description[index]; - exp = fits$expected_conc[index]; - - elc = fits$"est.log.conc"[index]; - ec = fits$"est.conc"[index]; - se = fits$"se"[index]; - - if (!is.na(exp)) { - runDataIndex = run.data$name == a & run.data$expConc == exp & run.data$description == desc & run.data$Standard == stndVal - } else { - runDataIndex = run.data$name == a & run.data$dilution == dil & run.data$description == desc & run.data$Standard == stndVal - } - run.data$EstLogConc_4pl[runDataIndex] = elc; - run.data$EstConc_4pl[runDataIndex] = ec; - run.data$SE_4pl[runDataIndex] = se; - } - } - } - } - } -} - -##################### STEP 5: WRITE THE RESULTS TO THE OUTPUT FILE LOCATION ##################### +##################### STEP 4: WRITE THE RESULTS TO THE OUTPUT FILE LOCATION ##################### # write the new set of run data out to an output file write.table(run.data, file=run.output.file, sep="\t", na="", row.names=FALSE, quote=FALSE); From 86efa5deca41459437786e42dfd6af0f8e9f8b93 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 6 Feb 2023 16:06:11 -0600 Subject: [PATCH 02/15] Remove Ruminex related props from Luminex test assay configurations and checks --- .../tests/luminex/LuminexAsyncImportTest.java | 2 +- .../test/tests/luminex/LuminexEC50Test.java | 3 +- .../tests/luminex/LuminexGuideSetTest.java | 2 +- .../tests/luminex/LuminexRTransformTest.java | 121 +----------------- .../test/tests/luminex/LuminexTest.java | 18 +-- 5 files changed, 12 insertions(+), 134 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexAsyncImportTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexAsyncImportTest.java index 5eae3f2e35..1fac346bd8 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexAsyncImportTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexAsyncImportTest.java @@ -174,7 +174,7 @@ private void assertLuminexLogInfoPresent() //Check for Batch Properties assertTextPresentInThisOrder("----- Start Batch Properties -----", "----- End Batch Properties -----"); - assertTextPresent("Participant Visit", "Species", "Lab ID", "Analysis", "Network", "Transform Script Version", "Ruminex Version"); + assertTextPresent("Participant Visit", "Species", "Lab ID", "Analysis", "Network", "Transform Script Version"); } @LogMethod diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java index 4368aeccbf..39f94ae1a6 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java @@ -58,8 +58,7 @@ public static void updateAssayDefinition() public void testEC50() { LuminexRTransformTest rTransformTest = new LuminexRTransformTest(); - rTransformTest.uploadRunWithoutRumiCalc(); - rTransformTest.reImportRunWithRumiCalc(); + rTransformTest.uploadRun(); createNewAssayRun(TEST_ASSAY_LUM, EC50_RUN_NAME); checkCheckbox(Locator.name("curveFitLogTransform")); diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java index d922927e55..c0e3eb8631 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java @@ -54,7 +54,7 @@ public LuminexGuideSetTest() _guideSetHelper.TESTDATE.add(Calendar.DATE, -GUIDE_SET_FILES.length); } - //requires drc, Ruminex, rlabkey and xtable packages installed in R + //requires drc, rlabkey and xtable packages installed in R @Test public void testGuideSet() { diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java index bb99d11a5e..eb07dae482 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java @@ -41,17 +41,6 @@ public final class LuminexRTransformTest extends LuminexTest private static final String ANALYTE3 = "Blank"; private static final String ANALYTE4 = "MyNegative"; - private static final String[] RTRANS_FIBKGDNEG_VALUES = {"-50.5", "-70.0", "25031.5", "25584.5", "391.5", "336.5", "263.8", "290.8", - "35.2", "35.2", "63.0", "71.0", "-34.0", "-33.0", "-29.8", "-19.8", "-639.8", "-640.2", "26430.8", "26556.2", "-216.2", "-204.2", "-158.5", - "-208.0", "-4.0", "-4.0", "194.2", "198.8", "-261.2", "-265.2", "-211.5", "-213.0"}; - private static final String[] RTRANS_ESTLOGCONC_VALUES_5PL = {"-6.9", "-6.9", "4.3", "4.3", "0.4", "0.4", "-0.0", "-0.0", "-6.9", "-6.9", - "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "4.2", "4.2", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", - "-6.9", "-0.6", "-0.6", "-6.9", "-6.9", "-6.9", "-6.9"}; - - private static final String[] RTRANS_ESTLOGCONC_VALUES_4PL = {"-6.9", "-6.9", "5.0", "5.0", "0.4", "0.4", "0.1", "0.1", "-6.9", "-6.9", - "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "5.5", "5.5", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", "-6.9", - "-0.8", "-0.8", "-6.9", "-6.9", "-6.9", "-6.9"}; - @BeforeClass public static void updateAssayDefinition() { @@ -66,25 +55,16 @@ public static void updateAssayDefinition() assayDesigner.clickFinish(); } - //requires drc, Ruminex and xtable packages installed in R + //requires drc and xtable packages installed in R @Test public void testRTransform() { log("Uploading Luminex run with a R transform script"); - - uploadRunWithoutRumiCalc(); - verifyPDFsGenerated(false); + uploadRun(); + verifyPDFsGenerated(); verifyScriptVersions(); verifyLotNumber(); - verifyRumiCalculatedValues(false, ANALYTE3); verifyAnalyteProperties(new String[]{ANALYTE3, ANALYTE3, " ", " "}); - - reImportRunWithRumiCalc(); - verifyPDFsGenerated(true); - verifyScriptVersions(); - verifyLotNumber(); - verifyRumiCalculatedValues(true, ANALYTE4); - verifyAnalyteProperties(new String[]{ANALYTE4, ANALYTE4, " ", " "}); } private void verifyAnalyteProperties(String[] expectedNegBead) @@ -98,65 +78,6 @@ private void verifyAnalyteProperties(String[] expectedNegBead) } } - private void verifyRumiCalculatedValues(boolean hasRumiCalcData, String negativeBead) - { - DataRegionTable table = new DataRegionTable("Data", this); - table.setFilter("FIBackgroundNegative", "Is Not Blank", null); - waitForElement(Locator.paginationText(1, 80, 80)); - table.setFilter("Type", "Equals", "C9"); // issue 20457 - assertEquals(4, table.getDataRowCount()); - for(int i = 0; i < table.getDataRowCount(); i++) - { - assertEquals(table.getDataAsText(i, "FI-Bkgd"), table.getDataAsText(i, "FI-Bkgd-Neg")); - } - table.clearFilter("Type"); - table.setFilter("Type", "Starts With", "X"); // filter to just the unknowns - waitForElement(Locator.paginationText(1, 32, 32)); - assertTextPresent(negativeBead, 32); - // check values in the fi-bkgd-neg column - for(int i = 0; i < RTRANS_FIBKGDNEG_VALUES.length; i++) - { - assertEquals(RTRANS_FIBKGDNEG_VALUES[i], table.getDataAsText(i, "FI-Bkgd-Neg")); - } - table.clearFilter("FIBackgroundNegative"); - - table.setFilter("EstLogConc_5pl", "Is Not Blank", null); - if (!hasRumiCalcData) - { - waitForText("No data to show."); - assertEquals(0, table.getDataRowCount()); - } - else - { - waitForElement(Locator.paginationText(1, 32, 32)); - // check values in the est log conc 5pl column - for(int i = 0; i < RTRANS_ESTLOGCONC_VALUES_5PL.length; i++) - { - assertEquals(RTRANS_ESTLOGCONC_VALUES_5PL[i], table.getDataAsText(i, "Est Log Conc Rumi 5 PL")); - } - } - table.clearFilter("EstLogConc_5pl"); - - table.setFilter("EstLogConc_4pl", "Is Not Blank", null); - if (!hasRumiCalcData) - { - waitForText("No data to show."); - assertEquals(0, table.getDataRowCount()); - } - else - { - waitForElement(Locator.paginationText(1, 32, 32)); - // check values in the est log conc 4pl column - for(int i = 0; i < RTRANS_ESTLOGCONC_VALUES_4PL.length; i++) - { - assertEquals(RTRANS_ESTLOGCONC_VALUES_4PL[i], table.getDataAsText(i, "Est Log Conc Rumi 4 PL")); - } - } - table.clearFilter("EstLogConc_4pl"); - - table.clearFilter("Type"); - } - private void verifyLotNumber() { clickAndWait(Locator.linkWithText("r script transformed assayId")); @@ -174,13 +95,12 @@ private void verifyScriptVersions() { assertTextPresent(TEST_ASSAY_LUM + " Runs"); DataRegionTable table = new DataRegionTable("Runs", this); - assertEquals("Unexpected Transform Script Version number", "10.1.20180903", table.getDataAsText(0, "Transform Script Version")); + assertEquals("Unexpected Transform Script Version number", "11.0.20230206", table.getDataAsText(0, "Transform Script Version")); assertEquals("Unexpected Lab Transform Script Version number", "3.1.20180903", table.getDataAsText(0, "Lab Transform Script Version")); - assertEquals("Unexpected Ruminex Version number", "0.0.9", table.getDataAsText(0, "Ruminex Version")); assertNotNull(table.getDataAsText(0, "R Version")); } - private void verifyPDFsGenerated(boolean hasStandardPDFs) + private void verifyPDFsGenerated() { DataRegionTable.DataRegion(getDriver()).find(); // Make sure page is loaded WebElement curvePng = Locator.tagWithAttribute("img", "src", "/labkey/_images/sigmoidal_curve.png").findElement(getDriver()); @@ -188,16 +108,11 @@ private void verifyPDFsGenerated(boolean hasStandardPDFs) mouseOver(curvePng); curvePng.click(); assertElementPresent(Locator.linkContainingText(".Standard1_Control_Curves_4PL.pdf")); - assertElementPresent(Locator.linkContainingText(".Standard1_Control_Curves_5PL.pdf")); - if (hasStandardPDFs) - { - assertElementPresent(Locator.linkWithText("WithAltNegativeBead.Standard1_5PL.pdf")); - assertElementPresent(Locator.linkWithText("WithAltNegativeBead.Standard1_4PL.pdf")); - } + assertElementPresent(Locator.linkWithText("WithAltNegativeBead.Standard1_4PL.pdf")); } @LogMethod - public void uploadRunWithoutRumiCalc() + public void uploadRun() { goToProjectHome(); clickAndWait(Locator.linkWithText(TEST_ASSAY_LUM)); @@ -209,8 +124,6 @@ public void uploadRunWithoutRumiCalc() checkCheckbox(Locator.name("subtNegativeFromAll")); setFormElement(Locator.name("stndCurveFitInput"), "FI"); setFormElement(Locator.name("unkCurveFitInput"), "FI-Bkgd-Neg"); - checkCheckbox(Locator.name("curveFitLogTransform")); - checkCheckbox(Locator.name("skipRumiCalculation")); setFormElement(Locator.name("__primaryFile__"), TEST_ASSAY_LUM_FILE4); clickButton("Next", defaultWaitForPage * 2); @@ -230,26 +143,6 @@ public void uploadRunWithoutRumiCalc() clickButton("Save and Finish"); } - @LogMethod - public void reImportRunWithRumiCalc() - { - goToProjectHome(); - clickAndWait(Locator.linkWithText(TEST_ASSAY_LUM)); - - // all batch, run, analyte properties should be remembered from the first upload - DataRegionTable table = new DataRegionTable("Runs", this); - table.checkCheckbox(0); - clickButton("Re-import run"); - clickButton("Next"); - uncheckCheckbox(Locator.name("skipRumiCalculation")); - clickButton("Next", defaultWaitForPage * 2); - // switch to using MyNegative bead for subtraction - checkCheckbox(Locator.name("_analyte_" + ANALYTE4 + "_NegativeControl")); - selectOptionByText(Locator.name("_analyte_" + ANALYTE1 + "_NegativeBead"), ANALYTE4); - selectOptionByText(Locator.name("_analyte_" + ANALYTE2 + "_NegativeBead"), ANALYTE4); - clickButton("Save and Finish"); - } - @Test public void testNegativeBead() { diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java index 37639f4b24..6f13afaeb6 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java @@ -213,14 +213,13 @@ private void doInit() throws Exception assayProtocol.setName(TEST_ASSAY_LUM) .setDescription(TEST_ASSAY_LUM_DESC); - // add batch properties for transform and Ruminex version numbers + // add batch properties for transform and R version numbers Domain batchesDomain = assayProtocol.getDomains().stream().filter(a->a.getName().equals("Batch Fields")).findFirst() .orElseThrow(()-> new IllegalStateException("The protocol template did not supply a [Batch Fields] domain")); List batchFields = batchesDomain.getFields(); // keep the template-supplied fields, add the following batchFields.add(new PropertyDescriptor("Network", "Network", "string")); batchFields.add(new PropertyDescriptor("TransformVersion", "Transform Script Version", "string")); batchFields.add(new PropertyDescriptor("LabTransformVersion", "Lab Transform Script Version", "string")); - batchFields.add(new PropertyDescriptor("RuminexVersion", "Ruminex Version", "string")); batchFields.add(new PropertyDescriptor("RVersion", "R Version", "string")); setFieldsToDomain(batchFields, batchesDomain); @@ -231,8 +230,6 @@ private void doInit() throws Exception runFields.add(new PropertyDescriptor("SubtNegativeFromAll", "Subtract Negative Bead from All Wells", "boolean")); runFields.add(new PropertyDescriptor("StndCurveFitInput", "Input Var for CurveFit Calc of Standards", "string")); runFields.add(new PropertyDescriptor("UnkCurveFitInput", "Input Var for Curve Fit Calc of Unknowns", "string")); - runFields.add(new PropertyDescriptor("CurveFitLogTransform", "Curve Fit Log Transform", "boolean")); - runFields.add(new PropertyDescriptor("SkipRumiCalculation", "Skip Ruminex Calculations", "boolean")); // add run properties for use with the Guide Set test runFields.add(new PropertyDescriptor("NotebookNo", "Notebook Number", "string")); @@ -258,22 +255,11 @@ private void doInit() throws Exception .orElseThrow(()-> new IllegalStateException("The protocol template did not supply a [Data Fields] domain")); List resultsFields = resultsDomain.getFields(); resultsFields.add(new PropertyDescriptor("FIBackgroundNegative", "FI-Bkgd-Neg", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Standard", "Stnd for Calc", "string")); - resultsFields.add(new PropertyDescriptor("EstLogConc_5pl", "Est Log Conc Rumi 5 PL", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("EstConc_5pl", "Est Conc Rumi 5 PL", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("SE_5pl", "SE Rumi 5 PL", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("EstLogConc_4pl", "Est Log Conc Rumi 4 PL", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("EstConc_4pl", "Est Conc Rumi 4 PL", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("SE_4pl", "SE Rumi 4 PL", "float").setFormat("0.0")); + resultsFields.add(new PropertyDescriptor("Standard", "Stnd for Calc", "string")); // TODO remove this? resultsFields.add(new PropertyDescriptor("Slope_4pl", "Slope_4pl", "float").setFormat("0.0")); resultsFields.add(new PropertyDescriptor("Lower_4pl", "Lower_4pl", "float").setFormat("0.0")); resultsFields.add(new PropertyDescriptor("Upper_4pl", "Upper_4pl", "float").setFormat("0.0")); resultsFields.add(new PropertyDescriptor("Inflection_4pl", "Inflection_4pl", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Slope_5pl", "Slope_5pl", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Lower_5pl", "Lower_5pl", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Upper_5pl", "Upper_5pl", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Inflection_5pl", "Inflection_5pl", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Asymmetry_5pl", "Asymmetry_5pl", "float").setFormat("0.0")); resultsFields.add(new PropertyDescriptor("Positivity", "Positivity", "string")); setFieldsToDomain(resultsFields, resultsDomain); From 0a3627eefe8ec94ac3972a0b23c193b2666d82ed Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 6 Feb 2023 16:07:41 -0600 Subject: [PATCH 03/15] missed from previous commit --- luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java index 6f13afaeb6..b3de7296cc 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java @@ -255,7 +255,6 @@ private void doInit() throws Exception .orElseThrow(()-> new IllegalStateException("The protocol template did not supply a [Data Fields] domain")); List resultsFields = resultsDomain.getFields(); resultsFields.add(new PropertyDescriptor("FIBackgroundNegative", "FI-Bkgd-Neg", "float").setFormat("0.0")); - resultsFields.add(new PropertyDescriptor("Standard", "Stnd for Calc", "string")); // TODO remove this? resultsFields.add(new PropertyDescriptor("Slope_4pl", "Slope_4pl", "float").setFormat("0.0")); resultsFields.add(new PropertyDescriptor("Lower_4pl", "Lower_4pl", "float").setFormat("0.0")); resultsFields.add(new PropertyDescriptor("Upper_4pl", "Upper_4pl", "float").setFormat("0.0")); From 22a6ef9d5827423f37b68e76f20a6c55145b2d06 Mon Sep 17 00:00:00 2001 From: cnathe Date: Mon, 6 Feb 2023 16:23:37 -0600 Subject: [PATCH 04/15] Remove Rumi reference and import of Rumi5PL curve fit calculations --- .../org/labkey/luminex/LuminexDataHandler.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/luminex/src/org/labkey/luminex/LuminexDataHandler.java b/luminex/src/org/labkey/luminex/LuminexDataHandler.java index 359fe6925b..194eba0fef 100644 --- a/luminex/src/org/labkey/luminex/LuminexDataHandler.java +++ b/luminex/src/org/labkey/luminex/LuminexDataHandler.java @@ -957,15 +957,11 @@ private void insertAnalyteTitrationMapping(User user, ExpRun expRun, List Date: Tue, 7 Feb 2023 09:56:44 -0600 Subject: [PATCH 05/15] Revert changes in LuminexDataHandler as we need to keep those for backwards compatibility --- .../org/labkey/luminex/LuminexDataHandler.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/luminex/src/org/labkey/luminex/LuminexDataHandler.java b/luminex/src/org/labkey/luminex/LuminexDataHandler.java index 194eba0fef..806098632f 100644 --- a/luminex/src/org/labkey/luminex/LuminexDataHandler.java +++ b/luminex/src/org/labkey/luminex/LuminexDataHandler.java @@ -957,11 +957,15 @@ private void insertAnalyteTitrationMapping(User user, ExpRun expRun, List Date: Tue, 7 Feb 2023 09:58:46 -0600 Subject: [PATCH 06/15] Misc Luminex test fixes/changes to remove checks for EC50 5PL related values --- .../test/tests/luminex/LuminexEC50Test.java | 50 +------------------ .../luminex/LuminexGuideSetDisablingTest.java | 29 ++++------- .../tests/luminex/LuminexGuideSetTest.java | 11 ++-- .../tests/luminex/LuminexRTransformTest.java | 4 ++ 4 files changed, 21 insertions(+), 73 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java index 39f94ae1a6..1570d366b0 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java @@ -20,14 +20,11 @@ import org.junit.experimental.categories.Category; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; -import org.labkey.test.SortDirection; import org.labkey.test.categories.Assays; import org.labkey.test.categories.Daily; import org.labkey.test.pages.ReactAssayDesignerPage; import org.labkey.test.util.DataRegionTable; -import org.labkey.test.util.RReportHelper; -import java.io.IOException; import java.util.List; import static org.junit.Assert.assertEquals; @@ -38,8 +35,7 @@ public class LuminexEC50Test extends LuminexTest { private final String EC50_RUN_NAME = "EC50"; - private final String rum4 = "Four Parameter"; - private final String rum5 = "Five Parameter"; + private final String drc4 = "Four Parameter"; private final String trapezoidal = "Trapezoidal"; @BeforeClass @@ -72,14 +68,6 @@ public void testEC50() checkEC50dataAndFailureFlag(); } - private boolean checkRversion() throws IOException - { - // quick check to see if we are using 32-bit or 64-bit R - log("Checking R 32-bit vs 64-bit"); - RReportHelper _rReportHelper = new RReportHelper(this); - return _rReportHelper.getRScriptOutput(".Machine$sizeof.pointer").contains("[1] 8"); - } - private void checkEC50dataAndFailureFlag() { // expect to already be viewing CurveFit query @@ -97,27 +85,17 @@ private void checkEC50dataAndFailureFlag() List ec50 = table.getColumnDataAsText("EC50"); List auc= table.getColumnDataAsText("AUC"); List inflectionPoint = table.getColumnDataAsText("Inflection"); - int rum5ec50count = 0; log("Write this"); for(int i=0; i 0) - rum5ec50count++; - - // auc should not be populated - assertEquals(" ", auc.get(i)); - } else if(formula.get(i).equals(trapezoidal)) { //ec50 should not be populated @@ -127,30 +105,6 @@ else if(formula.get(i).equals(trapezoidal)) assertTrue( "AUC was unpopulated for row " + i, auc.get(i).length()>0); } } - assertEquals("Unexpected number of Five Parameter EC50 values (expected 10 of 14).", 10, rum5ec50count); - - // check that the 5PL parameters are within the expected ranges (note: exact values can change based on R 32-bit vs R 64-bit) - // NOTE: the first two EC50s will be significantly different on Mac due to machine episolon. The test is adjusted for this, as these are "blanks" and thus provide the noisiest answers. - Double[] FiveParameterEC50mins = {107.64, 460.75, 36465.56, 21075.08, 7826.89, 32211.66, 44972.77, 107.64, 0.4199, 0.03962}; - Double[] FiveParameterEC50maxs = {112.85, 486.5, 36469.5, 21075.29, 7826.90, 32211.67, 45012.09, 112.85, 0.43771, 0.03967}; - table.setFilter("CurveType", "Equals", "Five Parameter"); - table.setFilter("EC50", "Is Not Blank", ""); - table.setSort("EC50", SortDirection.ASC); - table.setSort("AnalyteId", SortDirection.ASC); - table.setSort("TitrationId", SortDirection.ASC); - ec50 = table.getColumnDataAsText("EC50"); - assertEquals("Unexpected number of Five Parameter EC50 values (expected " + FiveParameterEC50maxs.length + ")", FiveParameterEC50maxs.length, ec50.size()); - for (int i = 0; i < ec50.size(); i++) - { - Double val = Double.parseDouble(ec50.get(i)); - Double min = FiveParameterEC50mins[i]; - Double max = FiveParameterEC50maxs[i]; - Double expected = (max + min) / 2; - Double delta = (max - min) / 2; - assertEquals(String.format("Unexpected 5PL EC50 value for %s - %s", table.getDataAsText(i, "Titration"), table.getDataAsText(i, "Analyte")), expected, val, delta); - } - table.clearFilter("EC50"); - table.clearFilter("CurveType"); // expect to already be viewing CurveFit query assertTextPresent("CurveFit"); diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java index 9500649a68..703169b456 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java @@ -133,8 +133,6 @@ private void createValueBasedGuideSet() metricInputs.put("EC504PLAverage", 42158.22); metricInputs.put("EC504PLStdDev", 4833.76); - metricInputs.put("EC505PLAverage", 40987.31); - metricInputs.put("EC505PLStdDev", 4280.84); metricInputs.put("AUCAverage", 85268.04); metricInputs.put("AUCStdDev", 738.55); metricInputs.put("MaxFIAverage", 32507.27); @@ -187,7 +185,6 @@ public void verifyQCFlagsDisabledOnImport() clickButtonContainingText("Details", 0); waitForElement(Locator.checkboxByName("EC504PLCheckBox")); click(Locator.checkboxByName("EC504PLCheckBox")); - click(Locator.checkboxByName("EC505PLCheckBox")); click(Locator.checkboxByName("MFICheckBox")); click(Locator.checkboxByName("AUCCheckBox")); click(SAVE_BTN); @@ -216,10 +213,8 @@ public void verifyQCFlagToggling() _guideSetHelper.setUpLeveyJenningsGraphParams(RUN_BASED_ANALYTE); final String plate1_AUC = "64608.73"; final String plate2_AUC = "61889.88"; - final String plate1_EC50_5PL = "2.66"; - final String plate2_EC50_5PL = "5.67"; - validateFlaggedForQC(plate1_EC50_5PL, plate1_AUC, plate2_AUC); + validateFlaggedForQC(plate1_AUC, plate2_AUC); clickButtonContainingText("Details", 0); // toggle off the AUC QC flag and then validate errors @@ -227,13 +222,10 @@ public void verifyQCFlagToggling() click(Locator.checkboxByName("AUCCheckBox")); saveGuideSetParameters(); - validateFlaggedForQC(plate1_EC50_5PL); - // toggle off the rest of the QC flags and then validate errors clickButtonContainingText("Details", 0); waitForElement(Locator.checkboxByName("EC504PLCheckBox")); click(Locator.checkboxByName("EC504PLCheckBox")); - click(Locator.checkboxByName("EC505PLCheckBox")); click(Locator.checkboxByName("MFICheckBox")); saveGuideSetParameters(); @@ -243,12 +235,11 @@ public void verifyQCFlagToggling() clickButtonContainingText("Details", 0); waitForElement(Locator.checkboxByName("EC504PLCheckBox")); click(Locator.checkboxByName("EC504PLCheckBox")); - click(Locator.checkboxByName("EC505PLCheckBox")); click(Locator.checkboxByName("MFICheckBox")); click(Locator.checkboxByName("AUCCheckBox")); saveGuideSetParameters(); - validateFlaggedForQC(plate2_AUC, plate1_EC50_5PL, plate1_AUC); + validateFlaggedForQC(plate2_AUC, plate1_AUC); } private void saveGuideSetParameters() @@ -287,10 +278,10 @@ public void verifyGuideSetParameterDetailsWindow() clickGuideSetDetailsByComment(RUN_BASED_COMMENT); validateGuideSetRunDetails("Run-based", "Titration"); validateGuideSetMetricsDetails(new String[][]{ - {"5.284", "0.323", "2" }, - {"5.339", "0.540", "2" }, - {"32086.500", "331.633", "2" }, - {"63299.346", "149.318", "2" }, + {"5.284", "0.323", "2" }, // EC50 4PL + {"N/A", "N/A", "0" }, // EC50 5PL + {"32086.500", "331.633", "2" }, // MFI + {"63299.346", "149.318", "2" }, // AUC }); // validate checkboxes are not displayed assertTextPresent("# Runs"); @@ -301,10 +292,10 @@ public void verifyGuideSetParameterDetailsWindow() clickGuideSetDetailsByComment(VALUE_BASED_COMMENT); validateGuideSetRunDetails("Value-based", "Titration"); validateGuideSetMetricsDetails(new String[][]{ - {"42158.220", "4833.760"}, - {"40987.310", "4280.840"}, - {"32507.270", "189.830"}, - {"85268.040", "738.550"} + {"42158.220", "4833.760"}, // EC50 4PL + {"N/A", "N/A", "0" }, // EC50 5PL + {"32507.270", "189.830"}, // MFI + {"85268.040", "738.550"} // AUC }); // validate checkboxes are not displayed assertTextNotPresent("# Runs"); diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java index c0e3eb8631..5f242f3bf1 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java @@ -44,7 +44,7 @@ public final class LuminexGuideSetTest extends LuminexTest { public LuminexGuideSetHelper _guideSetHelper = new LuminexGuideSetHelper(this); public static final File[] GUIDE_SET_FILES = {TEST_ASSAY_LUM_FILE5, TEST_ASSAY_LUM_FILE6, TEST_ASSAY_LUM_FILE7, TEST_ASSAY_LUM_FILE8, TEST_ASSAY_LUM_FILE9}; - public static final String[] INITIAL_EXPECTED_FLAGS = {"AUC, EC50-4, HMFI, PCV", "AUC, EC50-4, EC50-5, HMFI", "EC50-5, HMFI", "", "PCV"}; + public static final String[] INITIAL_EXPECTED_FLAGS = {"AUC, EC50-4, HMFI, PCV", "AUC, EC50-4, HMFI", "HMFI", "", "PCV"}; private final String GUIDE_SET_5_COMMENT = "analyte 2 guide set run removed"; @@ -284,7 +284,7 @@ private boolean verifyRunFileAssociations(int index) clickAndWait(Locator.linkWithText("Text View")); waitForElement(Locator.css(".labkey-protocol-applications")); // bottom section of the "Text View" tab for the run details page waitForElements(Locator.linkWithText("Guide Set plate " + index + ".Standard1_Control_Curves_4PL.pdf"), 3); - assertElementPresent(Locator.linkWithText("Guide Set plate " + index + ".Standard1_Control_Curves_5PL.pdf"), 3); + assertElementNotPresent(Locator.linkWithText("Guide Set plate " + index + ".Standard1_Control_Curves_5PL.pdf")); assertElementPresent(Locator.linkWithText("Guide Set plate " + index + ".xls"), 4); assertElementPresent(Locator.linkWithText("Guide Set plate " + index + ".labkey_luminex_transform.Rout"), 3); @@ -474,7 +474,7 @@ private void verifyQCFlagUpdatesAfterWellChange() // - QC Flags added for EC50 and HMFI _guideSetHelper.goToLeveyJenningsGraphPage(TEST_ASSAY_LUM, "Standard1"); _guideSetHelper.setUpLeveyJenningsGraphParams("GS Analyte B"); - String newQcFlags = "AUC, EC50-4, EC50-5, HMFI"; + String newQcFlags = "AUC, EC50-4, HMFI"; assertTextNotPresent(newQcFlags); _guideSetHelper.applyGuideSetToRun("NETWORK5", GUIDE_SET_5_COMMENT, false); //assert ec50 and HMFI red text present @@ -487,7 +487,7 @@ private void verifyQCFlagUpdatesAfterWellChange() goToTestAssayHome(); drt = new DataRegionTable("Runs", getDriver()); drt.goToView("QC Flags View"); - assertTextPresent("AUC, EC50-4, EC50-5, HMFI, PCV"); + assertTextPresent("AUC, EC50-4, HMFI, PCV"); //5. For GS Analyte B, apply the guide set for plate 5a back to the current guide set // - the EC50 and HMFI QC Flags that were added in step 4 are removed @@ -537,8 +537,7 @@ private void assertExpectedAnalyte1QCFlagsPresent() private void assertQCFlagsNotPresent() { - // NOTE: don't check EC50-5 as it is inconsistent - for(String flag : new String[] {"AUC", "HMFI", "EC50-4", "PCV"}) + for (String flag : new String[] {"AUC", "HMFI", "EC50-4", "EC50-5", "PCV"}) { assertElementNotPresent(Locator.xpath("//a[contains(text(),'" + flag + "')]")); } diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java index eb07dae482..73b514c580 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java @@ -140,6 +140,10 @@ public void uploadRun() checkCheckbox(Locator.name("_analyte_" + ANALYTE3 + "_NegativeControl")); selectOptionByText(Locator.name("_analyte_" + ANALYTE1 + "_NegativeBead"), ANALYTE3); selectOptionByText(Locator.name("_analyte_" + ANALYTE2 + "_NegativeBead"), ANALYTE3); + // switch to using MyNegative bead for subtraction + checkCheckbox(Locator.name("_analyte_" + ANALYTE4 + "_NegativeControl")); + selectOptionByText(Locator.name("_analyte_" + ANALYTE1 + "_NegativeBead"), ANALYTE4); + selectOptionByText(Locator.name("_analyte_" + ANALYTE2 + "_NegativeBead"), ANALYTE4); clickButton("Save and Finish"); } From 7146a9eef70e251ec948bfba48e704148c65b7fd Mon Sep 17 00:00:00 2001 From: cnathe Date: Tue, 7 Feb 2023 10:49:32 -0600 Subject: [PATCH 07/15] Update TestLuminexAssay.xar to remove Ruminex related assay design props --- .../sampledata/luminex/TestLuminexAssay.xar | Bin 2978 -> 2725 bytes .../tests/luminex/LuminexAsyncImportTest.java | 3 +-- .../test/tests/luminex/LuminexEC50Test.java | 1 - .../test/tests/luminex/LuminexTest.java | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/luminex/test/sampledata/luminex/TestLuminexAssay.xar b/luminex/test/sampledata/luminex/TestLuminexAssay.xar index b78792066678df7831cf87f7cc90a3f95a8ac346..738dfdf5244f6cb69259f9f739bd2113152d7e52 100644 GIT binary patch literal 2725 zcmbW3c{me}AIHa#GC3l0FLIVtlCY)7WJ0-;J4fzvY)xB^9Jx2imlR>SR?O!7V&q83 zEk`mpF*h@9u5Ufh_xJpMfB)X^=kt6%pXYP@{W60CIfMY*zujjJvjY69kJ$i#C_n~a z;T9NVjPUjJb3VHSw??9k<0Bjr#HUQxFHvnKI z)82HZTI|ajwtS*dInclRrmfS?G)SyQSttk>cJo|m;1gHDs%>yZS*2dQ!4?&}qMn$N zU;N{ueAzwOM2|h45a#ZV+y?yR`Gy{O0o3&b7b6pKSaCC#u3R=)9zW39yzjO(kvO5< zus6dqr65u!dO_#9evpk?>v`@@P3Tmk=XJ z&^_NVV^HsqkfIhK=r56lVId*aG{lJu`JnjWH3co=x6-0OANzhq{g2GWA6HW^r_GV9 z-kA`a))eNH7w&G|=*(RcP34FgmYQa3CT061+o}KZuh6cJFrEZ_!NtHjNei+jvhBo@ zN}CXp`~B96dd?j40w_zR5fmlaDEL0{BrGoau+cfj&`;ygUR^BV%$)V6bebim#Uta# zc?HWFXbV`Vuz%2y5VQg=@@`Ri)>ZxzfJ?v$2AYdXK5m>nB|E4Xs63D0eP`y1Wb1tf zIF^YM6PA4BpvsMjd%vJ3x=}vQB;^(-K-2YaWU6Zo%8aj(S{>^ipv0|OJi+m-1((bR^Wy=se$8Z0+ErvTspxN5wU~3! zSIZu*$+6y_;_0`$AWbUK7?2?Jz0YR4Nw-bnE&(&nNgH!U6VfoDCqIg8MufL=rBf%b zItEKqcxWHSbXHW?$dCp z@+EzW`u zWno~o^6})u;(0v1sZ!S|D|F6^XrG@q&m2M;dF`?!K^$^m*PQ-VFFA+Goei4D;g}t& z)U4hCY^zZCQL>3Gip!OOd<#ngHe74L=p2Yz z%T5q6cNf&S&h^rj8Sr{M^xn5n7huB+$`1q!A{b#Y@LXe0)X#eHH-mH@GZt4n#Umw<*h8v6v}!cxU|~ zpv)3`=>ib@RG3CHeypdb)6(XJK(vqJS7@EZ#9Y1DvidNSPLp*XBIeR4hro}0H8>;7 z_r;oFN>gaH?B{jt^ z7@e$hbYDIKZlFMITMhSHOgV>0_mlF00rdyjKwRu+Prju?!*iB-WVC^MvAGL+l#dA8 z1AC*&7DPsg>y4i1%gDfHCg=fv|gQIQ~!60k-(Y?=Gh2Hh=5IY6lH9vKs^T$zn!O|bf^pOapE z@1w^$NHJ2vysy@$e|{2EBoa01`^}!bqh6bH^{`c35gGQgCV(%s?h^d;_p%=p1DDh{ z1!H{)9l1c=V#ne;BAq=lFqXHk#$%C0NWY&$(t8W6D>mHjY0oB0L`tb(izGP6wUio? zwZ6~&Mc=1Y+ge(Y8J-hLQ8T3?LnfymoXj>{JM8aqsN04o2Qd&K+%7MUNhwzNIL2C( zZYM9fo8|Z)co1)lM$P)k+?io5^;O!Aez4OSE~&gCMhmUpU#ta;!^|>{^Ea!JLw(Ru zXyT7lovobU6N_U|jJ!#2E`VC9S&K9X&pmu=CT3uM3KZr7DqS;d2 z0*Y)vb%wrn%}<=ZIC+?7ep3RwVe1|pE5*02 zuG9tV4KtW4f1vC%wax+U(|@aBg?|+AHrOvb#AQp!Vq4#QoNMOj;@%^Qvu75Z^(iQS z@@3j0NH^mR4-eK2bvIQh59V1ROSJWu(wJFM5^Z32&2TZ5p+Ouc+ zh;C3{P!R5l*1P(4KZ_zS|LNU24IZzGd|>!AT_VL2Q6*p40?EMDSgSjGKG*ng5NA{{ zLR=A>(R$IBFZK1>nP2=cp=e9rJ?Dq<5p(^#vEBWjhoYIOre94YK8d^vQM0dH3_Q#$y)oVGJ=L~Sg}b=0=_qxN*>MO=Zp#LP*xd%=~;eZ^zVoD`?KM&2|N4TqX4Q@9d1pxlw rpc$NvT?qJJRQ%uBA5{F0%ESLu|1UV2!8y79+_U|*`|qrD>`(U(Lj*ui delta 2963 zcmV;E3vBeI6`~g%P)h>@6aWYa2mlmq@k9Us0000000000000;O0FfOTf9;*$Pvbfk z$KO}l|G_Hru=jx$xHHlY8n`MHw3T2kq6~N5Rwi*iHV+#i^X zI2vh0n{#aE>(6nIoy7U_$MYycPee${*-7KDdC)+F1w5o|aMJj8ce(%X#viBu{Kpq2 zk1w9bM9>josx5*ceIn>tf6`+a49N&z@c^r&h@n#B<5nveW7+Hv0}(X27_`XqxMgaZ z9i2=j%?Lj{l4)`@ zsc-UoVs&(IaM=2OeU;X;PnlGh1tjwlVZY4l@bl--t%IZ1(b2xPf7q8(rttGVlY0#< z4njtr4j*C4I;2k-HB!jrh<$AKMf%lOJR+xeM5?QJL>YN*ONpoG469&>E-8sZ`K6Va z*?wtJ-)_67v0%rMq~Y<0A6LC@ryZ*y7gSAu9w%j`hWkm0%}XAIMC^a~an5zib$z0F z%O~CN=W($KNv(@Yf9$Gn1lI(pX@|%_(6QoTemHONdac{L!9vl1jxkgBR8nC)EjEbyZWEhO}wXM zvanx1ROeXJk=W&>ixd?}3ff20z6#7H&DobqK|;Rh1g zU4W84K)Fsxjq;AyPZtb7L|XJdj%3>M#dmpI9ep^Clo>Qmudq}XOcC8(Dbv=mIn}Mz zSl5O6J1ho7^;8^AT|h6IuVQ;4>T0+bc0hL(-Kp+qAw2M0&U0@}0!pL{%eEFLnEQkxpr9Qy4AEq<$QBHlp zyBzPYL`}GObfMZd1(^N7Y-hj?grqzGLPN_^(r@@-Dj2!#BV_SZTevi*h@Tse{)})&%_6`UC>?!YQXtTaIP`P z*0sm%o8lLCwp+Y-+QAC%^xEmHbW?6^ub^($#=Sm0!@=V~@R)^X5oS(v<;(8E=_?sv zMW0|5Xcxg7@>mDg%fWlu-9LLAg!@|j%J99^=@?e02hD@TWcINxAzrsQ=qb!XEJCMC zQMN50fA8s9lwu*$Qw(&Dqo9VrS#<;YLVxB1Z5RCa(>QX$y2JzE_A{Pq>5`f8#yU6*(iHGY0BCQJ+&Cklz;hD?TB@ z75Tg|AnynHyy}4bw#a`QkKK{a8w2uwkk6|Q$Zv~$m-QnOn5XGoxi9em`2E0NB0WUF zZc=;Kb^M$$aPKp<{{+bIG_`j`K5q=j`%LWt`Q4`Wj>zYY0ePRPJs|%Isl6ljB_063 zfA`cLB4Ae$(3aEDh$u024S;0_;NK7b%gO`(+v0!2B~|oE7w5u$!5YB#v|nFd_IllV zpSo@QxLC5KiXv!Me(5G-ivr!_VODN!pBP)eeeH~cH;oFFhmedEpR{`FkQn`XXw*{a*0zFq9Kwbs%IaWC0 zgRk-E!F{M+b^^3LqCJxzS^5Ly)wd}qcewM$0JWzr3b_K7-S$gPH*+$Uf;(M|e=2hU zuD9d*f%Ax9_A=O+_{dhjUU+Z0IDZ?noo?l(%d72z{CaKM>q@R1*DP&Q2u?pgI=1HZ z3dyjBIO;k+?G><8k!>VR?S4*mLzjw8i6+Y*Sgv!I-v8at$h1mWbDaDpWg zF`J3CFoP)AI0{VdcXZ@%yUewZbD zVB6!{IkoWb?v9u>fo_K(o$pZp(#>7AEr9Il&MsSYV7N5lAc8(&SBeYPf55RHjtjB_ z$))K<{GgH-ja&*oYDaGS8EBvEQitsZV0)S%nhp>+P-uR^DY;LeuloY&&Om>WD0)|~ z311g_#lrJgXnLvYvT^KmYgf4wFz@N|jZqxkW0Age#PkEw9g)0#1o1w~p0EJyS8Qj% z9@nP6)CZui2lTpgm~Ip>f4}0Q<_z8|KA681)#>_Xl^?M0Dal#jZZ@RPcLK0pn$|qh zvDw4f3=eUHmE#*ftK5KeU+KK=0F=GY*6IZq*H_#c$raH8KYrydh!?DZY(M!+Ci^?Q z+jreXar3|PBhNWwfZ5ZscyikhT-;gxiks*04G|_!b9(?|$sTC-e?zkto(OZfQlWeY z0N16{$XQlK*4rMb$_*&@HHpkR0M&ZSq`F{!hNbRREtCt~d1FA@&z^Ti^+@*^T1XEp zuinVcIGqoTs4iGn`T^nF5l)$U8SjPq$jFZ@{p*E!>bCKNbQjM7iIV$zlV|F7z1dw- ze4QVz+n|AaMlIK^f9`U&5V?LP6$#GoZHv22NNxujH8aq0=K82onG103>$Y`E{df1= z(I$Z360A*oL{J>LZ^u#L1(^F8zEk;8s{cD&v)=^t8?(63?Kn*=EK@+*(?;3J+RsdM zz_@z6zB#A4!5nb*v-qQRM_PFFuX`i?Z$R~ne^lRLF(6LMnR#O%+EdP< ziMC{~7tF;q9_zC%jIv}tAk#x>If@57Djv%pMI!7U;|CtAlSVcAHpiLg8t6A(UV
    RRL(O>CiG%aNfV-55tQ#|5>sg4a{ z2E&tvE_@`L{b3*i6U%!m-E}Q-vfchkqn8AE@Y^`zI4nEIR~s$% zmTR{pI!iCMHmu>-wG(^ziS&{Y!7L3*zbCa+`EJf>X{cT6r0kUD469(c&Y)ff?(0;w zmLeA+e^utkSPuf8yEtES-THU4H*jsy%bQiWSzi;WbE&uL?yT~<_4xXGyl&Ck8@|xW zzbfm^+O|++gA1Pi)!1MnyM-j1UI?zrc#8Q4LHo#9md|{OGmxwu z*7q;YRDO-wK_R8j@?`%r1JfC6PYm8*GO+0!f1n=0k<#N99;vNcfaE6emLmkn&H^Mo zuU{cV5MKbWlSnD7D^;(ywUZZMuU>OYZF)o@-3SeA_Pr6_+bUeXF(_-(-!WlvRwNo^K&Xc$ Date: Tue, 7 Feb 2023 12:09:11 -0600 Subject: [PATCH 08/15] Misc Luminex test fixes/changes to remove checks for EC50 5PL related values --- .../labkey/test/tests/luminex/LuminexEC50Test.java | 4 ++-- .../test/tests/luminex/LuminexRTransformTest.java | 10 ++++++---- .../org/labkey/test/tests/luminex/LuminexTest.java | 11 +---------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java index 277154c26a..0f0b180dfa 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexEC50Test.java @@ -118,9 +118,9 @@ else if(formula.get(i).equals(trapezoidal)) assertTrue("Unexpected analyte for Four Parameter curve fit failure", values.size() == 1 && values.get(0).equals("ENV6")); table.clearFilter("CurveType"); - // expect four 5PL curve fit failures + // expect no 5PL curve fits table.setFilter("CurveType", "Equals", "Five Parameter"); - assertEquals("Unexpected number of Five Parameter curve fit failure flags", 4, table.getDataRowCount()); + assertEquals("Unexpected number of Five Parameter curve fit rows", 0, table.getDataRowCount()); table.clearFilter("CurveType"); table.clearFilter("FailureFlag"); diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java index 73b514c580..8eb6e6760c 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java @@ -28,8 +28,11 @@ import org.labkey.test.util.LogMethod; import org.openqa.selenium.WebElement; +import java.io.File; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; @Category({Daily.class, Assays.class}) @BaseWebDriverTest.ClassTimeout(minutes = 12) @@ -105,10 +108,9 @@ private void verifyPDFsGenerated() DataRegionTable.DataRegion(getDriver()).find(); // Make sure page is loaded WebElement curvePng = Locator.tagWithAttribute("img", "src", "/labkey/_images/sigmoidal_curve.png").findElement(getDriver()); shortWait().until(LabKeyExpectedConditions.animationIsDone(curvePng)); - mouseOver(curvePng); - curvePng.click(); - assertElementPresent(Locator.linkContainingText(".Standard1_Control_Curves_4PL.pdf")); - assertElementPresent(Locator.linkWithText("WithAltNegativeBead.Standard1_4PL.pdf")); + File curvePdf = clickAndWaitForDownload(curvePng); + assertTrue("Curve PDF has wrong name: " + curvePdf.getName(), + curvePdf.getName().endsWith(".Standard1_Control_Curves_4PL.pdf")); } @LogMethod diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java index fd144a8f5f..d7081cbb32 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexTest.java @@ -714,9 +714,8 @@ protected void verifyQCReport() //make sure all the columns we want are viable _customizeViewsHelper.openCustomizeViewPanel(); _customizeViewsHelper.showHiddenItems(); - _customizeViewsHelper.addColumn("Five ParameterCurveFit/FailureFlag"); _customizeViewsHelper.addColumn("Four ParameterCurveFit/FailureFlag"); - _customizeViewsHelper.addColumn("Five ParameterCurveFit/EC50"); + _customizeViewsHelper.addColumn("Four ParameterCurveFit/EC50"); _customizeViewsHelper.saveCustomView(); assertTextPresent("Titration QC Report"); @@ -735,14 +734,6 @@ protected void verifyQCReport() assertEquals(" ", flag); } - List fiveParamFlag = drt.getColumnDataAsText("Five Parameter Curve Fit Failure Flag"); - List fiveParamData = drt.getColumnDataAsText("Five Parameter Curve Fit EC50"); - - for (int i=0; i < fiveParamData.size(); i++) - { - assertTrue("Row " + i + " was flagged as 5PL failure but had EC50 data", ((fiveParamFlag.get(i).equals(" ")) ^ (fiveParamData.get(i).equals(" ")))); - } - //verify link to Levey-Jennings plot clickAndWait(Locator.linkWithText("graph").index(0)); waitForText(" - " + isotype + " " + conjugate); From 6da17bcec7f2fedb4de76152bb4897b40a5a42f8 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 10:56:14 -0600 Subject: [PATCH 09/15] Misc Luminex test fixes/changes to remove checks for EC50 5PL related values --- .../org/labkey/test/tests/luminex/LuminexGuideSetTest.java | 7 +++---- .../labkey/test/tests/luminex/LuminexRTransformTest.java | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java index 5f242f3bf1..69b5cd5878 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java @@ -479,10 +479,9 @@ private void verifyQCFlagUpdatesAfterWellChange() _guideSetHelper.applyGuideSetToRun("NETWORK5", GUIDE_SET_5_COMMENT, false); //assert ec50 and HMFI red text present waitForText(newQcFlags); - assertElementPresent(Locator.xpath("//div[text()='28040.51' and contains(@style,'red')]")); - assertElementPresent(Locator.xpath("//div[text()='27950.49' and contains(@style,'red')]")); - assertElementPresent(Locator.xpath("//div[text()='79121.45' and contains(@style,'red')]")); - assertElementPresent(Locator.xpath("//div[text()='32145.80' and contains(@style,'red')]")); + assertElementPresent(Locator.xpath("//div[text()='28040.51' and contains(@style,'red')]")); // EC50 + assertElementPresent(Locator.xpath("//div[text()='79121.45' and contains(@style,'red')]")); // AUC + assertElementPresent(Locator.xpath("//div[text()='32145.80' and contains(@style,'red')]")); // High MFI //verify new flags present in run list goToTestAssayHome(); drt = new DataRegionTable("Runs", getDriver()); diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java index 8eb6e6760c..575b1512dc 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexRTransformTest.java @@ -67,7 +67,7 @@ public void testRTransform() verifyPDFsGenerated(); verifyScriptVersions(); verifyLotNumber(); - verifyAnalyteProperties(new String[]{ANALYTE3, ANALYTE3, " ", " "}); + verifyAnalyteProperties(new String[]{ANALYTE4, ANALYTE4, " ", " "}); } private void verifyAnalyteProperties(String[] expectedNegBead) @@ -109,8 +109,7 @@ private void verifyPDFsGenerated() WebElement curvePng = Locator.tagWithAttribute("img", "src", "/labkey/_images/sigmoidal_curve.png").findElement(getDriver()); shortWait().until(LabKeyExpectedConditions.animationIsDone(curvePng)); File curvePdf = clickAndWaitForDownload(curvePng); - assertTrue("Curve PDF has wrong name: " + curvePdf.getName(), - curvePdf.getName().endsWith(".Standard1_Control_Curves_4PL.pdf")); + assertEquals("Curve PDF has wrong name: " + curvePdf.getName(), "WithAltNegativeBead.Standard1_Control_Curves_4PL.pdf", curvePdf.getName()); } @LogMethod From 8529f6cd5cb6b62c6d48572298405eddfea32224 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 12:17:45 -0600 Subject: [PATCH 10/15] Misc Luminex test fixes/changes to remove checks for EC50 5PL related values --- .../org/labkey/test/tests/luminex/LuminexGuideSetTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java index 69b5cd5878..73ad2d4d65 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java @@ -594,10 +594,7 @@ private void verifyHighlightUpdatesAfterQCFlagChange() String[] newColumns = {"AnalyteTitration/MaxFIQCFlagsEnabled", "AnalyteTitration/MaxFI", "AnalyteTitration/Four ParameterCurveFit/EC50", "AnalyteTitration/Four ParameterCurveFit/AUC", "AnalyteTitration/Four ParameterCurveFit/EC50QCFlagsEnabled", - "AnalyteTitration/Four ParameterCurveFit/AUCQCFlagsEnabled", - "AnalyteTitration/Five ParameterCurveFit/EC50", "AnalyteTitration/Five ParameterCurveFit/AUC", - "AnalyteTitration/Five ParameterCurveFit/EC50QCFlagsEnabled", - "AnalyteTitration/Five ParameterCurveFit/AUCQCFlagsEnabled"}; + "AnalyteTitration/Four ParameterCurveFit/AUCQCFlagsEnabled"}; for(String column : newColumns) { _customizeViewsHelper.addColumn(column); From c24816e99f7bdc55e8b6355785245e5a0c69fc80 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 14:43:55 -0600 Subject: [PATCH 11/15] Hide EC50 4PL/5PL related columns and guide set inputs for Levey-Jennings report if curve fit data not present --- .../luminex/query/AnalyteTitrationTable.java | 13 +++-- .../luminex/view/leveyJenningsReport.jsp | 27 +++++++++- luminex/webapp/luminex/GuideSetWindow.js | 52 ++++++++++++------- .../luminex/LeveyJenningsGuideSetPanel.js | 2 + .../luminex/LeveyJenningsTrackingDataPanel.js | 4 +- .../luminex/LeveyJenningsTrendPlotPanel.js | 7 ++- luminex/webapp/luminex/ManageGuideSetPanel.js | 28 +++++----- 7 files changed, 92 insertions(+), 41 deletions(-) diff --git a/luminex/src/org/labkey/luminex/query/AnalyteTitrationTable.java b/luminex/src/org/labkey/luminex/query/AnalyteTitrationTable.java index 86eee4ccfc..cdcdd7b710 100644 --- a/luminex/src/org/labkey/luminex/query/AnalyteTitrationTable.java +++ b/luminex/src/org/labkey/luminex/query/AnalyteTitrationTable.java @@ -116,6 +116,7 @@ public DisplayColumn createRenderer(ColumnInfo colInfo) addColumn(wrapColumn(getRealTable().getColumn("IncludeInGuideSetCalculation"))); + List curveTypes = _userSchema.getCurveTypes(); addCurveTypeColumns(); var ljPlots = addWrapColumn("L-J Plots", getRealTable().getColumn(FieldKey.fromParts("TitrationId"))); @@ -144,8 +145,10 @@ public void renderGridCellContents(RenderContext ctx, Writer out) throws IOExcep NavTree ljPlotsNav = new NavTree("Levey-Jennings Plot Menu"); ljPlotsNav.setImage(AppProps.getInstance().getContextPath() + "/luminex/ljPlotIcon.png", 27, 20); - ljPlotsNav.addChild("EC50 - 4PL", String.format(jsFuncCall, protocolId, analyte, titration, "EC504PL")); - ljPlotsNav.addChild("EC50 - 5PL Rumi", String.format(jsFuncCall, protocolId, analyte, titration, "EC505PL")); + if (curveTypes.contains(StatsService.CurveFitType.FOUR_PARAMETER.getLabel())) + ljPlotsNav.addChild("EC50 - 4PL", String.format(jsFuncCall, protocolId, analyte, titration, "EC504PL")); + if (curveTypes.contains(StatsService.CurveFitType.FIVE_PARAMETER.getLabel())) + ljPlotsNav.addChild("EC50 - 5PL Rumi", String.format(jsFuncCall, protocolId, analyte, titration, "EC505PL")); ljPlotsNav.addChild("AUC", String.format(jsFuncCall, protocolId, analyte, titration, "AUC")); ljPlotsNav.addChild("High MFI", String.format(jsFuncCall, protocolId, analyte, titration, "HighMFI")); @@ -187,8 +190,10 @@ public boolean isFilterable() defaultCols.add(FieldKey.fromParts("Titration", "Run", "Conjugate")); defaultCols.add(FieldKey.fromParts("Analyte", "Properties", "LotNumber")); defaultCols.add(FieldKey.fromParts("GuideSet", "Created")); - defaultCols.add(FieldKey.fromParts(StatsService.CurveFitType.FOUR_PARAMETER.getLabel() + "CurveFit", "EC50")); - defaultCols.add(FieldKey.fromParts(StatsService.CurveFitType.FIVE_PARAMETER.getLabel() + "CurveFit", "EC50")); + if (curveTypes.contains(StatsService.CurveFitType.FOUR_PARAMETER.getLabel())) + defaultCols.add(FieldKey.fromParts(StatsService.CurveFitType.FOUR_PARAMETER.getLabel() + "CurveFit", "EC50")); + if (curveTypes.contains(StatsService.CurveFitType.FIVE_PARAMETER.getLabel())) + defaultCols.add(FieldKey.fromParts(StatsService.CurveFitType.FIVE_PARAMETER.getLabel() + "CurveFit", "EC50")); defaultCols.add(FieldKey.fromParts("MaxFI")); defaultCols.add(FieldKey.fromParts("TrapezoidalCurveFit", "AUC")); setDefaultVisibleColumns(defaultCols); diff --git a/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp b/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp index 4f47a3e783..950fa8748f 100644 --- a/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp +++ b/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp @@ -62,7 +62,9 @@ var defaultRowSize = 30; // local variables for storing the selected graph parameters - var _protocolId, _protocolName, _controlName, _controlType, _analyte, _isotype, _conjugate, _protocolExists = false, _networkExists = false; + var _protocolId, _protocolName, _controlName, _controlType, _analyte, _isotype, _conjugate, + _protocolExists = false, _networkExists = false, + _has4PLCurveFit = false, _has5PLCurveFit = false; function init() { @@ -87,13 +89,28 @@ return; } - var getByNameQueryComplete = false, executeSqlQueryComplete = false; + var getByNameQueryComplete = false, executeSqlQueryComplete = false, getCurveFitTypes = false; var loader = function() { if (getByNameQueryComplete && executeSqlQueryComplete) { initializeReportPanels(); } }; + // Query to see if 4PL and/or 5PL curve fit data exists for this assay + LABKEY.Query.selectDistinctRows({ + schemaName: 'assay.Luminex.' + _protocolName, + queryName: 'CurveFit', + column: 'CurveType', + scope: this, + success: function(results){ + var curveTypes = results.values; + _has4PLCurveFit = curveTypes.indexOf('Four Parameter') > -1; + _has5PLCurveFit = curveTypes.indexOf('Five Parameter') > -1; + getCurveFitTypes = true; + loader(); + } + }); + // Query the assay design to check for the required columns for the L-J report and the existance of Network and Protocol columns LABKEY.Assay.getByName({ name: _protocolName, @@ -208,6 +225,8 @@ assayName: _protocolName, networkExists: _networkExists, protocolExists: _protocolExists, + has4PLCurveFit: _has4PLCurveFit, + has5PLCurveFit: _has5PLCurveFit, listeners: { 'currentGuideSetUpdated': function() { guideSetPanel.toggleExportBtn(false); @@ -233,6 +252,8 @@ defaultRowSize: defaultRowSize, networkExists: _networkExists, protocolExists: _protocolExists, + has4PLCurveFit: _has4PLCurveFit, + has5PLCurveFit: _has5PLCurveFit, listeners: { 'reportFilterApplied': function(startDate, endDate, network, networkAny, protocol, protocolAny) { trendPlotPanel.setTrendPlotLoading(); @@ -254,6 +275,8 @@ defaultRowSize: defaultRowSize, networkExists: _networkExists, protocolExists: _protocolExists, + has4PLCurveFit: _has4PLCurveFit, + has5PLCurveFit: _has5PLCurveFit, listeners: { 'appliedGuideSetUpdated': function() { guideSetPanel.toggleExportBtn(false); diff --git a/luminex/webapp/luminex/GuideSetWindow.js b/luminex/webapp/luminex/GuideSetWindow.js index eb332571b0..be16a43e7d 100644 --- a/luminex/webapp/luminex/GuideSetWindow.js +++ b/luminex/webapp/luminex/GuideSetWindow.js @@ -21,6 +21,8 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { assayName: null, currentGuideSetId: null, canEdit: false, + has4PLCurveFit: false, + has5PLCurveFit: false, statics: { viewTpl: new Ext4.XTemplate( @@ -56,24 +58,28 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { '', '', '', - '', - 'EC50 4PL', - '{[this.formatNumber(values.EC504PLAverage)]}', - '{[this.formatNumber(values.EC504PLStdDev)]}', - '', - '{EC504PLRunCount}', - '', - '', - '', - '', - 'EC50 5PL', - '{[this.formatNumber(values.EC505PLAverage)]}', - '{[this.formatNumber(values.EC505PLStdDev)]}', - '', - '{EC505PLRunCount}', - '', + '', + '', + 'EC50 4PL', + '{[this.formatNumber(values.EC504PLAverage)]}', + '{[this.formatNumber(values.EC504PLStdDev)]}', + '', + '{EC504PLRunCount}', + '', + '', + '', + '', + '', + '', + 'EC50 5PL', + '{[this.formatNumber(values.EC505PLAverage)]}', + '{[this.formatNumber(values.EC505PLStdDev)]}', + '', + '{EC505PLRunCount}', + '', '', - '', + '', + '', '', '', 'MFI', @@ -215,6 +221,8 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { {name: 'EC505PLRunCount', type: 'int'}, {name: 'AUCRunCount', type: 'int'}, {name: 'ControlType'}, + {name: 'CurveType_4PL'}, + {name: 'CurveType_5PL'}, {name: 'UserCanEdit', type: 'boolean', defaultValue: this.canEdit} ] }); @@ -236,6 +244,9 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { sql: 'SELECT RowId, AnalyteName, Conjugate, Isotype, Comment, Created, ValueBased, ' + 'ControlName, EC504PLEnabled, EC505PLEnabled, AUCEnabled, MaxFIEnabled, ' + 'MaxFIRunCount, EC504PLRunCount, EC505PLRunCount, AUCRunCount, ControlType, ' + + // if defined checks for curve fit types + 'IFDEFINED("Four ParameterCurveFit".CurveType) AS CurveType_4PL, ' + + 'IFDEFINED("Five ParameterCurveFit".CurveType) AS CurveType_5PL,' + // handle value-based vs run-based 'CASE ValueBased WHEN true THEN EC504PLAverage ELSE "Four ParameterCurveFit".EC50Average END "EC504PLAverage", ' + 'CASE ValueBased WHEN true THEN EC504PLStdDev ELSE "Four ParameterCurveFit".EC50StdDev END "EC504PLStdDev", ' + @@ -267,6 +278,9 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { var record = Ext4.create('Luminex.model.GuideSet', response.rows[0]); this.add(record); + this.has4PLCurveFit = record.get("CurveType_4PL") != null; + this.has5PLCurveFit = record.get("CurveType_5PL") != null; + // Now that store is loaded, set combos (if not value based) var form = document.forms['GuideSetForm']; if (!record.get("ValueBased")) @@ -275,8 +289,8 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { form.elements['MFICheckBox'].initial = record.get("MaxFIEnabled"); if(record.get("ControlType") == "Titration") { - form.elements['EC504PLCheckBox'].initial = record.get("EC504PLEnabled"); - form.elements['EC505PLCheckBox'].initial = record.get("EC505PLEnabled"); + if (this.has4PLCurveFit) form.elements['EC504PLCheckBox'].initial = record.get("EC504PLEnabled"); + if (this.has5PLCurveFit) form.elements['EC505PLCheckBox'].initial = record.get("EC505PLEnabled"); form.elements['AUCCheckBox'].initial = record.get("AUCEnabled"); } } diff --git a/luminex/webapp/luminex/LeveyJenningsGuideSetPanel.js b/luminex/webapp/luminex/LeveyJenningsGuideSetPanel.js index 4bdfc7207c..b65e0705c4 100644 --- a/luminex/webapp/luminex/LeveyJenningsGuideSetPanel.js +++ b/luminex/webapp/luminex/LeveyJenningsGuideSetPanel.js @@ -306,6 +306,8 @@ LABKEY.LeveyJenningsGuideSetPanel = Ext.extend(Ext.FormPanel, { conjugate: this.conjugate, networkExists: this.networkExists, protocolExists: this.protocolExists, + has4PLCurveFit: this.has4PLCurveFit, + has5PLCurveFit: this.has5PLCurveFit, listeners: { scope: this, 'closeManageGuideSetPanel': function(saveResults) { diff --git a/luminex/webapp/luminex/LeveyJenningsTrackingDataPanel.js b/luminex/webapp/luminex/LeveyJenningsTrackingDataPanel.js index cde7435080..ae4dd4e7dd 100644 --- a/luminex/webapp/luminex/LeveyJenningsTrackingDataPanel.js +++ b/luminex/webapp/luminex/LeveyJenningsTrackingDataPanel.js @@ -187,9 +187,9 @@ LABKEY.LeveyJenningsTrackingDataPanel = Ext.extend(Ext.grid.GridPanel, { if (this.controlType == "Titration") { cols.splice(2, 0, {header: 'Titration', dataIndex: 'Titration', hidden: true, renderer: this.encodingRenderer}); - cols.push({header: 'EC50 4PL', dataIndex: 'EC504PL', width: 75, renderer: this.outOfRangeRenderer("EC504PLQCFlagsEnabled"), scope: this, align: 'right'}); + cols.push({header: 'EC50 4PL', dataIndex: 'EC504PL', width: 75, renderer: this.outOfRangeRenderer("EC504PLQCFlagsEnabled"), scope: this, align: 'right', hidden: !this.has4PLCurveFit}); cols.push({header: 'EC50 4PL QC Flags Enabled', dataIndex: 'EC504PLQCFlagsEnabled', hidden: true}); - cols.push({header: 'EC50 5PL', dataIndex: 'EC505PL', width: 75, renderer: this.outOfRangeRenderer("EC505PLQCFlagsEnabled"), scope: this, align: 'right'}); + cols.push({header: 'EC50 5PL', dataIndex: 'EC505PL', width: 75, renderer: this.outOfRangeRenderer("EC505PLQCFlagsEnabled"), scope: this, align: 'right', hidden: !this.has5PLCurveFit}); cols.push({header: 'EC50 5PL QC Flags Enabled', dataIndex: 'EC505PLQCFlagsEnabled', hidden: true}); cols.push({header: 'AUC', dataIndex: 'AUC', width: 75, renderer: this.outOfRangeRenderer("AUCQCFlagsEnabled"), scope: this, align: 'right'}); cols.push({header: 'AUC QC Flags Enabled', dataIndex: 'AUCQCFlagsEnabled', hidden: true}); diff --git a/luminex/webapp/luminex/LeveyJenningsTrendPlotPanel.js b/luminex/webapp/luminex/LeveyJenningsTrendPlotPanel.js index 1e713ad02e..8c7c81a2d2 100644 --- a/luminex/webapp/luminex/LeveyJenningsTrendPlotPanel.js +++ b/luminex/webapp/luminex/LeveyJenningsTrendPlotPanel.js @@ -505,7 +505,12 @@ LABKEY.LeveyJenningsTrendPlotPanel = Ext.extend(Ext.FormPanel, { getTitrationSinglePointControlItems: function() { if (this.controlType == "Titration") { - return([this.ec504plPanel, this.ec505plPanel, this.aucPanel, this.mfiPanel]); + var panels = []; + if (this.has4PLCurveFit) panels.push(this.ec504plPanel); + if (this.has5PLCurveFit) panels.push(this.ec505plPanel); + panels.push(this.aucPanel); + panels.push(this.mfiPanel); + return(panels); } else if (this.controlType = "SinglePoint") { return([this.singlePointControlPanel]); } diff --git a/luminex/webapp/luminex/ManageGuideSetPanel.js b/luminex/webapp/luminex/ManageGuideSetPanel.js index 69a0445697..265dca3306 100644 --- a/luminex/webapp/luminex/ManageGuideSetPanel.js +++ b/luminex/webapp/luminex/ManageGuideSetPanel.js @@ -30,12 +30,7 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { labelStyleStr : 'padding: 0; margin: 0;', - metrics : [ - {name: 'EC504PL', label: 'EC50 4PL', includeForSinglePointControl: false}, - {name: 'EC505PL', label: 'EC50 5PL (Rumi)', includeForSinglePointControl: false}, - {name: 'AUC', label: 'AUC', includeForSinglePointControl: false}, - {name: 'MaxFI', label: 'High MFI', includeForSinglePointControl: true} - ], + metrics : [], constructor : function(config){ // check that the config properties needed are present @@ -63,6 +58,13 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { initComponent : function() { LABKEY.ManageGuideSetPanel.superclass.initComponent.call(this); + this.metrics = [ + {name: 'EC504PL', label: 'EC50 4PL', includeForSinglePointControl: false, hidden: !this.has4PLCurveFit}, + {name: 'EC505PL', label: 'EC50 5PL (Rumi)', includeForSinglePointControl: false, hidden: !this.has5PLCurveFit}, + {name: 'AUC', label: 'AUC', includeForSinglePointControl: false, hidden: false}, + {name: 'MaxFI', label: 'High MFI', includeForSinglePointControl: true, hidden: false} + ]; + var columns = 'RowId, CurrentGuideSet, Comment, Created, ValueBased'; Ext.each(this.metrics, function(metric){ if (this.isTitrationControlType() || metric.includeForSinglePointControl) @@ -219,8 +221,8 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { allRunsCols.push({header:'Acquisition Date', dataIndex:'Analyte/Data/AcquisitionDate', renderer: this.dateRenderer, width:100}); if (this.isTitrationControlType()) { - allRunsCols.push({header:'EC50 4PL', dataIndex:'Four ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right'}); - allRunsCols.push({header:'EC50 5PL', dataIndex:'Five ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right'}); + allRunsCols.push({header:'EC50 4PL', dataIndex:'Four ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right', hidden: !this.has4PLCurveFit}); + allRunsCols.push({header:'EC50 5PL', dataIndex:'Five ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right', hidden: !this.has5PLCurveFit}); allRunsCols.push({header:'AUC', dataIndex:'TrapezoidalCurveFit/AUC', width:75, renderer: this.numberRenderer, align: 'right'}); allRunsCols.push({header:'High MFI', dataIndex:'MaxFI', width:75, renderer: this.numberRenderer, align: 'right'}); } @@ -300,8 +302,8 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { guideRunSetCols.push({header:'Acquisition Date', dataIndex:'Analyte/Data/AcquisitionDate', renderer: this.dateRenderer, width:100}); if (this.isTitrationControlType()) { - guideRunSetCols.push({header:'EC50 4PL', dataIndex:'Four ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right'}); - guideRunSetCols.push({header:'EC50 5PL', dataIndex:'Five ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right'}); + guideRunSetCols.push({header:'EC50 4PL', dataIndex:'Four ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right', hidden: !this.has4PLCurveFit}); + guideRunSetCols.push({header:'EC50 5PL', dataIndex:'Five ParameterCurveFit/EC50', width:75, renderer: this.numberRenderer, align: 'right', hidden: !this.has5PLCurveFit}); guideRunSetCols.push({header:'AUC', dataIndex:'TrapezoidalCurveFit/AUC', width:75, renderer: this.numberRenderer, align: 'right'}); guideRunSetCols.push({header:'High MFI', dataIndex:'MaxFI', width:75, renderer: this.numberRenderer, align: 'right'}); } @@ -492,7 +494,7 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { }); Ext.each(this.metrics, function(metric){ - if (this.isTitrationControlType() || metric.includeForSinglePointControl) + if ((this.isTitrationControlType() || metric.includeForSinglePointControl) && !metric.hidden) this.metricLabelsPanel.add(this.createLabelField(metric.label + ':')); }, this); } @@ -510,7 +512,7 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { }); Ext.each(this.metrics, function(metric){ - if (this.isTitrationControlType() || metric.includeForSinglePointControl) + if ((this.isTitrationControlType() || metric.includeForSinglePointControl) && !metric.hidden) this.metricMeanValuesPanel.add(this.createNumberField(metric.name + 'Average')); }, this); } @@ -528,7 +530,7 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { }); Ext.each(this.metrics, function(metric){ - if (this.isTitrationControlType() || metric.includeForSinglePointControl) + if ((this.isTitrationControlType() || metric.includeForSinglePointControl) && !metric.hidden) { // issue 20152: don't allow std dev if average is blank var stdDevField = this.createNumberField(metric.name + 'StdDev'); From 633c412c4396e90c6807bd72bcf5deee7ef900c5 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 14:56:31 -0600 Subject: [PATCH 12/15] Hide EC50 4PL/5PL related columns and guide set inputs for Levey-Jennings report if curve fit data not present --- luminex/webapp/luminex/ManageGuideSetPanel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/luminex/webapp/luminex/ManageGuideSetPanel.js b/luminex/webapp/luminex/ManageGuideSetPanel.js index 265dca3306..10c11d66b2 100644 --- a/luminex/webapp/luminex/ManageGuideSetPanel.js +++ b/luminex/webapp/luminex/ManageGuideSetPanel.js @@ -67,7 +67,7 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { var columns = 'RowId, CurrentGuideSet, Comment, Created, ValueBased'; Ext.each(this.metrics, function(metric){ - if (this.isTitrationControlType() || metric.includeForSinglePointControl) + if ((this.isTitrationControlType() || metric.includeForSinglePointControl) && !metric.hidden) columns += ', ' + metric.name + 'Average, ' + metric.name + 'StdDev'; }, this); @@ -549,7 +549,7 @@ LABKEY.ManageGuideSetPanel = Ext.extend(Ext.FormPanel, { var values = {}; Ext.each(this.metrics, function(metric){ - if (this.isTitrationControlType() || metric.includeForSinglePointControl) + if ((this.isTitrationControlType() || metric.includeForSinglePointControl) && !metric.hidden) { var numFld = this.getMetricMeanValuesPanel().getComponent(metric.name + 'Average'); values[numFld.getName()] = numFld.getValue(); From db708ee36031dda0eb79cee31723e37542ae1ba7 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 15:04:23 -0600 Subject: [PATCH 13/15] Update L-J test to verify EC50 5PL tab is not present --- .../labkey/test/tests/luminex/LuminexGuideSetTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java index 73ad2d4d65..66acb7096f 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetTest.java @@ -395,10 +395,10 @@ private void verifyLeveyJenningsPlots() _guideSetHelper.waitForLeveyJenningsTrendPlot(); assertElementPresent(Locator.id("EC504PLTrendPlotDiv")); - // check5PL ec50 trending plot - click(Locator.tagWithText("span", "EC50 - 5PL Rumi")); - _guideSetHelper.waitForLeveyJenningsTrendPlot(); - assertElementPresent(Locator.id("EC505PLTrendPlotDiv")); + // check 5PL ec50 trending plot tab not shown + assertElementNotPresent(Locator.linkWithText("EC50 - 5PL Rumi")); + assertTextNotPresent("EC50 - 5PL Rumi"); + assertElementNotPresent(Locator.id("EC505PLTrendPlotDiv")); // check auc trending plot click(Locator.tagWithText("span", "AUC")); From 982cdd72a94e8de1d51594ed9b9d0d00c258eb05 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 15:59:37 -0600 Subject: [PATCH 14/15] remove EC50 5PL checks for guide set details dialog --- .../labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java index 703169b456..c20cf3c523 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexGuideSetDisablingTest.java @@ -279,7 +279,6 @@ public void verifyGuideSetParameterDetailsWindow() validateGuideSetRunDetails("Run-based", "Titration"); validateGuideSetMetricsDetails(new String[][]{ {"5.284", "0.323", "2" }, // EC50 4PL - {"N/A", "N/A", "0" }, // EC50 5PL {"32086.500", "331.633", "2" }, // MFI {"63299.346", "149.318", "2" }, // AUC }); @@ -293,7 +292,6 @@ public void verifyGuideSetParameterDetailsWindow() validateGuideSetRunDetails("Value-based", "Titration"); validateGuideSetMetricsDetails(new String[][]{ {"42158.220", "4833.760"}, // EC50 4PL - {"N/A", "N/A", "0" }, // EC50 5PL {"32507.270", "189.830"}, // MFI {"85268.040", "738.550"} // AUC }); From 36f2b2ff0ab919b53f4b5a58c077145e8e7eece1 Mon Sep 17 00:00:00 2001 From: cnathe Date: Wed, 8 Feb 2023 17:34:06 -0600 Subject: [PATCH 15/15] GuideSetWindow.js fix for query to get info on which curve fit types are present --- .../luminex/view/leveyJenningsReport.jsp | 2 +- .../LuminexValueBasedGuideSetTest.java | 8 ---- luminex/webapp/luminex/GuideSetWindow.js | 45 ++++++++++++------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp b/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp index 950fa8748f..f232529729 100644 --- a/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp +++ b/luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp @@ -91,7 +91,7 @@ var getByNameQueryComplete = false, executeSqlQueryComplete = false, getCurveFitTypes = false; var loader = function() { - if (getByNameQueryComplete && executeSqlQueryComplete) { + if (getByNameQueryComplete && executeSqlQueryComplete && getCurveFitTypes) { initializeReportPanels(); } }; diff --git a/luminex/test/src/org/labkey/test/tests/luminex/LuminexValueBasedGuideSetTest.java b/luminex/test/src/org/labkey/test/tests/luminex/LuminexValueBasedGuideSetTest.java index 6b228bfdc9..c9130d7374 100644 --- a/luminex/test/src/org/labkey/test/tests/luminex/LuminexValueBasedGuideSetTest.java +++ b/luminex/test/src/org/labkey/test/tests/luminex/LuminexValueBasedGuideSetTest.java @@ -83,8 +83,6 @@ private void createInitialGuideSets() _guideSetHelper.createGuideSet(true); metricInputs.put("EC504PLAverage", 179.60); metricInputs.put("EC504PLStdDev", 22.48); - metricInputs.put("EC505PLAverage", 249.04); - metricInputs.put("EC505PLStdDev", 12.41); metricInputs.put("AUCAverage", 8701.37); metricInputs.put("AUCStdDev", 466.82); metricInputs.put("MaxFIAverage", 11457.15); @@ -98,8 +96,6 @@ private void createInitialGuideSets() _guideSetHelper.createGuideSet(true); metricInputs.put("EC504PLAverage", 43426.10); metricInputs.put("EC504PLStdDev", 794.96); - metricInputs.put("EC505PLAverage", 45242.19); - metricInputs.put("EC505PLStdDev", 10007.113); metricInputs.put("AUCAverage", 80851.74); metricInputs.put("AUCStdDev", 6523.05); metricInputs.put("MaxFIAverage", 30992.25); @@ -131,8 +127,6 @@ private void updateGuideSets() // The automation library clears a txt box before entering a new value, so we need to update both the Avg and the StdDev. _guideSetHelper.setUpLeveyJenningsGraphParams(LuminexGuideSetHelper.GUIDE_SET_ANALYTE_NAMES[0]); clickButtonContainingText("Edit", 0); - metricInputs.put("EC505PLAverage", 325.0); - metricInputs.put("EC505PLStdDev", 100.0); metricInputs.put("AUCAverage", 7800.0); metricInputs.put("AUCStdDev", 466.82); metricInputs.put("MaxFIAverage", 10200.0); @@ -144,8 +138,6 @@ private void updateGuideSets() _guideSetHelper.createGuideSet(false); metricInputs.put("EC504PLAverage", 42158.38); metricInputs.put("EC504PLStdDev", 4833.95); - metricInputs.put("EC505PLAverage", 44249.59); - metricInputs.put("EC505PLStdDev", 8084.973); metricInputs.put("AUCAverage", 85267.94); metricInputs.put("AUCStdDev", 738.53); metricInputs.put("MaxFIAverage", 32507.27); diff --git a/luminex/webapp/luminex/GuideSetWindow.js b/luminex/webapp/luminex/GuideSetWindow.js index be16a43e7d..8eb9644c3b 100644 --- a/luminex/webapp/luminex/GuideSetWindow.js +++ b/luminex/webapp/luminex/GuideSetWindow.js @@ -21,6 +21,8 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { assayName: null, currentGuideSetId: null, canEdit: false, + + // These values will be set in the constructor via the LABKEY.Query.selectDistinctRows call has4PLCurveFit: false, has5PLCurveFit: false, @@ -58,7 +60,7 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { '', '', '', - '', + '', '', 'EC50 4PL', '{[this.formatNumber(values.EC504PLAverage)]}', @@ -69,7 +71,7 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { '', '', '', - '', + '', '', 'EC50 5PL', '{[this.formatNumber(values.EC505PLAverage)]}', @@ -182,9 +184,23 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { this.assayName = config['assayName']; this.addEvents('aftersave'); this.callParent([config]); - // wait till after constructed so that currentGuideSetId is set and assayName - this.getGuideSetStore().load(); - this.show(); + + // Query to see if 4PL and/or 5PL curve fit data exists for this assay + LABKEY.Query.selectDistinctRows({ + schemaName: 'assay.Luminex.'+LABKEY.QueryKey.encodePart(this.assayName), + queryName: 'CurveFit', + column: 'CurveType', + scope: this, + success: function(results){ + var curveTypes = results.values; + this.has4PLCurveFit = curveTypes.indexOf('Four Parameter') > -1; + this.has5PLCurveFit = curveTypes.indexOf('Five Parameter') > -1; + + // wait till after constructed so that currentGuideSetId is set and assayName + this.getGuideSetStore().load(); + this.show(); + } + }); }, // NOTE: consider putting store/model into seperate file... @@ -221,14 +237,15 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { {name: 'EC505PLRunCount', type: 'int'}, {name: 'AUCRunCount', type: 'int'}, {name: 'ControlType'}, - {name: 'CurveType_4PL'}, - {name: 'CurveType_5PL'}, + {name: 'has4PLCurveFit', type: 'boolean'}, + {name: 'has5PLCurveFit', type: 'boolean'}, {name: 'UserCanEdit', type: 'boolean', defaultValue: this.canEdit} ] }); var assayName = this.assayName; var currentGuideSetId = this.currentGuideSetId; + var gsWindow = this; Ext4.define('Luminex.store.GuideSet', { extend: 'Ext.data.Store', @@ -244,9 +261,6 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { sql: 'SELECT RowId, AnalyteName, Conjugate, Isotype, Comment, Created, ValueBased, ' + 'ControlName, EC504PLEnabled, EC505PLEnabled, AUCEnabled, MaxFIEnabled, ' + 'MaxFIRunCount, EC504PLRunCount, EC505PLRunCount, AUCRunCount, ControlType, ' + - // if defined checks for curve fit types - 'IFDEFINED("Four ParameterCurveFit".CurveType) AS CurveType_4PL, ' + - 'IFDEFINED("Five ParameterCurveFit".CurveType) AS CurveType_5PL,' + // handle value-based vs run-based 'CASE ValueBased WHEN true THEN EC504PLAverage ELSE "Four ParameterCurveFit".EC50Average END "EC504PLAverage", ' + 'CASE ValueBased WHEN true THEN EC504PLStdDev ELSE "Four ParameterCurveFit".EC50StdDev END "EC504PLStdDev", ' + @@ -274,13 +288,14 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { return; } + // add in the has4PLCurveFit and has5PLCurveFit info to the record so that it can be used in the tpl + response.rows[0]['has4PLCurveFit'] = gsWindow.has4PLCurveFit; + response.rows[0]['has5PLCurveFit'] = gsWindow.has5PLCurveFit; + this.removeAll(); var record = Ext4.create('Luminex.model.GuideSet', response.rows[0]); this.add(record); - this.has4PLCurveFit = record.get("CurveType_4PL") != null; - this.has5PLCurveFit = record.get("CurveType_5PL") != null; - // Now that store is loaded, set combos (if not value based) var form = document.forms['GuideSetForm']; if (!record.get("ValueBased")) @@ -289,8 +304,8 @@ Ext4.define('LABKEY.luminex.GuideSetWindow', { form.elements['MFICheckBox'].initial = record.get("MaxFIEnabled"); if(record.get("ControlType") == "Titration") { - if (this.has4PLCurveFit) form.elements['EC504PLCheckBox'].initial = record.get("EC504PLEnabled"); - if (this.has5PLCurveFit) form.elements['EC505PLCheckBox'].initial = record.get("EC505PLEnabled"); + if (gsWindow.has4PLCurveFit) form.elements['EC504PLCheckBox'].initial = record.get("EC504PLEnabled"); + if (gsWindow.has5PLCurveFit) form.elements['EC505PLCheckBox'].initial = record.get("EC505PLEnabled"); form.elements['AUCCheckBox'].initial = record.get("AUCEnabled"); } }