Lesson planning with org and the Carpentries workbench

To export with fenced blocks consider:

 1(defun export-as-workbench-md ()
 2  "Export the current Org buffer to Carpentries-style Markdown using Pandoc."
 3  (interactive)
 4  (let* ((source-file (buffer-file-name))
 5         (output-file (concat (file-name-sans-extension source-file) ".md"))
 6         ;; Enable fenced_divs to convert #+begin_name blocks to ::: name
 7         (pandoc-cmd (format "pandoc %s -f org -t markdown+fenced_divs -o %s"
 8                             (shell-quote-argument source-file)
 9                             (shell-quote-argument output-file))))
10    (if source-file
11        (progn
12          (save-buffer)
13          (shell-command pandoc-cmd)
14          (message "Exported to %s" output-file))
15      (error "Buffer is not associated with a file"))))

While this works for exporting to the same file name, with only the extension changed, using the EXPORT_FILE_NAME ends up trying to export an orgmode block containing the variable, so a bit more effort is needed.

 1(defun export-as-workbench-md ()
 2  "Save, strip the export line via grep, and pipe to Pandoc."
 3  (interactive)
 4  (save-buffer)
 5  (let* ((src (buffer-file-name))
 6         ;; Get the filename from the buffer keywords
 7         (kv  (org-collect-keywords '("EXPORT_FILE_NAME")))
 8         (out (or (cadr (assoc "EXPORT_FILE_NAME" kv))
 9                  (concat (file-name-sans-extension src) ".md")))
10         ;; Grep excludes the line -> Pipe -> Pandoc
11         (cmd (format "grep -vE '^#\\+(EXPORT_FILE_NAME|PROPERTY):' %s | pandoc -f org -t markdown-smart+fenced_divs --shift-heading-level-by=1 -o %s"
12                      (shell-quote-argument src)
13                      (shell-quote-argument out))))
14
15    (shell-command cmd)
16    (message "Exported to %s" out)))

Where the shift heading is because the workbench prefers having ## for all lesson headings.

Along with an additional macro for the challenge headings, so {{{markdown(### Details)}}} with the begin_discussion segments work:

1#+MACRO: markdown @@markdown:$1@@

and a helper to export on save:

1# Local Variables:
2# eval: (add-hook 'after-save-hook 'export-as-workbench-md nil t)
3# End:

Which together, correctly render lessons for Rscript -e "sandpaper::serve()" as seen here in this Python lesson.