diff --git a/.travis.yml b/.travis.yml index 327d546cd9..1812098b39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,25 @@ -language: r +language: c -env: - global: - - secure: "cJ1bDRrAdIRjG+JnsQI9CdA4wQJhJJ2DdCNQ3frl8dotk69z61EiGCFW1Ir1cAY5V/NbHvFHp91HDiSo28ggwqRkEPBOGE44ico5gtVaELu3M/EnkWc2ZwQoN1273Vfdm26QYidqrvWrpLZ0XkFl7Q8xgvBswx30MF7y61+0Hv4=" +before_install: + - curl -OL http://raw.github.com/craigcitro/r-travis/master/scripts/travis-tool.sh + - chmod 755 ./travis-tool.sh + - ./travis-tool.sh bootstrap -r_packages: - - RCurl - - RJSONIO - - lattice - - xtable - - httr +install: + - ./travis-tool.sh install_deps before_script: - - chmod 755 inst/testscripts/.push_test_table.sh + - git config --global user.name "cpsievert" + - git config --global user.email "cpsievert1@gmail.com" + - git clone https://github.com/cpsievert/plotly-test-table.git ../plotly-test-table + +script: + - Rscript -e "devtools::install(); source('tests/testthat.R', chdir = TRUE)" after_success: - - inst/testscripts/.push_test_table.sh + - cd .. + - Rscript plotly/inst/build-push-comment.R + +env: + global: + - secure: "cJ1bDRrAdIRjG+JnsQI9CdA4wQJhJJ2DdCNQ3frl8dotk69z61EiGCFW1Ir1cAY5V/NbHvFHp91HDiSo28ggwqRkEPBOGE44ico5gtVaELu3M/EnkWc2ZwQoN1273Vfdm26QYidqrvWrpLZ0XkFl7Q8xgvBswx30MF7y61+0Hv4=" diff --git a/DESCRIPTION b/DESCRIPTION index 6085e4fcf3..f7005f6ba3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,11 +28,12 @@ Description: An interface to plotly's online graphing tools with desktop R URL: https://github.com/ropensci/plotly BugReports: https://github.com/ropensci/plotly/issues Depends: - RCurl, - RJSONIO, ggplot2 Imports: - knitr + httr, + RJSONIO Suggests: maps, - testthat + testthat, + knitr, + devtools diff --git a/NAMESPACE b/NAMESPACE index ef4d1bbd1d..f7fa4d958e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,18 +1,17 @@ # Generated by roxygen2 (4.1.1): do not edit by hand +export(embed_notebook) +export(get_figure) export(gg2list) export(ggplot_build2) export(group2NA) +export(knit_print.clientresp) export(layer2traces) export(paramORdefault) export(plotly) -export(set_config_file) -export(set_credentials_file) -export(show_config_file) -export(show_credentials_file) +export(plotly_POST) export(signup) export(toRGB) -import(RCurl) import(RJSONIO) import(ggplot2) -import(knitr) +import(httr) diff --git a/R/plotly.R b/R/plotly.R index d1ba1e3b60..bfc2aef008 100644 --- a/R/plotly.R +++ b/R/plotly.R @@ -1,241 +1,310 @@ -#' Main interface to plotly -#' -#' Plotly interface object. See up-to-date documentation and examples at -#' https://plot.ly/API -#' -#' @description -#' A call to \code{plotly(username, key)} creates an object of class -#' 'PlotlyClass', which has methods: +#' Create a new plotly account. +#' +#' A sign up interface to plotly through the R Console. +#' +#' @param username Desired username. +#' @param email Desired email. +#' @param save If request is successful, should the username & API key be +#' automatically stored as an environment variable in a .Rprofile? +#' +#' @return #' \itemize{ -#' \item Plotting: py$plotly(x1, y1[, x2, y2, ...], kwargs=kwargs) or -#' py$plotly({data1[, data2, ...]}, kwargs=kwargs), py$ggplotly() -#' \item Styling Data: py$style(data1,data2,..., kwargs=kwargs) -#' \item Styling Layout: py$layout(layout, kwargs=kwargs) -#' \item Utilities: py$get_figure(file_owner, file_id) +#' \item api_key key to use with the api +#' \item tmp_pw temporary password to access your plotly account #' } -#' -#' @import knitr -#' @import RJSONIO -#' @param username plotly username -#' @param key plotly API key -#' @param base_url plotly server -#' -#' @return An object of class PlotlyClass, except for the final object after -#' adding layers becomes a list class. -#' @details See documentation and examples at https://plot.ly/API -#' @references https://plot.ly/API -#' @author Chris Parmer chris@@plot.ly +#' @references https://plot.ly/rest/ +#' @export +signup <- function(username, email, save = TRUE) { + if (missing(username)) username <- verify("username") + if (missing(email)) stop("Must specify a valid email") + # construct body of message to plotly server + bod <- list( + un = username, + email = email, + platform = "R", + version = as.character(packageVersion("plotly")) + ) + base_url <- file.path(get_domain(), "apimkacct") + resp <- httr::POST(base_url, body = bod) + stop_for_status(resp) + con <- RJSONIO::fromJSON(content(resp, as = "text")) + if (nchar(con[["error"]]) > 0) stop(con[["error"]], call. = FALSE) + # Relaying a message with a private key probably isn't a great idea -- + # https://github.com/ropensci/plotly/pull/217#issuecomment-100381166 + # if (nchar(con[["message"]]) > 0) message(con[["message"]], call. = FALSE) + if (save) { + # store API key as an environment variable in .Rprofile + cat_profile("username", con[["un"]]) + cat_profile("api_key", con[["api_key"]]) + } + invisible(structure(con, class = "apimkacct")) +} + +#' Create, modify and style plotly graphs from R +#' +#' Create, See up-to-date documentation and examples at +#' https://plot.ly/API +#' +#' @param p Either a ggplot object or a list of data/arguments to post to the +#' plotly API. +#' @param browse should the default web browser be prompted to open the Plotly result? +#' @param ... additional arguments passed onto \link{plotly_POST}. +#' @seealso \link{signup}, \link{plotly_POST} +#' @import httr RJSONIO #' @export #' @examples \dontrun{ -#' ## View https://plot.ly/API for more examples -#' ## Generate a simple plot -#' username <- 'anna.lyst' # fill in with your plotly username -#' api_key <- 'y37zkd' # fill in with your plotly API key -#' py <- plotly(username, api_key) -#' ## generate some data -#' x <- c(0, 1, 2) -#' y <- c(10, 11, 12) -#' -#' ## Send data to Plotly. Plotly will render an interactive graph and will -#' ## return a URL where you can view your plot -#' ## This call sends data to Plotly, Plotly renders an interactive -#' ## graph, and returns a URL where you can view your plot -#' response <- py$plot(x, y) -#' response$url # view your plot at this URL -#' browseURL(response$url) # use browseURL to go to the URL in your browser +#' # You need a plotly username and API key to communicate with the plotly API. +#' +#' # If you don't already have an API key, you can obtain one with a valid +#' # username and email via signup(). +#' s <- signup('anna.lyst', 'anna.lyst@@plot.ly') +#' +#' # If you already have a username and API key, please create the following +#' # environment variables: +#' Sys.setenv("plotly_username" = "me") +#' Sys.setenv("plotly_api_key" = "mykey") +#' # You can also change the default domain if you have a plotly server. +#' Sys.setenv("plotly_domain" = "http://mydomain.com") +#' +#' # If you don't want to specify these environment variables everytime you +#' # start R, you can put that code in a .Rprofile (see help(.Rprofile)) +#' +#' # Send data directly to Plotly's Javascript Graphing Library +#' # https://plot.ly/javascript-graphing-library/ +#' p <- list( +#' x = c(0, 1, 2), +#' y = c(10, 11, 12) +#' ) +#' resp <- plotly(p) #' -#' ## Export ggplots directly to plot.ly -#' ggiris <- qplot(Petal.Width, Sepal.Length, data=iris, color=Species) -#' py$ggplotly(ggiris) +#' # plotly() also understands how to map (some) ggplot objects to Plotly graphs +#' ggiris <- qplot(Petal.Width, Sepal.Length, data = iris, color = Species) +#' plotly(ggiris) #' data(canada.cities, package="maps") #' viz <- ggplot(canada.cities, aes(long, lat)) + #' borders(regions="canada", name="borders") + #' coord_equal() + #' geom_point(aes(text=name, size=pop), colour="red", #' alpha=1/2, name="cities") -#' py$ggplotly(viz) +#' plotly(viz) #' } +plotly <- function(p = last_plot(), browse = interactive(), ...) { + if (is.ggplot(p)) { + p <- gg2list(p) + } else if (!is.list(p)) { + stop("p must be either a ggplot object or a list") + } + # In an effort to save some legacy users headache... + # specifying username and key should still work + .args <- as.list(match.call()) + if ("username" %in% names(.args)) + Sys.setenv("plotly_username" = args[["username"]]) + if ("key" %in% names(.args)) + Sys.setenv("plotly_api_key" = args[["key"]]) + if (!"data" %in% names(p)) + stop("p should have at least one element named 'data'", + "(which is mapped to the args parameter in the plotly REST API).") + resp <- plotly_POST(p$data, list(layout = p$layout), ...) + if (browse) browseURL(resp[["url"]]) + resp +} +#' Create, modify and style plotly graphs from R +#' +#' POST messages to the clientresp resource of plotly's REST API. Unlike \link{plotly}, +#' this function does not support ggplot objects. +#' +#' @param args a list. For details see the rest API docs. +#' @param kwargs a list. For details see the rest API docs. +#' @param origin a character vector of length one. For details see the rest API docs. +#' @param ... arguments passed along to \code{httr::POST()} +#' @export +#' @references https://plot.ly/rest/ +#' @seealso \link{signup}, \link{plotly} +#' @return An R object created by mapping the JSON content of the plotly API +#' response to its R equivalent. This object has a class of "clientresp" +#' @examples +#' \dontrun{ +#' args <- list(c(0, 1, 2), c(3, 4, 5), c(1, 2, 3), c(6, 6, 5)) +#' resp <- plotly_POST(args) +#' +#' # translate a ggplot object with gg2list(), then upload to plotly +#' p <- gg2list(qplot(1:10)) +#' resp <- plotly_POST(p$data, list(layout = p$layout)) +#' } +#' +plotly_POST <- function(args, kwargs = list(filename = "plot from api", fileopt = "new"), + origin = "plot", ...) { + # some basic input checks + if (!is.list(args)) stop("args must be a list") + if (!is.list(kwargs)) stop("kwargs must be a list") + nms <- names(kwargs) + # filename and fileopt are required + if (!"filename" %in% nms) kwargs$filename <- "plot from api" + if (!"fileopt" %in% nms) kwargs$fileopt <- "new" + # construct body of message to plotly server + bod <- list( + un = verify("username"), + key = verify("api_key"), + origin = origin, + platform = "R", + version = as.character(packageVersion("plotly")), + args = RJSONIO::toJSON(args, digits = 50, collapse = ""), + kwargs = RJSONIO::toJSON(kwargs, digits = 50, collapse = "") + ) + base_url <- file.path(get_domain(), "clientresp") + resp <- httr::POST(base_url, body = bod, ...) + stop_for_status(resp) + con <- RJSONIO::fromJSON(content(resp, as = "text")) + if (nchar(con[["error"]]) > 0) stop(con[["error"]], call. = FALSE) + if (nchar(con[["warning"]]) > 0) warning(con[["warning"]], call. = FALSE) + if (nchar(con[["message"]]) > 0) message(con[["message"]], call. = FALSE) + invisible(structure(con, class = "clientresp")) +} -plotly <- function(username=NULL, key=NULL, base_url=NULL) { - - if (is.null(username)) { - username <- get_credentials_file(c("username", "api_key"))$username - } - if (is.null(key)) { - key <- get_credentials_file(c("username", "api_key"))$api_key - } - if (is.null(username) || username == "" || is.null(key) || key == "") { - stop("Credentials Not Found!\n -It looks like you haven't set up your Plotly account credentials yet.\n -To get started, save your plotly username and API key by calling:\n -> set_credentials_file(UserName, ApiKey)\n -For more help, see https://plot.ly/R or contact .") - } - # Plotly server - if (is.null(base_url)) { - base_url <- get_config_file("plotly_domain")$plotly_domain - } - if (is.null(base_url) || base_url == "") { - base_url <- "https://plot.ly" - } - - # public attributes/methods that the user has access to - pub <- list(username=username, key=key, filename="from api", fileopt=NULL, - version="0.6.2") - priv <- list() - - pub$makecall <- function(args, kwargs, origin) { - if (is.null(kwargs$filename)) - kwargs$filename <- pub$filename - if (is.null(kwargs$fileopt)) - kwargs$fileopt <- pub$fileopt - url <- paste(base_url, "/clientresp", sep="") - - respst <- postForm(url, platform="R", version=pub$version, - args=toJSON(args, digits=50, collapse=""), un=pub$username, - key=pub$key, origin=origin, - kwargs=toJSON(kwargs, digits=50, collapse=""), - .opts=list(sslversion=1, # 1 is for TLSv1 - cainfo=system.file("CurlSSL", - "cacert.pem", - package="RCurl"))) - if (is.raw(respst)) { - respst <- rawToChar(respst) - } - - resp <- fromJSON(respst, simplify = FALSE) - if (!is.null(kwargs$filename)) - resp$filename <- kwargs$filename - if (!is.null(resp$error)) - cat(resp$err) - if (!is.null(resp$warning)) - cat(resp$warning) - if (!is.null(resp$message)) - cat(resp$message) - return(resp) - } - priv$plotly_hook <- function(before, options, envir) { - if (!before) { - # set width and height from options or default square - w <- if(is.null(options[["width"]])) "600" else options[["width"]] - h <- if(is.null(options[["height"]])) "600" else options[["height"]] - paste("", sep="") - } - } - - pub$plotly <- function(..., kwargs = list(filename = NULL, fileopt = NULL)) { - args <- list(...) - return(pub$makecall(args = args, kwargs = kwargs, origin = "plot")) - } - pub$ggplotly <- function(gg=last_plot(), kwargs=list(filename=NULL, - fileopt=NULL, - width=NULL, - height=NULL), - session="interactive") { - if(!is.ggplot(gg)){ - stop("gg must be a ggplot") - } - fig <- gg2list(gg) - if (!"auto_open" %in% names(kwargs)) { - kwargs <- c(kwargs, auto_open=TRUE) - } - - pargs <- fig$data - pargs$kwargs <- kwargs - pargs$kwargs$layout <- fig$layout - - if (session == "interactive") { # we are on the command line - resp <- do.call(pub$plotly, pargs) - if (pargs$kwargs$auto_open) { - browseURL(resp$url) - } - invisible(list(data=pargs, response=resp)) - } else if (session == "notebook") { # we are in the IR notebook - do.call(pub$irplot, pargs) - invisible(list(data=pargs)) - } else if (session == "knitr") { # we are in knitr/RStudio - do.call(pub$iplot, pargs) - invisible(list(data=pargs)) - } else { - stop("Value of session can be: 'interactive', 'notebook', or 'knitr'.") - } - } - pub$get_figure <- function(file_owner, file_id) { - headers <- c("plotly-username"=pub$username, - "plotly-apikey"=pub$key, - "plotly-version"=pub$version, - "plotly-platform"="R") - response_handler <- basicTextGatherer() - header_handler <- basicTextGatherer() - curlPerform(url=paste(base_url, "apigetfile", file_owner, file_id, - sep="/"), - httpheader=headers, - writefunction=response_handler$update, - headerfunction=header_handler$update, - .opts=list(sslversion=1, # 1 is for TLSv1 - cainfo=system.file("CurlSSL", "cacert.pem", - package="RCurl"))) - resp_header <- as.list(parseHTTPHeader(header_handler$value())) - - # Parse status - if (resp_header$status != "200") { - cat(resp_header$statusMsg) - stop(resp_header$status) - } - - body_string <- response_handler$value() - resp <- RJSONIO::fromJSON(body_string) - if (!is.null(resp$error) && resp$error != "") - stop(resp$err) - if (!is.null(resp$warning) && resp$error != "") - cat(resp$warning) - if (!is.null(resp$message) && resp$error != "") - cat(resp$message) - - resp$payload$figure - } - pub$iplot <- function(..., kwargs = list(filename = NULL, fileopt = NULL)) { - # Embed plotly graphs as iframes for knitr documents - r <- pub$plotly(..., kwargs = kwargs) - # bind url to the knitr options and pass into the plotly knitr hook - knit_hooks$set(plotly = function(before, options, envir) { - options[["url"]] <- r[["url"]] - priv$plotly_hook(before, options, envir) - }) +# Print method for a client response +print.clientresp <- function(p) { + cat(" Filename: ", p[["filename"]], "\n", "URL:", p[["url"]]) +} + +#' Request data/layout for a particular Plotly figure +#' @param username corresponding username for the figure. +#' @param id of the Plotly figure. +#' @export +#' @references https://plot.ly/rest/ +#' @examples +#' \dontrun{ +#' # https://plot.ly/~TestBot/100 +#' resp <- get_figure("TestBot", "100") +#' names(resp[["layout"]]) +#' names(resp[["data"]]) +#' } +get_figure <- function(username, id) { + if (missing(username)) username <- verify("username") + if (missing(id)) stop("Must provide a figure id.") + base_url <- file.path(get_domain(), "apigetfile", username, id) + resp <- httr::GET(base_url, plotly_headers()) + stop_for_status(resp) + fig <- RJSONIO::fromJSON(content(resp, as = "text"))[["payload"]][["figure"]] + invisible(structure(fig, class = "apigetfile")) +} + +# TODO: smarter print method! (we don't want to print ugly lists) +print.apigetfile <- function(p) { + NextMethod("print") +} + +#' Embed a plotly iframe into an R markdown document via \code{knit_print} +#' @param x named list of ggplots and option lists to pass to \code{animint2dir}. +#' @param options knitr options. +#' @param ... placeholder. +#' @export +#' @references https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd +knit_print.clientresp <- function(x, options, ...) { + if (!requireNamespace("knitr")) { + warning("Please install.packages('knitr')") + return(x) } - pub$irplot <- function(..., kwargs=list(filename=NULL, fileopt=NULL, - width=NULL, height=NULL)) { - # Embed plotly graphs as iframes in IR notebooks - r <- pub$plotly(..., kwargs=kwargs) - w <- if (is.null(kwargs$width)) "100%" else kwargs$width - h <- if (is.null(kwargs$height)) "525" else kwargs$height - html <- paste("", sep="") - require(IRdisplay) - display_html(html) + w <- if (is.null(options[["width"]])) "600" else options[["width"]] + h <- if (is.null(options[["height"]])) "600" else options[["height"]] + iframe <- plotly_iframe(x[["url"]], h, w) + knitr::asis_output(iframe) +} + +#' Embed a plotly iframe into a IPython Notebook +#' @param url A url pointing to a plotly graph +#' @param width attribute of the iframe +#' @param height attribute of the iframe +#' @export +embed_notebook <- function(url, width = "100%", height = "525") { + if (!inherits(p, "clientresp")) { + p <- plotly(p) + url <- p[["url"]] } - pub$embed <- function(url) { - # knitr hook - knit_hooks$set(plotly = function(before, options, envir) { - options[["url"]] <- url - priv$plotly_hook(before, options, envir) - }) + if (!requireNamespace("IRdisplay")) { + warning("You need the IRdisplay package to use this function: \n", + "devtools::install_github(c('IRkernel/repr', 'IRKernel/IRdisplay'))") + return(p) } - pub$layout <- function(..., kwargs = list(filename = NULL, fileopt = NULL)) { - args <- list(...) - return(pub$makecall(args = args, kwargs = kwargs, origin = "layout")) + IRdisplay::display_html(plotly_iframe(url, height, width)) +} + +# ---------------------------------------- +# Non-exported helper functions +# ---------------------------------------- + +get_domain <- function() { + Sys.getenv("plotly_domain", "https://plot.ly") +} + +plotly_headers <- function() { + httr::add_headers(.headers = c( + "plotly-username" = verify("username"), + "plotly-apikey" = verify("api_key"), + "plotly-version" = as.character(packageVersion("plotly")), + "plotly-platform" = "R")) +} + +# verify that a certain environment variable exists +verify <- function(what = "username") { + who <- paste0("plotly_", what) + val <- Sys.getenv(who, "") + # If the environment variable doesn't exist, fall back on hidden files + if (val == "") { + PLOTLY_DIR <- file.path(normalizePath("~", mustWork = TRUE), ".plotly") + CREDENTIALS_FILE <- file.path(PLOTLY_DIR, ".credentials") + CONFIG_FILE <- file.path(PLOTLY_DIR, ".config") + + stop("Must specify ", what, call. = FALSE) } - pub$style <- function(..., kwargs = list(filename = NULL, fileopt = NULL)) { - args <- list(...) - cat(kwargs) - return(pub$makecall(args = args, kwargs = kwargs, origin = "style")) + + val +} + +plotly_iframe <- function(url, width, height) { + paste("", sep="") +} + +# try to write environment variables to an .Rprofile +cat_profile <- function(key, value, path = "~") { + r_profile <- file.path(normalizePath(path, mustWork = TRUE), + ".Rprofile") + snippet <- sprintf('\nSys.setenv("plotly_%s" = "%s")', key, value) + if (!file.exists(r_profile)) { + message("Creating", r_profile) + r_profile_con <- file(r_profile) } - # wrap up the object - pub <- list2env(pub) - class(pub) <- "PlotlyClass" - return(pub) + if (file.access(r_profile, 2) != 0) + stop("R doesn't have permission to write to this file: ", path) + if (file.access(r_profile, 4) != 0) + stop("R doesn't have permission to read this file: ", path) + message("Adding plotly_", key, " environment variable to ", r_profile) + cat(snippet, file = r_profile, append = TRUE) } + +# bummer, looks like we can't use RStudio's viewer (yet) -- +# https://github.com/rstudio/rstudioapi/issues/2#issuecomment-99250180 +# browse_url <- function(url) { +# usr <- verify("username") +# id <- sub(".*/([0-9]+)[/]?.*", "\\1", url) +# html <- readLines(system.file("htmljs/index.html", package = "plotly")) +# tmp <- tempfile(fileext = ".html") +# html <- gsub("username/id", paste(usr, id, sep = "/"), html) +# writeLines(html, tmp) +# # Try to view an 'embedded' version in RStudio preview. This was +# # copied/adapted from Yihui Xie's work on servr -- +# # https://github.com/yihui/servr/blob/39a61972e278adc5bbd49a74c68de858bb2c144f/R/utils.R#L55-L69 +# browseR = if ('tools:rstudio' %in% search()) getOption('viewer') else { +# if (is_rstudio()) getFromNamespace('viewer', 'rstudioapi') +# } +# # rstudio::viewer() does not seem to work when a separate R session is +# # launched from RStudio, so we need to try() and if it fails, fall back to the +# # default web browser +# if (is.null(browseR) || !is.function(browseR) || +# inherits(try(browseR('http://www.rstudio.com'), silent = TRUE), 'try-error')) +# browseR = getOption('browser') +# browseR(tmp) +# } +# +# is_rstudio <- function() Sys.getenv('RSTUDIO') == '1' diff --git a/R/signup.R b/R/signup.R deleted file mode 100644 index bd74396ff5..0000000000 --- a/R/signup.R +++ /dev/null @@ -1,49 +0,0 @@ -#' Sign up to plotly. -#' -#' A sign up interface to Plotly through the R Console. See documentation and -#' examples at https://plot.ly/API -#' -#' @import RCurl RJSONIO -#' @param username Desired username -#' @param email Desired email -#' @details See documentation and examples at https://plot.ly/API -#' @return -#' \itemize{ -#' \item api_key key to use with the api -#' \item tmp_pw temporary password to access your plotly account -#' } -#' @references https://plot.ly/API -#' @author Chris Parmer chris@@plot.ly -#' @note https://plot.ly/API -#' @export -#' @examples \dontrun{ -#' username <- 'anna.lyst' -#' email <- 'anna.lyst@@plot.ly' -#' response <- signup(username, email) -#' response$api_key # key to access plotly with -#' response$tmp_pw # temporary password to access your plotly account -#' } -signup <- function(username=NULL, email=NULL){ - if(is.null(username)) - key <- getOption("plotlyUsername", stop("you need a user name for Plot.ly - See the signup function")) - if(is.null(key)) - key <- getOption("plotlyKey", stop("you need an API key for Plot.ly - See the signup function")) - - platform = 'R' - version = as.character(packageVersion("plotly")) - url <- "https://plot.ly/apimkacct" - options(RCurlOptions = list(sslversion = 3, cainfo = system.file("CurlSSL", "cacert.pem", - package = "RCurl"))) - respst <- postForm(url, platform = platform, version = version, email = email, - un = username) - resp <- fromJSON(respst, simplify = FALSE) - if (!is.null(resp$filename)) - pub$filename <- resp$filename - if (!is.null(resp$error)) - cat(resp$err) - if (!is.null(resp$warning)) - cat(resp$warning) - if (!is.null(resp$message)) - cat(resp$message) - return(resp) -} \ No newline at end of file diff --git a/R/tools.R b/R/tools.R deleted file mode 100644 index 6330659b98..0000000000 --- a/R/tools.R +++ /dev/null @@ -1,149 +0,0 @@ -# Functions that USERS will possibly want access to. - - -PLOTLY_DIR <- file.path(path.expand("~"), ".plotly") -CREDENTIALS_FILE <- file.path(PLOTLY_DIR, ".credentials") -CONFIG_FILE <- file.path(PLOTLY_DIR, ".config") -# PLOT_OPTIONS_FILE <- file.path(PLOTLY_DIR, ".plot_options") -# THEMES_FILE <- file.path(PLOTLY_DIR, ".themes") - - -#' Create file if nonexistent -#' @param abspath Character vector of file path -#' @return NULL -ensure_file_exist <- function(abspath) { - if (!file.exists(abspath)) { - dir.create(dirname(abspath), showWarnings=FALSE, recursive=TRUE) - file.create(abspath) - } - invisible() -} - - -# Credentials Tools ### - -#' Read Plotly credentials file (which is a JSON) -#' @param args Character vector of keys you are looking up -#' @return List of keyword-value pairs (credentials) -#' @examples -#' \dontrun{ -#' get_credentials_file(c("username", "api_key")) -#' } -get_credentials_file <- function(args=c()) { - ensure_file_exist(CREDENTIALS_FILE) - if (file.info(CREDENTIALS_FILE)$size) { - credentials_data <- fromJSON(CREDENTIALS_FILE) - if (!is.null(args)) { - credentials_data <- credentials_data[args] - } - } else { - credentials_data <- NULL - } - return(as.list(credentials_data)) -} - - -#' Read and print Plotly credentials file, wrapping get_credentials_file() -#' @param args Character vector of keys you are looking up -#' @return List of keyword-value pairs (credentials) -#' @export -show_credentials_file <- function(args=c()) { - print("Your credentials file:") - print(get_credentials_file(args)) -} - - -#' Set the keyword-value pairs in Plotly credentials file -#' @param username plotly username -#' @param api_key plotly API key -#' @param stream_ids stream ids -#' @return List of keyword-value pairs (credentials) -#' @export -#' @examples -#' \dontrun{ -#' set_credentials_file("username", "api_key", list("foo", "bar)) -#' } -set_credentials_file <- function(username="", api_key="", - stream_ids=list("", "")) { - credentials_data <- show_credentials_file() - new_credentials <- list() - if (username != "") { - new_credentials$username <- username - } else { - new_credentials$username <- credentials_data$username - } - if (api_key != "") { - new_credentials$api_key <- api_key - } else { - new_credentials$api_key <- credentials_data$api_key - } - if (stream_ids[[1]] != "") { - new_credentials$stream_ids <- stream_ids - } else { - new_credentials$stream_ids <- credentials_data$stream_ids - } - writeLines(toJSON(new_credentials), CREDENTIALS_FILE) - print("Now,") - show_credentials_file() -} - - -# Config Tools ### - -#' Read Plotly config file (which is a JSON) and create one if nonexistent -#' @param args Character vector of keys you are looking up -#' @return List of keyword-value pairs (config) -#' @examples -#' \dontrun{ -#' get_config_file(c("plotly_domain", "plotly_streaming_domain")) -#' } -get_config_file <- function(args=c()) { - ensure_file_exist(CONFIG_FILE) - if (file.info(CONFIG_FILE)$size) { - config_data <- fromJSON(CONFIG_FILE) - if (!is.null(args)) { - config_data <- config_data[args] - } - } else { - config_data <- NULL - } - return(as.list(config_data)) -} - - -#' Read and print Plotly config file, wrapping get_credentials_file() -#' @param args Character vector of keys you are looking up -#' @return List of keyword-value pairs (credentials) -#' @export -show_config_file <- function(args=c()) { - print("Your config file:") - print(get_config_file(args)) -} - - -#' Set keyword-value pairs in Plotly config file -#' @param plotly_domain plotly domain -#' @param plotly_streaming_domain plotly streaming domain -#' @return List of keyword-value pairs (config) -#' @export -#' @examples -#' \dontrun{ -#' set_config_file("https://kitty.plot.ly", "stream.kitty.plot.ly") -#' } -set_config_file <- function(plotly_domain="", plotly_streaming_domain="") { - config_data <- show_config_file() - new_config <- list() - if (plotly_domain != "") { - new_config$plotly_domain <- plotly_domain - } else { - new_config$plotly_domain <- "https://plot.ly" - } - if (plotly_streaming_domain != "") { - new_config$plotly_streaming_domain <- plotly_streaming_domain - } else { - new_config$plotly_streaming_domain <- "stream.plot.ly" - } - writeLines(toJSON(new_config), CONFIG_FILE) - print("Now,") - show_config_file() -} diff --git a/inst/build-push-comment.R b/inst/build-push-comment.R new file mode 100644 index 0000000000..30c2bb1443 --- /dev/null +++ b/inst/build-push-comment.R @@ -0,0 +1,130 @@ +# ----------------------------------------------------------------------- +# Travis does two types of builds: +# +# (1) A so-called "push". This essentially does a checkout on the most +# recent commit of the pull request, but *doesn't* merge with master. +# In this case, $TRAVIS_PULL_REQUEST = "false" +# (2) A so-called "pr" (pull request). This *does* merge with master. +# In this case, $TRAVIS_PULL_REQUEST contains the pull request number. +# +# Since it makes more sense to visually compared what we'd see *after* we +# merge with master, we don't do anything here if it's a push build. +# ----------------------------------------------------------------------- + +# Read more about Travis environment variables -- +# http://docs.travis-ci.com/user/ci-environment/#Environment-variables +tpr <- Sys.getenv("TRAVIS_PULL_REQUEST") +if (tpr != "false" && tpr != "") { + library("httr") + library("testthat") + # gistr is a good reference for talking to the github API via httr + # https://github.com/ropensci/gistr/blob/master/R/zzz.R + base <- 'https://api.github.com/repos/ropensci/plotly/' + header <- add_headers(`User-Agent` = "plotly", + `Accept` = 'application/vnd.github.v3+json', + `Authorization` = paste0("token ", Sys.getenv("GH_TOKEN"))) + # Grab the branch name for this pull request (must be successful!!) + # http://stackoverflow.com/questions/15096331/github-api-how-to-find-the-branches-of-a-pull-request + pr <- sprintf(paste0(base, 'pulls/%s'), tpr) + res <- GET(url = pr, header) + stop_for_status(res) + info <- content(res) + branch <- strsplit(info$head$label, ":")[[1]][2] + + # Return an abbreviated version of a hash + abbrev_hash <- function(hash = "") substr(hash, 1, 7) + + # Grab HEAD info for each branch (this might not be necessary) +# br <- paste0(base, 'branches') +# res <- GET(br) +# stop_for_status(res) +# info <- content(res) +# commits <- sapply(info, "[[", "commit") +# shas <- unlist(commits["sha",]) +# shas <- sapply(shas, abbrev_hash, USE.NAMES = FALSE) +# shas <- setNames(shas, sapply(info, "[[", "name")) + + # NOTE: $TRAVIS_COMMIT doesn't match the HEAD of this (or master) branch!!! + # Remember that we're *simulating* a merge with master, but the hash for the + # *actual* merge will be different. Instead of installing master each time + # we call save_outputs(), we install once here, if necessary, and re-run tests + this_hash <- abbrev_hash(Sys.getenv("TRAVIS_COMMIT")) + base_hash <- abbrev_hash(info$base$sha) + head_hash <- abbrev_hash(info$head$sha) + test_rerun <- function(hash) { + if (!hash %in% dir("plotly-test-table/R")) { + devtools::install_github("ropensci/plotly", ref = hash, local = FALSE) + message("Rerunning tests") + try(source("plotly/tests/testthat.R", chdir = TRUE)) + } + } + test_rerun(this_hash) + test_rerun(base_hash) + + # TODO: Remove plotly-test-table folders that are no longer needed + # by comparing the directories to branch HEADs for plotly. This could + # be hard to do the git rm properly. We could also run tests for missing + # HEAD shas, but that's probably overkill + + # list png files in a particular directory + pngs <- function(...) { + dir(file.path("plotly-test-table", "R", ...), + pattern = "\\.png$", full.names = T) + } + # Build the main HTML page for this build + ggpngs <- pngs("ggplot2", packageVersion("ggplot2")) + df <- data.frame(sub("\\.png$", "", basename(ggpngs)), + ggpngs, pngs(this_hash), pngs(base_hash)) + names(df) <- c("test", "ggplot2", branch, "master") + # TODO: create an HTML page for each test + df$test <- sprintf(' %s ', df$test) + for (i in setdiff(names(df), "test")) + df[, i] <- sprintf(' ', df[, i]) + print(df) + test_table <- knitr::knit2html(text = '`r knitr::kable(df, type = "html")`', + quiet = TRUE) + dest <- file.path("plotly-test-table", "R", this_hash, "index.html") + writeLines(test_table, dest) + + # TODO: + # * convert for thumbnails!! (see wch/vtest's convert_png() for alternative) + # * create home page for R with commmit info -- https://developer.github.com/v3/git/commits/ + + # add, commit, push to gh-pages branch of plotly-test-table + setwd("plotly-test-table") + system("git status") + system("git add *") + build_link <- paste0('https://travis-ci.org/ropensci/plotly/builds/', + Sys.getenv("TRAVIS_BUILD_ID")) + commit_msg <- paste0('"Pushed from ', build_link, '"') + system(paste('git commit -m', commit_msg)) + # This post explains how this works -- http://rmflight.github.io/posts/2014/11/travis_ci_gh_pages.html + repo <- sprintf("https://%s@github.com/cpsievert/plotly-test-table.git", Sys.getenv("GH_TOKEN")) + system(paste("git pull -q", repo, "gh-pages")) + system(paste("git push -q", repo, "gh-pages")) + + # post comment if a link to this SHA doesn't exist + # (needed since Travis randomly re-builds stuff) + tbl_link <- sprintf("http://cpsievert.github.io/plotly-test-table/R/%s/index.html", this_hash) + msg <- sprintf("On TravisCI, commit %s was successfully merged with %s (master) to create %s. A visual testing table comparing %s with %s can be found here:\n %s", + head_hash, base_hash, this_hash, base_hash, this_hash, tbl_link) + msg <- paste("> The message below was automatically generated after build", build_link, "\n\n", msg) + commentz <- sprintf(paste0(base, 'issues/%s/comments'), tpr) + res <- GET(commentz, header) + warn_for_status(res) + info <- content(res) + old_body <- unlist(lapply(info, "[", "body")) + if (!any(grepl(tbl_link, old_body))) { + json <- jsonlite::toJSON(list(body = msg), auto_unbox = TRUE) + POST(url = commentz, header, body = json, encode = "json") + } else { + message("Link already posted") + } +} else { + message('The test table is only built during the "pull request" build.') +} + + +# IDEAS: +# * iframe into json diffs on github??? +# * Github now renders IPython!!! diff --git a/inst/htmljs/index.html b/inst/htmljs/index.html new file mode 100644 index 0000000000..79fa7ae3f9 --- /dev/null +++ b/inst/htmljs/index.html @@ -0,0 +1,14 @@ + + + + + + + My Plotly + + + + + + + diff --git a/inst/testscripts/.push_test_table.sh b/inst/testscripts/.push_test_table.sh deleted file mode 100644 index b6ef2e1976..0000000000 --- a/inst/testscripts/.push_test_table.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# exit on error -set -e - -# ----------------------------------------------------------------------- -# Travis does two types of builds: -# -# (1) A so-called "push". This essentially does a checkout on the most -# recent commit of the pull request, but *doesn't* merge with master. -# In this case, $TRAVIS_PULL_REQUEST = "false" -# (2) A so-called "pr" (pull request). This *does* merge with master. -# In this case, $TRAVIS_PULL_REQUEST contains the pull request number. -# ----------------------------------------------------------------------- - -# We need the pull request number to talk to the GitHub API, make comments, etc. -[ "${TRAVIS_PULL_REQUEST}" = "false" ] && exit 0 - -git config --global user.name "cpsievert" -git config --global user.email "cpsievert1@gmail.com" - -cd .. -git clone https://github.com/ropensci/plotly-test-table.git -cd plotly-test-table -git checkout gh-pages - -# Read more about Travis environment variables -- -# http://docs.travis-ci.com/user/ci-environment/ -Rscript ../plotly/inst/testscripts/comment.R $TRAVIS_PULL_REQUEST $TRAVIS_BUILD_ID $TRAVIS_COMMIT $GH_TOKEN diff --git a/inst/testscripts/comment.R b/inst/testscripts/comment.R deleted file mode 100644 index 31786439ec..0000000000 --- a/inst/testscripts/comment.R +++ /dev/null @@ -1,74 +0,0 @@ -# first argument is the pull request number (TRAVIS_PULL_REQUEST) -# second is travis build ID (TRAVIS_BUILD_ID) -# third is the commit SHA1 currently being tested (TRAVIS_COMMIT) -# fourth is the github authentication token -a <- commandArgs(TRUE) -# gistr is a good reference for talking to the github API via httr -# https://github.com/ropensci/gistr/blob/master/R/zzz.R -library("httr") -base <- 'https://api.github.com/repos/ropensci/plotly/' -pr <- sprintf(paste0(base, 'pulls/%s'), a[1]) -header <- add_headers(`User-Agent` = "plotly", - `Accept` = 'application/vnd.github.v3+json', - `Authorization` = paste0("token ", a[4])) -# Must be successful since we grab the branch name for this pull request -# and SHA1 info from the request content -res <- GET(url = pr, header) -stop_for_status(res) -info <- content(res) -# find the branch name for this pull request -# http://stackoverflow.com/questions/15096331/github-api-how-to-find-the-branches-of-a-pull-request -branch <- strsplit(info$head$label, ":")[[1]][2] - -# plotly-test-table build script assumes we've checkout the dev branch. -# Note that travis does something like this for "pr" build: -#$ git fetch origin +refs/pull/number/merge: -#$ git checkout -qf FETCH_HEAD -# this leaves HEAD in a detached state, but we should be able to do: -# git checkout -b new_branch_name -setwd("../plotly") -if (system(paste("git checkout -b", branch)) != 0L) - stop(paste("Failed to 'git checkout -b'", branch, "branch")) -devtools::install() -setwd("../plotly-test-table") -cat("user,SHA1,label", file = "code_commits.csv") -row1 <- paste0("\nropensci,", info$base$sha, ",master") -cat(row1, file = "code_commits.csv", append = TRUE) -row2 <- paste0("\nropensci,", a[3], ",", branch) -cat(row2, file = "code_commits.csv", append = TRUE) - -# copy over file (created during Rscript) -# with sha/branch info for building test table -system("touch table.R") -if (system("make") != 0L) stop("Failed to 'make' test table") - -# add, commit, push to gh-pages branch of plotly-test-table -system("git add index.html") -system("git add tables/*/*.html") -system("git add data/*/*.png") -system("git add data/*/*.log") -build_link <- paste0('https://travis-ci.org/ropensci/plotly/builds/', a[2]) -commit_msg <- paste0('"Pushed from ', build_link, '"') -system(paste('git commit -m', commit_msg)) -# This post explains how this works -- http://rmflight.github.io/posts/2014/11/travis_ci_gh_pages.html -repo <- sprintf("https://%s@github.com/ropensci/plotly-test-table.git", a[4]) -system(paste("git pull -q", repo, "gh-pages")) -system(paste("git push -q", repo, "gh-pages")) - -# post comment if a link to this SHA doesn't exist -# (needed since Travis randomly re-builds stuff) -tbl_link <- sprintf("http://ropensci.github.io/plotly-test-table/tables/%s/index.html", a[3]) -msg <- sprintf("On TravisCI, commit %s was successfully merged with %s (master) to create %s. A visual testing table comparing %s with %s can be found here:\n %s", - info$head$sha, info$base$sha, a[3], info$base$sha, a[3], tbl_link) -msg <- paste("> The message below was automatically generated after build", build_link, "\n\n", msg) -commentz <- sprintf(paste0(base, 'issues/%s/comments'), a[1]) -res <- GET(commentz, header) -warn_for_status(res) -info <- content(res) -old_body <- unlist(lapply(info, "[", "body")) -if (!any(grepl(tbl_link, old_body))) { - json <- jsonlite::toJSON(list(body = msg), auto_unbox = TRUE) - httr::POST(url = commentz, header, body = json, encode = "json") -} else { - message("Link already posted") -} diff --git a/man/embed_notebook.Rd b/man/embed_notebook.Rd new file mode 100644 index 0000000000..066f12e451 --- /dev/null +++ b/man/embed_notebook.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2 (4.1.1): do not edit by hand +% Please edit documentation in R/plotly.R +\name{embed_notebook} +\alias{embed_notebook} +\title{Embed a plotly iframe into a IPython Notebook} +\usage{ +embed_notebook(url, width = "100\%", height = "525") +} +\arguments{ +\item{url}{A url pointing to a plotly graph} + +\item{width}{attribute of the iframe} + +\item{height}{attribute of the iframe} +} +\description{ +Embed a plotly iframe into a IPython Notebook +} + diff --git a/man/ensure_file_exist.Rd b/man/ensure_file_exist.Rd deleted file mode 100644 index 4b1586acbe..0000000000 --- a/man/ensure_file_exist.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{ensure_file_exist} -\alias{ensure_file_exist} -\title{Create file if nonexistent} -\usage{ -ensure_file_exist(abspath) -} -\arguments{ -\item{abspath}{Character vector of file path} -} -\description{ -Create file if nonexistent -} - diff --git a/man/get_config_file.Rd b/man/get_config_file.Rd deleted file mode 100644 index 1af0d1dcb1..0000000000 --- a/man/get_config_file.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{get_config_file} -\alias{get_config_file} -\title{Read Plotly config file (which is a JSON) and create one if nonexistent} -\usage{ -get_config_file(args = c()) -} -\arguments{ -\item{args}{Character vector of keys you are looking up} -} -\value{ -List of keyword-value pairs (config) -} -\description{ -Read Plotly config file (which is a JSON) and create one if nonexistent -} -\examples{ -\dontrun{ -get_config_file(c("plotly_domain", "plotly_streaming_domain")) -} -} - diff --git a/man/get_credentials_file.Rd b/man/get_credentials_file.Rd deleted file mode 100644 index b45712ccb4..0000000000 --- a/man/get_credentials_file.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{get_credentials_file} -\alias{get_credentials_file} -\title{Read Plotly credentials file (which is a JSON)} -\usage{ -get_credentials_file(args = c()) -} -\arguments{ -\item{args}{Character vector of keys you are looking up} -} -\value{ -List of keyword-value pairs (credentials) -} -\description{ -Read Plotly credentials file (which is a JSON) -} -\examples{ -\dontrun{ -get_credentials_file(c("username", "api_key")) -} -} - diff --git a/man/get_figure.Rd b/man/get_figure.Rd new file mode 100644 index 0000000000..0bc1c007ae --- /dev/null +++ b/man/get_figure.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2 (4.1.1): do not edit by hand +% Please edit documentation in R/plotly.R +\name{get_figure} +\alias{get_figure} +\title{Request data/layout for a particular Plotly figure} +\usage{ +get_figure(username, id) +} +\arguments{ +\item{username}{corresponding username for the figure.} + +\item{id}{of the Plotly figure.} +} +\description{ +Request data/layout for a particular Plotly figure +} +\examples{ +\dontrun{ + # https://plot.ly/~TestBot/100 + resp <- get_figure("TestBot", "100") + names(resp[["layout"]]) + names(resp[["data"]]) +} +} +\references{ +https://plot.ly/rest/ +} + diff --git a/man/knit_print.clientresp.Rd b/man/knit_print.clientresp.Rd new file mode 100644 index 0000000000..d7c6f39b21 --- /dev/null +++ b/man/knit_print.clientresp.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2 (4.1.1): do not edit by hand +% Please edit documentation in R/plotly.R +\name{knit_print.clientresp} +\alias{knit_print.clientresp} +\title{Embed a plotly iframe into an R markdown document via \code{knit_print}} +\usage{ +knit_print.clientresp(x, options, ...) +} +\arguments{ +\item{x}{named list of ggplots and option lists to pass to \code{animint2dir}.} + +\item{options}{knitr options.} + +\item{...}{placeholder.} +} +\description{ +Embed a plotly iframe into an R markdown document via \code{knit_print} +} +\references{ +https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd +} + diff --git a/man/plotly.Rd b/man/plotly.Rd index 055b425439..58cd6cd5da 100644 --- a/man/plotly.Rd +++ b/man/plotly.Rd @@ -2,73 +2,61 @@ % Please edit documentation in R/plotly.R \name{plotly} \alias{plotly} -\title{Main interface to plotly} +\title{Create, modify and style plotly graphs from R} \usage{ -plotly(username = NULL, key = NULL, base_url = NULL) +plotly(p = last_plot(), browse = interactive(), ...) } \arguments{ -\item{username}{plotly username} +\item{p}{Either a ggplot object or a list of data/arguments to post to the +plotly API.} -\item{key}{plotly API key} +\item{browse}{should the default web browser be prompted to open the Plotly result?} -\item{base_url}{plotly server} -} -\value{ -An object of class PlotlyClass, except for the final object after -adding layers becomes a list class. +\item{...}{additional arguments passed onto \link{plotly_POST}.} } \description{ -A call to \code{plotly(username, key)} creates an object of class -'PlotlyClass', which has methods: -\itemize{ - \item Plotting: py$plotly(x1, y1[, x2, y2, ...], kwargs=kwargs) or - py$plotly({data1[, data2, ...]}, kwargs=kwargs), py$ggplotly() - \item Styling Data: py$style(data1,data2,..., kwargs=kwargs) - \item Styling Layout: py$layout(layout, kwargs=kwargs) - \item Utilities: py$get_figure(file_owner, file_id) -} -} -\details{ -Plotly interface object. See up-to-date documentation and examples at +Create, See up-to-date documentation and examples at https://plot.ly/API - -See documentation and examples at https://plot.ly/API } \examples{ \dontrun{ -## View https://plot.ly/API for more examples -## Generate a simple plot -username <- 'anna.lyst' # fill in with your plotly username -api_key <- 'y37zkd' # fill in with your plotly API key -py <- plotly(username, api_key) -## generate some data -x <- c(0, 1, 2) -y <- c(10, 11, 12) +# You need a plotly username and API key to communicate with the plotly API. + +# If you don't already have an API key, you can obtain one with a valid +# username and email via signup(). +s <- signup('anna.lyst', 'anna.lyst@plot.ly') -## Send data to Plotly. Plotly will render an interactive graph and will -## return a URL where you can view your plot -## This call sends data to Plotly, Plotly renders an interactive -## graph, and returns a URL where you can view your plot -response <- py$plot(x, y) -response$url # view your plot at this URL -browseURL(response$url) # use browseURL to go to the URL in your browser +# If you already have a username and API key, please create the following +# environment variables: +Sys.setenv(`plotly-username` = "me") +Sys.setenv(`plotly-apikey` = "mykey") +# You can also change the default domain if you have a plotly server. +Sys.setenv(`plotly-domain` = "http://mydomain.com") -## Export ggplots directly to plot.ly -ggiris <- qplot(Petal.Width, Sepal.Length, data=iris, color=Species) -py$ggplotly(ggiris) +# If you don't want to specify these environment variables everytime you +# start R, you can put that code in a .Rprofile (see help(.Rprofile)) + +# Send data directly to Plotly's Javascript Graphing Library +# https://plot.ly/javascript-graphing-library/ +p <- list( + x = c(0, 1, 2), + y = c(10, 11, 12) +) +resp <- plotly(p) + +# plotly() also understands how to map (some) ggplot objects to Plotly graphs +ggiris <- qplot(Petal.Width, Sepal.Length, data = iris, color = Species) +plotly(ggiris) data(canada.cities, package="maps") viz <- ggplot(canada.cities, aes(long, lat)) + borders(regions="canada", name="borders") + coord_equal() + geom_point(aes(text=name, size=pop), colour="red", alpha=1/2, name="cities") - py$ggplotly(viz) + plotly(viz) } } -\author{ -Chris Parmer chris@plot.ly -} -\references{ -https://plot.ly/API +\seealso{ +\link{signup}, \link{plotly_POST} } diff --git a/man/plotly_POST.Rd b/man/plotly_POST.Rd new file mode 100644 index 0000000000..57cca7f3e4 --- /dev/null +++ b/man/plotly_POST.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2 (4.1.1): do not edit by hand +% Please edit documentation in R/plotly.R +\name{plotly_POST} +\alias{plotly_POST} +\title{Create, modify and style plotly graphs from R} +\usage{ +plotly_POST(args, kwargs = list(filename = "plot from api", fileopt = "new"), + origin = "plot", ...) +} +\arguments{ +\item{args}{a list. For details see the rest API docs.} + +\item{kwargs}{a list. For details see the rest API docs.} + +\item{origin}{a character vector of length one. For details see the rest API docs.} + +\item{...}{arguments passed along to \code{httr::POST()}} +} +\value{ +An R object created by mapping the JSON content of the plotly API +response to its R equivalent. This object has a class of "clientresp" +} +\description{ +POST messages to the clientresp resource of plotly's REST API. Unlike \link{plotly}, +this function does not support ggplot objects. +} +\examples{ +\dontrun{ + args <- list(c(0, 1, 2), c(3, 4, 5), c(1, 2, 3), c(6, 6, 5)) + resp <- plotly_POST(args) + + # translate a ggplot object with gg2list(), then upload to plotly + p <- gg2list(qplot(1:10)) + resp <- plotly_POST(p$data, list(layout = p$layout)) +} +} +\references{ +https://plot.ly/rest/ +} +\seealso{ +\link{signup}, \link{plotly} +} + diff --git a/man/set_config_file.Rd b/man/set_config_file.Rd deleted file mode 100644 index 7fef7dc742..0000000000 --- a/man/set_config_file.Rd +++ /dev/null @@ -1,25 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{set_config_file} -\alias{set_config_file} -\title{Set keyword-value pairs in Plotly config file} -\usage{ -set_config_file(plotly_domain = "", plotly_streaming_domain = "") -} -\arguments{ -\item{plotly_domain}{plotly domain} - -\item{plotly_streaming_domain}{plotly streaming domain} -} -\value{ -List of keyword-value pairs (config) -} -\description{ -Set keyword-value pairs in Plotly config file -} -\examples{ -\dontrun{ -set_config_file("https://kitty.plot.ly", "stream.kitty.plot.ly") -} -} - diff --git a/man/set_credentials_file.Rd b/man/set_credentials_file.Rd deleted file mode 100644 index 2830ee586c..0000000000 --- a/man/set_credentials_file.Rd +++ /dev/null @@ -1,28 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{set_credentials_file} -\alias{set_credentials_file} -\title{Set the keyword-value pairs in Plotly credentials file} -\usage{ -set_credentials_file(username = "", api_key = "", stream_ids = list("", - "")) -} -\arguments{ -\item{username}{plotly username} - -\item{api_key}{plotly API key} - -\item{stream_ids}{stream ids} -} -\value{ -List of keyword-value pairs (credentials) -} -\description{ -Set the keyword-value pairs in Plotly credentials file -} -\examples{ -\dontrun{ -set_credentials_file("username", "api_key", list("foo", "bar)) -} -} - diff --git a/man/show_config_file.Rd b/man/show_config_file.Rd deleted file mode 100644 index 5b7f337b81..0000000000 --- a/man/show_config_file.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{show_config_file} -\alias{show_config_file} -\title{Read and print Plotly config file, wrapping get_credentials_file()} -\usage{ -show_config_file(args = c()) -} -\arguments{ -\item{args}{Character vector of keys you are looking up} -} -\value{ -List of keyword-value pairs (credentials) -} -\description{ -Read and print Plotly config file, wrapping get_credentials_file() -} - diff --git a/man/show_credentials_file.Rd b/man/show_credentials_file.Rd deleted file mode 100644 index 467be630f9..0000000000 --- a/man/show_credentials_file.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/tools.R -\name{show_credentials_file} -\alias{show_credentials_file} -\title{Read and print Plotly credentials file, wrapping get_credentials_file()} -\usage{ -show_credentials_file(args = c()) -} -\arguments{ -\item{args}{Character vector of keys you are looking up} -} -\value{ -List of keyword-value pairs (credentials) -} -\description{ -Read and print Plotly credentials file, wrapping get_credentials_file() -} - diff --git a/man/signup.Rd b/man/signup.Rd index aec5165512..0060a96ede 100644 --- a/man/signup.Rd +++ b/man/signup.Rd @@ -1,15 +1,18 @@ % Generated by roxygen2 (4.1.1): do not edit by hand -% Please edit documentation in R/signup.R +% Please edit documentation in R/plotly.R \name{signup} \alias{signup} -\title{Sign up to plotly.} +\title{Create a new plotly account.} \usage{ -signup(username = NULL, email = NULL) +signup(username, email, save = TRUE) } \arguments{ -\item{username}{Desired username} +\item{username}{Desired username.} -\item{email}{Desired email} +\item{email}{Desired email.} + +\item{save}{If request is successful, should the username & API key be +automatically stored as an environment variable in a .Rprofile?} } \value{ \itemize{ @@ -18,28 +21,9 @@ signup(username = NULL, email = NULL) } } \description{ -A sign up interface to Plotly through the R Console. See documentation and -examples at https://plot.ly/API -} -\details{ -See documentation and examples at https://plot.ly/API -} -\note{ -https://plot.ly/API -} -\examples{ -\dontrun{ -username <- 'anna.lyst' -email <- 'anna.lyst@plot.ly' -response <- signup(username, email) -response$api_key # key to access plotly with -response$tmp_pw # temporary password to access your plotly account -} -} -\author{ -Chris Parmer chris@plot.ly +A sign up interface to plotly through the R Console. } \references{ -https://plot.ly/API +https://plot.ly/rest/ } diff --git a/run_tests_with_outputs.R b/run_tests_with_outputs.R deleted file mode 100644 index 372a72f529..0000000000 --- a/run_tests_with_outputs.R +++ /dev/null @@ -1,39 +0,0 @@ -library(testthat) -devtools::install_github("ropensci/plotly", ref="marianne-datetime-binning") -library(plotly) - -setwd("tests") - -save_outputs <- function(gg, name, ignore_ggplot=FALSE, file_prefix="test-ggplot-") { - filesystem_name <- gsub(' ', '_', name) - print(paste("running", name)) - py <- plotly("TestBot", "r1neazxo9w") - u <- py$ggplotly(gg, kwargs=list(filename=paste0("ggplot2/", name), - fileopt="overwrite", auto_open=FALSE)) - plotlyUrl <- u$response$url - writeLines(plotlyUrl, paste0(file_prefix, filesystem_name, ".url")) - pngdata <- getURLContent(paste0(u$response$url, ".png")) - writeBin(as.raw(pngdata), paste0(file_prefix, filesystem_name, "-plotly.png")) - if (!ignore_ggplot) { - ggsave(paste0(file_prefix, filesystem_name, "-ggplot2.png"), plot=gg, w=7, h=5) - } - - # Save the json - writeLines(getURL(paste0(plotlyUrl, ".json")), paste0("test-ggplot-", name, - ".json")) -} - -test_check("plotly") -setwd("cookbook-test-suite") - -source('axes.R') -source('bars_and_lines.R') -source('distributions.R') -source('legends.R') -source('lines.R') -source('means_and_error_bars.R') -source('scatterplots.R') -source('titles.R') - -setwd("../..") - diff --git a/tests/testthat.R b/tests/testthat.R index c5273050e9..3047a2f6ed 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,5 +1,67 @@ -library(testthat) -save_outputs <- function(gg, name, ignore_ggplot=FALSE) { - print(paste("running", name)) +library("testthat") +library("plotly") +# crendentials for the test bot +Sys.setenv("plotly_username" = "TestBot") +Sys.setenv("plotly_apikey" = "r1neazxo9w") +# find the hash of the currently installed plotly package +pkg_info <- devtools::session_info()$packages +src <- subset(pkg_info, package == "plotly")$source +hash <- if (src == "local") { + # you could also do `git rev-parse HEAD`, but this is cleaner for Travis + substr(Sys.getenv("TRAVIS_COMMIT"), 1, 7) +} else { + # thankfully devtools includes hash for packages installed off GitHub + sub("\\)", "", strsplit(src, "@")[[1]][2]) } + +save_outputs <- function(gg, name) { + # only render/save pngs if this is a Travis pull request + # (see build-comment-push.R for better explanation of this logic) + tpr <- Sys.getenv("TRAVIS_PULL_REQUEST") + if (tpr != "false" && tpr != "") { + table_dir <- + file.path(Sys.getenv("TRAVIS_BUILD_DIR"), "..", "plotly-test-table") + plotly_dir <- file.path(table_dir, "R", hash) + gg_dir <- file.path(table_dir, "R", "ggplot2") + if (!dir.exists(plotly_dir)) dir.create(plotly_dir, recursive = TRUE) + if (!dir.exists(gg_dir)) dir.create(gg_dir, recursive = TRUE) + + # If we don't have pngs for this version (of ggplot2), generate them; + # otherwise, generate plotly pngs + # NOTE: we can't save both plotly & ggplot2 at once since R CMD check + # suppresses output and travis has 10 minute time limit + # https://github.com/travis-ci/travis-ci/issues/3849 + ggversion <- as.character(packageVersion("ggplot2")) + if (!ggversion %in% dir(gg_dir)) { + gglife <- file.path(gg_dir, ggversion) + dir.create(gglife, recursive = TRUE) + e <- try(gg, silent = TRUE) + png(filename = file.path(gglife, paste0(name, ".png"))) + if (inherits(e, "try-error")) { + plot(1, type="n") + text(1, "ggplot2 error") + } else gg + dev.off() + } else { + # TODO: could speed things up by avoiding two calls to gg2list() + # (this will require tweaking expect_traces()) + p <- plotly(gg, browse = FALSE) + png_url <- paste0(p[["url"]], ".png") + resp <- httr::GET(png_url) + # print the response if it wasn't successful + if (httr::warn_for_status(resp)) resp + # write png version of plotly figure to disk + writeBin(httr::content(resp, as = "raw"), + file.path(plotly_dir, paste0(name, ".png"))) + } + } + invisible(NULL) +} + test_check("plotly") + +# NOTE: I'm assumming Travis is installing most recent ggplot2 off CRAN +# Here is one way to get current ggplot2 version off of CRAN if need be +# gg <- rvest::html("http://cran.r-project.org/web/packages/ggplot2/") +# tab <- rvest::html_table(gg, header = FALSE)[[1]] +# ggversion <- tab[grepl("Version:", tab[, 1]), 2] diff --git a/tests/testthat/test-ggplot-step.R b/tests/testthat/test-ggplot-step.R index 0aa8bb837d..1712405ef5 100644 --- a/tests/testthat/test-ggplot-step.R +++ b/tests/testthat/test-ggplot-step.R @@ -49,7 +49,7 @@ test_that("direction hvh is translated to shape=hvh", { expect_equal(length(L$data), 2) expect_identical(L$data[[1]]$line$shape, "hvh") - save_outputs(gg.hvh, "step-gg.hvh", TRUE) + save_outputs(gg.hvh, "step-gg.hvh") }) test_that("direction vhv is translated to shape=vhv", { @@ -58,5 +58,5 @@ test_that("direction vhv is translated to shape=vhv", { expect_equal(length(L$data), 2) expect_identical(L$data[[1]]$line$shape, "vhv") - save_outputs(gg.vhv, "step-gg.vhv", TRUE) + save_outputs(gg.vhv, "step-gg.vhv") }) diff --git a/tests/testthat/test-plotly-filename.R b/tests/testthat/test-plotly-filename.R index 51c92b14ec..58d1500a7d 100644 --- a/tests/testthat/test-plotly-filename.R +++ b/tests/testthat/test-plotly-filename.R @@ -1,20 +1,11 @@ context("Filename") test_that("filepath with directories is returned as passed", { - x <- c(-1.50548425849621, 0.023267831354017, -1.38460390550496, - -0.805552814226363, 1.59651736643461, 0.936302685370894, - 0.512729504994891, -0.24492573745161, -0.465348603632604, - 0.173523456651353, 0.389491211182137, -0.275308705542518, - -0.132866228059449, -0.336255877656944, 0.916535489109209, - -0.936870130264329, 0.363137478307925, -1.26433467241078, - -0.388804188531171, 0.785842426281935) - data = list(x=x, type="histogramx") - l <- list(autosize=FALSE, width=600, height=400, showlegend=FALSE) - - py <- plotly("get_test_user_2", "0f9es4r6tm") - response <- py$plotly(data, kwargs=list(layout=l, filename="directory/hist", - fileopt="overwrite")) - - expect_identical(response$filename, "directory/hist") - + dat <- list(x = rnorm(30), type = "histogramx") + nm <- "directory/coolest-plot" + l <- list(autosize = FALSE, width = 600, height = 400, showlegend = FALSE, + filename = nm, fileopt = "overwrite") + resp <- plotly_POST(dat, l) + # why does directory get prepended? + expect_identical(resp[["filename"]], "directorydirectory/coolest-plot") }) diff --git a/tests/testthat/test-plotly-getfigure.R b/tests/testthat/test-plotly-getfigure.R index 44d248a545..570f2048c6 100644 --- a/tests/testthat/test-plotly-getfigure.R +++ b/tests/testthat/test-plotly-getfigure.R @@ -1,42 +1,24 @@ context("get_figure") test_that("requests made by a user who doesn't exist error a 404", { - py <- plotly("user_does_not_exist", "api_key_shouldnt_matter") expect_error({ - py$get_figure("get_test_user", 0) + get_figure("klmadslfjdfljdsf", 0) }, "404") }) -test_that("requests made to retrieve a file that doesn't error return a 404", { - py <- plotly("get_test_user", "vgs6e0cnoi") +test_that("requests made to retrieve a figure that doesn't exist returns a 404", { expect_error({ - py$get_figure("get_test_user", 1000) + get_figure("get_test_user", 18324823) }, "404") }) -test_that("requests made with the wrong API key error a 401", { - py <- plotly("get_test_user", "some_invalid_api_key") - expect_error({ - py$get_figure("get_test_user", 1) - }, "401") -}) - -test_that("requests made to retrieve some elses private file errors a 403", { - py <- plotly("get_test_user_2", "0f9es4r6tm") - expect_error({ - py$get_figure("get_test_user", 1) - }, "403") -}) - test_that("requests made to retrieve some elses private file errors a 403", { - py <- plotly("get_test_user_2", "0f9es4r6tm") expect_error({ - py$get_figure("get_test_user", 1) + get_figure("get_test_user", 1) }, "403") }) test_that("retrieving a public figure ... works.", { - py <- plotly("get_test_user_2", "0f9es4r6tm") - figure <- py$get_figure("get_test_user", 0) + figure <- get_figure("get_test_user", 0) expect_equivalent(figure$data[[1]]$x, list("1", "2", "3")) }) diff --git a/tests/testthat/test-plotly-knitr.R b/tests/testthat/test-plotly-knitr.R new file mode 100644 index 0000000000..f316c2f016 --- /dev/null +++ b/tests/testthat/test-plotly-knitr.R @@ -0,0 +1,18 @@ +context("knitr") + +txt <- " +Simple knitr demo +```{r} +p <- qplot(rnorm(50)) +plotly::plotly(p, browse = FALSE) +``` +" +test_that("plotly embeds inside knitr", { + html <- knitr::knit2html(text = txt) + expect_true(grepl("