diff --git a/resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql b/resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql
new file mode 100644
index 000000000..a67743eb7
--- /dev/null
+++ b/resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql
@@ -0,0 +1,2 @@
+ALTER TABLE targetedms.GeneralPrecursor
+ ADD COLUMN SpectrumFilter BYTEA;
\ No newline at end of file
diff --git a/resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql b/resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql
new file mode 100644
index 000000000..8cfa72753
--- /dev/null
+++ b/resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql
@@ -0,0 +1 @@
+ALTER TABLE targetedms.GeneralPrecursor ADD SpectrumFilter VARBINARY(MAX);
\ No newline at end of file
diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml
index 8b74a6b92..283a8c550 100644
--- a/resources/schemas/targetedms.xml
+++ b/resources/schemas/targetedms.xml
@@ -438,6 +438,7 @@
DateTime
+
diff --git a/src/org/labkey/targetedms/SkylineDocImporter.java b/src/org/labkey/targetedms/SkylineDocImporter.java
index bd4ba0ab3..b3388a101 100644
--- a/src/org/labkey/targetedms/SkylineDocImporter.java
+++ b/src/org/labkey/targetedms/SkylineDocImporter.java
@@ -1521,6 +1521,8 @@ else if (generalMolecule instanceof Molecule molecule)
{
if (precursor.getIsotopeLabelId() != numLabelId)
continue;
+ if (precursor.getSpectrumFilter() != null)
+ continue;
PrecursorAreaRatio pRatio = areaRatioCalculator.getPrecursorAreaRatio(sampleFile.getId(),
precursor,
diff --git a/src/org/labkey/targetedms/TargetedMSModule.java b/src/org/labkey/targetedms/TargetedMSModule.java
index 94344db8c..69bdeca96 100644
--- a/src/org/labkey/targetedms/TargetedMSModule.java
+++ b/src/org/labkey/targetedms/TargetedMSModule.java
@@ -69,6 +69,7 @@
import org.labkey.targetedms.folderImport.QCFolderWriterFactory;
import org.labkey.targetedms.parser.Protein;
import org.labkey.targetedms.parser.SampleFile;
+import org.labkey.targetedms.parser.SpectrumFilter;
import org.labkey.targetedms.parser.skyaudit.SkylineAuditLogParser;
import org.labkey.targetedms.passport.PassportController;
import org.labkey.targetedms.pipeline.TargetedMSPipelineProvider;
@@ -222,7 +223,7 @@ public String getName()
@Override
public Double getSchemaVersion()
{
- return 24.004;
+ return 24.005;
}
@Override
@@ -668,7 +669,8 @@ public Set getUnitTests()
TargetedMSController.TestCase.class,
PrecursorManager.TestCase.class,
CrossLinkedPeptideInfo.TestCase.class,
- Protein.TestCase.class
+ Protein.TestCase.class,
+ SpectrumFilter.TestCase.class
);
}
diff --git a/src/org/labkey/targetedms/parser/ChromatogramGroupId.java b/src/org/labkey/targetedms/parser/ChromatogramGroupId.java
index cf1847ce3..74a03a513 100644
--- a/src/org/labkey/targetedms/parser/ChromatogramGroupId.java
+++ b/src/org/labkey/targetedms/parser/ChromatogramGroupId.java
@@ -15,22 +15,32 @@
*/
package org.labkey.targetedms.parser;
+import org.apache.commons.lang3.StringUtils;
import org.labkey.targetedms.parser.proto.ChromatogramGroupDataOuterClass;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
public class ChromatogramGroupId
{
private Target _target;
private String _qcTraceName;
+ private SpectrumFilter _spectrumFilter;
private ChromatogramGroupId()
{
}
- public ChromatogramGroupId(Target target)
+ public ChromatogramGroupId(Target target, String qcTraceName, SpectrumFilter spectrumFilter)
{
_target = target;
+ _qcTraceName = StringUtils.isEmpty(qcTraceName) ? null : qcTraceName;
+ _spectrumFilter = spectrumFilter;
+ }
+
+ public ChromatogramGroupId(Target target, SpectrumFilter spectrumFilter)
+ {
+ this(target, null, spectrumFilter);
}
public Target getTarget()
@@ -43,6 +53,11 @@ public String getQcTraceName()
return _qcTraceName;
}
+ public SpectrumFilter getSpectrumFilter()
+ {
+ return _spectrumFilter;
+ }
+
public static ChromatogramGroupId forQcTraceName(String qcTraceName)
{
ChromatogramGroupId chromatogramGroupId = new ChromatogramGroupId();
@@ -55,14 +70,24 @@ public static List fromProtos(ChromatogramGroupDataOuterCla
List targets = new ArrayList<>();
// Make one-based lookups easy
targets.add(null);
+
+ List filterClauses = new ArrayList<>();
+
for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.Target target : proto.getTargetsList())
{
targets.add(new Target(target));
}
+ for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter spectrumFilter : proto.getFiltersList())
+ {
+ filterClauses.add(SpectrumFilter.FilterClause.fromProtocolMessage(spectrumFilter));
+ }
List list = new ArrayList<>();
for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.ChromatogramGroupId chromatogramGroupId : proto.getChromatogramGroupIdsList())
{
- list.add(new ChromatogramGroupId(targets.get(chromatogramGroupId.getTargetIndex())));
+ SpectrumFilter spectrumFilter = SpectrumFilter.fromFilterClauses(
+ chromatogramGroupId.getFilterIndexesList().stream()
+ .map(filterClauses::get).collect(Collectors.toList())).orElse(null);
+ list.add(new ChromatogramGroupId(targets.get(chromatogramGroupId.getTargetIndex()), chromatogramGroupId.getQcTraceName(), spectrumFilter));
}
return list;
}
diff --git a/src/org/labkey/targetedms/parser/GeneralPrecursor.java b/src/org/labkey/targetedms/parser/GeneralPrecursor.java
index 49a527de1..b40f843ef 100644
--- a/src/org/labkey/targetedms/parser/GeneralPrecursor.java
+++ b/src/org/labkey/targetedms/parser/GeneralPrecursor.java
@@ -38,6 +38,7 @@ public class GeneralPrecursor extends
private Double _explicitCcsSqa;
private Double _explicitCompensationVoltage;
private Double _precursorConcentration;
+ private byte[] _spectrumFilter;
public long getGeneralMoleculeId()
{
@@ -213,4 +214,14 @@ public void setPrecursorConcentration(Double precursorConcentration)
{
_precursorConcentration = precursorConcentration;
}
+
+ public byte[] getSpectrumFilter()
+ {
+ return _spectrumFilter;
+ }
+
+ public void setSpectrumFilter(byte[] spectrumFilter)
+ {
+ _spectrumFilter = spectrumFilter;
+ }
}
diff --git a/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java b/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java
index d0675e753..19d245f8e 100644
--- a/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java
+++ b/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java
@@ -58,6 +58,13 @@ public void init(Map skylineIdSamp
for(Precursor precursor: _peptide.getPrecursorList())
{
+ if (precursor.getSpectrumFilter() != null)
+ {
+ // Ideally, ratios would be calculated separately between Precursors with the same Spectrum Filter.
+ // However, we only store one ratio in the database for each Peptide, so we just skip precursors
+ // which have a Spectrum Filter.
+ continue;
+ }
for(PrecursorChromInfo precursorChromInfo : precursor.getChromInfoList())
{
if(precursorChromInfo.isOptimizationPeak())
diff --git a/src/org/labkey/targetedms/parser/SkylineBinaryParser.java b/src/org/labkey/targetedms/parser/SkylineBinaryParser.java
index 60c72bb05..e72f0df66 100644
--- a/src/org/labkey/targetedms/parser/SkylineBinaryParser.java
+++ b/src/org/labkey/targetedms/parser/SkylineBinaryParser.java
@@ -383,7 +383,7 @@ public ChromatogramGroupId getTextId(ChromGroupHeaderInfo chromGroupHeaderInfo)
{
return null;
}
- return new ChromatogramGroupId(target);
+ return new ChromatogramGroupId(target, null);
}
public String getFilePath(ChromGroupHeaderInfo chromGroupHeaderInfo) {
diff --git a/src/org/labkey/targetedms/parser/SkylineDocumentParser.java b/src/org/labkey/targetedms/parser/SkylineDocumentParser.java
index a9ad813f7..d798d635d 100644
--- a/src/org/labkey/targetedms/parser/SkylineDocumentParser.java
+++ b/src/org/labkey/targetedms/parser/SkylineDocumentParser.java
@@ -17,6 +17,7 @@
package org.labkey.targetedms.parser;
import com.google.common.collect.Iterables;
+import com.google.protobuf.InvalidProtocolBufferException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
@@ -63,6 +64,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -157,6 +159,7 @@ public class SkylineDocumentParser implements AutoCloseable
private static final String CHARGE = "charge" ;
public static final String TRANSITION_DATA = "transition_data";
private static final String LINKED_FRAGMENT_ION = "linked_fragment_ion";
+ private static final String SPECTRUM_FILTER = "spectrum_filter";
private static final double MIN_SUPPORTED_VERSION = 1.2;
public static final double MAX_SUPPORTED_VERSION = 23.1;
@@ -1860,7 +1863,7 @@ private MoleculePrecursor readMoleculePrecursor(XMLStreamReader reader, Molecule
String charge = reader.getAttributeValue(null, CHARGE);
if(null != charge)
moleculePrecursor.setCharge(Integer.parseInt(charge));
-
+ List spectrumFilterClauses = new ArrayList<>();
while(reader.hasNext())
{
int evtType = reader.next();
@@ -1879,8 +1882,11 @@ else if (XmlUtil.isStartElement(reader, evtType, ANNOTATION))
annotations.add(readAnnotation(reader, new PrecursorAnnotation()));
else if (XmlUtil.isStartElement(reader, evtType, NOTE))
moleculePrecursor.setNote(readNote(reader));
+ else if (XmlUtil.isStartElement(reader, evtType, SPECTRUM_FILTER))
+ spectrumFilterClauses.add(SpectrumFilter.FilterClause.parse(reader));
}
-
+ moleculePrecursor.setSpectrumFilter(SpectrumFilter.fromFilterClauses(spectrumFilterClauses)
+ .map(SpectrumFilter::toByteArray).orElse(null));
List chromatograms = tryLoadChromatogram(moleculeTransitionList, molecule, moleculePrecursor, _matchTolerance);
populateChromInfoChromatograms(moleculePrecursor, chromatograms);
@@ -2035,7 +2041,7 @@ private Precursor readPrecursor(XMLStreamReader reader, Peptide peptide, boolean
precursor.setExplicitCcsSqa(XmlUtil.readDoubleAttribute(reader, "explicit_ccs_sqa"));
precursor.setExplicitCompensationVoltage(XmlUtil.readDoubleAttribute(reader, "explicit_compensation_voltage"));
precursor.setPrecursorConcentration(XmlUtil.readDoubleAttribute(reader, "precursor_concentration"));
-
+ List spectrumFilterClauses = new ArrayList<>();
while (reader.hasNext()) {
int evtType = reader.next();
@@ -2083,8 +2089,15 @@ else if (XmlUtil.isStartElement(reader, evtType, NOTE))
{
precursor.setNote(readNote(reader));
}
+ else if (XmlUtil.isStartElement(reader, evtType, SPECTRUM_FILTER))
+ {
+ spectrumFilterClauses.add(SpectrumFilter.FilterClause.parse(reader));
+ }
}
+ precursor.setSpectrumFilter(SpectrumFilter.fromFilterClauses(spectrumFilterClauses)
+ .map(SpectrumFilter::toByteArray).orElse(null));
+
// Boolean type annotations are not listed in the .sky file if their value was false.
// We would still like to store them in the database.
List missingBooleanAnnotations = _dataSettings.getMissingBooleanAnnotations(annotations,
@@ -3113,7 +3126,6 @@ public List getSampleFileChromInfos(Map
{
return Collections.emptyList();
}
- int traceMetricIndex = 1;
for (ChromGroupHeaderInfo chromatogram : _binaryParser.getChromatograms())
{
// Sample-scoped chromatograms have a magic precursor MZ value
@@ -3145,14 +3157,7 @@ public List getSampleFileChromInfos(Map
ChromatogramGroupId chromatogramGroupId = _binaryParser.getTextId(chromatogram);
if (chromatogramGroupId != null)
{
- if (chromatogramGroupId.getQcTraceName() == null && chromatogram.getFlagValues().contains(ChromGroupHeaderInfo.FlagValues.extracted_qc_trace))
- {
- info.setTextId("QC Trace " + traceMetricIndex++);
- }
- else
- {
- info.setTextId(chromatogramGroupId.getQcTraceName());
- }
+ info.setTextId(chromatogramGroupId.getQcTraceName());
}
info.setChromatogramFormat(chromatogram.getChromatogramBinaryFormat().ordinal());
info.setChromatogramOffset(chromatogram.getLocationPoints());
@@ -3204,8 +3209,24 @@ private List tryLoadChromatogram(
ChromGroupHeaderInfo chrom = _binaryParser.getChromatograms()[i++];
// Sequence matching for extracted chromatogram data added in v1.5
ChromatogramGroupId chromTextId = _binaryParser.getTextId(chrom);
- if (chromTextId != null && !molecule.targetMatches(chromTextId.getTarget()))
- continue;
+ if (chromTextId != null)
+ {
+ if (!molecule.targetMatches(chromTextId.getTarget()))
+ continue;
+ try
+ {
+ SpectrumFilter spectrumFilter = SpectrumFilter.fromByteArray(precursor.getSpectrumFilter());
+ if (!Objects.equals(spectrumFilter, chromTextId.getSpectrumFilter()))
+ {
+ continue;
+ }
+ }
+ catch (InvalidProtocolBufferException e)
+ {
+ _log.warn("Error parsing spectrum filter {}", e);
+ return Collections.emptyList();
+ }
+ }
// If explicit retention time info is available, use that to discard obvious mismatches
if (explicitRT == null || !chrom.excludesTime(explicitRT))
diff --git a/src/org/labkey/targetedms/parser/SpectrumFilter.java b/src/org/labkey/targetedms/parser/SpectrumFilter.java
new file mode 100644
index 000000000..0c329795e
--- /dev/null
+++ b/src/org/labkey/targetedms/parser/SpectrumFilter.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2024 LabKey Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.labkey.targetedms.parser;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.labkey.targetedms.parser.proto.ChromatogramGroupDataOuterClass;
+import org.labkey.targetedms.parser.proto.ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.FilterOperation;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Specifies that a precursor's chromatogram should be extracted from a subset of the matching spectra.
+ * A SpectrumFilter contains a list of {@link FilterClause} objects which are combined with "OR" semantics.
+ * The individual {@link FilterPredicate} items with the FilterClause are combined with "AND".
+ */
+public class SpectrumFilter
+{
+ private static final Map _filterOperationMap = Map.ofEntries(
+ Map.entry("equals", FilterOperation.FILTER_OP_EQUALS),
+ Map.entry("<>", FilterOperation.FILTER_OP_NOT_EQUALS),
+ Map.entry("isnullorblank", FilterOperation.FILTER_OP_IS_BLANK),
+ Map.entry("isnotnullorblank", FilterOperation.FILTER_OP_IS_NOT_BLANK),
+ Map.entry(">", FilterOperation.FILTER_OP_IS_GREATER_THAN),
+ Map.entry("<", FilterOperation.FILTER_OP_IS_LESS_THAN),
+ Map.entry(">=", FilterOperation.FILTER_OP_IS_GREATER_THAN_OR_EQUAL_TO),
+ Map.entry("<=", FilterOperation.FILTER_OP_IS_LESS_THAN_OR_EQUAL_TO),
+ Map.entry("contains", FilterOperation.FILTER_OP_CONTAINS),
+ Map.entry("notcontains", FilterOperation.FILTER_OP_NOT_CONTAINS),
+ Map.entry("startswith", FilterOperation.FITLER_OP_STARTS_WITH),
+ Map.entry("notstartswith", FilterOperation.FILTER_OP_NOT_STARTS_WITH));
+ private static final Map _filterOperationReverseMap = _filterOperationMap.entrySet()
+ .stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
+ private final List _filterClauses;
+
+ public static Optional fromFilterClauses(List filterClauses)
+ {
+ if (filterClauses == null || filterClauses.isEmpty())
+ {
+ return Optional.empty();
+ }
+ return Optional.of(new SpectrumFilter(filterClauses));
+ }
+
+ public SpectrumFilter(List filterClauses)
+ {
+ _filterClauses = filterClauses;
+ }
+
+ public static SpectrumFilter fromProtocolMessage(ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto protocolMessage)
+ {
+ List filterClauses = new ArrayList<>();
+ for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter clause : protocolMessage.getFiltersList())
+ {
+ filterClauses.add(FilterClause.fromProtocolMessage(clause));
+ }
+ return new SpectrumFilter(filterClauses);
+ }
+
+ /**
+ * Constructs a SpectrumFilter from the bytes that are stored in the {@link GeneralPrecursor#getSpectrumFilter()}.
+ */
+ public static SpectrumFilter fromByteArray(byte[] byteArray) throws InvalidProtocolBufferException
+ {
+ if (byteArray == null || byteArray.length == 0)
+ {
+ return null;
+ }
+ return fromProtocolMessage(ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.parseFrom(byteArray));
+ }
+
+ public List getFilterClauses()
+ {
+ return _filterClauses;
+ }
+
+ public ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto toProtocolMessage()
+ {
+ return ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.newBuilder().addAllFilters(_filterClauses.stream().map(FilterClause::toProtocolMessage).collect(Collectors.toList())).build();
+ }
+
+ public byte[] toByteArray()
+ {
+ return toProtocolMessage().toByteArray();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SpectrumFilter that = (SpectrumFilter) o;
+ return Objects.equals(_filterClauses, that._filterClauses);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hashCode(_filterClauses);
+ }
+
+ public static class FilterClause
+ {
+ private final List _predicates;
+
+ public FilterClause(List predicates)
+ {
+ _predicates = predicates;
+ }
+
+ public static FilterClause fromProtocolMessage(ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter protocolMessage)
+ {
+ List predicates = new ArrayList<>();
+ for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.Predicate predicate : protocolMessage.getPredicatesList())
+ {
+ predicates.add(new FilterPredicate(predicate.getPropertyPath(), _filterOperationReverseMap.get(predicate.getOperation()), predicate.getOperand()));
+ }
+ return new FilterClause(predicates);
+ }
+
+ public static FilterClause parse(XMLStreamReader reader) throws XMLStreamException
+ {
+ String elementName = reader.getLocalName();
+ List predicates = new ArrayList<>();
+ while (reader.hasNext())
+ {
+ int evtType = reader.next();
+
+ if (evtType == XMLStreamReader.END_ELEMENT && elementName.equals(reader.getLocalName())) break;
+
+ if (XmlUtil.isStartElement(reader, evtType, "filter"))
+ {
+ predicates.add(new FilterPredicate(XmlUtil.readAttribute(reader, "column"),
+ XmlUtil.readAttribute(reader, "opname"),
+ XmlUtil.readAttribute(reader, "operand")));
+ }
+ }
+ return new FilterClause(predicates);
+ }
+
+ public List getPredicates()
+ {
+ return _predicates;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FilterClause that = (FilterClause) o;
+ return Objects.equals(_predicates, that._predicates);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hashCode(_predicates);
+ }
+
+ public ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter toProtocolMessage()
+ {
+
+ return ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.newBuilder()
+ .addAllPredicates(_predicates.stream().map(FilterPredicate::toProtocolMessage)
+ .collect(Collectors.toList())).build();
+ }
+ }
+
+ public static class FilterPredicate
+ {
+ private final String _column;
+ private final String _operation;
+ private final String _operand;
+
+ public FilterPredicate(String column, String operation, String operand)
+ {
+ _column = column;
+ _operation = operation;
+ _operand = "".equals(operand) ? null : operand;
+ }
+
+ public String getColumn()
+ {
+ return _column;
+ }
+
+ public String getOperation()
+ {
+ return _operation;
+ }
+
+ public String getOperand()
+ {
+ return _operand;
+ }
+
+ public ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.Predicate toProtocolMessage()
+ {
+ return ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.Predicate.newBuilder()
+ .setPropertyPath(getColumn()).setOperation(_filterOperationMap.get(getOperation()))
+ .setOperand(_operand == null ? "" : _operand).build();
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FilterPredicate that = (FilterPredicate) o;
+ return Objects.equals(_column, that._column) &&
+ Objects.equals(_operation, that._operation) &&
+ Objects.equals(_operand, that._operand);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(_column, _operation, _operand);
+ }
+ }
+
+ public static class TestCase
+ {
+ @Test
+ public void testParse() throws Exception
+ {
+ String xml = String.join("\n",
+ " ",
+ " ",
+ " ",
+ " \" operand=\"3\" />",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ");
+ XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(xml));
+ int bibliospecSpectrumInfoCount = 0;
+ List clauses = new ArrayList<>();
+ while (reader.hasNext())
+ {
+ int event = reader.next();
+ if (XmlUtil.isEndElement(reader, event, "precursor"))
+ {
+ break;
+ }
+ if (XmlUtil.isStartElement(reader, event, "spectrum_filter"))
+ {
+ clauses.add(FilterClause.parse(reader));
+ }
+ else if (XmlUtil.isStartElement(reader, event, "bibliospec_spectrum_info"))
+ {
+ bibliospecSpectrumInfoCount++;
+ }
+ }
+ Assert.assertEquals(2, clauses.size());
+ SpectrumFilter spectrumFilter = new SpectrumFilter(clauses);
+ Assert.assertEquals(1, bibliospecSpectrumInfoCount);
+ Assert.assertNotNull(spectrumFilter);
+ Assert.assertEquals(2, spectrumFilter.getFilterClauses().size());
+ byte[] bytes = spectrumFilter.toByteArray();
+ SpectrumFilter spectrumFilterRoundTrip = SpectrumFilter.fromByteArray(bytes);
+ Assert.assertNotNull(spectrumFilterRoundTrip);
+ Assert.assertEquals(spectrumFilter.getFilterClauses().size(), spectrumFilterRoundTrip.getFilterClauses().size());
+ Assert.assertArrayEquals(spectrumFilter.toByteArray(), spectrumFilterRoundTrip.toByteArray());
+ }
+ }
+}
diff --git a/test/sampledata/TargetedMS/DocumentFormats/SpectrumFilterTest.sky.zip b/test/sampledata/TargetedMS/DocumentFormats/SpectrumFilterTest.sky.zip
new file mode 100644
index 000000000..6ddee6b61
Binary files /dev/null and b/test/sampledata/TargetedMS/DocumentFormats/SpectrumFilterTest.sky.zip differ
diff --git a/test/src/org/labkey/test/tests/targetedms/TargetedMSDocumentFormatsTest.java b/test/src/org/labkey/test/tests/targetedms/TargetedMSDocumentFormatsTest.java
index 5aae5b164..b32db92ae 100644
--- a/test/src/org/labkey/test/tests/targetedms/TargetedMSDocumentFormatsTest.java
+++ b/test/src/org/labkey/test/tests/targetedms/TargetedMSDocumentFormatsTest.java
@@ -198,6 +198,14 @@ public void testSkyd17Format() throws Exception
Assert.assertEquals(0, countMissingChromatograms(fileName));
}
+ @Test
+ public void testSpectrumFilterDocument() throws Exception {
+ goToProjectHome(getProjectName());
+ String fileName = "SpectrumFilterTest.sky.zip";
+ importData(SAMPLEDATA_FOLDER + fileName, ++JOB_COUNT);
+ Assert.assertEquals(0, countMissingChromatograms(fileName));
+ }
+
private int countMissingChromatograms(String file) throws Exception
{
Connection cn = createDefaultConnection();