packages feed

spreadsheet 0.1.3.4 → 0.1.3.5

raw patch · 4 files changed

+193/−146 lines, 4 filesdep +spreadsheetdep −bytestringdep ~basedep ~explicit-exceptiondep ~transformers

Dependencies added: spreadsheet

Dependencies removed: bytestring

Dependency ranges changed: base, explicit-exception, transformers, utility-ht

Files

+ README.md view
@@ -0,0 +1,69 @@+## Example: `csvreplace`++If you build the package with the Cabal flag `-fbuildExamples`+then the program `csvreplace` will be built.+It allows you to replace placeholders in a template file+according to the columns of a CSV file.+E.g. given a file `template.txt` with content++~~~~+Name: FIRSTNAME SURNAME+Born: BIRTH+~~~~++and `names.csv` with content++~~~~+"FIRSTNAME","SURNAME",BIRTH+"Georg","Cantor",1845+"Haskell","Curry",1900+"Ada","Lovelace",1815+~~~~++the call++~~~~+csvreplace template.txt <names.csv+~~~~++produces the output++~~~~+Name: Georg Cantor+Born: 1845+Name: Haskell Curry+Born: 1900+Name: Ada Lovelace+Born: 1815+~~~~++You may also generate one file per CSV row in the following manner:++~~~~+csvreplace --multifile=FIRSTNAME-SURNAME.txt template.txt <names.csv+~~~~+++### Character Encoding++For simple replacement of parts of the text+we would not need to decode the input texts+and thus we would not need to know the used encoding scheme.+Essentially, we would only require that both CSV and template file+employ the same character encoding.++However, it is not as simple as that.+We need to decode the structure of the CSV file.+In multi-file mode we also need to generate proper file names.+Both requirements force us to decode both CSV and template file.+For the de- and encoding we use the default locale encoding.++If you want essentially a byte-by-byte replacement+and you assert that all files are in the same encoding+where the commas and quotation marks are compatible with ASCII+then you can set the encoding locally+to a complete 8-bit encoding like `latin1` as in:++~~~~+LANG=de_DE csvreplace --multifile=FIRSTNAME-SURNAME.txt template.txt <names.csv+~~~~
+ example/CSVReplace.hs view
@@ -0,0 +1,101 @@+module Main where++import qualified Data.Spreadsheet as Sheet+import qualified Data.List.HT as ListHT++import System.Console.GetOpt (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )+import System.Environment (getArgs, getProgName, )+import qualified System.Exit as Exit+import qualified System.IO as IO++import qualified Control.Monad.Exception.Asynchronous as AExc+import qualified Control.Monad.Exception.Synchronous as Exc+import Control.Monad.Trans.Class (lift, )+import Control.Monad (when, )+import Control.Applicative ((<$>), )++import qualified Data.Foldable as Fold+import Data.Foldable (forM_, )+++data Flags =+   Flags {+      optMultiFile :: Maybe FilePath,+      optQuotation,+      optDelimiter :: Char+   }++defltFlags :: Flags+defltFlags = Flags {+      optMultiFile = Nothing,+      optQuotation = '"',+      optDelimiter = ','+   }++exitFailureMsg :: String -> IO a+exitFailureMsg msg = do+   IO.hPutStrLn IO.stderr msg+   Exit.exitFailure++options :: [OptDescr (Flags -> IO Flags)]+options =+   Option ['h'] ["help"]+      (NoArg (\_ -> do+         programName <- getProgName+         putStrLn $ flip usageInfo options $+            "Usage: " ++ programName ++ " [OPTIONS] TEMPLATE-FILE\n" +++            "The CSV file is read from standard input.\n"+         Exit.exitSuccess))+      "show options" :+   Option [] ["multifile"]+      (flip ReqArg "FILEPATTERN" $ \str flags ->+         return $ flags{optMultiFile = Just str})+      "generate one file per CSV row" :+   Option ['d'] ["delimiter"]+      (flip ReqArg "CHAR" $ \str flags -> do+         case str of+            [c] -> return $ flags{optDelimiter = c}+            _ -> exitFailureMsg $ "delimiter must be one character, which " ++ show str ++ " is not")+      "field delimiter character" :+   Option ['q'] ["quotation"]+      (flip ReqArg "CHAR" $ \str flags -> do+         case str of+            [c] -> return $ flags{optQuotation = c}+            _ -> exitFailureMsg $ "quotation mark must be one character, which " ++ show str ++ " is not")+      "quotation mark character" :+   []+++replaceRow :: String -> [String] -> [String] -> String+replaceRow template names row =+   ListHT.multiReplace (filter (not . null . fst) $ zip names row) template++replace :: String -> [String] -> Sheet.T -> String+replace template names = concatMap (replaceRow template names)+++main :: IO ()+main =+   Exc.resolveT (\e -> exitFailureMsg $ "Aborted: " ++ e) $ do+      argv <- lift getArgs+      let (opts, files, errors) = getOpt RequireOrder options argv+      when (not (null errors)) $ Exc.throwT $ concat $ errors+      flags <- lift $ foldr (=<<) (return defltFlags) opts+      case files of+         [templateName] -> do+            template <- lift $ readFile templateName+            sheet <-+               Sheet.fromString (optQuotation flags) (optDelimiter flags) <$>+               lift getContents+            case AExc.result sheet of+               [] -> Exc.throwT "empty CSV input"+               names:rows ->+                  lift $+                  case optMultiFile flags of+                     Nothing -> putStr $ replace template names rows+                     Just filePattern ->+                        forM_ rows $ \row ->+                           writeFile (replaceRow filePattern names row) $+                              replaceRow template names row+            Fold.mapM_ Exc.throwT $ AExc.exception sheet+         _ -> Exc.throwT "I need exactly one template file."
spreadsheet.cabal view
@@ -1,5 +1,5 @@ Name:             spreadsheet-Version:          0.1.3.4+Version:          0.1.3.5 License:          BSD3 License-File:     LICENSE Author:           Henning Thielemann <haskell@henning-thielemann.de>@@ -10,58 +10,39 @@ Description:   Read and write spreadsheets from and to files   containing comma separated values (CSV) in a lazy way.-  See also the-     csv package <http://hackage.haskell.org/package/csv> and-     <http://www.xoltar.org/languages/haskell.html>,-     <http://www.xoltar.org/languages/haskell/CSV.hs>.-  Both do not parse lazy.   Reading from other source than plain 'String's could be easily added.   .   If you install this package by   .-  cabal install -fbuildExamples+  > cabal install -fbuildExamples   .-  then an example program is compiled and installed, too.+  then the example program @csvreplace@ is compiled and installed, too.   This program fills a template text using data from a CSV file.-  E.g. given a file @template.txt@ with content-  .-  > Name: FIRSTNAME SURNAME-  > Born: BIRTH-  .-  and @names.csv@ with content-  .-  > "FIRSTNAME","SURNAME",BIRTH-  > "Georg","Cantor",1845-  > "Haskell","Curry",1900-  > "Ada","Lovelace",1815-  .-  the call-  .-  > csvreplace template.txt <names.csv+  For similar (non-Haskell) programs see @cut@, @csvfix@, @csvtool@.   .-  produces the output+  Related packages:   .-  > Name: Georg Cantor-  > Born: 1845-  > Name: Haskell Curry-  > Born: 1900-  > Name: Ada Lovelace-  > Born: 1815+  * @csv@: strict parser   .-  You may also generate one file per CSV row in the following manner:+  * <http://www.xoltar.org/languages/haskell.html>,+    <http://www.xoltar.org/languages/haskell/CSV.hs>: strict parser   .-  > csvreplace --multifile=FIRSTNAME-SURNAME.txt template.txt <names.csv+  * @lazy-csv@: lazy @String@ and @ByteString@ parser   .-  For similar (non-Haskell) programs see @cut@, @csvfix@, @csvtool@.-Tested-With:       GHC==6.8.2, GHC==6.12.3-Cabal-Version:     >=1.6+  * @cassava@: high-level CSV parser that treats rows as records,+    parses ByteStrings and is biased towards UTF-8 encoding.+Tested-With:       GHC==7.4.2, GHC==7.8.4+Cabal-Version:     >=1.8 Build-Type:        Simple+Extra-Source-Files:+  README.md+ Source-Repository head   Type:     darcs   Location: http://code.haskell.org/~thielema/spreadsheet/  Source-Repository this-  Tag:      0.1.3.4+  Tag:      0.1.3.5   Type:     darcs   Location: http://code.haskell.org/~thielema/spreadsheet/ @@ -93,9 +74,12 @@ Executable csvreplace   If flag(buildExamples)     Build-Depends:-      bytestring >=0.9 && <0.11+      spreadsheet,+      utility-ht,+      transformers,+      explicit-exception,+      base   Else     Buildable:      False   GHC-Options:      -Wall-  Hs-Source-Dirs:   src-  Main-Is: CSVReplace.hs+  Main-Is: example/CSVReplace.hs
− src/CSVReplace.hs
@@ -1,107 +0,0 @@-module Main where--import qualified Data.Spreadsheet as Sheet-import qualified Data.List.HT as ListHT--import System.Console.GetOpt (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )-import System.Environment (getArgs, getProgName, )-import qualified System.Exit as Exit-import qualified System.IO as IO--import qualified Data.ByteString.Char8 as B-import qualified Data.ByteString.Lazy.Char8 as BL--import qualified Control.Monad.Exception.Asynchronous as AExc-import qualified Control.Monad.Exception.Synchronous as Exc-import Control.Monad.Trans.Class (lift, )-import Control.Monad (when, )-import Data.Foldable (forM_, )---data Flags =-   Flags {-      optMultiFile :: Maybe FilePath,-      optQuotation,-      optDelimiter :: Char-   }--defltFlags :: Flags-defltFlags = Flags {-      optMultiFile = Nothing,-      optQuotation = '"',-      optDelimiter = ','-   }--exitFailureMsg :: String -> IO a-exitFailureMsg msg = do-   IO.hPutStrLn IO.stderr msg-   Exit.exitFailure--options :: [OptDescr (Flags -> IO Flags)]-options =-   Option ['h'] ["help"]-      (NoArg (\_ -> do-         programName <- getProgName-         putStrLn $ flip usageInfo options $-            "Usage: " ++ programName ++ " [OPTIONS] TEMPLATE-FILE\n" ++-            "The CSV file is read from standard input.\n"-         Exit.exitSuccess))-      "show options" :-   Option [] ["multifile"]-      (flip ReqArg "FILEPATTERN" $ \str flags ->-         return $ flags{optMultiFile = Just str})-      "generate one file per CSV row" :-   Option ['d'] ["delimiter"]-      (flip ReqArg "CHAR" $ \str flags -> do-         case str of-            [c] -> return $ flags{optDelimiter = c}-            _ -> exitFailureMsg $ "delimiter must be one character, which " ++ show str ++ " is not")-      "field delimiter character" :-   Option ['q'] ["quotation"]-      (flip ReqArg "CHAR" $ \str flags -> do-         case str of-            [c] -> return $ flags{optQuotation = c}-            _ -> exitFailureMsg $ "quotation mark must be one character, which " ++ show str ++ " is not")-      "quotation mark character" :-   []---replaceRow :: String -> [String] -> [String] -> String-replaceRow template names row =-   ListHT.multiReplace (filter (not . null . fst) $ zip names row) template--replaceRowBS :: String -> [String] -> [String] -> BL.ByteString-replaceRowBS template names row =-   BL.pack $ replaceRow template names row--replace :: String -> [String] -> Sheet.T -> BL.ByteString-replace template names =-   BL.concat . map (replaceRowBS template names)---main :: IO ()-main =-   Exc.resolveT (\e -> exitFailureMsg $ "Aborted: " ++ e) $ do-      argv <- lift getArgs-      let (opts, files, errors) = getOpt RequireOrder options argv-      when (not (null errors)) $ Exc.throwT $ concat $ errors-      flags <- lift $ foldr (=<<) (return defltFlags) opts-      case files of-         [templateName] -> do-            template <- fmap B.unpack $ lift $ B.readFile templateName-            sheet <--               fmap (Sheet.fromString (optQuotation flags) (optDelimiter flags)-                      . BL.unpack) $-               lift BL.getContents-            case AExc.result sheet of-               [] -> Exc.throwT "empty CSV input"-               (names:rows) ->-                  case optMultiFile flags of-                     Nothing -> do-                        lift $ BL.putStr $ replace template names rows-                        forM_ (AExc.exception sheet) $ Exc.throwT-                     Just filePattern ->-                        lift $ forM_ rows $ \row ->-                           BL.writeFile (replaceRow filePattern names row) $-                              replaceRowBS template names row-         _ -> Exc.throwT "I need exactly one template file."