Nothing
# Why not `new("CellData", ...)`? It seems excessive to store the schema as
# an attribute for each cell. Possibly a premature concern.
new_CellData <- function(...) {
# explicit 'list' class is a bit icky but it makes jsonlite happy
structure(list2(...), class = c(
"googlesheets4_schema_CellData", "googlesheets4_schema", "list"
))
}
# Use this instead of `new_CellData()` when (light) validation makes sense.
CellData <- function(...) {
dots <- list2(...)
stopifnot(is_dictionaryish(dots))
check_against_schema(dots, id = "CellData")
new_CellData(...)
}
is_CellData <- function(x) inherits(x, "googlesheets4_schema_CellData")
as_CellData <- function(x, .na = NULL) {
UseMethod("as_CellData")
}
#' @export
as_CellData.default <- function(x, .na = NULL) {
abort_unsupported_conversion(x, to = "CellData")
}
# I want to centralize what we send for NA, even though -- for now, at
# least -- I have not exposed this in user-facing functions. You could imagine
# generalizing to allow user to request we send #N/A.
# More about #N/A:
# https://support.google.com/docs/answer/3093359?hl=en
# However, again, for now, we leave NA as NA and let jsonlite do its usual
# thing, which is to encode as JSON `null`.
empty_cell <- function(.na = NULL) {
if (is.null(.na)) {
new_CellData(userEnteredValue = NA)
} else {
CellData(!!!.na)
}
}
# Note that this always returns a **list** of instances of
# googlesheets4_schema_CellData
# of the same length as x.
cell_data <- function(x, val_type, .na = NULL) {
force(val_type)
f <- function(y) {
new_CellData(userEnteredValue = list2(!!val_type := y))
}
out <- map(x, f)
out[is.na(x)] <- list(empty_cell(.na = .na))
out
}
#' @export
as_CellData.NULL <- function(x, .na = NULL) {
list(empty_cell(.na))
}
#' @export
as_CellData.googlesheets4_schema_CellData <- function(x, .na = NULL) {
x
}
#' @export
as_CellData.logical <- function(x, .na = NULL) {
cell_data(x, val_type = "boolValue", .na = .na)
}
#' @export
as_CellData.character <- function(x, .na = NULL) {
cell_data(x, val_type = "stringValue", .na = .na)
}
#' @export
as_CellData.factor <- function(x, .na = NULL) {
as_CellData(as.character(x), .na = .na)
}
#' @export
as_CellData.numeric <- function(x, .na = NULL) {
cell_data(x, val_type = "numberValue", .na = .na)
}
#' @export
as_CellData.list <- function(x, .na = NULL) {
out <- map(x, as_CellData, .na = .na)
# awkwardness possibly solved by using vctrs to create an S3 class for
# CellData ... but not pursuing at this time
needs_flatten <- !map_lgl(x, is_CellData)
out[needs_flatten] <- purrr::flatten(out[needs_flatten])
out
}
#' @export
as_CellData.googlesheets4_formula <- function(x, .na = NULL) {
cell_data(vec_data(x), val_type = "formulaValue", .na = .na)
}
add_format <- function(x, fmt) {
x[["userEnteredFormat"]] <- list(numberFormat = list2(!!!fmt))
x
}
#' @export
as_CellData.Date <- function(x, .na = NULL) {
# 25569 = DATEVALUE("1970-01-01), i.e. Unix epoch as a serial date, when the
# date origin is December 30th 1899
x <- unclass(x) + 25569
x <- cell_data(x, val_type = "numberValue", .na = .na)
map(x, add_format, fmt = list(type = "DATE", pattern = "yyyy-mm-dd"))
}
#' @export
as_CellData.POSIXct <- function(x, .na = NULL) {
# 86400 = 60 * 60 * 24 = number of seconds in a day
x <- (unclass(x) / 86400) + 25569
x <- cell_data(x, val_type = "numberValue", .na = .na)
map(
x,
add_format,
# I decided that going with R's default format was more important than
# a militant stance re: ISO 8601
# the space (vs. a 'T') between date and time is "blessed" in RFC 3339
# https://tools.ietf.org/html/rfc3339#section-5.6
fmt = list(type = "DATE_TIME", pattern = "yyyy-mm-dd hh:mm:ss")
)
}
# Currently (overly) focused on userEnteredValue, because I am thinking about
# writing. But with a reading focus, one would want to see effectiveValue.
format.googlesheets4_schema_CellData <- function(x, ...) {
# TODO: convey something about userEnteredFormat?
user_entered_value <- pluck(x, "userEnteredValue")
if (is.null(user_entered_value) || is.na(user_entered_value)) {
return("--no userEnteredValue --")
}
nm <- pluck(user_entered_value, names)
fval <- format(user_entered_value)
as.character(glue("{nm}: {fval}"))
}
print.googlesheets4_schema_CellData <- function(x, ...) {
header <- as.character(glue("<CellData>"))
cat(c(header, format(x)), sep = "\n")
invisible(x)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.