diff --git a/src/main/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltration.java b/src/main/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltration.java index dd6a2e93838..a2642f20150 100644 --- a/src/main/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltration.java +++ b/src/main/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltration.java @@ -114,6 +114,7 @@ public final class VariantFiltration extends VariantWalker { public static final String CLUSTER_WINDOW_SIZE_LONG_NAME = "cluster-window-size"; public static final String MASK_EXTENSION_LONG_NAME = "mask-extension"; public static final String MASK_NAME_LONG_NAME = "mask-name"; + public static final String MASK_DESCRIPTION_LONG_NAME = "mask-description"; public static final String FILTER_NOT_IN_MASK_LONG_NAME = "filter-not-in-mask"; public static final String MISSING_VAL_LONG_NAME = "missing-values-evaluate-as-failing"; public static final String INVERT_LONG_NAME = "invert-filter-expression"; @@ -238,6 +239,14 @@ public final class VariantFiltration extends VariantWalker { @Argument(fullName=ALLELE_SPECIFIC_LONG_NAME, optional=true, doc="Set mask at the allele level. This option is not compatible with clustering.") public boolean applyForAllele = false; + /** + * If a mask interval list is provided, then set the description of the filter in the VCF header to this String. + * Note that if spaces are needed, then the entire description should be enclosed in quotes. Also note that if + * --filter-not-in-mask is used, the description should be adapted to reflect the reverse logic. + */ + @Argument(fullName=MASK_DESCRIPTION_LONG_NAME, optional=true, doc="Description to add to the FILTER field in VCF header for the mask filter.") + public String maskDescription; + // JEXL expressions for the filters private List filterExps; private List genotypeFilterExps; @@ -305,7 +314,9 @@ private void initializeVcfWriter() { } if ( mask != null ) { - if (filterRecordsNotInMask) { + if (maskDescription != null) { + hInfo.add(new VCFFilterHeaderLine(maskName, maskDescription)); + } else if (filterRecordsNotInMask) { hInfo.add(new VCFFilterHeaderLine(maskName, "Doesn't overlap a user-input mask")); } else { hInfo.add(new VCFFilterHeaderLine(maskName, "Overlaps a user-input mask")); @@ -331,6 +342,9 @@ public void onTraversalStart() { if (filterRecordsNotInMask && mask == null) { throw new CommandLineException.BadArgumentValue(FILTER_NOT_IN_MASK_LONG_NAME, "argument not allowed if mask argument is not provided"); } + if (maskDescription != null && mask == null) { + throw new CommandLineException.BadArgumentValue(MASK_DESCRIPTION_LONG_NAME, "argument not allowed if mask argument is not provided"); + } filterExps = VariantContextUtils.initializeMatchExps(filterNames, filterExpressions); genotypeFilterExps = VariantContextUtils.initializeMatchExps(genotypeFilterNames, genotypeFilterExpressions); howToTreatMissingValues = failMissingValues ? JexlMissingValueTreatment.TREAT_AS_MATCH : JexlMissingValueTreatment.TREAT_AS_MISMATCH; diff --git a/src/test/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltrationIntegrationTest.java b/src/test/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltrationIntegrationTest.java index c6e8851a096..d5466ef905d 100644 --- a/src/test/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltrationIntegrationTest.java +++ b/src/test/java/org/broadinstitute/hellbender/tools/walkers/filters/VariantFiltrationIntegrationTest.java @@ -1,12 +1,14 @@ package org.broadinstitute.hellbender.tools.walkers.filters; import htsjdk.variant.variantcontext.VariantContext; +import htsjdk.variant.vcf.VCFHeader; import org.broadinstitute.hellbender.CommandLineProgramTest; import org.broadinstitute.hellbender.cmdline.StandardArgumentDefinitions; import org.broadinstitute.hellbender.engine.FeatureDataSource; import org.broadinstitute.hellbender.exceptions.UserException; import org.broadinstitute.hellbender.testutils.ArgumentsBuilder; import org.broadinstitute.hellbender.testutils.IntegrationTestSpec; +import org.broadinstitute.hellbender.testutils.VariantContextTestUtils; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -91,6 +93,22 @@ public void testMaskReversed() throws IOException { spec.executeTest("testMaskReversed", this); } + @Test + public void testMaskDescription() throws IOException { + final File output = createTempFile("testHeader", ".vcf"); + + final ArgumentsBuilder args = new ArgumentsBuilder(); + args.add("V", getTestFile("vcfexample2.vcf")) + .add("mask-name", "foo") + .add("mask-description", "description") + .add("mask:BED", getToolTestDataDir() + "goodMask.bed"); + args.addOutput(output); + + runCommandLine(args); + VCFHeader header = VariantContextTestUtils.getVCFHeader(output.getAbsolutePath()); + header.getFilterLines().stream().filter(f -> f.getID().equals("foo")).forEach(f -> Assert.assertEquals(f.getDescription(), "description")); + } + @Test public void testIllegalFilterName() throws IOException { final IntegrationTestSpec spec = new IntegrationTestSpec(