def test_1(self): import janis_core as j class FileWithSec(j.File): def __init__(self, optional=False): super().__init__(optional=optional, extension=".txt") def secondary_files(self): return [".sec"] Tool = j.CommandToolBuilder( tool="ls", base_command=["ls"], inputs=[ j.ToolInput("inp", FileWithSec, secondaries_present_as={".sec": "^.sec"}) ], outputs=[ j.ToolOutput("std", j.Stdout), j.ToolOutput( "out", FileWithSec, secondaries_present_as={".sec": "^.sec"}, glob=j.InputSelector("inp"), ), ], container="ubuntu:latest", version="v0.1.0", ) Tool.translate("wdl")
def parse_command_tool_input(self, inp: WDL.Decl): default = None if inp.expr: default = self.translate_expr(inp.expr) # explicitly skip "runtime_*" inputs because they're from janis if inp.name.startswith("runtime_"): return None return j.ToolInput(inp.name, self.parse_wdl_type(inp.type), default=default)
def get_inputs(self, names: List[NamedArgument]) -> List[janis.ToolInput]: ret = [] for arg in names: assert arg.name != "", arg ret.append( janis.ToolInput( tag="in_" + arg.name, input_type=self.arg_to_janis_type(arg.arg), position=arg.arg.position if isinstance( arg.arg, Positional) else None, prefix=arg.arg.longest_synonym if isinstance( arg.arg, Flag) else None, doc=arg.arg.description, )) return ret
def ingest_command_tool_input(self, inp): inpBinding = inp.inputBinding if inpBinding and inpBinding.valueFrom: j.Logger.warn( f"Won't translate the expression for input {inp.id}: {inpBinding.valueFrom}" ) inp_type = self.ingest_cwl_type(inp.type, secondary_files=inp.secondaryFiles) return j.ToolInput( tag=self.get_tag_from_identifier(inp.id), input_type=inp_type, position=inpBinding.position if inpBinding else None, prefix=inpBinding.prefix if inpBinding else None, separate_value_from_prefix=inpBinding.separate if inpBinding else None, separator=inpBinding.itemSeparator if inpBinding else None, shell_quote=inpBinding.shellQuote if inpBinding else None, default=inp.default, )
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ How to create an empty directory, using the value of an input """ import janis_core as j CLT = j.CommandToolBuilder( tool="create_initial_stuff", base_command=["ls", "*"], version="dev", container="ubuntu:latest", inputs=[ j.ToolInput("name_of_output_folder", j.String(optional=True), default="some-string") ], outputs=[j.ToolOutput("out_dir", j.Directory, selector="some-string")], directories_to_create=[j.InputSelector("name_of_output_folder")], files_to_create=[( j.StringFormatter("{dir}/file.txt", dir=j.InputSelector("name_of_output_folder")), "contents of file", )], ) if __name__ == "__main__": CLT().translate("cwl")
class MyDataTypeWithSecondaries(j.File): def __init__(self, optional=False): super().__init__(optional=optional) @staticmethod def secondary_files(): return [".csv"] ToolWithSecondaryFiles = j.CommandToolBuilder( tool="secondary_files", version="v0.1.0", container="ubuntu:latest", base_command="echo", inputs=[j.ToolInput("inp", MyDataTypeWithSecondaries, position=0)], outputs=[ j.ToolOutput( "out", MyDataTypeWithSecondaries, selector="file.txt", ) ], ) WorkflowWithSecondaries = j.WorkflowBuilder("workflow_with_secondaries") inner_dt = MyDataTypeWithSecondaries scatter = None # helper method to show how scatters / not scatters work
def ingest_expression_tool_input(self, inp): inp_type = self.ingest_cwl_type(inp.type, secondary_files=inp.secondaryFiles) return j.ToolInput(tag=self.get_tag_from_identifier(inp.id), input_type=inp_type)
def inputs(self) -> List[j.ToolInput]: return [ j.ToolInput("truthVCF", Vcf(), position=1), j.ToolInput("compareVCF", Vcf(), position=2), j.ToolInput( "reportPrefix", j.Filename(), prefix="--report-prefix", doc="(-o) Filename prefix for report output.", ), j.ToolInput( "reference", FastaWithDict(), prefix="--reference", doc="(-r) Specify a reference file.", ), j.ToolInput( "intervals", Bed(optional=True), prefix="--target-regions", doc= "(-T) Restrict analysis to given (dense) regions (using -T in bcftools).", ), j.ToolInput( "version", j.Boolean(optional=True), prefix="--version", doc="(-v) Show version number and exit.", ), j.ToolInput( "scratchPrefix", j.String(optional=True), prefix="--scratch-prefix", doc="Directory for scratch files.", ), j.ToolInput( "keepScratch", j.String(optional=True), prefix="--keep-scratch", doc= "Filename prefix for scratch report output. Annotation format in input VCF file.", ), j.ToolInput( "falsePositives", Bed(optional=True), prefix="--false-positives", doc= "(-f) False positive / confident call regions (.bed or .bed.gz). " "Calls outside these regions will be labelled as UNK.", ), j.ToolInput( "stratification", Tsv(optional=True), prefix="--stratification", doc= " Stratification file list (TSV format -- first column is region name, " "second column is file name).", ), j.ToolInput( "stratificationRegion", j.String(optional=True), prefix="--stratification-region", doc= "Add single stratification region, e.g. --stratification-region TEST:test.bed", ), j.ToolInput( "stratificationFixchr", j.String(optional=True), prefix="--stratification-fixchr", doc=" Add chr prefix to stratification files if necessary", ), j.ToolInput( "writeVcf", j.Boolean(optional=True), prefix="--write-vcf", doc="(-V) Write an annotated VCF.", ), j.ToolInput( "writeCounts", j.Boolean(optional=True), prefix="--write-counts", doc="(-X) Write advanced counts and metrics.", ), j.ToolInput( "noWriteCounts", j.Boolean(optional=True), prefix="--no-write-counts", doc="Do not write advanced counts and metrics.", ), j.ToolInput( "outputVtc", j.Boolean(optional=True), prefix="--output-vtc", doc= "Write VTC field in the final VCF which gives the counts each position has contributed to.", ), j.ToolInput( "preserveInfo", j.Boolean(optional=True), prefix="--preserve-info", doc= "When using XCMP, preserve and merge the INFO fields in truth and query. " "Useful for ROC computation.", ), j.ToolInput( "roc", j.String(optional=True), prefix="--roc", doc= "Select a feature to produce a ROC on (INFO feature, QUAL, GQX, ...).", ), j.ToolInput( "noRoc", j.Boolean(optional=True), prefix="--no-roc", doc= "Disable ROC computation and only output summary statistics for more concise output.", ), j.ToolInput( "rocRegions", j.String(optional=True), prefix="--roc-regions", doc=" Select a list of regions to compute ROCs in. By default, " "only the '*' region will produce ROC output (aggregate variant counts).", ), j.ToolInput( "rocFilter", j.String(optional=True), prefix="--roc-filter", doc=" Select a filter to ignore when making ROCs.", ), j.ToolInput( "rocDelta", j.Int(optional=True), prefix="--roc-delta", doc=" Minimum spacing between ROC QQ levels.", ), j.ToolInput( "ciAlpha", j.Int(optional=True), prefix="--ci-alpha", doc= "Confidence level for Jeffrey's CI for recall, precision and fraction of non-assessed calls.", ), j.ToolInput( "noJson", j.Boolean(optional=True), prefix="--no-json", doc="Disable JSON file output.", ), # j.ToolInput("location", Array(j.String(), optional=True), prefix="--location", separator=",", # doc="(-l) Comma-separated list of locations [use naming after preprocessing], " # "when not specified will use whole VCF."), j.ToolInput( "passOnly", j.Boolean(optional=True), prefix="--pass-only", doc="Keep only PASS variants.", ), # j.ToolInput("filtersOnly", Array(j.String(), optional=True), prefix="--filters-only", separator=",", # doc=" Specify a comma-separated list of filters to apply " # "(by default all filters are ignored / passed on."), j.ToolInput( "restrictRegions", j.Boolean(optional=True), prefix="--restrict-regions", doc= "(-R) Restrict analysis to given (sparse) regions (using -R in bcftools).", ), j.ToolInput( "leftshift", j.Boolean(optional=True), prefix="--leftshift", doc="(-L) Left-shift variants safely.", ), j.ToolInput( "noLeftshift", j.Boolean(optional=True), prefix="--no-leftshift", doc="Do not left-shift variants safely.", ), j.ToolInput( "decompose", j.Boolean(optional=True), prefix="--decompose", doc= "Decompose variants into primitives. This results in more granular counts.", ), j.ToolInput( "noDecompose", j.Boolean(optional=True), prefix="--no-decompose", doc="(-D) Do not decompose variants into primitives.", ), j.ToolInput( "bcftoolsNorm", j.Boolean(optional=True), prefix="--bcftools-norm", doc="Enable preprocessing through bcftools norm -c x -D " "(requires external preprocessing to be switched on).", ), j.ToolInput( "fixchr", j.Boolean(optional=True), prefix="--fixchr", doc= "Add chr prefix to VCF records where necessary (default: auto, attempt to match reference).", ), j.ToolInput( "noFixchr", j.Boolean(optional=True), prefix="--no-fixchr", doc= "Do not add chr prefix to VCF records (default: auto, attempt to match reference).", ), j.ToolInput( "bcf", j.Boolean(optional=True), prefix="--bcf", doc= "Use BCF internally. This is the default when the input file is in BCF format already. " "Using BCF can speed up temp file access, but may fail for VCF files that have broken " "headers or records that don't comply with the header.", ), j.ToolInput( "somatic", j.Boolean(optional=True), prefix="--somatic", doc= "Assume the input file is a somatic call file and squash all columns into one, " "putting all FORMATs into INFO + use half genotypes (see also --set-gt). " "This will replace all sample columns and replace them with a single one. " "This is used to treat Strelka somatic files Possible values for this parameter: " "half / hemi / het / hom / half to assign one of the following genotypes to the " "resulting sample: 1 | 0/1 | 1/1 | ./1. This will replace all sample columns and " "replace them with a single one.", ), j.ToolInput( "setGT", j.Boolean(optional=True), prefix="--set-gt", doc= "This is used to treat Strelka somatic files Possible values for this parameter: " "half / hemi / het / hom / half to assign one of the following genotypes to the resulting " "sample: 1 | 0/1 | 1/1 | ./1. " "This will replace all sample columns and replace them with a single one.", ), j.ToolInput( "gender", j.String(optional=True), prefix="--gender", doc= "({male,female,auto,none}) Specify gender. This determines how haploid calls on chrX " "get treated: for male samples, all non-ref calls (in the truthset only when " "running through hap.py) are given a 1/1 genotype.", ), j.ToolInput( "preprocessTruth", j.Boolean(optional=True), prefix="--preprocess-truth", doc="Preprocess truth file with same settings as query " "(default is to accept truth in original format).", ), j.ToolInput( "usefilteredTruth", j.Boolean(optional=True), prefix="--usefiltered-truth", doc="Use filtered variant calls in truth file " "(by default, only PASS calls in the truth file are used)", ), j.ToolInput( "preprocessingWindowSize", j.Boolean(optional=True), prefix="--preprocessing-window-size", doc=" Preprocessing window size (variants further apart than " "that size are not expected to interfere).", ), j.ToolInput( "adjustConfRegions", j.Boolean(optional=True), prefix="--adjust-conf-regions", doc= " Adjust confident regions to include variant locations. Note this will only include " "variants that are included in the CONF regions already when viewing with bcftools; " "this option only makes sure insertions are padded correctly in the CONF regions (to " "capture these, both the base before and after must be contained in the bed file).", ), j.ToolInput( "noAdjustConfRegions", j.Boolean(optional=True), prefix="--no-adjust-conf-regions", doc=" Do not adjust confident regions for insertions.", ), j.ToolInput( "noHaplotypeComparison", j.Boolean(optional=True), prefix="--no-haplotype-comparison", doc= "(--unhappy) Disable haplotype comparison (only count direct GT matches as TP).", ), j.ToolInput( "windowSize", j.Int(optional=True), prefix="--window-size", doc= "(-w) Minimum distance between variants such that they fall into the same superlocus.", ), j.ToolInput( "xcmpEnumerationThreshold", j.Int(optional=True), prefix="--xcmp-enumeration-threshold", doc= " Enumeration threshold / maximum number of sequences to enumerate per block.", ), j.ToolInput( "xcmpExpandHapblocks", j.String(optional=True), prefix="--xcmp-expand-hapblocks", doc= " Expand haplotype blocks by this many basepairs left and right.", ), j.ToolInput( "threads", j.Int(optional=True), prefix="--threads", default=j.CpuSelector(), doc="Number of threads to use. Comparison engine to use.", ), # j.ToolInput("engineVcfevalPath", j.String(optional=True), prefix="--engine-vcfeval-path", # doc=" This parameter should give the path to the \"rtg\" executable. " # "The default is /opt/hap.py/lib/python27/Haplo/../../../libexec/rtg- tools-install/rtg"), j.ToolInput( "engine", j.String(optional=True), prefix="--engine", doc= " {xcmp,vcfeval,scmp-somatic,scmp-distance} Comparison engine to use.", ), j.ToolInput( "engineVcfevalTemplate", j.String(optional=True), prefix="--engine-vcfeval-template", doc= " Vcfeval needs the reference sequence formatted in its own file format (SDF -- run rtg " "format -o ref.SDF ref.fa). You can specify this here to save time when running hap.py " "with vcfeval. If no SDF folder is specified, hap.py will create a temporary one.", ), j.ToolInput( "scmpDistance", j.Int(optional=True), prefix="--scmp-distance", doc= " For distance-based matching, this is the distance between variants to use.", ), j.ToolInput( "logfile", j.Filename(suffix="-log", extension=".txt"), prefix="--logfile", doc="Write logging information into file rather than to stderr", ), j.ToolInput( "verbose", j.Boolean(optional=True), prefix="--verbose", doc="Raise logging level from warning to info.", ), j.ToolInput( "quiet", j.Boolean(optional=True), prefix="--quiet", doc="Set logging level to output errors only.", ), ]
# (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import janis_core as j HelloWorldTool = j.CommandToolBuilder( tool="HelloWorldTool", container=None, version="development", base_command="echo", inputs=[j.ToolInput("inp", j.String, position=0, default="Hello, World!")], outputs=[ j.ToolOutput("out", j.String, selector=j.standard.ReadContents(j.Stdout())) ], ) # as there is no container, you need to provide "allow_empty_containers=True" to the translate method: HelloWorldTool.translate("wdl", allow_empty_container=True) # OR (bash): # $ janis translate --allow-empty-container hello wdl
However, in WDL, we have do this slightly differently: File? out = if length(glob("*.txt")) > 0 then glob("*.txt")[0] else None NB: Although WDL supports optional files, some backends of Cromwell do not. """ import janis_core as j ToolWithOptionalWildcardOutput = j.CommandToolBuilder( tool="optional_wildcard_output_tool", version="v0.1.0", container="ubuntu:latest", base_command=None, # write '1' to a file called 'out.csv' arguments=[j.ToolArgument("echo 1 > out.csv", shell_quote=False)], inputs=[j.ToolInput("inp", j.File, localise_file=True)], outputs=[ j.ToolOutput("out_csv_files", j.Array(j.File), selector=j.WildcardSelector("*.csv")), # the next two are functionally equivalent j.ToolOutput("out_single_csv_file_1", j.Array(j.File), selector=j.WildcardSelector("*.csv", select_first=True)), j.ToolOutput("out_single_csv_file_2", j.Array(j.File), selector=j.WildcardSelector("*.csv")[0]), # OPTIONAL glob outputs # capture all files with *.txt pattern (but select the first if possible) j.ToolOutput("out_optional_glob",
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import janis_core as j CommandLineThatBindsArrays = j.CommandToolBuilder( tool="CommandLineThatBindsArrays", container="ubuntu:latest", version="development", base_command="echo", inputs=[ # NO prefix, joined by space (' ') [DEFAULT] # need a position to ensure it binds onto the command line j.ToolInput("array1", j.Array(j.String), position=0), # # No prefix, joined by commans (',') j.ToolInput("array2", j.Array(j.String), separator=",", position=1), # # Single prefix, then joined by space (' ') j.ToolInput("array3", j.Array(j.String), prefix="--prefix"), # # Single prefix, then joined by commas (',') j.ToolInput("array4", j.Array(j.String), prefix="--prefix", separator=","), # # Prefix applies to each element j.ToolInput(
import janis_core as j from janis_unix.tools import Cat # Declare a new subclass of j.File and write a new tool to accept it class SubFile(j.File): @staticmethod def name(): return "SubFile" ToolThatAcceptsSubFile = j.CommandToolBuilder( tool="acceptingSubFile", base_command="cat", inputs=[j.ToolInput("inp", SubFile)], outputs=[j.ToolOutput("out", j.Stdout(SubFile))], version="test", container="ubuntu:latest", ) # Declare workflow we're we'll use the type alias w = j.WorkflowBuilder("aliasing") w.input("inp", SubFile) w.step("stp1", Cat(file=w.inp)) # This line would cause the following critical message to be logged: # [CRITICAL]: Mismatch of types when joining 'stp1.out' to 'stp2.inp': stdout<File> -/→ SubFile # w.step("stp2", ToolThatAcceptsSubFile(inp=w.stp1.out))
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ NB: Although WDL supports optional files, some backends of Cromwell do not. """ import janis_core as j ToolWithOptionalOutput = j.CommandToolBuilder( tool="optional_output_tool", version="v0.1.0", container="ubuntu:latest", base_command=[], arguments=[j.ToolArgument("echo 1 > ", shell_quote=False)], inputs=[ j.ToolInput("outputFilename", j.String(optional=True), default="out.csv", position=1) ], outputs=[ j.ToolOutput("out", j.File(optional=True), selector=j.InputSelector("outputFilename")) ], ) if __name__ == "__main__": ToolWithOptionalOutput().translate("wdl")
# # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ We'll use Janis to _try_ and collect a SINGLE file with the extension '.txt', but one won't exist. CWL will automatically coerce the empty File[0] to File? (null). However, in WDL, we have do this slightly differently: File? out = if length(glob("*.txt")) > 0 then glob("*.txt")[0] else None NB: Although WDL supports optional files, some backends of Cromwell do not. """ import janis_core as j ToolWithDynamicGlob = j.CommandToolBuilder( tool="tool_with_dynamic_glob", version="v0.1.0", container="ubuntu:latest", base_command=None, # write '1' to a file called 'out.csv' arguments=[j.ToolArgument("echo 1 > out.csv", shell_quote=False)], inputs=[j.ToolInput("extension", str)], outputs=[ j.ToolOutput("out_csv_files", j.Array(j.File), selector=j.WildcardSelector("*" + j.InputSelector("extension"))), ], ) if __name__ == "__main__": ToolWithDynamicGlob().translate("cwl")