---
title: "Simple regression with Yesod and R"
date: "2016-09-04"
---

<link rel="stylesheet" type="text/css" href="assets/css/hscolour.css">

```{r setup, include=FALSE}
knitr::knit_engines$set(ghc = function (options) 
                  {
                    engine = options$engine
                    f = basename(tempfile(engine, ".", ".txt"))
                    writeLines(options$code, f)
                    on.exit(unlink(f))
                    code = paste(f, options$engine.opts)
                    cmd = options$engine.path 
                    out = if (options$eval) {
                      message("running: ", cmd, " ", code)
                      tryCatch(system2(cmd, code, stdout = TRUE, stderr = FALSE,
                                       env = options$engine.env), error = function(e) {
                                         if (!options$error)
                                           stop(e)
                                         paste("Error in running command", cmd)
                                       })
                    }
                    else ""
                    if (!options$error && !is.null(attr(out, "status"))) 
                      stop(paste(out, collapse = "\n"))
                    knitr::engine_output(options, options$code, out)
                  }
)
## chunk options
knitr::opts_chunk$set(engine = 'ghc', 
                      engine.path = 'ghcscriptrender', 
                      engine.opts = '--module --fragment',
                      echo = FALSE,
                      results = 'asis', 
                      cache = TRUE)
```



As announced in [a previous article](http://stla.github.io/stlapblog/posts/ocpusimplereg.html), I did an app with `opencpu` that performs a simple linear regression in R and returns a report.

Now I have converted this app to a [Yesod+R app](http://stla.github.io/stlapblog/posts/RunRInYesod.html). Thus it does not use `opencpu` anymore. Only Haskell and standard JavaScript libraries.

The contents of this article are my notes about the way I achieved this goal. 


## Structure of the folder

This folder is available in [a Github repo](https://github.com/stla/yesodsimplereg).

```bash
.
├── .cabal-sandbox -> /home/stla/.cabal-sandbox/
├── cabal.sandbox.config -> /home/stla/cabal.sandbox.config
├── FileToBase64.hs
├── index.hamlet
├── R
│   ├── child_regression.Rmd
│   ├── knitRegression.R
│   └── regression.Rmd
├── simplereg.hs
└── static
    ├── bootstrap
    │   ├── bootstrap-4.0.0.min.css
    │   ├── bootstrap-4.0.0.min.js
    │   └── bootstrap.file-input.js
    ├── css
    │   └── regression.css
    ├── jqplot-1.0.9
    │   ├── jquery.jqplot.min.css
    │   ├── jquery.jqplot.min.js
    │   └── plugins
    │       ├── jqplot.canvasAxisLabelRenderer.js
    │       ├── jqplot.canvasTextRenderer.js
    │       ├── jqplot.cursor.js
    │       ├── jqplot.highlighter.js
    │       └── jqplot.trendline.js
    ├── jquery
    │   └── jquery-1.10.2.min.js
    ├── js
    │   ├── jsontotable.js
    │   └── main.js
    ├── jsonTable
    │   └── jsonTable.js
    └── PapaParse
        └── papaparse-4.1.2.min.js
```

- `simplereg.hs` is the main Haskell code;

- `FileToBase64` is an auxiliary module called by `simplereg.hs`;

- `index.hamlet` is the html code in hamlet format;

- the `static` folder contains all `js` and `css` files;

- the `R` folder contains an `R` script and auxiliary files.


## The hamlet file, the main `css`, and the main `js`

- Take the file [index.html](https://github.com/stla/ocpusimplereg/blob/master/inst/www/index.html).

- Remove all the `css`, put it in the main `css` file `static/css/regression.css` (or another `css` file).

- Remove all the remote `<script>` and `<link>` tags (they will be included by `simplereg.hs`).

- Remove the main `js` script, and put its contents in the file `static/js/main.js`.

- Once everything above is done, convert `index.html` to `index.hamlet` with [html2hamlet](https://github.com/tanakh/html2hamlet).


The main `js` script is modified at one place only: the place where it uses the `opencpu.js` library, obviously. 
Instead, an [Ajax PUT request](https://github.com/stla/yesodsimplereg/blob/master/static/js/main.js#L159) is used.


## Include the remote scripts and links in the main Haskell file

The remote scripts and links are included as follows (see [here](https://github.com/stla/yesodsimplereg/blob/master/simplereg.hs#L40)):

```{r}
  addScript $ StaticR jquery_jquery_1_10_2_min_js
  addScript $ StaticR _PapaParse_papaparse_4_1_2_min_js
  addStylesheet $ StaticR bootstrap_bootstrap_4_0_0_min_css
  ...
```

Instead of writing this code by hand, load the following module in GHCi:

```{r}
import System.FilePath.Find (find, always, extension, (==?), (||?))
import System.Directory (setCurrentDirectory, getCurrentDirectory)
import System.FilePath (takeExtension)
import Data.String.Utils (replace)
import Data.Char (isUpper)

getFiles :: FilePath -> IO [FilePath]
getFiles directory = do
  filePaths <- find always (extension ==? ".js" ||? extension ==? ".css") "./"
  return $ map (drop 2) filePaths

linecode :: FilePath -> String
linecode file | extension == ".js" = "  addScript $ StaticR " ++ typeSafeURL
              | extension == ".css" = "  addStylesheet $ StaticR " ++ typeSafeURL
              where extension = takeExtension file
                    typeSafeURL = underscore ++ (replace "/" "_" $ replace "." "_" $ replace "-" "_" file)
                    underscore = if isUpper (file !! 0) then "_" else ""

code :: FilePath -> IO()
code directory = do
  currentDirectory <- getCurrentDirectory
  setCurrentDirectory directory
  files <- getFiles directory
  mapM_ putStrLn $ map linecode files
  setCurrentDirectory currentDirectory
```

You get the desired code by running `code "path/to/static"` in GHCi.


## Calling R

The method, using an Ajax request, is demonstrated on a simple example in [Running R in a Yesod application](http://stla.github.io/stlapblog/posts/RunRInYesod.html). 

Here we put the main `R` function `knitRegression` in [the R folder](https://github.com/stla/yesodsimplereg/tree/master/R) and we source it in Haskell ([here](https://github.com/stla/yesodsimplereg/blob/master/simplereg.hs#L60)):


```{r}
runR :: Args -> IO FilePath
runR (Args dat conflevel filetype) = 
  do
    tmp <- getTemporaryDirectory
    r <- [r|source("R/knitRegression.R")
    knitRegression(jsonlite::fromJSON(dat_hs), conflevel_hs, filetype_hs, tmp_hs)|]
    return $ (fromSomeSEXP r :: FilePath) 
```

It returns the absolute path of the output file, saved in a temporary folder.


## Base64 encoding

I have not found a sure way to make the output file available to the client. 
The solution I adopted consists in encoding the file in base64 with the module I wrote, [FileToBase64](https://github.com/stla/yesodsimplereg/blob/master/FileToBase64.hs).
Thus the file is encoded to a string which can be used in the `href` attribute of a `<a>` tag. 
It is sent to the client as the result of the Ajax PUT request.