packages feed

xls 0.1.2 → 0.1.3

raw patch · 7 files changed

+108/−27 lines, 7 filesdep +hspecdep ~filepathdep ~resourcetdep ~transformersbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: hspec

Dependency ranges changed: filepath, resourcet, transformers

API changes (from Hackage documentation)

+ Data.Xls: decodeXlsIO :: FilePath -> IO [[[String]]]

Files

Changelog.md view
@@ -1,3 +1,6 @@+## 0.1.3+* Add IO version of `decodeXls` function.+ ## 0.1.2  * Upgrade libxls upstream package version from 1.4.0 to 1.5.1
README.md view
@@ -1,5 +1,9 @@ ## Haskell xls Parsing +[![Hackage](https://img.shields.io/hackage/v/xls.svg?style=flat)](https://hackage.haskell.org/package/xls)+[![Build Status](https://travis-ci.com/harendra-kumar/xls.svg?branch=master)](https://travis-ci.com/harendra-kumar/xls)+[![Build status](https://ci.appveyor.com/api/projects/status/nwknaf0gw1p9vqnv?svg=true)](https://ci.appveyor.com/project/harendra-kumar/xls)+ `xls` is a Haskell library to parse Microsoft Excel spreadsheet files. It parses the xls file format (extension `.xls`) more specifically known as `BIFF/Excel 97-2004`.@@ -7,15 +11,17 @@ It can be useful for mining data from old Microsoft Excel spreadsheets.  ## API-Use `decodeXls` to get a streaming Conduit. For example to convert an-xls file to comma separated csv:+Use `decodeXlsIO` to get a list of all worksheets. For example to convert all+worksheets in an xls file to comma separated csv:  ```haskell+import Data.List (intercalate)+import Data.Xls (decodeXlsIO)+ xlsToCSV :: String -> IO ()-xlsToCSV file =-      runResourceT-    $ decodeXls file-    $$ CL.mapM_ (liftIO . putStrLn . intercalate ",")+xlsToCSV file = do+    worksheets <- decodeXlsIO file+    mapM_ (mapM_ (putStrLn . intercalate ",")) worksheets ```  An `xls2csv` utility is shipped with the package.
bin/xls2csv.hs view
@@ -1,21 +1,16 @@ #!/usr/bin/env stack -- stack --resolver lts runhaskell --package getopt-generics -import           Control.Monad.IO.Class-import           Control.Monad.Trans.Resource-import           Data.Conduit-import           Data.Conduit.List            as CL-import           Data.List                    (intercalate)-import           Data.Xls-import           WithCli+import Data.List (intercalate)+import Data.Xls (decodeXlsIO)+import WithCli (withCli)  -- TODO need to escape the separator and the escaping quotes themselves  xlsToCSV :: String -> IO ()-xlsToCSV file =-      runResourceT-    $ runConduit-    $ decodeXls file-    .| CL.mapM_ (liftIO . putStrLn . intercalate ",")+xlsToCSV file = do+    worksheets <- decodeXlsIO file+    mapM_ (mapM_ (putStrLn . intercalate ",")) worksheets +main :: IO () main = withCli xlsToCSV
lib/Data/Xls.hs view
@@ -19,15 +19,21 @@ {-# LANGUAGE DeriveDataTypeable #-} #endif -module Data.Xls (decodeXls, XlsException(..)) where+module Data.Xls+    ( decodeXlsIO+    , decodeXls+    , XlsException(..)+    )+where -import           Control.Exception            (Exception, throwIO)+import           Control.Exception (Exception, throwIO, bracket) import           Control.Monad.IO.Class+import           Control.Monad (when, void) import           Control.Monad.Trans.Resource-import           Data.Conduit                 hiding (Conduit, Sink, Source)+import           Data.Conduit hiding (Conduit, Sink, Source) import           Data.Data import           Data.Int-import           Data.Maybe                   (catMaybes, fromJust, isJust)+import           Data.Maybe (catMaybes, fromJust, isJust, fromMaybe) import           Foreign.C import           Foreign.Ptr import           Text.Printf@@ -80,8 +86,9 @@ -- rows in a worksheet. Each row represented by a list of Strings, each String -- representing an individual cell. ----- Currently there is no separation of worksheets, all worksheets in a--- workbook get concatenated.+-- Important Note: This API concatenates rows from all worksheets into a single+-- stream. Please use the non-streaming 'decodeXlsIO' API to get individual+-- worksheets. -- -- Throws 'XlsException' --@@ -104,6 +111,27 @@             count <- liftIO $ c_xls_wb_sheetcount pWB             mapM_ (decodeOneWorkSheet file pWB) [0 .. count - 1] ++-- | Parse a Microsoft excel xls workbook file into a list of worksheets, each+-- worksheet consists of a list of rows and each row consists of a list of+-- cells.  Cells are plain 'String'.+--+-- Throws 'XlsException'+--+decodeXlsIO+    :: FilePath+    -> IO [[[String]]]+decodeXlsIO file = do+    file' <- newCString file+    pWB <- newCString "UTF-8" >>= c_xls_open file'+    when (pWB == nullPtr) $+        throwIO $ XlsFileNotFound+                $ "XLS file " ++ file ++ " not found."+    count <- liftIO $ c_xls_wb_sheetcount pWB+    results <- mapM (decodeOneWorkSheetIO file pWB) [0 .. count - 1]+    void $ c_xls_close_WB pWB+    return results+ decodeOneWorkSheet     :: MonadResource m     => FilePath -> XLSWorkbook -> CInt -> ConduitM i [String] m ()@@ -123,19 +151,57 @@          decodeWS = decodeRows +decodeOneWorkSheetIO+    :: FilePath+    -> XLSWorkbook+    -> CInt+    -> IO [[String]]+decodeOneWorkSheetIO file pWB index =+    bracket alloc cleanup decodeRowsIO+    where+        alloc = do+            pWS <- c_xls_getWorkSheet pWB index+            if pWS == nullPtr then+                throwIO $ XlsParseError+                        $ "XLS file "+                        ++ file+                        ++ " could not be parsed."+            else do+              c_xls_parseWorkSheet pWS+              return pWS+        cleanup = c_xls_close_WS+ decodeRows :: MonadResource m => XLSWorksheet -> ConduitM i [String] m () decodeRows pWS = do     rows <- liftIO $ c_xls_ws_rowcount pWS     cols <- liftIO $ c_xls_ws_colcount pWS     mapM_ (decodeOneRow pWS cols) [r | r <- [0 .. rows - 1]] +decodeRowsIO+    :: XLSWorksheet+    -> IO [[String]]+decodeRowsIO pWS = do+    rows <- c_xls_ws_rowcount pWS+    cols <- c_xls_ws_colcount pWS+    mapM (decodeOneRowIO pWS cols) [r | r <- [0 .. rows - 1]]+ decodeOneRow     :: MonadResource m     => XLSWorksheet -> Int16 -> Int16 -> ConduitM i [String] m () decodeOneRow pWS cols rowindex =     mapM (liftIO . (c_xls_cell pWS rowindex)) [0 .. cols - 1]-        >>= mapM (liftIO. decodeOneCell)+        >>= mapM (liftIO . decodeOneCell)         >>= yield . catMaybes++decodeOneRowIO+    :: XLSWorksheet+    -> Int16+    -> Int16+    -> IO [String]+decodeOneRowIO pWS cols rowindex =+    mapM (c_xls_cell pWS rowindex) [0 .. cols - 1]+        >>= mapM decodeOneCell+        >>= pure . (map $ fromMaybe "")  data CellType = Numerical | Formula | Str | Other 
test/Spec.hs view
@@ -1,2 +1,11 @@+import           Data.Xls+import           Test.Hspec+ main :: IO ()-main = putStrLn "Test suite not yet implemented"+main = hspec $ describe "Sanity check" $ do+    it "Test file parsing" $ do+        content <- decodeXlsIO "test/data/test.xls"+        content `shouldBe` testFileContent++testFileContent :: [[[String]]]+testFileContent = [[["1.000000000000000","2.3","text"]],[["1.000000000000000","2.3","text"]]]
+ test/data/test.xls view

binary file changed (absent → 6144 bytes)

xls.cabal view
@@ -1,5 +1,5 @@ name:                xls-version:             0.1.2+version:             0.1.3 synopsis:            Parse Microsoft Excel xls files (BIFF/Excel 97-2004) description:  Parse Microsoft Excel spreadsheet files in @.xls@ file format@@ -29,6 +29,7 @@     lib/libxls/include/*.h     lib/libxls/include/libxls/*.h     lib/libxls/include/libxls/*.c.h+    test/data/test.xls  flag force-has-iconv   description: force using iconv library on Windows@@ -82,6 +83,7 @@   main-is:             Spec.hs   build-depends:        base >= 4.7 && < 5                       , xls+                      , hspec   ghc-options:         -threaded -rtsopts -with-rtsopts=-N  source-repository head