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'?
)