{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
module Data.JuPyTer where
import System.Exit (exitFailure, exitSuccess)
import System.IO (stderr, hPutStrLn)
import qualified Data.ByteString.Lazy.Char8 as BSL
import System.Environment (getArgs)
import Control.Monad (forM_, mzero, join)
import Control.Applicative
import Data.Aeson.AutoType.Alternative
import Data.Aeson(decode, Value(..), FromJSON(..), ToJSON(..),
pairs,
(.:), (.:?), (.=), object)
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as Text
import qualified GHC.Generics
-- | Workaround for https://github.com/bos/aeson/issues/287.
o .:?? val = fmap join (o .:? val)
data CodemirrorMode = CodemirrorMode {
codemirrorModeName :: Text,
codemirrorModeVersion :: Double
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON CodemirrorMode where
parseJSON (Object v) = CodemirrorMode <$> v .: "name" <*> v .: "version"
parseJSON _ = mzero
instance ToJSON CodemirrorMode where
toJSON (CodemirrorMode {..}) = object ["name" .= codemirrorModeName, "version" .= codemirrorModeVersion]
toEncoding (CodemirrorMode {..}) = pairs ("name" .= codemirrorModeName<>"version" .= codemirrorModeVersion)
data LanguageInfo = LanguageInfo {
languageInfoNbconvertExporter :: Text,
languageInfoFileExtension :: Text,
languageInfoPygmentsLexer :: Text,
languageInfoMimetype :: Text,
languageInfoName :: Text,
languageInfoVersion :: Text,
languageInfoCodemirrorMode :: CodemirrorMode
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON LanguageInfo where
parseJSON (Object v) = LanguageInfo <$> v .: "nbconvert_exporter" <*> v .: "file_extension" <*> v .: "pygments_lexer" <*> v .: "mimetype" <*> v .: "name" <*> v .: "version" <*> v .: "codemirror_mode"
parseJSON _ = mzero
instance ToJSON LanguageInfo where
toJSON (LanguageInfo {..}) = object ["nbconvert_exporter" .= languageInfoNbconvertExporter, "file_extension" .= languageInfoFileExtension, "pygments_lexer" .= languageInfoPygmentsLexer, "mimetype" .= languageInfoMimetype, "name" .= languageInfoName, "version" .= languageInfoVersion, "codemirror_mode" .= languageInfoCodemirrorMode]
toEncoding (LanguageInfo {..}) = pairs ("nbconvert_exporter" .= languageInfoNbconvertExporter<>"file_extension" .= languageInfoFileExtension<>"pygments_lexer" .= languageInfoPygmentsLexer<>"mimetype" .= languageInfoMimetype<>"name" .= languageInfoName<>"version" .= languageInfoVersion<>"codemirror_mode" .= languageInfoCodemirrorMode)
data Kernelspec = Kernelspec {
kernelspecDisplayName :: Text,
kernelspecName :: Text,
kernelspecLanguage :: Text
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON Kernelspec where
parseJSON (Object v) = Kernelspec <$> v .: "display_name" <*> v .: "name" <*> v .: "language"
parseJSON _ = mzero
instance ToJSON Kernelspec where
toJSON (Kernelspec {..}) = object ["display_name" .= kernelspecDisplayName, "name" .= kernelspecName, "language" .= kernelspecLanguage]
toEncoding (Kernelspec {..}) = pairs ("display_name" .= kernelspecDisplayName<>"name" .= kernelspecName<>"language" .= kernelspecLanguage)
data Slideshow = Slideshow {
slideshowSlideType :: Text
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON Slideshow where
parseJSON (Object v) = Slideshow <$> v .: "slide_type"
parseJSON _ = mzero
instance ToJSON Slideshow where
toJSON (Slideshow {..}) = object ["slide_type" .= slideshowSlideType]
toEncoding (Slideshow {..}) = pairs ("slide_type" .= slideshowSlideType)
data Metadata = Metadata {
metadataScrolled :: (Maybe (Bool:|:[(Maybe Value)])),
metadataCelltoolbar :: (Maybe (Text:|:[(Maybe Value)])),
metadataLanguageInfo :: (Maybe (LanguageInfo:|:[(Maybe Value)])),
metadataKernelspec :: (Maybe (Kernelspec:|:[(Maybe Value)])),
metadataEditable :: (Maybe (Bool:|:[(Maybe Value)])),
metadataDeletable :: (Maybe (Bool:|:[(Maybe Value)])),
metadataSlideshow :: (Maybe (Slideshow:|:[(Maybe Value)])),
metadataCollapsed :: (Maybe (Bool:|:[(Maybe Value)]))
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON Metadata where
parseJSON (Object v) = Metadata <$> v .:?? "scrolled" <*> v .:?? "celltoolbar" <*> v .:?? "language_info" <*> v .:?? "kernelspec" <*> v .:?? "editable" <*> v .:?? "deletable" <*> v .:?? "slideshow" <*> v .:?? "collapsed"
parseJSON _ = mzero
instance ToJSON Metadata where
toJSON (Metadata {..}) = object ["scrolled" .= metadataScrolled, "celltoolbar" .= metadataCelltoolbar, "language_info" .= metadataLanguageInfo, "kernelspec" .= metadataKernelspec, "editable" .= metadataEditable, "deletable" .= metadataDeletable, "slideshow" .= metadataSlideshow, "collapsed" .= metadataCollapsed]
toEncoding (Metadata {..}) = pairs ("scrolled" .= metadataScrolled<>"celltoolbar" .= metadataCelltoolbar<>"language_info" .= metadataLanguageInfo<>"kernelspec" .= metadataKernelspec<>"editable" .= metadataEditable<>"deletable" .= metadataDeletable<>"slideshow" .= metadataSlideshow<>"collapsed" .= metadataCollapsed)
data CellsElt = CellsElt {
cellsEltExecutionCount :: (Maybe (Double:|:[(Maybe Value)])),
cellsEltOutputs :: (Maybe ([[(Maybe Value)]])),
cellsEltMetadata :: Metadata,
cellsEltSource :: [Text:|:[(Maybe Value)]],
cellsEltCellType :: Text
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON CellsElt where
parseJSON (Object v) = CellsElt <$> v .:?? "execution_count" <*> v .:?? "outputs" <*> v .: "metadata" <*> v .: "source" <*> v .: "cell_type"
parseJSON _ = mzero
instance ToJSON CellsElt where
toJSON (CellsElt {..}) = object ["execution_count" .= cellsEltExecutionCount, "outputs" .= cellsEltOutputs, "metadata" .= cellsEltMetadata, "source" .= cellsEltSource, "cell_type" .= cellsEltCellType]
toEncoding (CellsElt {..}) = pairs ("execution_count" .= cellsEltExecutionCount<>"outputs" .= cellsEltOutputs<>"metadata" .= cellsEltMetadata<>"source" .= cellsEltSource<>"cell_type" .= cellsEltCellType)
data IPyNb = IPyNb {
iPyNbNbformatMinor :: Double,
iPyNbNbformat :: Double,
iPyNbCells :: [CellsElt],
iPyNbMetadata :: Metadata
} deriving (Show,Eq,GHC.Generics.Generic)
instance FromJSON IPyNb where
parseJSON (Object v) = IPyNb <$> v .: "nbformat_minor" <*> v .: "nbformat" <*> v .: "cells" <*> v .: "metadata"
parseJSON _ = mzero
instance ToJSON IPyNb where
toJSON (IPyNb {..}) = object ["nbformat_minor" .= iPyNbNbformatMinor, "nbformat" .= iPyNbNbformat, "cells" .= iPyNbCells, "metadata" .= iPyNbMetadata]
toEncoding (IPyNb {..}) = pairs ("nbformat_minor" .= iPyNbNbformatMinor<>"nbformat" .= iPyNbNbformat<>"cells" .= iPyNbCells<>"metadata" .= iPyNbMetadata)
parse :: FilePath -> IO IPyNb
parse filename = do input <- BSL.readFile filename
case decode input of
Nothing -> fatal $ case (decode input :: Maybe Value) of
Nothing -> "Invalid JSON file: " ++ filename
Just v -> "Mismatched JSON value from file: " ++ filename
Just r -> return (r :: IPyNb)
where
fatal :: String -> IO a
fatal msg = do hPutStrLn stderr msg
exitFailure
main :: IO ()
main = do
filenames <- getArgs
forM_ filenames (\f -> parse f >>= (\p -> p `seq` putStrLn $ "Successfully parsed " ++ f))
exitSuccess