From 19943eaef45e30253c98387eb6a16802342221b8 Mon Sep 17 00:00:00 2001 From: Brancen Gregory Date: Thu, 23 Apr 2026 15:49:16 -0500 Subject: [PATCH 1/2] feat: add Google Cloud Storage utilities - add gcs_auth_bucket, gcs_read_csv, gcs_write_csv, gcs_list_objects and tar_gcs_csv functions for GCS operations via arrow --- DESCRIPTION | 7 +- NAMESPACE | 20 +++ R/gcs.R | 292 ++++++++++++++++++++++++++++++++++++++++ R/zzz.R | 10 ++ man/gcs_auth_bucket.Rd | 42 ++++++ man/gcs_list_objects.Rd | 45 +++++++ man/gcs_read_csv.Rd | 51 +++++++ man/gcs_write_csv.Rd | 69 ++++++++++ man/ojoutils-package.Rd | 7 + man/tar_gcs_csv.Rd | 78 +++++++++++ 10 files changed, 620 insertions(+), 1 deletion(-) create mode 100644 R/gcs.R create mode 100644 R/zzz.R create mode 100644 man/gcs_auth_bucket.Rd create mode 100644 man/gcs_list_objects.Rd create mode 100644 man/gcs_read_csv.Rd create mode 100644 man/gcs_write_csv.Rd create mode 100644 man/tar_gcs_csv.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 1bfc580..c18da67 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -8,19 +8,24 @@ Description: We find ourselves repeating the same simple tasks or running a seri License: GPL (>= 3) Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 +RoxygenNote: 7.3.3 Imports: + arrow, cli, dbplyr, dplyr, fs, + gargle, gert, gh, glue, + googleCloudStorageR, + janitor, readr, renv, rlang, stringr, + targets, usethis, withr Suggests: diff --git a/NAMESPACE b/NAMESPACE index 0ef8850..03dc042 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,10 +1,30 @@ # Generated by roxygen2: do not edit by hand export(describe_change) +export(gcs_auth_bucket) +export(gcs_list_objects) +export(gcs_read_csv) +export(gcs_write_csv) export(limit) export(ojo_counties) export(ojo_parse_county) export(ojo_use_template) +export(tar_gcs_csv) +importFrom(arrow,read_csv_arrow) +importFrom(arrow,write_csv_arrow) importFrom(dplyr,if_else) +importFrom(dplyr,pull) +importFrom(gargle,token_fetch) +importFrom(glue,glue) +importFrom(googleCloudStorageR,gcs_auth) +importFrom(googleCloudStorageR,gcs_get_object) +importFrom(googleCloudStorageR,gcs_global_bucket) +importFrom(googleCloudStorageR,gcs_list_objects) +importFrom(janitor,clean_names) +importFrom(rlang,as_name) +importFrom(rlang,enexpr) +importFrom(rlang,ensym) +importFrom(rlang,expr) importFrom(stringr,str_remove_all) importFrom(stringr,str_to_lower) +importFrom(targets,tar_target_raw) diff --git a/R/gcs.R b/R/gcs.R new file mode 100644 index 0000000..d12c78e --- /dev/null +++ b/R/gcs.R @@ -0,0 +1,292 @@ +#' Authenticate with Google Cloud Storage +#' +#' @description +#' Authenticates with Google Cloud Storage (GCS) using the gargle package for +#' OAuth2 token fetching, then sets the specified bucket as the global bucket +#' for subsequent GCS operations. +#' +#' @details +#' This function uses `gargle::token_fetch()` to obtain an OAuth2 token with +#' cloud-platform scope, then authenticates with `googleCloudStorageR`. It sets +#' the global bucket so that subsequent GCS operations don't need to specify +#' the bucket parameter repeatedly. +#' +#' The function returns the bucket name invisibly, making it suitable for use +#' in pipelines where you want to authenticate and continue processing. +#' +#' @param bucket Character string. The name of the GCS bucket to set as the +#' global bucket for subsequent operations. +#' +#' @return The bucket name (invisibly). +#' +#' @examples +#' \dontrun{ +#' # Authenticate and set a specific bucket as global +#' gcs_auth_bucket("my-project-data") +#' +#' # Can be used in a pipeline +#' "my-project-data" |> gcs_auth_bucket() +#' } +#' +#' @seealso [googleCloudStorageR::gcs_auth()] for more authentication options +#' +#' @importFrom gargle token_fetch +#' @importFrom googleCloudStorageR gcs_auth gcs_global_bucket +#' @export +gcs_auth_bucket <- function(bucket) { + scope <- "https://www.googleapis.com/auth/cloud-platform" + token <- gargle::token_fetch(scopes = scope) + googleCloudStorageR::gcs_auth(token = token) + googleCloudStorageR::gcs_global_bucket(bucket = bucket) + invisible(bucket) +} + +#' Read a CSV file from Google Cloud Storage +#' +#' @description +#' Reads a CSV file from Google Cloud Storage into a data frame using the +#' arrow package for efficient reading. Optionally cleans column names using +#' janitor::clean_names(). +#' +#' @details +#' This function uses arrow's CSV reader which is optimized for performance +#' and can handle large files efficiently. The GCS path is constructed using +#' glue for safe string interpolation. +#' +#' By default, column names are cleaned using janitor::clean_names() to convert +#' them to snake_case and remove special characters. Set `clean_names = FALSE` +#' to preserve original column names. +#' +#' @param bucket Character string. The name of the GCS bucket containing the file. +#' @param object Character string. The path to the CSV file within the bucket. +#' @param clean_names Logical. If `TRUE` (default), column names are cleaned +#' using [janitor::clean_names()]. If `FALSE`, original column names are preserved. +#' +#' @return A data frame (tibble) containing the CSV data. +#' +#' @examples +#' \dontrun{ +#' # Read a CSV and clean column names +#' data <- gcs_read_csv("my-project-data", "raw/customers.csv") +#' +#' # Read a CSV preserving original column names +#' data <- gcs_read_csv("my-project-data", "raw/customers.csv", clean_names = FALSE) +#' +#' # Use with gcs_auth_bucket for authenticated reading +#' gcs_auth_bucket("my-project-data") +#' data <- gcs_read_csv("my-project-data", "processed/sales_2024.csv") +#' } +#' +#' @seealso [arrow::read_csv_arrow()] for reading options, +#' [janitor::clean_names()] for name cleaning details +#' +#' @importFrom arrow read_csv_arrow +#' @importFrom glue glue +#' @importFrom janitor clean_names +#' @export +gcs_read_csv <- function(bucket, object, clean_names = TRUE) { + path <- glue::glue("gs://{bucket}/{object}") + data <- arrow::read_csv_arrow(path) + if (clean_names) data <- janitor::clean_names(data) + data +} + +#' Write a CSV file to Google Cloud Storage +#' +#' @description +#' Writes a data frame to a CSV file in Google Cloud Storage using the arrow +#' package. Optionally returns metadata about the uploaded object. +#' +#' @details +#' This function uses arrow's CSV writer for efficient writing of data frames +#' to GCS. The file is written directly to the specified GCS path without +#' requiring temporary local storage. +#' +#' When `meta = TRUE`, the function retrieves and returns metadata about the +#' uploaded object including: +#' - The GCS path +#' - MD5 hash for integrity verification +#' - Generation number for versioning +#' - File size +#' - Last updated timestamp +#' +#' @param data A data frame to write to GCS. +#' @param bucket Character string. The name of the GCS bucket to write to. +#' @param object Character string. The destination path for the CSV file within +#' the bucket (e.g., "folder/subfolder/filename.csv"). +#' @param meta Logical. If `TRUE`, returns a list with object metadata. If `FALSE` +#' (default), returns the GCS path invisibly. +#' +#' @return If `meta = TRUE`, a list containing: +#' - `path`: The full GCS path to the object +#' - `md5_hash`: The MD5 hash of the uploaded object +#' - `generation`: The object's generation number +#' - `size`: The object size in bytes +#' - `updated`: The last update timestamp +#' +#' If `meta = FALSE`, the GCS path (invisibly). +#' +#' @examples +#' \dontrun{ +#' # Write a data frame to GCS +#' gcs_write_csv(mtcars, "my-project-data", "output/mtcars.csv") +#' +#' # Write and get metadata back +#' meta <- gcs_write_csv(mtcars, "my-project-data", "output/mtcars.csv", meta = TRUE) +#' print(meta$md5_hash) +#' +#' # Use with authentication +#' gcs_auth_bucket("my-project-data") +#' gcs_write_csv(my_data, "my-project-data", "processed/results.csv") +#' } +#' +#' @seealso [arrow::write_csv_arrow()] for write options, +#' [googleCloudStorageR::gcs_get_object()] for metadata retrieval +#' +#' @importFrom arrow write_csv_arrow +#' @importFrom googleCloudStorageR gcs_get_object +#' @export +gcs_write_csv <- function(data, bucket, object, meta = FALSE) { + path <- paste0("gs://", bucket, "/", object) + arrow::write_csv_arrow(data, path) + + if (meta) { + metadata <- googleCloudStorageR::gcs_get_object(object, bucket = bucket, meta = TRUE) + list( + path = path, + md5_hash = metadata$md5Hash, + generation = metadata$generation, + size = metadata$size, + updated = metadata$updated + ) + } else { + invisible(path) + } +} + +#' List objects in a Google Cloud Storage bucket +#' +#' @description +#' Lists all object names in a GCS bucket, optionally filtered by a prefix. +#' Returns a character vector of object names. +#' +#' @details +#' This function wraps `googleCloudStorageR::gcs_list_objects()` and extracts +#' just the object names as a character vector using dplyr::pull(). +#' +#' The prefix parameter can be used to filter results to objects within a +#' specific "folder" or matching a specific pattern. Note that GCS uses a +#' flat namespace, so prefixes simulate directory structures. +#' +#' @param bucket Character string. The name of the GCS bucket to list objects from. +#' @param prefix Character string (optional). A prefix to filter objects. Only +#' objects whose names begin with this prefix will be returned. +#' +#' @return A character vector of object names in the bucket. +#' +#' @examples +#' \dontrun{ +#' # List all objects in a bucket +#' all_objects <- gcs_list_objects("my-project-data") +#' +#' # List objects in a specific "folder" +#' csv_files <- gcs_list_objects("my-project-data", prefix = "raw/") +#' +#' # List objects with a specific prefix +#' sales_files <- gcs_list_objects("my-project-data", prefix = "sales_2024") +#' } +#' +#' @seealso [googleCloudStorageR::gcs_list_objects()] for full object details +#' +#' @importFrom googleCloudStorageR gcs_list_objects +#' @importFrom dplyr pull +#' @export +gcs_list_objects <- function(bucket, prefix = NULL) { + googleCloudStorageR::gcs_list_objects(bucket = bucket, prefix = prefix) |> dplyr::pull(name) +} + +#' Create a targets pipeline target for writing CSVs to GCS +#' +#' @description +#' Creates a targets pipeline target that writes a data frame to Google Cloud +#' Storage as a CSV file. This is a convenience wrapper around targets::tar_target() +#' specifically designed for GCS CSV outputs. +#' +#' @details +#' This function creates a targets target that: +#' 1. Authenticates with GCS using `gcs_auth_bucket()` +#' 2. Writes the data to GCS using `gcs_write_csv()` with metadata enabled +#' 3. Returns the metadata as the target value +#' +#' The target is created with format = "qs" for efficient serialization of +#' the metadata list. +#' +#' The `data` parameter is captured as an expression and evaluated at build time, +#' allowing you to reference upstream targets or other R objects. +#' +#' @param name Symbol. The name of the target (unquoted). +#' @param data Expression. The data frame to write to GCS. Can reference upstream +#' targets or other R objects. +#' @param bucket Character string. The name of the GCS bucket to write to. +#' @param object Character string. The destination path for the CSV file within +#' the bucket. +#' @param ... Additional arguments passed to [targets::tar_target()]. +#' +#' @return A targets target object suitable for use in a `_targets.R` file. +#' +#' @examples +#' \dontrun{ +#' # In your _targets.R file: +#' library(targets) +#' library(ojoutils) +#' +#' tar_plan( +#' # Process some data +#' tar_target(raw_data, read_csv("input.csv")), +#' tar_target(clean_data, clean_my_data(raw_data)), +#' +#' # Write to GCS as a target +#' tar_gcs_csv( +#' gcs_output, +#' clean_data, +#' bucket = "my-project-data", +#' object = "processed/clean_data.csv" +#' ) +#' ) +#' +#' # With additional tar_target options +#' tar_gcs_csv( +#' gcs_output, +#' processed_data, +#' bucket = "my-project-data", +#' object = "output/results.csv", +#' priority = 1 +#' ) +#' } +#' +#' @seealso [targets::tar_target()] for general target options, +#' [gcs_write_csv()] for the underlying write operation +#' +#' @importFrom rlang ensym as_name enexpr expr +#' @importFrom targets tar_target_raw +#' @export +tar_gcs_csv <- function(name, data, bucket, object, ...) { + name <- rlang::ensym(name) + name_string <- rlang::as_name(name) + + # Capture the data expression + data_expr <- rlang::enexpr(data) + + # Build the command expression using rlang + cmd <- rlang::expr({ + gcs_auth_bucket(!!bucket) + gcs_write_csv(!!data_expr, !!bucket, !!object, meta = TRUE) + }) + + tar_target_raw( + name = name_string, + command = cmd, + format = "qs", + ... + ) +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..ea483c3 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,10 @@ +#' @keywords internal +.onLoad <- function(libname, pkgname) { + if (!arrow::arrow_with_gcs()) { + cli::cli_warn(c( + "Arrow was built without GCS support.", + "i" = "GCS functions will not work properly.", + "i" = "See {.url https://arrow.apache.org/docs/r/articles/install.html} for installation details." + )) + } +} diff --git a/man/gcs_auth_bucket.Rd b/man/gcs_auth_bucket.Rd new file mode 100644 index 0000000..59f4de7 --- /dev/null +++ b/man/gcs_auth_bucket.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gcs.R +\name{gcs_auth_bucket} +\alias{gcs_auth_bucket} +\title{Authenticate with Google Cloud Storage} +\usage{ +gcs_auth_bucket(bucket) +} +\arguments{ +\item{bucket}{Character string. The name of the GCS bucket to set as the +global bucket for subsequent operations.} +} +\value{ +The bucket name (invisibly). +} +\description{ +Authenticates with Google Cloud Storage (GCS) using the gargle package for +OAuth2 token fetching, then sets the specified bucket as the global bucket +for subsequent GCS operations. +} +\details{ +This function uses \code{gargle::token_fetch()} to obtain an OAuth2 token with +cloud-platform scope, then authenticates with \code{googleCloudStorageR}. It sets +the global bucket so that subsequent GCS operations don't need to specify +the bucket parameter repeatedly. + +The function returns the bucket name invisibly, making it suitable for use +in pipelines where you want to authenticate and continue processing. +} +\examples{ +\dontrun{ +# Authenticate and set a specific bucket as global +gcs_auth_bucket("my-project-data") + +# Can be used in a pipeline +"my-project-data" |> gcs_auth_bucket() +} + +} +\seealso{ +\code{\link[googleCloudStorageR:gcs_auth]{googleCloudStorageR::gcs_auth()}} for more authentication options +} diff --git a/man/gcs_list_objects.Rd b/man/gcs_list_objects.Rd new file mode 100644 index 0000000..f0ddd8c --- /dev/null +++ b/man/gcs_list_objects.Rd @@ -0,0 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gcs.R +\name{gcs_list_objects} +\alias{gcs_list_objects} +\title{List objects in a Google Cloud Storage bucket} +\usage{ +gcs_list_objects(bucket, prefix = NULL) +} +\arguments{ +\item{bucket}{Character string. The name of the GCS bucket to list objects from.} + +\item{prefix}{Character string (optional). A prefix to filter objects. Only +objects whose names begin with this prefix will be returned.} +} +\value{ +A character vector of object names in the bucket. +} +\description{ +Lists all object names in a GCS bucket, optionally filtered by a prefix. +Returns a character vector of object names. +} +\details{ +This function wraps \code{googleCloudStorageR::gcs_list_objects()} and extracts +just the object names as a character vector using dplyr::pull(). + +The prefix parameter can be used to filter results to objects within a +specific "folder" or matching a specific pattern. Note that GCS uses a +flat namespace, so prefixes simulate directory structures. +} +\examples{ +\dontrun{ +# List all objects in a bucket +all_objects <- gcs_list_objects("my-project-data") + +# List objects in a specific "folder" +csv_files <- gcs_list_objects("my-project-data", prefix = "raw/") + +# List objects with a specific prefix +sales_files <- gcs_list_objects("my-project-data", prefix = "sales_2024") +} + +} +\seealso{ +\code{\link[googleCloudStorageR:gcs_list_objects]{googleCloudStorageR::gcs_list_objects()}} for full object details +} diff --git a/man/gcs_read_csv.Rd b/man/gcs_read_csv.Rd new file mode 100644 index 0000000..94f9dba --- /dev/null +++ b/man/gcs_read_csv.Rd @@ -0,0 +1,51 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gcs.R +\name{gcs_read_csv} +\alias{gcs_read_csv} +\title{Read a CSV file from Google Cloud Storage} +\usage{ +gcs_read_csv(bucket, object, clean_names = TRUE) +} +\arguments{ +\item{bucket}{Character string. The name of the GCS bucket containing the file.} + +\item{object}{Character string. The path to the CSV file within the bucket.} + +\item{clean_names}{Logical. If \code{TRUE} (default), column names are cleaned +using \code{\link[janitor:clean_names]{janitor::clean_names()}}. If \code{FALSE}, original column names are preserved.} +} +\value{ +A data frame (tibble) containing the CSV data. +} +\description{ +Reads a CSV file from Google Cloud Storage into a data frame using the +arrow package for efficient reading. Optionally cleans column names using +janitor::clean_names(). +} +\details{ +This function uses arrow's CSV reader which is optimized for performance +and can handle large files efficiently. The GCS path is constructed using +glue for safe string interpolation. + +By default, column names are cleaned using janitor::clean_names() to convert +them to snake_case and remove special characters. Set \code{clean_names = FALSE} +to preserve original column names. +} +\examples{ +\dontrun{ +# Read a CSV and clean column names +data <- gcs_read_csv("my-project-data", "raw/customers.csv") + +# Read a CSV preserving original column names +data <- gcs_read_csv("my-project-data", "raw/customers.csv", clean_names = FALSE) + +# Use with gcs_auth_bucket for authenticated reading +gcs_auth_bucket("my-project-data") +data <- gcs_read_csv("my-project-data", "processed/sales_2024.csv") +} + +} +\seealso{ +\code{\link[arrow:read_delim_arrow]{arrow::read_csv_arrow()}} for reading options, +\code{\link[janitor:clean_names]{janitor::clean_names()}} for name cleaning details +} diff --git a/man/gcs_write_csv.Rd b/man/gcs_write_csv.Rd new file mode 100644 index 0000000..64c64d8 --- /dev/null +++ b/man/gcs_write_csv.Rd @@ -0,0 +1,69 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gcs.R +\name{gcs_write_csv} +\alias{gcs_write_csv} +\title{Write a CSV file to Google Cloud Storage} +\usage{ +gcs_write_csv(data, bucket, object, meta = FALSE) +} +\arguments{ +\item{data}{A data frame to write to GCS.} + +\item{bucket}{Character string. The name of the GCS bucket to write to.} + +\item{object}{Character string. The destination path for the CSV file within +the bucket (e.g., "folder/subfolder/filename.csv").} + +\item{meta}{Logical. If \code{TRUE}, returns a list with object metadata. If \code{FALSE} +(default), returns the GCS path invisibly.} +} +\value{ +If \code{meta = TRUE}, a list containing: +\itemize{ +\item \code{path}: The full GCS path to the object +\item \code{md5_hash}: The MD5 hash of the uploaded object +\item \code{generation}: The object's generation number +\item \code{size}: The object size in bytes +\item \code{updated}: The last update timestamp +} + +If \code{meta = FALSE}, the GCS path (invisibly). +} +\description{ +Writes a data frame to a CSV file in Google Cloud Storage using the arrow +package. Optionally returns metadata about the uploaded object. +} +\details{ +This function uses arrow's CSV writer for efficient writing of data frames +to GCS. The file is written directly to the specified GCS path without +requiring temporary local storage. + +When \code{meta = TRUE}, the function retrieves and returns metadata about the +uploaded object including: +\itemize{ +\item The GCS path +\item MD5 hash for integrity verification +\item Generation number for versioning +\item File size +\item Last updated timestamp +} +} +\examples{ +\dontrun{ +# Write a data frame to GCS +gcs_write_csv(mtcars, "my-project-data", "output/mtcars.csv") + +# Write and get metadata back +meta <- gcs_write_csv(mtcars, "my-project-data", "output/mtcars.csv", meta = TRUE) +print(meta$md5_hash) + +# Use with authentication +gcs_auth_bucket("my-project-data") +gcs_write_csv(my_data, "my-project-data", "processed/results.csv") +} + +} +\seealso{ +\code{\link[arrow:write_csv_arrow]{arrow::write_csv_arrow()}} for write options, +\code{\link[googleCloudStorageR:gcs_get_object]{googleCloudStorageR::gcs_get_object()}} for metadata retrieval +} diff --git a/man/ojoutils-package.Rd b/man/ojoutils-package.Rd index 7e447f7..3228b07 100644 --- a/man/ojoutils-package.Rd +++ b/man/ojoutils-package.Rd @@ -7,6 +7,13 @@ \title{ojoutils: A Collection of Nifty Functions and Objects for OJO Analysts} \description{ We find ourselves repeating the same simple tasks or running a series of R commands over and over again. No more! +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://openjusticeok.github.io/ojoutils/} +} + } \author{ \strong{Maintainer}: Brancen Gregory \email{brancengregory@gmail.com} diff --git a/man/tar_gcs_csv.Rd b/man/tar_gcs_csv.Rd new file mode 100644 index 0000000..b20ef64 --- /dev/null +++ b/man/tar_gcs_csv.Rd @@ -0,0 +1,78 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gcs.R +\name{tar_gcs_csv} +\alias{tar_gcs_csv} +\title{Create a targets pipeline target for writing CSVs to GCS} +\usage{ +tar_gcs_csv(name, data, bucket, object, ...) +} +\arguments{ +\item{name}{Symbol. The name of the target (unquoted).} + +\item{data}{Expression. The data frame to write to GCS. Can reference upstream +targets or other R objects.} + +\item{bucket}{Character string. The name of the GCS bucket to write to.} + +\item{object}{Character string. The destination path for the CSV file within +the bucket.} + +\item{...}{Additional arguments passed to \code{\link[targets:tar_target]{targets::tar_target()}}.} +} +\value{ +A targets target object suitable for use in a \verb{_targets.R} file. +} +\description{ +Creates a targets pipeline target that writes a data frame to Google Cloud +Storage as a CSV file. This is a convenience wrapper around targets::tar_target() +specifically designed for GCS CSV outputs. +} +\details{ +This function creates a targets target that: +\enumerate{ +\item Authenticates with GCS using \code{gcs_auth_bucket()} +\item Writes the data to GCS using \code{gcs_write_csv()} with metadata enabled +\item Returns the metadata as the target value +} + +The target is created with format = "qs" for efficient serialization of +the metadata list. + +The \code{data} parameter is captured as an expression and evaluated at build time, +allowing you to reference upstream targets or other R objects. +} +\examples{ +\dontrun{ +# In your _targets.R file: +library(targets) +library(ojoutils) + +tar_plan( + # Process some data + tar_target(raw_data, read_csv("input.csv")), + tar_target(clean_data, clean_my_data(raw_data)), + + # Write to GCS as a target + tar_gcs_csv( + gcs_output, + clean_data, + bucket = "my-project-data", + object = "processed/clean_data.csv" + ) +) + +# With additional tar_target options +tar_gcs_csv( + gcs_output, + processed_data, + bucket = "my-project-data", + object = "output/results.csv", + priority = 1 +) +} + +} +\seealso{ +\code{\link[targets:tar_target]{targets::tar_target()}} for general target options, +\code{\link[=gcs_write_csv]{gcs_write_csv()}} for the underlying write operation +} From 494cc1e188327c7c4829e112574e45964d24db13 Mon Sep 17 00:00:00 2001 From: Brancen Gregory Date: Thu, 23 Apr 2026 15:56:45 -0500 Subject: [PATCH 2/2] fix: resolve R CMD check warnings and notes - add missing @param documentation to ojo_create_project() - fix undefined global variable 'name' in gcs_list_objects() - add credential files to .Rbuildignore --- .Rbuildignore | 2 ++ NAMESPACE | 13 +++++++++++++ R/gcs.R | 2 +- R/project.R | 25 +++++++++++++++++++++++++ man/ojo_create_project.Rd | 24 ++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/.Rbuildignore b/.Rbuildignore index ed61153..ef4cbbf 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -9,3 +9,5 @@ ^docs$ ^pkgdown$ ^\.github$ +^client_secret.*\.json$ +^.*auth-key\.json$ diff --git a/NAMESPACE b/NAMESPACE index 03dc042..af33bf0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,6 +7,7 @@ export(gcs_read_csv) export(gcs_write_csv) export(limit) export(ojo_counties) +export(ojo_create_project) export(ojo_parse_county) export(ojo_use_template) export(tar_gcs_csv) @@ -14,17 +15,29 @@ importFrom(arrow,read_csv_arrow) importFrom(arrow,write_csv_arrow) importFrom(dplyr,if_else) importFrom(dplyr,pull) +importFrom(fs,path_abs) +importFrom(fs,path_wd) importFrom(gargle,token_fetch) +importFrom(gert,git_add) +importFrom(gert,git_commit) +importFrom(gert,git_push) +importFrom(gh,gh) importFrom(glue,glue) importFrom(googleCloudStorageR,gcs_auth) importFrom(googleCloudStorageR,gcs_get_object) importFrom(googleCloudStorageR,gcs_global_bucket) importFrom(googleCloudStorageR,gcs_list_objects) importFrom(janitor,clean_names) +importFrom(readr,write_lines) +importFrom(renv,init) +importFrom(renv,install) +importFrom(rlang,abort) importFrom(rlang,as_name) importFrom(rlang,enexpr) importFrom(rlang,ensym) importFrom(rlang,expr) +importFrom(rlang,is_interactive) importFrom(stringr,str_remove_all) importFrom(stringr,str_to_lower) importFrom(targets,tar_target_raw) +importFrom(usethis,create_from_github) diff --git a/R/gcs.R b/R/gcs.R index d12c78e..ea57611 100644 --- a/R/gcs.R +++ b/R/gcs.R @@ -202,7 +202,7 @@ gcs_write_csv <- function(data, bucket, object, meta = FALSE) { #' @importFrom dplyr pull #' @export gcs_list_objects <- function(bucket, prefix = NULL) { - googleCloudStorageR::gcs_list_objects(bucket = bucket, prefix = prefix) |> dplyr::pull(name) + googleCloudStorageR::gcs_list_objects(bucket = bucket, prefix = prefix) |> dplyr::pull("name") } #' Create a targets pipeline target for writing CSVs to GCS diff --git a/R/project.R b/R/project.R index a5d5dfb..d4fd33b 100644 --- a/R/project.R +++ b/R/project.R @@ -5,7 +5,32 @@ #' To learn more about creating projects, see the vignette: #' \code{vignette("project-creation", package = "ojotools")} #' +#' @param name Character string. Name of the project/repo. If `NULL`, will attempt +#' interactive prompt. +#' @param description Character string. Description of the project for GitHub. +#' @param dir Character string. Directory where project should be created. +#' Defaults to current working directory. +#' @param private Logical. Whether the GitHub repository should be private. +#' Defaults to `TRUE`. +#' @param packages Character vector. Additional packages to install in the +#' project's renv environment. Currently not implemented. #' +#' @return Invisible path to the created project directory. +#' @export +#' +#' @examples +#' \dontrun{ +#' ojo_create_project("my-analysis", "Analysis of court data") +#' } +#' +#' @importFrom fs path_wd path_abs +#' @importFrom rlang is_interactive abort +#' @importFrom gh gh +#' @importFrom usethis create_from_github +#' @importFrom glue glue +#' @importFrom readr write_lines +#' @importFrom renv init install +#' @importFrom gert git_add git_commit git_push ojo_create_project <- function(name = NULL, description = NULL, dir = ".", private = TRUE, packages = NULL) { # Get the initial working directory init_wd <- fs::path_wd() diff --git a/man/ojo_create_project.Rd b/man/ojo_create_project.Rd index 33a7454..4562caf 100644 --- a/man/ojo_create_project.Rd +++ b/man/ojo_create_project.Rd @@ -12,6 +12,24 @@ ojo_create_project( packages = NULL ) } +\arguments{ +\item{name}{Character string. Name of the project/repo. If \code{NULL}, will attempt +interactive prompt.} + +\item{description}{Character string. Description of the project for GitHub.} + +\item{dir}{Character string. Directory where project should be created. +Defaults to current working directory.} + +\item{private}{Logical. Whether the GitHub repository should be private. +Defaults to \code{TRUE}.} + +\item{packages}{Character vector. Additional packages to install in the +project's renv environment. Currently not implemented.} +} +\value{ +Invisible path to the created project directory. +} \description{ Creates a new R project with a standard directory structure } @@ -21,3 +39,9 @@ To learn more about creating projects, see the vignette: \code{vignette("project-creation", package = "ojotools")} } +\examples{ +\dontrun{ +ojo_create_project("my-analysis", "Analysis of court data") +} + +}