#' @title Trace functions which create files
#' @description Some functions create output files during their execution;
#' some of them are slow, so the probability that something goes wrong
#' during their execution is not null, with the consequence that output
#' files are incomplete, or that undesired temporary files are left on
#' the filesystem.
#'
#' This functions is though to manage these situations.
#'
#' [trace_function] runs a function and checks if errors occur during its execution;
#' in this case, the files created by it are deleted (only if the timestamp
#' of the files is subsequent to the time of execution of the function).
#' If the code interrupts before the function ends, the paths of the files
#' intended to be created by the function are saved within the package, so that
#' they can be easily deleted in a second time; to do it, simply
#' run [clean_traces].
#'
#' Other intermediate functions are used internally:
#' [start_trace] saves the paths of the files intended to be created within
#' a text file;
#' [end_trace] deletes this text file (it is used by [trace_function] when
#' a function stops without errors);
#' [clean_trace] deletes this text file and the intended output files (it is
#' used by [trace_function] when a function stops with errors).
#'
#' @param trace_fun The function to be run.
#' @param ... Arguments of the function `fun`
#' @param trace_files Vector of the files intended to be created by `fun`
#' (for now, providing it is mandatory). Also temporary files can be indicated
#' here.
#' @param trace_funname The name of the function to be run (in [start_trace]) or
#' to be cleaned (in [clean_traces]; if NA, all the traces are cleaned).
#' @param tracename The path of the text file containing the log information
#' generated by [start_trace].
#' @return NULL (the function is called for its side effects)
#' @author Luigi Ranghetti, phD (2019)
#' @references L. Ranghetti, M. Boschetti, F. Nutini, L. Busetto (2020).
#' "sen2r": An R toolbox for automatically downloading and preprocessing
#' Sentinel-2 satellite data. _Computers & Geosciences_, 139, 104473.
#' \doi{10.1016/j.cageo.2020.104473}, URL: \url{https://sen2r.ranghetti.info/}.
#' @note License: GPL 3.0
#' @importFrom methods is
#' @name start_trace
#' @rdname trace_function
#' @keywords internal
start_trace <- function(trace_files, trace_funname) {
# path in which trace txt files are placed
trace_path <- file.path(dirname(attr(load_binpaths(), "path")),"logs")
dir.create(trace_path, showWarnings = FALSE)
# define name of the tracelog
tracename <- file.path(
trace_path,
paste0(
strftime(Sys.time(), "%y%m%d%H%M%S"), "_",
trace_funname, ".txt"
)
)
# write tracename with the paths of the expected files
writeLines(trace_files, tracename)
# return its path
tracename
}
#' @name end_trace
#' @rdname trace_function
end_trace <- function(tracename) {
if (file.exists(tracename)) {unlink(tracename)}
}
#' @name clean_trace
#' @rdname trace_function
clean_trace <- function(tracename) {
# do nothing if tracename does not exist
if (!file.exists(tracename)) {return(invisible(NULL))}
# retrieve when the trace started
tracetime <- as.POSIXct(
gsub("^([0-9]+)\\_.*\\.txt$","\\1",basename(tracename)),
format="%y%m%d%H%M%S"
)
# read expected output filenames
outfilenames <- readLines(tracename)
# delete files if they were created after tracetime
for (outfilename in outfilenames) {
if (file.exists(outfilename)) {
outfiletime <- file.mtime(outfilename)
if (outfiletime > tracetime) {
unlink(outfilename, recursive=TRUE)
}
}
}
# delete trace file
unlink(tracename)
}
#' @name trace_function
#' @rdname trace_function
trace_function <- function(trace_fun, trace_files, trace_funname=NA, ...) {
# Start tracing
if (is.na(trace_funname)) {
trace_funname <- deparse(substitute(trace_fun))
}
tracename <- start_trace(trace_files, trace_funname)
# run function
fun_out <- tryCatch(
trace_fun(...),
error = print
)
# in case of errors, clean the ouput and return an error;
# otherwise, end the trace and return the output
if (is(fun_out, "error")) {
clean_trace(tracename)
stop(fun_out)
} else {
end_trace(tracename)
return(fun_out)
}
}
#' @name clean_traces
#' @rdname trace_function
clean_traces <- function(trace_funname=NA) {
# import the content of the path in which trace txt files are placed
trace_path <- system.file("logs",package="sen2r")
tracenames_df <- data.frame(
"name" = list.files(trace_path, "^([0-9]+)\\_(.*)\\.txt$", full.names = TRUE),
stringsAsFactors = FALSE
)
tracenames_df$time <- as.POSIXct(
gsub("^([0-9]+)\\_(.*)\\.txt$","\\1",basename(tracenames_df$name)),
format="%y%m%d%H%M%S"
)
tracenames_df$fun <- gsub("^([0-9]+)\\_(.*)\\.txt$","\\2",basename(tracenames_df$name))
# if required, filter on trace_funname
if (!is.na(trace_funname)) {
tracenames_df <- tracenames_df[tracenames_df$fun==trace_funname,]
}
# if empty, exit
if (nrow(tracenames_df)==0) {return(invisible(NULL))}
# read the names of the single files
outfilenames <- lapply(tracenames_df$name, readLines)
outfiles_df <- data.frame(
"name" = unlist(outfilenames),
"tracename" = rep(tracenames_df$name, sapply(outfilenames, length)),
stringsAsFactors = FALSE
)
outfiles_df$time <- file.mtime(outfiles_df$name)
outfiles_df$tracetime <- tracenames_df[
match(outfiles_df$tracename, tracenames_df$name),
"time"
]
# delete files if they were created after each tracetime
for (i in seq_len(nrow(outfiles_df))) {
if (file.exists(outfiles_df[i,"name"])) {
if (outfiles_df[i,"time"] > outfiles_df[i,"tracetime"]) {
unlink(outfiles_df[i,"name"], recursive=TRUE)
}
}
}
# delete trace files
unlink(tracenames_df$name)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.