Skip to content

Commit

Permalink
Initial commit of {relay}
Browse files Browse the repository at this point in the history
  • Loading branch information
al-obrien committed Jul 12, 2021
0 parents commit 6c38390
Show file tree
Hide file tree
Showing 39 changed files with 2,275 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
^renv$
^renv\.lock$
^.*\.Rproj$
^\.Rproj\.user$
^LICENSE\.md$
1 change: 1 addition & 0 deletions .Rprofile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source("renv/activate.R")
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.Rproj.user
.Rhistory
.RData
.Ruserdata
21 changes: 21 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Package: relay
Type: Package
Title: Flexible workflow tracking
Version: 0.1.5
Author: person('Allen', 'OBrien', email= '[email protected]', role = c('aut', 'cre'))
Maintainer: <[email protected]>
Description: Create objects and YAML configuration files to track progress, status, and completion of workflows in a flexible way.
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
Depends:
R (>= 3.5.0)
Imports:
purrr,
utils,
yaml
Suggests:
haven,
readr,
readxl
RoxygenNote: 7.1.1
2 changes: 2 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
YEAR: 2021
COPYRIGHT HOLDER: relay authors
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# MIT License

Copyright (c) 2021 relay authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
38 changes: 38 additions & 0 deletions LICENSE.note
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
The {relay} package as a whole is distributed under MIT; however, {relay} depends
upon other open source software components, in particular another package called
{yaml} which is covered by a BSD-3-Clause copyright. The description of this
copyright for {yaml} is provided below:


# BSD_3_clause

Copyright (c) 2008-2016 Vanderbilt University
Copyright (c) 2016-2018 Vanderbilt University Medical Center

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.

Neither the name of Vanderbilt University nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17 changes: 17 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by roxygen2: do not edit by hand

S3method(summary,baton)
export(batch_load)
export(clear_batons)
export(compress_bundle)
export(copy_dir)
export(copy_files)
export(create_baton)
export(create_bundle)
export(drop_baton)
export(grab_baton)
export(intercept_baton)
export(list_files_recursive)
export(locate_batons)
export(pass_baton)
export(quick_copy)
124 changes: 124 additions & 0 deletions R/constructor.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#' Create a baton
#'
#' \code{create_baton} is a constructor of an S3 class used for tracking workflows.
#'
#' By default, the content of the \emph{baton} will be empty, only the metadata will be populated. The S3 object created will have an associated
#' YAML file made, in a temporary location if no set location provided to \code{loc}. There is also the ability to automatically assign the S3 object
#' to the R environment in case that is easier to remember.
#'
#' The metadata of a bundle includes the following:
#' \enumerate{
#' \item id: unique ID of baton based upon time stamp and random numbers.
#' \item relay_start: time stamp when baton first created (matched ID).
#' \item relay_finish: time the baton was last passed (will not be populated if in middle of pass).
#' \item all_passes: time stamps for all completed passes.
#' \item pass_complete: whether or not the baton completed its latest pass.
#' \item passes_completed: the number of successful passes completed.
#' \item location: where the baton YAML file has been saved.
#' \item dropped: boolean value of whether the baton has been dropped, signalling and end to the relay.
#' }
#'
#' @note Although some generics (\code{\link{summary.baton}}) are used in \{relay\}, most functions just check for the class and do not proceed unless
#' it is a \emph{baton}. This may be rewritten to use only generics or R6 classes but right now this hybrid approach was used for simplicity.
#'
#' @param content A list of custom content that the baton should carry.
#' @param loc The location of the associated YAML file (defaults to a temporary location).
#' @param auto_assign Boolean value to determine if name assignment is to be automatic).
#' @param envir Environment where \code{auto_assign} should write to.
#' @param bundled Boolean value to determine if the baton should be created within a bundle; default is set to \code{FALSE}.
#' @param bundle_params List of parameters to pass to \code{\link{create_bundle}}.
#'
#' @return S3 class object.
#' @export
#' @examples
#' \dontrun{
#' my_baton <- create_baton(loc = file.path('path', 'to', 'save', 'yaml'))
#' }
create_baton <- function(content = list(), loc = NULL, auto_assign = FALSE, envir = .GlobalEnv, bundled = FALSE, bundle_params = list()) {

if(!is.list(content)) stop('`content` parameter must be a list.')

# Metadata
start <- Sys.time()
id <- paste0(c(format(start, '%Y%m%d%H%M%S'), '-',
sample(c(LETTERS, letters), replace = T, size = 4),
sample(c(0:9), 4, replace = T)),
collapse = '')

# _baton file location
loc <- if(is.null(loc)) tempdir() else normalizePath(loc, mustWork = TRUE);
loc <- file.path(loc, paste0('_baton-', id, '.yml'))

baton <- structure(list(metadata = list(id = id,
relay_start = format(start, '%Y-%m-%d %H-%M-%S'),
relay_finish = NA,
all_passes = NULL,
pass_complete = FALSE,
passes_completed = 0,
location = loc,
dropped = FALSE),
content = content),
class = "baton")

# Perform validation
validate_baton(baton)

# Create YAML for tracking
convert_baton2yml(baton = baton, write = TRUE)
message('Baton created... associated file created at: ', baton$metadata$loc)

if(bundled) {
if(!all(is.list(bundle_params), names(bundle_params) %in% c('dir', 'tree', 'tag', 'mode', '...'))) stop('Invalid bundle parameter provided. Give as a list.')
bundle_path <- do.call(create_bundle, args = c(baton = list(baton), bundle_params))
baton <- relocate_baton(baton, loc = bundle_path, silent = TRUE)
}

if(auto_assign) {
assign(x = paste0('_baton-', baton$metadata$id), value = baton, envir = envir)
} else {
return(baton)
}
}

#' Create a bundle
#'
#' \code{create_bundle} is a helper function to create a set of skeleton folders under a main bundle directory.
#'
#' Creating a bundle can be done at the same time as \code{\link{create_baton}}. However, it can be used without a baton as well
#' to easily make a set of empty folders to populate later. When using as part of a \{relay\}, the primary purpose is to store information that
#' passes checks from batons.
#'
#' @param baton R object of S3 class, created by \code{\link{create_baton}}.
#' @param dir character value for loaction to create the bundle on the file system.
#' @param tree character vector for sub directories; default has no sub-directories set.
#' @param tag character value for prefixed label to the bundled folder.
#' @param mode passed to \code{\link{Sys.chmod}}.
#' @param ... passed to \code{\link{dir.create}}.
#'
#' @return Character vector for the location of created bundle.
#' @export
#' @examples
#' \dontrun{
#' create_bundle(my_baton, dir = '/path/to/relay/raw_bundle',
#' tree = c('raw', 'processed', 'metadata', 'output', '/raw/sub-raw'))
#' }
create_bundle <- function(baton, dir, tree, tag = '_bundle-', mode, ...){

validate_baton(baton)

dir <- normalizePath(dir)
baton_id <- baton$metadata$id
bundle_path <- file.path(dir, paste0(tag, baton$metadata$id))

# Create the folder skeleton
dir.create(bundle_path, ...)
purrr::walk(tree, ~dir.create(file.path(bundle_path, .)))

if(!missing(mode)){
purrr::walk(tree, ~Sys.chmod(file.path(bundle_path, .), mode = mode, use_umask = FALSE));
Sys.chmod(bundle_path, mode = mode, use_umask = FALSE);
}

return(bundle_path)

}
35 changes: 35 additions & 0 deletions R/conversion.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#' Convert from YAML to a baton
#'
#' Internal function.
convert_yml2baton <- function(x) {
if(file.exists(x)) x <- yaml::read_yaml(x) else x <- yaml::read_yaml(text = x)

if(is.null(x[['metadata']]) || is.null(x[['metadata']][['id']])) stop('Metadata missing from baton, likely due to corrupted or non-baton generated YAML file')

class(x) <- "baton"
x
}

#' Convert baton to YAML
#'
#' Internal function.
convert_baton2yml <- function(baton, write = TRUE, ...) {

# Error check
validate_baton(baton)

# Check for errors (e.g. duplicates in yaml)
tryCatch(expr = {
invisible(yaml::yaml.load(yaml::as.yaml(baton)))

if(write) {
if(file.exists(baton$metadata$location)) warning('Overwritting existing baton file...')
yaml::write_yaml(baton, baton$metadata$location, ...)
} else {
yaml::as.yaml(baton)
}
},
error=function(err) {
stop('Error encountered when converting to YAML: ', err)
})
}
Loading

0 comments on commit 6c38390

Please sign in to comment.