{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{- |
   Module      : Text.Pandoc.Writers.Ipynb
   Copyright   : Copyright (C) 2019-2022 John MacFarlane
   License     : GNU GPL, version 2 or above

   Maintainer  : John MacFarlane <jgm@berkeley.edu>
   Stability   : alpha
   Portability : portable

Ipynb (Jupyter notebook JSON format) writer for pandoc.

-}
module Text.Pandoc.Writers.Ipynb ( writeIpynb )
where
import Control.Monad.State
import qualified Data.Map as M
import Data.Maybe (catMaybes, fromMaybe)
import Text.Pandoc.Options
import Text.Pandoc.Definition
import Data.Ipynb as Ipynb
import Text.Pandoc.Walk (walkM)
import qualified Text.Pandoc.Builder as B
import Text.Pandoc.Class.PandocMonad
import Text.Pandoc.Logging
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Data.Aeson as Aeson
import qualified Text.Pandoc.UTF8 as UTF8
import Text.Pandoc.Shared (safeRead, isURI)
import Text.Pandoc.Writers.Shared (metaToContext')
import Text.Pandoc.Writers.Markdown (writeMarkdown)
import qualified Data.Text.Encoding as TE
import qualified Data.ByteString.Lazy as BL
import Data.Aeson.Encode.Pretty (Config(..), defConfig,
           encodePretty', keyOrder, Indent(Spaces))
import Text.DocLayout (literal)
import Text.Pandoc.UUID (getRandomUUID)
import Data.Char (isAscii, isAlphaNum)

writeIpynb :: PandocMonad m => WriterOptions -> Pandoc -> m Text
writeIpynb :: WriterOptions -> Pandoc -> m Text
writeIpynb opts :: WriterOptions
opts d :: Pandoc
d = do
  Notebook NbV4
notebook <- WriterOptions -> Pandoc -> m (Notebook NbV4)
forall (m :: * -> *).
PandocMonad m =>
WriterOptions -> Pandoc -> m (Notebook NbV4)
pandocToNotebook WriterOptions
opts Pandoc
d
  Text -> m Text
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> m Text) -> Text -> m Text
forall a b. (a -> b) -> a -> b
$ ByteString -> Text
TE.decodeUtf8 (ByteString -> Text)
-> (Notebook NbV4 -> ByteString) -> Notebook NbV4 -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
BL.toStrict (ByteString -> ByteString)
-> (Notebook NbV4 -> ByteString) -> Notebook NbV4 -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Config -> Notebook NbV4 -> ByteString
forall a. ToJSON a => Config -> a -> ByteString
encodePretty' Config
defConfig{
             confIndent :: Indent
confIndent  = Int -> Indent
Spaces 1,
             confTrailingNewline :: Bool
confTrailingNewline = Bool
True,
             confCompare :: Text -> Text -> Ordering
confCompare = [Text] -> Text -> Text -> Ordering
keyOrder
               [ "cells", "nbformat", "nbformat_minor",
                 "cell_type", "output_type",
                 "execution_count", "metadata",
                 "outputs", "source",
                 "data", "name", "text" ] (Text -> Text -> Ordering)
-> (Text -> Text -> Ordering) -> Text -> Text -> Ordering
forall a. Semigroup a => a -> a -> a
<> Text -> Text -> Ordering
forall a. Ord a => a -> a -> Ordering
compare }
         (Notebook NbV4 -> Text) -> Notebook NbV4 -> Text
forall a b. (a -> b) -> a -> b
$ Notebook NbV4
notebook

pandocToNotebook :: PandocMonad m
                 => WriterOptions -> Pandoc -> m (Notebook NbV4)
pandocToNotebook :: WriterOptions -> Pandoc -> m (Notebook NbV4)
pandocToNotebook opts :: WriterOptions
opts (Pandoc meta :: Meta
meta blocks :: [Block]
blocks) = do
  let blockWriter :: [Block] -> f (Doc Text)
blockWriter bs :: [Block]
bs = Text -> Doc Text
forall a. HasChars a => a -> Doc a
literal (Text -> Doc Text) -> f Text -> f (Doc Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> WriterOptions -> Pandoc -> f Text
forall (m :: * -> *).
PandocMonad m =>
WriterOptions -> Pandoc -> m Text
writeMarkdown
           WriterOptions
opts{ writerTemplate :: Maybe (Template Text)
writerTemplate = Maybe (Template Text)
forall a. Maybe a
Nothing } (Meta -> [Block] -> Pandoc
Pandoc Meta
nullMeta [Block]
bs)
  let inlineWriter :: [Inline] -> f (Doc Text)
inlineWriter ils :: [Inline]
ils = Text -> Doc Text
forall a. HasChars a => a -> Doc a
literal (Text -> Doc Text) -> (Text -> Text) -> Text -> Doc Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
T.stripEnd (Text -> Doc Text) -> f Text -> f (Doc Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> WriterOptions -> Pandoc -> f Text
forall (m :: * -> *).
PandocMonad m =>
WriterOptions -> Pandoc -> m Text
writeMarkdown
           WriterOptions
opts{ writerTemplate :: Maybe (Template Text)
writerTemplate = Maybe (Template Text)
forall a. Maybe a
Nothing } (Meta -> [Block] -> Pandoc
Pandoc Meta
nullMeta [[Inline] -> Block
Plain [Inline]
ils])
  let jupyterMeta :: Meta
jupyterMeta =
        case Text -> Meta -> Maybe MetaValue
lookupMeta "jupyter" Meta
meta of
          Just (MetaMap m :: Map Text MetaValue
m) -> Map Text MetaValue -> Meta
Meta Map Text MetaValue
m
          _ -> Meta
forall a. Monoid a => a
mempty
  let nbformat :: (Int, Int)
nbformat =
         case (Text -> Meta -> Maybe MetaValue
lookupMeta "nbformat" Meta
jupyterMeta,
               Text -> Meta -> Maybe MetaValue
lookupMeta "nbformat_minor" Meta
jupyterMeta) of
               (Just (MetaInlines [Str "4"]), Just (MetaInlines [Str y :: Text
y])) ->
                 case Text -> Maybe Int
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead Text
y of
                        Just z :: Int
z  -> (4, Int
z)
                        Nothing -> (4, 5)
               _                -> (4, 5) -- write as v4.5
  Value
metadata' <- Context Text -> Value
forall a. ToJSON a => a -> Value
toJSON (Context Text -> Value) -> m (Context Text) -> m Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ([Block] -> m (Doc Text))
-> ([Inline] -> m (Doc Text)) -> Meta -> m (Context Text)
forall (m :: * -> *) a.
(Monad m, TemplateTarget a) =>
([Block] -> m (Doc a))
-> ([Inline] -> m (Doc a)) -> Meta -> m (Context a)
metaToContext' [Block] -> m (Doc Text)
forall (f :: * -> *). PandocMonad f => [Block] -> f (Doc Text)
blockWriter [Inline] -> m (Doc Text)
forall (f :: * -> *). PandocMonad f => [Inline] -> f (Doc Text)
inlineWriter
                 (Text -> Meta -> Meta
forall a. HasMeta a => Text -> a -> a
B.deleteMeta "nbformat" (Meta -> Meta) -> (Meta -> Meta) -> Meta -> Meta
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
                  Text -> Meta -> Meta
forall a. HasMeta a => Text -> a -> a
B.deleteMeta "nbformat_minor" (Meta -> Meta) -> Meta -> Meta
forall a b. (a -> b) -> a -> b
$
                  Meta
jupyterMeta)
  -- convert from a Value (JSON object) to a M.Map Text Value:
  let metadata :: JSONMeta
metadata = case Value -> Result JSONMeta
forall a. FromJSON a => Value -> Result a
fromJSON Value
metadata' of
                   Error _ -> JSONMeta
forall a. Monoid a => a
mempty -- TODO warning here? shouldn't happen
                   Success x :: JSONMeta
x -> JSONMeta
x
  [Cell NbV4]
cells <- (Int, Int) -> WriterOptions -> [Block] -> m [Cell NbV4]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts [Block]
blocks
  Notebook NbV4 -> m (Notebook NbV4)
forall (m :: * -> *) a. Monad m => a -> m a
return (Notebook NbV4 -> m (Notebook NbV4))
-> Notebook NbV4 -> m (Notebook NbV4)
forall a b. (a -> b) -> a -> b
$ Notebook :: forall a. JSONMeta -> (Int, Int) -> [Cell a] -> Notebook a
Notebook{
       notebookMetadata :: JSONMeta
notebookMetadata = JSONMeta
metadata
     , notebookFormat :: (Int, Int)
notebookFormat = (Int, Int)
nbformat
     , notebookCells :: [Cell NbV4]
notebookCells = [Cell NbV4]
cells }

addAttachment :: PandocMonad m
              => Inline
              -> StateT (M.Map Text MimeBundle) m Inline
addAttachment :: Inline -> StateT (Map Text MimeBundle) m Inline
addAttachment (Image attr :: Attr
attr lab :: [Inline]
lab (src :: Text
src,tit :: Text
tit))
  | Bool -> Bool
not (Text -> Bool
isURI Text
src) = do
  (img :: ByteString
img, mbmt :: Maybe Text
mbmt) <- Text -> StateT (Map Text MimeBundle) m (ByteString, Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
Text -> m (ByteString, Maybe Text)
fetchItem Text
src
  let mt :: Text
mt = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe "application/octet-stream" Maybe Text
mbmt
  (Map Text MimeBundle -> Map Text MimeBundle)
-> StateT (Map Text MimeBundle) m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Map Text MimeBundle -> Map Text MimeBundle)
 -> StateT (Map Text MimeBundle) m ())
-> (Map Text MimeBundle -> Map Text MimeBundle)
-> StateT (Map Text MimeBundle) m ()
forall a b. (a -> b) -> a -> b
$ Text -> MimeBundle -> Map Text MimeBundle -> Map Text MimeBundle
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Text
src
          (Map Text MimeData -> MimeBundle
MimeBundle (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Text
mt (ByteString -> MimeData
BinaryData ByteString
img) Map Text MimeData
forall a. Monoid a => a
mempty))
  Inline -> StateT (Map Text MimeBundle) m Inline
forall (m :: * -> *) a. Monad m => a -> m a
return (Inline -> StateT (Map Text MimeBundle) m Inline)
-> Inline -> StateT (Map Text MimeBundle) m Inline
forall a b. (a -> b) -> a -> b
$ Attr -> [Inline] -> Target -> Inline
Image Attr
attr [Inline]
lab ("attachment:" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
src, Text
tit)
addAttachment x :: Inline
x = Inline -> StateT (Map Text MimeBundle) m Inline
forall (m :: * -> *) a. Monad m => a -> m a
return Inline
x

extractCells :: PandocMonad m
             => (Int, Int) -> WriterOptions -> [Block] -> m [Ipynb.Cell a]
extractCells :: (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells _ _ [] = [Cell a] -> m [Cell a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
extractCells nbformat :: (Int, Int)
nbformat opts :: WriterOptions
opts (Div (ident :: Text
ident,classes :: [Text]
classes,kvs :: [Target]
kvs) xs :: [Block]
xs : bs :: [Block]
bs)
  | "cell" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes
  , "markdown" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes = do
      let meta :: JSONMeta
meta = [Target] -> JSONMeta
pairsToJSONMeta [Target]
kvs
      (newdoc :: Pandoc
newdoc, attachments :: Map Text MimeBundle
attachments) <-
        StateT (Map Text MimeBundle) m Pandoc
-> Map Text MimeBundle -> m (Pandoc, Map Text MimeBundle)
forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
runStateT ((Inline -> StateT (Map Text MimeBundle) m Inline)
-> Pandoc -> StateT (Map Text MimeBundle) m Pandoc
forall a b (m :: * -> *).
(Walkable a b, Monad m, Applicative m, Functor m) =>
(a -> m a) -> b -> m b
walkM Inline -> StateT (Map Text MimeBundle) m Inline
forall (m :: * -> *).
PandocMonad m =>
Inline -> StateT (Map Text MimeBundle) m Inline
addAttachment (Meta -> [Block] -> Pandoc
Pandoc Meta
nullMeta [Block]
xs)) Map Text MimeBundle
forall a. Monoid a => a
mempty
      Text
source <- WriterOptions -> Pandoc -> m Text
forall (m :: * -> *).
PandocMonad m =>
WriterOptions -> Pandoc -> m Text
writeMarkdown WriterOptions
opts{ writerTemplate :: Maybe (Template Text)
writerTemplate = Maybe (Template Text)
forall a. Maybe a
Nothing } Pandoc
newdoc
      Maybe Text
uuid <- (Int, Int) -> Text -> m (Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
(Int, Int) -> Text -> m (Maybe Text)
uuidFrom (Int, Int)
nbformat Text
ident
      (Cell :: forall a.
CellType a
-> Maybe Text
-> Source
-> JSONMeta
-> Maybe MimeAttachments
-> Cell a
Ipynb.Cell{
          cellType :: CellType a
cellType = CellType a
forall a. CellType a
Markdown
        , cellId :: Maybe Text
cellId = Maybe Text
uuid
        , cellSource :: Source
cellSource = [Text] -> Source
Source ([Text] -> Source) -> [Text] -> Source
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
breakLines (Text -> [Text]) -> Text -> [Text]
forall a b. (a -> b) -> a -> b
$ Text -> Text
T.stripEnd Text
source
        , cellMetadata :: JSONMeta
cellMetadata = JSONMeta
meta
        , cellAttachments :: Maybe MimeAttachments
cellAttachments = if Map Text MimeBundle -> Bool
forall k a. Map k a -> Bool
M.null Map Text MimeBundle
attachments
                               then Maybe MimeAttachments
forall a. Maybe a
Nothing
                               else MimeAttachments -> Maybe MimeAttachments
forall a. a -> Maybe a
Just (MimeAttachments -> Maybe MimeAttachments)
-> MimeAttachments -> Maybe MimeAttachments
forall a b. (a -> b) -> a -> b
$ Map Text MimeBundle -> MimeAttachments
MimeAttachments Map Text MimeBundle
attachments } Cell a -> [Cell a] -> [Cell a]
forall a. a -> [a] -> [a]
:)
            ([Cell a] -> [Cell a]) -> m [Cell a] -> m [Cell a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts [Block]
bs
  | "cell" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes
  , "code" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes = do
      let (codeContent :: Text
codeContent, rest :: [Block]
rest) =
            case [Block]
xs of
               (CodeBlock _ t :: Text
t : ys :: [Block]
ys) -> (Text
t, [Block]
ys)
               ys :: [Block]
ys                   -> (Text
forall a. Monoid a => a
mempty, [Block]
ys)
      let meta :: JSONMeta
meta = [Target] -> JSONMeta
pairsToJSONMeta [Target]
kvs
      [Output a]
outputs <- [Maybe (Output a)] -> [Output a]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe (Output a)] -> [Output a])
-> m [Maybe (Output a)] -> m [Output a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Block -> m (Maybe (Output a))) -> [Block] -> m [Maybe (Output a)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Block -> m (Maybe (Output a))
forall (m :: * -> *) a.
PandocMonad m =>
Block -> m (Maybe (Output a))
blockToOutput [Block]
rest
      let exeCount :: Maybe Int
exeCount = Text -> [Target] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup "execution_count" [Target]
kvs Maybe Text -> (Text -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Int
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead
      Maybe Text
uuid <- (Int, Int) -> Text -> m (Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
(Int, Int) -> Text -> m (Maybe Text)
uuidFrom (Int, Int)
nbformat Text
ident
      (Cell :: forall a.
CellType a
-> Maybe Text
-> Source
-> JSONMeta
-> Maybe MimeAttachments
-> Cell a
Ipynb.Cell{
          cellType :: CellType a
cellType = Code :: forall a. Maybe Int -> [Output a] -> CellType a
Ipynb.Code {
                codeExecutionCount :: Maybe Int
codeExecutionCount = Maybe Int
exeCount
              , codeOutputs :: [Output a]
codeOutputs = [Output a]
outputs
              }
        , cellId :: Maybe Text
cellId = Maybe Text
uuid
        , cellSource :: Source
cellSource = [Text] -> Source
Source ([Text] -> Source) -> [Text] -> Source
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
breakLines Text
codeContent
        , cellMetadata :: JSONMeta
cellMetadata = JSONMeta
meta
        , cellAttachments :: Maybe MimeAttachments
cellAttachments = Maybe MimeAttachments
forall a. Maybe a
Nothing } Cell a -> [Cell a] -> [Cell a]
forall a. a -> [a] -> [a]
:) ([Cell a] -> [Cell a]) -> m [Cell a] -> m [Cell a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts [Block]
bs
  | "cell" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes
  , "raw" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes =
      case [Block] -> [Block]
consolidateAdjacentRawBlocks [Block]
xs of
        [RawBlock (Format f :: Text
f) raw :: Text
raw] -> do
          let format' :: Text
format' =
                case Text -> Text
T.toLower Text
f of
                  "html"     -> "text/html"
                  "html4"    -> "text/html"
                  "html5"    -> "text/html"
                  "s5"       -> "text/html"
                  "slidy"    -> "text/html"
                  "slideous" -> "text/html"
                  "dzslides" -> "text/html"
                  "revealjs" -> "text/html"
                  "latex"    -> "text/latex"
                  "markdown" -> "text/markdown"
                  "rst"      -> "text/restructuredtext"
                  "asciidoc" -> "text/asciidoc"
                  _          -> Text
f
          Maybe Text
uuid <- (Int, Int) -> Text -> m (Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
(Int, Int) -> Text -> m (Maybe Text)
uuidFrom (Int, Int)
nbformat Text
ident
          (Cell :: forall a.
CellType a
-> Maybe Text
-> Source
-> JSONMeta
-> Maybe MimeAttachments
-> Cell a
Ipynb.Cell{
              cellType :: CellType a
cellType = CellType a
forall a. CellType a
Raw
            , cellId :: Maybe Text
cellId = Maybe Text
uuid
            , cellSource :: Source
cellSource = [Text] -> Source
Source ([Text] -> Source) -> [Text] -> Source
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
breakLines Text
raw
            , cellMetadata :: JSONMeta
cellMetadata = if Text
format' Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== "ipynb" -- means no format given
                                then JSONMeta
forall a. Monoid a => a
mempty
                                else Map Text Value -> JSONMeta
JSONMeta (Map Text Value -> JSONMeta) -> Map Text Value -> JSONMeta
forall a b. (a -> b) -> a -> b
$ Text -> Value -> Map Text Value -> Map Text Value
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert "raw_mimetype"
                                       (Text -> Value
Aeson.String Text
format') Map Text Value
forall a. Monoid a => a
mempty
            , cellAttachments :: Maybe MimeAttachments
cellAttachments = Maybe MimeAttachments
forall a. Maybe a
Nothing } Cell a -> [Cell a] -> [Cell a]
forall a. a -> [a] -> [a]
:) ([Cell a] -> [Cell a]) -> m [Cell a] -> m [Cell a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts [Block]
bs
        _ -> (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts [Block]
bs
extractCells nbformat :: (Int, Int)
nbformat opts :: WriterOptions
opts (CodeBlock (ident :: Text
ident,classes :: [Text]
classes,kvs :: [Target]
kvs) raw :: Text
raw : bs :: [Block]
bs)
  | "code" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes = do
      let meta :: JSONMeta
meta = [Target] -> JSONMeta
pairsToJSONMeta [Target]
kvs
      let exeCount :: Maybe Int
exeCount = Text -> [Target] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup "execution_count" [Target]
kvs Maybe Text -> (Text -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Int
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead
      Maybe Text
uuid <- (Int, Int) -> Text -> m (Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
(Int, Int) -> Text -> m (Maybe Text)
uuidFrom (Int, Int)
nbformat Text
ident
      (Cell :: forall a.
CellType a
-> Maybe Text
-> Source
-> JSONMeta
-> Maybe MimeAttachments
-> Cell a
Ipynb.Cell{
          cellType :: CellType a
cellType = Code :: forall a. Maybe Int -> [Output a] -> CellType a
Ipynb.Code {
                codeExecutionCount :: Maybe Int
codeExecutionCount = Maybe Int
exeCount
              , codeOutputs :: [Output a]
codeOutputs = []
              }
        , cellId :: Maybe Text
cellId = Maybe Text
uuid
        , cellSource :: Source
cellSource = [Text] -> Source
Source ([Text] -> Source) -> [Text] -> Source
forall a b. (a -> b) -> a -> b
$ Text -> [Text]
breakLines Text
raw
        , cellMetadata :: JSONMeta
cellMetadata = JSONMeta
meta
        , cellAttachments :: Maybe MimeAttachments
cellAttachments = Maybe MimeAttachments
forall a. Maybe a
Nothing } Cell a -> [Cell a] -> [Cell a]
forall a. a -> [a] -> [a]
:) ([Cell a] -> [Cell a]) -> m [Cell a] -> m [Cell a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts [Block]
bs
extractCells nbformat :: (Int, Int)
nbformat opts :: WriterOptions
opts (b :: Block
b:bs :: [Block]
bs) = do
      let isCodeOrDiv :: Block -> Bool
isCodeOrDiv (CodeBlock (_,cl :: [Text]
cl,_) _) = "code" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
cl
          isCodeOrDiv (Div (_,cl :: [Text]
cl,_) _)       = "cell" Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
cl
          isCodeOrDiv _                      = Bool
False
      let (mds :: [Block]
mds, rest :: [Block]
rest) = (Block -> Bool) -> [Block] -> ([Block], [Block])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Block -> Bool
isCodeOrDiv [Block]
bs
      (Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
forall (m :: * -> *) a.
PandocMonad m =>
(Int, Int) -> WriterOptions -> [Block] -> m [Cell a]
extractCells (Int, Int)
nbformat WriterOptions
opts
        (Attr -> [Block] -> Block
Div ("",["cell","markdown"],[]) (Block
bBlock -> [Block] -> [Block]
forall a. a -> [a] -> [a]
:[Block]
mds) Block -> [Block] -> [Block]
forall a. a -> [a] -> [a]
: [Block]
rest)

-- Return Nothing if nbformat < 4.5.
-- Otherwise construct a UUID, using the existing identifier
-- if it is a valid UUID, otherwise constructing a new one.
uuidFrom :: PandocMonad m => (Int, Int) -> Text -> m (Maybe Text)
uuidFrom :: (Int, Int) -> Text -> m (Maybe Text)
uuidFrom nbformat :: (Int, Int)
nbformat ident :: Text
ident =
  if (Int, Int)
nbformat (Int, Int) -> (Int, Int) -> Bool
forall a. Ord a => a -> a -> Bool
>= (4,5)
     then
       if Text -> Bool
isValidUUID Text
ident
          then Maybe Text -> m (Maybe Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Text -> m (Maybe Text)) -> Maybe Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text
forall a. a -> Maybe a
Just Text
ident
          else Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> (UUID -> Text) -> UUID -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack (String -> Text) -> (UUID -> String) -> UUID -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String -> String
forall a. Int -> [a] -> [a]
drop 9 (String -> String) -> (UUID -> String) -> UUID -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UUID -> String
forall a. Show a => a -> String
show (UUID -> Maybe Text) -> m UUID -> m (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m UUID
forall (m :: * -> *). PandocMonad m => m UUID
getRandomUUID
     else Maybe Text -> m (Maybe Text)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Text
forall a. Maybe a
Nothing
 where
  isValidUUID :: Text -> Bool
isValidUUID t :: Text
t = Bool -> Bool
not (Text -> Bool
T.null Text
t) Bool -> Bool -> Bool
&& Text -> Int
T.length Text
t Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= 64 Bool -> Bool -> Bool
&&
                  (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isValidUUIDChar Text
t
  isValidUUIDChar :: Char -> Bool
isValidUUIDChar c :: Char
c = Char -> Bool
isAscii Char
c Bool -> Bool -> Bool
&& (Char -> Bool
isAlphaNum Char
c Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '-' Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '_')

blockToOutput :: PandocMonad m => Block -> m (Maybe (Output a))
blockToOutput :: Block -> m (Maybe (Output a))
blockToOutput (Div (_,["output","stream",sname :: Text
sname],_) (CodeBlock _ t :: Text
t:_)) =
  Maybe (Output a) -> m (Maybe (Output a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Output a) -> m (Maybe (Output a)))
-> Maybe (Output a) -> m (Maybe (Output a))
forall a b. (a -> b) -> a -> b
$ Output a -> Maybe (Output a)
forall a. a -> Maybe a
Just
         (Output a -> Maybe (Output a)) -> Output a -> Maybe (Output a)
forall a b. (a -> b) -> a -> b
$ Stream :: forall a. Text -> Source -> Output a
Stream{ streamName :: Text
streamName = Text
sname
               , streamText :: Source
streamText = [Text] -> Source
Source (Text -> [Text]
breakLines Text
t) }
blockToOutput (Div (_,["output","error"],kvs :: [Target]
kvs) (CodeBlock _ t :: Text
t:_)) =
  Maybe (Output a) -> m (Maybe (Output a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Output a) -> m (Maybe (Output a)))
-> Maybe (Output a) -> m (Maybe (Output a))
forall a b. (a -> b) -> a -> b
$ Output a -> Maybe (Output a)
forall a. a -> Maybe a
Just
         (Output a -> Maybe (Output a)) -> Output a -> Maybe (Output a)
forall a b. (a -> b) -> a -> b
$ Err :: forall a. Text -> Text -> [Text] -> Output a
Err{ errName :: Text
errName = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
forall a. Monoid a => a
mempty (Text -> [Target] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup "ename" [Target]
kvs)
              , errValue :: Text
errValue = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
forall a. Monoid a => a
mempty (Text -> [Target] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup "evalue" [Target]
kvs)
              , errTraceback :: [Text]
errTraceback = Text -> [Text]
breakLines Text
t }
blockToOutput (Div (_,["output","execute_result"],kvs :: [Target]
kvs) bs :: [Block]
bs) = do
  (data' :: MimeBundle
data', metadata' :: JSONMeta
metadata') <- [Block] -> m (MimeBundle, JSONMeta)
forall (m :: * -> *).
PandocMonad m =>
[Block] -> m (MimeBundle, JSONMeta)
extractData [Block]
bs
  Maybe (Output a) -> m (Maybe (Output a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Output a) -> m (Maybe (Output a)))
-> Maybe (Output a) -> m (Maybe (Output a))
forall a b. (a -> b) -> a -> b
$ Output a -> Maybe (Output a)
forall a. a -> Maybe a
Just
         (Output a -> Maybe (Output a)) -> Output a -> Maybe (Output a)
forall a b. (a -> b) -> a -> b
$ ExecuteResult :: forall a. Int -> MimeBundle -> JSONMeta -> Output a
ExecuteResult{ executeCount :: Int
executeCount = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe 0 (Maybe Int -> Int) -> Maybe Int -> Int
forall a b. (a -> b) -> a -> b
$
                          Text -> [Target] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup "execution_count" [Target]
kvs Maybe Text -> (Text -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Text -> Maybe Int
forall (m :: * -> *) a. (MonadPlus m, Read a) => Text -> m a
safeRead
                        , executeData :: MimeBundle
executeData = MimeBundle
data'
                        , executeMetadata :: JSONMeta
executeMetadata = [Target] -> JSONMeta
pairsToJSONMeta [Target]
kvs JSONMeta -> JSONMeta -> JSONMeta
forall a. Semigroup a => a -> a -> a
<> JSONMeta
metadata'}
blockToOutput (Div (_,["output","display_data"],kvs :: [Target]
kvs) bs :: [Block]
bs) = do
  (data' :: MimeBundle
data', metadata' :: JSONMeta
metadata') <- [Block] -> m (MimeBundle, JSONMeta)
forall (m :: * -> *).
PandocMonad m =>
[Block] -> m (MimeBundle, JSONMeta)
extractData [Block]
bs
  Maybe (Output a) -> m (Maybe (Output a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Output a) -> m (Maybe (Output a)))
-> Maybe (Output a) -> m (Maybe (Output a))
forall a b. (a -> b) -> a -> b
$ Output a -> Maybe (Output a)
forall a. a -> Maybe a
Just
         (Output a -> Maybe (Output a)) -> Output a -> Maybe (Output a)
forall a b. (a -> b) -> a -> b
$ DisplayData :: forall a. MimeBundle -> JSONMeta -> Output a
DisplayData { displayData :: MimeBundle
displayData = MimeBundle
data'
                       , displayMetadata :: JSONMeta
displayMetadata = [Target] -> JSONMeta
pairsToJSONMeta [Target]
kvs JSONMeta -> JSONMeta -> JSONMeta
forall a. Semigroup a => a -> a -> a
<> JSONMeta
metadata'}
blockToOutput _ = Maybe (Output a) -> m (Maybe (Output a))
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (Output a)
forall a. Maybe a
Nothing

extractData :: PandocMonad m => [Block] -> m (MimeBundle, JSONMeta)
extractData :: [Block] -> m (MimeBundle, JSONMeta)
extractData bs :: [Block]
bs = do
  (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) <- ((Map Text MimeData, JSONMeta)
 -> Block -> m (Map Text MimeData, JSONMeta))
-> (Map Text MimeData, JSONMeta)
-> [Block]
-> m (Map Text MimeData, JSONMeta)
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM (Map Text MimeData, JSONMeta)
-> Block -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *).
PandocMonad m =>
(Map Text MimeData, JSONMeta)
-> Block -> m (Map Text MimeData, JSONMeta)
go (Map Text MimeData, JSONMeta)
forall a. Monoid a => a
mempty ([Block] -> m (Map Text MimeData, JSONMeta))
-> [Block] -> m (Map Text MimeData, JSONMeta)
forall a b. (a -> b) -> a -> b
$ [Block] -> [Block]
consolidateAdjacentRawBlocks [Block]
bs
  (MimeBundle, JSONMeta) -> m (MimeBundle, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return (Map Text MimeData -> MimeBundle
MimeBundle Map Text MimeData
mmap, JSONMeta
meta)
  where
    go :: (Map Text MimeData, JSONMeta)
-> Block -> m (Map Text MimeData, JSONMeta)
go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) b :: Block
b@(Para [Image (_,_,kvs :: [Target]
kvs) _ (src :: Text
src,_)]) = do
      (img :: ByteString
img, mbmt :: Maybe Text
mbmt) <- Text -> m (ByteString, Maybe Text)
forall (m :: * -> *).
PandocMonad m =>
Text -> m (ByteString, Maybe Text)
fetchItem Text
src
      case Maybe Text
mbmt of
        Just mt :: Text
mt -> (Map Text MimeData, JSONMeta) -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return
          (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Text
mt (ByteString -> MimeData
BinaryData ByteString
img) Map Text MimeData
mmap,
           JSONMeta
meta JSONMeta -> JSONMeta -> JSONMeta
forall a. Semigroup a => a -> a -> a
<> [Target] -> JSONMeta
pairsToJSONMeta [Target]
kvs)
        Nothing -> (Map Text MimeData
mmap, JSONMeta
meta) (Map Text MimeData, JSONMeta)
-> m () -> m (Map Text MimeData, JSONMeta)
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (Block -> LogMessage
BlockNotRendered Block
b)
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) b :: Block
b@(CodeBlock (_,["json"],_) code :: Text
code) =
      case ByteString -> Maybe Value
forall a. FromJSON a => ByteString -> Maybe a
decode (Text -> ByteString
UTF8.fromTextLazy (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Text -> Text
TL.fromStrict Text
code) of
        Just v :: Value
v  -> (Map Text MimeData, JSONMeta) -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return
                    (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert "application/json" (Value -> MimeData
JsonData Value
v) Map Text MimeData
mmap, JSONMeta
meta)
        Nothing -> (Map Text MimeData
mmap, JSONMeta
meta) (Map Text MimeData, JSONMeta)
-> m () -> m (Map Text MimeData, JSONMeta)
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (Block -> LogMessage
BlockNotRendered Block
b)
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) (CodeBlock ("",[],[]) code :: Text
code) =
       (Map Text MimeData, JSONMeta) -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert "text/plain" (Text -> MimeData
TextualData Text
code) Map Text MimeData
mmap, JSONMeta
meta)
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) (RawBlock (Format "html") raw :: Text
raw) =
       (Map Text MimeData, JSONMeta) -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert "text/html" (Text -> MimeData
TextualData Text
raw) Map Text MimeData
mmap, JSONMeta
meta)
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) (RawBlock (Format "latex") raw :: Text
raw) =
       (Map Text MimeData, JSONMeta) -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert "text/latex" (Text -> MimeData
TextualData Text
raw) Map Text MimeData
mmap, JSONMeta
meta)
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) (RawBlock (Format "markdown") raw :: Text
raw) =
       (Map Text MimeData, JSONMeta) -> m (Map Text MimeData, JSONMeta)
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> MimeData -> Map Text MimeData -> Map Text MimeData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert "text/markdown" (Text -> MimeData
TextualData Text
raw) Map Text MimeData
mmap, JSONMeta
meta)
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) (Div _ bs' :: [Block]
bs') = ((Map Text MimeData, JSONMeta)
 -> Block -> m (Map Text MimeData, JSONMeta))
-> (Map Text MimeData, JSONMeta)
-> [Block]
-> m (Map Text MimeData, JSONMeta)
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM (Map Text MimeData, JSONMeta)
-> Block -> m (Map Text MimeData, JSONMeta)
go (Map Text MimeData
mmap, JSONMeta
meta) [Block]
bs'
    go (mmap :: Map Text MimeData
mmap, meta :: JSONMeta
meta) b :: Block
b = (Map Text MimeData
mmap, JSONMeta
meta) (Map Text MimeData, JSONMeta)
-> m () -> m (Map Text MimeData, JSONMeta)
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ LogMessage -> m ()
forall (m :: * -> *). PandocMonad m => LogMessage -> m ()
report (Block -> LogMessage
BlockNotRendered Block
b)

pairsToJSONMeta :: [(Text, Text)] -> JSONMeta
pairsToJSONMeta :: [Target] -> JSONMeta
pairsToJSONMeta kvs :: [Target]
kvs = Map Text Value -> JSONMeta
JSONMeta (Map Text Value -> JSONMeta) -> Map Text Value -> JSONMeta
forall a b. (a -> b) -> a -> b
$
  [(Text, Value)] -> Map Text Value
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(Text
k, case ByteString -> Maybe Value
forall a. FromJSON a => ByteString -> Maybe a
Aeson.decode (Text -> ByteString
UTF8.fromTextLazy (Text -> ByteString) -> Text -> ByteString
forall a b. (a -> b) -> a -> b
$ Text -> Text
TL.fromStrict Text
v) of
                           Just val :: Value
val -> Value
val
                           Nothing  -> Text -> Value
String Text
v)
             | (k :: Text
k,v :: Text
v) <- [Target]
kvs
             , Text
k Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= "execution_count"
             ]

consolidateAdjacentRawBlocks :: [Block] -> [Block]
consolidateAdjacentRawBlocks :: [Block] -> [Block]
consolidateAdjacentRawBlocks [] = []
consolidateAdjacentRawBlocks (RawBlock f1 :: Format
f1 x :: Text
x : RawBlock f2 :: Format
f2 y :: Text
y : xs :: [Block]
xs)
  | Format
f1 Format -> Format -> Bool
forall a. Eq a => a -> a -> Bool
== Format
f2
  = [Block] -> [Block]
consolidateAdjacentRawBlocks (Format -> Text -> Block
RawBlock Format
f1 (Text
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> "\n" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
y) Block -> [Block] -> [Block]
forall a. a -> [a] -> [a]
: [Block]
xs)
consolidateAdjacentRawBlocks (x :: Block
x : xs :: [Block]
xs) =
  Block
x Block -> [Block] -> [Block]
forall a. a -> [a] -> [a]
: [Block] -> [Block]
consolidateAdjacentRawBlocks [Block]
xs