Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
350 changes: 8 additions & 342 deletions luminex/resources/transformscripts/labkey_luminex_transform.R

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion luminex/src/org/labkey/luminex/LuminexDataHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ else if (well.getDose() == 0.0)
return null;
}

/** @return null if we can't find matching Rumi curve fit data */
/** @return null if we can't find matching Rumi/drc curve fit data */
@Nullable
private CurveFit importRumiCurveFit(StatsService.CurveFitType fitType, LuminexDataRow dataRow, LuminexWellGroup wellGroup, User user, Titration titration, Analyte analyte, CurveFit[] existingCurveFits)
{
Expand Down
13 changes: 9 additions & 4 deletions luminex/src/org/labkey/luminex/query/AnalyteTitrationTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public DisplayColumn createRenderer(ColumnInfo colInfo)

addColumn(wrapColumn(getRealTable().getColumn("IncludeInGuideSetCalculation")));

List<String> curveTypes = _userSchema.getCurveTypes();
addCurveTypeColumns();

var ljPlots = addWrapColumn("L-J Plots", getRealTable().getColumn(FieldKey.fromParts("TitrationId")));
Expand Down Expand Up @@ -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"));

Expand Down Expand Up @@ -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);
Expand Down
29 changes: 26 additions & 3 deletions luminex/src/org/labkey/luminex/view/leveyJenningsReport.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -87,13 +89,28 @@
return;
}

var getByNameQueryComplete = false, executeSqlQueryComplete = false;
var getByNameQueryComplete = false, executeSqlQueryComplete = false, getCurveFitTypes = false;
var loader = function() {
if (getByNameQueryComplete && executeSqlQueryComplete) {
if (getByNameQueryComplete && executeSqlQueryComplete && getCurveFitTypes) {
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,
Expand Down Expand Up @@ -208,6 +225,8 @@
assayName: _protocolName,
networkExists: _networkExists,
protocolExists: _protocolExists,
has4PLCurveFit: _has4PLCurveFit,
has5PLCurveFit: _has5PLCurveFit,
listeners: {
'currentGuideSetUpdated': function() {
guideSetPanel.toggleExportBtn(false);
Expand All @@ -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();
Expand All @@ -254,6 +275,8 @@
defaultRowSize: defaultRowSize,
networkExists: _networkExists,
protocolExists: _protocolExists,
has4PLCurveFit: _has4PLCurveFit,
has5PLCurveFit: _has5PLCurveFit,
listeners: {
'appliedGuideSetUpdated': function() {
guideSetPanel.toggleExportBtn(false);
Expand Down
Binary file modified luminex/test/sampledata/luminex/TestLuminexAssay.xar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ private void reimportLuminexRunPageTwo(String runId, String isotype, String conj
assertFormElementEquals(Locator.name("conjugate"), conjugate);
assertFormElementEquals(Locator.name("stndCurveFitInput"), stndCurveFitInput);
assertFormElementEquals(Locator.name("unkCurveFitInput"), unkCurveFitInput);
uncheckCheckbox(Locator.name("curveFitLogTransform"));
assertFormElementEquals(Locator.name("notebookNo"), notebookNo);
assertFormElementEquals(Locator.name("assayType"), assayType);
assertFormElementEquals(Locator.name("expPerformer"), expPerformer);
Expand Down Expand Up @@ -169,12 +168,12 @@ private void assertLuminexLogInfoPresent()
assertTextPresentInThisOrder("----- Start Run Properties -----", "----- End Run Properties -----");
assertTextPresent("Uploaded Files", "Assay ID", "Isotype", "Conjugate", "Test Date", "Replaces Previous File", "Date file was modified",
"Specimen Type", "Additive", "Derivative", "Subtract Negative Bead", "Calc of Standards", "Calc of Unknown",
"Curve Fit Log", "Notebook Number", "Assay Type", "Experiment Performer", "Calculate Positivity",
"Notebook Number", "Assay Type", "Experiment Performer", "Calculate Positivity",
"Baseline Visit", "Positivity Fold Change");

//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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -58,11 +54,9 @@ 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"));
uploadMultipleCurveData();
clickButton("Save and Finish", longWaitForPage);

Expand All @@ -73,14 +67,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
Expand All @@ -98,27 +84,17 @@ private void checkEC50dataAndFailureFlag()
List<String> ec50 = table.getColumnDataAsText("EC50");
List<String> auc= table.getColumnDataAsText("AUC");
List<String> inflectionPoint = table.getColumnDataAsText("Inflection");
int rum5ec50count = 0;

log("Write this");
for(int i=0; i<formula.size(); i++)
{
if(formula.get(i).equals(rum4))
if(formula.get(i).equals(drc4))
{
//ec50=populated=inflectionPoint
assertEquals(ec50.get(i), inflectionPoint.get(i));
//auc=unpopulated
assertEquals(" ", auc.get(i));
}
else if(formula.get(i).equals(rum5))
{
// ec50 will be populated for well formed curves (i.e. not expected for every row, so we'll keep a count and check at the end of the loop)
if (!ec50.get(i).equals(" ") && ec50.get(i).length() > 0)
rum5ec50count++;

// auc should not be populated
assertEquals(" ", auc.get(i));
}
else if(formula.get(i).equals(trapezoidal))
{
//ec50 should not be populated
Expand All @@ -128,30 +104,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");
Expand All @@ -166,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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -216,24 +213,19 @@ 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
waitForElement(Locator.checkboxByName("AUCCheckBox"));
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();

Expand All @@ -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()
Expand Down Expand Up @@ -287,10 +278,9 @@ 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
{"32086.500", "331.633", "2" }, // MFI
{"63299.346", "149.318", "2" }, // AUC
});
// validate checkboxes are not displayed
assertTextPresent("# Runs");
Expand All @@ -301,10 +291,9 @@ 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
{"32507.270", "189.830"}, // MFI
{"85268.040", "738.550"} // AUC
});
// validate checkboxes are not displayed
assertTextNotPresent("# Runs");
Expand Down
Loading