diff --git a/DESCRIPTION b/DESCRIPTION index c288e23..357bfb1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,9 +1,11 @@ Package: openxlsx2Extras Title: Extra Functions for the openxlsx2 package Version: 0.0.0.9000 -Authors@R: +Authors@R: c( person("Eli", "Pousson", , "eli.pousson@gmail.com", role = c("aut", "cre", "cph"), - comment = c(ORCID = "0000-0001-8280-1706")) + comment = c(ORCID = "0000-0001-8280-1706")), + person("Jan Marvin", "Garbuszus", , "jan.garbuszus@ruhr-uni-bochum.de", role = "ctb") + ) Description: Extends the openxlsx2 package with wrapper and helper functions designed to add new features and options when working with Excel workbooks. @@ -11,15 +13,16 @@ License: MIT + file LICENSE URL: https://github.com/elipousson/openxlsx2Extras, https://elipousson.github.io/openxlsx2Extras/ BugReports: https://github.com/elipousson/openxlsx2Extras/issues -Imports: +Imports: cli, fs, lifecycle, openxlsx2, purrr, rlang, + utils, vctrs -Suggests: +Suggests: dplyr, gt, marquee, @@ -27,8 +30,8 @@ Suggests: testthat (>= 3.0.0), tidyselect, withr +Config/testthat/edition: 3 Encoding: UTF-8 Language: en-US Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 -Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index dfab75b..b5cdaeb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,8 @@ export(as_sheet_list) export(as_sheet_names) export(as_wb) +export(csv_to_wb) +export(csv_to_xlsx) export(fmt_lgl_cols) export(fmt_marquee_txt) export(map_wb) @@ -24,10 +26,13 @@ export(wb_sheets_fmt) export(wb_split) export(wb_to_df_list) export(write_xlsx_ext) +export(xlsx_to_csv) import(rlang) importFrom(cli,cli_abort) importFrom(cli,cli_warn) importFrom(fs,file_exists) +importFrom(fs,file_temp) +importFrom(fs,path_ext_set) importFrom(lifecycle,badge) importFrom(lifecycle,deprecated) importFrom(openxlsx2,current_sheet) @@ -36,6 +41,9 @@ importFrom(openxlsx2,wb_add_numfmt) importFrom(openxlsx2,wb_data) importFrom(openxlsx2,wb_set_col_widths) importFrom(openxlsx2,wb_to_df) +importFrom(openxlsx2,write_xlsx) importFrom(purrr,list_cbind) importFrom(purrr,map_chr) +importFrom(utils,read.csv) +importFrom(utils,write.csv) importFrom(vctrs,vec_recycle) diff --git a/R/convert_to.R b/R/convert_to.R new file mode 100644 index 0000000..207fa8a --- /dev/null +++ b/R/convert_to.R @@ -0,0 +1,113 @@ +#' Convert CSV to XLSX files or XLSX to CSV +#' +#' @description A set of functions to convert between CSV and XLSX formats using +#' flexible input and output options. +#' +#' @details +#' These functions allow seamless conversion between CSV and XLSX formats: +#' +#' - `csv_to_wb`: Reads one or more CSV files and writes them to a workbook +#' object. +#' - `csv_to_xlsx`: Converts one or more CSV files to a XLSX file. +#' - `xlsx_to_csv`: Converts an XLSX file to a CSV file. +#' +#' @name convert_to +#' @author Jordan Mark Barbone \email{jmbarbone@gmail.com} +#' @author Jan Marvin Garbuszus \email{jan.garbuszus@ruhr-uni-bochum.de} +#' @author Eli Pousson \email{eli.pousson@gmail.com} +#' +#' @importFrom utils read.csv write.csv +#' @importFrom openxlsx2 write_xlsx wb_to_df +#' +#' @examples +#' # Create example CSV file +#' csv <- tempfile(fileext = ".csv") +#' utils::write.csv(x = mtcars, file = csv) +#' +#' # Convert CSV to Workbook +#' wb <- csv_to_wb(csv = csv) +#' +#' # Convert CSV to XLSX +#' xlsx <- openxlsx2::temp_xlsx() +#' csv_to_xlsx(csv = csv, xlsx = xlsx) +#' +#' # Convert XLSX back to CSV +#' xlsx_to_csv(x = xlsx, csv = csv) +NULL + +#' @rdname convert_to +#' @param file Path or paths to input files. For [csv_to_wb()], users +#' can pass multiple CSV files when creating a workbook or xlsx file. If these +#' inputs are named, the names are used as worksheet names. +#' @param new_file Path to output file. Optional. If `new_file` is not supplied +#' and `file` is a string, `new_file` is set to use the same path with a new +#' file extension or (if file is not a string) `new_file` is set to a temporary +#' file. +#' @param .f Function used to read or write the csv file. Defaults to +#' `utils::read.csv` for [csv_to_wb()] and [csv_to_xlsx()] and +#' `utils::write.csv` for [xlsx_to_csv()]. Other functions are allowed but must +#' use the input or output file name as the second argument. +#' @param ... additional arguments passed to [openxlsx2::write_xlsx()] +#' @export +csv_to_wb <- function(file, + new_file = NULL, + .f = utils::read.csv, + ...) { + x <- lapply(file, .f) + openxlsx2::write_xlsx(x = x, file = set_new_file(file, new_file), ...) +} + +#' @rdname convert_to +#' @export +csv_to_xlsx <- function(file, + new_file = NULL, + .f = utils::read.csv, + ...) { + csv_to_wb( + file, + .f = .f, + ... + )$save( + file = set_new_file(file, new_file, ext = "xlsx") + ) +} + +#' @rdname convert_to +#' @param sheet A sheet in the workbook specified by `file` (either an index or +#' a sheet name). Defaults to 1. +#' @param ext File extension for output file. Defaults to "csv". +#' @param ... Additional arguments passed to `.f` +#' @export +xlsx_to_csv <- function(file, + new_file = NULL, + sheet = 1, + .f = utils::write.csv, + ext = "csv", + ...) { + .f( + openxlsx2::wb_to_df( + file = file, + sheet = sheet + ), + set_new_file(file, new_file, ext = ext), + ... + ) +} + +#' [set_new_file()] sets new_file based on file or as temporary file. +#' @noRd +#' @importFrom fs path_ext_set file_temp +set_new_file <- function(file = NULL, + new_file = NULL, + tmp_dir = tempdir(), + ext = "xlsx") { + if (!is.null(new_file)) { + return(new_file) + } + + if (is.character(file)) { + return(fs::path_ext_set(file, ext)) + } + + fs::file_temp(tmp_dir = tmp_dir, ext = ext) +} diff --git a/man/convert_to.Rd b/man/convert_to.Rd new file mode 100644 index 0000000..8941732 --- /dev/null +++ b/man/convert_to.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/convert_to.R +\name{convert_to} +\alias{convert_to} +\alias{csv_to_wb} +\alias{csv_to_xlsx} +\alias{xlsx_to_csv} +\title{Convert CSV to XLSX files or XLSX to CSV} +\usage{ +csv_to_wb(file, new_file = NULL, .f = utils::read.csv, ...) + +csv_to_xlsx(file, new_file = NULL, .f = utils::read.csv, ...) + +xlsx_to_csv( + file, + new_file = NULL, + sheet = 1, + .f = utils::write.csv, + ext = "csv", + ... +) +} +\arguments{ +\item{file}{Path or paths to input files. For \code{\link[=csv_to_wb]{csv_to_wb()}}, users +can pass multiple CSV files when creating a workbook or xlsx file. If these +inputs are named, the names are used as worksheet names.} + +\item{new_file}{Path to output file. Optional. If \code{new_file} is not supplied +and \code{file} is a string, \code{new_file} is set to use the same path with a new +file extension or (if file is not a string) \code{new_file} is set to a temporary +file.} + +\item{.f}{Function used to read or write the csv file. Defaults to +\code{utils::read.csv} for \code{\link[=csv_to_wb]{csv_to_wb()}} and \code{\link[=csv_to_xlsx]{csv_to_xlsx()}} and +\code{utils::write.csv} for \code{\link[=xlsx_to_csv]{xlsx_to_csv()}}. Other functions are allowed but must +use the input or output file name as the second argument.} + +\item{...}{Additional arguments passed to \code{.f}} + +\item{sheet}{A sheet in the workbook specified by \code{file} (either an index or +a sheet name). Defaults to 1.} + +\item{ext}{File extension for output file. Defaults to "csv".} +} +\description{ +A set of functions to convert between CSV and XLSX formats using +flexible input and output options. +} +\details{ +These functions allow seamless conversion between CSV and XLSX formats: +\itemize{ +\item \code{csv_to_wb}: Reads one or more CSV files and writes them to a workbook +object. +\item \code{csv_to_xlsx}: Converts one or more CSV files to a XLSX file. +\item \code{xlsx_to_csv}: Converts an XLSX file to a CSV file. +} +} +\examples{ +# Create example CSV file +csv <- tempfile(fileext = ".csv") +utils::write.csv(x = mtcars, file = csv) + +# Convert CSV to Workbook +wb <- csv_to_wb(csv = csv) + +# Convert CSV to XLSX +xlsx <- openxlsx2::temp_xlsx() +csv_to_xlsx(csv = csv, xlsx = xlsx) + +# Convert XLSX back to CSV +xlsx_to_csv(x = xlsx, csv = csv) +} +\author{ +Jordan Mark Barbone \email{jmbarbone@gmail.com} + +Jan Marvin Garbuszus \email{jan.garbuszus@ruhr-uni-bochum.de} + +Eli Pousson \email{eli.pousson@gmail.com} +} diff --git a/man/openxlsx2Extras-package.Rd b/man/openxlsx2Extras-package.Rd index 12af450..644358f 100644 --- a/man/openxlsx2Extras-package.Rd +++ b/man/openxlsx2Extras-package.Rd @@ -20,5 +20,10 @@ Useful links: \author{ \strong{Maintainer}: Eli Pousson \email{eli.pousson@gmail.com} (\href{https://orcid.org/0000-0001-8280-1706}{ORCID}) [copyright holder] +Other contributors: +\itemize{ + \item Jan Marvin Garbuszus \email{jan.garbuszus@ruhr-uni-bochum.de} [contributor] +} + } \keyword{internal} diff --git a/man/wb_set_col_widths_ext.Rd b/man/wb_set_col_widths_ext.Rd index 351b91e..792667b 100644 --- a/man/wb_set_col_widths_ext.Rd +++ b/man/wb_set_col_widths_ext.Rd @@ -26,7 +26,7 @@ software. See \strong{Details} for general information on column widths.} \description{ \code{\link[=wb_set_col_widths_ext]{wb_set_col_widths_ext()}} extends \code{\link[openxlsx2:col_widths-wb]{openxlsx2::wb_set_col_widths()}} by making \code{cols} optional (defaults to all columns) and allows setting columns based on -column names. +column names (rather than index position alone). } \examples{ wb <- as_wb(mtcars) diff --git a/tests/testthat/test-convert_to.R b/tests/testthat/test-convert_to.R new file mode 100644 index 0000000..f0c1316 --- /dev/null +++ b/tests/testthat/test-convert_to.R @@ -0,0 +1,31 @@ +test_that("magic conversion functions work", { + ## temporary files + csv <- tempfile(fileext = ".csv") + xlsx <- openxlsx2::temp_xlsx() + + ## create example csv + utils::write.csv(x = mtcars, file = csv, row.names = FALSE) + + ## works even with other packages + wb <- csv_to_wb(csv) + expect_equal(openxlsx2::wb_to_df(wb), mtcars, ignore_attr = TRUE) + + csv_to_xlsx(csv, xlsx) + expect_equal(openxlsx2::wb_to_df(xlsx), mtcars, ignore_attr = TRUE) + + csv_to_xlsx(list(foo = csv), xlsx) + expect_equal( + openxlsx2::wb_get_sheet_names(openxlsx2::wb_load(xlsx)), + c(foo = "foo") + ) + + ## use other arguments frequently passed to openxlsx2::write_xlsx() + csv_to_xlsx(csv, xlsx, as_table = TRUE) + + expect_equal(openxlsx2::wb_load(xlsx)$get_tables()$tab_name, "Table1") + + ## go the other way around from spreadsheet to csv + xlsx_to_csv(xlsx, csv, row.names = FALSE) + + expect_equal(read.csv(csv), mtcars, ignore_attr = TRUE) +})