How to click on element with Chrome DevTools Protocol? - r

I'm using chromote R package and I'm testing it with shiny application. I'm trying to click on the icon that should duplicate few select elements. But all I have is tooltip when I take a screenshot and if I open the browser it freezes the R process.
Here is my code:
#' Run shiny in background - based on shinytest source code
#' #export
shiny.bg <- function(path, loadTimeout = 10000, shinyOptions = list()) {
tempfile_format <- tempfile("%s-", fileext = ".log")
p <- callr::r_bg(function(path, shinyOptions) {
do.call(shiny::runApp, c(path, shinyOptions))
},
args = list(
path = normalizePath(path),
shinyOptions = shinyOptions
),
stdout = sprintf(tempfile_format, "shiny-stdout"),
stderr = sprintf(tempfile_format, "shiny-stderr"),
supervise = TRUE
)
if (! p$is_alive()) {
abort(paste0(
"Failed to start shiny. Error: ",
strwrap(readLines(p$get_error_file()))
))
}
## Try to read out the port. Try 5 times/sec, until timeout.
max_i <- loadTimeout / 1000 * 5
for (i in seq_len(max_i)) {
err_lines <- readLines(p$get_error_file())
if (!p$is_alive()) {
abort(paste0(
"Error starting application:\n", paste(err_lines, collapse = "\n")
))
}
if (any(grepl("Listening on http", err_lines))) break
Sys.sleep(0.2)
}
if (i == max_i) {
abort(paste0(
"Cannot find shiny port number. Error:\n", paste(err_lines, collapse = "\n")
))
}
line <- err_lines[grepl("Listening on http", err_lines)]
m <- rematch::re_match(text = line, "https?://(?<host>[^:]+):(?<port>[0-9]+)")
url <- sub(".*(https?://.*)", "\\1", line)
list(
process = p,
url = url
)
}
#' Run shiny application and Chromeote instance
chromote.shiny <- function() {
chr <- chromote::ChromoteSession$new()
app <- shiny.bg('.')
chr$Page$navigate(app$url)
chr$Page$loadEventFired()
chr$screenshot()
list(
chr = chr,
app = app
)
}
#' kill browser and R shiny process
cleanUp <- function(obj) {
obj$chr$Browser$close()
obj$app$process$kill()
}
#' click on the element
chromote.click <- function(chromote, selector) {
doc = chromote$DOM$getDocument()
node = chromote$DOM$querySelector(doc$root$nodeId, selector)
box <- chromote$DOM$getBoxModel(node$nodeId)
left <- box$model$content[[1]]
top <- box$model$content[[2]]
x <- left + (box$model$width / 2)
y <- top + (box$model$height / 2)
chromote$Input$dispatchMouseEvent(type = "mousePressed", x = x, y = y, button="left")
chromote$Input$dispatchMouseEvent(type = "mouseReleased", x = x, y = y, button="left")
}
tmp <- chromote.shiny()
chromote.click(tmp$chr, ".clone-pair")
tmp$chr$screenshot()
I have no idea how I can debug this and there are not much information how to make a click, I've found dispatchMouseEvent in issue in GitHub repo for chromote.
Links to repo https://github.com/rstudio/chromote
The reason why I want to use chromote is I want to create unit/integration test for my application and shinytest is way outdated it use phantomJS that was abandoned years ago (so you need to use very old JavaScript because otherwise pantomJS will throw error and test will fail) and RSelenium is also not maintained anymore.

Had the same issue..
I found this library that uses chromote but has a number of functions (GetElement, Click) from RSelenium.
install.packages("remotes")
remotes::install_github("rundel/hayalbaz")

Related

Using callr to display an (estimated) progress bar without stopping the script

I would like to run a very simple script concurrently or asynchronously, displaying an estimated progress bar.
This works well enough when using system2() like this:
path <- '../Desktop/.../My_Skript_Dir/'
system2(command = "cmd.exe",
input = paste('"./R-4.2.1/bin/Rscript.exe"',
paste0(path, '/Progress_Bar.R')), wait = FALSE)
If possible I would like to avoid using system2 though and I recently found out that callr might do the trick. It almost works, using the function from the "Progress_Bar" script:
estimated_progress <- function(df = NULL, add_time = FALSE){
require(tcltk)
require(callr)
pred <- round(nrow(df)*0.6) # prediction
callr::r_bg(func = function(pred){ # open background r session
pb1 <- tcltk::tkProgressBar(title='PB', label='PB', min=0, max=pred, initial=0)
for (index in seq(pred)){
tcltk::setTkProgressBar(pb=pb1, value=index)
Sys.sleep(1)
}
}, args = list(pred))
}
df <- data.frame(matrix(nrow = 200, ncol = 3)) # dummy data
estimated_progress(df = df, add_time = FALSE)
When I do this, the progress bar opens in a new window as expected.
It keeps going for the next 1-3 function(s) (for example invisible(pbapply::pblapply(1:200000, function(x) x**3)) ) but any more than that and estimated_progress() abborts.
What am I missing here? I am sure it's quite obvious and I have read that callr can work asynchronously (look here) but I can't make it work.

Error when trying to download species models in R with ebirdst::ebirdst_download()

I've requested an access key and set it. Sys.getenv("EBIRDST_KEY") returns the correct key. ebirdst_download(species = "Sharp-tailed Grouse") returns an error
Error in ebirdst_download(species = "Sharp-tailed Grouse") : Cannot
access Status and Trends data URL. Ensure that you have a working
internet connection and a valid API key for the Status and Trends
data.
My internet is working. Looking at the source code, I believe the error is generated because the function reads a url, essentially read_json(stringr::str_glue("{api_url}list-obj/{species}?key={key}")) which for some reason is null.
https://rdrr.io/github/CornellLabofOrnithology/ebirdst/src/R/ebirdst-loading.R?fbclid=IwAR1JYbCoD_VGwtZ0e1tz7yEPIR1buwN3GUyraZqokeS8rFTox4g3ceWRnns
But I can get it to work if I use the following lines of the source code. I'm not sure why the ebirdst function is failing.
species<-"Sharp-tailed Grouse"
path = rappdirs::user_data_dir("ebirdst")
species <- get_species(species)
which_run <- which(ebirdst::ebirdst_runs$species_code == species)
run <- ebirdst::ebirdst_runs$run_name[which_run]
key<-Sys.getenv("EBIRDST_KEY")
api_url <- "https://st-download.ebird.org/v1/"
list_obj_url <- stringr::str_glue("{api_url}list-obj/{species}?key={key}")
files <-jsonlite::read_json(list_obj_url, simplifyVector = TRUE)
files <- data.frame(file = files)
files <- files[!stringr::str_detect(files$file, "\\.db$"), , drop = FALSE]
files$src_path <- stringr::str_glue("{api_url}fetch?objKey={files$file}",
"&key={key}")
files$dest_path <- file.path(path, files$file)
files$exists <- file.exists(files$dest_path)
dirs <- unique(dirname(files$dest_path))
for (d in dirs) {
dir.create(d, showWarnings = FALSE, recursive = TRUE)
}
old_timeout <- getOption("timeout")
options(timeout = max(3000, old_timeout))
for (i in seq_len(nrow(files))) {
dl_response <- utils::download.file(files$src_path[i],
files$dest_path[i],
mode = "wb")
if (dl_response != 0) {
stop("Error downloading file: ", files$file[i])
}
}

Why does the font size change after I click the save button?

first post and I hope I'm doing everything correctly.
I'm using RStudio: Version 1.1.423; R-Version: 3.4.2, OS: Windows 10
Short problem summary: I start a gwidgets based GUI and the text shows in the desired font size, but when I click the save button, it suddenly changes to a smaller font.
Context: I'm currently doing a systematic literature review and I use the Metagear package by mjlajeunesse (https://github.com/cran/metagear) to screen the abstracts and decide if I shuold include them. Here's a picture of the original GUI:
Original Abstract Screener GUI
Short description of functionality: I just click on one of the buttons (Yes, No, Maybe) and the abstract screener moves on to the next abstract in the file. I then click save to save my progress.
Changes I made to the code: However, I added some additional fields so I had to change that behaviour, so that it doesn't immediately jump to the next entry as soon as I click one of the buttons. So I merged that part of the function with the save function.
Now I get the following behaviour: When I first open the abstract screener the fontsize is as desired as seen in this picture:
Adapted Abstract Screener before clicking Save & Next
As soon as I click Save & Next it shows me the next abstract (as desired), but suddenly the font changes to a much smaller one, making it rather unpleasant to read, because it's so small, as seen here
Adapted Abstract Screener after clicking Save & Next
Here's the code that I use/adapted to override the original functions of the metagear package. I iteratively adapted the code so I only included the functions of the metagear package that I needed to make things work.
Here's the code I'm using(I included the code I use to execute the different functions at the bottom, including some sample data frame, instead of my CSV file):
# Load libraries
library(metagear)
library(EBImage)
library(cairoDevice)
library(gWidgets)
library(gWidgetsRGtk2)
library(gWidgets2)
library(gWidgets2RGtk2)
library(RGtk2)
library(utils)
##############################
# dirty adaptations for the METAGEAR package start here
##############################
##########################
# better_effort_utils (important for effort_save)
##########################
effort_save <- function (aDataFrame,
column_name = "REVIEWERS",
directory = getwd(),
quiet = FALSE) {
split_List <- split(aDataFrame, f = aDataFrame[, column_name])
lapply(split_List, function(x, column_name) {
fileName <- paste0("effort_", x[1, column_name], ".csv")
if(file.exists(file.path(directory, fileName))) {
while (file.exists(file.path(directory, fileName)) != FALSE) fileName <- renameFile(fileName)
if(!quiet) message(paste0("File already existed, renamed: ", fileName))
}
fileName <- file.path(directory, fileName)
write.csv(x, file = fileName, na = "NA", row.names = FALSE)
}, column_name = column_name)
if(!quiet) message(paste0(length(split_List), " files saved in: ", directory))
}
######################
# better_effort_initialize
######################
better_effort_initialize <- function(aDataFrame,
study_ID = TRUE,
unscreenedValue = "not vetted",
unscreenedCommentValue = "Please paste text here",
studyTypeValue = "not vetted",
studyTypeCommentValue = "Please paste text here",
dual = FALSE,
front = TRUE) {
# adding new columns start, add the new columns as part of the "if" statement as well as the "else" statement
if(dual == TRUE) {
new_cols <- data.frame(REVIEWERS_A = NA,
INCLUDE_A = unscreenedValue,
REVIEWERS_B = NA,
INCLUDE_B = unscreenedValue,
INCLUDE_COMMENT = unscreenedCommentValue,
TYPE_OF_STUDY = studyTypeValue,
TYPE_OF_STUDY_COMMENT = studyTypeCommentValue) # There will have to be a version A and version B, just like with the reviewers
}
else new_cols <- data.frame(REVIEWERS = NA,
INCLUDE = unscreenedValue,
INCLUDE_COMMENT = unscreenedCommentValue,
TYPE_OF_STUDY = studyTypeValue,
TYPE_OF_STUDY_COMMENT = studyTypeCommentValue)
if(study_ID == TRUE) new_cols <- cbind(data.frame(STUDY_ID = 1:nrow(aDataFrame)),
new_cols)
if(front == TRUE) newDataFrame <- cbind(new_cols, aDataFrame)
else newDataFrame <- cbind(aDataFrame, new_cols)
return(newDataFrame)
}
#################################
# better_effort_distribute
#################################
better_effort_distribute <- function (aDataFrame = NULL,
dual = FALSE,
reviewers = theTeam,
column_name = "REVIEWERS",
effort = NULL,
initialize = FALSE,
save_split = FALSE,
directory = getwd() ) {
if(is.null(reviewers)) .metagearPROBLEM("error",
"no reviewers were assigned")
if(is.null(aDataFrame)) .metagearPROBLEM("error",
"a dataframe with refs was not specified")
number_REFS <- nrow(aDataFrame)
number_reviewers <- length(reviewers)
# add REVIEWER, STUDY_ID, and INCLUDE columns to reference library
if(initialize == TRUE) aDataFrame <- better_effort_initialize(aDataFrame,
dual = dual)
if(dual == TRUE) {
if(!is.null(effort)) .metagearPROBLEM("error",
"can only assign dual effort evenly among reviewers")
if(number_reviewers %% 2 == 1) .metagearPROBLEM("error",
"can only assign dual effort with an even number of reviewers")
reviewers_A <- reviewers[1:number_reviewers %% 2 == 1]
theEffort_A <- gl(length(reviewers_A),
ceiling(number_REFS / length(reviewers_A)),
number_REFS,
labels = reviewers_A)
reviewers_B <- reviewers[1:number_reviewers %% 2 == 0]
theEffort_B <- gl(length(reviewers_B),
ceiling(number_REFS / length(reviewers_B)),
number_REFS,
labels = reviewers_B)
dualEffort <- data.frame(A = theEffort_A, B = theEffort_B)
theEffort <- dualEffort[sample(nrow(dualEffort),
nrow(dualEffort), replace = FALSE), ]
aDataFrame["REVIEWERS_A"] <- theEffort["A"]
aDataFrame["REVIEWERS_B"] <- theEffort["B"]
}
else {
# generate reviewers tasks evenly or via custom 'effort'
if(is.vector(effort) && length(unique(effort)) != 1) {
if(sum(effort) != 100) .metagearPROBLEM("error",
"Effort does not sum to 100.")
theEffort <- rep(reviewers, round((number_REFS * (effort / 100))))
} else {
theEffort <- gl(number_REFS,
ceiling(number_REFS / number_reviewers),
number_REFS,
labels = reviewers)
}
# randomly populate REVIEWERS column with tasks
aDataFrame[, column_name] <- sample(theEffort,
length(theEffort),
replace = FALSE)
}
# splits reference library into seperate reviewer csv files and
# hides teams if dual reviewing
if(save_split == TRUE) {
if(dual == TRUE) {
removeVars <- names(aDataFrame) %in% c("REVIEWERS_B", "INCLUDE_B")
effort_save(aDataFrame[!removeVars],
column_name = "REVIEWERS_A", directory)
removeVars <- names(aDataFrame) %in% c("REVIEWERS_A", "INCLUDE_A")
effort_save(aDataFrame[!removeVars],
column_name = "REVIEWERS_B", directory)
}
else effort_save(aDataFrame, column_name, directory)
}
return(aDataFrame)
}
#######################################
# better_abstract_screener
#######################################
#' #param file The file name and location of a .csv file containing the
#' abstracts and titles. The .csv file should have been initialized with
#' \code{effort_initialize} and populated with screeners (reviewers) using
#' \code{better_effort_distribute}.
#' #param aReviewer The name (a string) of the reviewer to screen abstracts.
#' It is used when there are multiple reviewers assigned to screen abstracts.
#' The default column label is "REVIEWERS" as initialized with
#' \code{better_effort_distribute}.
#' #param reviewerColumnName The name of the column heading in the .csv file
#' that contains the reviewer names that will screen abstracts. The default
#' column label is "REVIEWERS".
#' #param unscreenedColumnName The name of the column heading in the .csv file
#' that contains the screening outcomes (i.e. vetting outcomes by a reviewer).
#' Unscreened references are by default labeled as "not vetted". The
#' reviewer then can code to "YES" (is a relevant study), "NO" is not relevant
#' and should be excluded, or "MAYBE" if the title/abstract is missing or
#' does not contains enough information to fully assess inclusivity.
#' The default label of this column is "INCLUDE".
#' #param unscreenedValue Changes the default coding (a string) of "not vetted"
#' that designates whether an abstract remains to be screened or vetted.
#' #param abstractColumnName The name of the column heading in the .csv file
#' that contains the abstracts. The default label of this column is
#' "ABSTRACT".
#' #param titleColumnName The name of the column heading in the .csv file
#' that contains the titles. The default label of this column is "TITLE".
#' #param browserSearch Change the url for the browser title search; the
#' default is Google.
#' #param protect When \code{"TRUE"}, prevents the title and abstract from being
#' clicked, selected or edited.
#' #param fontSize Change the font size of the title and abstract text.
#' #param windowWidth Change the default width of the GUI window.
#' #param windowHeight Change the default height of the GUI window.
#' #param buttonSize Change the default size of the "YES" and "NO" buttons.
#'
#'
#' #return NULL
#'
#' #examples \dontrun{
#'
#' data(example_references_metagear)
#' better_effort_distribute(example_references_metagear,
#' initialize = TRUE, reviewers = "marc", save_split = TRUE)
#' abstract_screener("effort_marc.csv", aReviewer = "marc")
#'}
#'
#' #note \strong{Installation and troubleshooting}\cr\cr Upon first use,
#' \code{abstract_screener} will download the gWidgets package
#' and associated toolkits needed to build GUI interfaces. A small window will
#' also prompt you to download GTK+ asking "Need GTK+ ?". From the listed
#' options answer: "Install GTK+" and click 'OK'. Once installed these will
#' not be downloaded again. Sometimes there is an issue with the installation
#' of GTK+, see \url{http://www.learnanalytics.in/blog/?p=31} for advice based
#' on the \code{Rattle} R Package (both \code{Rattle} and \code{metagear} use
#' the same GUI dependencies). \cr\cr \strong{How to use the screener}
#' \cr\cr The GUI itself will appear as a single window with the first
#' title/abstract listed in the .csv file. If abstracts have already been
#' screened/coded, it will begin at the nearest reference labeled as
#' "not vetted". The SEARCH WEB button opens the default browser and
#' searches Google with the title of the reference. The YES, MAYBE, NO
#' buttons, which also have shortcuts ALT-Y and ALT-N, are used to code the
#' inclusion/exclusion of the reference. Once clicked/coded the next
#' reference is loaded. The SAVE button is used to save the coding progress
#' of screening tasks. It will save coding progress directly to the
#' loaded .csv file. \strong{Closing the GUI and not saving will result in
#' the loss of screening efforts relative to last save.} \cr\cr There is
#' also an ISSUE FIXES menu bar with quick corrections to screening errors.
#' These include ISSUE FIXES: REFRESH TITLE AND ABSTRACT TEXT which reloads
#' the text of the current abstract in case portions were deleted when
#' copying and pasting sections (this can be avoided if
#' \code{protect = TRUE} is enabled), ISSUE FIXES: STATUS OF CURRENT ABSTRACT
#' which provides information on whether or not the abstract was previously
#' screened, and ISSUE FIXES: RETURN TO PREVIOUS ABSTRACT that
#' backtracks to the previous abstract if a selection error occurred (note a
#' warning will appear of there is a change to its inclusion/exclusion
#' coding).
#'
#' #import gWidgets
#' #import gWidgetsRGtk2
#' #importFrom utils browseURL read.csv write.csv
#' #export abstract_screener
############################
better_abstract_screener <- function(file = file.choose(),
aReviewer = NULL,
reviewerColumnName = "REVIEWERS",
unscreenedColumnName = "INCLUDE",
unscreenedValue = "not vetted",
unscreenedCommentColumnName = "INCLUDE_COMMENT",
unscreenedCommentValue = "",
studyTypeColumnName = "TYPE_OF_STUDY",
studyTypeValue = "not vetted",
studyTypeCommentColumnName = "TYPE_OF_STUDY_COMMENT",
studyTypeCommentValue = "",
abstractColumnName = "Abstract",
titleColumnName = "Title",
browserSearch = "https://scholar.google.de/scholar?hl=de&as_sdt=0%2C5&q=",
protect = FALSE,
fontSize = 13,
windowWidth = 700,
windowHeight = 400,
buttonSize = 30) {
# get file with abstract
aDataFrame <- read.csv(file, header = TRUE)
# subset abstracts based on the current screener (aka 'reviewer')
subData <- subset(aDataFrame, aDataFrame[reviewerColumnName] == aReviewer)
subData <- data.frame(lapply(subData, as.character), stringsAsFactors = FALSE)
# check if all abstracts have been already vetted
if(unscreenedValue %in% subData[, unscreenedColumnName] ) {
# start screener at first unvetted abstract
currentItem <- max.col(t(subData[unscreenedColumnName] == unscreenedValue),
"first")
} else {
.metagearPROBLEM("error",
paste("all abstracts have already been screened,
no more abstracts coded as:", unscreenedValue))
}
options("guiToolkit" = "RGtk2")
# used to update the reference list
theAnswer <- function(theValue, ...) {
updateAll(theValue, ...)
}
theAnswer_INCLUDE_COMMENT <- function(theValue_INCLUDE_COMMENT, ...) {
updateAll_INCLUDE_COMMENT(theValue_INCLUDE_COMMENT, ...)
}
theAnswer_ToS <- function(theValue_ToS, ...) {
updateAll_ToS(theValue_ToS, ...)
}
theAnswer_ToS_COMMENT <- function(theValue_ToS_COMMENT, ...) {
updateAll_ToS_COMMENT(theValue_ToS_COMMENT, ...)
}
# helper function used to update and keep track of screened abstracts
updateAll <- function(theValue, ...) {
if(currentItem <= nrow(subData)) {
subData[[currentItem, unscreenedColumnName]] <<- theValue
}
}
updateAll_INCLUDE_COMMENT <- function(theValue_INCLUDE_COMMENT, ...) { ################### EXPERIMENTAL
if(currentItem <= nrow(subData)) {
subData[[currentItem, unscreenedCommentColumnName]] <<- theValue_INCLUDE_COMMENT
}
}
updateAll_ToS <- function(theValue_ToS, ...) {
if(currentItem <= nrow(subData)) {
subData[[currentItem, studyTypeColumnName]] <<- theValue_ToS
}
}
updateAll_ToS_COMMENT <- function(theValue_ToS_COMMENT, ...) {
if(currentItem <= nrow(subData)) {
subData[[currentItem, studyTypeCommentColumnName]] <<- theValue_ToS_COMMENT
}
}
####################
# START of SCREENER GUI
####################
win <- gwindow("metagear: Abstract Screener", visible = TRUE)
paned <- ggroup(container = win, horizontal = FALSE)
# Frame(s) for title, abstract and websearch button
#####
#beginnig frame_TITLE
frame_TITLE <- gframe("Title", container = paned, horizontal = TRUE)
text_TITLE <- gtext(subData[currentItem, titleColumnName],
container = frame_TITLE,
expand = TRUE,
font.attr = list(style = "normal", size = fontSize))
size(text_TITLE) <- c(windowWidth, 50)
if(protect == TRUE) enabled(text_TITLE) <- FALSE
addSpace(frame_TITLE, 2)
aButton_webSearch <- gbutton("Search\n Web",
container = frame_TITLE,
handler = function(h, ...)
browseURL(paste0(browserSearch,
subData[currentItem, titleColumnName])))
size(aButton_webSearch) <- c(50, 40)
addSpace(frame_TITLE, 5)
# end of frame_TITLE
# beginning frame_Abstract
frame_ABSTRACT <- gframe("Abstract", container = paned, horizontal = FALSE)
text_ABSTRACT <- gtext(subData[currentItem, abstractColumnName],
container = frame_ABSTRACT,
expand = TRUE,
font.attr = list(style = "normal", size = fontSize)
)
size(text_ABSTRACT) <- c(windowWidth + 50, 300)
if(protect == TRUE) enabled(text_ABSTRACT) <- FALSE
# end of frame_ABSTRACT
# beginning: Type of Study (ToS), buttons and comment
frame_TYPE_OF_STUDY <- gframe("Type of Study", container = paned, horizontal = TRUE)
addSpace(frame_TYPE_OF_STUDY, 20)
radioButtonNames_ToS <- c("not vetted", "empirical", "conceptual", "review")
radioButtons_ToS <- gradio(radioButtonNames_ToS, selected = which(radioButtonNames_ToS == subData[currentItem, studyTypeColumnName]), horizontal = FALSE, container = frame_TYPE_OF_STUDY,
handler = function(h,...){
svalue(h) <- subData[currentItem, studyTypeColumnName]
theAnswer_ToS(theValue_ToS = radioButtonNames_ToS[svalue(radioButtons_ToS, index = TRUE)], ...) # EXPERIMENTAL: Need to find a way t make the 3 a dynamic option that shows current selection (Current solution seems to be working)
}
)
frame_TYPE_OF_STUDY_COMMENT <- gframe("Please copy the text snippet upon which you base your decision",
container = frame_TYPE_OF_STUDY, horizontal = TRUE)
text_TYPE_OF_STUDY_COMMENT <- gtext(subData[currentItem, studyTypeCommentColumnName],
container = frame_TYPE_OF_STUDY_COMMENT,
#container = frame_TYPE_OF_STUDY, ### change to this frame and disable frame_TYPE_OF_STUDY_COMMENT, if you want the text field width to be expandable
expand = TRUE,
font.attr = list(style = "normal", size = fontSize),
handler = function(h, ...) {
theAnswer_ToS_COMMENT(theValue_ToS_COMMENT = svalue(text_TYPE_OF_STUDY_COMMENT))
}
)
size(text_TYPE_OF_STUDY_COMMENT) <- c(windowWidth, 70)
# end: Type of Study (ToS), buttons and comment
# beginning: Include/Exclude (YES/NO), buttons and comment
frame_INCLUDE <- gframe("Should the Abstract be included?", container = paned, horizontal = TRUE)
addSpace(frame_INCLUDE, 20)
radioButtonNames_INCLUDE <- c("not vetted", "YES", "NO", "MAYBE")
radioButtons_INCLUDE <- gradio(radioButtonNames_INCLUDE, selected = which(radioButtonNames_INCLUDE == subData[currentItem, unscreenedColumnName]),
horizontal = FALSE, container = frame_INCLUDE,
handler = function(h,...){
svalue(h) <- subData[currentItem, unscreenedColumnName]
theAnswer(theValue = radioButtonNames_INCLUDE[svalue(radioButtons_INCLUDE, index = TRUE)], ...)
}
)
frame_INCLUDE_COMMENT <- gframe("Please copy the text snippet upon which you base your decision", container = frame_INCLUDE,
horizontal = TRUE)
text_INCLUDE_COMMENT <- gtext(subData[currentItem, unscreenedCommentColumnName],
container = frame_INCLUDE_COMMENT,
# container = frame_INCLUDE, ### change to this frame and disable frame_INCLUDE_COMMENT, if you want the text field width to be expandable
expand = TRUE,
font.attr = list(style = "normal", size = fontSize),
handler = function(h, ...) {
theAnswer_INCLUDE_COMMENT(theValue_INCLUDE_COMMENT = svalue(text_INCLUDE_COMMENT))
}
)
size(text_INCLUDE_COMMENT) <- c(windowWidth, 70)
#end: Include/Exclude (YES/NO), buttons and comment
#beginning: Progress and Save
buttons_paned <- ggroup(container = paned)
addSpace(buttons_paned, 50)
frame_PROGRESS <- gframe("Progress", container = buttons_paned, expand = TRUE)
addSpace(frame_PROGRESS, 10)
text_progress <- glabel(paste0("Reviewer: ",
aReviewer,
" | ",
round(((currentItem - 1.0)/nrow(subData)) * 100, digits = 1),
"% complete (", currentItem, " of ",
nrow(subData) , ")"),
container = frame_PROGRESS)
aButton_save <- gbutton("SAVE & CONTINUE",
container = frame_PROGRESS,
handler = function(h, ...)
{
{
write.csv(subData,
file = file, #file was dataFilename
row.names = FALSE)
svalue(text_lastSaved) <- paste("last saved: ", Sys.time())
}
currentItem <<- currentItem + 1
if(currentItem > nrow(subData))
{svalue(text_ABSTRACT) <- "You have screened all the Abstracts!"
svalue(text_TITLE) <- ""
} else
{svalue(text_ABSTRACT) <- subData[currentItem, abstractColumnName]
svalue(text_TITLE) <- subData[currentItem, titleColumnName]
svalue(text_progress) <- paste0(
round(((currentItem - 1.0)/nrow(subData)) * 100, digits = 1),
"% complete (", currentItem, " of ", nrow(subData), ")")
svalue(text_TYPE_OF_STUDY_COMMENT) <- subData[currentItem, studyTypeCommentColumnName]
svalue(radioButtons_ToS) <- subData[currentItem, studyTypeColumnName]
svalue(radioButtons_INCLUDE) <- subData[currentItem, unscreenedColumnName]
svalue(text_INCLUDE_COMMENT) <- subData[currentItem, unscreenedCommentColumnName]
}
}
)
size(aButton_save) <- c(130, 40)
text_lastSaved <- glabel(paste(" last saved: not this session"), # empty space at beginning on purpose to leave some distance to button, as addSpace() screwed things up
container = frame_PROGRESS)
#end of Progress and Save
#end of buttons_paned
#end of paned
visible(win) <- TRUE
# end of win
}
###############
# Preparation of the files and starting of the GUI
###############
# Set working directory
setwd("C:/Users/Hilser/Documents/R")
# Add team member(s)
theTeam <- c("Stefan")
#import .csv-file and name it review" ##### REPLACED BY SAMPLE DATA BELOW
# review <- read.csv("C:/Users/Hilser/Documents/R/Metagear/Better_Metagear/review_duplicates_removed.csv", header = TRUE, sep = ",")
# Create Sample Data instead of importing .csv-file
Title <- c("Title 1", "Title 2", "Title 3", "Title 4")
Abstract <- c( "Abstract 1", "Abstract 2", "Abstract 3", "Abstract 4")
review <- data.frame(Title, Abstract)
# Prepare file: Adding columns "STUDY_ID", "REVIEWERS", "INCLUDE" (and fill with "not vetted"), "INCLUDE_COMMENT", "TYPE_OF_STUDY"
review_initialized <- better_effort_initialize(review, study_ID = TRUE, unscreenedValue = "not vetted", dual = FALSE, front = TRUE)
# distribute effort amongst Reviewers, adding their name (reviewers = ) to the column "REVIEWERS"
review_distributed <- better_effort_distribute(review_initialized, dual = FALSE, reviewers = theTeam, column_name = "REVIEWERS",
effort = 100, initialize = FALSE, save_split = TRUE)
# opens a graphical user interface (GUI) that allows for abstract screening
better_abstract_screener("effort_Stefan.csv", aReviewer = "Stefan")
From the described behaviour, I assume that the error lies somewhere in one of the following functions:
aButton_save # The button that saves the file and moves to the next abstract
the theAnswer functions # used to update the reference list
the updateAll functions # helper function used to update and keep track of screened abstracts
Sorry, my guess is that the font.attribute you specify for text_TITLE isn't being preserved after you call svalue(text_TITLE) <-. The easy solution would be to call font(text_TITLE)<- ... after setting the text. The proper solution would be to fix this method: https://github.com/jverzani/gWidgets2RGtk2/blob/master/R/gtext.R#L84 (this should use the method insert_text https://github.com/jverzani/gWidgets2RGtk2/blob/master/R/gtext.R#L160 which does adjust for the font attribute.
With that in mind, you might try this 1-2 punch to set text:
svalue(text_TITLE) <- ""
text_TITLE$insert_text(new_text, "beginning")

R Parallel Programming: Error in { : task 1 failed - "could not find function "%>%""

I tried to do Parallel Programming in R by modified my script. On my script I did two parallel programming. First one was done but the second was error whereas the script structure were same. Below is my code:
library(rvest)
library(RMySQL)
library(curl)
library(gdata)
library(doMC)
library(foreach)
library(doParallel)
library(raster)
trim <- function (x) gsub("^\\s+|\\s+$", "", x)
setwd('/home/chandra/R/IlmuOne/MisterAladin')
no_cores <- detectCores()
cl<-makeCluster(no_cores)
registerDoParallel(cl)
MasterData = read.xls("Master Hotels - FINAL.xlsx", sheet = 1, header = TRUE)
MasterData$url_agoda = as.character(MasterData$url_agoda)
today = as.Date(format(Sys.time(), "%Y-%m-%d"))+2
ntasks <- nrow(MasterData)
#This section perfomed well
foreach(i=1:ntasks) %dopar% {
url = MasterData$url_agoda[i]
if (trim(url)!='-' & trim(url)!='')
{
from = gregexpr(pattern ='=',url)[[1]][1]
piece1 = substr(url,1,from)
from = gregexpr(pattern ='&los=',url)[[1]][1]
piece2 = substr(url,from,nchar(url))
MasterData$url_agoda[i] = paste0(piece1,today,piece2)
}
}
con <- dbConnect(RMySQL::MySQL(), username = "root", password = "master",host = "localhost", dbname = "mister_aladin")
#Tried first 10 data
#Below section was error and always return error: Error in { : task 1 failed - "could not find function "%>%""
foreach(a=1:10, .packages='foreach') %dopar% {
hotel_id = MasterData$id[a]
vendor = 'Agoda'
url = MasterData$url_agoda[a]
if (url!='-')
{
tryCatch({
hotel <- curl(url) %>%
read_html() %>%
html_nodes(xpath='//*[#id="room-grouping"]') %>%
html_table(fill = TRUE)
hotel <- hotel[[1]]
hotel$hotel_id= hotel_id
hotel$vendor= vendor
colnames(hotel)[1] = 'TheSpace'
colnames(hotel)[4] = 'PricePerNight'
room = '-'
hotel$NormalPrice = 0
hotel$FinalPrice = 0
for(i in 1:nrow(hotel))
{
if (i==1 | (!grepl('See photos',hotel$TheSpace[i]) & hotel$TheSpace[i]!='') )
{
room = hotel$TheSpace[i]
}
hotel$TheSpace[i] = room
#Normal Price
if (gregexpr(pattern ='IDR',hotel$PricePerNight[i])[[1]][1][1]==1)
{
split = strsplit(hotel$PricePerNight[i],'\n')[[1]]
NormalPrice = trim(split[2])
hotel$NormalPrice[i] = NormalPrice
NormalPrice = as.integer(gsub(",","",NormalPrice))
hotel$NormalPrice[i] = NormalPrice
}
#Final Price
if (gregexpr(pattern ='IDR',hotel$PricePerNight[i])[[1]][1][1]==1)
{
split = strsplit(hotel$PricePerNight[i],'\n')[[1]]
FinalPrice = trim(split[6])
hotel$FinalPrice[i] = FinalPrice
FinalPrice = as.integer(gsub(",","",FinalPrice))
hotel$FinalPrice[i] = FinalPrice
}
hotel$NormalPrice[is.na(hotel$NormalPrice)] <- 0
hotel$FinalPrice[is.na(hotel$FinalPrice)] <- 0
}
hotel = hotel[which(hotel$FinalPrice!=0),c("TheSpace","NormalPrice","FinalPrice")]
colnames(hotel) = c('room','normal_price','final_price')
hotel$log = format(Sys.time(), "%Y-%m-%d %H:%M:%S")
hotel$hotel_id = hotel_id
hotel$vendor = vendor
Push = hotel[,c('hotel_id','room','normal_price','final_price','vendor','log')]
#print(paste0('Agoda: push one record, hotel id ',hotel_id,'!'))
#cat(paste(paste0('Agoda: push one record, hotel id ',hotel_id,'!'),'\n'))
dbWriteTable(conn=con,name='prices_',value=as.data.frame(Push), append = TRUE, row.names = F)
},
error = function(e) {
Sys.sleep(2)
e
})
}
}
dbDisconnect(con)
stopImplicitCluster()
Every time I run the script it always gives me error: Error in { : task 1 failed - "could not find function "%>%""
I already check every post on this forum and tried to apply it but no one works.
Please advise any solution
you have to use .packages = c("magrittr", ...) and include all the packages, which are necessary to run the code within the foreach loop. However, .packages = "foreach" is not helping.
See, you can imagine that all the packages you define in .packages are forwareded / loaded in each parallel worker.
The %>% operator requires the package magrittr. In this case however it does not suffice to load it at the beginning of your script - it needs to be loaded for each of the nodes. You could add this line to the creation of your cluster to accomplish this:
cl<-makeCluster(no_cores)
registerDoParallel(cl)
clusterCall(cl, function() library(magrittr))

Using image files on disk with R animation package

I used R to create a series of images that took a long time to run. I'd like to use the animation package to make a video from them without re-running the analysis.
I can't find an example using existing images from file. The closest is Yihui Xie's demo('flowers') to create an HTML animation. I changed his code and can successfully make an mp4 of the flower images but I'm not sure how to access images already on file.
Based on his code, it should be something like this:
library(animation)
oopts = if (.Platform$OS.type == "windows") {
ani.options(ffmpeg = "C:/Software/ffmpeg/ffmpeg-20151015-git-0418541-win64-static/bin/ffmpeg.exe")}
#My list of images from disk
extList = list.files(myDir, pattern='.jpg', full.names=T)
saveVideo({
for (i in 1:length(extList)) {
#Yihui Xie's example downloads jpegs from web
#This code works to make an mp4 but I want to use images from disk
#extList = c('http://i.imgur.com/rJ7xF.jpg',
# 'http://i.imgur.com/Lyr9o.jpg',
# 'http://i.imgur.com/18Qrb.jpg')
#download.file(url = extList[i], destfile = sprintf(ani.options('img.fmt'), i), mode = 'wb')
someFunctionToAccessImage(extList[i])
}
}, video.name='notFlowers.mp4',
use.dev = FALSE,
ani.type = 'jpg',
interval = 2,
single.opts = "'dwellMultiplier': 1")
Bonus question - Can I do this with PNGs or other image types?
I found there to be a couple of problems with this. saveVideo uses a temporary directory to process the files and make the movie. Also, the postfix it was adding to the image name wasn't working correctly. So, here is a way to do it where you copy the images from the folder you have them stored in into the temporary directory used by saveVideo. The tricky part is finding the path to that directory, which is done using sys.frame from a function defined in the expression.
Note: another possible option could be to manually copy the images to the temporary folder that you know saveVideo will use (it will call tempdir()), or redefine tempdir() to return the path to your current images, but I haven't tested that.
library(animation)
oopts = if (.Platform$OS.type == "windows")
ani.options(ffmpeg = "C:\\home\\src\\ffmpeg-20151017-git-e9299df-win64-static\\bin\\ffmpeg.exe")
## Some variables
dirPath <- normalizePath("images/") # path to folder containing images
postfix <- "%03d" # I created my files with "Rplot%03d"
## Make the animation
saveVideo({
## Need to retrieve some variables from environments up the call stack
env.info <- (function() { list(wd = sys.frame(-1)$owd, fmt=sys.frame(-2)$img.fmt,
e=sys.frame(-2)) })()
postfix <- postfix
img.fmt <- gsub("%d", postfix, env.info$fmt, fixed=TRUE)
assign('img.fmt', img.fmt, envir=env.info$e)
file.copy(list.files(dirPath, full.names = TRUE), to=env.info$wd, overwrite = TRUE)
}, video.name='heatBalls.mp4', img.name='Rplot', interval = .05, use.dev=FALSE)
Full example
## Random function to save some images to disk
circ <- function(x,y,r) { s <- seq(-pi,pi,len=30); data.frame(x=x+r*cos(s), y=y+r*sin(s)) }
imgFun <- function(n, ncircs, dirPath) {
if (!require(scales)) stop("install scales package")
rads <- runif(ncircs, 0.5, 3)
xs <- runif(ncircs, 0.1+rads, 19.9-rads)
ys <- runif(ncircs, 0.1+rads, 19.9-rads)
vs <- matrix(runif(ncircs*2), 2)
cols <- colorRampPalette(c('lightblue','darkblue'), alpha=0.3)(ncircs)
png(file.path(dirPath, 'Rplot%03d.png'))
for (i in seq_len(n)) {
image(x=seq(0, 20, length=20), y=seq(0, 20, length=20),
z=matrix(rnorm(400),20), col=heat.colors(20, alpha=0.6), xlab='', ylab='')
for(j in 1:ncircs) polygon(x=circ(xs[j], ys[j], rads[j]), col=alpha(cols[j],0.7))
condx <- (xs + rads) > 20 | (xs - rads) < 0
condy <- (ys + rads) > 20 | (ys - rads) < 0
vs[1,condx] <- -vs[1,condx]
vs[2,condy] <- -vs[2,condy]
xs <- xs + vs[1,]
ys <- ys + vs[2,]
}
dev.off()
}
## Create some images on disk in a folder called "images"
dirPath <- normalizePath("images/")
dir.create(dirPath)
imgFun(50, 18, dirPath)
Then run the above code and a movie like the following should be made.

Resources