Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/nf-core/custom/geneticmapconvert/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dependencies:
- conda-forge::r-data.table=1.17.8
- conda-forge::r-janitor=2.2.1
- conda-forge::r-r.utils=2.13.0
- conda-forge::r-nfcore.utils=0.0.2
11 changes: 3 additions & 8 deletions modules/nf-core/custom/geneticmapconvert/main.nf
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ process CUSTOM_GENETICMAPCONVERT {

conda "${moduleDir}/environment.yml"
container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ?
'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/06/062aabd31ebac6f139125e485d5566e928c1b79caf488daa596df02bd1ccbf23/data':
'community.wave.seqera.io/library/r-data.table_r-janitor_r-r.utils:c8ebef5bb002374e' }"
'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/af/afe3fd20fa8b3096a9fc6b9b2725d92516a036651545a5f9ec1e6a53b5a365d9/data':
'community.wave.seqera.io/library/r-data.table_r-janitor_r-nfcore.utils_r-r.utils:e881939d5868e7db' }"

input:
tuple val(meta), path(map_file)
Expand Down Expand Up @@ -39,11 +39,6 @@ process CUSTOM_GENETICMAPCONVERT {
touch ${prefix}.minimac.map
touch ${prefix}.eagle.map

cat <<-END_VERSIONS > versions.yml
"${task.process}":
r-base: \$(R --version | sed '1!d; s/.*version //; s/ .*//')
r-data.table: \$(Rscript -e "cat(as.character(packageVersion('data.table')))")
r-janitor: \$(Rscript -e "cat(as.character(packageVersion('janitor')))")
END_VERSIONS
Rscript -e "nfcore.utils::process_end(packages = list('r-data.table' = 'data.table', 'r-janitor' = 'janitor'), task_name = '${task.process}')"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the export file name "versions.yml" should also be added as an argument, to make it clearer what happens.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could do that !
I will need to update the package.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and instead of process_end it could be called export_versions or so

@LouisLeNezet LouisLeNezet Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process_end() also create the R_session.log

"""
}
129 changes: 25 additions & 104 deletions modules/nf-core/custom/geneticmapconvert/templates/geneticmapconvert.R
Original file line number Diff line number Diff line change
@@ -1,69 +1,22 @@
#!/usr/bin/env Rscript

# Needed to avoid the error from lubridate in janitor
# Error: (converted from warning)
# Your system is mis-configured: ‘/etc/localtime’ is not a symlink
Sys.setenv(TZ = "UTC")

# Load necessary libraries
library(data.table)
library(stringr)
library(janitor)
library(nfcore.utils)

################################################
################################################
## Functions ##
################################################
################################################

#' Check for Non-Empty, Non-Whitespace String
#'
#' This function checks if the input is non-NULL and contains more than
#' just whitespace.
#' It returns TRUE if the input is a non-empty, non-whitespace string,
#' and FALSE otherwise.
#'
#' @param input A variable to check.
#' @return A logical value: TRUE if the input is a valid, non-empty,
#' non-whitespace string; FALSE otherwise.
#' @examples
#' is_valid_string("Hello World") # Returns TRUE
#' is_valid_string(" ") # Returns FALSE
#' is_valid_string(NULL) # Returns FALSE
is_valid_string <- function(input) {
!is.null(input) && nzchar(trimws(input))
}

#' Parse out options from a string without recourse to optparse
#'
#' @param x Long-form argument list like --opt1 val1 --opt2 val2
#'
#' @return named list of options and values similar to optparse
parse_args <- function(x) {
args_list <- unlist(strsplit(x, " ?--")[[1]])[-1]
args_vals <- lapply(
args_list,
function(x) scan(text = x, what = "character", quiet = TRUE)
)

# Ensure the option vectors are length 2 (key/ value) to catch empty ones
args_vals <- lapply(args_vals, function(z) {
length(z) <- 2
z
})

parsed_args <- structure(
lapply(args_vals, function(x) x[2]),
names = lapply(args_vals, function(x) x[1])
)
parsed_args[! is.na(parsed_args)]
}

#' Turn “null” or empty strings into actual NULL
#'
#' @param x Input option
#'
#' @return NULL or x
#'
nullify <- function(x) {
if (is.character(x) && (tolower(x) == "null" || x == "")) NULL else x
}

#' Parse tolerance value
#'
#' @param x Tolerance to check
Expand Down Expand Up @@ -269,65 +222,33 @@ process_map_file <- function(
################################################
################################################

# I've defined these in a single array like this so that we could go back to an
# optparse-driven method in future with module bin/ directories, rather than
# the template

# Set defaults and classes

opt <- list(
output_prefix = "${prefix}",
map_file = "${map_file}",
chr = "${meta.chr}",
tolerance = NULL
)

opt_types <- lapply(opt, class)

# Apply parameter overrides
args_opt <- parse_args("${args}")
for (ao in names(args_opt)) {
if (! ao %in% names(opt)) {
stop(paste("Invalid option:", ao))
} else {
# Preserve classes from defaults where possible
if (! is.null(opt[[ao]])) {
args_opt[[ao]] <- as(args_opt[[ao]], opt_types[[ao]])
}
opt[[ao]] <- args_opt[[ao]]
}
}

keys <- c("tolerance", "chr")
opt[keys] <- lapply(opt[keys], nullify)

for (file_input in c("map_file")) {
if (! is_valid_string(opt[[file_input]])) {
stop(paste("Please provide", file_input), call. = FALSE)
}

if (! file.exists(opt[[file_input]])) {
stop(paste0(
"Value of ", file_input, ": ",
opt[[file_input]], " is not a valid file"
))
}
}
opt_valid <- process_inputs(
opt,
args = '${args}',
keys_to_nullify = c("output_prefix", "chr", "tolerance"),
expected_files = c("map_file"),
expected_double = c("tolerance"),
required_opts = c("map_file", "output_prefix")
)

process_map_file(
file_path = opt[["map_file"]],
chr = opt[["chr"]],
prefix = opt[["output_prefix"]],
tolerance = parse_tolerance(opt[["tolerance"]])
file_path = opt_valid[["map_file"]],
chr = opt_valid[["chr"]],
prefix = opt_valid[["output_prefix"]],
tolerance = parse_tolerance(opt_valid[["tolerance"]])
)

version_rbase <- paste(R.version[["major"]], R.version[["minor"]], sep = ".")
version_datatable <- packageVersion("data.table")
version_janitor <- packageVersion("janitor")

writeLines(c(
'"${task.process}":',
paste(" r-base:", version_rbase),
paste(" r-data.table:", version_datatable),
paste(" r-janitor:", version_janitor)
), "versions.yml")
process_end(
packages = list(
"r-data.table" = "data.table",
"r-janitor" = "janitor"
),
task_name = "${task.process}"
)
63 changes: 35 additions & 28 deletions modules/nf-core/custom/geneticmapconvert/tests/main.nf.test.snap
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
Expand All @@ -70,10 +71,10 @@
"chr21\t12970435\t0.00133"
]
],
"timestamp": "2026-04-21T17:17:09.741694079",
"timestamp": "2026-06-09T09:57:35.284767264",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
},
"Convert map with chr\\tpos\\tcm - with header - meta.chr (minimac format)": {
Expand Down Expand Up @@ -116,12 +117,13 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
Expand All @@ -147,10 +149,10 @@
"chr21\t12970435\t0.00133"
]
],
"timestamp": "2026-04-21T17:17:40.527355799",
"timestamp": "2026-06-09T09:57:49.714082507",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
},
"Convert map with pos rate cm - with header - meta.chr (stitch format)": {
Expand Down Expand Up @@ -193,12 +195,13 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
Expand All @@ -224,10 +227,10 @@
"chr21\t9928594\t0.0418492"
]
],
"timestamp": "2026-04-21T16:56:42.982751661",
"timestamp": "2026-06-09T09:57:56.606846375",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
},
"Convert map with chr id cm pos - no header - meta.chr (plink format)": {
Expand Down Expand Up @@ -270,12 +273,13 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
Expand All @@ -301,10 +305,10 @@
"chr21\t12968320\t0"
]
],
"timestamp": "2026-04-21T17:18:47.986410489",
"timestamp": "2026-06-09T09:58:23.743979403",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
},
"Test with comma separator and keep id": {
Expand Down Expand Up @@ -343,12 +347,13 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
Expand All @@ -374,10 +379,10 @@
"chr21\t12970435\t0.00133"
]
],
"timestamp": "2026-04-21T17:19:48.646532306",
"timestamp": "2026-06-09T09:58:55.457049681",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
},
"Convert map with pos\\tchr\\tcM - with header - meta.chr (glimpse compressed format)": {
Expand Down Expand Up @@ -420,12 +425,13 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
Expand All @@ -451,10 +457,10 @@
"chr21\t12970435\t0.00133"
]
],
"timestamp": "2026-04-21T17:17:25.458379776",
"timestamp": "2026-06-09T09:57:43.003329474",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
},
"Test stub": {
Expand Down Expand Up @@ -497,21 +503,22 @@
]
],
"versions": [
"versions.yml:md5,9e24243bcd40c742e200d57da746a8b9"
"versions.yml:md5,b13b16c330d7d7043e96070dc75d5b93"
]
},
{
"CUSTOM_GENETICMAPCONVERT": {
"r-base": "4.5.3",
"r-nfcore.utils": "0.0.2",
"r-data.table": "1.17.8",
"r-janitor": "2.2.1"
}
}
],
"timestamp": "2026-04-21T17:20:09.156535388",
"timestamp": "2026-06-09T10:01:09.735797861",
"meta": {
"nf-test": "0.9.5",
"nextflow": "25.10.4"
"nf-test": "0.9.4",
"nextflow": "26.03.0"
}
}
}