├── .Rbuildignore ├── LICENSE ├── NEWS.md ├── NAMESPACE ├── tests ├── testthat.R └── testthat │ ├── data │ ├── notebookR.rds │ ├── notebook01.rds │ ├── notebookSh.rds │ ├── python_Notebook.rds │ └── ipyOut.ipynb │ ├── test-ipyToJson.R │ ├── test-cellLanguage.R │ └── test-cell_to_ipynb.R ├── R ├── rcloud.jupyter.notebooks.R ├── onload.R ├── 01_kernel.R ├── import_jupyter.R └── export_jupyter.R ├── .travis.yml ├── man ├── cellType.Rd ├── cellToIpynb.Rd ├── ipyToJson.Rd ├── cellLanguage.Rd ├── rcloud.jupyter.noteooks.Rd ├── cellImportCheck.Rd ├── importIpynb.Rd ├── shellContent.Rd ├── getKernel.Rd ├── exportIpynb.Rd └── cellMagicExt.Rd ├── DESCRIPTION ├── README.md └── inst └── javascript └── rcloud.jupyter.notebooks.js /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\.travis\.yml$ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2017 2 | COPYRIGHT HOLDER: AT&T Intellectual Property 3 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | ## 0.1.4 2 | Fix accidental global that could clobber other RCloud extensions 3 | 4 | (start of log) 5 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(importIpynb) 4 | importFrom(jsonlite,write_json) 5 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(rcloud.jupyter.notebooks) 3 | 4 | test_check("rcloud.jupyter.notebooks") 5 | -------------------------------------------------------------------------------- /tests/testthat/data/notebookR.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.jupyter.notebooks/master/tests/testthat/data/notebookR.rds -------------------------------------------------------------------------------- /tests/testthat/data/notebook01.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.jupyter.notebooks/master/tests/testthat/data/notebook01.rds -------------------------------------------------------------------------------- /tests/testthat/data/notebookSh.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.jupyter.notebooks/master/tests/testthat/data/notebookSh.rds -------------------------------------------------------------------------------- /tests/testthat/data/python_Notebook.rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/att/rcloud.jupyter.notebooks/master/tests/testthat/data/python_Notebook.rds -------------------------------------------------------------------------------- /R/rcloud.jupyter.notebooks.R: -------------------------------------------------------------------------------- 1 | #' rcloud.jupyter.noteooks 2 | #' 3 | #' This package is an extension to RCloud, it allows import and export of Jupyter notebooks. 4 | #' 5 | #' 6 | #' @name rcloud.jupyter.noteooks 7 | #' @docType package 8 | NULL 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | sudo: false 5 | cache: packages 6 | warnings_are_errors: false 7 | r_github_packages: 8 | - r-lib/covr 9 | 10 | after_success: 11 | - Rscript -e 'covr::codecov()' 12 | 13 | -------------------------------------------------------------------------------- /man/cellType.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{cellType} 4 | \alias{cellType} 5 | \title{Check if cell is code or markdown} 6 | \usage{ 7 | cellType(cell) 8 | } 9 | \arguments{ 10 | \item{cell}{A single notebook cell} 11 | } 12 | \value{ 13 | cell type 14 | } 15 | \description{ 16 | Check if cell is code or markdown 17 | } 18 | -------------------------------------------------------------------------------- /man/cellToIpynb.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{cellToIpynb} 4 | \alias{cellToIpynb} 5 | \title{Converts notebook cells to JSON cells} 6 | \usage{ 7 | cellToIpynb(cells) 8 | } 9 | \arguments{ 10 | \item{cells}{A list of cells.} 11 | } 12 | \value{ 13 | A list 14 | } 15 | \description{ 16 | Converts notebook cells to JSON cells 17 | } 18 | -------------------------------------------------------------------------------- /man/ipyToJson.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/import_jupyter.R 3 | \name{ipyToJson} 4 | \alias{ipyToJson} 5 | \title{Maps Ipynotebook to gist list} 6 | \usage{ 7 | ipyToJson(json, filename) 8 | } 9 | \arguments{ 10 | \item{json}{Json list} 11 | 12 | \item{filename}{filename} 13 | } 14 | \value{ 15 | list 16 | } 17 | \description{ 18 | Maps Ipynotebook to gist list 19 | } 20 | -------------------------------------------------------------------------------- /man/cellLanguage.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{cellLanguage} 4 | \alias{cellLanguage} 5 | \title{Checks the language of a cell.} 6 | \usage{ 7 | cellLanguage(cell) 8 | } 9 | \arguments{ 10 | \item{cell}{A single notebook cell} 11 | } 12 | \value{ 13 | Language of given cell (string) 14 | } 15 | \description{ 16 | Checks the language of a cell. 17 | } 18 | -------------------------------------------------------------------------------- /man/rcloud.jupyter.noteooks.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rcloud.jupyter.notebooks.R 3 | \docType{package} 4 | \name{rcloud.jupyter.noteooks} 5 | \alias{rcloud.jupyter.noteooks} 6 | \alias{rcloud.jupyter.noteooks-package} 7 | \title{rcloud.jupyter.noteooks} 8 | \description{ 9 | This package is an extension to RCloud, it allows import and export of Jupyter notebooks. 10 | } 11 | -------------------------------------------------------------------------------- /man/cellImportCheck.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/import_jupyter.R 3 | \name{cellImportCheck} 4 | \alias{cellImportCheck} 5 | \title{Checks cell type} 6 | \usage{ 7 | cellImportCheck(cell, fileEtx) 8 | } 9 | \arguments{ 10 | \item{cell}{Json cell} 11 | 12 | \item{fileEtx}{extracted from Json metadata} 13 | } 14 | \value{ 15 | character string 16 | } 17 | \description{ 18 | Checks cell type 19 | } 20 | -------------------------------------------------------------------------------- /man/importIpynb.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/import_jupyter.R 3 | \name{importIpynb} 4 | \alias{importIpynb} 5 | \title{Converts Json list to Rcloud notebook} 6 | \usage{ 7 | importIpynb(text, filename) 8 | } 9 | \arguments{ 10 | \item{text}{Json list} 11 | 12 | \item{filename}{filename} 13 | } 14 | \value{ 15 | notebook 16 | } 17 | \description{ 18 | Converts Json list to Rcloud notebook 19 | } 20 | -------------------------------------------------------------------------------- /tests/testthat/test-ipyToJson.R: -------------------------------------------------------------------------------- 1 | context("ipyToJson") 2 | 3 | test_that("ipyToJson takes a JSON structure and converts to a notebook like list",{ 4 | 5 | file <- "ipyOut.ipynb" 6 | file_path <- file.path(paste0("data/", file)) 7 | 8 | json <- jsonlite::read_json(file_path) 9 | notebook <- ipyToJson(json, "ipynbOut.ipynb") 10 | 11 | expect_equal(class(notebook), "list") 12 | expect_equal(names(notebook), c("description", "files")) 13 | 14 | }) 15 | 16 | -------------------------------------------------------------------------------- /man/shellContent.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{shellContent} 4 | \alias{shellContent} 5 | \title{Converts as shell cell to jupyter executable format} 6 | \usage{ 7 | shellContent(content) 8 | } 9 | \arguments{ 10 | \item{content}{shell cell content} 11 | } 12 | \value{ 13 | content 14 | } 15 | \description{ 16 | Running shell code in jupyter notebooks is currently only supported in a python kernel 17 | } 18 | -------------------------------------------------------------------------------- /man/getKernel.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{getKernel} 4 | \alias{getKernel} 5 | \title{Return list in the format used for Jupyter kernel} 6 | \usage{ 7 | getKernel(lang = c("R", "Python")) 8 | } 9 | \arguments{ 10 | \item{lang}{either 'R' or 'Python'} 11 | } 12 | \value{ 13 | list containing either python or R kernel nad language info 14 | } 15 | \description{ 16 | Return list in the format used for Jupyter kernel 17 | } 18 | -------------------------------------------------------------------------------- /man/exportIpynb.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{exportIpynb} 4 | \alias{exportIpynb} 5 | \title{Creates an ipynb list structure and writes to file} 6 | \usage{ 7 | exportIpynb(id, version, file = NULL) 8 | } 9 | \arguments{ 10 | \item{id}{notebook id} 11 | 12 | \item{version}{notebook version} 13 | 14 | \item{file}{Optional file path to write to} 15 | } 16 | \value{ 17 | A list 18 | } 19 | \description{ 20 | Creates an ipynb list structure and writes to file 21 | } 22 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rcloud.jupyter.notebooks 2 | Title: Jupyter notebook support for RCloud 3 | Version: 0.1.4 4 | Authors@R: person("Beth", "Ashlee", email = "bashlee@mango-solutions.com", role = c("aut", "cre")) 5 | Description: Import and Export Jupyter notebooks to/from RCloud. 6 | Depends: R (>= 3.1.2) 7 | License: MIT + file LICENSE 8 | RCloud-Extension: gui 9 | Encoding: UTF-8 10 | LazyData: true 11 | RoxygenNote: 6.0.1 12 | Imports: 13 | rcloud.support, 14 | jsonlite 15 | Remotes: MangoTheCat/rcloud.support 16 | Suggests: 17 | testthat, 18 | covr 19 | -------------------------------------------------------------------------------- /man/cellMagicExt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/export_jupyter.R 3 | \name{cellMagicExt} 4 | \alias{cellMagicExt} 5 | \title{Adds a cell to load extentions required to run R magic cells} 6 | \usage{ 7 | cellMagicExt(kernel, cellLanguages, cells) 8 | } 9 | \arguments{ 10 | \item{kernel}{kernle} 11 | 12 | \item{cellLanguages}{all cell languages in notebook} 13 | 14 | \item{cells}{list to append to} 15 | } 16 | \value{ 17 | content 18 | } 19 | \description{ 20 | Currently only supports R cells in Python, this funtion will be lkely to expand to support new languages 21 | Appends cell at top of notebook. 22 | } 23 | -------------------------------------------------------------------------------- /R/onload.R: -------------------------------------------------------------------------------- 1 | caps <- NULL 2 | 3 | .onLoad <- function(libname, pkgname) { 4 | 5 | ## Not in RCloud? Return silently 6 | if (! requireNamespace("rcloud.support", quietly = TRUE)) return() 7 | 8 | path <- system.file( 9 | package = "rcloud.jupyter.notebooks", 10 | "javascript", 11 | "rcloud.jupyter.notebooks.js" 12 | ) 13 | 14 | caps <<- rcloud.support::rcloud.install.js.module( 15 | "rcloud.jupyter.notebooks", 16 | paste(readLines(path), collapse = '\n') 17 | ) 18 | 19 | ocaps <- list( 20 | importIpynb = makeOc(importIpynb), 21 | exportIpynb = makeOc(exportIpynb) 22 | ) 23 | 24 | if (!is.null(caps)) caps$init(ocaps) 25 | } 26 | 27 | makeOc <- function(x) { 28 | do.call(base::`:::`, list("rcloud.support", "make.oc"))(x) 29 | } 30 | -------------------------------------------------------------------------------- /R/01_kernel.R: -------------------------------------------------------------------------------- 1 | # R ---------------- 2 | kernelspecR <- list(display_name = "R", 3 | language = "R", 4 | name = "ir") 5 | 6 | languageInfoR <- list( 7 | codemirror_mode = "r", 8 | file_extension = ".r", 9 | mimetype = "text/x-r-source", 10 | name = "R", 11 | pygments_lexer = "r", 12 | version = paste(version$major, version$minor, sep = ".")) 13 | 14 | # Python ------------ 15 | kernelspecPy <- list(display_name = "Python 3", 16 | language = "python", 17 | name = "python3") 18 | 19 | languageInfoPy <- list( 20 | codemirror_mode = list(name = "ipython", version = 3), 21 | file_extension = ".py", 22 | mimetype = "text/x-python", 23 | name = "python", 24 | nbconvert_exporter = "python", 25 | pygments_lexer = "ipython3", 26 | version = "3.6.1") ## TODO: determine Python version from user installation 27 | 28 | -------------------------------------------------------------------------------- /tests/testthat/test-cellLanguage.R: -------------------------------------------------------------------------------- 1 | context("cellLanguage") 2 | 3 | test_that("cell language of a notebook can be detected and kernel returned",{ 4 | 5 | file <- "python_Notebook.rds" 6 | file_path <- file.path(paste0("data/", file)) 7 | 8 | notebook <- readRDS(file_path) 9 | language <- cellLanguage(notebook$content$files$part1.py) 10 | 11 | kernelOut <- getKernel(lang = language) 12 | 13 | expect_equal(language, "Python") 14 | expect_equal(kernelOut, list(language_info = languageInfoPy, 15 | kernelspec = kernelspecPy)) 16 | expect_equal(getKernel("R"), list(language_info = languageInfoR, 17 | kernelspec = kernelspecR)) 18 | 19 | }) 20 | 21 | test_that("notebok cell type", { 22 | 23 | file <- "notebook01.rds" 24 | file_path <- file.path(paste0("data/", file)) 25 | 26 | notebook <- readRDS(file_path) 27 | type <- cellType(notebook$content$files$part1.R) 28 | 29 | expect_equal(type, "code") 30 | }) 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/testthat/test-cell_to_ipynb.R: -------------------------------------------------------------------------------- 1 | context("cellToIpynb") 2 | 3 | 4 | test_that("notebook list is converted to json list", { 5 | 6 | file <- "notebook01.rds" 7 | file_path <- file.path(paste0("data/", file)) 8 | 9 | notebook <- readRDS(file_path) 10 | json <- cellToIpynb(notebook$content$files) 11 | 12 | expect_equal(length(json), 4) 13 | expect_equal(class(json), "list") 14 | expect_equal(names(json), c("cells", "metadata", "nbformat", "nbformat_minor")) 15 | 16 | 17 | }) 18 | 19 | test_that("An R notebook list is converted to json list", { 20 | 21 | file <- "notebookR.rds" 22 | fileSh <- "notebookSh.rds" 23 | file_path <- file.path(paste0("data/", file)) 24 | file_path_sh <- file.path(paste0("data/", fileSh)) 25 | 26 | notebook <- readRDS(file_path) 27 | notebookSh <- readRDS(file_path_sh) 28 | json <- cellToIpynb(notebook$content$files) 29 | jsonSh <- cellToIpynb(notebookSh$content$files) 30 | 31 | expect_equal(names(json), c("cells", "metadata", "nbformat", "nbformat_minor")) 32 | expect_equal(names(jsonSh), c("cells", "metadata", "nbformat", "nbformat_minor")) 33 | 34 | 35 | }) 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rcloud.jupyter.notebooks 2 | [](https://travis-ci.org/att/rcloud.jupyter.notebooks)[](https://codecov.io/gh/att/rcloud.jupyter.notebooks?branch=master) 3 | 4 | RCloud is an environment for collaboratively creating and sharing data analysis scripts. RCloud lets you mix analysis code in R, HTML5, Markdown, Python, and others. 5 | 6 | The rcloud.jupyter.notebooks package provides an extension to import and export Jupyter notebooks in RCloud. 7 | 8 | 9 | ## Install 10 | 11 | 1. Install this package on RCloud, using `devtools::install_github()`, 12 | or `install-github.me`: 13 | ```R 14 | source("https://github.com/MangoTheCat/rcloud.jupyter.notebooks") 15 | ``` 16 | 2. In the RCloud *Settings* menu, in the *Enable Extensions* line, add 17 | `rcloud.jupyter.notebooks`, so that the package is loaded automatically. 18 | 3. Reload RCloud in the browser. This loads the package, and you should 19 | have the *Import as Jupyter Notebook* and *Export as Jupyter Notebook* items added 20 | int the *Advanced* menu on the top. 21 | 22 | ## Usage 23 | 24 | ### In RCloud 25 | 26 | Choose the *Import as Jupyter Notebook* item from the *Advanced* menu and 27 | then select the *.ipynb* file to import from your computer. 28 | 29 | or 30 | 31 | Choose the *Export as Jupyter Notebook* item from the *Advanced* menu and the current 32 | RCloud notebook will be exported. 33 | -------------------------------------------------------------------------------- /tests/testthat/data/ipyOut.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/html": [ 11 | "9" 12 | ], 13 | "text/latex": [ 14 | "9" 15 | ], 16 | "text/markdown": [ 17 | "9" 18 | ], 19 | "text/plain": [ 20 | "[1] 9" 21 | ] 22 | }, 23 | "metadata": {}, 24 | "output_type": "display_data" 25 | } 26 | ], 27 | "source": [ 28 | "4 +5 " 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "data": { 38 | "text/html": [ 39 | "9" 40 | ], 41 | "text/latex": [ 42 | "9" 43 | ], 44 | "text/markdown": [ 45 | "9" 46 | ], 47 | "text/plain": [ 48 | "[1] 9" 49 | ] 50 | }, 51 | "metadata": {}, 52 | "output_type": "display_data" 53 | } 54 | ], 55 | "source": [ 56 | "%%R y <- 8" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 3, 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "data": { 66 | "text/html": [ 67 | "9" 68 | ], 69 | "text/latex": [ 70 | "9" 71 | ], 72 | "text/markdown": [ 73 | "9" 74 | ], 75 | "text/plain": [ 76 | "[1] 9" 77 | ] 78 | }, 79 | "metadata": {}, 80 | "output_type": "display_data" 81 | } 82 | ], 83 | "source": [ 84 | "%load_ext rpy2.ipython" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "## Markdown example" 92 | ] 93 | } 94 | ], 95 | "metadata": { 96 | "kernelspec": { 97 | "display_name": "R", 98 | "language": "R", 99 | "name": "ir" 100 | }, 101 | "language_info": { 102 | "codemirror_mode": "r", 103 | "file_extension": ".r", 104 | "mimetype": "text/x-r-source", 105 | "name": "R", 106 | "pygments_lexer": "r", 107 | "version": "3.3.3" 108 | } 109 | }, 110 | "nbformat": 4, 111 | "nbformat_minor": 2 112 | } 113 | -------------------------------------------------------------------------------- /R/import_jupyter.R: -------------------------------------------------------------------------------- 1 | #' Maps Ipynotebook to gist list 2 | #' 3 | #' @param json Json list 4 | #' @param filename filename 5 | #' @return list 6 | ipyToJson <- function(json, filename){ 7 | 8 | notebookName <- tools::file_path_sans_ext(basename(filename)) 9 | 10 | notebook <- list(description = notebookName, files = list()) 11 | 12 | for(i in seq_along(json$cells)){ 13 | 14 | 15 | cellContent <- cellImportCheck(json$cells[[i]], 16 | fileEtx = json$metadata$language_info$file_extension) 17 | 18 | 19 | # Check cell has content before creating 20 | if(nchar(paste0(cellContent$content, collapse = "")) > 0){ 21 | notebook$files[[paste0("part", i, cellContent$ext)]] <- list(content = paste(unlist(cellContent$content), collapse = "")) 22 | 23 | } 24 | } 25 | 26 | notebook 27 | 28 | } 29 | 30 | #' Converts Json list to Rcloud notebook 31 | #' 32 | #' @param text Json list 33 | #' @param filename filename 34 | #' @return notebook 35 | #' @export 36 | 37 | importIpynb <- function(text, filename){ 38 | 39 | notebook <- ipyToJson(text, filename) 40 | 41 | res <- rcloud.support::rcloud.create.notebook(notebook, FALSE) 42 | 43 | if (!isTRUE(res$ok)) stop("failed to create new notebook") 44 | 45 | res$content 46 | } 47 | 48 | #' Checks cell type 49 | #' 50 | #' @param cell Json cell 51 | #' @param fileEtx extracted from Json metadata 52 | #' @return character string 53 | 54 | cellImportCheck <- function(cell, fileEtx){ 55 | 56 | content <- cell$source 57 | 58 | ext <- if(cell$cell_type == "code"){ 59 | fileEtx 60 | } else if(cell$cell_type == "markdown"){ 61 | ".md" 62 | } else{ 63 | stop("Cell type unknown") 64 | } 65 | 66 | ## Cell magics %%R %%! %%sh 67 | ## Line magics %R ! 68 | lookUp <- data.frame(magic = c("%%R", "%R", "^!", "%%!", "%%sh"), 69 | extn = c(".R", ".R", ".sh", ".sh", ".sh")) 70 | 71 | for(i in seq_along(lookUp$magic)){ 72 | theMagic <- paste0("^", lookUp$magic[i]) 73 | 74 | if(length(grep(theMagic, content)) > 0){ 75 | 76 | content <- gsub(theMagic, replacement = "", x = content ) 77 | content[1] <- gsub("^\n", "",content[1]) # Clean up first line after removing the magic 78 | ext <- lookUp$extn[i] 79 | } 80 | } 81 | 82 | # Remove rpy2.ipython cell/line created by export 83 | content <- gsub("%load_ext rpy2.ipython", "", content) 84 | 85 | return(c(content = list(content), ext = as.character(ext))) 86 | 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /R/export_jupyter.R: -------------------------------------------------------------------------------- 1 | #' Creates an ipynb list structure and writes to file 2 | #' 3 | #' @param id notebook id 4 | #' @param version notebook version 5 | #' @param file Optional file path to write to 6 | #' @return A list 7 | #' @importFrom jsonlite write_json 8 | 9 | exportIpynb <- function(id ,version, file = NULL){ 10 | 11 | # Use rcloud.support to read notebook 12 | notebook <- rcloud.support::rcloud.get.notebook(id, version) 13 | 14 | if (!notebook$ok) return(NULL) # Check notebook 15 | 16 | cells <- notebook$content$files 17 | cells <- cells[grep("^part", names(cells))] # Pull out cells parts 18 | 19 | if (!length(names(cells))) return(NULL) 20 | 21 | #Extracts the part numbers 22 | cnums <- suppressWarnings(as.integer( 23 | gsub("^\\D+(\\d+)\\..*", "\\1", names(cells)) 24 | )) 25 | 26 | cells <- cells[match(sort.int(cnums), cnums)] # Order cells 27 | 28 | # Create a temp file if file not specifed 29 | tmp <- file 30 | 31 | if (is.null(tmp)) { 32 | tmp <- tempfile(fileext = ".ipynb") 33 | on.exit(unlink(tmp), add = TRUE) 34 | } 35 | 36 | # Write list to file 37 | jsonlite::write_json(x = cellToIpynb(cells), path = tmp, auto_unbox = TRUE) 38 | 39 | if (is.null(file)) { 40 | list( 41 | description = notebook$content$description, 42 | jup = readChar(tmp, file.info(tmp)$size), 43 | file = tmp 44 | ) 45 | } else { 46 | invisible() 47 | } 48 | 49 | } 50 | 51 | #' Converts notebook cells to JSON cells 52 | #' 53 | #' @param cells A list of cells. 54 | #' @return A list 55 | 56 | cellToIpynb <- function(cells){ 57 | 58 | # Check language of all cells 59 | cellLanguages <- lapply(cells, cellLanguage) 60 | 61 | #If any cells are Python, kernel is Python 62 | kernel <- if(any(cellLanguages == "Python")){ 63 | "Python" 64 | }else if(any(cellLanguages == "R")){ 65 | "R" 66 | # Eg. markdown or bash cells - set kernel to python 67 | } else{ 68 | "Python" 69 | } 70 | 71 | metaData <- getKernel(lang = kernel) 72 | 73 | 74 | # Create json 75 | json <- list(cells = list(), 76 | metadata = list(kernelspec = metaData$kernelspec, 77 | language_info = metaData$language_info), 78 | nbformat = 4L, 79 | nbformat_minor = 2L) 80 | 81 | # If mulitple language add cell to load the correct extentions 82 | cells <- cellMagicExt(kernel, cellLanguages, cells) 83 | 84 | for(i in seq_along(cells)){ 85 | 86 | json$cells[[i]] <- list(cell_type = cellType(cells[[i]]), 87 | execution_count = i, # Cell number 88 | metadata = structure(list(), .Names = character(0)), # Named list 89 | outputs = list(), # Ignore output 90 | source = list(cells[[i]]$content)) # Pull content of each cell 91 | 92 | 93 | # Markdown cells do not require an output or execution count 94 | if(cellType(cells[[i]]) == "markdown"){ 95 | json$cells[[i]]$outputs <- NULL 96 | json$cells[[i]]$execution_count <- NULL 97 | } 98 | 99 | #If RCloud cells are shell script paste each line of content with ! to run in Jupyter 100 | if(cellLanguage(cells[[i]]) == "Shell"){ 101 | json$cells[[i]]$source <- shellContent(json$cells[[i]]$source[[1]]) 102 | 103 | } 104 | 105 | #If multi-language - need to update content to include magics 106 | if(kernel == "Python" && cellLanguage(cells[[i]]) == "R"){ 107 | json$cells[[i ]]$source <- paste0("%%R\n", json$cells[[i]]$source[[1]], collapse = "") 108 | 109 | } 110 | } 111 | return(json) 112 | 113 | } 114 | 115 | #' Check if cell is code or markdown 116 | #' 117 | #' @param cell A single notebook cell 118 | #' @return cell type 119 | 120 | cellType <- function(cell){ 121 | if(grepl("^part.*\\.md$", cell$filename)){ 122 | return("markdown") 123 | }else if(grepl("^part.*\\.Rmd$", cell$filename)){ 124 | return("markdown") 125 | } else{ 126 | return("code") 127 | } 128 | } 129 | 130 | #' Checks the language of a cell. 131 | #' 132 | #' @param cell A single notebook cell 133 | #' @return Language of given cell (string) 134 | 135 | cellLanguage <- function(cell){ 136 | 137 | lang <- if (grepl("^part.*\\.R$", cell$filename)) { 138 | "R" 139 | } else if(grepl("^part.*\\.py$", cell$filename)){ 140 | "Python" 141 | } else if(grepl("^part.*\\.md$", cell$filename)){ 142 | "Markdown" 143 | } else if(grepl("^part.*\\.sh$", cell$filename)){ 144 | "Shell" 145 | } else{ 146 | "Cell Language unknown" 147 | } 148 | 149 | lang 150 | 151 | } 152 | 153 | #' Return list in the format used for Jupyter kernel 154 | #' 155 | #' @param lang either 'R' or 'Python' 156 | #' @return list containing either python or R kernel nad language info 157 | getKernel <- function(lang = c("R", "Python")){ 158 | if(lang == "R"){ 159 | return(list(language_info = languageInfoR, 160 | kernelspec = kernelspecR)) 161 | }else if(lang == "Python") { 162 | return(list(language_info = languageInfoPy, 163 | kernelspec = kernelspecPy)) 164 | } else{ 165 | return(NULL) 166 | } 167 | } 168 | 169 | 170 | #' Converts as shell cell to jupyter executable format 171 | #' @description Running shell code in jupyter notebooks is currently only supported in a python kernel 172 | #' 173 | #' @param content shell cell content 174 | #' @return content 175 | shellContent <- function(content){ 176 | 177 | splitLine <- strsplit(content, split = "\n")[[1]] 178 | pasteShell <- paste0("!", splitLine) 179 | pasteShell[nchar(pasteShell) == 1] <- "" # Remove magic from blank lines 180 | bindContent <- paste(pasteShell, collapse = "\n") 181 | return(bindContent) 182 | 183 | } 184 | 185 | 186 | #' Adds a cell to load extentions required to run R magic cells 187 | #' @description Currently only supports R cells in Python, this funtion will be lkely to expand to support new languages 188 | #' Appends cell at top of notebook. 189 | #' 190 | #' @param kernel kernle 191 | #' @param cellLanguages all cell languages in notebook 192 | #' @param cells list to append to 193 | #' @return content 194 | cellMagicExt <- function(kernel, cellLanguages, cells){ 195 | 196 | if(kernel == "Python" && "R" %in% cellLanguages){ 197 | ## Insert cell to load rpy2.ipython 198 | cells <- c(part0.py = list(list(filename = "part0.py", 199 | language = "python", 200 | content = "%load_ext rpy2.ipython")), 201 | cells) 202 | } 203 | return(cells) 204 | } 205 | 206 | -------------------------------------------------------------------------------- /inst/javascript/rcloud.jupyter.notebooks.js: -------------------------------------------------------------------------------- 1 | 2 | ((function() { 3 | 4 | function download_as_file(filename, content, mimetype) { 5 | var file = new Blob([content], {type: mimetype}); 6 | saveAs(file, filename); // FileSaver.js 7 | } 8 | 9 | return { 10 | init: function(ocaps, k) { 11 | 12 | // Are we in a notebook? 13 | if (RCloud.UI.advanced_menu.add) { 14 | 15 | var oc = RCloud.promisify_paths(ocaps, [ 16 | [ 'importIpynb' ], 17 | [ 'exportIpynb' ] 18 | ], true); 19 | 20 | RCloud.UI.advanced_menu.add({ 21 | 22 | jupImport: { 23 | sort: 11001, 24 | text: 'Import Jupyter Notebook', 25 | modes: ['edit'], 26 | action: function() { 27 | var that = this; 28 | 29 | function create_import_file_dialog() { 30 | var rmd_raw = null; 31 | var notebook = null; 32 | var rmd_status = null; 33 | var rmd_filename = null; 34 | var import_button = null; 35 | 36 | function do_upload(file) { 37 | 38 | rmd_status.hide(); 39 | var fr = new FileReader(); 40 | fr.onloadend = function(e) { 41 | 42 | rmd_status.show(); 43 | rmd_status.html( 44 | '
' + fr.result.split("\n")
45 | .slice(0,15)
46 | .join("\n") + '\n...\n'
47 | );
48 | ui_utils.enable_bs_button(import_button);
49 | rmd_raw = fr.result;
50 | rmd_filename = file.name;
51 | };
52 | fr.readAsText(file);
53 | }
54 |
55 | function do_import() {
56 |
57 | // Need to call back to R to import the notebook
58 | var jsonData = JSON.parse(rmd_raw);
59 | oc.importIpynb(jsonData, rmd_filename).then(
60 | function(notebook) {
61 | console.log(notebook);
62 | if (notebook) {
63 | editor.star_notebook(true, {notebook: notebook}).then(function() {
64 | editor.set_notebook_visibility(notebook.id, true);
65 |
66 | // highlight the node:
67 | editor.highlight_imported_notebooks(notebook);
68 | });
69 | }
70 |
71 | dialog.modal('hide');
72 | }
73 | );
74 | }
75 |
76 | var body = $('');
77 | var file_select = $('');
78 |
79 | file_select
80 | .click(function() {
81 | ui_utils.disable_bs_button(import_button);
82 | rmd_status.hide();
83 | file_select.val(null);
84 | })
85 | .change(function() {
86 | do_upload(file_select[0].files[0]);
87 | });
88 |
89 | rmd_status = $('');
90 | rmd_status.append(rmd_status);
91 |
92 | body.append($('').append(file_select))
93 | .append($('').append(rmd_status.hide()));
94 | var cancel = $('Cancel')
95 | .on('click', function() { $(dialog).modal('hide'); });
96 | import_button = $('Import')
97 | .on('click', do_import);
98 |
99 | ui_utils.disable_bs_button(import_button);
100 |
101 | var footer = $('')
102 | .append(cancel).append(import_button);
103 | var header = $(['