Literate.jl icon indicating copy to clipboard operation
Literate.jl copied to clipboard

Feature request: unliterate()

Open PaulSoderlind opened this issue 4 years ago • 7 comments

Just so this (from Julia Discourse) does not get lost: have you considered adding an unliterate()?

PaulSoderlind avatar May 04 '20 08:05 PaulSoderlind

from your reply on Discourse: See also https://github.com/oxinabox/ipynb2LiterateJulia

PaulSoderlind avatar May 04 '20 10:05 PaulSoderlind

Just pasting a link to the referenced discourse post: https://discourse.julialang.org/t/way-to-add-tests-that-utilize-jupyter-notebooks/38341/6

fredrikekre avatar Feb 21 '21 11:02 fredrikekre

Maybe also relevant: ipynb2LiterateJulia does not work with the new nbconvert, see https://github.com/oxinabox/ipynb2LiterateJulia/issues/8

PaulSoderlind avatar Feb 21 '21 13:02 PaulSoderlind

To soothe my mind (after a certain cross country relay) I attempted a rewrite of the archived JuliaAcademy material. The simple code below actually allows for a roundtrip (Literate.notebook(),unliterate(),...) without changing anything. Since my notebooks do not contain any important metadata, I did not implement anything for that.

"""
    unliterate(input,outputdir=pwd())

Convert a ipynb notebook to a jl script. Based on code from the Julia Academy, but simplified.
If input is a directory, then all notebooks in that directly are converted.

"""
function unliterate(input,outputdir=pwd())

  !isdir(outputdir) && error("$outputdir is not a directory")

  if isdir(input)
    Files = filter(x->splitext(x)[2]==".ipynb", readdir(input,join=true))  #all .ipynb files
    for file in Files
      unliterate(file,outputdir)
    end
    return
  end

  outputfile = joinpath(outputdir, splitext(basename(input))[1] * ".jl")
  println(input,"\n",outputfile,"\n")

  nb = open(JSON.parse,input, "r")
  ioscript = IOBuffer()

  prev_type = ""
  for cell in nb["cells"]

    cell["cell_type"] == prev_type && print(ioscript, "#-\n\n")

    #if !isempty(cell["metadata"])
    #  println(cell["metadata"])
    #  @warn("Still not implemented. Use parse_nbmeta here?")
    #end

    if cell["cell_type"] == "markdown"
      for line in cell["source"]
        print(ioscript, "# ", line)
      end
    elseif cell["cell_type"] == "code"
      for line in cell["source"]
        line_i = lstrip(line)
        if startswith(line_i, "#") && !startswith(line_i, "##")
          print(ioscript, "#", line_i)
        else
          print(ioscript, line)
        end
      end
    end

    print(ioscript,"\n\n")
    prev_type = cell["cell_type"]

  end

  content = String(take!(ioscript))
  open(outputfile, "w") do io
    print(io, content)
  end

  return

end
#------------------------------------------------------------------------------


PaulSoderlind avatar Mar 04 '21 15:03 PaulSoderlind

and from stevengj's answer at discourse (https://discourse.julialang.org/t/export-jupyter-to-jl-does-not-preserve-markdown-cells/57556)

using JSON, Markdown

function ipynb2jl(ipynfile)
    jlfile = replace(ipynfile, r"(\.ipynb)?$" => ".jl")
    nb = open(JSON.parse, ipynfile, "r")
    open(jlfile, "w") do f
        for cell in nb["cells"]
            if cell["cell_type"] == "code"
                print(f, "\n\n")
                print.(Ref(f), cell["source"])
            elseif cell["cell_type"] == "markdown"
                md = Markdown.parse(join(cell["source"]))
                print(f, "\n\n# ", replace(repr("text/plain", md), '\n' => "\n# "))
            end
        end
    end
end

PaulSoderlind avatar Mar 21 '21 09:03 PaulSoderlind

@PaulSoderlind thanks a million, I just found this - you've saved me a lot of hassle.

miguelraz avatar Apr 18 '21 00:04 miguelraz

The package NBInclude provides a function nbexport(filename, notebookfile) that generates a .jl from a .pynb.

kbarros avatar Oct 24 '22 22:10 kbarros