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();