Search code examples
rggplot2r-rook-package

Error message trying to include ggplot2 chart with Rook


The following code uses Rook to build a very simple webapp for plotting a stock candle chart built with ggplot2. It follows the same pattern as the original example by Jeff Horner.

The error message I get in my RStudio when I execute the script is:

Warning Message: In Multipart$parse(env) : bad content body

Any ideas where I am going wrong and losing the image for my web page?

(Updated to assign my.plot <- ggplot)

(Updated to include ggplot(plot=my.plot, ...))

require(Rook)  # for web functionality
require(ggplot2)  # for graphing
require(tseries) # used to grab time series from yahoo for stock symbols
require(plyr) # data tweaks

# define the web page form
newapp = function(env) {
  req = Rook::Request$new(env)
  res = Rook::Response$new()
  res$write('What stock ticker would you like to see:\n')
  res$write('<BR/>')
  res$write('Stock Symbol:\n')
  res$write('<form method="POST">\n')
  res$write('<input type="text" name="stock.symbol" value="AAPL">      \n')
  res$write('<form method="POST">\n')
  res$write('<input type="radio" name="day.window" value="30">30 Days \n')
  res$write('<input type="radio" name="day.window" value="60" checked>60 Days \n')
  res$write('<input type="radio" name="day.window" value="90">90 Days \n')
  res$write('<input type="submit" name="Go!">\n</form>\n<br>')
  myNormalize = function (target) {
    return((target - min(target))/(max(target) - min(target)))
  }

  if (!is.null(req$POST())) {
    stock.symbol <- req$POST()[["stock.symbol"]]
    day.window <- req$POST()[["day.window"]]

    # get the stock data as a data frame
    df <- as.data.frame(get.hist.quote(stock.symbol,start=as.character(Sys.Date() -  as.numeric(day.window)),quote=c("Open", "High", "Low", "Close")))

    # add an average and the top/bottom for the candle
    df <- mutate(df, Average =(High + Low + Close)/3, Bottom = pmin(Open, Close), Top = pmax(Open, Close), Open.to.Close = ifelse(sign(Open - Close) == 1,'Increase','Decrease'), Date = row.names(df), Date.Label = ifelse(weekdays(as.Date(row.names(df))) == 'Friday',row.names(df),'')) # this gets the date from row.names into a column

    # create a box plot
    my.plot <- ggplot(data=df, aes(x=Date, lower=Bottom, upper=Top, middle=Average,  ymin=Low,  ymax=High, color=Open.to.Close, fill=Open.to.Close), xlab='Date', ylab='Price') +
    geom_boxplot(stat='identity') +
    # add the line for average price from HCL
    geom_line(data=df, aes(x=Date,y=Average, group=0), color='black') +
    # tweak the labeling
    opts(axis.text.x = theme_text(angle=270), legend.position = 'top', legend.direction='horizontal') +
    scale_x_discrete(labels=df$Date.Label)
    ggsave(plot=my.plot, paste("/tmp/pic", stock.symbol, day.window, ".png", sep = ""))

    res$write(paste(day.window,' days stock price trend for ',stock.symbol,'<BR/>', sep=''))
    res$write(paste("<img src='", s$full_url("pic"), stock.symbol, day.window, ".png'", " />", sep = ""))
  }
  res$finish()
}
s = Rhttpd$new()
s$add(app = newapp, name = "visbin")
s$add(app = File$new("/tmp"), name = "pic")
s$start()
s$browse("visbin")

Solution

  • I have solved this, after much trial and error, by setting up a directory called pic at the same level of the R script and saving the images into it, then retrieving the charts from that location. I am not sure if the problem is a permissions issue on the /tmp directory or just a result of the way the img src attribute was constructed, but anyway the solution below has none of those problems.

    A Screenshot of the Result

    Screenshot of share price chart retrieval

    The Code

    While the code below works, the Warning about the content still appears, but it does not seem to stop the script from working and still allows new queries to be made in succession, and new images to be viewed.

    library(Rook) # for web functionality
    library(ggplot2) # for graphing
    library(tseries) # used to grab time series from yahoo for stock symbols
    library(plyr) # data tweaks
    
    PIC.DIR = paste(getwd(), 'pic', sep='/')
    
    # define the web page form
    newapp = function(env) {
        req = Rook::Request$new(env)
        res = Rook::Response$new()
    
        if (!is.null(req$POST())) {
            stock.symbol <- req$POST()[["stock.symbol"]]
            day.window <- req$POST()[["day.window"]]
        } else {
            stock.symbol <- 'AAPL'
            day.window <- 60
        }
        res$write('What stock ticker would you like to see:\n')
        res$write('<BR/>')
        res$write('Stock Symbol:\n')
        res$write('<form method="POST">\n')
        stock.input <- paste('<input type="text" name="stock.symbol" value="',
                             stock.symbol,
                             '">\n', sep='')
        res$write( stock.input ) 
        res$write('<form method="POST">\n')
        res$write('<input type="radio" name="day.window" value="30">30 Days \n')
        res$write('<input type="radio" name="day.window" value="60" checked>60 Days \n')
        res$write('<input type="radio" name="day.window" value="90">90 Days \n')
        res$write('<input type="submit" name="Go!">\n</form>\n<br>')
        myNormalize = function (target) {
            return((target - min(target))/(max(target) - min(target)))
        }
    
        if (!is.null(req$POST())) {
            # get the stock data as a data frame
            df <- as.data.frame(get.hist.quote(stock.symbol,start=as.character(Sys.Date() -  as.numeric(day.window)),quote=c("Open", "High", "Low", "Close")))
    
            # add an average and the top/bottom for the candle
            df <- mutate(df, Average =(High + Low + Close)/3, Bottom = pmin(Open, Close), Top = pmax(Open, Close), Open.to.Close = ifelse(sign(Open - Close) == 1,'Increase','Decrease'), Date = row.names(df), Date.Label = ifelse(weekdays(as.Date(row.names(df))) == 'Friday',row.names(df),'')) # this gets the date from row.names into a column
    
            # create a box plot
            my.plot <- ggplot(data=df, aes(x=Date, lower=Bottom, upper=Top, middle=Average,  ymin=Low,  ymax=High, color=Open.to.Close, fill=Open.to.Close), xlab='Date', ylab='Price') +
                geom_boxplot(stat='identity') +
                # add the line for average price from HCL
                geom_line(data=df, aes(x=Date,y=Average, group=0), color='black') +
                # tweak the labeling
                opts(axis.text.x = theme_text(angle=270), legend.position = 'top', legend.direction='horizontal') +
                scale_x_discrete(labels=df$Date.Label)
            ggsave(plot=my.plot, paste(PIC.DIR, "/pic", stock.symbol, day.window, ".png", sep = ""))
    
            res$write(paste(day.window,' days stock price trend for ',stock.symbol,'<BR/>', sep=''))
            res$write(paste("<img src='", 
                            s$full_url("pic"), 
                            '/pic', stock.symbol, day.window, ".png'", 
                            "width='650 px' height='650 px' />", sep = ""))
        }
        res$finish()
    }
    s = Rhttpd$new()
    s$add(app = newapp, name = "visbin")
    s$add(app = File$new(PIC.DIR), name = "pic")
    s$start()
    s$browse("visbin")
    

    I hope this helps, and maybe someone can work out what the warning is about. My theory is that it has to do with the headers beng formulated by Rook or with the fact that the HTML being pulled served is not valid. Since I have solved the core problem, I have somehow lost energy to chase those theories...