diff --git a/DESCRIPTION b/DESCRIPTION index 3bc3d08..7b29a36 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: MsIO Title: Serializing and restoring/importing mass spectrometry data objects -Version: 0.0.6 +Version: 0.0.7 Authors@R: c(person(given = "Johannes", family = "Rainer", email = "Johannes.Rainer@eurac.edu", @@ -45,7 +45,8 @@ Suggests: xcms, alabaster.se, alabaster.matrix, - MsBackendMetaboLights, + MsBackendMetaboLights (>= 0.99.1), + BiocFileCache, QFeatures License: Artistic-2.0 Encoding: UTF-8 @@ -61,6 +62,7 @@ Collate: 'AllGenerics.R' 'MetaboLightsParam.R' 'MsBackend.R' + 'MsBackendMetaboLights.R' 'MsBackendMzR.R' 'MsExperiment.R' 'MsExperimentFiles.R' diff --git a/NAMESPACE b/NAMESPACE index c628a80..e9b5e09 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -26,6 +26,7 @@ importFrom(jsonlite,read_json) importFrom(jsonlite,serializeJSON) importFrom(jsonlite,unserializeJSON) importFrom(jsonlite,write_json) +importFrom(methods,"slot<-") importFrom(methods,as) importFrom(methods,callNextMethod) importFrom(methods,existsMethod) diff --git a/NEWS.md b/NEWS.md index b0bda56..756be9d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # Version 0.0 +## Changes in 0.0.7 + +- Add `saveObject()` for `MsBackendMetaboLights`. + ## Changes in 0.0.6 - Expand unit tests. diff --git a/R/AlabasterParam.R b/R/AlabasterParam.R index fcc67b4..864fe44 100644 --- a/R/AlabasterParam.R +++ b/R/AlabasterParam.R @@ -27,8 +27,15 @@ #' #' - `MsBackendMzR`, defined in the #' [*Spectra*](https://bioconductor.org/packages/Spectra) package. +#' - `MsBackendMetaboLights`, defined in the +#' [*MsBackendMetaboLights*](https://github.com/RforMassSpectrometry/MsBackendMetaboLights) +#' package. #' - `Spectra`, defined in the #' [*Spectra*](https://bioconductor.org/packages/Spectra) package. +#' - `MsExperiment`, defined in the +#' [*MsExperiment*](https://bioconductor.org/packages/MsExperiment) package. +#' - `XcmsExperiment`, defined in the +#' [*xcms*](https://bioconductor.org/packages/xcms) package. #' #' In addition, the *MsIO* package defines the `AlabasterParam` which can be #' used to write or read MS objects using the `saveMsObject()` and @@ -55,11 +62,17 @@ #' listed below. #' #' @param spectraPath For `readMsObject()`: `character(1)` optionally allowing -#' to define the (absolute) path where the spectra files (*data storage -#' files*) can be found. This parameter is used for `MsBackendMzR` (see -#' descriptions below) and can be passed through `...` also to -#' `readMsObject()` functions for other classes (such as `Spectra`, -#' `MsExperiment` etc). +#' to define the (absolute) path where the spectra files (*data storage +#' files*) can be found. This parameter is used for `MsBackendMzR` (see +#' descriptions below) and can be passed through `...` also to +#' `readMsObject()` functions for other classes (such as `Spectra`, +#' `MsExperiment` etc). +#' +#' @param offline For `readMsObject()` and `readObject()` to load MS data as a +#' `MsBackendMetaboLights()`: `logical(1)` to evaluate the local file cache +#' and only load local files. Thus `offline = TRUE` does not need an active +#' internet connection, but fails if one of more files are not cached +#' locally. #' #' @param ... optional additional parameters passed to the downstream #' functions, such as for example `spectraPath` described above. @@ -73,6 +86,7 @@ #' first parameter, from the specified path. `saveObject()` and #' `saveMsObject()` don't return anything. #' +#' #' @section On-disk storage for `MsBackendMzR` objects: #' #' `MsBackendMzR` objects can be exported or imported using the @@ -94,6 +108,15 @@ #' below for details. #' #' +#' @section On-disk storage for `MsBackendMetaboLights` objects: +#' +#' The `MsBackendMetaboLights` extends the `MsBackendMzR` backend and hence the +#' same files are stored. When a `MsBackendMetaboLights` object is restored, +#' the `mtbls_sync()` function is called to check for presence of all MS data +#' files and, if missing, re-download them from the *MetaboLights* repository +#' (if parameter `offline = FALSE` is used). +#' +#' #' @section On-disk storage for `Spectra` objects: #' #' `Spectra` objects can be exported/imported using `saveMsObject()` and diff --git a/R/MsBackendMetaboLights.R b/R/MsBackendMetaboLights.R new file mode 100644 index 0000000..e76fdb0 --- /dev/null +++ b/R/MsBackendMetaboLights.R @@ -0,0 +1,72 @@ +#' @rdname PlainTextParam +setMethod("readMsObject", signature(object = "MsBackendMetaboLights", + param = "PlainTextParam"), + function(object, param, offline = FALSE) { + fl <- file.path(param@path, "ms_backend_data.txt") + if (!file.exists(fl)) + stop("No 'backend_data.txt' file found in the provided path.") + l2 <- readLines(fl, n = 2) + if (l2[1] != "# MsBackendMetaboLights") + stop("Invalid class in 'ms_backend_data.txt' file.", + "Should run with object = ", l2[1]) + if (length(l2) > 1L) { + data <- read.table(file = fl, sep = "\t", header = TRUE) + rownames(data) <- NULL + slot(object, "spectraData", check = FALSE) <- DataFrame(data) + MsBackendMetaboLights::mtbls_sync(object, offline = offline) + } + validObject(object) + object + }) + +################################################################################ +## +## alabaster saveObject/readObject +## +################################################################################ +#' @rdname AlabasterParam +setMethod("saveObject", "MsBackendMetaboLights", function(x, path, ...) { + .save_object_spectra_data(x, path, object = "ms_backend_metabo_lights") +}) + +#' @noRd +#' +#' @importFrom methods slot<- +readAlabasterMsBackendMetaboLights <- function(path = character(), + metadata = list(), + offline = FALSE) { + if (!.is_spectra_installed()) + stop("Required package 'Spectra' missing. Please install ", + "and try again.", call. = FALSE) + if (!.is_ms_backend_metabo_lights_installed()) + stop("Required package 'MsBackendMetaboLights' missing. ", + "Please install and try again.", call. = FALSE) + validateAlabasterMsBackendMzR(path, metadata) + sdata <- altReadObject(file.path(path, "spectra_data")) + pvars <- altReadObject(file.path(path, "peaks_variables")) + be <- MsBackendMetaboLights::MsBackendMetaboLights() + slot(be, "spectraData", check = FALSE) <- sdata + slot(be, "peaksVariables", check = FALSE) <- pvars + MsBackendMetaboLights::mtbls_sync(be, offline = offline) + validObject(be) + be +} + +#' @rdname AlabasterParam +setMethod("saveMsObject", signature(object = "MsBackendMetaboLights", + param = "AlabasterParam"), + function(object, param) { + if (file.exists(param@path)) + stop("Overwriting or saving to an existing directory is not", + " supported. Please remove the directory defined with", + " parameter `path` first.") + saveObject(object, param@path) + }) + +#' @rdname AlabasterParam +setMethod("readMsObject", signature(object = "MsBackendMetaboLights", + param = "AlabasterParam"), + function(object, param, offline = FALSE) { + readAlabasterMsBackendMetaboLights(path = param@path, + offline = offline) + }) diff --git a/R/MsBackendMzR.R b/R/MsBackendMzR.R index 18702b8..9dd8732 100644 --- a/R/MsBackendMzR.R +++ b/R/MsBackendMzR.R @@ -88,9 +88,14 @@ setMethod("readMsObject", signature(object = "MsBackendMzR", #' #' @rdname AlabasterParam setMethod("saveObject", "MsBackendMzR", function(x, path, ...) { + .save_object_spectra_data(x, path, object = "ms_backend_mz_r") +}) + +.save_object_spectra_data <- function(x, path, object, version = "1.0") { dir.create(path = path, recursive = TRUE, showWarnings = FALSE) - saveObjectFile(path, "ms_backend_mz_r", - list(ms_backend_mz_r =list(version = "1.0"))) + l <- list(list(version = version)) + names(l) <- object + saveObjectFile(path, object, l) tryCatch({ do.call(altSaveObject, list(x = x@spectraData, path = file.path(path, "spectra_data"))) @@ -104,9 +109,9 @@ setMethod("saveObject", "MsBackendMzR", function(x, path, ...) { path = file.path(path, "peaks_variables"))) }, error = function(e) { stop("failed to save 'peaksVariables' of ", class(x)[1L], "\n - ", - e$message) + e$message, call. = FALSE) }) -}) +} #' @importFrom alabaster.base registerValidateObjectFunction #' diff --git a/R/PlainTextParam.R b/R/PlainTextParam.R index a5d82d4..5dbeede 100644 --- a/R/PlainTextParam.R +++ b/R/PlainTextParam.R @@ -26,13 +26,16 @@ #' are: #' #' - `MsBackendMzR` object, defined in the -#' ([Spectra](https://bioconductor.org/packages/Spectra)) package. +#' [*Spectra*](https://bioconductor.org/packages/Spectra) package. +#' - `MsBackendMetaboLights` object, defined in the +#' [*MsBackendMetaboLights*](https://bioconductor.org/packages/MsBackendMetaboLights) +#' package. #' - `Spectra` object, defined in the -#' ([Spectra](https://bioconductor.org/packages/Spectra)) package. +#' [*Spectra*](https://bioconductor.org/packages/Spectra) package. #' - `MsExperiment` object, defined in the -#' ([MsExperiment](https://bioconductor.org/packages/MsExperiment)) package. +#' [*MsExperiment*](https://bioconductor.org/packages/MsExperiment) package. #' - `XcmsExperiment` object, defined in the -#' ([xcms](https://bioconductor.org/packages/xcms)) package. +#' [*xcms*](https://bioconductor.org/packages/xcms) package. #' #' See their respective section below for details and formats of the #' exported files. @@ -48,6 +51,11 @@ #' `readMsObject()` functions for other classes (such as `Spectra`, #' `MsExperiment` etc). #' +#' @param offline For `readMsObject()` to load MS data as a +#' `MsBackendMetaboLights()`: `logical(1)` to evaluate **only** the local +#' file cache. Thus `offline = TRUE` does not need an active internet +#' connection, but fails if one of more files are not cached locally. +#' #' @param ... Additional parameters passed down to internal functions. E.g. #' parameter `spectraPath` (see above). #' @@ -68,6 +76,15 @@ #' named *ms_backend_data.txt*. Each row of this tab-delimited text file #' corresponds to a spectrum with its respective metadata in the columns. #' +#' +#' @section On-disk storage for `MsBackendMetaboLights` objects: +#' +#' The `MsBackendMetaboLights` extends the `MsBackendMzR` backend and hence the +#' same files are stored. When a `MsBackendMetaboLights` object is restored, +#' the `mtbls_sync()` function is called to check for presence of all MS data +#' files and, if missing, re-download them from the *MetaboLights* repository. +#' +#' #' @section On-disk storage for `Spectra` objects: #' #' For `Spectra` objects, defined in the `Spectra` package, the files listed diff --git a/R/zzz.R b/R/zzz.R index 6e7bcda..b9b9b0d 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -3,6 +3,10 @@ registerValidateObjectFunction("ms_backend_mz_r", validateAlabasterMsBackendMzR) registerReadObjectFunction("ms_backend_mz_r", readAlabasterMsBackendMzR) + registerValidateObjectFunction("ms_backend_metabo_lights", + validateAlabasterMsBackendMzR) + registerReadObjectFunction("ms_backend_metabo_lights", + readAlabasterMsBackendMetaboLights) registerValidateObjectFunction("spectra", validateAlabasterSpectra) registerReadObjectFunction("spectra", readAlabasterSpectra) registerValidateObjectFunction("ms_experiment_files", diff --git a/man/AlabasterParam.Rd b/man/AlabasterParam.Rd index 5f7583e..c6c85d8 100644 --- a/man/AlabasterParam.Rd +++ b/man/AlabasterParam.Rd @@ -1,10 +1,13 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/AlabasterParam.R, R/MsBackendMzR.R, -% R/MsExperiment.R, R/Spectra.R, R/XcmsExperiment.R +% Please edit documentation in R/AlabasterParam.R, R/MsBackendMetaboLights.R, +% R/MsBackendMzR.R, R/MsExperiment.R, R/Spectra.R, R/XcmsExperiment.R \name{AlabasterParam} \alias{AlabasterParam} \alias{readObject} \alias{saveObject,MsExperimentFiles-method} +\alias{saveObject,MsBackendMetaboLights-method} +\alias{saveMsObject,MsBackendMetaboLights,AlabasterParam-method} +\alias{readMsObject,MsBackendMetaboLights,AlabasterParam-method} \alias{saveObject,MsBackendMzR-method} \alias{saveMsObject,MsBackendMzR,AlabasterParam-method} \alias{readMsObject,MsBackendMzR,AlabasterParam-method} @@ -21,6 +24,12 @@ \usage{ AlabasterParam(path = tempdir()) +\S4method{saveObject}{MsBackendMetaboLights}(x, path, ...) + +\S4method{saveMsObject}{MsBackendMetaboLights,AlabasterParam}(object, param) + +\S4method{readMsObject}{MsBackendMetaboLights,AlabasterParam}(object, param, offline = FALSE) + \S4method{saveObject}{MsBackendMzR}(x, path, ...) \S4method{saveMsObject}{MsBackendMzR,AlabasterParam}(object, param) @@ -64,6 +73,12 @@ functions, such as for example \code{spectraPath} described above.} and file name or directory to/from which the data object should be exported/imported.} +\item{offline}{For \code{readMsObject()} and \code{readObject()} to load MS data as a +\code{MsBackendMetaboLights()}: \code{logical(1)} to evaluate the local file cache +and only load local files. Thus \code{offline = TRUE} does not need an active +internet connection, but fails if one of more files are not cached +locally.} + \item{spectraPath}{For \code{readMsObject()}: \code{character(1)} optionally allowing to define the (absolute) path where the spectra files (\emph{data storage files}) can be found. This parameter is used for \code{MsBackendMzR} (see @@ -95,8 +110,15 @@ these objects: \itemize{ \item \code{MsBackendMzR}, defined in the \href{https://bioconductor.org/packages/Spectra}{\emph{Spectra}} package. +\item \code{MsBackendMetaboLights}, defined in the +\href{https://github.com/RforMassSpectrometry/MsBackendMetaboLights}{\emph{MsBackendMetaboLights}} +package. \item \code{Spectra}, defined in the \href{https://bioconductor.org/packages/Spectra}{\emph{Spectra}} package. +\item \code{MsExperiment}, defined in the +\href{https://bioconductor.org/packages/MsExperiment}{\emph{MsExperiment}} package. +\item \code{XcmsExperiment}, defined in the +\href{https://bioconductor.org/packages/xcms}{\emph{xcms}} package. } In addition, the \emph{MsIO} package defines the \code{AlabasterParam} which can be @@ -137,6 +159,16 @@ and an additional file (in HDF5 format) containing the data. See examples below for details. } +\section{On-disk storage for \code{MsBackendMetaboLights} objects}{ + + +The \code{MsBackendMetaboLights} extends the \code{MsBackendMzR} backend and hence the +same files are stored. When a \code{MsBackendMetaboLights} object is restored, +the \code{mtbls_sync()} function is called to check for presence of all MS data +files and, if missing, re-download them from the \emph{MetaboLights} repository +(if parameter \code{offline = FALSE} is used). +} + \section{On-disk storage for \code{Spectra} objects}{ diff --git a/man/PlainTextParam.Rd b/man/PlainTextParam.Rd index 501b901..2d42552 100644 --- a/man/PlainTextParam.Rd +++ b/man/PlainTextParam.Rd @@ -1,8 +1,9 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/PlainTextParam.R, R/MsBackendMzR.R, -% R/MsExperiment.R, R/Spectra.R, R/XcmsExperiment.R +% Please edit documentation in R/PlainTextParam.R, R/MsBackendMetaboLights.R, +% R/MsBackendMzR.R, R/MsExperiment.R, R/Spectra.R, R/XcmsExperiment.R \name{PlainTextParam} \alias{PlainTextParam} +\alias{readMsObject,MsBackendMetaboLights,PlainTextParam-method} \alias{saveMsObject,MsBackendMzR,PlainTextParam-method} \alias{readMsObject,MsBackendMzR,PlainTextParam-method} \alias{saveMsObject,MsExperiment,PlainTextParam-method} @@ -15,6 +16,8 @@ \usage{ PlainTextParam(path = tempdir()) +\S4method{readMsObject}{MsBackendMetaboLights,PlainTextParam}(object, param, offline = FALSE) + \S4method{saveMsObject}{MsBackendMzR,PlainTextParam}(object, param) \S4method{readMsObject}{MsBackendMzR,PlainTextParam}(object, param, spectraPath = character()) @@ -43,6 +46,11 @@ are going to be stored/ should be loaded from. The default is and file name or directory to/from which the data object should be exported/imported.} +\item{offline}{For \code{readMsObject()} to load MS data as a +\code{MsBackendMetaboLights()}: \code{logical(1)} to evaluate \strong{only} the local +file cache. Thus \code{offline = TRUE} does not need an active internet +connection, but fails if one of more files are not cached locally.} + \item{spectraPath}{For \code{readMsObject()}: \code{character(1)} optionally allowing to define the (absolute) path where the spectra files (\emph{data storage files}) can be found. This parameter is used for \code{MsBackendMzR} (see @@ -78,13 +86,16 @@ The MS object currently supported for import and export with this parameter are: \itemize{ \item \code{MsBackendMzR} object, defined in the -(\href{https://bioconductor.org/packages/Spectra}{Spectra}) package. +\href{https://bioconductor.org/packages/Spectra}{\emph{Spectra}} package. +\item \code{MsBackendMetaboLights} object, defined in the +\href{https://bioconductor.org/packages/MsBackendMetaboLights}{\emph{MsBackendMetaboLights}} +package. \item \code{Spectra} object, defined in the -(\href{https://bioconductor.org/packages/Spectra}{Spectra}) package. +\href{https://bioconductor.org/packages/Spectra}{\emph{Spectra}} package. \item \code{MsExperiment} object, defined in the -(\href{https://bioconductor.org/packages/MsExperiment}{MsExperiment}) package. +\href{https://bioconductor.org/packages/MsExperiment}{\emph{MsExperiment}} package. \item \code{XcmsExperiment} object, defined in the -(\href{https://bioconductor.org/packages/xcms}{xcms}) package. +\href{https://bioconductor.org/packages/xcms}{\emph{xcms}} package. } See their respective section below for details and formats of the @@ -102,6 +113,15 @@ corresponds to a spectrum with its respective metadata in the columns. } } +\section{On-disk storage for \code{MsBackendMetaboLights} objects}{ + + +The \code{MsBackendMetaboLights} extends the \code{MsBackendMzR} backend and hence the +same files are stored. When a \code{MsBackendMetaboLights} object is restored, +the \code{mtbls_sync()} function is called to check for presence of all MS data +files and, if missing, re-download them from the \emph{MetaboLights} repository. +} + \section{On-disk storage for \code{Spectra} objects}{ diff --git a/tests/testthat/test_MsBackendMetaboLights.R b/tests/testthat/test_MsBackendMetaboLights.R new file mode 100644 index 0000000..ae4cc60 --- /dev/null +++ b/tests/testthat/test_MsBackendMetaboLights.R @@ -0,0 +1,94 @@ +## Tests for the MsBackendMetaboLights backend. + +library(Spectra) +library(MsBackendMetaboLights) + +be_mtbls <- backendInitialize(MsBackendMetaboLights(), mtblsId = "MTBLS39", + filePattern = "63A.cdf", offline = FALSE) + +test_that("readMsObject,PlainTextParam,MsBackendMetaboLights works", { + pth <- file.path(tempdir(), "test_backend_ml") + saveMsObject(be_mtbls, PlainTextParam(pth)) + + ## read + res <- readMsObject(MsBackendMetaboLights(), PlainTextParam(pth), + offline = TRUE) + expect_true(validObject(res)) + expect_equal(rtime(be_mtbls), rtime(res)) + expect_equal(mz(be_mtbls), mz(res)) + + ## Clean cache first to check errors etc. + bfc <- BiocFileCache::BiocFileCache() + BiocFileCache::cleanbfc(bfc, days = -10, ask = FALSE) + + ## read again offline throws error + expect_error(readMsObject(MsBackendMetaboLights(), PlainTextParam(pth), + offline = TRUE), "No locally cached") + + ## read re-downloading the data + res <- readMsObject(MsBackendMetaboLights(), PlainTextParam(pth), + offline = FALSE) + expect_true(validObject(res)) + expect_equal(rtime(be_mtbls), rtime(res)) + expect_equal(mz(be_mtbls), mz(res)) + + unlink(pth, recursive = TRUE) +}) + +test_that("saveObject,readObject,MsBackendMetaboLights works", { + b <- be_mtbls + pth <- file.path(tempdir(), "save_object_ms_backend_metabo_lights") + saveObject(b, pth) + res <- dir(pth) + expect_true(length(res) > 0) + expect_true(all(c("OBJECT", "spectra_data", "peaks_variables") %in% res)) + res <- readObject(pth, offline = TRUE) + expect_true(validObject(res)) + expect_equal(rtime(res), rtime(b)) + expect_equal(mz(res[1:10]), mz(b[1:10])) + ## Clear cache and repeat, syncing the data + bfc <- BiocFileCache::BiocFileCache() + BiocFileCache::cleanbfc(bfc, days = -10, ask = FALSE) + res <- readObject(pth) + expect_true(validObject(res)) + expect_equal(rtime(res), rtime(b)) + expect_equal(mz(res[1:10]), mz(b[1:10])) + + ## readAlabasterMsBackendMzR + expect_error(readAlabasterMsBackendMetaboLights("some_path"), + "required file/directory") + res <- readAlabasterMsBackendMetaboLights(pth, offline = TRUE) + expect_s4_class(res, "MsBackendMetaboLights") + expect_equal(length(b), length(res)) + expect_true(validObject(res)) + + ## package Spectra not available: + with_mock( + "MsIO:::.is_spectra_installed" = function() FALSE, + expect_error(MsIO:::readAlabasterMsBackendMetaboLights(pth), + "package 'Spectra'") + ) + ## package Spectra not available: + with_mock( + "MsIO:::.is_ms_backend_metabo_lights_installed" = function() FALSE, + expect_error(MsIO:::readAlabasterMsBackendMetaboLights(pth), + "package 'MsBackendMetaboLights'") + ) + unlink(pth, recursive = TRUE) +}) + +test_that("saveMsObject,readMsObject,MsBackendMetaboLights works", { + b <- be_mtbls + pth <- file.path(tempdir(), "save_object_ms_backend_metabo_lights") + prm <- AlabasterParam(pth) + saveMsObject(b, param = prm) + expect_error(saveMsObject(b, param = prm), "Overwriting or") + + res <- readMsObject(MsBackendMetaboLights(), prm, offline = TRUE) + expect_s4_class(res, "MsBackendMetaboLights") + expect_equal(length(b), length(res)) + expect_true(validObject(res)) + expect_equal(mz(res[1:10]), mz(b[1:10])) + + unlink(pth, recursive = TRUE) +}) diff --git a/tests/testthat/test_MsBackendMzR.R b/tests/testthat/test_MsBackendMzR.R index 11597a7..2f78865 100644 --- a/tests/testthat/test_MsBackendMzR.R +++ b/tests/testthat/test_MsBackendMzR.R @@ -64,6 +64,12 @@ test_that("saveMsObject,readMsObject,PlainTextParam,MsBackendMzR works", { test_that("saveObject,readObject,MsBackendMzR works", { b <- sciex_mzr pth <- file.path(tempdir(), "save_object_ms_backend_mz_r") + .save_object_spectra_data(b, pth, object = "some_object") + res <- dir(pth) + expect_true(length(res) > 0) + expect_true(all(c("OBJECT", "spectra_data", "peaks_variables") %in% res)) + + unlink(pth, recursive = TRUE) saveObject(b, pth) res <- dir(pth) expect_true(length(res) > 0)