Skip to content

Commit

Permalink
Merge pull request #87 from molgenis/chore/refactor_metadata
Browse files Browse the repository at this point in the history
CLI: add option -m / --metadata (rename -m / --mode to -t  /--type)
  • Loading branch information
bartcharbon authored Jan 26, 2024
2 parents 98f1fb7 + ce665cd commit 201ca09
Show file tree
Hide file tree
Showing 16 changed files with 651 additions and 105 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ contain the following:
## Usage

```
usage: java -jar vcf-decision-tree.jar -i <arg> -c <arg> [-o <arg>] [-f]
usage: java -jar vcf-decision-tree.jar -i <arg> -c <arg> [-m <arg>] [-o <arg>] [-f]
[-s] [-l] [-p] [-d] [-pb <arg>] [-pd <arg>] [-ph <arg>] [-m <arg>]
-i,--input <arg> VEP* annotated input VCF file.
-m,--metadata <arg> VCF metadata file (.json).
-c,--config <arg> Input decision tree file (.json).
-o,--output <arg> Output VCF file (.vcf or .vcf.gz).
-f,--force Override the output file if it already exists.
Expand Down Expand Up @@ -85,9 +86,9 @@ usage: java -jar vcf-decision-tree.jar -v

## Examples
```
java -jar vcf-decision-tree.jar -i my.vcf -c decision_tree.json -o out.vcf
java -jar vcf-decision-tree.jar -i my.vcf.gz -c decision_tree.json -o out.vcf.gz
java -jar vcf-decision-tree.jar -i my.vcf.gz -c decision_tree.json -o out.vcf.gz -f -l -p
java -jar vcf-decision-tree.jar -i my.vcf -m field_metadata.json -c decision_tree.json -o out.vcf
java -jar vcf-decision-tree.jar -i my.vcf.gz -m field_metadata.json -c decision_tree.json -o out.vcf.gz
java -jar vcf-decision-tree.jar -i my.vcf.gz -m field_metadata.json -c decision_tree.json -o out.vcf.gz -f -l -p
java -jar vcf-decision-tree.jar -v
```

Expand Down
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<version>3.2.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>org.molgenis</groupId>
<artifactId>vip-decision-tree</artifactId>
<version>3.9.0</version>
<version>4.0.0</version>

<name>vip-decision-tree</name>
<description>Decision tree module for filtering and labelling VCF files</description>
Expand Down Expand Up @@ -41,9 +41,9 @@
<properties>
<java.version>17</java.version>
<commons.cli.version>1.6.0</commons.cli.version>
<samtools.htsjdk.version>4.0.2</samtools.htsjdk.version>
<samtools.htsjdk.version>4.1.0</samtools.htsjdk.version>
<jacoco-maven-plugin.version>0.8.11</jacoco-maven-plugin.version>
<vip.utils.version>1.4.5</vip.utils.version>
<vip.utils.version>2.0.0</vip.utils.version>
</properties>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class AppCommandLineOptions {

static final String OPT_INPUT = "i";
static final String OPT_INPUT_LONG = "input";
static final String OPT_METADATA = "m";
static final String OPT_METADATA_LONG = "metadata";
static final String OPT_CONFIG = "c";
static final String OPT_CONFIG_LONG = "config";
static final String OPT_OUTPUT = "o";
Expand All @@ -37,8 +39,8 @@ class AppCommandLineOptions {
static final String OPT_PED_LONG = "pedigree";
static final String OPT_PHENOTYPES = "ph";
static final String OPT_PHENOTYPES_LONG = "phenotypes";
static final String OPT_MODE = "m";
static final String OPT_MODE_LONG = "mode";
static final String OPT_TYPE = "t";
static final String OPT_TYPE_LONG = "type";
private static final Options APP_OPTIONS;
private static final Options APP_VERSION_OPTIONS;

Expand All @@ -58,6 +60,13 @@ class AppCommandLineOptions {
.longOpt(OPT_CONFIG_LONG)
.desc("Input decision tree file (.json).")
.build());
appOptions.addOption(
Option.builder(OPT_METADATA)
.hasArg(true)
.required()
.longOpt(OPT_METADATA_LONG)
.desc("VCF metadata file (.json).")
.build());
appOptions.addOption(
Option.builder(OPT_OUTPUT)
.hasArg(true)
Expand Down Expand Up @@ -110,11 +119,11 @@ class AppCommandLineOptions {
"Comma-separated list of sample-phenotypes (e.g. HP:123 or HP:123;HP:234 or sample0/HP:123,sample1/HP:234). Phenotypes are CURIE formatted (prefix:reference) and separated by a semicolon.")
.build());
appOptions.addOption(
Option.builder(OPT_MODE)
Option.builder(OPT_TYPE)
.hasArg(true)
.longOpt(OPT_MODE_LONG)
.longOpt(OPT_TYPE_LONG)
.desc(
"Run mode: 'variant' (default) or 'sample', 'sample' mode classifies provided probands, or all samples if no probands given.")
"Type: 'variant' (default) or 'sample', 'sample' classifies provided probands, or all samples if no probands given.")
.build());
APP_OPTIONS = appOptions;
Options appVersionOptions = new Options();
Expand All @@ -139,9 +148,10 @@ static Options getAppVersionOptions() {

static void validateCommandLine(CommandLine commandLine) {
validateInput(commandLine);
validateMetadata(commandLine);
validateConfig(commandLine);
validateOutput(commandLine);
validateMode(commandLine);
validateType(commandLine);
}

private static void validateInput(CommandLine commandLine) {
Expand All @@ -165,19 +175,39 @@ private static void validateInput(CommandLine commandLine) {
}
}

private static void validateMetadata(CommandLine commandLine) {
Path metadataPath = Path.of(commandLine.getOptionValue(OPT_METADATA));
if (!Files.exists(metadataPath)) {
throw new IllegalArgumentException(
format("Metadata file '%s' does not exist.", metadataPath));
}
if (Files.isDirectory(metadataPath)) {
throw new IllegalArgumentException(
format("Metadata file '%s' is a directory.", metadataPath));
}
if (!Files.isReadable(metadataPath)) {
throw new IllegalArgumentException(
format("Metadata file '%s' is not readable.", metadataPath));
}
String inputPathStr = metadataPath.toString();
if (!inputPathStr.endsWith(".json")) {
throw new IllegalArgumentException(
format("Metadata file '%s' is not a .json file.", inputPathStr));
}
}
private static void validateConfig(CommandLine commandLine) {
Path configPath = Path.of(commandLine.getOptionValue(OPT_CONFIG));
if (!Files.exists(configPath)) {
throw new IllegalArgumentException(
format("Config file '%s' does not exist.", configPath.toString()));
format("Config file '%s' does not exist.", configPath));
}
if (Files.isDirectory(configPath)) {
throw new IllegalArgumentException(
format("Config file '%s' is a directory.", configPath.toString()));
format("Config file '%s' is a directory.", configPath));
}
if (!Files.isReadable(configPath)) {
throw new IllegalArgumentException(
format("Config file '%s' is not readable.", configPath.toString()));
format("Config file '%s' is not readable.", configPath));
}
String inputPathStr = configPath.toString();
if (!inputPathStr.endsWith(".json")) {
Expand All @@ -199,18 +229,18 @@ private static void validateOutput(CommandLine commandLine) {
}
}

private static void validateMode(CommandLine commandLine) {
if (!commandLine.hasOption(OPT_MODE)) {
private static void validateType(CommandLine commandLine) {
if (!commandLine.hasOption(OPT_TYPE)) {
return;
}

String mode = commandLine.getOptionValue(OPT_MODE);
String mode = commandLine.getOptionValue(OPT_TYPE);
List<String> modes = Arrays.stream(Mode.values()).map(Mode::toString)
.toList();

if (!modes.contains(mode.toUpperCase())) {
throw new IllegalArgumentException(
"Illegal 'mode' argument '%s', only 'variant' and 'sample' are allowed.".formatted(mode));
"Illegal 'type' argument '%s', only 'variant' and 'sample' are allowed.".formatted(mode));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
package org.molgenis.vcf.decisiontree;

import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_CONFIG;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_FORCE;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_INPUT;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_LABELS;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_MODE;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_OUTPUT;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_PATH;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_PED;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_PHENOTYPES;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_PROBANDS;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.OPT_STRICT;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.molgenis.vcf.decisiontree.filter.model.Mode;
import org.molgenis.vcf.decisiontree.loader.ConfigDecisionTreeLoader;
import org.molgenis.vcf.decisiontree.loader.model.ConfigDecisionTree;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
import static org.molgenis.vcf.decisiontree.AppCommandLineOptions.*;

@Component
class AppCommandLineToSettingsMapper {

Expand All @@ -45,15 +36,17 @@ class AppCommandLineToSettingsMapper {
Settings map(CommandLine commandLine, String... args) {
AppSettings appSettings = createAppSettings(args);
Path inputPath = Path.of(commandLine.getOptionValue(OPT_INPUT));
Path metadataPath = Path.of(commandLine.getOptionValue(OPT_METADATA));
ConfigDecisionTree configDecisionTree = createDecisionTree(commandLine);
WriterSettings writerSettings = createWriterSettings(commandLine);
boolean strict = commandLine.hasOption(OPT_STRICT);
Mode mode = getMode(commandLine);
Mode mode = getType(commandLine);
SampleSettings sampleSettings = createSampleSettings(commandLine);

return Settings.builder()
.mode(mode)
.inputVcfPath(inputPath)
.metadataPath(metadataPath)
.configDecisionTree(configDecisionTree)
.appSettings(appSettings)
.writerSettings(writerSettings)
Expand All @@ -62,10 +55,10 @@ Settings map(CommandLine commandLine, String... args) {
.build();
}

private Mode getMode(CommandLine commandLine) {
private Mode getType(CommandLine commandLine) {
Mode mode;
if (commandLine.hasOption(OPT_MODE)) {
mode = Mode.valueOf(commandLine.getOptionValue(OPT_MODE).toUpperCase());
if (commandLine.hasOption(OPT_TYPE)) {
mode = Mode.valueOf(commandLine.getOptionValue(OPT_TYPE).toUpperCase());
} else {
mode = Mode.VARIANT;
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/molgenis/vcf/decisiontree/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class Settings {

Mode mode;
Path inputVcfPath;
Path metadataPath;
ConfigDecisionTree configDecisionTree;
AppSettings appSettings;
WriterSettings writerSettings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@
import org.molgenis.vcf.decisiontree.filter.VcfReader;
import org.molgenis.vcf.decisiontree.runner.info.GenotypeMetadataMapper;
import org.molgenis.vcf.decisiontree.runner.info.VepMetadataParser;
import org.molgenis.vcf.decisiontree.runner.info.VepMetadataParserFactory;
import org.springframework.stereotype.Component;

@Component
class VcfReaderFactoryImpl implements VcfReaderFactory {

private final VepMetadataParserFactory vepMetadataParserFactory;
private final GenotypeMetadataMapper genotypeMetadataMapper;
private VepMetadataParser vepMetadataParser;

VcfReaderFactoryImpl(VepMetadataParser vepMetadataParser,
GenotypeMetadataMapper genotypeMetadataMapper) {
this.vepMetadataParser = requireNonNull(vepMetadataParser);
VcfReaderFactoryImpl(VepMetadataParserFactory vepMetadataParserFactory,
GenotypeMetadataMapper genotypeMetadataMapper) {
this.vepMetadataParserFactory = requireNonNull(vepMetadataParserFactory);
this.genotypeMetadataMapper = requireNonNull(genotypeMetadataMapper);
}

@Override
public VcfReader create(Settings settings) {
VepMetadataParser vepMetadataParser = vepMetadataParserFactory.create(settings);

Path inputVcfPath = settings.getInputVcfPath();
boolean strict = settings.isStrict();
return new VcfReader(new VCFFileReader(inputVcfPath.toFile(), false), vepMetadataParser,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.molgenis.vcf.decisiontree.runner;

import static java.util.Collections.singletonList;
import static org.molgenis.vcf.decisiontree.runner.info.VepInfoMetadataMapper.ALLELE_NUM;
import static org.molgenis.vcf.decisiontree.runner.info.VepMetadataMapperImpl.ALLELE_NUM;

import htsjdk.variant.variantcontext.VariantContext;
import htsjdk.variant.variantcontext.VariantContextBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.molgenis.vcf.decisiontree.runner.info;

import org.molgenis.vcf.decisiontree.Settings;

public interface VepMetadataMapperFactory {
VepMetadataMapper create(Settings settings);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.molgenis.vcf.decisiontree.runner.info;

import htsjdk.variant.vcf.VCFInfoHeaderLine;
import org.molgenis.vcf.decisiontree.Settings;
import org.molgenis.vcf.utils.metadata.FieldMetadataService;
import org.molgenis.vcf.utils.metadata.AbstractFieldMetadataService;
import org.molgenis.vcf.utils.model.FieldMetadata;
import org.molgenis.vcf.utils.vep.VepMetadataService;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static java.util.Objects.requireNonNull;

@Component
public class VepMetadataMapperFactoryImpl implements VepMetadataMapperFactory {
@Override
public VepMetadataMapper create(Settings settings) {
Path metadataPath = settings.getMetadataPath();
FieldMetadataService fieldMetadataService = new CustomFieldMetadataService(metadataPath);
VepMetadataService vepMetadataService = new VepMetadataService(fieldMetadataService);
return new VepMetadataMapperImpl(vepMetadataService);
}

private static class CustomFieldMetadataService extends AbstractFieldMetadataService {
private final Path metadataPath;

public CustomFieldMetadataService(Path metadataPath) {
this.metadataPath = requireNonNull(metadataPath);
}

@Override
public FieldMetadata load(VCFInfoHeaderLine vcfInfoHeaderLine) {
try(InputStream inputStream = Files.newInputStream(metadataPath)) {
return this.load(inputStream, vcfInfoHeaderLine);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}
Loading

0 comments on commit 201ca09

Please sign in to comment.