Skip to content

Instantly share code, notes, and snippets.

@brandonwillard
Created March 25, 2020 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandonwillard/ec882315091feceae63bb79e2af4fd12 to your computer and use it in GitHub Desktop.
Save brandonwillard/ec882315091feceae63bb79e2af4fd12 to your computer and use it in GitHub Desktop.
Notes on a Better Implementation of org-to-jupyter-notebook Conversion

Overall, it looks easier to write a Jupyter Notebook exporter from scratch. The Notebook format is given here.

(require 'ox-gfm)

(org-export-define-derived-backend 'jnb 'gfm
  :filters-alist '((:filter-parse-tree . org-jnb-separate-elements))
  :menu-entry
  '(?g "Export to Jupyter Notebook"
       ((?G "To temporary buffer"
            (lambda (a s v b) (org-jnb-export-as-markdown a s v)))
        (?g "To file" (lambda (a s v b) (org-jnb-export-to-markdown a s v)))
        (?o "To file and open"
            (lambda (a s v b)
              (if a (org-jnb-export-to-markdown t s v)
                (org-open-file (org-jnb-export-to-markdown nil s v)))))))
  :filters-alist '((:filter-parse-tree . org-jnb-separate-elements))
  :translate-alist '((src-block . org-jnb-src-block)
                     (template . org-jnb-template)))

(defun org-jnb-export-as-markdown (&rest r))
(defun org-gfm-export-to-markdown (&rest r))

;; NOTES: We could use `org-export-data-with-backend' to pre-convert
;; sections to Markdown.
;; FYI: I think `ox-freemind' does a lot of things we might want to do.

;;; Filters

(defun org-jnb-separate-elements (&rest r)
  (org-element-map tree
      '(src-block)
      ;; (remq 'item org-element-all-elements)
    (lambda (e)
      ;; TODO: Split code blocks into their own sections (at top level?).
      ;; Looks like we'll need to use `org-element-adopt-elements'

      ;; (org-element-put-property
      ;;  e :post-blank
      ;;  (if (and (eq (org-element-type e) 'paragraph)
		  ;;           (eq (org-element-type (org-element-property :parent e)) 'item)
		  ;;           (org-export-first-sibling-p e info)
		  ;;           (let ((next (org-export-get-next-element e info)))
		  ;;             (and (eq (org-element-type next) 'plain-list)
		  ;;                  (not (org-export-get-next-element next info)))))
	    ;;      0
	    ;;    1))
      ))
  tree)

;;; Translators

(defun org-jnb-template (contents info)
  ;; Top-level Structure
  ;; {
  ;;   "metadata" : {
  ;;     "kernel_info": {
  ;;         # if kernel_info is defined, its name field is required.
  ;;         "name" : "the name of the kernel"
  ;;     },
  ;;     "language_info": {
  ;;         # if language_info is defined, its name field is required.
  ;;         "name" : "the programming language of the kernel",
  ;;         "version": "the version of the language",
  ;;         "codemirror_mode": "The name of the codemirror mode to use [optional]"
  ;;     }
  ;;   },
  ;;   "nbformat": 4,
  ;;   "nbformat_minor": 0,
  ;;   "cells" : [
  ;;       # list of cell dictionaries, see below
  ;;   ],
  ;; }

  ;; TODO: contents should be the string list of cells?
  ;; We could also use `(plist-get info :parse-tree)'
  )

;; Markdown Cells
;; {
;;   "cell_type" : "markdown",
;;   "metadata" : {},
;;   "source" : "[multi-line *markdown*]",
;; }
;; These can have attachments
;; {
;;   "cell_type" : "markdown",
;;   "metadata" : {},
;;   "source" : ["Here is an *inline* image ![inline image](attachment:test.png)"],
;;   "attachments" : {
;;     "test.png": {
;;         "image/png" : "base64-encoded-png-data"
;;     }
;;   }
;; }

;; Code Cells
;; {
;;   "cell_type" : "code",
;;   "execution_count": 1, # integer or null
;;   "metadata" : {
;;       "collapsed" : True, # whether the output of the cell is collapsed
;;       "scrolled": False, # any of true, false or "auto"
;;   },
;;   "source" : "[some multi-line code]",
;;   "outputs": [{
;;       # list of output dicts (described below)
;;       "output_type": "stream",
;;       ...
;;   }],
;; }
;; Output Types
;; {
;;   "output_type" : "stream",
;;   "name" : "stdout", # or stderr
;;   "text" : "[multiline stream text]",
;; }
;; {
;;   "output_type" : "display_data",
;;   "data" : {
;;     "text/plain" : "[multiline text data]",
;;     "image/png": "[base64-encoded-multiline-png-data]",
;;     "application/json": {
;;       # JSON data is included as-is
;;       "json": "data",
;;     },
;;   },
;;   "metadata" : {
;;     "image/png": {
;;       "width": 640,
;;       "height": 480,
;;     },
;;   },
;; }
;; {
;;   "output_type" : "execute_result",
;;   "execution_count": 42,
;;   "data" : {
;;     "text/plain" : "[multiline text data]",
;;     "image/png": "[base64-encoded-multiline-png-data]",
;;     "application/json": {
;;       # JSON data is included as-is
;;       "json": "data",
;;     },
;;   },
;;   "metadata" : {
;;     "image/png": {
;;       "width": 640,
;;       "height": 480,
;;     },
;;   },
;; }
;; {
;;   'output_type': 'error',
;;   'ename' : str,   # Exception name, as a string
;;   'evalue' : str,  # Exception value, as a string
;;
;;   # The traceback will contain a list of frames,
;;   # represented each as a string.
;;   'traceback' : list,
;; }

(defun org-jnb-src-block (&rest r)
  ;; Use `json-encode-alist'?
  )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment