Skip to content

Commit 35e59c2

Browse files
committed
First commit
0 parents  commit 35e59c2

9 files changed

+899
-0
lines changed

.Rbuildignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
^.*\.Rproj$
2+
^\.Rproj\.user$
3+
^LICENSE\.md$

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.Rproj.user
2+
.Rhistory
3+
.RData
4+
.Ruserdata

DESCRIPTION

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Package: micerest
2+
Type: Package
3+
Title: Server Helper for Webmice RESTFUL API
4+
Version: 0.1.0
5+
Description: Webmice is an online service for running mice through a
6+
RESTFUL API. The micerest package bundles the R functions needed to
7+
define the end points.
8+
License: GPL (>= 3)
9+
Encoding: UTF-8
10+
LazyData: true
11+
Authors@R:
12+
person("Christine", "Staiger", , "[email protected]",
13+
role = c("aut", "cre"), comment = c(ORCID = "YOUR-ORCID-ID"))
14+
Depends:
15+
RestRserve,
16+
mice

LICENSE.md

+595
Large diffs are not rendered by default.

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exportPattern("^[[:alpha:]]+")

R/webmice.R

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
library(RestRserve)
2+
library(mice, warn.conflicts = FALSE)
3+
library(jsonlite)
4+
library(readr)
5+
library(digest)
6+
7+
#' Parameters
8+
#' Code location in Docker, set through bash variable
9+
base_folder <- Sys.getenv("WEBMICE_LOC")
10+
if(base_folder == "") {
11+
base_folder <- getwd()
12+
print('No base folder for webmice set (export WEBMICE_LOC="directory").')
13+
print(paste("Set to: ", base_folder))
14+
}
15+
16+
#' Imports
17+
source(file.path(base_folder, "webmice_handlers.R"))
18+
source(file.path(base_folder, "webmice_functions.R"))
19+
20+
#' Data upload location
21+
data_uploads = file.path(base_folder, "data_uploads")
22+
if(!file.exists(data_uploads)){
23+
dir.create(data_uploads)
24+
}
25+
26+
#' Application
27+
webmice = Application$new()
28+
29+
#' Endpoints
30+
webmice$add_post(
31+
path = "/data",
32+
FUN = function(request, response) {
33+
cnt <- request$get_file("csvfile") # 'csv' from the upload form field
34+
# parse CSV
35+
dt <- read_csv(cnt)
36+
hash <- md5_string(request$parameters_body$csvfile)
37+
tmp <- file.path(data_uploads, hash)
38+
write_csv(dt, tmp)
39+
40+
# set output body
41+
response$append_header("data_token", hash)
42+
}
43+
)
44+
45+
webmice$add_get(path = "/version", FUN = mice_version_handler)
46+
webmice$add_get(path = "/exampledata", FUN = example_data_handler)
47+
webmice$add_get(path = "/long", FUN = impute_longfmt_handler)
48+
webmice$add_get(path = "/fit", FUN = fit_handler)
49+
webmice$add_get(path = "/pool", FUN = pool_handler)
50+
51+
#' Swagger
52+
yaml_file = file.path(base_folder, "openapi.yaml")
53+
webmice$add_openapi(path = "/openapi.yaml", file_path = yaml_file)
54+
webmice$add_swagger_ui(path = "/doc", path_openapi = "/openapi.yaml", use_cdn = TRUE)
55+
56+
backend = BackendRserve$new()
57+
backend$start(webmice, http_port = 8080)

R/webmice_functions.R

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#' Data
2+
#' Fetches example data from mice, returns data as json
3+
example_data_to_json = function(name) {
4+
result = tryCatch({
5+
return(toJSON(get(name)))
6+
}, error = function(e) {
7+
err <- c()
8+
err$error <- "Error: data name not found"
9+
return(toJSON(err))
10+
})
11+
return(result)
12+
}
13+
14+
#' Reads a csv file
15+
read_file = function(path){
16+
tryCatch({
17+
data <- read.csv(path)
18+
return(data)
19+
}, error = function(e){
20+
return(NULL)
21+
})
22+
}
23+
24+
#' Creates a hash for a data file name
25+
md5_string = function(string) {
26+
return(digest(paste(Sys.time(), string), algo="md5", serialize=F))
27+
}
28+
29+
#' Convert JSON to R
30+
#' Takes a json string and returns it as R list
31+
#' Test:
32+
#' input <- list(data="nhanes", maxit=2, m=2, seed=1)
33+
json_to_parameters = function(json_payload){
34+
result = tryCatch({
35+
params = fromJSON(json_payload)
36+
return(params)
37+
}, error = function(e) {
38+
return(NULL)
39+
})
40+
}
41+
42+
#' Mice return functions
43+
#' Takes the result of the imputation and returns the long format of the data
44+
imp_result_long_fmt = function(imp){
45+
return(toJSON(complete(imp, "long")))
46+
}
47+
48+
imp_result_pred_matrix = function(imp){
49+
res <- c()
50+
res$error <- "not implemented"
51+
return(toJSON(res))
52+
}
53+
54+
#' Mice functions
55+
impute = function(data, maxit, m, seed) {
56+
imp <- list()
57+
imp$error <- ""
58+
result = tryCatch({
59+
imp <- mice(data, maxit=maxit, m=m, seed=seed)
60+
return(imp)
61+
}, error = function(e) {
62+
return("Failure: mice")
63+
})
64+
65+
if(result == "Failure: mice"){
66+
imp$error <- result
67+
return(imp)
68+
}
69+
}
70+
71+
#' Calls mice's imputation function with parameters provided in a list 'params'
72+
#' Expected: params$data, params$maxit, params$m, params$seed
73+
#' data: example data name, a hash from an uploaded file, or a csv filee
74+
call_mice = function(params){
75+
imp <- list()
76+
result = tryCatch({
77+
data <- get(params$data)
78+
}, error = function(e){
79+
return(-1)
80+
})
81+
82+
if(typeof(result) == "list") {
83+
print("DEBUG: Imputation on example data set")
84+
imp <- impute(data, maxit=params$maxit, m=params$m, seed=params$seed)
85+
return(imp)
86+
}
87+
if(typeof(params$data) == "character" && endsWith(params$data, ".csv")){
88+
print("DEBUG: Imputation on local csv file")
89+
df <- read_file(params$data)
90+
if(is.null(df)){
91+
imp$error <- "Failure: reading local csv file"
92+
return(imp)
93+
}
94+
imp <- impute(nhanes, maxit=params$maxit, m=params$m, seed=params$seed)
95+
return(imp)
96+
}
97+
if(typeof(params$data) == "character"){
98+
print("DEBUG: Imputation on uploaded file")
99+
df <- read_file(file.path(data_uploads, params$data))
100+
if(is.null(df)){
101+
imp$error <- "Failure: reading file, not an example dataset or file on server"
102+
return(imp)
103+
}
104+
imp <- impute(df, maxit=params$maxit, m=params$m, seed=params$seed)
105+
return(imp)
106+
}
107+
}
108+
109+
call_with = function(data, model, formula){
110+
fit <- c()
111+
if(model == "lm"){
112+
fit <- with(data, lm(as.formula(formula)))
113+
}
114+
if(model == "glm"){
115+
fit <- with(data, glm(as.formula(formula)))
116+
}
117+
fit$error <- "Model not known"
118+
return(toJSON(summary(fit), force=TRUE))
119+
}
120+
121+
call_pool = function(data){
122+
pool <- c()
123+
if(packageVersion("mice") < "3.16.4" ){
124+
pool$error <- "ERROR pool.table: need mice version 3.16.4 or higher"
125+
return(toJSON(pool, force=TRUE))
126+
}
127+
128+
if(typeof(data) == "list"){
129+
pool <- pool.table(data)
130+
} else {
131+
pool$error <- "Input data not of correct type (summary(fit))"
132+
}
133+
return(toJSON(pool, force=TRUE))
134+
}

R/webmice_handlers.R

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
pool_handler = function(.req, .res) {
2+
poolJson <- ''
3+
json_payload <- as.character(.req$parameters_query[["payload"]])
4+
if (length(json_payload) == 0L) {raise(HTTPError$bad_request())}
5+
params <- json_to_parameters(json_payload)
6+
if(is.null(params$data)) {poolJson <- "Error: no data"}
7+
if(poolJson == ''){
8+
print("DEBUG: Calling pool.table (requires 3.16.4)")
9+
poolJson <- call_pool(params$data)
10+
}
11+
.res$set_body(poolJson)
12+
.res$set_content_type("text/plain")
13+
}
14+
15+
fit_handler = function(.req, .res) {
16+
fitJson <- ''
17+
json_payload <- as.character(.req$parameters_query[["payload"]])
18+
# if answers are copied straight from the Swagger interface, there are too many backslashes
19+
# json_payload <- gsub('\\\\', '', input)
20+
if (length(json_payload) == 0L) {raise(HTTPError$bad_request())}
21+
params <- json_to_parameters(json_payload)
22+
if(is.null(params$data)) {fitJson <- "Error: no data"}
23+
if(is.null(params$model)) {fitJson <- "Error: no model"}
24+
if(is.null(params$formula)) {fitJson <- "Error: no formula"}
25+
26+
if(fitJson == ''){
27+
print("DEBUG: Calling with (fitting function)")
28+
fitJson <- call_with(params$data, params$model, params$formula)
29+
}
30+
.res$set_body(fitJson)
31+
.res$set_content_type("text/plain")
32+
}
33+
34+
impute_longfmt_handler = function(.req, .res) {
35+
json_payload <- as.character(.req$parameters_query[["payload"]])
36+
37+
if (length(json_payload) == 0L) {raise(HTTPError$bad_request())}
38+
# check if convertible to json
39+
params <- json_to_parameters(json_payload)
40+
41+
# impute function needs data, maxit, m, seed
42+
if(is.null(params$data)) {raise(HTTPError$not_acceptable())}
43+
if(is.null(params$maxit)) {raise(HTTPError$not_acceptable())}
44+
if(is.null(params$m)) {raise(HTTPError$not_acceptable())}
45+
if(is.null(params$seed)) {raise(HTTPError$not_acceptable())}
46+
47+
print("DEBUG: Calling mice")
48+
imp <- call_mice(params)
49+
50+
if(is.null(imp$error)){
51+
.res$set_body(imp_result_long_fmt(imp))
52+
} else{
53+
.res$set_body(toJSON(imp))
54+
}
55+
.res$set_content_type("text/plain")
56+
}
57+
58+
example_data_handler = function(.req, .res) {
59+
example_name <- as.character(.req$parameters_query[["name"]])
60+
.res$set_body(example_data_to_json(example_name))
61+
.res$set_content_type("text/plain")
62+
}
63+
64+
mice_version_handler = function(.req, .res) {
65+
version <- list()
66+
version$mice <- sessionInfo("mice")$otherPkgs$mice$Version
67+
.res$set_body(toJSON(version))
68+
.res$set_content_type("text/plain")
69+
}

micerest.Rproj

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Version: 1.0
2+
3+
RestoreWorkspace: Default
4+
SaveWorkspace: Default
5+
AlwaysSaveHistory: Default
6+
7+
EnableCodeIndexing: Yes
8+
UseSpacesForTab: Yes
9+
NumSpacesForTab: 2
10+
Encoding: UTF-8
11+
12+
RnwWeave: Sweave
13+
LaTeX: pdfLaTeX
14+
15+
AutoAppendNewline: Yes
16+
StripTrailingWhitespace: Yes
17+
18+
BuildType: Package
19+
PackageUseDevtools: Yes
20+
PackageInstallArgs: --no-multiarch --with-keep.source

0 commit comments

Comments
 (0)