From c85e6fe28e8efc298cdfe58882193f75b4e4e9e7 Mon Sep 17 00:00:00 2001 From: Nicolas Vannieuwkerke Date: Fri, 17 Jan 2025 11:14:54 +0100 Subject: [PATCH] add simple support for CSV and TSV --- .../validation/ValidationExtension.groovy | 8 +-- .../SamplesheetCreationException.groovy | 16 +++++ .../samplesheet/ListConverter.groovy | 63 ++++++++++++++++++- .../nextflow/validation/utils/Files.groovy | 16 +++-- 4 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 plugins/nf-schema/src/main/nextflow/validation/exceptions/SamplesheetCreationException.groovy diff --git a/plugins/nf-schema/src/main/nextflow/validation/ValidationExtension.groovy b/plugins/nf-schema/src/main/nextflow/validation/ValidationExtension.groovy index fa298a0..de94363 100644 --- a/plugins/nf-schema/src/main/nextflow/validation/ValidationExtension.groovy +++ b/plugins/nf-schema/src/main/nextflow/validation/ValidationExtension.groovy @@ -110,21 +110,21 @@ class ValidationExtension extends PluginExtensionPoint { @Function public void listToSamplesheet( - final List inputList, + final List inputList, final CharSequence samplesheet, final Object schema = null ) { def Path samplesheetFile = Nextflow.file(samplesheet) as Path - listToSamplesheet(inputList, samplesheetFile, schemaFile) + listToSamplesheet(inputList, samplesheetFile, schema) } @Function public void listToSamplesheet( - final List inputList, + final List inputList, final Path samplesheet, final Object schema = null ) { - def Path schemaFile = null + def Path schemaFile if(schema) { if(schema instanceof String) { schemaFile = Nextflow.file(getBasePath(session.baseDir.toString(), schema as String)) as Path diff --git a/plugins/nf-schema/src/main/nextflow/validation/exceptions/SamplesheetCreationException.groovy b/plugins/nf-schema/src/main/nextflow/validation/exceptions/SamplesheetCreationException.groovy new file mode 100644 index 0000000..966d217 --- /dev/null +++ b/plugins/nf-schema/src/main/nextflow/validation/exceptions/SamplesheetCreationException.groovy @@ -0,0 +1,16 @@ +package nextflow.validation.exceptions + +import groovy.transform.CompileStatic +import nextflow.exception.AbortOperationException +/** + * Exception thrown to notify issues with the samplesheet creation + * + * @author Nicolas Vannieuwkerke + */ +@CompileStatic +class SamplesheetCreationException extends AbortOperationException { + + SamplesheetCreationException(String message) { + super(message) + } +} diff --git a/plugins/nf-schema/src/main/nextflow/validation/samplesheet/ListConverter.groovy b/plugins/nf-schema/src/main/nextflow/validation/samplesheet/ListConverter.groovy index af96890..56172d0 100644 --- a/plugins/nf-schema/src/main/nextflow/validation/samplesheet/ListConverter.groovy +++ b/plugins/nf-schema/src/main/nextflow/validation/samplesheet/ListConverter.groovy @@ -4,6 +4,8 @@ import groovy.util.logging.Slf4j import java.nio.file.Path import nextflow.validation.config.ValidationConfig +import nextflow.validation.exceptions.SamplesheetCreationException +import static nextflow.validation.utils.Files.getFileType /** * @@ -20,10 +22,67 @@ class ListConverter { } public void validateAndConvertToSamplesheet( - List inputList, + List inputList, Path samplesheet, Path schema ) { - println("Hi") + def String fileType = getFileType(samplesheet) + if(["csv", "tsv"].contains(fileType) && isNested(inputList)) { + def String msg = "Cannot create a CSV or TSV samplesheet from a list of nested values." + throw new SamplesheetCreationException(msg) + } + + switch(fileType) { + case "csv": + createSeparatedValueFile(inputList, samplesheet, ",") + break + case "tsv": + createSeparatedValueFile(inputList, samplesheet, "\t") + break + } + } + + private Boolean isNested(List input) { + def Boolean nested = false + input.each { entry -> + entry.each { key, element -> + if(element instanceof List || element instanceof Map) { + nested = true + return + } + } + if(nested) return + } + return nested + } + + private List determineHeader(List inputList) { + def List headers = [] + inputList.each { entry -> + entry.each { key, element -> + if(!headers.contains(key)) { + headers.add(key) + } + } + } + return headers + } + + private void createSeparatedValueFile(List inputList, Path samplesheet, String delimiter) { + def List headers = determineHeader(inputList) + def List content = [headers.join(delimiter)] + inputList.each { entry -> + def List entryContent = [] + headers.each { header -> + def Object item = entry.get(header) + if(item == null) { + entryContent.add("") + } else { + entryContent.add(item.toString()) + } + } + content.add(entryContent.join(delimiter)) + } + samplesheet.text = content.join("\n") } } diff --git a/plugins/nf-schema/src/main/nextflow/validation/utils/Files.groovy b/plugins/nf-schema/src/main/nextflow/validation/utils/Files.groovy index c53e624..945ac93 100644 --- a/plugins/nf-schema/src/main/nextflow/validation/utils/Files.groovy +++ b/plugins/nf-schema/src/main/nextflow/validation/utils/Files.groovy @@ -9,6 +9,7 @@ import groovy.json.JsonGenerator import groovy.json.JsonSlurper import groovy.util.logging.Slf4j import java.nio.file.Path +import java.nio.file.NoSuchFileException import nextflow.validation.exceptions.SchemaValidationException import static nextflow.validation.utils.Common.getValueFromJsonPointer @@ -34,13 +35,18 @@ public class Files { return extension == "yml" ? "yaml" : extension } - def String header = getFileHeader(file) - - def Integer commaCount = header.count(",") - def Integer tabCount = header.count("\t") + def Integer commaCount = 0 + def Integer tabCount = 0 + try { + def String header = getFileHeader(file) + commaCount = header.count(",") + tabCount = header.count("\t") + } catch (NoSuchFileException e) { + log.debug("${file.toString()} does not exist, cannot infer file type from file content.") + } if ( commaCount == tabCount ){ - log.error("Could not derive file type from ${file}. Please specify the file extension (CSV, TSV, YML, YAML and JSON are supported).".toString()) + throw new SchemaValidationException("Could not derive file type from ${file}. Please specify the file extension (CSV, TSV, YML, YAML and JSON are supported).".toString()) } if ( commaCount > tabCount ){ return "csv"