diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 0000000..2d19fc7 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/.github/workflows/check-bioc.yml b/.github/workflows/check-bioc.yml new file mode 100644 index 0000000..8535c4d --- /dev/null +++ b/.github/workflows/check-bioc.yml @@ -0,0 +1,292 @@ +## Read more about GitHub actions the features of this GitHub Actions workflow +## at https://lcolladotor.github.io/biocthis/articles/biocthis.html#use_bioc_github_action +## +## For more details, check the biocthis developer notes vignette at +## https://lcolladotor.github.io/biocthis/articles/biocthis_dev_notes.html +## +## You can add this workflow to other packages using: +## > biocthis::use_bioc_github_action() +## +## Using GitHub Actions exposes you to many details about how R packages are +## compiled and installed in several operating system.s +### If you need help, please follow the steps listed at +## https://github.com/r-lib/actions#where-to-find-help +## +## If you found an issue specific to biocthis's GHA workflow, please report it +## with the information that will make it easier for others to help you. +## Thank you! + +## Acronyms: +## * GHA: GitHub Action +## * OS: operating system + +on: + push: + pull_request: + +name: R-CMD-check-bioc + +## These environment variables control whether to run GHA code later on that is +## specific to testthat, covr, and pkgdown. +## +## If you need to clear the cache of packages, update the number inside +## cache-version as discussed at https://github.com/r-lib/actions/issues/86. +## Note that you can always run a GHA test without the cache by using the word +## "/nocache" in the commit message. +env: + has_testthat: 'true' + run_covr: 'true' + run_pkgdown: 'true' + has_RUnit: 'false' + has_BiocCheck: 'false' + cache-version: 'cache-v1' + +jobs: + build-check: + runs-on: ${{ matrix.config.os }} + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + container: ${{ matrix.config.cont }} + ## Environment variables unique to this job. + + strategy: + fail-fast: false + matrix: + config: + - { os: ubuntu-latest, r: 'devel', bioc: 'devel', cont: "bioconductor/bioconductor_docker:devel", rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest" } + - { os: macOS-latest, r: 'devel', bioc: '3.20'} + - { os: windows-latest, r: 'devel', bioc: '3.20'} + env: + R_REMOTES_NO_ERRORS_FROM_WARNINGS: true + RSPM: ${{ matrix.config.rspm }} + NOT_CRAN: true + TZ: UTC + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + + ## Set the R library to the directory matching the + ## R packages cache step further below when running on Docker (Linux). + - name: Set R Library home on Linux + if: runner.os == 'Linux' + run: | + mkdir /__w/_temp/Library + echo ".libPaths('/__w/_temp/Library')" > ~/.Rprofile + + ## Most of these steps are the same as the ones in + ## https://github.com/r-lib/actions/blob/master/examples/check-standard.yaml + ## If they update their steps, we will also need to update ours. + - name: Checkout Repository + uses: actions/checkout@v2 + + ## R is already included in the Bioconductor docker images + - name: Setup R from r-lib + if: runner.os != 'Linux' + uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + + ## pandoc is already included in the Bioconductor docker images + - name: Setup pandoc from r-lib + if: runner.os != 'Linux' + uses: r-lib/actions/setup-pandoc@v2 + + - name: Query dependencies + run: | + install.packages('remotes') + saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) + shell: Rscript {0} + + - name: Cache R packages + if: "!contains(github.event.head_commit.message, '/nocache') && runner.os != 'Linux'" + uses: actions/cache@v2 + with: + path: ${{ env.R_LIBS_USER }} + key: ${{ env.cache-version }}-${{ runner.os }}-biocversion-devel-r-devel-${{ hashFiles('.github/depends.Rds') }} + restore-keys: ${{ env.cache-version }}-${{ runner.os }}-biocversion-devel-r-devel- + + - name: Cache R packages on Linux + if: "!contains(github.event.head_commit.message, '/nocache') && runner.os == 'Linux' " + uses: actions/cache@v2 + with: + path: /home/runner/work/_temp/Library + key: ${{ env.cache-version }}-${{ runner.os }}-biocversion-devel-r-devel-${{ hashFiles('.github/depends.Rds') }} + restore-keys: ${{ env.cache-version }}-${{ runner.os }}-biocversion-devel-r-devel- + + - name: Install Linux system dependencies + if: runner.os == 'Linux' + run: | + sysreqs=$(Rscript -e 'cat("apt-get update -y && apt-get install -y", paste(gsub("apt-get install -y ", "", remotes::system_requirements("ubuntu", "20.04")), collapse = " "))') + echo $sysreqs + sudo -s eval "$sysreqs" + + - name: Install macOS system dependencies + if: matrix.config.os == 'macOS-latest' + run: | + ## Enable installing XML from source if needed + brew install libxml2 + echo "XML_CONFIG=/usr/local/opt/libxml2/bin/xml2-config" >> $GITHUB_ENV + + ## Required to install magick as noted at + ## https://github.com/r-lib/usethis/commit/f1f1e0d10c1ebc75fd4c18fa7e2de4551fd9978f#diff-9bfee71065492f63457918efcd912cf2 + brew install imagemagick@6 + + ## For textshaping, required by ragg, and required by pkgdown + brew install harfbuzz fribidi + + ## For installing usethis's dependency gert + brew install libgit2 + + ## required for ncdf4 - can not use the homebrew one because that uses GCC + ## Use pre-compiled libraries from https://mac.r-project.org/libs-4/ + curl -O https://mac.r-project.org/libs-4/netcdf-4.7.4-darwin.17-x86_64.tar.gz + tar fvxzm netcdf-4.7.4-darwin.17-x86_64.tar.gz -C / + rm netcdf-4.7.4-darwin.17-x86_64.tar.gz + curl -O https://mac.r-project.org/libs-4/hdf5-1.12.0-darwin.17-x86_64.tar.gz + tar fvxzm hdf5-1.12.0-darwin.17-x86_64.tar.gz -C / + rm hdf5-1.12.0-darwin.17-x86_64.tar.gz + curl -O https://mac.r-project.org/libs-4/szip-2.1.1-darwin.17-x86_64.tar.gz + tar fvxzm szip-2.1.1-darwin.17-x86_64.tar.gz -C / + rm szip-2.1.1-darwin.17-x86_64.tar.gz + + - name: Install Windows system dependencies + if: runner.os == 'Windows' + run: | + ## Edit below if you have any Windows system dependencies + shell: Rscript {0} + + - name: Install BiocManager + run: | + message(paste('****', Sys.time(), 'installing BiocManager ****')) + remotes::install_cran("BiocManager") + shell: Rscript {0} + + - name: Set BiocVersion + run: | + BiocManager::install(version = "${{ matrix.config.bioc }}", ask = FALSE) + shell: Rscript {0} + + - name: Install dependencies pass 1 + run: | + ## Try installing the package dependencies in steps. First the local + ## dependencies, then any remaining dependencies to avoid the + ## issues described at + ## https://stat.ethz.ch/pipermail/bioc-devel/2020-April/016675.html + ## https://github.com/r-lib/remotes/issues/296 + ## Ideally, all dependencies should get installed in the first pass. + + ## Pass #1 at installing dependencies + message(paste('****', Sys.time(), 'pass number 1 at installing dependencies: local dependencies ****')) + remotes::install_local(dependencies = TRUE, repos = BiocManager::repositories(), build_vignettes = FALSE, upgrade = TRUE) + continue-on-error: true + shell: Rscript {0} + + - name: Install dependencies pass 2 + run: | + ## Pass #2 at installing dependencies + message(paste('****', Sys.time(), 'pass number 2 at installing dependencies: any remaining dependencies ****')) + remotes::install_local(dependencies = TRUE, repos = BiocManager::repositories(), build_vignettes = FALSE, upgrade = TRUE) + + ## Manually install required packages + BiocManager::install('rhdf5', dependencies = TRUE, ask = FALSE, update = FALSE, INSTALL_opts = '--force-biarch') + BiocManager::install("mzR", dependencies = TRUE, ask = FALSE, update = FALSE) + BiocManager::install("msdata") + + message(paste('****', Sys.time(), 'force installation of selected packages ****')) + BiocManager::install(c("BiocStyle", "rmarkdown", "magick", "Spectra")) + + ## For running the checks + message(paste('****', Sys.time(), 'installing rcmdcheck and BiocCheck ****')) + remotes::install_cran("rcmdcheck") + BiocManager::install("BiocCheck") + shell: Rscript {0} + + - name: Install BiocGenerics + if: env.has_RUnit == 'true' + run: | + ## Install BiocGenerics + BiocManager::install("BiocGenerics") + shell: Rscript {0} + + - name: Install covr + if: github.ref == 'refs/heads/main' && env.run_covr == 'true' && runner.os == 'Linux' + run: | + remotes::install_cran("covr") + shell: Rscript {0} + + - name: Install pkgdown + if: github.ref == 'refs/heads/main' && env.run_pkgdown == 'true' && runner.os == 'Linux' + run: | + remotes::install_github("r-lib/pkgdown") + shell: Rscript {0} + + - name: Session info + run: | + options(width = 100) + pkgs <- installed.packages()[, "Package"] + sessioninfo::session_info(pkgs, include_base = TRUE) + shell: Rscript {0} + + - name: Run CMD check + env: + _R_CHECK_CRAN_INCOMING_: false + run: | + rcmdcheck::rcmdcheck( + args = c("--no-build-vignettes", "--no-manual", "--timings"), + build_args = c("--no-manual", "--no-resave-data"), + error_on = "warning", + check_dir = "check" + ) + shell: Rscript {0} + + ## Might need an to add this to the if: && runner.os == 'Linux' + - name: Reveal testthat details + if: env.has_testthat == 'true' + run: find . -name testthat.Rout -exec cat '{}' ';' + + - name: Run RUnit tests + if: env.has_RUnit == 'true' + run: | + BiocGenerics:::testPackage() + shell: Rscript {0} + + - name: Run BiocCheck + if: env.has_BiocCheck == 'true' + run: | + BiocCheck::BiocCheck( + dir('check', 'tar.gz$', full.names = TRUE), + `quit-with-status` = TRUE, + `no-check-R-ver` = TRUE, + `no-check-bioc-help` = TRUE + ) + shell: Rscript {0} + + - name: Test coverage + if: github.ref == 'refs/heads/main' && env.run_covr == 'true' && runner.os == 'Linux' + run: | + covr::codecov() + shell: Rscript {0} + + - name: Install package + if: github.ref == 'refs/heads/main' && env.run_pkgdown == 'true' && runner.os == 'Linux' + run: R CMD INSTALL . + + - name: Deploy package + if: github.ref == 'refs/heads/main' && env.run_pkgdown == 'true' && runner.os == 'Linux' + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + git config --global --add safe.directory /__w/MsIO/MsIO + Rscript -e "pkgdown::deploy_to_branch(new_process = FALSE)" + shell: bash {0} + ## Note that you need to run pkgdown::deploy_to_branch(new_process = FALSE) + ## at least one locally before this will work. This creates the gh-pages + ## branch (erasing anything you haven't version controlled!) and + ## makes the git history recognizable by pkgdown. + + - name: Upload check results + if: failure() + uses: actions/upload-artifact@master + with: + name: ${{ runner.os }}-biocversion-devel-r-devel-results + path: check diff --git a/DESCRIPTION b/DESCRIPTION index 55eaa0f..d781c40 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: MsIO Title: Serializing and restoring/importing mass spectrometry data objects -Version: 0.0.1 +Version: 0.0.2 Authors@R: c(person(given = "Johannes", family = "Rainer", email = "Johannes.Rainer@eurac.edu", @@ -38,7 +38,7 @@ Suggests: rmarkdown, roxygen2, Spectra, - testthat, + testthat, xcms License: Artistic-2.0 Encoding: UTF-8 @@ -48,7 +48,7 @@ URL: https://github.com/RforMassSpectrometry/MsIO biocViews: Infrastructure, MassSpectrometry, Metabolomics, DataImport, Proteomics Roxygen: list(markdown=TRUE) RoxygenNote: 7.3.2 -Collate: +Collate: 'AllGenerics.R' 'MsBackend.R' 'PlainTextParam.R' diff --git a/NAMESPACE b/NAMESPACE index 91c7cb4..9a1bbc0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,8 +17,10 @@ importFrom(jsonlite,write_json) importFrom(methods,as) importFrom(methods,callNextMethod) importFrom(methods,existsMethod) +importFrom(methods,getFunction) importFrom(methods,new) importFrom(methods,validObject) importFrom(stats,setNames) importFrom(utils,read.table) importFrom(utils,write.table) +importMethodsFrom(S4Vectors,"mcols<-") diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..ef10f2a --- /dev/null +++ b/NEWS.md @@ -0,0 +1,5 @@ +# Version 0.0 + +## Changes in 0.0.2 + +- Refactor code for text-based import and export of MS data objects. diff --git a/R/MsBackendMzR.R b/R/MsBackendMzR.R index bd78da8..2b2d2f4 100644 --- a/R/MsBackendMzR.R +++ b/R/MsBackendMzR.R @@ -24,27 +24,28 @@ setMethod("saveMsObject", signature(object = "MsBackendMzR", recursive = TRUE, showWarnings = FALSE) object <- Spectra::dropNaSpectraVariables(object) - fl <- file.path(param@path, "backend_data.txt") + fl <- file.path(param@path, "ms_backend_data.txt") if (file.exists(fl)) - warning("Overwriting already present 'backend_data.txt' file") + warning("Overwriting already present ", + "'ms_backend_data.txt' file") writeLines(paste0("# ", class(object)[1L]), con = fl) if (nrow(object@spectraData)) suppressWarnings( write.table(object@spectraData, file = fl, sep = "\t", quote = TRUE, - append = TRUE, row.names = FALSE)) + append = TRUE)) }) #' @rdname PlainTextParam setMethod("readMsObject", signature(object = "MsBackendMzR", param = "PlainTextParam"), function(object, param, spectraPath = character()) { - fl <- file.path(param@path, "backend_data.txt") + 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] != "# MsBackendMzR") - stop("Invalid class in 'backend_data.txt' file.", + 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) @@ -53,8 +54,8 @@ setMethod("readMsObject", signature(object = "MsBackendMzR", if (length(spectraPath) > 0) { old <- common_path(dataStorage(object)) dataStoragePaths <- dataStorage(object) - normalizedDataStoragePaths <- normalizePath(dataStoragePaths, - winslash = "/") + normalizedDataStoragePaths <- normalizePath( + dataStoragePaths, winslash = "/", mustWork = FALSE) dataStorage(object) <- sub(old, spectraPath, normalizedDataStoragePaths) } diff --git a/R/MsExperiment.R b/R/MsExperiment.R index ddff5ea..1a672ef 100644 --- a/R/MsExperiment.R +++ b/R/MsExperiment.R @@ -1,5 +1,5 @@ -#'@include PlainTextParam.R -#'@title Methods to save and load contents of a MsExperiment object +#' @include PlainTextParam.R +#' @title Methods to save and load contents of a MsExperiment object #' #' @author Philippine Louail #' @@ -19,62 +19,73 @@ setMethod("saveMsObject", recursive = TRUE, showWarnings = FALSE) ## sample data - write.table(as.data.frame(object@sampleData), sep = "\t", + sdata <- object@sampleData + if (!length(sdata)) # initialize with empty data frame + sdata <- DataFrame(sample_name = character()) + write.table(as.data.frame(sdata), sep = "\t", file = file.path(param@path, - "sample_data.txt")) + "ms_experiment_sample_data.txt")) - ## sample data links ## maybe here is better if we create a separate folder for all these. + ## sample data links sdl <- object@sampleDataLinks - lapply(names(sdl), function(x){ - fl <- file.path(param@path, paste0("sample_data_links_", x, - ".txt")) - write.table(sdl[[x]], file = fl, row.names = FALSE, - col.names = FALSE, sep = "\t") + if (length(sdl) > 0) { + lapply(names(sdl), function(x){ + fl <- file.path( + param@path, + paste0("ms_experiment_sample_data_links_", x, ".txt")) + write.table(sdl[[x]], file = fl, row.names = FALSE, + col.names = FALSE, sep = "\t") }) - write.table(sdl@elementMetadata, sep = "\t", - file = file.path(param@path, "element_metadata.txt")) + write.table( + sdl@elementMetadata, sep = "\t", quote = TRUE, + file = file.path(param@path, + "ms_experiment_link_mcols.txt")) + } ## call export of individual other objects (not MsExperiment data) - saveMsObject(spectra(object), param) + if (length(spectra(object))) + saveMsObject(spectra(object), param) ## at some point also chromatograms, etc. } ) #' @rdname PlainTextParam +#' +#' @importMethodsFrom S4Vectors mcols<- setMethod("readMsObject", signature(object = "MsExperiment", param = "PlainTextParam"), - function(object, param, spectraPath = character()) { + function(object, param, ...) { ## read sample data - fl <- file.path(param@path, "sample_data.txt") + fl <- file.path(param@path, "ms_experiment_sample_data.txt") if (!file.exists(fl)) - stop("No 'sample_data.txt' file found in the provided path.") - sd <- read.table(fl, sep = "\t") + stop("No 'ms_experiment_sample_data.txt' file found in ", + "the provided path.") + sd <- read.table(fl, sep = "\t", header = TRUE) object@sampleData <- DataFrame(sd, row.names = NULL) ## read spectra - object@spectra <- readMsObject(Spectra::Spectra(), param, spectraPath = spectraPath) - + if (file.exists(file.path(param@path, "spectra_slots.txt"))) + object@spectra <- readMsObject(Spectra::Spectra(), param, ...) ## sample data links - fl <- list.files(param@path, - pattern = "sample_data_links_.*\\.txt", - full.names = TRUE) - if (length(fl) == 0) - stop("No 'sample_data_links_*.txt' files found in the ", - "provided path.") - n <- gsub("sample_data_links_|\\.txt", "", basename(fl)) - sdl <- lapply(fl, - function(x) unname( - as.matrix( - read.table(x, sep = "\t")))) - names(sdl) <- n - object@sampleDataLinks <- SimpleList(sdl) - em <- read.table(file.path(param@path, "element_metadata.txt"), - sep = "\t") - object@sampleDataLinks@elementMetadata <- DataFrame(em, - row.names = NULL) + fl <- list.files( + param@path, + pattern = "ms_experiment_sample_data_links_.*\\.txt", + full.names = TRUE) + if (length(fl) > 0) { + n <- gsub("ms_experiment_sample_data_links_|\\.txt", "", + basename(fl)) + sdl <- lapply(fl, function(x) { + unname(as.matrix(read.table(x, sep = "\t"))) + }) + names(sdl) <- n + object@sampleDataLinks <- SimpleList(sdl) + em <- read.table(file.path(param@path, + "ms_experiment_link_mcols.txt"), + sep = "\t", header = TRUE) + mcols(object@sampleDataLinks) <- DataFrame( + em, row.names = NULL) + } validObject(object) object }) - - diff --git a/R/PlainTextParam.R b/R/PlainTextParam.R index 42291de..7b2c5fb 100644 --- a/R/PlainTextParam.R +++ b/R/PlainTextParam.R @@ -9,30 +9,45 @@ #' @description #' #' The `saveMsObject()` and `readMsObject()` methods with the `PlainTextParam` -#' option enable users to save/load different type of MS object as a -#' collections of plain text files in/from a specified folder. This folder, -#' defined with the `path` parameter, will be created by the `storeResults()` -#' function. Any previous exports eventually present in that folder will be -#' overwritten. +#' option enable users to save/load different type of mass spectrometry (MS) +#' object as a collections of plain text files in/from a specified folder. +#' This folder, defined with the `path` parameter, will be created by the +#' `storeResults()` function. Writing data to a folder that contains already +#' exported data will result in an error. +#' +#' All data is exported to plain text files, where possible as tabulator +#' delimited text files. Data is exported using R's [write.table()] function, +#' thus, the text files will also contain row names (first column) as well as +#' column names (header). Strings in the text files are quoted. Some +#' information, in particular the content of *parameter* classes within the +#' objects, is stored in JSON format instead. #' #' The MS object currently supported for import and export with this parameter -#' are : +#' are: #' -#' - `MsBackendMzR` object -#' - `Spectra` object -#' - `MsExperiment` object -#' - `XcmsExperiment` object +#' - `MsBackendMzR` object, defined in the +#' ([Spectra](https://bioconductor.org/packages/Spectra)) package. +#' - `Spectra` object, defined in the +#' ([Spectra](https://bioconductor.org/packages/Spectra)) package. +#' - `MsExperiment` object, defined in the +#' ([MsExperiment](https://bioconductor.org/packages/MsExperiment)) package. +#' - `XcmsExperiment` object, defined in the +#' ([xcms](https://bioconductor.org/packages/xcms)) package. #' -#' See their respective section below for a detail of the exported files. +#' See their respective section below for details and formats of the +#' exported files. #' #' @param path For `PlainTextParam()`: `character(1)`, defining where the files #' are going to be stored/ should be loaded from. The default is #' `path = tempdir()`. #' -#' @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 passed to the `loadResults()` method of -#' the MsBackend(). +#' @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 passed to the `loadResults()` +#' method of the MsBackend(). +#' +#' @param ... Additional parameters passed down to internal functions. E.g. +#' parameter `spectraPath` (see above). #' #' @inheritParams saveMsObject #' @@ -41,22 +56,22 @@ #' plain text files to a folder. The `readMsObject()` method returns the #' restored data as an instance of the class specified with parameter `object`. #' -#' @section On disk storage for `MsBackendMzR` objects: +#' +#' @section On-disk storage for `MsBackendMzR` objects: #' #' For `MsBackendMzR` objects, defined in the `Spectra` package, the following #' file is stored: #' #' - The backend's `spectraData()` is stored in a tabular format in a text file -#' named *backend_data.txt*. Each row of this file corresponds to a spectrum -#' with its respective metadata in the columns. +#' 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 `Spectra` objects: #' -#' For `Spectra` objects, defined in the `Spectra` package, the following files -#' are stored: -#' -#' - The backend data storage depends on its class; refer to its respective -#' section for more details. +#' For `Spectra` objects, defined in the `Spectra` package, the files listed +#' below are stored. Any parameter passed to the `saveMsObject()` method using +#' its `...` parameter are passed to the `saveMsObject()` call of the +#' `Spectra`'s backend. #' #' - The `processingQueueVariables`, `processing`, `processingChunkSize()`, and #' `backend` class information of the object are stored in a text file named @@ -69,54 +84,74 @@ #' processing step is separated by a line and includes all information about #' the parameters and functions used for the step. #' +#' - The `Spectra`'s MS data (i.e. it's backend) is stored/exported using +#' the `saveMsObject()` method of the respective backend type. Currently +#' only backends for which the `saveMsObject()` method is implemented (see +#' above) are supported. +#' +#' #' @section On-disk storage for `MsExperiment` objects: #' #' For `MsExperiment` objects, defined in the `MsExperiment` package, the -#' exported data and related text files are: +#' exported data and related text files are listed below. Any parameter passed +#' to the `saveMsObject()` through `...` are passed to the `saveMsObject()` +#' calls of the individual MS data object(s) within the `MsExperiment`. #' -#' - The previously defined `Spectra` object-related files. See the respective -#' section for more information. +#' Note that at present `saveMsObject()` with `PlainTextParam` does **not** +#' export the full content of the `MsExperiment`, i.e. slots `@experimentFiles`, +#' `@qdata`, `@otherData` and `@metadata` are currently not saved. #' -#' - The `sampleData()` is stored as a text file named *sample_data.txt*. Each -#' row of this file corresponds to a sample with its respective metadata in -#' the columns. +#' - The `sampleData()` is stored as a text file named +#' *ms_experiment_sample_data.txt*. Each row of this file corresponds to a +#' sample with its respective metadata in the columns. #' -#' - The links between the sample data and other data are stored in text -#' files named *sample_data_links_....txt*, with "..." referring to the data -#' type. Each file corresponds to a mapping between the sample data and a -#' specific data type (e.g., Spectra). One file is written to map samples to -#' each data type. In each file, the first column corresponds to the sample -#' number and the second to the other data type number (e.g., spectrum number). -#' The table "element_metadata.txt" contains the metadata of each mapping -#' files [johannes could you maybe help me explain that one aha] +#' - The links between the sample data and any other data within the +#' `MsExperiment` are stored in text files named +#' *ms_experiment_sample_data_links_....txt*, +#' with "..." referring to the data slot to which samples are linked. +#' Each file contains the mapping between the sample data and the elements in +#' a specific data slot (e.g., `Spectra`). The files are tabulator delimited +#' text files with two columns of integer values, the first representing the +#' index of a sample in the objects `sampleData()`, the second the index of +#' the assigned element in the respective object slot. +#' The table "ms_experiment_element_metadata.txt" contains the metadata of +#' each of the available mappings. #' -#' @section On-disk storage for `XcmsExperiment` objects: +#' - If the `MsExperiment` contains a `Spectra` object with MS data, it's +#' content is exported to the same folder using a `saveMsObject()` call on +#' it (see above for details of exporting `Spectra` objects to text files). #' -#' For `XcmsExperiment` objects, defined in the `xcms` package, the exported -#' data and related text files are: #' -#' - The previously defined `MsExperiment` object-related files. See the -#' respective section for more information. #' -#' - The `processHistory()` information of the object is stored in a `json` -#' file named *process_history.json*. The file is written such that each -#' processing step is separated by a line and includes all information about -#' the parameters and functions used for that step. +#' @section On-disk storage for `XcmsExperiment` objects: +#' +#' For `XcmsExperiment` objects, defined in the *xcms* package, the exported +#' data and related text files are listed below. Any parameter passed +#' to the `saveMsObject()` through `...` are passed to the `saveMsObject()` +#' calls of the individual MS data object(s) within the `XcmsExperiment`. #' #' - The chromatographic peak information obtained with `chromPeaks()` and #' `chromPeaksData()` is stored in tabular format in the text files -#' *chrom_peaks.txt* and *chrom_peak_data.txt*, respectively. The first file's -#' rows represent single peaks with their respective metadata in the columns. -#' The second file contains the same peaks by rows but with processing -#' information such as whether they are filled or merged in the columns. +#' *xcms_experiment_chrom_peaks.txt* and +#' *xcms_experiment_chrom_peak_data.txt*, respectively. The first file's +#' rows represent single peaks with their respective metadata in the columns +#' (only numeric information). The second file contains arbitrary additional +#' information/metadata for each peak (each row being one chrom peak). #' #' - The `featureDefinitions()` are stored in a text file named -#' *feature_definitions.txt*. Additionally, a second file named -#' *feature_peak_index.txt* is generated to connect the features' definitions -#' with their names. Each row of the first file corresponds to a feature with -#' its respective metadata in the columns. The second file contains the -#' mapping of each chromatographic peak (e.g., one peak ID per row) to its -#' respective feature ID. +#' *xcms_experiment_feature_definitions.txt*. Additionally, a second file +#' named *ms_experiment_feature_peak_index.txt* is generated to connect the +#' features with the corresponding chromatographic peaks. Each row of the +#' first file corresponds to a feature with its respective metadata in the +#' columns. The second file contains the mapping between features and +#' chromatographic peaks (one peak ID per row). +#' +#' - The `processHistory()` information of the object is stored to a +#' file named *xcms_experiment_process_history.json* in JSON format. +#' +#' - The `XcmsExperiment` directly extends the `MsExperiment` class, thus, +#' any MS data is saved using a call to the `saveMsObject` of the +#' `MsExperiment` (see above for more information). #' #' #' @author Philippine Louail diff --git a/R/Spectra.R b/R/Spectra.R index c023ea1..e3f21a1 100644 --- a/R/Spectra.R +++ b/R/Spectra.R @@ -29,9 +29,11 @@ setMethod("saveMsObject", signature(object = "Spectra", }) #' @rdname PlainTextParam +#' +#' @importFrom methods getFunction setMethod("readMsObject", signature(object = "Spectra", param = "PlainTextParam"), - function(object, param, spectraPath = character()) { + function(object, param, ...) { fl <- file.path(param@path, "spectra_slots.txt") if (!file.exists(fl)) stop("No 'spectra_slots.txt' file found in ", param@path) @@ -43,15 +45,26 @@ setMethod("readMsObject", signature(object = "Spectra", "PlainTextParam"))) stop("Can not read a 'Spectra' object with backend '", variables["backend"], "'") - b <- readMsObject(object = do.call(what = variables[["backend"]], - args = list()), - param = param, spectraPath = spectraPath) + ## Check if the library to load the backend class is available. + ## This should also enable backends that are defined in other + ## packages than Spectra. Accessing directly the "globalenv" to + ## ensure we can access functions/classes there. + fun <- getFunction(variables[["backend"]], mustFind = FALSE, + where = topenv(globalenv())) + if (!length(fun)) + stop("Can not create an instance of the MsBackend class \"", + variables[["backend"]], "\". Please first load the ", + "library that provides this class and try again.", + call. = FALSE) + b <- readMsObject(fun(), param, ...) object@backend <- b - object@processingQueueVariables <- unlist(strsplit(variables[["processingQueueVariables"]], - "|", fixed = TRUE)) - object@processing <- unlist(strsplit(variables[["processing"]], "|" , - fixed = TRUE)) - object@processingChunkSize <- as.numeric(variables[["processingChunkSize"]]) + object@processingQueueVariables <- unlist( + strsplit(variables[["processingQueueVariables"]], + "|", fixed = TRUE)) + object@processing <- unlist( + strsplit(variables[["processing"]], "|" , fixed = TRUE)) + object@processingChunkSize <- as.numeric( + variables[["processingChunkSize"]]) fl <- file.path(param@path, "spectra_processing_queue.json") if (file.exists(fl)) object <- .import_spectra_processing_queue(object, file = fl) @@ -77,8 +90,8 @@ setMethod("readMsObject", signature(object = "Spectra", con = con) p <- x@processing writeLines(paste0("processing = ", paste(p, collapse = "|")), con = con) - writeLines(paste0("processingChunkSize = ", Spectra::processingChunkSize(x)), - con = con) + writeLines(paste0("processingChunkSize = ", + Spectra::processingChunkSize(x)), con = con) writeLines(paste0("backend = ", class(x@backend)[1L]), con = con) } diff --git a/R/XcmsExperiment.R b/R/XcmsExperiment.R index c0dd91d..1a61c59 100644 --- a/R/XcmsExperiment.R +++ b/R/XcmsExperiment.R @@ -27,7 +27,7 @@ setMethod("saveMsObject", setMethod("readMsObject", signature(object = "XcmsExperiment", param = "PlainTextParam"), - function(object, param, spectraPath = character()) { + function(object, param, ...) { res <- callNextMethod() res <- .load_xcmsexperiment(res, path = param@path) validObject(res) @@ -37,24 +37,19 @@ setMethod("readMsObject", #' @noRd .store_xcmsexperiment <- function(x, path = tempdir()) { .export_process_history(x, path = path) - if (xcms::hasChromPeaks(x)) - .export_chrom_peaks(x, path) + ## if (xcms::hasChromPeaks(x)) # maybe also export chromPeaks. + .export_chrom_peaks(x, path) if (xcms::hasFeatures(x)) .export_features(x, path) } - #' @noRd .load_xcmsexperiment <- function(x, path = character(), - spectraExport = logical()){ + spectraExport = logical()) { x <- as(x, "XcmsExperiment") - fl <- file.path(path, "chrom_peaks.txt") x <- .import_chrom_peaks(x, path) - fl <- file.path(path, "process_history.json") - if (file.exists(fl)) - x <- .import_process_history(x, fl) - else stop("No \"process_history.json\" file found in ", path) - fl <- file.path(path, "feature_definitions.txt") + x <- .import_process_history(x, path) + fl <- file.path(path, "xcms_experiment_feature_definitions.txt") if (file.exists(fl)) x <- .import_features(x, path) x @@ -64,12 +59,16 @@ setMethod("readMsObject", #' @noRd .export_process_history <- function(x, path = character()) { ph <- xcms::processHistory(x) - write_json(serializeJSON(ph), file.path(path, "process_history.json")) + write_json(serializeJSON(ph), + file.path(path, "xcms_experiment_process_history.json")) } #' @noRd -.import_process_history <- function(x, file = character()) { - ph <- unserializeJSON(read_json(file)[[1L]]) +.import_process_history <- function(x, path = character()) { + fl <- file.path(path, "xcms_experiment_process_history.json") + if (!file.exists(fl)) + stop("No \"xcms_experiment_process_history.json\" file found in ", path) + ph <- unserializeJSON(read_json(fl)[[1L]]) x@processHistory <- ph x } @@ -77,20 +76,23 @@ setMethod("readMsObject", #' Chromatographic peaks #' @noRd .export_chrom_peaks <- function(x, path = character()) { - write.table(xcms::chromPeaks(x), file = file.path(path, "chrom_peaks.txt"), + write.table(xcms::chromPeaks(x), + file = file.path(path, "xcms_experiment_chrom_peaks.txt"), sep = "\t") write.table(as.data.frame(xcms::chromPeakData(x)), sep = "\t", - file = file.path(path, "chrom_peak_data.txt")) + file = file.path(path, "xcms_experiment_chrom_peak_data.txt")) } #' @noRd .import_chrom_peaks <- function(x, path = character()) { - f <- file.path(path, "chrom_peaks.txt") - pk <- as.matrix(read.table(f, sep = "\t")) - f <- file.path(path, "chrom_peak_data.txt") + f <- file.path(path, "xcms_experiment_chrom_peaks.txt") + if (!file.exists(f)) + stop("No \"xcms_experiment_chrom_peaks.txt\" file found in ", path) + pk <- as.matrix(read.table(f, sep = "\t", header = TRUE)) + f <- file.path(path, "xcms_experiment_chrom_peak_data.txt") if (!file.exists(f)) - stop("No \"chrom_peak_data.txt\" file found in ", path) - pkd <- read.table(f, sep = "\t") + stop("No \"xcms_experiment_chrom_peak_data.txt\" file found in ", path) + pkd <- read.table(f, sep = "\t", header = TRUE) x@chromPeaks <- pk x@chromPeakData <- pkd x @@ -104,20 +106,23 @@ setMethod("readMsObject", feature_index = rep(seq_len(nrow(fts)), lengths(fts$peakidx)), peak_index = unlist(fts$peakidx, use.names = FALSE)) fts$peakidx <- NA - write.table(fts, file = file.path(path, "feature_definitions.txt"), - sep = "\t") - write.table(pkidx, file = file.path(path, "feature_peak_index.txt"), - sep = "\t") + write.table( + fts, file = file.path(path, "xcms_experiment_feature_definitions.txt"), + sep = "\t") + write.table( + pkidx, file = file.path(path, "xcms_experiment_feature_peak_index.txt"), + sep = "\t") } #' @noRd .import_features <- function(x, path = character()) { - f <- file.path(path, "feature_definitions.txt") - fts <- read.table(f, sep = "\t") - f <- file.path(path, "feature_peak_index.txt") + f <- file.path(path, "xcms_experiment_feature_definitions.txt") + fts <- read.table(f, sep = "\t", header = TRUE) + f <- file.path(path, "xcms_experiment_feature_peak_index.txt") if (!file.exists(f)) - stop("No \"feature_peak_index.txt\" file found in ", path) - pkidx <- read.table(f, sep = "\t") + stop("No \"xcms_experiment_feature_peak_index.txt\" file found in ", + path) + pkidx <- read.table(f, sep = "\t", header = TRUE) fts$peakidx <- unname(split(pkidx$peak_index, pkidx$feature_index)) x@featureDefinitions <- fts x diff --git a/man/PlainTextParam.Rd b/man/PlainTextParam.Rd index 6c2079d..1da84bc 100644 --- a/man/PlainTextParam.Rd +++ b/man/PlainTextParam.Rd @@ -21,15 +21,15 @@ PlainTextParam(path = tempdir()) \S4method{saveMsObject}{MsExperiment,PlainTextParam}(object, param) -\S4method{readMsObject}{MsExperiment,PlainTextParam}(object, param, spectraPath = character()) +\S4method{readMsObject}{MsExperiment,PlainTextParam}(object, param, ...) \S4method{saveMsObject}{Spectra,PlainTextParam}(object, param) -\S4method{readMsObject}{Spectra,PlainTextParam}(object, param, spectraPath = character()) +\S4method{readMsObject}{Spectra,PlainTextParam}(object, param, ...) \S4method{saveMsObject}{XcmsExperiment,PlainTextParam}(object, param) -\S4method{readMsObject}{XcmsExperiment,PlainTextParam}(object, param, spectraPath = character()) +\S4method{readMsObject}{XcmsExperiment,PlainTextParam}(object, param, ...) } \arguments{ \item{path}{For \code{PlainTextParam()}: \code{character(1)}, defining where the files @@ -43,10 +43,13 @@ 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{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 passed to the \code{loadResults()} method of -the MsBackend().} +\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 passed to the \code{loadResults()} +method of the MsBackend().} + +\item{...}{Additional parameters passed down to internal functions. E.g. +parameter \code{spectraPath} (see above).} } \value{ For \code{PlainTextParam()}: a \code{PlainTextParam} class. \code{saveMsObject()} @@ -56,43 +59,55 @@ restored data as an instance of the class specified with parameter \code{object} } \description{ The \code{saveMsObject()} and \code{readMsObject()} methods with the \code{PlainTextParam} -option enable users to save/load different type of MS object as a -collections of plain text files in/from a specified folder. This folder, -defined with the \code{path} parameter, will be created by the \code{storeResults()} -function. Any previous exports eventually present in that folder will be -overwritten. +option enable users to save/load different type of mass spectrometry (MS) +object as a collections of plain text files in/from a specified folder. +This folder, defined with the \code{path} parameter, will be created by the +\code{storeResults()} function. Writing data to a folder that contains already +exported data will result in an error. + +All data is exported to plain text files, where possible as tabulator +delimited text files. Data is exported using R's \code{\link[=write.table]{write.table()}} function, +thus, the text files will also contain row names (first column) as well as +column names (header). Strings in the text files are quoted. Some +information, in particular the content of \emph{parameter} classes within the +objects, is stored in JSON format instead. The MS object currently supported for import and export with this parameter -are : +are: \itemize{ -\item \code{MsBackendMzR} object -\item \code{Spectra} object -\item \code{MsExperiment} object -\item \code{XcmsExperiment} object +\item \code{MsBackendMzR} object, defined in the +(\href{https://bioconductor.org/packages/Spectra}{Spectra}) package. +\item \code{Spectra} object, defined in the +(\href{https://bioconductor.org/packages/Spectra}{Spectra}) package. +\item \code{MsExperiment} object, defined in the +(\href{https://bioconductor.org/packages/MsExperiment}{MsExperiment}) package. +\item \code{XcmsExperiment} object, defined in the +(\href{https://bioconductor.org/packages/xcms}{xcms}) package. } -See their respective section below for a detail of the exported files. +See their respective section below for details and formats of the +exported files. } -\section{On disk storage for \code{MsBackendMzR} objects}{ +\section{On-disk storage for \code{MsBackendMzR} objects}{ For \code{MsBackendMzR} objects, defined in the \code{Spectra} package, the following file is stored: \itemize{ \item The backend's \code{spectraData()} is stored in a tabular format in a text file -named \emph{backend_data.txt}. Each row of this file corresponds to a spectrum -with its respective metadata in the columns. +named \emph{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 \code{Spectra} objects}{ -For \code{Spectra} objects, defined in the \code{Spectra} package, the following files -are stored: +For \code{Spectra} objects, defined in the \code{Spectra} package, the files listed +below are stored. Any parameter passed to the \code{saveMsObject()} method using +its \code{...} parameter are passed to the \code{saveMsObject()} call of the +\code{Spectra}'s backend. \itemize{ -\item The backend data storage depends on its class; refer to its respective -section for more details. \item The \code{processingQueueVariables}, \code{processing}, \code{processingChunkSize()}, and \code{backend} class information of the object are stored in a text file named \emph{spectra_slots.txt}. Each of these slots is stored such that the name of @@ -102,6 +117,10 @@ data modifications are retained, is stored in a \code{json} file named \emph{spectra_processing_queue.json}. The file is written such that each processing step is separated by a line and includes all information about the parameters and functions used for the step. +\item The \code{Spectra}'s MS data (i.e. it's backend) is stored/exported using +the \code{saveMsObject()} method of the respective backend type. Currently +only backends for which the \code{saveMsObject()} method is implemented (see +above) are supported. } } @@ -109,49 +128,61 @@ the parameters and functions used for the step. For \code{MsExperiment} objects, defined in the \code{MsExperiment} package, the -exported data and related text files are: +exported data and related text files are listed below. Any parameter passed +to the \code{saveMsObject()} through \code{...} are passed to the \code{saveMsObject()} +calls of the individual MS data object(s) within the \code{MsExperiment}. + +Note that at present \code{saveMsObject()} with \code{PlainTextParam} does \strong{not} +export the full content of the \code{MsExperiment}, i.e. slots \verb{@experimentFiles}, +\verb{@qdata}, \verb{@otherData} and \verb{@metadata} are currently not saved. \itemize{ -\item The previously defined \code{Spectra} object-related files. See the respective -section for more information. -\item The \code{sampleData()} is stored as a text file named \emph{sample_data.txt}. Each -row of this file corresponds to a sample with its respective metadata in -the columns. -\item The links between the sample data and other data are stored in text -files named \emph{sample_data_links_....txt}, with "..." referring to the data -type. Each file corresponds to a mapping between the sample data and a -specific data type (e.g., Spectra). One file is written to map samples to -each data type. In each file, the first column corresponds to the sample -number and the second to the other data type number (e.g., spectrum number). -The table "element_metadata.txt" contains the metadata of each mapping -files \link{johannes could you maybe help me explain that one aha} +\item The \code{sampleData()} is stored as a text file named +\emph{ms_experiment_sample_data.txt}. Each row of this file corresponds to a +sample with its respective metadata in the columns. +\item The links between the sample data and any other data within the +\code{MsExperiment} are stored in text files named +\emph{ms_experiment_sample_data_links_....txt}, +with "..." referring to the data slot to which samples are linked. +Each file contains the mapping between the sample data and the elements in +a specific data slot (e.g., \code{Spectra}). The files are tabulator delimited +text files with two columns of integer values, the first representing the +index of a sample in the objects \code{sampleData()}, the second the index of +the assigned element in the respective object slot. +The table "ms_experiment_element_metadata.txt" contains the metadata of +each of the available mappings. +\item If the \code{MsExperiment} contains a \code{Spectra} object with MS data, it's +content is exported to the same folder using a \code{saveMsObject()} call on +it (see above for details of exporting \code{Spectra} objects to text files). } } \section{On-disk storage for \code{XcmsExperiment} objects}{ -For \code{XcmsExperiment} objects, defined in the \code{xcms} package, the exported -data and related text files are: +For \code{XcmsExperiment} objects, defined in the \emph{xcms} package, the exported +data and related text files are listed below. Any parameter passed +to the \code{saveMsObject()} through \code{...} are passed to the \code{saveMsObject()} +calls of the individual MS data object(s) within the \code{XcmsExperiment}. \itemize{ -\item The previously defined \code{MsExperiment} object-related files. See the -respective section for more information. -\item The \code{processHistory()} information of the object is stored in a \code{json} -file named \emph{process_history.json}. The file is written such that each -processing step is separated by a line and includes all information about -the parameters and functions used for that step. \item The chromatographic peak information obtained with \code{chromPeaks()} and \code{chromPeaksData()} is stored in tabular format in the text files -\emph{chrom_peaks.txt} and \emph{chrom_peak_data.txt}, respectively. The first file's -rows represent single peaks with their respective metadata in the columns. -The second file contains the same peaks by rows but with processing -information such as whether they are filled or merged in the columns. +\emph{xcms_experiment_chrom_peaks.txt} and +\emph{xcms_experiment_chrom_peak_data.txt}, respectively. The first file's +rows represent single peaks with their respective metadata in the columns +(only numeric information). The second file contains arbitrary additional +information/metadata for each peak (each row being one chrom peak). \item The \code{featureDefinitions()} are stored in a text file named -\emph{feature_definitions.txt}. Additionally, a second file named -\emph{feature_peak_index.txt} is generated to connect the features' definitions -with their names. Each row of the first file corresponds to a feature with -its respective metadata in the columns. The second file contains the -mapping of each chromatographic peak (e.g., one peak ID per row) to its -respective feature ID. +\emph{xcms_experiment_feature_definitions.txt}. Additionally, a second file +named \emph{ms_experiment_feature_peak_index.txt} is generated to connect the +features with the corresponding chromatographic peaks. Each row of the +first file corresponds to a feature with its respective metadata in the +columns. The second file contains the mapping between features and +chromatographic peaks (one peak ID per row). +\item The \code{processHistory()} information of the object is stored to a +file named \emph{xcms_experiment_process_history.json} in JSON format. +\item The \code{XcmsExperiment} directly extends the \code{MsExperiment} class, thus, +any MS data is saved using a call to the \code{saveMsObject} of the +\code{MsExperiment} (see above for more information). } } diff --git a/tests/testthat/test_MsBackendMzR.R b/tests/testthat/test_MsBackendMzR.R index fbdbaf3..6e3eb4a 100644 --- a/tests/testthat/test_MsBackendMzR.R +++ b/tests/testthat/test_MsBackendMzR.R @@ -10,7 +10,7 @@ test_that("saveMsObject,readMsObject,PlainTextParam,MsBackendMzR works", { param <- PlainTextParam(path = pth) saveMsObject(b, param = param) expect_true(dir.exists(pth)) - expect_true(file.exists(file.path(param@path, "backend_data.txt"))) + expect_true(file.exists(file.path(param@path, "ms_backend_data.txt"))) ## Loading data again b_load <- readMsObject(object = MsBackendMzR(), param) expect_true(inherits(b_load, "MsBackendMzR")) @@ -22,11 +22,13 @@ test_that("saveMsObject,readMsObject,PlainTextParam,MsBackendMzR works", { ## Check the spectraPath parameter. bp <- dataStorageBasePath(b) ## manually change dataStorage path of backend - sd <- read.table(file.path(param@path, "backend_data.txt"), sep = "\t", header = TRUE) + sd <- read.table(file.path(param@path, "ms_backend_data.txt"), + sep = "\t", header = TRUE) sd$dataStorage <- sub("msdata", "other", sd$dataStorage) - writeLines("# MsBackendMzR", con = file.path(param@path, "backend_data.txt")) + writeLines("# MsBackendMzR", + con = file.path(param@path, "ms_backend_data.txt")) write.table(sd, - file = file.path(param@path, "backend_data.txt"), + file = file.path(param@path, "ms_backend_data.txt"), sep = "\t", quote = FALSE, append = TRUE, row.names = FALSE) expect_error(readMsObject(MsBackendMzR(), param), "invalid class") @@ -40,7 +42,9 @@ test_that("saveMsObject,readMsObject,PlainTextParam,MsBackendMzR works", { param <- PlainTextParam(path = pth) saveMsObject(b_empty, param = param) expect_true(dir.exists(pth)) - expect_true(file.exists(file.path(param@path, "backend_data.txt"))) + expect_true(file.exists(file.path(param@path, "ms_backend_data.txt"))) expect_no_error(readMsObject(object = MsBackendMzR(), param)) - + res <- readMsObject(MsBackendMzR(), param) + expect_s4_class(res, "MsBackendMzR") + expect_true(length(res) == 0) }) diff --git a/tests/testthat/test_MsExperiment.R b/tests/testthat/test_MsExperiment.R index 0b1445b..736d9d2 100644 --- a/tests/testthat/test_MsExperiment.R +++ b/tests/testthat/test_MsExperiment.R @@ -15,15 +15,16 @@ mse_filt <- filterMzRange(mse, c(200, 500)) mse_filt <- filterRt(mse_filt, c(3000, 3500)) test_that("saveMsObject,readMsObject,PlainTextParam,MsExperiment works", { - pth <- file.path( "test_MsExperiment") + pth <- file.path(tempdir(), "test_MsExperiment") param <- PlainTextParam(path = pth) saveMsObject(mse_filt, param = param) expect_true(dir.exists(pth)) - expect_true(file.exists(file.path(param@path, "sample_data.txt"))) - expect_true(file.exists(file.path(param@path, "backend_data.txt"))) + expect_true(file.exists(file.path(param@path, + "ms_experiment_sample_data.txt"))) + expect_true(file.exists(file.path(param@path, "ms_backend_data.txt"))) expect_true(file.exists(file.path(param@path, "spectra_slots.txt"))) expect_true(file.exists(file.path(param@path, "spectra_processing_queue.json"))) - pattern <- "sample_data_links_.*\\.txt" + pattern <- "ms_experiment_sample_data_links_.*\\.txt" expect_true(length(list.files(param@path, pattern = pattern)) > 0) ## Loading data again load_mse <- readMsObject(object = MsExperiment(), param) @@ -35,21 +36,30 @@ test_that("saveMsObject,readMsObject,PlainTextParam,MsExperiment works", { expect_equal(length(a@processingQueue), length(b@processingQueue)) expect_equal(a@processingQueue[[1L]]@ARGS, b@processingQueue[[1L]]@ARGS) expect_equal(rtime(a), rtime(b)) + expect_equal(mz(a[1:10]), mz(b[1:10])) expect_no_error(filterRt(load_mse, c(3000, 3500))) - ## Check the spectraPath parameter. - bp <- dataStorageBasePath(mse_filt@spectra) - ## manually change dataStorage path of backend - sd <- read.table(file.path(param@path, "backend_data.txt"), header = TRUE) - sd$dataStorage <- sub("faahKO", "other", sd$dataStorage) - writeLines("# MsBackendMzR", con = file.path(param@path, "backend_data.txt")) - write.table(sd, - file = file.path(param@path, "backend_data.txt"), - sep = "\t", quote = FALSE, - append = TRUE, row.names = FALSE) - expect_error(readMsObject(MsExperiment(), param), "invalid class") - expect_no_error(readMsObject(MsExperiment(), param, spectraPath = bp)) - param <- PlainTextParam(tempdir()) - expect_error(readMsObject(MsExperiment(), param), "No 'sample_data") + expect_error(readMsObject(MsExperiment(), param), + "No 'ms_experiment_sample_data") + + ## Export of object without links + mse_2 <- MsExperiment(spectra = spectra(mse), sampleData = sampleData(mse)) + pth <- file.path(tempdir(), "test_MsExperiment2") + param <- PlainTextParam(path = pth) + saveMsObject(mse_2, param = param) + load_mse <- readMsObject(object = MsExperiment(), param) + expect_true(inherits(load_mse, "MsExperiment")) + expect_equal(sampleData(mse_2), sampleData(load_mse)) + expect_equal(mse_2@sampleDataLinks, load_mse@sampleDataLinks) + + ## Export an empty MsExperiment + a <- MsExperiment() + pth <- file.path(tempdir(), "text_msexperiment_empty") + param <- PlainTextParam(path = pth) + saveMsObject(a, param = param) + res <- readMsObject(MsExperiment(), param = param) + expect_true(nrow(sampleData(res)) == nrow(sampleData(a))) + res@sampleData <- a@sampleData + expect_equal(res, a) }) diff --git a/tests/testthat/test_Spectra.R b/tests/testthat/test_Spectra.R index 822f83a..987d592 100644 --- a/tests/testthat/test_Spectra.R +++ b/tests/testthat/test_Spectra.R @@ -17,50 +17,49 @@ test_that("saveMsObject,readMsObject,PlainTextParam,Spectra works", { expect_false(is.null(param2)) expect_error(new("PlainTextParam", path = c(tempdir(), tempdir()))) saveMsObject(s, param = param) - expect_true(file.exists(file.path(param@path, "backend_data.txt"))) + expect_true(file.exists(file.path(param@path, "ms_backend_data.txt"))) expect_true(file.exists(file.path(param@path, "spectra_slots.txt"))) expect_true(file.exists(file.path(param@path, "spectra_processing_queue.json"))) ## Loading data again - s_load <- readMsObject(object = Spectra(), param) # for this test dataset we have error when validating the backend object + s_load <- readMsObject(object = Spectra(), param) expect_true(inherits(s_load, "Spectra")) expect_true(inherits(s_load@backend, "MsBackendMzR")) - s <- dropNaSpectraVariables(s) + ## Check spectra content expect_equal(length(s@processingQueue), length(s_load@processingQueue)) expect_equal(s@processingQueue[[1L]]@ARGS, s_load@processingQueue[[1L]]@ARGS) expect_equal(s@processingQueueVariables, s_load@processingQueueVariables) expect_equal(s@processing, s_load@processing) expect_equal(processingChunkSize(s), processingChunkSize(s_load)) + ## Check backend data + expect_equal(s@backend@peaksVariables, s_load@backend@peaksVariables) + s <- dropNaSpectraVariables(s) expect_equal(s@backend@spectraData, s_load@backend@spectraData) + ## Expect same actual content expect_equal(rtime(s), rtime(s_load)) expect_equal(mz(s[1:10]), mz(s_load[1:10])) expect_no_error(filterRt(s_load, c(3000, 3500))) - ## Check the spectraPath parameter - bp <- dataStorageBasePath(s) - ## Changing the path in the MsBackendMzR to simulate moving the exported data - sd <- read.table(file.path(param@path, "backend_data.txt"), header = TRUE, sep = "\t") - sd$dataStorage <- sub("msdata", "other", sd$dataStorage) - writeLines("# MsBackendMzR", con = file.path(param@path, "backend_data.txt")) - write.table(sd, - file = file.path(param@path, "backend_data.txt"), - sep = "\t", quote = FALSE, - append = TRUE, row.names = FALSE) - expect_error(readMsObject(Spectra(), param), "invalid class") - expect_no_error(readMsObject(Spectra(), param, spectraPath = bp)) - + ## Errors param <- PlainTextParam(file.path(tempdir())) expect_error(readMsObject(Spectra(), param), "No 'spectra_slots") + ## Unsupported backend + s2 <- setBackend(s, MsBackendMemory()) + expect_error(saveMsObject(s2, param = param), "MsBackendMemory") + ## check empty spectra s <- filterRt(s, c(900, 1000)) + expect_true(length(s) == 0) pth <- file.path(tempdir(), "test_spectra_empty") param <- PlainTextParam(path = pth) saveMsObject(s, param = param) - expect_true(file.exists(file.path(param@path, "backend_data.txt"))) + expect_true(file.exists(file.path(param@path, "ms_backend_data.txt"))) expect_true(file.exists(file.path(param@path, "spectra_slots.txt"))) expect_true(file.exists(file.path(param@path, "spectra_processing_queue.json"))) expect_no_error(readMsObject(object = Spectra(), param)) + s_load <- readMsObject(object = Spectra(), param) + expect_equal(length(s), length(s_load)) }) diff --git a/tests/testthat/test_XcmsExperiment.R b/tests/testthat/test_XcmsExperiment.R index c3f3b46..2dd2c3e 100644 --- a/tests/testthat/test_XcmsExperiment.R +++ b/tests/testthat/test_XcmsExperiment.R @@ -10,18 +10,30 @@ test_that("saveMsObject,readMsObject,PlainTextParam,XcmsExperiment works", { param <- PlainTextParam(path = pth) saveMsObject(xmseg_filt, param = param) expect_true(dir.exists(pth)) - expect_true(file.exists(file.path(param@path, "sample_data.txt"))) - expect_true(file.exists(file.path(param@path, "backend_data.txt"))) - expect_true(file.exists(file.path(param@path, "spectra_slots.txt"))) - expect_true(file.exists(file.path(param@path, "spectra_processing_queue.json"))) - expect_true(file.exists(file.path(param@path, "process_history.json"))) - expect_true(file.exists(file.path(param@path, "chrom_peaks.txt"))) - expect_true(file.exists(file.path(param@path, "chrom_peak_data.txt"))) - expect_true(file.exists(file.path(param@path, "feature_definitions.txt"))) - expect_true(file.exists(file.path(param@path, "feature_peak_index.txt"))) + expect_true(file.exists( + file.path(param@path, "ms_experiment_sample_data.txt"))) + expect_true(file.exists( + file.path(param@path, "ms_backend_data.txt"))) + expect_true(file.exists( + file.path(param@path, "spectra_slots.txt"))) + expect_true(file.exists( + file.path(param@path, "spectra_processing_queue.json"))) + expect_true(file.exists( + file.path(param@path, "xcms_experiment_process_history.json"))) + expect_true(file.exists( + file.path(param@path, "xcms_experiment_chrom_peaks.txt"))) + expect_true(file.exists( + file.path(param@path, "xcms_experiment_chrom_peak_data.txt"))) + expect_true(file.exists( + file.path(param@path, "xcms_experiment_feature_definitions.txt"))) + expect_true(file.exists( + file.path(param@path, "xcms_experiment_feature_peak_index.txt"))) ## load data again - load_xmse <- readMsObject(object = XcmsExperiment(), param) + ## This error is not thrown with rcmdcheck::rcmdcheck() + ## expect_error(readMsObject(new("XcmsExperiment"), param), "load the library") + ## library(Spectra) + load_xmse <- readMsObject(new("XcmsExperiment"), param) expect_true(inherits(load_xmse, "XcmsExperiment")) expect_equal(xmseg_filt@featureDefinitions, load_xmse@featureDefinitions) @@ -44,16 +56,32 @@ test_that("saveMsObject,readMsObject,PlainTextParam,XcmsExperiment works", { ## Check the spectraPath parameter. bp <- dataStorageBasePath(xmseg_filt@spectra) ## manually change dataStorage path of backend - sd <- read.table(file.path(param@path, "backend_data.txt"), header = TRUE) + sd <- read.table(file.path(param@path, "ms_backend_data.txt"), + header = TRUE) sd$dataStorage <- sub("faahKO", "other", sd$dataStorage) - writeLines("# MsBackendMzR", con = file.path(param@path, "backend_data.txt")) + writeLines( + "# MsBackendMzR", con = file.path(param@path, "ms_backend_data.txt")) write.table(sd, - file = file.path(param@path, "backend_data.txt"), - sep = "\t", quote = FALSE, - append = TRUE, row.names = FALSE) - expect_error(readMsObject(XcmsExperiment(), param), "invalid class") - expect_no_error(readMsObject(XcmsExperiment(), param, spectraPath = bp)) + file = file.path(param@path, "ms_backend_data.txt"), + sep = "\t", quote = TRUE, append = TRUE) + expect_error(readMsObject(new("XcmsExperiment"), param), "invalid class") + expect_no_error(readMsObject(XcmsExperiment(), + param, spectraPath = bp)) param <- PlainTextParam(tempdir()) - expect_error(readMsObject(XcmsExperiment(), param), "No 'sample_data") + expect_error(readMsObject(XcmsExperiment(), param), + "No 'ms_experiment_sample_data") + + ## Export an empty object. + a <- XcmsExperiment() + pth = file.path(tempdir(), "test_xcmsexp_empty") + param <- PlainTextParam(path = pth) + saveMsObject(a, param) + res <- readMsObject(XcmsExperiment(), param) + expect_equal(nrow(chromPeaks(a)), nrow(chromPeaks(res))) + expect_equal(colnames(chromPeaks(a)), colnames(chromPeaks(res))) + expect_equal(nrow(chromPeakData(a)), nrow(chromPeakData(res))) + expect_equal(colnames(chromPeakData(a)), colnames(chromPeakData(res))) + expect_equal(a@featureDefinitions, res@featureDefinitions) + expect_equal(a@processHistory, res@processHistory) })