From b784061503456fdb7ab669fa6df93999b0881a0a Mon Sep 17 00:00:00 2001 From: JulFrey Date: Thu, 22 Jan 2026 16:35:07 +0100 Subject: [PATCH 1/5] start implementing an s4 class for pipeable workflows --- GalaxyR.Rproj | 1 + NAMESPACE | 17 +- R/2025-10-27_JF_R_Galaxy_functions.R | 433 +----------- R/2026-01-21_s4_class_methods.R | 659 ++++++++++++++++++ ...esult.Rd => dot-galaxy_download_result.Rd} | 6 +- ...ait_for_job.Rd => dot-galaxy_poll_tool.Rd} | 8 +- ...axy_run_tool.Rd => dot-galaxy_run_tool.Rd} | 8 +- man/galaxy.Rd | 22 + man/galaxy_initialize.Rd | 53 +- man/galaxy_poll_workflow-character-method.Rd | 39 ++ man/galaxy_poll_workflow.Rd | 52 +- man/galaxy_start_workflow-character-method.Rd | 42 ++ man/galaxy_start_workflow.Rd | 36 +- man/galaxy_upload_ftp-missing-method.Rd | 51 ++ man/galaxy_upload_ftp.Rd | 43 +- man/galaxy_upload_https-character-method.Rd | 60 ++ man/galaxy_upload_https.Rd | 53 +- 17 files changed, 978 insertions(+), 605 deletions(-) create mode 100644 R/2026-01-21_s4_class_methods.R rename man/{galaxy_download_result.Rd => dot-galaxy_download_result.Rd} (94%) rename man/{galaxy_wait_for_job.Rd => dot-galaxy_poll_tool.Rd} (94%) rename man/{galaxy_run_tool.Rd => dot-galaxy_run_tool.Rd} (94%) create mode 100644 man/galaxy.Rd create mode 100644 man/galaxy_poll_workflow-character-method.Rd create mode 100644 man/galaxy_start_workflow-character-method.Rd create mode 100644 man/galaxy_upload_ftp-missing-method.Rd create mode 100644 man/galaxy_upload_https-character-method.Rd diff --git a/GalaxyR.Rproj b/GalaxyR.Rproj index 69fafd4..a3a0e83 100644 --- a/GalaxyR.Rproj +++ b/GalaxyR.Rproj @@ -19,4 +19,5 @@ LineEndingConversion: Posix BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source +PackageCheckArgs: --no-examples PackageRoxygenize: rd,collate,namespace diff --git a/NAMESPACE b/NAMESPACE index 5b70e0a..90d6e23 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,11 @@ # Generated by roxygen2: do not edit by hand +export(.galaxy_download_result) +export(.galaxy_poll_tool) +export(.galaxy_run_tool) +export(galaxy) export(galaxy_delete_dataset) export(galaxy_delete_datasets) -export(galaxy_download_result) export(galaxy_get_file_info) export(galaxy_get_tool) export(galaxy_get_tool_id) @@ -14,15 +17,13 @@ export(galaxy_list_histories) export(galaxy_list_invocations) export(galaxy_list_tools) export(galaxy_list_workflows) -export(galaxy_poll_workflow) -export(galaxy_run_tool) export(galaxy_set_credentials) -export(galaxy_start_workflow) -export(galaxy_upload_ftp) -export(galaxy_upload_https) -export(galaxy_wait_for_job) +exportMethods(galaxy_initialize) +exportMethods(galaxy_poll_workflow) +exportMethods(galaxy_start_workflow) +exportMethods(galaxy_upload_ftp) +exportMethods(galaxy_upload_https) importFrom(httr,VERB) importFrom(httr,add_headers) importFrom(httr,content) importFrom(httr,status_code) -importFrom(stats,setNames) diff --git a/R/2025-10-27_JF_R_Galaxy_functions.R b/R/2025-10-27_JF_R_Galaxy_functions.R index e38dc2c..8e8ce93 100644 --- a/R/2025-10-27_JF_R_Galaxy_functions.R +++ b/R/2025-10-27_JF_R_Galaxy_functions.R @@ -38,125 +38,6 @@ galaxy_has_key <- function() { galaxy_url } - -#' Create a new Galaxy history -#' -#' Creates a new history on a Galaxy instance and returns its ID. -#' -#' @param name Name of the history to create -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' -#' @details -#' A valid Galaxy API key is required and must be available via the -#' \code{GALAXY_API_KEY} environment variable. You can either add the variable to your .Renviron file and restart or use \code{\link{galaxy_set_credentials}} to set this up. This function should be called prior to any tool or workflow invocations to set up a history. -#' -#' @return -#' Character scalar. Encoded Galaxy history ID, which must be supplied to -#' subsequent upload, tool, or workflow functions. -#' -#' @examplesIf galaxy_has_key() -#' -#' # Requires a Galaxy API key (GALAXY_API_KEY) -#' -#' # set up your API Key in your .Renviron file first -#' history_id <- galaxy_initialize("My history name") -#' -#' @export galaxy_initialize -galaxy_initialize <- function(name = "R API request", galaxy_url = "https://usegalaxy.eu") { - api_key <- Sys.getenv("GALAXY_API_KEY") - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - hist_res <- httr::POST( - paste0(galaxy_url, "/api/histories"), - httr::add_headers(`x-api-key` = api_key, `Content-Type` = "application/json"), - body = jsonlite::toJSON(list(name = name), auto_unbox = TRUE) - ) - httr::stop_for_status(hist_res) - history <- httr::content(hist_res, "parsed") - history_id <- history$id - message("Using history:", history_id, "\n") - return(history_id) -} - - -#' FTP file upload to galaxy. -#' -#' Upload a file to Galaxy and register it in a history -#' -#' @param input_file Path to the local file to upload -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' @param galaxy_ftp FTP server address of the Galaxy instance -#' @param history_id The ID of the Galaxy history where the dataset will be uploaded -#' -#' @returns dataset_id The ID of the uploaded dataset in Galaxy -#' @export galaxy_upload_ftp -#' -#' @examplesIf galaxy_has_key() & Sys.getenv("GALAXY_USERNAME") != "" & Sys.getenv("GALAXY_PASSWORD") != "" -#' # set up your API Key, username and password in your .Renviron file first -#' galaxy_ftp <- "ftp.usegalaxy.eu" -#' input_file <- tempfile(fileext = ".txt") -#' test_text <- "This is an example \nfile." -#' writeLines(test_text,input_file) -#' history_id <- galaxy_initialize("test upload") -#' dataset_id <- galaxy_upload_ftp(input_file, history_id, galaxy_ftp) -#' print(dataset_id) -#' -galaxy_upload_ftp <- function(input_file, history_id, galaxy_ftp = "ftp.usegalaxy.eu", galaxy_url = "https://usegalaxy.eu"){ - api_key <- Sys.getenv("GALAXY_API_KEY") - username <- Sys.getenv("GALAXY_USERNAME") - password <- Sys.getenv("GALAXY_PASSWORD") - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - username_enc <- utils::URLencode(username, reserved = TRUE) - password_enc <- utils::URLencode(password, reserved = TRUE) - ftp_url <- paste0("ftp://", username_enc, ":", password_enc, "@", galaxy_ftp, "/") - - # UPLOAD using ftp - system2( - "curl", - c( - "--ssl-reqd", "-T", shQuote(input_file), - ftp_url - ), - stdout = TRUE, stderr = TRUE - ) - - # fetch the dataset using API - ftp_filename <- basename(input_file) - - fetch_payload <- list( - history_id = history_id, - targets = list(list( - destination = list(type = "hdas"), - elements = list(list( - src = "ftp_import", - ftp_path = ftp_filename, - ext = "auto", - dbkey = "?" - )) - )) - ) - - res <- httr::POST( - paste0(galaxy_url, "/api/tools/fetch"), - httr::add_headers(`x-api-key` = api_key, `Content-Type` = "application/json"), - body = jsonlite::toJSON(fetch_payload, auto_unbox = TRUE) - ) - - httr::stop_for_status(res) - upload_result <- httr::content(res, "parsed") - #print(upload_result) - - dataset_id <- upload_result$outputs[[1]]$id - return(dataset_id) -} - #' List workflows available to the user #' #' Retrieves workflows accessible to the authenticated user from a Galaxy @@ -328,176 +209,6 @@ galaxy_get_workflow_inputs <- function( do.call(rbind, inputs) } -#' Start a Galaxy workflow with inputs and parameters -#' -#' @param dataset_id Character. ID of the input dataset (HDA). -#' Used only if \code{inputs} is NULL. -#' @param workflow_id Character. Galaxy workflow ID. -#' @param history_id Character. History ID where the workflow will run. -#' @param inputs Named list. Optional workflow input mapping. -#' Keys must be workflow input step IDs (as characters). -#' Values must be lists describing datasets and/or parameters. -#' @param galaxy_url Character. Base URL of the Galaxy instance. -#' -#' @return Character. Workflow invocation ID. -#' -#' @export -#' @importFrom stats setNames -galaxy_start_workflow <- function( - dataset_id, - workflow_id, - history_id = NA, - inputs = NULL, - galaxy_url = "https://usegalaxy.eu" -) { - - if (missing(workflow_id) || !nzchar(workflow_id)) { - stop("workflow_id is required.") - } - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - api_key <- Sys.getenv("GALAXY_API_KEY") - if (!nzchar(api_key)) { - stop("GALAXY_API_KEY environment variable is not set.") - } - - # Build workflow inputs - if (is.null(inputs)) { - if (missing(dataset_id) || !nzchar(dataset_id)) { - stop("Either dataset_id or inputs must be provided.") - } - - # Default: map dataset to workflow input step "0" - inputs <- setNames( - list(list(src = "hda", id = dataset_id)), - "0" - ) - } - - run_body <- list(inputs = inputs) - - # Optional history - if (!is.na(history_id)) { - run_body$history_id <- history_id - } - - run_url <- paste0(galaxy_url, "/api/workflows/", workflow_id, "/invocations") - - run_res <- httr::POST( - run_url, - httr::add_headers( - `x-api-key` = api_key, - `Content-Type` = "application/json" - ), - body = jsonlite::toJSON(run_body, auto_unbox = TRUE) - ) - - httr::stop_for_status(run_res) - invocation <- httr::content(run_res, as = "parsed") - - message("Workflow invocation ID: ", invocation$id) - invocation$id -} - -#' Poll a Galaxy workflow invocation until completion -#' -#' @param invocation_id The ID of the workflow invocation to poll -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' @param poll_interval Time in seconds between polling attempts in seconds -#' -#' @returns A vector of HDA IDs corresponding to the output datasets of the workflow -#' -#' @examplesIf galaxy_has_key() -#' # iris example -#' tmp_dir <- tempdir() -#' f_name <- "iris.csv" -#' f_path <- paste(tmp_dir, f_name, sep = "\\") -#' write.csv(datasets::iris, f_path, row.names = FALSE) -#' -#' workflows <- galaxy_list_workflows(include_public = TRUE) -#' iris_workflow <- workflows[ -#' workflows$name == "Exploring Iris dataset with statistics and scatterplots", -#' ][1,] -#' -#' history_id <- galaxy_initialize("IRIS") -#' file_id <- galaxy_upload_https(f_path, history_id) -#' invocation_id <- galaxy_start_workflow(file_id, iris_workflow$id, history_id = history_id) -#' dataset_ids <- galaxy_poll_workflow(invocation_id) -#' -#' result_files <- galaxy_get_file_info(dataset_ids$output_ids) -#' head(result_files) -#' -#' -#' @export galaxy_poll_workflow -galaxy_poll_workflow <- function(invocation_id, galaxy_url = "https://usegalaxy.eu", poll_interval = 30) { - api_key <- Sys.getenv("GALAXY_API_KEY") - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - any_error <- FALSE - repeat { - Sys.sleep(poll_interval) - - # Get workflow invocation - status_res <- httr::GET( - paste0(galaxy_url, "/api/invocations/", invocation_id), - httr::add_headers(`x-api-key` = api_key) - ) - httr::stop_for_status(status_res) - status <- httr::content(status_res, "parsed") - - steps <- status$steps - - # Get all job IDs from the steps - job_ids <- sapply(steps, function(step) step$job_id) - job_ids <- job_ids[!sapply(job_ids, is.null)] - - if (length(job_ids) == 0) { - message(Sys.time(), " ,No jobs yet, waiting...") - next - } - - # Check each job state - job_states <- sapply(job_ids, function(jid) { - job_res <- httr::GET( - paste0(galaxy_url, "/api/jobs/", jid), - httr::add_headers(`x-api-key` = api_key) - ) - job <- httr::content(job_res, "parsed") - job$state - }) - - message(Sys.time(), " ,Job states: ", paste(job_states, collapse = ", ")) - - if (all(job_states == "ok")) { - message("All jobs finished successfully!") - break - } - if (any(job_states == "error" | job_states == "failed" | job_states == "deleted")) { - any_error <- TRUE - message("Some workflow jobs failed or were cancelled.") - break - } - } - - # Once all jobs are ok, return the HDA IDs in the workflow history - history_id <- status$history_id - datasets_res <- httr::GET( - paste0(galaxy_url, "/api/histories/", history_id, "/contents"), - httr::add_headers(`x-api-key` = api_key) - ) - datasets <- httr::content(datasets_res, "parsed") - - output_ids <- sapply(datasets, function(d) if(d$state == "ok" && !isTRUE(d$deleted)) d$id else NULL) - output_ids <- output_ids[!sapply(output_ids, is.null)] - output <- list(success = !any_error, output_ids = output_ids) - - return(output) -} - #' Download final result dataset from Galaxy #' #' @param output_ids Vector of HDA IDs from the workflow outputs the last one will be downloaded @@ -532,8 +243,8 @@ galaxy_poll_workflow <- function(invocation_id, galaxy_url = "https://usegalaxy. #' paste(tmp_dir, result_files$name[nrow(result_files)], sep = "\\") #' ) #' -#' @export galaxy_download_result -galaxy_download_result <- function(output_ids, out_file = "result.laz", galaxy_url = "https://usegalaxy.eu" ){ +#' @export .galaxy_download_result +.galaxy_download_result <- function(output_ids, out_file = "result.laz", galaxy_url = "https://usegalaxy.eu" ){ if(!is.null(output_ids$output_ids)){ output_ids <- output_ids$output_ids } @@ -1256,14 +967,14 @@ galaxy_get_tool_id <- function(name, #' options = "header" # optional, but explicit is good #' )) #' -#' result <- galaxy_wait_for_job(job_id) +#' result <- galaxy_poll_tool(job_id) #' #' test_file_result <- tempfile(fileext = ".txt") #' galaxy_download_result(list(output_ids = result$outputs$outfile$id), test_file_result) #' readLines(test_file_result) #' -#' @export -galaxy_run_tool <- function(tool_id, +#' @export .galaxy_run_tool +.galaxy_run_tool <- function(tool_id, history_id, inputs, galaxy_url = "https://usegalaxy.eu") { @@ -1294,134 +1005,6 @@ galaxy_run_tool <- function(tool_id, return(job$jobs[[1]]$id) } -#' Upload a dataset via HTTPS (direct POST) into Galaxy -#' -#' @param input_file Character. Path to the local file to upload. -#' @param history_id Character. ID of the Galaxy history to receive the data set. -#' @param wait Logical. Whether to wait for Galaxy to finish processing -#' @param wait_timeout Integer. Time in seconds until wait times out with an error. -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' @param file_type Character. Galaxy datatype identifier -#' (for example \code{"auto"}, \code{"fastq"}, \code{"bam"}). Default: \code{"auto"}. -#' @param dbkey Character. Reference genome identifier (for example \code{"?"} or \code{"hg38"}). Default: \code{"?"}. -#' -#' @return A list describing the newly created dataset(s) as returned by Galaxy. -#' -#' @details -#' This function uses the built-in \code{upload1} tool and performs a -#' multipart form POST. Large files may still require FTP depending on -#' the Galaxy server's configuration limits. -#' -#' @examplesIf galaxy_has_key() -#' history_id <- galaxy_initialize("test upload") -#' test_file <- tempfile(fileext = ".txt") -#' test_text <- "This is an example \ntest file." -#' writeLines(test_text,test_file) -#' -#' file_id <- galaxy_upload_https(test_file, history_id) -#' -#' @export -galaxy_upload_https <- function( - input_file, - history_id, - wait = FALSE, - wait_timeout = 600, - galaxy_url = "https://usegalaxy.eu", - file_type = "auto", - dbkey = "?" -) { - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - if (!file.exists(input_file)) - stop("input_file does not exist: ", input_file) - - if (missing(history_id) || !nzchar(history_id)) - stop("history_id is required.") - - api_key <- Sys.getenv("GALAXY_API_KEY") - if (!nzchar(api_key)) - stop("GALAXY_API_KEY environment variable is not set.") - - galaxy_wait_for_dataset <- function( - dataset_id, - galaxy_url = "https://usegalaxy.eu", - poll_interval = 3, - timeout = 600 - ) { - api_key <- Sys.getenv("GALAXY_API_KEY") - start_time <- Sys.time() - - repeat { - res <- httr::GET( - url = paste0(galaxy_url, "/api/datasets/", dataset_id), - httr::add_headers(`x-api-key` = api_key) - ) - httr::stop_for_status(res) - - ds <- httr::content(res, as = "parsed") - - if (ds$state == "ok") { - return(ds) - } - - if (ds$state == "error") { - stop("Galaxy dataset failed: ", ds$misc_info) - } - - if (as.numeric(Sys.time() - start_time, units = "secs") > timeout) { - stop("Timed out waiting for dataset to finish") - } - - Sys.sleep(poll_interval) - } - } - - targets <- list(list( - destination = list(type = "hdas"), - elements = list(list( - dbkey = dbkey, - ext = file_type, - name = basename(input_file), - space_to_tab = FALSE, - src = "files", - to_posix_lines = TRUE - )) - )) - - res <- httr::POST( - url = paste0(galaxy_url, "/api/tools/fetch"), - httr::add_headers(`x-api-key` = api_key), - body = list( - auto_decompress = TRUE, - history_id = history_id, - targets = jsonlite::toJSON(targets, auto_unbox = TRUE), - files_0 = httr::upload_file(input_file) - ), - encode = "multipart" - ) - - httr::stop_for_status(res) - response <- httr::content(res, as = "parsed") - - ## Extract encoded dataset ID - dataset_id <- response$outputs[[1]]$id - - ## Wait until Galaxy finishes processing it - if(wait){ - dataset <- galaxy_wait_for_dataset( - dataset_id = dataset_id, - galaxy_url = galaxy_url, - timeout = wait_timeout - ) - } - ## Return completed dataset (or dataset$id) - return(dataset_id) -} - - #' Wait for a Galaxy job to complete #' #' Polls the Galaxy API until a job reaches a terminal state @@ -1470,10 +1053,10 @@ galaxy_upload_https <- function( #' options = "header" #' )) #' -#' result <- galaxy_wait_for_job(job_id) +#' result <- galaxy_poll_tool(job_id) #' -#' @export -galaxy_wait_for_job <- function( +#' @export .galaxy_poll_tool +.galaxy_poll_tool <- function( job_id, galaxy_url = "https://usegalaxy.eu", poll_interval = 3, diff --git a/R/2026-01-21_s4_class_methods.R b/R/2026-01-21_s4_class_methods.R new file mode 100644 index 0000000..197e672 --- /dev/null +++ b/R/2026-01-21_s4_class_methods.R @@ -0,0 +1,659 @@ +########################## +## S4 class definition and create function +########################## + + +setClass( + "Galaxy", + slots = list( + history_name = "character", + history_id = "character", + input_dataset_id = "character", + inputs = "list", + invocation_id = "character", + output_dataset_ids = "character", + state = "character", + galaxy_url = "character" + ), + prototype = list( + history_name = "R API request", + history_id = NA_character_, + input_dataset_id = NA_character_, + inputs = list(), + invocation_id = NA_character_, + output_dataset_ids = character(0), + state = "new", + galaxy_url = NA_character_ + ) +) + +setValidity("Galaxy", function(object) { + allowed_states <- c("new", "pending", "success", "error") + + if (!object@state %in% allowed_states) { + return( + paste( + "state must be one of:", + paste(allowed_states, collapse = ", ") + ) + ) + } + + if (object@state != "new" && !nzchar(object@galaxy_url)) { + return("galaxy_url must be set once the Galaxy object is initialised.") + } + + TRUE +}) + +#' Create a Galaxy session object +#' +#' Constructor for a `Galaxy` S4 object used for pipe‑based workflows. +#' +#' @param history_name Character. Default name to give to a new history, +#' stored in the object and used by `galaxy_initialize()` if you don’t +#' override it. +#' @param galaxy_url Character. Base URL of the Galaxy instance. If the +#' environment variable `GALAXY_URL` is set, it takes precedence. +#' +#' @return A `Galaxy` object in state `"new"`. +#' @export +galaxy <- function(history_name = "R API request", galaxy_url = "https://usegalaxy.eu") { + resolved_url <- .resolve_galaxy_url(galaxy_url) + + obj <- new( + "Galaxy", + history_name = history_name, + galaxy_url = resolved_url, + state = "new" + ) + + validObject(obj) + obj +} + +############################# +## initialize history +############################# + + +#' @keywords internal +#' @noRd +.galaxy_initialize <- function(name = "R API request", galaxy_url = "https://usegalaxy.eu") { + api_key <- Sys.getenv("GALAXY_API_KEY") + + galaxy_url <- .resolve_galaxy_url(galaxy_url) + + hist_res <- httr::POST( + paste0(galaxy_url, "/api/histories"), + httr::add_headers(`x-api-key` = api_key, `Content-Type` = "application/json"), + body = jsonlite::toJSON(list(name = name), auto_unbox = TRUE) + ) + httr::stop_for_status(hist_res) + history <- httr::content(hist_res, "parsed") + history_id <- history$id + message("Using history:", history_id, "\n") + return(history_id) +} + +#' Create a new Galaxy history +#' +#' `galaxy_initialize()` is an S4 generic. With no `x` supplied it creates a +#' new history on the given Galaxy instance and returns its encoded ID. When +#' called with a [`Galaxy`] object it uses the object’s `history_name` and +#' `galaxy_url`, creates the history, and updates the object with the new +#' `history_id` and state `"pending"`. +#' +#' A valid Galaxy API key is required and must be available via the +#' `GALAXY_API_KEY` environment variable. +#' +#' @param x A `Galaxy` object, or missing to use the default method. +#' @param name Name of the history to create. Ignored when `x` is a +#' `Galaxy`, in which case `x@history_name` is used. +#' @param galaxy_url Base URL of the Galaxy instance. Ignored when `x` is a +#' `Galaxy`, in which case `x@galaxy_url` is used. +#' @return For the default method (`x` missing), a character scalar history ID. +#' For the `Galaxy` method, the modified `Galaxy` object. +#' @examplesIf galaxy_has_key() +#' history_id <- galaxy_initialize("My history name") +#' g <- galaxy(history_name = "My history name") +#' g <- galaxy_initialize(g) +#' @export +setGeneric("galaxy_initialize", + function(x, name = "R API request", galaxy_url = "https://usegalaxy.eu", ...) + standardGeneric("galaxy_initialize"), + signature = "x") + +#' @rdname galaxy_initialize +#' @export +setMethod("galaxy_initialize", "missing", + function(name, galaxy_url, ...) .galaxy_initialize(name, galaxy_url)) + + +#' @rdname galaxy_initialize +#' @export +setMethod("galaxy_initialize", "Galaxy", + function(x, ...) { + x@history_id <- .galaxy_initialize(x@history_name, x@galaxy_url) + x@state <- "pending" + validObject(x) + x + }) + +############################# +## File upload +############################# + + +#' @keywords internal +#' @noRd +.galaxy_upload_ftp <- function(input_file, + history_id, + galaxy_ftp = "ftp.usegalaxy.eu", + galaxy_url = "https://usegalaxy.eu") { + api_key <- Sys.getenv("GALAXY_API_KEY") + username <- Sys.getenv("GALAXY_USERNAME") + password <- Sys.getenv("GALAXY_PASSWORD") + galaxy_url <- .resolve_galaxy_url(galaxy_url) + + username_enc <- utils::URLencode(username, reserved = TRUE) + password_enc <- utils::URLencode(password, reserved = TRUE) + ftp_url <- paste0("ftp://", username_enc, ":", password_enc, "@", galaxy_ftp, "/") + + system2("curl", + c("--ssl-reqd", "-T", shQuote(input_file), ftp_url), + stdout = TRUE, stderr = TRUE) + + ftp_filename <- basename(input_file) + fetch_payload <- list( + history_id = history_id, + targets = list(list( + destination = list(type = "hdas"), + elements = list(list( + src = "ftp_import", + ftp_path = ftp_filename, + ext = "auto", + dbkey = "?" + )) + )) + ) + res <- httr::POST( + paste0(galaxy_url, "/api/tools/fetch"), + httr::add_headers(`x-api-key` = api_key, + `Content-Type` = "application/json"), + body = jsonlite::toJSON(fetch_payload, auto_unbox = TRUE) + ) + httr::stop_for_status(res) + httr::content(res, "parsed")$outputs[[1]]$id +} + +## generic with dispatch on x +setGeneric("galaxy_upload_ftp", + function(x, + input_file, + galaxy_ftp = "ftp.usegalaxy.eu", + galaxy_url = "https://usegalaxy.eu", + ...) + standardGeneric("galaxy_upload_ftp"), + signature = "x") + + +#' FTP file upload to Galaxy +#' +#' `galaxy_upload_ftp()` is an S4 generic. With no `x` supplied it uploads a +#' local file via FTP and registers it in the specified history, returning the +#' encoded dataset ID. When called with a [`Galaxy`] object it uses the +#' object's `history_id` and `galaxy_url` and updates the object with the new +#' `input_dataset_id`. +#' +#' A valid API key (`GALAXY_API_KEY`) and FTP credentials (`GALAXY_USERNAME`, +#' `GALAXY_PASSWORD`) must be available in the environment. +#' +#' @param x A `Galaxy` object, or a `history_id` to use the default method. +#' @param input_file Path to the local file to upload. +#' @param galaxy_ftp FTP server address of the Galaxy instance. +#' @param galaxy_url Base URL of the Galaxy instance, used by the default +#' method. If `GALAXY_URL` is set it takes precedence. +#' @return For the default method, a character scalar dataset ID. For the +#' `Galaxy` method, the modified `Galaxy` object. +#' @examplesIf galaxy_has_key() && nzchar(Sys.getenv("GALAXY_USERNAME")) && nzchar(Sys.getenv("GALAXY_PASSWORD")) +#' galaxy_ftp <- "ftp.usegalaxy.eu" +#' input_file <- tempfile(fileext = ".txt") +#' writeLines("Example", input_file) +#' hid <- galaxy_initialize("test upload") +#' did <- galaxy_upload_ftp(input_file, hid, galaxy_ftp) +#' g <- galaxy() +#' g <- galaxy_initialize(g) +#' g <- galaxy_upload_ftp(g, input_file, galaxy_ftp = galaxy_ftp) +#' @export +setMethod("galaxy_upload_ftp", "missing", + function(x, + input_file, + galaxy_ftp = "ftp.usegalaxy.eu", + galaxy_url = "https://usegalaxy.eu", + ...) + .galaxy_upload_ftp(input_file = input_file, history_id = x, galaxy_ftp, galaxy_url)) + +## method for Galaxy objects: update the object and return it +#' Galaxy upload via ftp S4 method +#' @rdname galaxy_upload_ftp +#' @export +setMethod("galaxy_upload_ftp", "Galaxy", + function(x, + input_file, + galaxy_ftp = "ftp.usegalaxy.eu", + ...) + { + did <- .galaxy_upload_ftp(input_file = input_file, history_id = x@history_id, galaxy_ftp, x@galaxy_url) + x@input_dataset_id <- did + validObject(x) + x + }) + +# internal helper, not exported +#' @keywords internal +#' @noRd +.galaxy_upload_https <- function( + input_file, + history_id, + wait = FALSE, + wait_timeout = 600, + galaxy_url = "https://usegalaxy.eu", + file_type = "auto", + dbkey = "?" +) { + galaxy_url <- .resolve_galaxy_url(galaxy_url) + if (!file.exists(input_file)) + stop("input_file does not exist: ", input_file) + if (missing(history_id) || !nzchar(history_id)) + stop("history_id is required.") + api_key <- Sys.getenv("GALAXY_API_KEY") + if (!nzchar(api_key)) + stop("GALAXY_API_KEY environment variable is not set.") + + galaxy_wait_for_dataset <- function( + dataset_id, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 3, + timeout = 600 + ) { + api_key <- Sys.getenv("GALAXY_API_KEY") + start_time <- Sys.time() + repeat { + res <- httr::GET( + url = paste0(galaxy_url, "/api/datasets/", dataset_id), + httr::add_headers(`x-api-key` = api_key) + ) + httr::stop_for_status(res) + ds <- httr::content(res, as = "parsed") + if (ds$state == "ok") { + return(ds) + } + if (ds$state == "error") { + stop("Galaxy dataset failed: ", ds$misc_info) + } + if (as.numeric(Sys.time() - start_time, units = "secs") > timeout) { + stop("Timed out waiting for dataset to finish") + } + Sys.sleep(poll_interval) + } + } + + targets <- list(list( + destination = list(type = "hdas"), + elements = list(list( + dbkey = dbkey, + ext = file_type, + name = basename(input_file), + space_to_tab = FALSE, + src = "files", + to_posix_lines = TRUE + )) + )) + + res <- httr::POST( + url = paste0(galaxy_url, "/api/tools/fetch"), + httr::add_headers(`x-api-key` = api_key), + body = list( + auto_decompress = TRUE, + history_id = history_id, + targets = jsonlite::toJSON(targets, auto_unbox = TRUE), + files_0 = httr::upload_file(input_file) + ), + encode = "multipart" + ) + httr::stop_for_status(res) + response <- httr::content(res, as = "parsed") + dataset_id <- response$outputs[[1]]$id + + if (wait) { + galaxy_wait_for_dataset( + dataset_id = dataset_id, + galaxy_url = galaxy_url, + timeout = wait_timeout + ) + } + + dataset_id +} + +## generic with dispatch on the first argument +setGeneric("galaxy_upload_https", + function(x, + input_file, + wait = FALSE, + wait_timeout = 600, + galaxy_url = "https://usegalaxy.eu", + file_type = "auto", + dbkey = "?", + ...) + standardGeneric("galaxy_upload_https"), + signature = "x") + +#' Upload a dataset via HTTPS (direct POST) into Galaxy +#' +#' `galaxy_upload_https()` is an S4 generic. With no `x` supplied it uploads a +#' local file via HTTPS to the specified history and returns the encoded dataset +#' ID. When called with a [`Galaxy`] object it uses the object's `history_id` and +#' `galaxy_url`, uploads the file, and updates the object with the new +#' `input_dataset_id`. +#' +#' This uses Galaxy's built‑in `upload1` tool and performs a multipart form +#' POST. Large files may still require FTP depending on server configuration. +#' A valid API key (`GALAXY_API_KEY`) must be available in the environment. +#' +#' @param x A `Galaxy` object, or a `history_id` to use the default method. +#' @param input_file Path to the local file to upload. +#' @param wait Logical. Whether to wait for Galaxy to finish processing. +#' @param wait_timeout Time in seconds until `wait` times out with an error. +#' @param galaxy_url Base URL of the Galaxy instance, used by the default method. +#' If `GALAXY_URL` is set it takes precedence. +#' @param file_type Galaxy datatype identifier (e.g. `"auto"`, `"fastq"`, `"bam"`). +#' @param dbkey Reference genome identifier (e.g. `"?"` or `"hg38"`). +#' @return For the default method, a character scalar dataset ID. For the +#' `Galaxy` method, the modified `Galaxy` object. +#' @examplesIf galaxy_has_key() +#' hid <- galaxy_initialize("test upload") +#' test_file <- tempfile(fileext = ".txt") +#' writeLines("This is an example test file.", test_file) +#' file_id <- galaxy_upload_https(hid, test_file) +#' g <- galaxy() +#' g <- galaxy_initialize(g) +#' g <- galaxy_upload_https(g, test_file) +#' @export +setMethod("galaxy_upload_https", "character", + function(x, + input_file, + wait = FALSE, + wait_timeout = 600, + galaxy_url = "https://usegalaxy.eu", + file_type = "auto", + dbkey = "?", + ...) { + .galaxy_upload_https(input_file = input_file, + history_id = x, + wait = wait, + wait_timeout = wait_timeout, + galaxy_url = galaxy_url, + file_type = file_type, + dbkey = dbkey) + }) + +#' S4 Method for galaxy ftp upload +#' @rdname galaxy_upload_https +#' @export +setMethod("galaxy_upload_https", "Galaxy", + function(x, + input_file, + wait = FALSE, + wait_timeout = 600, + file_type = "auto", + dbkey = "?", + ...) + { + did <- .galaxy_upload_https(input_file, + x@history_id, + wait = wait, + wait_timeout = wait_timeout, + galaxy_url = x@galaxy_url, + file_type = file_type, + dbkey = dbkey) + x@input_dataset_id <- did + validObject(x) + x + }) + +######################### +## Workflow invocation and polling +######################### + +# internal helper, not exported +#' @keywords internal +#' @noRd +.galaxy_start_workflow <- function( + history_id, + dataset_id, + workflow_id, + inputs = NULL, + galaxy_url = "https://usegalaxy.eu" +) { + if (missing(workflow_id) || !nzchar(workflow_id)) { + stop("workflow_id is required.") + } + galaxy_url <- .resolve_galaxy_url(galaxy_url) + api_key <- Sys.getenv("GALAXY_API_KEY") + if (!nzchar(api_key)) { + stop("GALAXY_API_KEY environment variable is not set.") + } + if (is.null(inputs)) { + if (missing(dataset_id) || !nzchar(dataset_id)) { + stop("Either dataset_id or inputs must be provided.") + } + inputs <- setNames(list(list(src = "hda", id = dataset_id)), "0") + } + run_body <- list(inputs = inputs) + if (!is.na(history_id)) { + run_body$history_id <- history_id + } + run_url <- paste0(galaxy_url, "/api/workflows/", workflow_id, "/invocations") + res <- httr::POST( + run_url, + httr::add_headers( + `x-api-key` = api_key, + `Content-Type` = "application/json" + ), + body = jsonlite::toJSON(run_body, auto_unbox = TRUE) + ) + httr::stop_for_status(res) + inv <- httr::content(res, as = "parsed") + message("Workflow invocation ID: ", inv$id) + inv$id +} + +## generic dispatches on the first argument +setGeneric("galaxy_start_workflow", + function(x, + workflow_id, + dataset_id, + inputs = NULL, + galaxy_url = "https://usegalaxy.eu", + ...) + standardGeneric("galaxy_start_workflow"), + signature = "x") + +#' Start a Galaxy workflow with inputs and parameters +#' +#' `galaxy_start_workflow()` is an S4 generic. With `x` as a character vector +#' it is treated as a history ID: the given workflow is invoked in that history +#' and the invocation ID is returned. With `x` as a [`Galaxy`] object, the +#' history ID and URL are taken from the object; the workflow is started and +#' the object is updated with the resulting `invocation_id`. +#' +#' @param x A `Galaxy` object, or a history ID (`character`) to use the default +#' method. +#' @param workflow_id Character. Galaxy workflow ID. +#' @param dataset_id Character. ID of the input dataset (HDA). Ignored if +#' `inputs` is supplied. When `x` is a `Galaxy` and `dataset_id` is missing, +#' `x@input_dataset_id` is used. +#' @param inputs Named list. Optional workflow input mapping; keys are workflow +#' input step IDs, values are lists describing datasets/parameters. +#' @param galaxy_url Base URL of the Galaxy instance, used by the character +#' method. If `GALAXY_URL` is set it takes precedence. +#' @return For the character method, a character scalar invocation ID. For the +#' `Galaxy` method, the modified `Galaxy` object. +#' @export +setMethod("galaxy_start_workflow", "character", + function(x, + workflow_id, + dataset_id, + inputs = NULL, + galaxy_url = "https://usegalaxy.eu", + ...) { + .galaxy_start_workflow(history_id = x, + dataset_id = dataset_id, + workflow_id = workflow_id, + inputs = inputs, + galaxy_url = galaxy_url) + }) + +#' S4 function to start a galaxy workflow +#' @rdname galaxy_start_workflow +#' @export +setMethod("galaxy_start_workflow", "Galaxy", + function(x, + workflow_id, + dataset_id, + inputs = NULL, + ...) { + hid <- x@history_id + ds <- if (!missing(dataset_id)) dataset_id else x@input_dataset_id + inp <- if (!is.null(inputs)) inputs else if (length(x@inputs) > 0) x@inputs else NULL + inv <- .galaxy_start_workflow(history_id = hid, + dataset_id = ds, + workflow_id = workflow_id, + inputs = inp, + galaxy_url = x@galaxy_url) + x@invocation_id <- inv + validObject(x) + x + }) + +#' Helper function for workflow polling +#' @keywords internal +#' @noRd +.galaxy_poll_workflow <- function(invocation_id, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 30) { + api_key <- Sys.getenv("GALAXY_API_KEY") + galaxy_url <- .resolve_galaxy_url(galaxy_url) + any_error <- FALSE + + repeat { + ## Get workflow invocation + status_res <- httr::GET( + paste0(galaxy_url, "/api/invocations/", invocation_id), + httr::add_headers(`x-api-key` = api_key) + ) + httr::stop_for_status(status_res) + status <- httr::content(status_res, "parsed") + steps <- status$steps + + ## Get all job IDs from the steps + job_ids <- lapply(steps, function(step) step$job_id) + job_ids <- job_ids[!sapply(job_ids, is.null)] + job_ids <- job_ids[nzchar(job_ids)] + if (!length(job_ids)) { + message(Sys.time(), " ,No jobs yet, waiting...") + next + } + + ## Check each job state + job_states <- vapply(job_ids, function(jid) { + job_res <- httr::GET( + paste0(galaxy_url, "/api/jobs/", jid), + httr::add_headers(`x-api-key` = api_key) + ) + httr::content(job_res, "parsed")$state + }, character(1L)) + + message(Sys.time(), " ,Job states: ", paste(job_states, collapse = ", ")) + + if (all(job_states == "ok")) { + message("All jobs finished successfully!") + break + } + if (any(job_states %in% c("error", "failed", "deleted"))) { + any_error <- TRUE + message("Some workflow jobs failed or were cancelled.") + break + } + Sys.sleep(poll_interval) + } + + ## Once all jobs are ok, return the HDA IDs in the workflow history + history_id <- status$history_id + datasets_res <- httr::GET( + paste0(galaxy_url, "/api/histories/", history_id, "/contents"), + httr::add_headers(`x-api-key` = api_key) + ) + datasets <- httr::content(datasets_res, "parsed") + output_ids <- vapply(datasets, function(d) { + if (isTRUE(d$state == "ok") && !isTRUE(d$deleted)) d$id else NA_character_ + }, character(1L)) + output_ids <- output_ids[!is.na(output_ids)] + + list(success = !any_error, output_ids = output_ids) +} + +setGeneric("galaxy_poll_workflow", + function(x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 30, + ...) + standardGeneric("galaxy_poll_workflow"), + signature = "x") + +#' Poll a Galaxy workflow invocation until completion +#' +#' `galaxy_poll_workflow()` is an S4 generic. With `x` as a character vector it +#' is treated as a workflow invocation ID; the invocation is polled until it +#' completes and a list of output dataset IDs is returned. With `x` as a +#' [`Galaxy`] object, the `invocation_id` and `galaxy_url` are taken from the +#' object, and the object is updated with the resulting `output_dataset_ids` and +#' state. +#' +#' @param x A workflow invocation ID (`character`) or a `Galaxy` object. +#' @param galaxy_url Base URL of the Galaxy instance, used by the character +#' method. If `GALAXY_URL` is set it takes precedence. +#' @param poll_interval Time in seconds between polling attempts. +#' @return For the character method, a list with elements `success` and +#' `output_ids`. For the `Galaxy` method, the modified `Galaxy` object. +#' @examplesIf galaxy_has_key() +#' invocation_id <- "abc123" +#' galaxy_poll_workflow(invocation_id) +#' @export +setMethod("galaxy_poll_workflow", "character", + function(x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 30, + ...) { + .galaxy_poll_workflow(invocation_id = x, + galaxy_url = galaxy_url, + poll_interval = poll_interval) + }) + +#' S4 object galaxy workflow polling function +#' @rdname galaxy_poll_workflow +#' @export +setMethod("galaxy_poll_workflow", "Galaxy", + function(x, + poll_interval = 30, + ...) { + res <- .galaxy_poll_workflow(invocation_id = x@invocation_id, + galaxy_url = x@galaxy_url, + poll_interval = poll_interval) + x@output_dataset_ids <- res$output_ids + x@state <- if (isTRUE(res$success)) "success" else "error" + validObject(x) + x + }) diff --git a/man/galaxy_download_result.Rd b/man/dot-galaxy_download_result.Rd similarity index 94% rename from man/galaxy_download_result.Rd rename to man/dot-galaxy_download_result.Rd index f527236..1c7b0d3 100644 --- a/man/galaxy_download_result.Rd +++ b/man/dot-galaxy_download_result.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_download_result} -\alias{galaxy_download_result} +\name{.galaxy_download_result} +\alias{.galaxy_download_result} \title{Download final result dataset from Galaxy} \usage{ -galaxy_download_result( +.galaxy_download_result( output_ids, out_file = "result.laz", galaxy_url = "https://usegalaxy.eu" diff --git a/man/galaxy_wait_for_job.Rd b/man/dot-galaxy_poll_tool.Rd similarity index 94% rename from man/galaxy_wait_for_job.Rd rename to man/dot-galaxy_poll_tool.Rd index 7fda39f..b2f290d 100644 --- a/man/galaxy_wait_for_job.Rd +++ b/man/dot-galaxy_poll_tool.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_wait_for_job} -\alias{galaxy_wait_for_job} +\name{.galaxy_poll_tool} +\alias{.galaxy_poll_tool} \title{Wait for a Galaxy job to complete} \usage{ -galaxy_wait_for_job( +.galaxy_poll_tool( job_id, galaxy_url = "https://usegalaxy.eu", poll_interval = 3, @@ -59,7 +59,7 @@ job_id <- galaxy_run_tool(tool_id = tool, history_id = history_id, inputs = list options = "header" )) -result <- galaxy_wait_for_job(job_id) +result <- galaxy_poll_tool(job_id) \dontshow{\}) # examplesIf} } \seealso{ diff --git a/man/galaxy_run_tool.Rd b/man/dot-galaxy_run_tool.Rd similarity index 94% rename from man/galaxy_run_tool.Rd rename to man/dot-galaxy_run_tool.Rd index 937d421..f70fe0f 100644 --- a/man/galaxy_run_tool.Rd +++ b/man/dot-galaxy_run_tool.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_run_tool} -\alias{galaxy_run_tool} +\name{.galaxy_run_tool} +\alias{.galaxy_run_tool} \title{Run a Galaxy tool programmatically} \usage{ -galaxy_run_tool( +.galaxy_run_tool( tool_id, history_id, inputs, @@ -56,7 +56,7 @@ job_id <- galaxy_run_tool(tool_id = tool, history_id = history_id, inputs = list options = "header" # optional, but explicit is good )) -result <- galaxy_wait_for_job(job_id) +result <- galaxy_poll_tool(job_id) test_file_result <- tempfile(fileext = ".txt") galaxy_download_result(list(output_ids = result$outputs$outfile$id), test_file_result) diff --git a/man/galaxy.Rd b/man/galaxy.Rd new file mode 100644 index 0000000..2c086ac --- /dev/null +++ b/man/galaxy.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy} +\alias{galaxy} +\title{Create a Galaxy session object} +\usage{ +galaxy(history_name = "R API request", galaxy_url = "https://usegalaxy.eu") +} +\arguments{ +\item{history_name}{Character. Default name to give to a new history, +stored in the object and used by \code{galaxy_initialize()} if you don’t +override it.} + +\item{galaxy_url}{Character. Base URL of the Galaxy instance. If the +environment variable \code{GALAXY_URL} is set, it takes precedence.} +} +\value{ +A \code{Galaxy} object in state \code{"new"}. +} +\description{ +Constructor for a \code{Galaxy} S4 object used for pipe‑based workflows. +} diff --git a/man/galaxy_initialize.Rd b/man/galaxy_initialize.Rd index 239e273..c21d8db 100644 --- a/man/galaxy_initialize.Rd +++ b/man/galaxy_initialize.Rd @@ -1,35 +1,60 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R +% Please edit documentation in R/2026-01-21_s4_class_methods.R \name{galaxy_initialize} \alias{galaxy_initialize} +\alias{galaxy_initialize,missing-method} +\alias{galaxy_initialize,Galaxy-method} \title{Create a new Galaxy history} \usage{ -galaxy_initialize(name = "R API request", galaxy_url = "https://usegalaxy.eu") +galaxy_initialize( + x, + name = "R API request", + galaxy_url = "https://usegalaxy.eu", + ... +) + +\S4method{galaxy_initialize}{missing}( + x, + name = "R API request", + galaxy_url = "https://usegalaxy.eu", + ... +) + +\S4method{galaxy_initialize}{Galaxy}( + x, + name = "R API request", + galaxy_url = "https://usegalaxy.eu", + ... +) } \arguments{ -\item{name}{Name of the history to create} +\item{x}{A \code{Galaxy} object, or missing to use the default method.} + +\item{name}{Name of the history to create. Ignored when \code{x} is a +\code{Galaxy}, in which case \code{x@history_name} is used.} -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} +\item{galaxy_url}{Base URL of the Galaxy instance. Ignored when \code{x} is a +\code{Galaxy}, in which case \code{x@galaxy_url} is used.} } \value{ -Character scalar. Encoded Galaxy history ID, which must be supplied to -subsequent upload, tool, or workflow functions. +For the default method (\code{x} missing), a character scalar history ID. +For the \code{Galaxy} method, the modified \code{Galaxy} object. } \description{ -Creates a new history on a Galaxy instance and returns its ID. +\code{galaxy_initialize()} is an S4 generic. With no \code{x} supplied it creates a +new history on the given Galaxy instance and returns its encoded ID. When +called with a \code{\link{Galaxy}} object it uses the object’s \code{history_name} and +\code{galaxy_url}, creates the history, and updates the object with the new +\code{history_id} and state \code{"pending"}. } \details{ A valid Galaxy API key is required and must be available via the -\code{GALAXY_API_KEY} environment variable. You can either add the variable to your .Renviron file and restart or use \code{\link{galaxy_set_credentials}} to set this up. This function should be called prior to any tool or workflow invocations to set up a history. +\code{GALAXY_API_KEY} environment variable. } \examples{ \dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} - -# Requires a Galaxy API key (GALAXY_API_KEY) - -# set up your API Key in your .Renviron file first history_id <- galaxy_initialize("My history name") +g <- galaxy(history_name = "My history name") +g <- galaxy_initialize(g) \dontshow{\}) # examplesIf} } diff --git a/man/galaxy_poll_workflow-character-method.Rd b/man/galaxy_poll_workflow-character-method.Rd new file mode 100644 index 0000000..e3e23d3 --- /dev/null +++ b/man/galaxy_poll_workflow-character-method.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_poll_workflow,character-method} +\alias{galaxy_poll_workflow,character-method} +\title{Poll a Galaxy workflow invocation until completion} +\usage{ +\S4method{galaxy_poll_workflow}{character}( + x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 30, + ... +) +} +\arguments{ +\item{x}{A workflow invocation ID (\code{character}) or a \code{Galaxy} object.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the character +method. If \code{GALAXY_URL} is set it takes precedence.} + +\item{poll_interval}{Time in seconds between polling attempts.} +} +\value{ +For the character method, a list with elements \code{success} and +\code{output_ids}. For the \code{Galaxy} method, the modified \code{Galaxy} object. +} +\description{ +\code{galaxy_poll_workflow()} is an S4 generic. With \code{x} as a character vector it +is treated as a workflow invocation ID; the invocation is polled until it +completes and a list of output dataset IDs is returned. With \code{x} as a +\code{\link{Galaxy}} object, the \code{invocation_id} and \code{galaxy_url} are taken from the +object, and the object is updated with the resulting \code{output_dataset_ids} and +state. +} +\examples{ +\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} +invocation_id <- "abc123" +galaxy_poll_workflow(invocation_id) +\dontshow{\}) # examplesIf} +} diff --git a/man/galaxy_poll_workflow.Rd b/man/galaxy_poll_workflow.Rd index f142462..3ee425a 100644 --- a/man/galaxy_poll_workflow.Rd +++ b/man/galaxy_poll_workflow.Rd @@ -1,50 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_poll_workflow} -\alias{galaxy_poll_workflow} -\title{Poll a Galaxy workflow invocation until completion} +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_poll_workflow,Galaxy-method} +\alias{galaxy_poll_workflow,Galaxy-method} +\title{S4 object galaxy workflow polling function} \usage{ -galaxy_poll_workflow( - invocation_id, +\S4method{galaxy_poll_workflow}{Galaxy}( + x, galaxy_url = "https://usegalaxy.eu", - poll_interval = 30 + poll_interval = 30, + ... ) } -\arguments{ -\item{invocation_id}{The ID of the workflow invocation to poll} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} - -\item{poll_interval}{Time in seconds between polling attempts in seconds} -} -\value{ -A vector of HDA IDs corresponding to the output datasets of the workflow -} \description{ -Poll a Galaxy workflow invocation until completion -} -\examples{ -\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} -# iris example -tmp_dir <- tempdir() -f_name <- "iris.csv" -f_path <- paste(tmp_dir, f_name, sep = "\\\\") -write.csv(datasets::iris, f_path, row.names = FALSE) - -workflows <- galaxy_list_workflows(include_public = TRUE) -iris_workflow <- workflows[ - workflows$name == "Exploring Iris dataset with statistics and scatterplots", -][1,] - -history_id <- galaxy_initialize("IRIS") -file_id <- galaxy_upload_https(f_path, history_id) -invocation_id <- galaxy_start_workflow(file_id, iris_workflow$id, history_id = history_id) -dataset_ids <- galaxy_poll_workflow(invocation_id) - -result_files <- galaxy_get_file_info(dataset_ids$output_ids) -head(result_files) - -\dontshow{\}) # examplesIf} +S4 object galaxy workflow polling function } diff --git a/man/galaxy_start_workflow-character-method.Rd b/man/galaxy_start_workflow-character-method.Rd new file mode 100644 index 0000000..c53040a --- /dev/null +++ b/man/galaxy_start_workflow-character-method.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_start_workflow,character-method} +\alias{galaxy_start_workflow,character-method} +\title{Start a Galaxy workflow with inputs and parameters} +\usage{ +\S4method{galaxy_start_workflow}{character}( + x, + workflow_id, + dataset_id, + inputs = NULL, + galaxy_url = "https://usegalaxy.eu", + ... +) +} +\arguments{ +\item{x}{A \code{Galaxy} object, or a history ID (\code{character}) to use the default +method.} + +\item{workflow_id}{Character. Galaxy workflow ID.} + +\item{dataset_id}{Character. ID of the input dataset (HDA). Ignored if +\code{inputs} is supplied. When \code{x} is a \code{Galaxy} and \code{dataset_id} is missing, +\code{x@input_dataset_id} is used.} + +\item{inputs}{Named list. Optional workflow input mapping; keys are workflow +input step IDs, values are lists describing datasets/parameters.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the character +method. If \code{GALAXY_URL} is set it takes precedence.} +} +\value{ +For the character method, a character scalar invocation ID. For the +\code{Galaxy} method, the modified \code{Galaxy} object. +} +\description{ +\code{galaxy_start_workflow()} is an S4 generic. With \code{x} as a character vector +it is treated as a history ID: the given workflow is invoked in that history +and the invocation ID is returned. With \code{x} as a \code{\link{Galaxy}} object, the +history ID and URL are taken from the object; the workflow is started and +the object is updated with the resulting \code{invocation_id}. +} diff --git a/man/galaxy_start_workflow.Rd b/man/galaxy_start_workflow.Rd index 70143dc..64a94e7 100644 --- a/man/galaxy_start_workflow.Rd +++ b/man/galaxy_start_workflow.Rd @@ -1,34 +1,18 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_start_workflow} -\alias{galaxy_start_workflow} -\title{Start a Galaxy workflow with inputs and parameters} +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_start_workflow,Galaxy-method} +\alias{galaxy_start_workflow,Galaxy-method} +\title{S4 function to start a galaxy workflow} \usage{ -galaxy_start_workflow( - dataset_id, +\S4method{galaxy_start_workflow}{Galaxy}( + x, workflow_id, - history_id = NA, + dataset_id, inputs = NULL, - galaxy_url = "https://usegalaxy.eu" + galaxy_url = "https://usegalaxy.eu", + ... ) } -\arguments{ -\item{dataset_id}{Character. ID of the input dataset (HDA). -Used only if \code{inputs} is NULL.} - -\item{workflow_id}{Character. Galaxy workflow ID.} - -\item{history_id}{Character. History ID where the workflow will run.} - -\item{inputs}{Named list. Optional workflow input mapping. -Keys must be workflow input step IDs (as characters). -Values must be lists describing datasets and/or parameters.} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance.} -} -\value{ -Character. Workflow invocation ID. -} \description{ -Start a Galaxy workflow with inputs and parameters +S4 function to start a galaxy workflow } diff --git a/man/galaxy_upload_ftp-missing-method.Rd b/man/galaxy_upload_ftp-missing-method.Rd new file mode 100644 index 0000000..0903707 --- /dev/null +++ b/man/galaxy_upload_ftp-missing-method.Rd @@ -0,0 +1,51 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_upload_ftp,missing-method} +\alias{galaxy_upload_ftp,missing-method} +\title{FTP file upload to Galaxy} +\usage{ +\S4method{galaxy_upload_ftp}{missing}( + x, + input_file, + galaxy_ftp = "ftp.usegalaxy.eu", + galaxy_url = "https://usegalaxy.eu", + ... +) +} +\arguments{ +\item{x}{A \code{Galaxy} object, or a \code{history_id} to use the default method.} + +\item{input_file}{Path to the local file to upload.} + +\item{galaxy_ftp}{FTP server address of the Galaxy instance.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the default +method. If \code{GALAXY_URL} is set it takes precedence.} +} +\value{ +For the default method, a character scalar dataset ID. For the +\code{Galaxy} method, the modified \code{Galaxy} object. +} +\description{ +\code{galaxy_upload_ftp()} is an S4 generic. With no \code{x} supplied it uploads a +local file via FTP and registers it in the specified history, returning the +encoded dataset ID. When called with a \code{\link{Galaxy}} object it uses the +object's \code{history_id} and \code{galaxy_url} and updates the object with the new +\code{input_dataset_id}. +} +\details{ +A valid API key (\code{GALAXY_API_KEY}) and FTP credentials (\code{GALAXY_USERNAME}, +\code{GALAXY_PASSWORD}) must be available in the environment. +} +\examples{ +\dontshow{if (galaxy_has_key() && nzchar(Sys.getenv("GALAXY_USERNAME")) && nzchar(Sys.getenv("GALAXY_PASSWORD"))) withAutoprint(\{ # examplesIf} +galaxy_ftp <- "ftp.usegalaxy.eu" +input_file <- tempfile(fileext = ".txt") +writeLines("Example", input_file) +hid <- galaxy_initialize("test upload") +did <- galaxy_upload_ftp(input_file, hid, galaxy_ftp) +g <- galaxy() +g <- galaxy_initialize(g) +g <- galaxy_upload_ftp(g, input_file, galaxy_ftp = galaxy_ftp) +\dontshow{\}) # examplesIf} +} diff --git a/man/galaxy_upload_ftp.Rd b/man/galaxy_upload_ftp.Rd index c2e4d25..3d13d1b 100644 --- a/man/galaxy_upload_ftp.Rd +++ b/man/galaxy_upload_ftp.Rd @@ -1,42 +1,17 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_upload_ftp} -\alias{galaxy_upload_ftp} -\title{FTP file upload to galaxy.} +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_upload_ftp,Galaxy-method} +\alias{galaxy_upload_ftp,Galaxy-method} +\title{Galaxy upload via ftp S4 method} \usage{ -galaxy_upload_ftp( +\S4method{galaxy_upload_ftp}{Galaxy}( + x, input_file, - history_id, galaxy_ftp = "ftp.usegalaxy.eu", - galaxy_url = "https://usegalaxy.eu" + galaxy_url = "https://usegalaxy.eu", + ... ) } -\arguments{ -\item{input_file}{Path to the local file to upload} - -\item{history_id}{The ID of the Galaxy history where the dataset will be uploaded} - -\item{galaxy_ftp}{FTP server address of the Galaxy instance} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} -} -\value{ -dataset_id The ID of the uploaded dataset in Galaxy -} \description{ -Upload a file to Galaxy and register it in a history -} -\examples{ -\dontshow{if (galaxy_has_key() & Sys.getenv("GALAXY_USERNAME") != "" & Sys.getenv("GALAXY_PASSWORD") != "") withAutoprint(\{ # examplesIf} -# set up your API Key, username and password in your .Renviron file first -galaxy_ftp <- "ftp.usegalaxy.eu" -input_file <- tempfile(fileext = ".txt") -test_text <- "This is an example \nfile." -writeLines(test_text,input_file) -history_id <- galaxy_initialize("test upload") -dataset_id <- galaxy_upload_ftp(input_file, history_id, galaxy_ftp) -print(dataset_id) -\dontshow{\}) # examplesIf} +Galaxy upload via ftp S4 method } diff --git a/man/galaxy_upload_https-character-method.Rd b/man/galaxy_upload_https-character-method.Rd new file mode 100644 index 0000000..f3e27b7 --- /dev/null +++ b/man/galaxy_upload_https-character-method.Rd @@ -0,0 +1,60 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_upload_https,character-method} +\alias{galaxy_upload_https,character-method} +\title{Upload a dataset via HTTPS (direct POST) into Galaxy} +\usage{ +\S4method{galaxy_upload_https}{character}( + x, + input_file, + wait = FALSE, + wait_timeout = 600, + galaxy_url = "https://usegalaxy.eu", + file_type = "auto", + dbkey = "?", + ... +) +} +\arguments{ +\item{x}{A \code{Galaxy} object, or a \code{history_id} to use the default method.} + +\item{input_file}{Path to the local file to upload.} + +\item{wait}{Logical. Whether to wait for Galaxy to finish processing.} + +\item{wait_timeout}{Time in seconds until \code{wait} times out with an error.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the default method. +If \code{GALAXY_URL} is set it takes precedence.} + +\item{file_type}{Galaxy datatype identifier (e.g. \code{"auto"}, \code{"fastq"}, \code{"bam"}).} + +\item{dbkey}{Reference genome identifier (e.g. \code{"?"} or \code{"hg38"}).} +} +\value{ +For the default method, a character scalar dataset ID. For the +\code{Galaxy} method, the modified \code{Galaxy} object. +} +\description{ +\code{galaxy_upload_https()} is an S4 generic. With no \code{x} supplied it uploads a +local file via HTTPS to the specified history and returns the encoded dataset +ID. When called with a \code{\link{Galaxy}} object it uses the object's \code{history_id} and +\code{galaxy_url}, uploads the file, and updates the object with the new +\code{input_dataset_id}. +} +\details{ +This uses Galaxy's built‑in \code{upload1} tool and performs a multipart form +POST. Large files may still require FTP depending on server configuration. +A valid API key (\code{GALAXY_API_KEY}) must be available in the environment. +} +\examples{ +\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} +hid <- galaxy_initialize("test upload") +test_file <- tempfile(fileext = ".txt") +writeLines("This is an example test file.", test_file) +file_id <- galaxy_upload_https(hid, test_file) +g <- galaxy() +g <- galaxy_initialize(g) +g <- galaxy_upload_https(g, test_file) +\dontshow{\}) # examplesIf} +} diff --git a/man/galaxy_upload_https.Rd b/man/galaxy_upload_https.Rd index 2ad7d25..0849f67 100644 --- a/man/galaxy_upload_https.Rd +++ b/man/galaxy_upload_https.Rd @@ -1,55 +1,20 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{galaxy_upload_https} -\alias{galaxy_upload_https} -\title{Upload a dataset via HTTPS (direct POST) into Galaxy} +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_upload_https,Galaxy-method} +\alias{galaxy_upload_https,Galaxy-method} +\title{S4 Method for galaxy ftp upload} \usage{ -galaxy_upload_https( +\S4method{galaxy_upload_https}{Galaxy}( + x, input_file, - history_id, wait = FALSE, wait_timeout = 600, galaxy_url = "https://usegalaxy.eu", file_type = "auto", - dbkey = "?" + dbkey = "?", + ... ) } -\arguments{ -\item{input_file}{Character. Path to the local file to upload.} - -\item{history_id}{Character. ID of the Galaxy history to receive the data set.} - -\item{wait}{Logical. Whether to wait for Galaxy to finish processing} - -\item{wait_timeout}{Integer. Time in seconds until wait times out with an error.} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} - -\item{file_type}{Character. Galaxy datatype identifier -(for example \code{"auto"}, \code{"fastq"}, \code{"bam"}). Default: \code{"auto"}.} - -\item{dbkey}{Character. Reference genome identifier (for example \code{"?"} or \code{"hg38"}). Default: \code{"?"}.} -} -\value{ -A list describing the newly created dataset(s) as returned by Galaxy. -} \description{ -Upload a dataset via HTTPS (direct POST) into Galaxy -} -\details{ -This function uses the built-in \code{upload1} tool and performs a -multipart form POST. Large files may still require FTP depending on -the Galaxy server's configuration limits. -} -\examples{ -\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} -history_id <- galaxy_initialize("test upload") -test_file <- tempfile(fileext = ".txt") -test_text <- "This is an example \ntest file." -writeLines(test_text,test_file) - -file_id <- galaxy_upload_https(test_file, history_id) -\dontshow{\}) # examplesIf} +S4 Method for galaxy ftp upload } From a7f462290571e4740709559f52fe45e27c448602 Mon Sep 17 00:00:00 2001 From: JulFrey Date: Thu, 22 Jan 2026 17:27:18 +0100 Subject: [PATCH 2/5] adding s4 methods for single tool invocations --- NAMESPACE | 6 +- R/2025-10-27_JF_R_Galaxy_functions.R | 230 -------------- R/2026-01-21_s4_class_methods.R | 280 ++++++++++++++++++ man/dot-galaxy_download_result.Rd | 53 ---- man/dot-galaxy_poll_tool.Rd | 69 ----- man/dot-galaxy_run_tool.Rd | 65 ---- ...galaxy_download_result-character-method.Rd | 57 ++++ man/galaxy_download_result.Rd | 16 + man/galaxy_poll_tool-character-method.Rd | 31 ++ man/galaxy_poll_tool.Rd | 17 ++ man/galaxy_run_tool-character-method.Rd | 28 ++ man/galaxy_run_tool.Rd | 11 + 12 files changed, 443 insertions(+), 420 deletions(-) delete mode 100644 man/dot-galaxy_download_result.Rd delete mode 100644 man/dot-galaxy_poll_tool.Rd delete mode 100644 man/dot-galaxy_run_tool.Rd create mode 100644 man/galaxy_download_result-character-method.Rd create mode 100644 man/galaxy_download_result.Rd create mode 100644 man/galaxy_poll_tool-character-method.Rd create mode 100644 man/galaxy_poll_tool.Rd create mode 100644 man/galaxy_run_tool-character-method.Rd create mode 100644 man/galaxy_run_tool.Rd diff --git a/NAMESPACE b/NAMESPACE index 90d6e23..041468a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,8 +1,5 @@ # Generated by roxygen2: do not edit by hand -export(.galaxy_download_result) -export(.galaxy_poll_tool) -export(.galaxy_run_tool) export(galaxy) export(galaxy_delete_dataset) export(galaxy_delete_datasets) @@ -18,8 +15,11 @@ export(galaxy_list_invocations) export(galaxy_list_tools) export(galaxy_list_workflows) export(galaxy_set_credentials) +exportMethods(galaxy_download_result) exportMethods(galaxy_initialize) +exportMethods(galaxy_poll_tool) exportMethods(galaxy_poll_workflow) +exportMethods(galaxy_run_tool) exportMethods(galaxy_start_workflow) exportMethods(galaxy_upload_ftp) exportMethods(galaxy_upload_https) diff --git a/R/2025-10-27_JF_R_Galaxy_functions.R b/R/2025-10-27_JF_R_Galaxy_functions.R index 8e8ce93..9d18ad7 100644 --- a/R/2025-10-27_JF_R_Galaxy_functions.R +++ b/R/2025-10-27_JF_R_Galaxy_functions.R @@ -209,58 +209,6 @@ galaxy_get_workflow_inputs <- function( do.call(rbind, inputs) } -#' Download final result dataset from Galaxy -#' -#' @param output_ids Vector of HDA IDs from the workflow outputs the last one will be downloaded -#' @param out_file Path to save the downloaded file -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' -#' @returns The response object from the download request for debugging -#' -#' @examplesIf galaxy_has_key() -#' # iris example -#' tmp_dir <- tempdir() -#' f_name <- "iris.csv" -#' f_path <- paste(tmp_dir, f_name, sep = "\\") -#' write.csv(datasets::iris, f_path, row.names = FALSE) -#' -#' workflows <- galaxy_list_workflows(include_public = TRUE) -#' iris_workflow <- workflows[ -#' workflows$name == "Exploring Iris dataset with statistics and scatterplots", -#' ][1,] -#' -#' history_id <- galaxy_initialize("IRIS") -#' file_id <- galaxy_upload_https(f_path, history_id) -#' invocation_id <- galaxy_start_workflow(file_id, iris_workflow$id, history_id = history_id) -#' dataset_ids <- galaxy_poll_workflow(invocation_id) -#' -#' result_files <- galaxy_get_file_info(dataset_ids$output_ids) -#' head(result_files) -#' galaxy_download_result( -#' list(output_ids = result_files$id[nrow(result_files)]), -#' paste(tmp_dir, result_files$name[nrow(result_files)], sep = "\\") -#' ) -#' -#' @export .galaxy_download_result -.galaxy_download_result <- function(output_ids, out_file = "result.laz", galaxy_url = "https://usegalaxy.eu" ){ - if(!is.null(output_ids$output_ids)){ - output_ids <- output_ids$output_ids - } - - api_key <- Sys.getenv("GALAXY_API_KEY") - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - download_res <- httr::GET( - paste0(galaxy_url, "/api/datasets/", output_ids[length(output_ids)], "/display"), - httr::add_headers(`x-api-key` = api_key), - httr::write_disk(out_file, overwrite = TRUE) - ) - return(download_res) -} - # Helper to trim trailing slash .rtrim <- function(x, char = "/") { sub(paste0(char, "+$"), "", x) @@ -929,184 +877,6 @@ galaxy_get_tool_id <- function(name, return(sort(tool_entries[matches,2], decreasing = TRUE)) } -#' Run a Galaxy tool programmatically -#' -#' @param tool_id Character. Tool identifier to execute. -#' @param history_id Character. History ID where outputs will be stored. -#' @param inputs Named list describing tool inputs exactly as required -#' by the Galaxy tool. The list will be JSON-encoded automatically. -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' -#' @return The job_id for the invocation. -#' -#' @details -#' This sends a POST request to \code{/api/tools} with the required -#' payload. You can check the structure expected for \code{inputs} by -#' inspecting \code{galaxy_get_tool()} or by looking at Galaxy's -#' "Paste Request" feature in the web UI. -#' -#' @examplesIf galaxy_has_key() -#' history_id <- galaxy_initialize("add line") -#' tool <- galaxy_get_tool_id("Add line to file") -#' test_file <- tempfile(fileext = ".txt") -#' test_text <- "This is an example \ntest file." -#' writeLines(test_text,test_file) -#' -#' file_id <- galaxy_upload_https(test_file, history_id) -#' -#' inputs <- galaxy_get_tool(tool) -#' -#' job_id <- galaxy_run_tool(tool_id = tool, history_id = history_id, inputs = list( -#' text_input = "added text", -#' infile = list( -#' src = "hda", -#' id = file_id -#' ), -#' options = "header" # optional, but explicit is good -#' )) -#' -#' result <- galaxy_poll_tool(job_id) -#' -#' test_file_result <- tempfile(fileext = ".txt") -#' galaxy_download_result(list(output_ids = result$outputs$outfile$id), test_file_result) -#' readLines(test_file_result) -#' -#' @export .galaxy_run_tool -.galaxy_run_tool <- function(tool_id, - history_id, - inputs, - galaxy_url = "https://usegalaxy.eu") { - galaxy_url <- .resolve_galaxy_url(galaxy_url) - if (missing(tool_id) || !nzchar(tool_id)) stop("tool_id is required.") - if (missing(history_id) || !nzchar(history_id)) stop("history_id is required.") - if (missing(inputs) || !is.list(inputs)) stop("inputs must be a named list.") - - api_key <- Sys.getenv("GALAXY_API_KEY") - if (!nzchar(api_key)) stop("GALAXY_API_KEY environment variable is not set.") - - payload <- list( - history_id = history_id, - tool_id = tool_id, - inputs = inputs - ) - - res <- httr::POST( - url = paste0(galaxy_url, "/api/tools"), - httr::add_headers( - `x-api-key` = api_key, - `Content-Type` = "application/json" - ), - body = jsonlite::toJSON(payload, auto_unbox = TRUE) - ) - httr::stop_for_status(res) - job <- httr::content(res, as = "parsed", simplifyVector = FALSE) - return(job$jobs[[1]]$id) -} - -#' Wait for a Galaxy job to complete -#' -#' Polls the Galaxy API until a job reaches a terminal state -#' (`ok`, `error`, or `deleted`). -#' -#' @param job_id Character scalar. Encoded Galaxy job ID. -#' @param galaxy_url Character. Base URL of the Galaxy instance -#' (for example \code{"https://usegalaxy.eu"}). -#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. -#' @param poll_interval Numeric. Seconds to wait between status checks. -#' Defaults to 3. -#' @param timeout Numeric. Maximum time to wait in seconds. -#' Defaults to 600 (10 minutes). -#' -#' @return A named list containing the final Galaxy job object. -#' -#' @details -#' Galaxy jobs are executed asynchronously. This function polls -#' `/api/jobs/{job_id}` until the job state becomes `"ok"`. -#' -#' If the job enters state `"error"` or `"deleted"`, an error is raised. -#' If the timeout is exceeded, the function stops with an error. -#' -#' @seealso -#' \itemize{ -#' \item \code{\link{galaxy_run_tool}} for submitting tools -#' } -#' -#' @examplesIf galaxy_has_key() -#' history_id <- galaxy_initialize("add line") -#' tool <- galaxy_get_tool_id("Add line to file") -#' test_file <- tempfile(fileext = ".txt") -#' test_text <- "This is an example \ntest file." -#' writeLines(test_text,test_file) -#' -#' file_id <- galaxy_upload_https(test_file, history_id) -#' -#' inputs <- galaxy_get_tool(tool) -#' -#' job_id <- galaxy_run_tool(tool_id = tool, history_id = history_id, inputs = list( -#' text_input = "added text", -#' infile = list( -#' src = "hda", -#' id = file_id -#' ), -#' options = "header" -#' )) -#' -#' result <- galaxy_poll_tool(job_id) -#' -#' @export .galaxy_poll_tool -.galaxy_poll_tool <- function( - job_id, - galaxy_url = "https://usegalaxy.eu", - poll_interval = 3, - timeout = 600 -) { - - galaxy_url <- .resolve_galaxy_url(galaxy_url) - - if (missing(job_id) || !nzchar(job_id)) { - stop("job_id is required.") - } - - api_key <- Sys.getenv("GALAXY_API_KEY") - if (!nzchar(api_key)) { - stop("GALAXY_API_KEY environment variable is not set.") - } - - start_time <- Sys.time() - - repeat { - - res <- httr::GET( - url = paste0(galaxy_url, "/api/jobs/", job_id), - httr::add_headers(`x-api-key` = api_key) - ) - httr::stop_for_status(res) - - job <- httr::content(res, as = "parsed") - - state <- job$state - - if (state == "ok") { - return(job) - } - - if (state %in% c("error", "deleted")) { - stop( - "Galaxy job failed (state = '", state, "').\n", - if (!is.null(job$stderr)) job$stderr else "" - ) - } - - if (as.numeric(difftime(Sys.time(), start_time, units = "secs")) > timeout) { - stop("Timed out waiting for Galaxy job to finish.") - } - - Sys.sleep(poll_interval) - } -} - #' Get information for one or more Galaxy datasets #' #' Retrieves metadata for one or more Galaxy history datasets (HDAs), diff --git a/R/2026-01-21_s4_class_methods.R b/R/2026-01-21_s4_class_methods.R index 197e672..169b35c 100644 --- a/R/2026-01-21_s4_class_methods.R +++ b/R/2026-01-21_s4_class_methods.R @@ -657,3 +657,283 @@ setMethod("galaxy_poll_workflow", "Galaxy", validObject(x) x }) + +############################# +## File download +############################# + +# internal helper, not exported +#' @keywords internal +#' @noRd +.galaxy_download_result <- function(output_ids, + out_file = "result.laz", + galaxy_url = "https://usegalaxy.eu") { + ## allow a list with element output_ids + if (is.list(output_ids) && "output_ids" %in% names(output_ids)) { + output_ids <- output_ids$output_ids + } + api_key <- Sys.getenv("GALAXY_API_KEY") + galaxy_url <- .resolve_galaxy_url(galaxy_url) + httr::GET( + paste0(galaxy_url, "/api/datasets/", output_ids[length(output_ids)], "/display"), + httr::add_headers(`x-api-key` = api_key), + httr::write_disk(out_file, overwrite = TRUE) + ) +} + +setGeneric("galaxy_download_result", + function(x, + out_file = "result.laz", + galaxy_url = "https://usegalaxy.eu", + ...) + standardGeneric("galaxy_download_result"), + signature = "x") + +#' Download final result dataset from Galaxy +#' +#' `galaxy_download_result()` is an S4 generic. With `x` as a character vector +#' it is treated as a set of HDA output IDs; the last one is downloaded to +#' `out_file` and the `httr` response is returned. With `x` as a [`Galaxy`] +#' object, its `output_dataset_ids` and `galaxy_url` are used and the object is +#' returned invisibly after performing the download. +#' +#' @param x A vector of HDA output IDs (`character`), or a `Galaxy` object. +#' @param out_file Path to save the downloaded file. +#' @param galaxy_url Base URL of the Galaxy instance, used by the character +#' method. If `GALAXY_URL` is set it takes precedence. +#' @return For the character method, the `httr` response object from the +#' download request. For the `Galaxy` method, the (unchanged) `Galaxy` +#' object invisibly. +#' @examplesIf galaxy_has_key() +#' # prepare data +#' tmp_dir <- tempdir() +#' f_path <- file.path(tmp_dir, "iris.csv") +#' write.csv(datasets::iris, f_path, row.names = FALSE) +#' +#' #select workflow +#' workflows <- galaxy_list_workflows(include_public = TRUE) +#' iris_workflow <- workflows[workflows$name == +#' "Exploring Iris dataset with statistics and scatterplots", ][1, ] +#' # upload and run workflow +#' gxy <- galaxy(history_name = "IRIS") |> +#' galaxy_initialize() |> +#' galaxy_upload_https(f_path) |> +#' galaxy_start_workflow(workflow_id = iris_workflow$id) |> +#' galaxy_poll_workflow() |> +#' galaxy_download_result(out_file = file.path(tmp_dir, result_files$name[nrow(result_files)])) +#' +#' # inspect the outputs +#' result_files <- galaxy_get_file_info(gxy@output_dataset_ids) +#' head(result_files) +#' +#' @export +setMethod("galaxy_download_result", "character", + function(x, + out_file = "result.laz", + galaxy_url = "https://usegalaxy.eu", + ...) { + .galaxy_download_result(output_ids = x, + out_file = out_file, + galaxy_url = galaxy_url) + }) + +#' S4 download function for workflow or tool outputs +#' @rdname galaxy_download_result +#' @export +setMethod("galaxy_download_result", "Galaxy", + function(x, + out_file = "result.laz", + ...) { + .galaxy_download_result(output_ids = x@output_dataset_ids, + out_file = out_file, + galaxy_url = x@galaxy_url) + invisible(x) + }) + + +############################# +## Tool invocation and polling +############################# + +#' Helper function for single tool invocations +#' @keywords internal +#' @noRd +.galaxy_run_tool <- function(tool_id, history_id, inputs, + galaxy_url = "https://usegalaxy.eu") { + galaxy_url <- .resolve_galaxy_url(galaxy_url) + if (missing(tool_id) || !nzchar(tool_id)) stop("tool_id is required.") + if (missing(history_id) || !nzchar(history_id)) stop("history_id is required.") + if (missing(inputs) || !is.list(inputs)) stop("inputs must be a named list.") + + api_key <- Sys.getenv("GALAXY_API_KEY") + if (!nzchar(api_key)) stop("GALAXY_API_KEY environment variable is not set.") + + payload <- list( + history_id = history_id, + tool_id = tool_id, + inputs = inputs + ) + + res <- httr::POST( + url = paste0(galaxy_url, "/api/tools"), + httr::add_headers( + `x-api-key` = api_key, + `Content-Type` = "application/json" + ), + body = jsonlite::toJSON(payload, auto_unbox = TRUE) + ) + httr::stop_for_status(res) + job <- httr::content(res, as = "parsed", simplifyVector = FALSE) + return(job$jobs[[1]]$id) +} + +setGeneric("galaxy_run_tool", + function(x, + tool_id, + inputs, + galaxy_url = "https://usegalaxy.eu", + ...) + standardGeneric("galaxy_run_tool"), + signature = "x") + +#' Run a Galaxy tool programmatically +#' +#' `galaxy_run_tool()` is an S4 generic. With `x` as a character vector it is +#' treated as a history ID; the specified tool is invoked in that history and +#' the job ID is returned. With `x` as a [`Galaxy`] object, the history ID and +#' URL are taken from the object and the object is updated with the job ID. +#' +#' @param x A history ID (`character`) or a `Galaxy` object. +#' @param tool_id Tool identifier to execute. +#' @param inputs Named list of tool inputs. +#' @param galaxy_url Base URL of the Galaxy instance, used by the character +#' method. +#' @return For the character method, a job ID; for the `Galaxy` method, the +#' modified `Galaxy` object. +#' @export +setMethod("galaxy_run_tool", "character", + function(x, tool_id, inputs, + galaxy_url = "https://usegalaxy.eu", ...) { + .galaxy_run_tool(tool_id = tool_id, + history_id = x, + inputs = inputs, + galaxy_url = galaxy_url) + }) + +#' S4 Method for single tool invocation +#' @rdname galaxy_run_tool +#' @export +setMethod("galaxy_run_tool", "Galaxy", + function(x, tool_id, inputs, ...) { + inp <- if (!is.null(inputs)) inputs else if (length(x@inputs) > 0) x@inputs else NULL + job_id <- .galaxy_run_tool(tool_id = tool_id, + history_id = x@history_id, + inputs = inp, + galaxy_url = x@galaxy_url) + x@invocation_id <- job_id + validObject(x) + x + }) + +#' Helper function for tool polling +#' @keywords internal +#' @noRd +.galaxy_poll_tool <- function(invocation_id, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 3, + timeout = 600) { + galaxy_url <- .resolve_galaxy_url(galaxy_url) + + if (missing(invocation_id) || !nzchar(invocation_id)) { + stop("invocation_id is required.") + } + + api_key <- Sys.getenv("GALAXY_API_KEY") + if (!nzchar(api_key)) { + stop("GALAXY_API_KEY environment variable is not set.") + } + + start_time <- Sys.time() + + repeat { + + res <- httr::GET( + url = paste0(galaxy_url, "/api/jobs/", invocation_id), + httr::add_headers(`x-api-key` = api_key) + ) + httr::stop_for_status(res) + + job <- httr::content(res, as = "parsed") + + state <- job$state + + if (state == "ok") { + return(job) + } + + if (state %in% c("error", "deleted")) { + stop( + "Galaxy job failed (state = '", state, "').\n", + if (!is.null(job$stderr)) job$stderr else "" + ) + } + + if (as.numeric(difftime(Sys.time(), start_time, units = "secs")) > timeout) { + stop("Timed out waiting for Galaxy job to finish.") + } + + Sys.sleep(poll_interval) + } +} + +setGeneric("galaxy_poll_tool", + function(x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 3, + timeout = 600, + ...) + standardGeneric("galaxy_poll_tool"), + signature = "x") + +#' Wait for a Galaxy job to complete +#' +#' @param x A job ID (`character`) or a `Galaxy` object. +#' @param galaxy_url Base URL of the Galaxy instance, used by the character +#' method. +#' @param poll_interval Seconds between status checks. +#' @param timeout Maximum time to wait in seconds. +#' @return For the character method, the final job object; for the `Galaxy` +#' method, the modified `Galaxy` object. +#' @export +setMethod("galaxy_poll_tool", "character", + function(x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 3, + timeout = 600, + ...) { + .galaxy_poll_tool(invocation_id = x, + galaxy_url = galaxy_url, + poll_interval = poll_interval, + timeout = timeout) + }) + +#' S4 method to poll the status of a tool invocation +#' @rdname galaxy_poll_tool +#' @export +setMethod("galaxy_poll_tool", "Galaxy", + function(x, + poll_interval = 3, + timeout = 600, + ...) { + job <- .galaxy_poll_tool(invocation_id = x@invocation_id, + galaxy_url = x@galaxy_url, + poll_interval = poll_interval, + timeout = timeout) + if (!is.null(job$outputs)) { + out_ids <- vapply(job$outputs, function(o) o$id, character(1L)) + x@output_dataset_ids <- out_ids + } + x@state <- if (identical(job$state, "ok")) "success" else "error" + validObject(x) + x + }) diff --git a/man/dot-galaxy_download_result.Rd b/man/dot-galaxy_download_result.Rd deleted file mode 100644 index 1c7b0d3..0000000 --- a/man/dot-galaxy_download_result.Rd +++ /dev/null @@ -1,53 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{.galaxy_download_result} -\alias{.galaxy_download_result} -\title{Download final result dataset from Galaxy} -\usage{ -.galaxy_download_result( - output_ids, - out_file = "result.laz", - galaxy_url = "https://usegalaxy.eu" -) -} -\arguments{ -\item{output_ids}{Vector of HDA IDs from the workflow outputs the last one will be downloaded} - -\item{out_file}{Path to save the downloaded file} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} -} -\value{ -The response object from the download request for debugging -} -\description{ -Download final result dataset from Galaxy -} -\examples{ -\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} -# iris example -tmp_dir <- tempdir() -f_name <- "iris.csv" -f_path <- paste(tmp_dir, f_name, sep = "\\\\") -write.csv(datasets::iris, f_path, row.names = FALSE) - -workflows <- galaxy_list_workflows(include_public = TRUE) -iris_workflow <- workflows[ - workflows$name == "Exploring Iris dataset with statistics and scatterplots", -][1,] - -history_id <- galaxy_initialize("IRIS") -file_id <- galaxy_upload_https(f_path, history_id) -invocation_id <- galaxy_start_workflow(file_id, iris_workflow$id, history_id = history_id) -dataset_ids <- galaxy_poll_workflow(invocation_id) - -result_files <- galaxy_get_file_info(dataset_ids$output_ids) -head(result_files) -galaxy_download_result( - list(output_ids = result_files$id[nrow(result_files)]), - paste(tmp_dir, result_files$name[nrow(result_files)], sep = "\\\\") -) -\dontshow{\}) # examplesIf} -} diff --git a/man/dot-galaxy_poll_tool.Rd b/man/dot-galaxy_poll_tool.Rd deleted file mode 100644 index b2f290d..0000000 --- a/man/dot-galaxy_poll_tool.Rd +++ /dev/null @@ -1,69 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{.galaxy_poll_tool} -\alias{.galaxy_poll_tool} -\title{Wait for a Galaxy job to complete} -\usage{ -.galaxy_poll_tool( - job_id, - galaxy_url = "https://usegalaxy.eu", - poll_interval = 3, - timeout = 600 -) -} -\arguments{ -\item{job_id}{Character scalar. Encoded Galaxy job ID.} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} - -\item{poll_interval}{Numeric. Seconds to wait between status checks. -Defaults to 3.} - -\item{timeout}{Numeric. Maximum time to wait in seconds. -Defaults to 600 (10 minutes).} -} -\value{ -A named list containing the final Galaxy job object. -} -\description{ -Polls the Galaxy API until a job reaches a terminal state -(\code{ok}, \code{error}, or \code{deleted}). -} -\details{ -Galaxy jobs are executed asynchronously. This function polls -\verb{/api/jobs/\{job_id\}} until the job state becomes \code{"ok"}. - -If the job enters state \code{"error"} or \code{"deleted"}, an error is raised. -If the timeout is exceeded, the function stops with an error. -} -\examples{ -\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} -history_id <- galaxy_initialize("add line") -tool <- galaxy_get_tool_id("Add line to file") -test_file <- tempfile(fileext = ".txt") -test_text <- "This is an example \ntest file." -writeLines(test_text,test_file) - -file_id <- galaxy_upload_https(test_file, history_id) - -inputs <- galaxy_get_tool(tool) - -job_id <- galaxy_run_tool(tool_id = tool, history_id = history_id, inputs = list( - text_input = "added text", - infile = list( - src = "hda", - id = file_id - ), - options = "header" -)) - -result <- galaxy_poll_tool(job_id) -\dontshow{\}) # examplesIf} -} -\seealso{ -\itemize{ -\item \code{\link{galaxy_run_tool}} for submitting tools -} -} diff --git a/man/dot-galaxy_run_tool.Rd b/man/dot-galaxy_run_tool.Rd deleted file mode 100644 index f70fe0f..0000000 --- a/man/dot-galaxy_run_tool.Rd +++ /dev/null @@ -1,65 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R -\name{.galaxy_run_tool} -\alias{.galaxy_run_tool} -\title{Run a Galaxy tool programmatically} -\usage{ -.galaxy_run_tool( - tool_id, - history_id, - inputs, - galaxy_url = "https://usegalaxy.eu" -) -} -\arguments{ -\item{tool_id}{Character. Tool identifier to execute.} - -\item{history_id}{Character. History ID where outputs will be stored.} - -\item{inputs}{Named list describing tool inputs exactly as required -by the Galaxy tool. The list will be JSON-encoded automatically.} - -\item{galaxy_url}{Character. Base URL of the Galaxy instance -(for example \code{"https://usegalaxy.eu"}). -If the environment variable \code{GALAXY_URL} is set, it takes precedence.} -} -\value{ -The job_id for the invocation. -} -\description{ -Run a Galaxy tool programmatically -} -\details{ -This sends a POST request to \code{/api/tools} with the required -payload. You can check the structure expected for \code{inputs} by -inspecting \code{galaxy_get_tool()} or by looking at Galaxy's -"Paste Request" feature in the web UI. -} -\examples{ -\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} -history_id <- galaxy_initialize("add line") -tool <- galaxy_get_tool_id("Add line to file") -test_file <- tempfile(fileext = ".txt") -test_text <- "This is an example \ntest file." -writeLines(test_text,test_file) - -file_id <- galaxy_upload_https(test_file, history_id) - -inputs <- galaxy_get_tool(tool) - -job_id <- galaxy_run_tool(tool_id = tool, history_id = history_id, inputs = list( - text_input = "added text", - infile = list( - src = "hda", - id = file_id - ), - options = "header" # optional, but explicit is good -)) - -result <- galaxy_poll_tool(job_id) - -test_file_result <- tempfile(fileext = ".txt") -galaxy_download_result(list(output_ids = result$outputs$outfile$id), test_file_result) -readLines(test_file_result) -\dontshow{\}) # examplesIf} -} diff --git a/man/galaxy_download_result-character-method.Rd b/man/galaxy_download_result-character-method.Rd new file mode 100644 index 0000000..7427b12 --- /dev/null +++ b/man/galaxy_download_result-character-method.Rd @@ -0,0 +1,57 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_download_result,character-method} +\alias{galaxy_download_result,character-method} +\title{Download final result dataset from Galaxy} +\usage{ +\S4method{galaxy_download_result}{character}( + x, + out_file = "result.laz", + galaxy_url = "https://usegalaxy.eu", + ... +) +} +\arguments{ +\item{x}{A vector of HDA output IDs (\code{character}), or a \code{Galaxy} object.} + +\item{out_file}{Path to save the downloaded file.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the character +method. If \code{GALAXY_URL} is set it takes precedence.} +} +\value{ +For the character method, the \code{httr} response object from the +download request. For the \code{Galaxy} method, the (unchanged) \code{Galaxy} +object invisibly. +} +\description{ +\code{galaxy_download_result()} is an S4 generic. With \code{x} as a character vector +it is treated as a set of HDA output IDs; the last one is downloaded to +\code{out_file} and the \code{httr} response is returned. With \code{x} as a \code{\link{Galaxy}} +object, its \code{output_dataset_ids} and \code{galaxy_url} are used and the object is +returned invisibly after performing the download. +} +\examples{ +\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} +# prepare data +tmp_dir <- tempdir() +f_path <- file.path(tmp_dir, "iris.csv") +write.csv(datasets::iris, f_path, row.names = FALSE) + +#select workflow +workflows <- galaxy_list_workflows(include_public = TRUE) +iris_workflow <- workflows[workflows$name == + "Exploring Iris dataset with statistics and scatterplots", ][1, ] +# upload and run workflow +gxy <- galaxy(history_name = "IRIS") |> + galaxy_initialize() |> + galaxy_upload_https(f_path) |> + galaxy_start_workflow(workflow_id = iris_workflow$id) |> + galaxy_poll_workflow() |> + galaxy_download_result(out_file = file.path(tmp_dir, result_files$name[nrow(result_files)])) + +# inspect the outputs +result_files <- galaxy_get_file_info(gxy@output_dataset_ids) +head(result_files) +\dontshow{\}) # examplesIf} +} diff --git a/man/galaxy_download_result.Rd b/man/galaxy_download_result.Rd new file mode 100644 index 0000000..0be158f --- /dev/null +++ b/man/galaxy_download_result.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_download_result,Galaxy-method} +\alias{galaxy_download_result,Galaxy-method} +\title{S4 download function for workflow or tool outputs} +\usage{ +\S4method{galaxy_download_result}{Galaxy}( + x, + out_file = "result.laz", + galaxy_url = "https://usegalaxy.eu", + ... +) +} +\description{ +S4 download function for workflow or tool outputs +} diff --git a/man/galaxy_poll_tool-character-method.Rd b/man/galaxy_poll_tool-character-method.Rd new file mode 100644 index 0000000..b86f63d --- /dev/null +++ b/man/galaxy_poll_tool-character-method.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_poll_tool,character-method} +\alias{galaxy_poll_tool,character-method} +\title{Wait for a Galaxy job to complete} +\usage{ +\S4method{galaxy_poll_tool}{character}( + x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 3, + timeout = 600, + ... +) +} +\arguments{ +\item{x}{A job ID (\code{character}) or a \code{Galaxy} object.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the character +method.} + +\item{poll_interval}{Seconds between status checks.} + +\item{timeout}{Maximum time to wait in seconds.} +} +\value{ +For the character method, the final job object; for the \code{Galaxy} +method, the modified \code{Galaxy} object. +} +\description{ +Wait for a Galaxy job to complete +} diff --git a/man/galaxy_poll_tool.Rd b/man/galaxy_poll_tool.Rd new file mode 100644 index 0000000..a92b38c --- /dev/null +++ b/man/galaxy_poll_tool.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_poll_tool,Galaxy-method} +\alias{galaxy_poll_tool,Galaxy-method} +\title{S4 method to poll the status of a tool invocation} +\usage{ +\S4method{galaxy_poll_tool}{Galaxy}( + x, + galaxy_url = "https://usegalaxy.eu", + poll_interval = 3, + timeout = 600, + ... +) +} +\description{ +S4 method to poll the status of a tool invocation +} diff --git a/man/galaxy_run_tool-character-method.Rd b/man/galaxy_run_tool-character-method.Rd new file mode 100644 index 0000000..3bca530 --- /dev/null +++ b/man/galaxy_run_tool-character-method.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_run_tool,character-method} +\alias{galaxy_run_tool,character-method} +\title{Run a Galaxy tool programmatically} +\usage{ +\S4method{galaxy_run_tool}{character}(x, tool_id, inputs, galaxy_url = "https://usegalaxy.eu", ...) +} +\arguments{ +\item{x}{A history ID (\code{character}) or a \code{Galaxy} object.} + +\item{tool_id}{Tool identifier to execute.} + +\item{inputs}{Named list of tool inputs.} + +\item{galaxy_url}{Base URL of the Galaxy instance, used by the character +method.} +} +\value{ +For the character method, a job ID; for the \code{Galaxy} method, the +modified \code{Galaxy} object. +} +\description{ +\code{galaxy_run_tool()} is an S4 generic. With \code{x} as a character vector it is +treated as a history ID; the specified tool is invoked in that history and +the job ID is returned. With \code{x} as a \code{\link{Galaxy}} object, the history ID and +URL are taken from the object and the object is updated with the job ID. +} diff --git a/man/galaxy_run_tool.Rd b/man/galaxy_run_tool.Rd new file mode 100644 index 0000000..4cc8152 --- /dev/null +++ b/man/galaxy_run_tool.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2026-01-21_s4_class_methods.R +\name{galaxy_run_tool,Galaxy-method} +\alias{galaxy_run_tool,Galaxy-method} +\title{S4 Method for single tool invocation} +\usage{ +\S4method{galaxy_run_tool}{Galaxy}(x, tool_id, inputs, galaxy_url = "https://usegalaxy.eu", ...) +} +\description{ +S4 Method for single tool invocation +} From 1044db751785c98cbc788aaf04fcef1d0384792b Mon Sep 17 00:00:00 2001 From: JulFrey Date: Thu, 22 Jan 2026 17:48:47 +0100 Subject: [PATCH 3/5] debug run_tool --- R/2026-01-21_s4_class_methods.R | 4 ++-- man/galaxy_run_tool-character-method.Rd | 8 +++++++- man/galaxy_run_tool.Rd | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/R/2026-01-21_s4_class_methods.R b/R/2026-01-21_s4_class_methods.R index 169b35c..2a678ba 100644 --- a/R/2026-01-21_s4_class_methods.R +++ b/R/2026-01-21_s4_class_methods.R @@ -790,7 +790,7 @@ setMethod("galaxy_download_result", "Galaxy", setGeneric("galaxy_run_tool", function(x, tool_id, - inputs, + inputs = NULL, galaxy_url = "https://usegalaxy.eu", ...) standardGeneric("galaxy_run_tool"), @@ -824,7 +824,7 @@ setMethod("galaxy_run_tool", "character", #' @rdname galaxy_run_tool #' @export setMethod("galaxy_run_tool", "Galaxy", - function(x, tool_id, inputs, ...) { + function(x, tool_id, inputs = NULL, ...) { inp <- if (!is.null(inputs)) inputs else if (length(x@inputs) > 0) x@inputs else NULL job_id <- .galaxy_run_tool(tool_id = tool_id, history_id = x@history_id, diff --git a/man/galaxy_run_tool-character-method.Rd b/man/galaxy_run_tool-character-method.Rd index 3bca530..7bd48f7 100644 --- a/man/galaxy_run_tool-character-method.Rd +++ b/man/galaxy_run_tool-character-method.Rd @@ -4,7 +4,13 @@ \alias{galaxy_run_tool,character-method} \title{Run a Galaxy tool programmatically} \usage{ -\S4method{galaxy_run_tool}{character}(x, tool_id, inputs, galaxy_url = "https://usegalaxy.eu", ...) +\S4method{galaxy_run_tool}{character}( + x, + tool_id, + inputs = NULL, + galaxy_url = "https://usegalaxy.eu", + ... +) } \arguments{ \item{x}{A history ID (\code{character}) or a \code{Galaxy} object.} diff --git a/man/galaxy_run_tool.Rd b/man/galaxy_run_tool.Rd index 4cc8152..a15304f 100644 --- a/man/galaxy_run_tool.Rd +++ b/man/galaxy_run_tool.Rd @@ -4,7 +4,13 @@ \alias{galaxy_run_tool,Galaxy-method} \title{S4 Method for single tool invocation} \usage{ -\S4method{galaxy_run_tool}{Galaxy}(x, tool_id, inputs, galaxy_url = "https://usegalaxy.eu", ...) +\S4method{galaxy_run_tool}{Galaxy}( + x, + tool_id, + inputs = NULL, + galaxy_url = "https://usegalaxy.eu", + ... +) } \description{ S4 Method for single tool invocation From acc7b9d26150b80775c4e32351495a1c705a8ea3 Mon Sep 17 00:00:00 2001 From: JulFrey Date: Fri, 23 Jan 2026 13:26:09 +0100 Subject: [PATCH 4/5] add better tool and workflow input handling and validation --- NAMESPACE | 1 + R/2025-10-27_JF_R_Galaxy_functions.R | 27 +++ R/2026-01-21_s4_class_methods.R | 213 ++++++++++++------ man/galaxy_get_workflow.Rd | 28 +++ man/galaxy_run_tool-character-method.Rd | 1 + man/galaxy_run_tool.Rd | 1 + man/galaxy_start_workflow-character-method.Rd | 8 +- man/galaxy_start_workflow.Rd | 2 +- 8 files changed, 204 insertions(+), 77 deletions(-) create mode 100644 man/galaxy_get_workflow.Rd diff --git a/NAMESPACE b/NAMESPACE index 041468a..4fb6067 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ export(galaxy_delete_datasets) export(galaxy_get_file_info) export(galaxy_get_tool) export(galaxy_get_tool_id) +export(galaxy_get_workflow) export(galaxy_get_workflow_inputs) export(galaxy_has_key) export(galaxy_history_size) diff --git a/R/2025-10-27_JF_R_Galaxy_functions.R b/R/2025-10-27_JF_R_Galaxy_functions.R index 9d18ad7..eab8e8e 100644 --- a/R/2025-10-27_JF_R_Galaxy_functions.R +++ b/R/2025-10-27_JF_R_Galaxy_functions.R @@ -115,6 +115,33 @@ galaxy_list_workflows <- function( ) } + +#' Receive workflow metadata from the API +#' +#' @param workflow_id Character. Galaxy workflow ID. +#' @param galaxy_url Character. Base URL of the Galaxy instance +#' (for example \code{"https://usegalaxy.eu"}). +#' If the environment variable \code{GALAXY_URL} is set, it takes precedence. +#' +#' @returns a structured list with all metadata +#' @export +#' +#' @examplesIf nzchar(Sys.getenv("GALAXY_API_KEY")) +#' \dontrun{ +#' galaxy_get_workflow("f2db41e1fa331b3e") +#' } +galaxy_get_workflow <- function(workflow_id, + galaxy_url = "https://usegalaxy.eu") { + galaxy_url <- .resolve_galaxy_url(galaxy_url) + api_key <- Sys.getenv("GALAXY_API_KEY") + url <- paste0(galaxy_url, "/api/workflows/", workflow_id) + res <- httr::GET(url, + httr::add_headers(`x-api-key` = api_key), + query = list(io_details = "true")) + httr::stop_for_status(res) + httr::content(res, "parsed") +} + #' Retrieve input definitions for a Galaxy workflow #' #' Retrieves and summarizes the input steps required by a Galaxy workflow. diff --git a/R/2026-01-21_s4_class_methods.R b/R/2026-01-21_s4_class_methods.R index 2a678ba..eb726da 100644 --- a/R/2026-01-21_s4_class_methods.R +++ b/R/2026-01-21_s4_class_methods.R @@ -427,55 +427,74 @@ setMethod("galaxy_upload_https", "Galaxy", ## Workflow invocation and polling ######################### -# internal helper, not exported +#' Internal helper to build workflow inputs #' @keywords internal #' @noRd -.galaxy_start_workflow <- function( - history_id, - dataset_id, - workflow_id, - inputs = NULL, - galaxy_url = "https://usegalaxy.eu" -) { - if (missing(workflow_id) || !nzchar(workflow_id)) { - stop("workflow_id is required.") +.galaxy_build_wf_inputs <- function(wf_def, dataset_id = NULL, args = list()) { + if (is.null(args)) args <- list() + wf_inputs <- wf_def$inputs + + # if we have a dataset_id and nothing provided, bind it to the first input + if (!is.null(dataset_id) && !length(args) && length(wf_inputs)) { + first <- names(wf_inputs)[1L] + args[[first]] <- list(src = "hda", id = dataset_id) } - galaxy_url <- .resolve_galaxy_url(galaxy_url) - api_key <- Sys.getenv("GALAXY_API_KEY") - if (!nzchar(api_key)) { - stop("GALAXY_API_KEY environment variable is not set.") + args +} + +#' Internal helper to validate workflow inputs +#' @keywords internal +#' @noRd +.galaxy_validate_wf_inputs <- function(wf_def, inputs) { + expected <- names(wf_def$inputs) + unknown <- setdiff(names(inputs), expected) + if (length(unknown)) { + stop("Unknown workflow inputs: ", paste(unknown, collapse = ", ")) } - if (is.null(inputs)) { - if (missing(dataset_id) || !nzchar(dataset_id)) { - stop("Either dataset_id or inputs must be provided.") - } - inputs <- setNames(list(list(src = "hda", id = dataset_id)), "0") + has_default <- function(inp) !is.null(inp$value) + req_idx <- !vapply(wf_def$inputs, + function(inp) isTRUE(inp$optional) || has_default(inp), + logical(1L)) + required <- expected[req_idx] + missing <- setdiff(required, names(inputs)) + if (length(missing)) { + stop("Missing required workflow inputs: ", paste(missing, collapse = ", ")) } - run_body <- list(inputs = inputs) - if (!is.na(history_id)) { - run_body$history_id <- history_id + invisible(TRUE) +} + +# internal helper, not exported +#' @keywords internal +#' @noRd +.galaxy_start_workflow <- function(history_id, + workflow_id, + inputs = NULL, + dataset_id = NULL, + galaxy_url = "https://usegalaxy.eu") { + if (missing(workflow_id) || !nzchar(workflow_id)) { + stop("workflow_id is required.") } - run_url <- paste0(galaxy_url, "/api/workflows/", workflow_id, "/invocations") - res <- httr::POST( - run_url, - httr::add_headers( - `x-api-key` = api_key, - `Content-Type` = "application/json" - ), - body = jsonlite::toJSON(run_body, auto_unbox = TRUE) - ) + wf_def <- galaxy_get_workflow(workflow_id, galaxy_url = galaxy_url) + built_in <- .galaxy_build_wf_inputs(wf_def, dataset_id = dataset_id, args = inputs) + .galaxy_validate_wf_inputs(wf_def, built_in) + + run_body <- list(inputs = built_in, history_id = history_id) + run_url <- paste0(.resolve_galaxy_url(galaxy_url), + "/api/workflows/", workflow_id, "/invocations") + res <- httr::POST(run_url, + httr::add_headers(`x-api-key` = Sys.getenv("GALAXY_API_KEY"), + `Content-Type` = "application/json"), + body = jsonlite::toJSON(run_body, auto_unbox = TRUE)) httr::stop_for_status(res) - inv <- httr::content(res, as = "parsed") - message("Workflow invocation ID: ", inv$id) - inv$id + httr::content(res, "parsed")$id } ## generic dispatches on the first argument setGeneric("galaxy_start_workflow", function(x, workflow_id, - dataset_id, inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ...) standardGeneric("galaxy_start_workflow"), @@ -503,37 +522,27 @@ setGeneric("galaxy_start_workflow", #' `Galaxy` method, the modified `Galaxy` object. #' @export setMethod("galaxy_start_workflow", "character", - function(x, - workflow_id, - dataset_id, - inputs = NULL, - galaxy_url = "https://usegalaxy.eu", - ...) { + function(x, workflow_id, inputs = NULL, dataset_id = NULL, + galaxy_url = "https://usegalaxy.eu", ...) { .galaxy_start_workflow(history_id = x, - dataset_id = dataset_id, workflow_id = workflow_id, - inputs = inputs, - galaxy_url = galaxy_url) + inputs = inputs, + dataset_id = dataset_id, + galaxy_url = galaxy_url) }) #' S4 function to start a galaxy workflow #' @rdname galaxy_start_workflow #' @export setMethod("galaxy_start_workflow", "Galaxy", - function(x, - workflow_id, - dataset_id, - inputs = NULL, - ...) { - hid <- x@history_id - ds <- if (!missing(dataset_id)) dataset_id else x@input_dataset_id - inp <- if (!is.null(inputs)) inputs else if (length(x@inputs) > 0) x@inputs else NULL - inv <- .galaxy_start_workflow(history_id = hid, - dataset_id = ds, + function(x, workflow_id, inputs = NULL, dataset_id = NULL, ...) { + inv <- .galaxy_start_workflow(history_id = x@history_id, workflow_id = workflow_id, - inputs = inp, - galaxy_url = x@galaxy_url) + inputs = if (is.null(inputs)) x@inputs else inputs, + dataset_id = if (is.null(dataset_id)) x@input_dataset_id else dataset_id, + galaxy_url = x@galaxy_url) x@invocation_id <- inv + x@state <- "pending" validObject(x) x }) @@ -755,42 +764,97 @@ setMethod("galaxy_download_result", "Galaxy", ## Tool invocation and polling ############################# +# build an inputs list from a tool definition, a dataset id and a user list +#' @keywords internal +#' @noRd +.galaxy_build_tool_inputs <- function(tool_def, + dataset_id = NULL, + args = list()) { + if (is.null(args)) args <- list() + + ## find the first data parameter in the tool definition + param_defs <- tool_def$inputs + data_param <- NULL + if (!is.null(dataset_id)) { + for (p in param_defs) { + if (!is.null(p$type) && p$type == "data") { + data_param <- p$name + break + } + } + } + + ## if no data input supplied and we have a dataset id, insert it + if (!is.null(data_param) && is.null(args[[data_param]])) { + args[[data_param]] <- list(src = "hda", id = dataset_id) + } + + args +} + +# very basic name‑based validation +#' @keywords internal +#' @noRd +.galaxy_validate_tool_inputs <- function(tool_def, inputs) { + expected <- vapply(tool_def$inputs, function(p) p$name, character(1L)) + unknown <- setdiff(names(inputs), expected) + if (length(unknown)) { + stop("Unknown tool inputs: ", paste(unknown, collapse = ", ")) + } + + has_default <- function(p) { + !is.null(p$value) && !(is.list(p$value) && length(p$value) == 0L) + } + + req_idx <- !vapply(tool_def$inputs, + function(p) isTRUE(p$optional) || has_default(p), + logical(1L)) + required <- expected[req_idx] + missing <- setdiff(required, names(inputs)) + if (length(missing)) { + stop("Missing required inputs: ", paste(missing, collapse = ", ")) + } + invisible(TRUE) +} + #' Helper function for single tool invocations #' @keywords internal #' @noRd -.galaxy_run_tool <- function(tool_id, history_id, inputs, +.galaxy_run_tool <- function(tool_id, + history_id, + inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu") { galaxy_url <- .resolve_galaxy_url(galaxy_url) if (missing(tool_id) || !nzchar(tool_id)) stop("tool_id is required.") if (missing(history_id) || !nzchar(history_id)) stop("history_id is required.") - if (missing(inputs) || !is.list(inputs)) stop("inputs must be a named list.") + + tool_def <- galaxy_get_tool(tool_id, galaxy_url = galaxy_url, tool_version = NULL) + built <- .galaxy_build_tool_inputs(tool_def, dataset_id = dataset_id, args = inputs) + .galaxy_validate_tool_inputs(tool_def, built) api_key <- Sys.getenv("GALAXY_API_KEY") if (!nzchar(api_key)) stop("GALAXY_API_KEY environment variable is not set.") - - payload <- list( - history_id = history_id, - tool_id = tool_id, - inputs = inputs - ) + payload <- list(history_id = history_id, tool_id = tool_id, inputs = built) res <- httr::POST( url = paste0(galaxy_url, "/api/tools"), httr::add_headers( - `x-api-key` = api_key, + `x-api-key` = api_key, `Content-Type` = "application/json" ), body = jsonlite::toJSON(payload, auto_unbox = TRUE) ) httr::stop_for_status(res) job <- httr::content(res, as = "parsed", simplifyVector = FALSE) - return(job$jobs[[1]]$id) + job$jobs[[1]]$id } setGeneric("galaxy_run_tool", function(x, tool_id, - inputs = NULL, + inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ...) standardGeneric("galaxy_run_tool"), @@ -812,11 +876,12 @@ setGeneric("galaxy_run_tool", #' modified `Galaxy` object. #' @export setMethod("galaxy_run_tool", "character", - function(x, tool_id, inputs, + function(x, tool_id, inputs = NULL, dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ...) { .galaxy_run_tool(tool_id = tool_id, history_id = x, inputs = inputs, + dataset_id = dataset_id, galaxy_url = galaxy_url) }) @@ -824,11 +889,15 @@ setMethod("galaxy_run_tool", "character", #' @rdname galaxy_run_tool #' @export setMethod("galaxy_run_tool", "Galaxy", - function(x, tool_id, inputs = NULL, ...) { - inp <- if (!is.null(inputs)) inputs else if (length(x@inputs) > 0) x@inputs else NULL - job_id <- .galaxy_run_tool(tool_id = tool_id, + function(x, + tool_id, + inputs = NULL, + dataset_id = NULL, + ...) { + job_id <- .galaxy_run_tool(tool_id = tool_id, history_id = x@history_id, - inputs = inp, + inputs = if (is.null(inputs)) x@inputs else inputs, + dataset_id = if (is.null(dataset_id)) x@input_dataset_id else dataset_id, galaxy_url = x@galaxy_url) x@invocation_id <- job_id validObject(x) diff --git a/man/galaxy_get_workflow.Rd b/man/galaxy_get_workflow.Rd new file mode 100644 index 0000000..003c2b3 --- /dev/null +++ b/man/galaxy_get_workflow.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/2025-10-27_JF_R_Galaxy_functions.R +\name{galaxy_get_workflow} +\alias{galaxy_get_workflow} +\title{Receive workflow metadata from the API} +\usage{ +galaxy_get_workflow(workflow_id, galaxy_url = "https://usegalaxy.eu") +} +\arguments{ +\item{workflow_id}{Character. Galaxy workflow ID.} + +\item{galaxy_url}{Character. Base URL of the Galaxy instance +(for example \code{"https://usegalaxy.eu"}). +If the environment variable \code{GALAXY_URL} is set, it takes precedence.} +} +\value{ +a structured list with all metadata +} +\description{ +Receive workflow metadata from the API +} +\examples{ +\dontshow{if (nzchar(Sys.getenv("GALAXY_API_KEY"))) withAutoprint(\{ # examplesIf} +\dontrun{ +galaxy_get_workflow("f2db41e1fa331b3e") +} +\dontshow{\}) # examplesIf} +} diff --git a/man/galaxy_run_tool-character-method.Rd b/man/galaxy_run_tool-character-method.Rd index 7bd48f7..bf84563 100644 --- a/man/galaxy_run_tool-character-method.Rd +++ b/man/galaxy_run_tool-character-method.Rd @@ -8,6 +8,7 @@ x, tool_id, inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ... ) diff --git a/man/galaxy_run_tool.Rd b/man/galaxy_run_tool.Rd index a15304f..d524d49 100644 --- a/man/galaxy_run_tool.Rd +++ b/man/galaxy_run_tool.Rd @@ -8,6 +8,7 @@ x, tool_id, inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ... ) diff --git a/man/galaxy_start_workflow-character-method.Rd b/man/galaxy_start_workflow-character-method.Rd index c53040a..d186144 100644 --- a/man/galaxy_start_workflow-character-method.Rd +++ b/man/galaxy_start_workflow-character-method.Rd @@ -7,8 +7,8 @@ \S4method{galaxy_start_workflow}{character}( x, workflow_id, - dataset_id, inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ... ) @@ -19,13 +19,13 @@ method.} \item{workflow_id}{Character. Galaxy workflow ID.} +\item{inputs}{Named list. Optional workflow input mapping; keys are workflow +input step IDs, values are lists describing datasets/parameters.} + \item{dataset_id}{Character. ID of the input dataset (HDA). Ignored if \code{inputs} is supplied. When \code{x} is a \code{Galaxy} and \code{dataset_id} is missing, \code{x@input_dataset_id} is used.} -\item{inputs}{Named list. Optional workflow input mapping; keys are workflow -input step IDs, values are lists describing datasets/parameters.} - \item{galaxy_url}{Base URL of the Galaxy instance, used by the character method. If \code{GALAXY_URL} is set it takes precedence.} } diff --git a/man/galaxy_start_workflow.Rd b/man/galaxy_start_workflow.Rd index 64a94e7..7a1c547 100644 --- a/man/galaxy_start_workflow.Rd +++ b/man/galaxy_start_workflow.Rd @@ -7,8 +7,8 @@ \S4method{galaxy_start_workflow}{Galaxy}( x, workflow_id, - dataset_id, inputs = NULL, + dataset_id = NULL, galaxy_url = "https://usegalaxy.eu", ... ) From 3063ebf497059acc1e556dfde4baa73135aacc95 Mon Sep 17 00:00:00 2001 From: JulFrey Date: Fri, 23 Jan 2026 14:22:59 +0100 Subject: [PATCH 5/5] now handing over parameterst to tools in workflows possible download more user friendly --- R/2026-01-21_s4_class_methods.R | 151 +++++++++++------- ...galaxy_download_result-character-method.Rd | 51 ++---- man/galaxy_download_result.Rd | 7 +- 3 files changed, 113 insertions(+), 96 deletions(-) diff --git a/R/2026-01-21_s4_class_methods.R b/R/2026-01-21_s4_class_methods.R index eb726da..c142d5f 100644 --- a/R/2026-01-21_s4_class_methods.R +++ b/R/2026-01-21_s4_class_methods.R @@ -432,12 +432,11 @@ setMethod("galaxy_upload_https", "Galaxy", #' @noRd .galaxy_build_wf_inputs <- function(wf_def, dataset_id = NULL, args = list()) { if (is.null(args)) args <- list() - wf_inputs <- wf_def$inputs - - # if we have a dataset_id and nothing provided, bind it to the first input - if (!is.null(dataset_id) && !length(args) && length(wf_inputs)) { - first <- names(wf_inputs)[1L] - args[[first]] <- list(src = "hda", id = dataset_id) + if (!is.null(dataset_id)) { + wf_inputs <- names(wf_def$inputs) + if (!any(wf_inputs %in% names(args))) { + args[[wf_inputs[1L]]] <- list(src = "hda", id = dataset_id) + } } args } @@ -446,11 +445,28 @@ setMethod("galaxy_upload_https", "Galaxy", #' @keywords internal #' @noRd .galaxy_validate_wf_inputs <- function(wf_def, inputs) { + # top–level workflow inputs expected <- names(wf_def$inputs) - unknown <- setdiff(names(inputs), expected) + + # allow overrides of tool parameters: step_id|param_name + step_allowed <- character() + if (!is.null(wf_def$steps)) { + for (st in wf_def$steps) { + if (!is.null(st$tool_id) && length(st$tool_inputs)) { + params <- names(st$tool_inputs) + step_allowed <- c(step_allowed, + paste(st$id, params, sep = "|")) + } + } + } + + allowed <- c(expected, step_allowed) + unknown <- setdiff(names(inputs), allowed) if (length(unknown)) { stop("Unknown workflow inputs: ", paste(unknown, collapse = ", ")) } + + ## only the true workflow inputs without defaults are required has_default <- function(inp) !is.null(inp$value) req_idx <- !vapply(wf_def$inputs, function(inp) isTRUE(inp$optional) || has_default(inp), @@ -671,95 +687,114 @@ setMethod("galaxy_poll_workflow", "Galaxy", ## File download ############################# -# internal helper, not exported +#' Helper function for unique naming +#' @keywords internal +#' @noRd +.make_unique_names <- function(names, out_dir, overwrite = FALSE) { + out <- character(length(names)) + for (i in seq_along(names)) { + nm <- names[i] + if (!nzchar(nm)) nm <- sprintf("dataset_%02d", i) + ext <- tools::file_ext(nm) + base <- if (nzchar(ext)) tools::file_path_sans_ext(nm) else nm + cand <- nm + idx <- 1L + while (cand %in% out || + (!overwrite && file.exists(file.path(out_dir, cand)))) { + cand <- if (nzchar(ext)) sprintf("%s_%d.%s", base, idx, ext) + else sprintf("%s_%d", base, idx) + idx <- idx + 1L + } + if (cand != nm) { + warning("File '", nm, "' exists; using '", cand, "' instead.") + } + out[i] <- cand + } + out +} + +#' Helper function for downloading the results of a history #' @keywords internal #' @noRd .galaxy_download_result <- function(output_ids, - out_file = "result.laz", - galaxy_url = "https://usegalaxy.eu") { - ## allow a list with element output_ids + out_dir = ".", + galaxy_url = "https://usegalaxy.eu", + overwrite = FALSE) { if (is.list(output_ids) && "output_ids" %in% names(output_ids)) { output_ids <- output_ids$output_ids } - api_key <- Sys.getenv("GALAXY_API_KEY") galaxy_url <- .resolve_galaxy_url(galaxy_url) - httr::GET( - paste0(galaxy_url, "/api/datasets/", output_ids[length(output_ids)], "/display"), - httr::add_headers(`x-api-key` = api_key), - httr::write_disk(out_file, overwrite = TRUE) - ) + api_key <- Sys.getenv("GALAXY_API_KEY") + if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE) + + info <- galaxy_get_file_info(output_ids, galaxy_url = galaxy_url) + targets <- .make_unique_names(info$name, out_dir, overwrite = overwrite) + + mapply(function(fid, fname) { + dest <- file.path(out_dir, fname) + httr::GET( + paste0(galaxy_url, "/api/datasets/", fid, "/display"), + httr::add_headers(`x-api-key` = api_key), + httr::write_disk(dest, overwrite = TRUE) # we've ensured uniqueness + ) + }, info$id, targets, SIMPLIFY = FALSE) } setGeneric("galaxy_download_result", function(x, - out_file = "result.laz", + out_dir = ".", galaxy_url = "https://usegalaxy.eu", + overwrite = FALSE, ...) standardGeneric("galaxy_download_result"), signature = "x") -#' Download final result dataset from Galaxy +#' Download result datasets from a Galaxy history #' #' `galaxy_download_result()` is an S4 generic. With `x` as a character vector -#' it is treated as a set of HDA output IDs; the last one is downloaded to -#' `out_file` and the `httr` response is returned. With `x` as a [`Galaxy`] -#' object, its `output_dataset_ids` and `galaxy_url` are used and the object is -#' returned invisibly after performing the download. +#' of HDA output IDs, all corresponding datasets are downloaded into `out_dir` +#' using their Galaxy names; duplicate names are disambiguated by appending +#' `_` before the extension. Existing files are not overwritten if +#' `overwrite = FALSE`, and a warning is issued when a name is adjusted. +#' With `x` as a [`Galaxy`] object its `output_dataset_ids` and `galaxy_url` +#' are used; the object is returned invisibly after performing the downloads. #' #' @param x A vector of HDA output IDs (`character`), or a `Galaxy` object. -#' @param out_file Path to save the downloaded file. +#' @param out_dir Directory in which to save the downloaded files. #' @param galaxy_url Base URL of the Galaxy instance, used by the character -#' method. If `GALAXY_URL` is set it takes precedence. -#' @return For the character method, the `httr` response object from the -#' download request. For the `Galaxy` method, the (unchanged) `Galaxy` -#' object invisibly. -#' @examplesIf galaxy_has_key() -#' # prepare data -#' tmp_dir <- tempdir() -#' f_path <- file.path(tmp_dir, "iris.csv") -#' write.csv(datasets::iris, f_path, row.names = FALSE) -#' -#' #select workflow -#' workflows <- galaxy_list_workflows(include_public = TRUE) -#' iris_workflow <- workflows[workflows$name == -#' "Exploring Iris dataset with statistics and scatterplots", ][1, ] -#' # upload and run workflow -#' gxy <- galaxy(history_name = "IRIS") |> -#' galaxy_initialize() |> -#' galaxy_upload_https(f_path) |> -#' galaxy_start_workflow(workflow_id = iris_workflow$id) |> -#' galaxy_poll_workflow() |> -#' galaxy_download_result(out_file = file.path(tmp_dir, result_files$name[nrow(result_files)])) -#' -#' # inspect the outputs -#' result_files <- galaxy_get_file_info(gxy@output_dataset_ids) -#' head(result_files) -#' +#' method. +#' @param overwrite Logical; if `FALSE` (default), do not overwrite existing +#' files but choose unique names instead. +#' @return For the character method, a list of `httr` responses; for the +#' `Galaxy` method, the (unchanged) `Galaxy` object invisibly. #' @export setMethod("galaxy_download_result", "character", function(x, - out_file = "result.laz", + out_dir = ".", galaxy_url = "https://usegalaxy.eu", + overwrite = FALSE, ...) { .galaxy_download_result(output_ids = x, - out_file = out_file, - galaxy_url = galaxy_url) + out_dir = out_dir, + galaxy_url = galaxy_url, + overwrite = overwrite) }) -#' S4 download function for workflow or tool outputs +#' S4 method to download files from a history #' @rdname galaxy_download_result #' @export setMethod("galaxy_download_result", "Galaxy", function(x, - out_file = "result.laz", + out_dir = ".", + overwrite = FALSE, ...) { .galaxy_download_result(output_ids = x@output_dataset_ids, - out_file = out_file, - galaxy_url = x@galaxy_url) + out_dir = out_dir, + galaxy_url = x@galaxy_url, + overwrite = overwrite) invisible(x) }) - ############################# ## Tool invocation and polling ############################# diff --git a/man/galaxy_download_result-character-method.Rd b/man/galaxy_download_result-character-method.Rd index 7427b12..1df6320 100644 --- a/man/galaxy_download_result-character-method.Rd +++ b/man/galaxy_download_result-character-method.Rd @@ -2,56 +2,37 @@ % Please edit documentation in R/2026-01-21_s4_class_methods.R \name{galaxy_download_result,character-method} \alias{galaxy_download_result,character-method} -\title{Download final result dataset from Galaxy} +\title{Download result datasets from a Galaxy history} \usage{ \S4method{galaxy_download_result}{character}( x, - out_file = "result.laz", + out_dir = ".", galaxy_url = "https://usegalaxy.eu", + overwrite = FALSE, ... ) } \arguments{ \item{x}{A vector of HDA output IDs (\code{character}), or a \code{Galaxy} object.} -\item{out_file}{Path to save the downloaded file.} +\item{out_dir}{Directory in which to save the downloaded files.} \item{galaxy_url}{Base URL of the Galaxy instance, used by the character -method. If \code{GALAXY_URL} is set it takes precedence.} +method.} + +\item{overwrite}{Logical; if \code{FALSE} (default), do not overwrite existing +files but choose unique names instead.} } \value{ -For the character method, the \code{httr} response object from the -download request. For the \code{Galaxy} method, the (unchanged) \code{Galaxy} -object invisibly. +For the character method, a list of \code{httr} responses; for the +\code{Galaxy} method, the (unchanged) \code{Galaxy} object invisibly. } \description{ \code{galaxy_download_result()} is an S4 generic. With \code{x} as a character vector -it is treated as a set of HDA output IDs; the last one is downloaded to -\code{out_file} and the \code{httr} response is returned. With \code{x} as a \code{\link{Galaxy}} -object, its \code{output_dataset_ids} and \code{galaxy_url} are used and the object is -returned invisibly after performing the download. -} -\examples{ -\dontshow{if (galaxy_has_key()) withAutoprint(\{ # examplesIf} -# prepare data -tmp_dir <- tempdir() -f_path <- file.path(tmp_dir, "iris.csv") -write.csv(datasets::iris, f_path, row.names = FALSE) - -#select workflow -workflows <- galaxy_list_workflows(include_public = TRUE) -iris_workflow <- workflows[workflows$name == - "Exploring Iris dataset with statistics and scatterplots", ][1, ] -# upload and run workflow -gxy <- galaxy(history_name = "IRIS") |> - galaxy_initialize() |> - galaxy_upload_https(f_path) |> - galaxy_start_workflow(workflow_id = iris_workflow$id) |> - galaxy_poll_workflow() |> - galaxy_download_result(out_file = file.path(tmp_dir, result_files$name[nrow(result_files)])) - -# inspect the outputs -result_files <- galaxy_get_file_info(gxy@output_dataset_ids) -head(result_files) -\dontshow{\}) # examplesIf} +of HDA output IDs, all corresponding datasets are downloaded into \code{out_dir} +using their Galaxy names; duplicate names are disambiguated by appending +\verb{_} before the extension. Existing files are not overwritten if +\code{overwrite = FALSE}, and a warning is issued when a name is adjusted. +With \code{x} as a \code{\link{Galaxy}} object its \code{output_dataset_ids} and \code{galaxy_url} +are used; the object is returned invisibly after performing the downloads. } diff --git a/man/galaxy_download_result.Rd b/man/galaxy_download_result.Rd index 0be158f..f609297 100644 --- a/man/galaxy_download_result.Rd +++ b/man/galaxy_download_result.Rd @@ -2,15 +2,16 @@ % Please edit documentation in R/2026-01-21_s4_class_methods.R \name{galaxy_download_result,Galaxy-method} \alias{galaxy_download_result,Galaxy-method} -\title{S4 download function for workflow or tool outputs} +\title{S4 method to download files from a history} \usage{ \S4method{galaxy_download_result}{Galaxy}( x, - out_file = "result.laz", + out_dir = ".", galaxy_url = "https://usegalaxy.eu", + overwrite = FALSE, ... ) } \description{ -S4 download function for workflow or tool outputs +S4 method to download files from a history }