Skip to content
This repository has been archived by the owner on Oct 28, 2019. It is now read-only.

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
andrie committed Dec 19, 2015
2 parents 8f74228 + 43e1453 commit 5f25624
Show file tree
Hide file tree
Showing 15 changed files with 418 additions and 246 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Title: Interface with Azure Machine Learning datasets and web services
Description: Functions and datasets to support Azure Machine Learning. This
allows you to interact with datasets, as well as publish and consume R functions
as API services.
Version: 0.2.6
Date: 2015-12-18
Version: 0.2.7
Date: 2015-12-19
Authors@R: c(
person("Raymond", "Laghaeian", role=c("aut", "cre"), email="[email protected]"),
person(family="Microsoft Corporation", role="cph"),
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ export(is.Endpoint)
export(is.Service)
export(is.Workspace)
export(publishWebService)
export(read.AzureML.config)
export(refresh)
export(services)
export(updateWebService)
export(upload.dataset)
export(workspace)
export(write.AzureML.config)
import(codetools)
importFrom(base64enc,base64encode)
importFrom(curl,curl)
Expand Down
79 changes: 79 additions & 0 deletions R/config.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
validate.AzureML.config <- function(config = getOption("AzureML.config"), stopOnError = FALSE){
# Stop if the config file is missing
if(!file.exists(config)) {
msg <- sprintf("config file is missing: '%s'", config)
if(stopOnError)
stop(msg, call. = FALSE)
else
return(simpleError(msg))
}

# Stop if the config is a directory, not a file
if(file.info(config)$isdir){
msg <- paste(
"The config argument should point to a file.",
sprintf(" You provided a directory (%s)",
normalizePath(config, winslash = "/", mustWork = FALSE)
), sep = "\n"
)
if(stopOnError)
stop(msg, call. = FALSE)
else
return(simpleError(msg))
}
TRUE
}

#' Reads settings from configuration file in JSON format.
#'
#' @inheritParams workspace
#'
#' @export
#' @seealso write.AzureML.config
#' @seealso workspace
read.AzureML.config <- function(config = getOption("AzureML.config")){
z <- tryCatch(fromJSON(file(config)),
error = function(e)e
)
# Error check the settings file for invalid JSON
if(inherits(z, "error")) {
msg <- sprintf("Your config file contains invalid json", config)
msg <- paste(msg, z$message, sep = "\n\n")
stop(msg, call. = FALSE)
}
z
}

#' Writes settings to configuration file.
#'
#' @inheritParams workspace
#' @param file either a character string naming a file or a connection open for writing. "" indicates output to the console.
#'
#' @rdname read.AzureML.config
#'
#' @export
#' @seealso write.AzureML.config
#' @seealso workspace
write.AzureML.config <- function(id = NULL, auth = NULL,
api_endpoint = NULL,
management_endpoint = NULL,
file = ""){
# Construct list
x <- list(
id = id,
authorization_token = auth,
api_endpoint = api_endpoint,
management_endpoint = management_endpoint
)
# Remove null values
conf <- list(
workspace = x[!sapply(x, is.null)]
)
# Convert to JSON
js <- jsonlite::toJSON(conf, pretty = TRUE)
if(!missing(file) && !is.null(file)) {
writeLines(js, con = file)
} else {
js
}
}
61 changes: 33 additions & 28 deletions R/internal.R
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,32 @@ urlconcat <- function(a,b)
get_datasets <- function(ws)
{
h = new_handle()
handle_setheaders(h, .list=ws$.headers)
r = curl(sprintf("%s/workspaces/%s/datasources", ws$.studioapi, ws$id), handle=h)
on.exit(close(r))
x = tryCatch(fromJSON(readLines(r, warn=FALSE)), error=invisible)
handle_setheaders(h, .list = ws$.headers)
uri <- sprintf("%s/workspaces/%s/datasources", ws$.studioapi, ws$id)
r <- try_fetch(uri = uri, handle = h, delay = 0.25, tries = 3)
if(inherits(r, "error")){
msg <- paste("No results returned from datasets(ws).",
"Please check your workspace credentials and api_endpoint are correct.")
stop(msg)
}
x <- fromJSON(rawToChar(r$content))
if(is.null(x) || is.na(x$Name[1])){
x = data.frame()
class(x) = c("Datasets", "data.frame")
return(x)
}
# Use strict variable name matching to look up data
d = x[,"DownloadLocation"]
x$DownloadLocation = paste(d[,"BaseUri"],
d[,"Location"],
d[,"AccessCredential"], sep="")
d = x[, "DownloadLocation"]
x$DownloadLocation = paste0(d[, "BaseUri"],
d[, "Location"],
d[, "AccessCredential"])
d = x[,"VisualizeEndPoint"]
x$VisualizeEndPoint = paste(d[,"BaseUri"],
d[,"AccessCredential"], sep="")
x$VisualizeEndPoint = paste0(d[, "BaseUri"],
d[, "AccessCredential"])
d = x[,"SchemaEndPoint"]
x$SchemaEndPoint = paste(d[,"BaseUri"],
d[,"Location"],
d[,"AccessCredential"], sep="")
x$SchemaEndPoint = paste0(d[, "BaseUri"],
d[, "Location"],
d[, "AccessCredential"])
class(x) = c("Datasets", "data.frame")
x
}
Expand Down Expand Up @@ -158,18 +163,18 @@ get_dataset <- function(x, h, quote = "\"", ...)
if(tolower(x$DataTypeId) == "zip") conn = "rb"
uri = curl(x$DownloadLocation, handle=h, open=conn)
on.exit(tryCatch(close(uri), error=invisible), add=TRUE)

# Existence of DataTypeId, DowloadLocation guaranteed by caller
switch(tolower(x$DataTypeId),
arff = read.arff(uri),
plaintext = paste(readLines(uri, warn=FALSE), collapse="\n"),
generictsvnoheader = read.table(uri, sep="\t", header=FALSE, quote, ...),
generictsv = read.table(uri, sep="\t", header=TRUE, quote, ...),
genericcsvnoheader = read.table(uri, sep=",", header=FALSE, quote, ...),
genericcsv = read.table(uri, sep=",", header=TRUE, quote, ...),
zip = readBin(uri, what="raw", n=x$Size, ...),
stop("unsupported data type: '",x$DataTypeId,"'")
)
# Existence of DataTypeId, DowloadLocation guaranteed by caller
switch(tolower(x$DataTypeId),
arff = read.arff(uri),
plaintext = paste(readLines(uri, warn=FALSE), collapse="\n"),
generictsvnoheader = read.table(uri, sep="\t", header=FALSE, quote, ...),
generictsv = read.table(uri, sep="\t", header=TRUE, quote, ...),
genericcsvnoheader = read.table(uri, sep=",", header=FALSE, quote, ...),
genericcsv = read.table(uri, sep=",", header=TRUE, quote, ...),
zip = readBin(uri, what="raw", n=x$Size, ...),
stop("unsupported data type: '",x$DataTypeId,"'")
)
}


Expand Down Expand Up @@ -205,7 +210,7 @@ packageEnv <- function(exportenv, packages=NULL, version="3.1.0")
setwd(d)
# save export environment to an RData file
save(exportenv, file="env.RData")

# Package up dependencies
if(!is.null(packages))
{
Expand All @@ -214,9 +219,9 @@ packageEnv <- function(exportenv, packages=NULL, version="3.1.0")
p = paste(d,"packages",sep="/")
tryCatch(dir.create(p), warning=function(e) stop(e))
tryCatch(makeRepo(pkgDep(packages, repos=re, suggests=FALSE), path=p, re, type="win.binary", Rversion=version),
error=function(e) stop(e))
error=function(e) stop(e))
}

z = try({
zip(zipfile="export.zip", files=dir(), flags = "-r9Xq")
})
Expand Down
19 changes: 0 additions & 19 deletions R/makeConfig.R

This file was deleted.

2 changes: 2 additions & 0 deletions R/options.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
AzureML.config.default <- "~/.azureml/settings.json"
options(AzureML.config = AzureML.config.default)
81 changes: 50 additions & 31 deletions R/workspace.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@
default_api <- function(api_endpoint = "https://studioapi.azureml.net"){
defaults <- list(

"https://studio.azureml.net" = list(
api_endpoint = "https://studioapi.azureml.net",
management_endpoint = "https://management.azureml.net",
studioapi = "https://studioapi.azureml.net/api"
),
"https://studioapi.azureml.net" = list(
api_endpoint = "https://studioapi.azureml.net",
management_endpoint = "https://management.azureml.net",
studioapi = "https://studioapi.azureml.net/api"

), "https://studioapi.azureml-int.net" = list(

),
"https://studioapi.azureml-int.net" = list(
api_endpoint = "https://studio.azureml-int.net",
management_endpoint = "https://management.azureml-int.net",
studioapi = "https://studioapi.azureml-int.net/api"

)
)

Expand All @@ -55,7 +58,7 @@ default_api <- function(api_endpoint = "https://studioapi.azureml.net"){
#' @param auth Optional authorization token from ML studio -> settings -> AUTHORIZATION TOKENS
#' @param api_endpoint Optional AzureML API web service URI. Defaults to \url{https://studio.azureml.net} if not provided and not specified in config. See note.
#' @param management_endpoint Optional AzureML management web service URI. Defaults to \url{https://management.azureml.net} if not provided and not specified in config. See note.
#' @param config Optional settings file containing id and authorization info. Used if any of the other arguments are missing. The default config file is \code{~/.azureml/settings.json}.
#' @param config Optional settings file containing id and authorization info. Used if any of the other arguments are missing. The default config file is \code{~/.azureml/settings.json}, but you can change this location by setting \code{options(AzureML.config = "newlocation")}
#'
#' @note If any of the \code{id}, \code{auth}, \code{api_endpoint} or \code{management_endpoint} arguments are missing, the function attempts to read values from the \code{config} file with JSON format: \preformatted{
#' {"workspace":{
Expand All @@ -82,54 +85,70 @@ default_api <- function(api_endpoint = "https://studioapi.azureml.net"){
#' @seealso \code{\link{datasets}}, \code{\link{experiments}}, \code{\link{refresh}},
#' \code{\link{services}}, \code{\link{consume}}, \code{\link{publishWebService}}
workspace <- function(id, auth, api_endpoint, management_endpoint,
config="~/.azureml/settings.json")
config = getOption("AzureML.config"))
{
if(missing(id) || missing(auth) || missing(api_endpoint) || missing(management_endpoint))
{
if(!file.exists(config)) stop(sprintf("config file is missing: '%s'", config))
settings = tryCatch(fromJSON(file(config)),
error = function(e)e
)
if(inherits(settings, "error")) {
msg <- sprintf("Your config file contains invalid json", config)
msg <- paste(msg, settings$message, sep = "\n\n")
stop(msg, call. = FALSE)
}


# If workspace_id or auth are missing, read from config. Stop if unavailable.
if(missing(id) || missing(auth)) {
x <- validate.AzureML.config(config, stopOnError = TRUE)
if(inherits(x, "error")) stop(x$message)
settings <- read.AzureML.config(config)

if(missing(id)){
id <- settings[["workspace"]][["id"]]
}
if(missing(auth)){
auth <- settings[["workspace"]][["authorization_token"]]
}
if(missing(api_endpoint)){
api_endpoint <- settings[["workspace"]][["api_endpoint"]]
}
if(missing(management_endpoint)){
management_endpoint <- settings[["workspace"]][["management_endpoint"]]
}

# If workspace_id or auth are missing, read from config, if available.
if(missing(api_endpoint) || missing(management_endpoint)){
x <- validate.AzureML.config(config, stopOnError = FALSE)
if(!inherits(x, "error")){
settings <- read.AzureML.config(config)

if(missing(api_endpoint)){
api_endpoint <- settings[["workspace"]][["api_endpoint"]]
}
if(missing(management_endpoint)){
management_endpoint <- settings[["workspace"]][["management_endpoint"]]
}
}
}
default_api <- if(is.null(api_endpoint)) {
default_api()

# Assign a default api_endpoint if this was not provided
default_api <- if(missing(api_endpoint) || is.null(api_endpoint)) {
default_api()
} else {
default_api(api_endpoint)
}
if(is.null(api_endpoint)) api_endpoint <- default_api[["api_endpoint"]]
if(is.null(management_endpoint)) management_endpoint <- default_api[["management_endpoint"]]
if(missing(api_endpoint) || is.null(api_endpoint)){
api_endpoint <- default_api[["api_endpoint"]]
}

# test to see if api_endpoint is a valid url
# Assign a default management_endpoint if this was not provided
if(missing(management_endpoint) || is.null(management_endpoint)){
management_endpoint <- default_api[["management_endpoint"]]
}

# Test to see if api_endpoint is a valid url
resp <- tryCatch(
suppressWarnings(curl::curl_fetch_memory(api_endpoint)),
error = function(e)e
)
if(inherits(resp, "error")) stop("Invalid api_endpoint: ", api_endpoint)

# test to see if api_endpoint is a valid url
# Test to see if management_endpoint is a valid url
resp <- tryCatch(
suppressWarnings(curl::curl_fetch_memory(management_endpoint)),
error = function(e)e
)
if(inherits(resp, "error")) stop("Invalid management_endpoint: ", management_endpoint)

# It seems all checks passed. Now construct the Workspace object

e <- new.env()
class(e) <- "Workspace"
e$id <- id
Expand All @@ -143,9 +162,9 @@ workspace <- function(id, auth, api_endpoint, management_endpoint,
`x-ms-client-session-id` = "DefaultSession",
`x-ms-metaanalytics-authorizationtoken` = auth
)
delayedAssign("experiments", get_experiments(e), assign.env=e)
delayedAssign("datasets", get_datasets(e), assign.env=e)
delayedAssign("services", services(e), assign.env=e)
delayedAssign("experiments", get_experiments(e), assign.env = e)
delayedAssign("datasets", get_datasets(e), assign.env = e)
delayedAssign("services", services(e), assign.env = e)
e
}

Expand Down
11 changes: 11 additions & 0 deletions R/zzz_test_helpers.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This function is used in unit testing to skip tests if the config file is missing
#
skip_if_missing_config <- function(f){
if(!file.exists(f)) {
msg <- paste("To run tests, add a file ~/.azureml/settings.json containing AzureML keys.",
"See ?workspace for help",
sep = "\n")
message(msg)
skip("settings.json file is missing")
}
}
Loading

0 comments on commit 5f25624

Please sign in to comment.