# HG changeset patch # User ecology # Date 1742298154 0 # Node ID 9e5a6e52a6e87bac4dbd340de065ba1f91b36ee9 planemo upload for repository https://github.com/galaxyecology/tools-ecology/tree/master/tools/ocean_neural_network commit a54328ae8c3f3cddaa65f81e9396b2ed18b0bcd6 diff -r 000000000000 -r 9e5a6e52a6e8 canyonb.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/canyonb.xml Tue Mar 18 11:42:34 2025 +0000 @@ -0,0 +1,146 @@ + + estimation of ocean CO2 variables and nutrient concentrations + + 0.9.3 + 0 + + + r-base + r-r.utils + r-canyonb + r-logger + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10.3389/fmars.2018.00328 + + diff -r 000000000000 -r 9e5a6e52a6e8 run_tool_canyonb.R --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/run_tool_canyonb.R Tue Mar 18 11:42:34 2025 +0000 @@ -0,0 +1,729 @@ +# ██████╗ ███████╗███████╗ ██████╗██████╗ ██╗██████╗ ████████╗██╗ ██████╗ ███╗ ██╗#nolint +# ██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗██║██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║#nolint +# ██║ ██║█████╗ ███████╗██║ ██████╔╝██║██████╔╝ ██║ ██║██║ ██║██╔██╗ ██║#nolint +# ██║ ██║██╔══╝ ╚════██║██║ ██╔══██╗██║██╔═══╝ ██║ ██║██║ ██║██║╚██╗██║#nolint +# ██████╔╝███████╗███████║╚██████╗██║ ██║██║██║ ██║ ██║╚██████╔╝██║ ╚████║#nolint +# ╚═════╝ ╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝#nolint + +# this R script run canyon B on multi profile! +# launch : +# Rscript run_tool_galaxy.R $1 $2 $3 $4 $5 $6 $7 $8 $9 +# where arguments are +# $1 (mandatory) path of the txt or csv file to be processed +# $2 to 5 (optional 1) name of the column pressure, temperature, salinity and oxygen +# $6 to 9 (optional 2) name of the column error for pres, temp, sal and oxy +# +# input : +# .csv built by the user with : +# separator ";" +# mandatory column name : +# date/latitude/longitude/pressure/temperature/salinity/oxygen +# optional column name for error: +# epre/etemp/epsal/edoxy +# only arg 1 +# +# or +# +# .txt built by the tools QCV-ingestor-harmonizer then QCV-odv-tool +# (from easy) +# only arg 1 +# +# or +# .txt extract from the (web)ODV software +# (from ODV) +# need args 1 to 5 at list (optional, args 6 to 9) +# +# Important : +# canyon B reads : +# latitude [-90 to 90°N] +# longitude [-180 to 180°E] +# pressure in dbar +# tempereture in °C +# salinity in psu +# oxygen in µmol/kg +# +# ouput : +# input csv or txt enriched with canyonb output including their errors: +# for more information read +# https://github.com/HCBScienceProducts/CANYON-B/blob/master/CANYONB.R +# NO3 (nitrate) +# PO4 (phosphate) +# AT (total alkalinity) +# CT (total carbon) +# pH (ph) +# pCO2 (pCO2) +# SiOH4 (silicate) +# output is visible with odv +# +# + tool_canyonb_galaxy.log save where the tool runs +# +# Remarks : +# (1) for argo floats, read synthetic profile with qcv-ingestor-harmonizer +# then qcv-odv-tool to have temperature, salinity and oxygen alined at the +# same level +# (2) the tool merge complementary lines only, it does not interpolate TS, +# on BGC level or vice versa +# exple : L1 : P = 4.5 T = 25 S = 35.4 O2 = NA +# L2 : P = 4.5 T = NA S = 35.4 O2 = 345.6 +# merged line : P = 4.5 T = NA S = 35.4 O2 = 345.6 +# (3) Regarding .txt built by the tools QCV-ingestor-harmonizer then +# QCV-odv-tool, if PRES_DM or RTADJUSTED are empty for BGC variables +# (like in argo files), then tool take PRES_RAW for all variables + +# ███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗#nolint +# ██╔════╝██║ ██║████╗ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝#nolint +# █████╗ ██║ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗#nolint +# ██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║#nolint +# ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║#nolint +# ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝#nolint +# +initialisation_log_file <- function() { + + # directory + in_log <- getwd() + + # file name + datetime <- format(Sys.time(), "%Y-%m-%dT%H%M") + log_file_name <- paste0(paste("tool_canyonb_galaxy", + datetime, + sep = "_"), + ".log") + log_file <- paste(in_log, log_file_name, sep = "/") + + if (file.exists(log_file)) { + file.remove(log_file) + } + + # creation + logger::log_appender(logger::appender_file(log_file, + max_lines = 5000, + max_files = 5L)) +} + +update_log_file <- function(type, msge, stop = FALSE) { + + # update lofg file according to the message type + if (type == "info") { + logger::log_info(msge) + }else if (type == "warning") { + logger::log_warn(msge) + }else if (type == "error") { + logger::log_warn(msge) + }else if (type == "done") { + logger::log_info(msge) + logger::log_info("END of the process") + } + message(paste(toupper(type), msge, sep = " ")) + + # stop process + if (stop == TRUE) { + logger::log_info("END of the process") + stop() + } + +} + +exist_in_list <- function(value, lst) { + return(value %in% lst) +} + +convert_name_from_semantics <- function(elem, matrix) { + + # convert names to canyonB semantics + if (!(elem %in% names(matrix))) { + # Use lapply to check if the value is in any list + result <- lapply(matrix, exist_in_list, value = elem) + # Get the key where the value is found + key <- names(matrix)[which(unlist(result))] + return(key) + }else { + return(elem) + } +} + +datetime_standardisation <- function(datetime, + in_mode = NULL, + out_mode = NULL) { + + if ((!is.null(in_mode) && in_mode == "odv") + || (!is.null(out_mode) && out_mode == "odv")) { + date_format <- "%Y-%m-%dT%H:%M:%OS3" + if (any(grepl("T", datetime))) { + datetime <- gsub("Z", + "", + gsub("T", " ", datetime)) + date_format <- "%Y-%m-%d %H:%M:%S" + } + }else if ((!is.null(in_mode) && in_mode == "canyonb") + || (!is.null(out_mode) && out_mode == "canyonb")) { + date_format <- "%Y-%m-%d %H:%M:%S" + } + + if (!is.null(in_mode)) { + out_datetime <- as.POSIXct(datetime, format = date_format) + }else if (!is.null(out_mode)) { + out_datetime <- format(datetime, date_format) + } + + return(out_datetime) + +} + +collapse_dupli_by_column <- function(x) { + + # collapse if and only if rows are complementary + x <- as.data.frame(x) + # default output + collapse <- NULL + + # test complementary + to_be_collapse <- FALSE + test1 <- colSums(!is.na(x)) + if (any(test1 > 1) == FALSE) { + to_be_collapse <- TRUE + } + if (to_be_collapse == FALSE) { + test2 <- unlist(lapply(seq(1, dim(x)[2], 1), + function(y) dim(unique(x[, y]))[1])) + if (any(test2[test1 > 1] > 1) == FALSE) { + to_be_collapse <- TRUE + } + } + + if (to_be_collapse == TRUE) { + # it is complementary + reduce_tmp <- lapply(seq(1, dim(x)[2], 1), + function(xy) dplyr::coalesce(!!! x[, xy])) + reduce_tmp <- unlist(reduce_tmp) + reduce <- data.frame(matrix(NA, 1, dim(x)[2])) + reduce[!(test1 == 0)] <- reduce_tmp[!(test1 == 0)] + names(reduce) <- names(x) + collapse <- reduce + } + return(collapse) +} + +arrange_duplicate <- function(df, list_common, list_sort = NULL) { + + library(tidyr) + library(dplyr) + library(stringr) + + # need list of column help to group df and identify duplicate + # 1. collapse real duplicate + # 2. remove line with other column all na + + # list of column to be collapsed if complementary + list_all <- names(df) + line_common <- lapply(list_common, function(x) which(list_all == x)) + list_others <- list_all[-unlist(line_common)] + + # arrange df to reduce the number of action (action 2 by row) + df <- filter(df, rowSums(is.na(df[, list_others])) != ncol(df[, list_others])) + + # define duplicate in df (action 1) + dupli <- df %>% + group_by(across(all_of(list_common))) %>% + arrange(across(all_of(list_common))) %>% + filter(n() > 1) %>% + mutate(n_line = row_number()) %>% + ungroup() + + # working on potential duplicate if exist + if (dim(dupli)[1] > 0) { + # remove potential duplicate + without_dupli <- df %>% + group_by(across(all_of(list_common))) %>% + filter(!n() > 1) %>% + ungroup() + + # how many duplicate and where are they? + pos1 <- which(dupli$n_line == 1) + rep <- pos1[seq(2, length(pos1), 1)] - pos1[seq(1, length(pos1) - 1, 1)] + rep <- c(rep, length(dupli$n_line) - pos1[length(pos1)] + 1) + n_dupli <- lapply(seq(1, length(rep), 1), + function(x) rep(x, rep[x])) + dupli$n_dupli <- unlist(n_dupli) + + # collapse duplicate when they are complementary + reduce_dupli <- lapply(seq(1, max(dupli$n_dupli), 1), + function(group) { + lignes <- which(dupli$n_dupli == group) + x <- dupli[lignes, list_others] + collapse <- collapse_dupli_by_column(x) + if (!is.null(collapse)) { + table <- cbind.data.frame(dupli[lignes[1], + list_common], + collapse) + }else { + table <- dupli[lignes, c(list_common, + list_others)] + } + return(table) + }) + reduce_dupli <- do.call(rbind.data.frame, reduce_dupli) + + # combine with table wihtout duplicate + df <- rbind.data.frame(without_dupli, reduce_dupli) + } + + return(df) +} + +# ███╗ ███╗ █████╗ ██╗███╗ ██╗ +# ████╗ ████║██╔══██╗██║████╗ ██║ +# ██╔████╔██║███████║██║██╔██╗ ██║ +# ██║╚██╔╝██║██╔══██║██║██║╚██╗██║ +# ██║ ╚═╝ ██║██║ ██║██║██║ ╚████║ +# ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ +# + +# full path of csv file name ----------------------------------- +##Load arguments + +args_full <- commandArgs(trailingOnly = TRUE) + +# Filter out empty arguments +cmd_args <- args_full[nzchar(args_full)] # Removes elements that are empty ("") or just spaces + +# Replace __ob__ with [ and __cb__ with ] +args <- gsub("__ob__", "[", cmd_args) +args <- gsub("__cb__", "]", args) +if (length(args) < 1) { + stop("This tool needs at least 1 argument") +}else { + csv_file <- args_full[1] +} + +# compatibility matrix ----------------------------------------- +odv_date_name <- "yyyy-mm-ddThh:mm:ss.sss" +mapping_date <- c("date", "Date", odv_date_name) +mapping_lat <- c("latitude", + "latitude [degrees_north]", + "Latitude [degrees_north]") +mapping_lon <- c("longitude", + "longitude [degrees_east]", + "Longitude [degrees_east]") + +error_args <- paste0("This tool needs at least 5 mapped ", + "variables if the mapping is required") +if (length(args) > 1 ) { + mapping_pres <- args[2] + if (mapping_pres == "") { + stop(error_args) + } + mapping_temp <- args[3] + if (mapping_temp == "") { + stop(error_args) + } + mapping_psal <- args[4] + if (mapping_psal == "") { + stop(error_args) + } + mapping_doxy <- args[5] + if (mapping_doxy == "") { + stop(error_args) + } +}else if (length(args) == 1) { + mapping_pres <- c("pressure", + "pressure_raw [decibar]", + "pressure_dmadjusted [decibar]", + "pressure_rtadjusted [decibar]") + mapping_temp <- c("temperature", + "temperature_raw [degree_celsius]", + "temperature_dmadjusted [degree_celsius]", + "temperature_rtadjusted [degree_celsius]") + mapping_psal <- c("salinity", + "salinity_raw [psu]", + "salinity_dmadjusted [psu]", + "salinity_rtadjusted [psu]") + mapping_doxy <- c("oxygen", + "oxygen_raw [micromole/kg]", + "oxygen_dmadjusted [micromole/kg]", + "oxygen_rtadjusted [micromole/kg]") +}else { + stop(error_args) +} + +if (length(args) >= 6 && !(args[6] == "")) { + mapping_epres <- args[6] +}else { + mapping_epres <- c("epres") +} +if (length(args) >= 7 && !(args[7] == "")) { + mapping_etemp <- args[7] +}else { + mapping_etemp <- c("etemp") +} +if (length(args) >= 8 && !(args[8] == "")) { + mapping_epsal <- args[8] +}else { + mapping_epsal <- c("epsal") +} +if (length(args) >= 9 && !(args[9] == "")) { + mapping_edoxy <- args[9] +}else { + mapping_edoxy <- c("edoxy") +} + +semantics_canyonB <- list( + date = mapping_date, + lat = mapping_lat, + lon = mapping_lon, + pres = mapping_pres, + temp = mapping_temp, + psal = mapping_psal, + doxy = mapping_doxy, + epres = mapping_epres, + etemp = mapping_etemp, + epsal = mapping_epsal, + edoxy = mapping_edoxy +) +semantics_mandatory <- c("date", "lat", "lon", "pres", "temp", "psal", "doxy") + +# default values ----------------------------------------------- +from_odv <- 0 +from_easy <- 0 + +# log file ----------------------------------------------------- +initialisation_log_file() +update_log_file("info", "start process") + +# check input file ----------------------------------------------- +update_log_file("info", "check input file") +# check file extension +ext <- tools::file_ext(csv_file) +if (!(ext == "txt" || ext == "csv")) { + msge <- "the extension should be .txt or .csv" + update_log_file("error", msge, stop = TRUE) +} + +# check format +if (ext == "csv") { + # user file + table <- read.table(csv_file, sep = ";", header = TRUE) + if (dim(table)[2] < 2) { + msge <- "column number < 2, csv separator might be wrong" + update_log_file("error", msge, stop = TRUE) + } else if (dim(table) [2] < 7) { + msge <- "column number > 2 & < 7, missing column" + update_log_file("error", msge, stop = TRUE) + } + +}else if (ext == "txt") { + # odv collection + count <- 1 + is_continue <- TRUE + while (is_continue == TRUE) { + first_lines <- read.csv(file = csv_file, + header = FALSE, + sep = "\n", nrows = count) + table_tmp <- first_lines[count, ] + if (length(grep("^//", table_tmp)) == 0) { + is_continue <- FALSE + }else { + count <- count + 1 + } + } + + if (count == 1) { + msge <- "txt file not from the odv collection generator tool" + update_log_file("error", msge, stop = TRUE) + } + + odv_pattern1 <- "FAIR-EASE-CALVAL-PLATFORM" + odv_pattern2 <- "Web Ocean Data View" + odv_pattern3 <- "Ocean Data View" + if (!any(grepl(odv_pattern1, first_lines)) == TRUE + && !any(grepl(odv_pattern2, first_lines)) == TRUE + && !any(grepl(odv_pattern3, first_lines)) == TRUE) { + msge <- paste0("txt file do not come from the odv ", + "collection generator tool or (web)ODV software") + update_log_file("error", msge, stop = TRUE) + } + + # read table + header <- first_lines[dim(first_lines)[1], ] + if (any(grepl(odv_pattern1, first_lines)) == TRUE) { + table <- read.table(csv_file, + sep = ";", + header = FALSE, + skip = count) + header <- unlist(strsplit(header, split = ";")) + from_easy <- 1 + }else { + table <- read.csv(csv_file, + sep = "\t", + header = FALSE, + comment.char = "/") + table <- table[-1, ] + header <- unlist(strsplit(header, split = "\t")) + from_odv <- 1 + } + + names(table) <- header + first_lines <- first_lines[-dim(first_lines)[1], ] +} + +# check input variables +header <- names(table) +canb_obj <- lapply(header, convert_name_from_semantics, semantics_canyonB) +for (var in semantics_mandatory){ + if (!any(unlist(canb_obj) == var)) { + msge <- "at least one of the mandatory column name is missing" + update_log_file("error", msge, stop = TRUE) + } +} + +# odv software specificity +if (from_odv == 1) { + # metadata available for the first line / profile + # where is useful metadata + col_dat <- unlist(lapply(mapping_date, function(x) which(header == x))) + col_lon <- unlist(lapply(mapping_lon, function(x) which(header == x))) + col_lat <- unlist(lapply(mapping_lat, function(x) which(header == x))) + + # lat and lon should be numeric variables + table[[col_lat]] <- as.numeric(table[[col_lat]]) + table[[col_lon]] <- as.numeric(table[[col_lon]]) + + # where is latitude/longitude information + line_lat <- which(is.finite(table[[col_lat]]) == TRUE) + line_lat <- c(line_lat, dim(table)[1] + 1) + + # repeat the lat/lon/data for each line of the profile + for (i in seq(1, length(line_lat) - 1, 1)) { + line_tbr <- seq(line_lat[i], line_lat[i + 1] - 1, 1) + table[line_tbr, col_lat] <- rep(table[line_lat[i], col_lat], + line_lat[i + 1] - line_lat[i]) + table[line_tbr, col_lon] <- rep(table[line_lat[i], col_lon], + line_lat[i + 1] - line_lat[i]) + table[line_tbr, col_dat] <- rep(table[line_lat[i], col_dat], + line_lat[i + 1] - line_lat[i]) + } + + # transform character into numeric + col_temp <- unlist(lapply(mapping_temp, function(x) which(header == x))) + col_psal <- unlist(lapply(mapping_psal, function(x) which(header == x))) + col_doxy <- unlist(lapply(mapping_doxy, function(x) which(header == x))) + col_pres <- unlist(lapply(mapping_pres, function(x) which(header == x))) + for (i in c(col_temp, col_psal, col_doxy, col_pres)) { + table[[i]] <- as.numeric(table[[i]]) + } +} + +# define date format --------------------------------------------- +if (ext == "txt") { + date_name <- odv_date_name + in_format <- "odv" +}else if (ext == "csv") { + date_name <- "date" + in_format <- "canyonb" +} +table[[date_name]] <- datetime_standardisation(table[[date_name]], + in_mode = in_format) + +# prepare output table +table_out <- table + +# loop for running CANYON D-------------------------------------- +if (ext == "txt" && from_easy == 1) { + nb_run <- 3 +}else if (ext == "csv" || (ext == "txt" && from_easy == 0)) { + nb_run <- 1 +} + +# check dm and rtadjusted pressure for odv collection - empty for BGC argo +if (ext == "txt" && from_easy == 1) { + sufx <- c("dmadjusted", "rtadjusted") + for (suf in sufx) { + oxyname <- semantics_canyonB$doxy[grep(suf, + semantics_canyonB$doxy)] + prsname <- semantics_canyonB$pres[grep(suf, + semantics_canyonB$pres)] + rawpres <- semantics_canyonB$pres[grep("raw", + semantics_canyonB$pres)] + if (any(oxyname == header) + && any(prsname == header) + && !any(is.finite(prsname[is.finite(table[[oxyname]]) == TRUE]))) { + + table[prsname] <- table[rawpres] + update_log_file("info", paste0(prsname, + " is empty for bgc variables, ", + "tool replace by ", + rawpres)) + } + } +} + +count <- 0 +count1 <- 0 +for (run in seq(1, nb_run, 1)) { + update_log_file("info", paste0("run nb : ", run)) + update_log_file("info", "reduce input table to essential element") + + # txt (easy odv collection) specificite + if (ext == "txt" && from_easy == 1 && run == 1) { + sufix <- "raw" + }else if (ext == "txt" && from_easy == 1 && run == 2) { + sufix <- "rtadjusted" + }else if (ext == "txt" && from_easy == 1 && run == 3) { + sufix <- "dmadjusted" + } + + # indexes of interesting column + index <- NULL + for (i in seq(1, length(canb_obj), 1)) { + if (length(canb_obj[[i]]) > 0 + && (ext == "csv" + || (ext == "txt" && from_easy == 0))) { + index <- c(index, i) + }else if (length(canb_obj[[i]]) > 0 && ext == "txt" && from_easy == 1) { + if (!grepl("_", header[[i]]) + || grepl("degrees", header[[i]]) + || grepl(sufix, header[[i]])) { + index <- c(index, i) + } + } + } + df <- table[, index] + # default condition + is_continu <- 1 + + # check if one column is empty + new_table <- df[, apply(df, 2, function(x) !all(is.na(x)))] + if (length(index) > length(names(new_table))) { + message(names(new_table)) + msge <- "at least one of the mandatory column name is missing" + update_log_file("warning", msge) + is_continu <- 0 + } + + # list of column for searching duplicate + if (is_continu == 1) { + list_common <- NULL + index_oxy_col <- NULL + for (i in index) { + name <- canb_obj[[i]] + if (name == "date" + || name == "lat" + || name == "lon" + || name == "pres") { + list_common <- c(list_common, header[i]) + }else if (name == "doxy") { + index_oxy_col <- i + } + } + + # where is oxygen + index_oxy_row <- seq(1, dim(table)[1], 1) + is_noxy <- is.na(table[, index_oxy_col]) + index_oxy_row[is_noxy == TRUE] <- NA + new_table$index_oxy <- index_oxy_row + + # arrange dataframe for collapsing complementary duplicate + new_table <- arrange_duplicate(new_table, list_common) + + # identification non na + is_nok <- lapply(seq(1, dim(new_table)[1], 1), + function(x) any(is.na(new_table[x, ]))) + is_nok <- unlist(is_nok) + + + if (!any(is_nok == FALSE)) { + count <- count + 1 + msge <- "at least one essential element is missing by row" + if (count == nb_run) { + update_log_file("error", msge, stop = TRUE) + } else { + update_log_file("warning", msge) + is_continu <- 0 + } + } + } + # creation of canb_list + if (is_continu == 1) { + update_log_file("info", "define canyon b object") + canb_list <- list() + for (i in index) { + name <- canb_obj[[i]] + canb_list[[name]] <- new_table[[header[[i]]]][is_nok == FALSE] + if (name == "date" && ext == "txt") { + canb_list$date <- datetime_standardisation(canb_list$date, + out_mode = "canyonb") + } + } + + # double check obj for txt (odv collection) + for (var in semantics_mandatory) { + if (length(canb_list[[var]]) == 0) { + count <- count + 1 + msge <- paste(sufix, + " is not used because at least ", + "one of the mandatory column name is missing") + if (count == nb_run) { + update_log_file("error", msge, stop = TRUE) + }else { + update_log_file("warning", msge) + is_continu <- 0 + } + } + } + } + + # run canyonb + if (is_continu == 1) { + update_log_file("info", "run canyonB") + res <- do.call(canyonb::CANYONB, canb_list) + + # transformer la liste en data.frame + table_sus_tmp <- NULL + header_sus <- NULL + for (name in names(res)) { + sus <- matrix(res[[name]], length(res$AT), 1) + table_sus_tmp <- cbind(table_sus_tmp, sus) + if (ext == "txt" && from_easy == 1) { + name <- paste(name, "fromcanyon", sufix, sep = "_") + }else { + name <- paste(name, "fromcanyon", sep = "_") + } + header_sus <- c(header_sus, name) + } + + # resize table with additional data + table_sus <- data.frame(matrix(NA, dim(table)[1], length(header_sus))) + ligne_oxy <- new_table$index_oxy[is_nok == FALSE] + table_sus[ligne_oxy, ] <- table_sus_tmp + names(table_sus) <- header_sus + + # output file + table_out <- cbind(table_out, table_sus) + } +} + +# prepare date for odv +if (ext == "csv") { + datetime <- table_out$date + table_out$date <- datetime_standardisation(datetime, + out_mode = "odv") + header <- names(table_out) + header[header == "date"] <- odv_date_name + names(table_out) <- header +} + +# write output file +file_out <- gsub(paste0(".", ext), paste0("_canyonb.", ext), csv_file) +file.create(file_out) +if (ext == "txt") { + write.table(first_lines, + file_out, + append = TRUE, + col.names = FALSE, + row.names = FALSE, + quote = FALSE) +} +write.table(table_out, file_out, append = TRUE, row.names = FALSE, col.names = TRUE, sep = ";") + +# final log update +msge <- paste0(file_out, " is available") +update_log_file("done", msge)