Skip to content

Commit

Permalink
VariantFiltration: added arg to write custom mask filter description …
Browse files Browse the repository at this point in the history
…in VCF header (#8831)

Added a --mask-description argument to VariantFiltration to write a custom description for the mask filter in the VCF header
  • Loading branch information
meganshand authored and mcovarr committed Jul 15, 2024
1 parent f2a85b8 commit 78b8231
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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<JexlVCMatchExp> filterExps;
private List<JexlVCMatchExp> genotypeFilterExps;
Expand Down Expand Up @@ -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"));
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit 78b8231

Please sign in to comment.