clerk 0.1.0.1 → 0.1.0.2
raw patch · 5 files changed
+19/−306 lines, 5 filesdep −clerkPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies removed: clerk
API changes (from Hackage documentation)
- Example: main :: IO ()
Files
- README.md +6/−4
- app/Main.hs +0/−6
- clerk.cabal +1/−42
- src/Clerk.hs +12/−12
- src/Example.lhs +0/−242
README.md view
@@ -37,7 +37,7 @@ First, we import the necessary stuff. ```haskell-module Example (main) where+module Main (main) where import Clerk import Codec.Xlsx qualified as X import Codec.Xlsx.Formatted qualified as X@@ -288,10 +288,12 @@ Run ```console-stack run+cd example+nix develop+cabal run ``` -to get `example-1.xlsx`.+to get `example/example-1.xlsx`. With formulas enabled, `example-1.xlsx` looks like this: @@ -310,7 +312,7 @@ Next, run ```sh-nix develop nix-dev/+nix develop write-settings-json codium . ```
− app/Main.hs
@@ -1,6 +0,0 @@-module Main (main) where--import Example qualified as E (main)--main :: IO ()-main = E.main
clerk.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: clerk-version: 0.1.0.1+version: 0.1.0.2 synopsis: Declaratively describe spreadsheets and generate xlsx description: Please see the README on GitHub at <https://github.com/deemp/clerk#readme> category: spreadsheet@@ -28,7 +28,6 @@ library exposed-modules: Clerk- Example other-modules: Paths_clerk hs-source-dirs:@@ -39,10 +38,8 @@ FlexibleContexts FlexibleInstances OverloadedStrings- OverloadedRecordDot GeneralizedNewtypeDeriving RankNTypes- ImportQualifiedPost InstanceSigs NamedFieldPuns RecordWildCards@@ -54,44 +51,6 @@ build-depends: base >=4.9.0.0 && <5.0 , bytestring >=0.10.8.0- , containers >=0.5.0.0- , data-default >=0.7.1.1- , lens >=3.8 && <5.3- , mtl >=2.1- , text >=0.11.3.1- , time >=1.4.0.1- , transformers >=0.3.0.0- , xlsx >=1.1.0.1- default-language: Haskell2010--executable clerk- main-is: Main.hs- other-modules:- Paths_clerk- hs-source-dirs:- app- default-extensions:- DataKinds- DeriveFunctor- FlexibleContexts- FlexibleInstances- OverloadedStrings- OverloadedRecordDot- GeneralizedNewtypeDeriving- RankNTypes- ImportQualifiedPost- InstanceSigs- NamedFieldPuns- RecordWildCards- TupleSections- MonoLocalBinds- TypeSynonymInstances- DuplicateRecordFields- ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints- build-depends:- base >=4.9.0.0 && <5.0- , bytestring >=0.10.8.0- , clerk , containers >=0.5.0.0 , data-default >=0.7.1.1 , lens >=3.8 && <5.3
src/Clerk.hs view
@@ -31,11 +31,11 @@ (|^|), (|$|), (<|),- Expr(..)+ Expr (..), ) where -import Codec.Xlsx qualified as X-import Codec.Xlsx.Formatted qualified as X+import qualified Codec.Xlsx as X+import qualified Codec.Xlsx.Formatted as X import Control.Lens (Identity (runIdentity), (%~), (&), (?~)) import Control.Lens.Operators ((.~)) import Control.Monad.State (@@ -53,9 +53,9 @@ import Data.Default (Default (..)) import Data.Foldable (Foldable (..)) import Data.List (intercalate)-import Data.Map.Strict qualified as Map (Map, insert)+import qualified Data.Map.Strict as Map (Map, insert) import Data.Maybe (isJust, maybeToList)-import Data.Text qualified as T+import qualified Data.Text as T -- Coords @@ -242,13 +242,13 @@ let columnsProperties = Just $ (unColumnsProperties def)- { X.cpMin = coords.col- , X.cpMax = coords.col+ { X.cpMin = coords & col+ , X.cpMax = coords & col , X.cpWidth = width } tell (Template [CellTemplate{format, mkOutput, columnsProperties}]) cell <- gets Cell- modify (\x -> x{col = (x.col) + 1})+ modify (\x -> x{col = (x & col) + 1}) return cell columnWidth :: ToCellData output => Double -> FormatCell -> (input -> output) -> Builder input CellData (Cell a)@@ -302,12 +302,12 @@ composeXlsx sheetBuilders = workBook' where getTransform x = execWriter $ unSheetBuilder x- workBook = X.formatWorkbook ((\(name, tf') -> (name, (getTransform tf').fmTransform X.def)) <$> sheetBuilders) X.def+ workBook = X.formatWorkbook ((\(name, tf') -> (name, (getTransform tf' & fmTransform) X.def)) <$> sheetBuilders) X.def filterWidths ws = ws & X.wsColumnsProperties %~ filter (isJust . X.cpWidth) workBook' = workBook & X.xlSheets- %~ \sheets -> zipWith (\x (name, ws) -> (name, (getTransform x).wsTransform ws & filterWidths)) (snd <$> sheetBuilders) sheets+ %~ \sheets -> zipWith (\x (name, ws) -> (name, (getTransform x & wsTransform) ws & filterWidths)) (snd <$> sheetBuilders) sheets {- Lib. Formulas -} @@ -416,10 +416,10 @@ newtype Cell a = Cell {unCell :: Coords} deriving (Functor) cellCol :: Cell a -> Int-cellCol (Cell c) = c.col+cellCol (Cell c) = c & col cellRow :: Cell a -> Int-cellRow (Cell c) = c.row+cellRow (Cell c) = c & row overCol :: (Int -> Int) -> Coords -> Coords overCol f (Coords row col) = Coords row (f col)
− src/Example.lhs
@@ -1,242 +0,0 @@- ## Example--This is a demo program that uses `clerk` to produce an `xlsx` file that looks as follows:--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/demoValues.png" width = "80%">--Alternatively, with formulas enabled:--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/demoFormulas.png" width = "80%">--This file has a sheet with several tables. These are tables for-constants' header, a table per a constant's value (three of them), volume & pressure header, volume & pressure values.-Let's see how we can construct such a sheet.-- ### Imports--First, we import the necessary stuff.--> module Example (main) where-> import Clerk-> import Codec.Xlsx qualified as X-> import Codec.Xlsx.Formatted qualified as X-> import Control.Lens ((%~), (&), (?~))-> import Data.ByteString.Lazy qualified as L-> import Data.Text qualified as T-> import Data.Time.Clock.POSIX (getPOSIXTime)-> import Control.Monad (void)-- ### Inputs--Following that, we declare a number of data types that we'll use to store the input values.--A type for constants' headers.--> data ConstantsHeader = ConstantsHeader-> { hConstant :: String-> , hSymbol :: String-> , hValue :: String-> , hUnits :: String-> }-> -> constantsHeader :: ConstantsHeader-> constantsHeader =-> ConstantsHeader-> { hConstant = "constant"-> , hSymbol = "symbol"-> , hValue = "value"-> , hUnits = "units"-> }--A type for constants' data.--> data ConstantsData a = ConstantsData-> { name :: String-> , symbol :: String-> , value :: a-> , units :: String-> }--Additionally, we declare a helper type that will store all constants together.--> data ConstantsInput = ConstantsInput-> { gas :: ConstantsData Double-> , nMoles :: ConstantsData Double-> , temperature :: ConstantsData Double-> }-> -> constants :: ConstantsInput-> constants =-> ConstantsInput-> { gas = ConstantsData "GAS CONSTANT" "R" 0.08206 "L.atm/mol.K"-> , nMoles = ConstantsData "NUMBER OF MOLES" "n" 1 "moles"-> , temperature = ConstantsData "TEMPERATURE(K)" "T" 273.2 "K"-> }--A type for the Volume & Pressure header.--> data ValuesHeader = ValuesHeader-> { hVolume :: String-> , hPressure :: String-> }-> -> valuesHeader :: ValuesHeader-> valuesHeader =-> ValuesHeader-> { hVolume = "VOLUME (L)"-> , hPressure = "PRESSURE (atm)"-> }--The last type is for volume inputs. We just generate them--> newtype Volume = Volume-> { volume :: Double-> }-> -> volumeData :: [Volume]-> volumeData = take 10 $ Volume <$> [1 ..]-- ### Styles--Following the headers and data types, we define the styles. Let's start with colors.-We select several color codes and store them into `colors`--> data Colors = Colors-> { lightBlue :: T.Text-> , lightGreen :: T.Text-> , blue :: T.Text-> , green :: T.Text-> }-> -> colors :: Colors-> colors =-> Colors-> { lightGreen = "90CCFFCC"-> , lightBlue = "90CCFFFF"-> , blue = "FF99CCFF"-> , green = "FF00FF00"-> }--Next, we convert them to `FormatCell` function--> colorBlue :: FormatCell-> colorBlue = mkColorStyle colors.blue-> -> colorLightBlue :: FormatCell-> colorLightBlue = mkColorStyle colors.lightBlue-> -> colorGreen :: FormatCell-> colorGreen = mkColorStyle colors.green-> -> colorMixed :: FormatCell-> colorMixed coords idx = mkColorStyle (if even idx then colors.lightGreen else colors.lightBlue) coords idx--Additionally, we compose a transform for the number format--> -- | allow 2 decimal digits-> nf2decimal :: FCTransform-> nf2decimal fc = fc & X.formattedFormat %~ (\ff -> ff & X.formatNumberFormat ?~ X.StdNumberFormat X.Nf2Decimal)--And a transform for centering the cell contents--> alignCenter :: FCTransform-> alignCenter = horizontalAlignment X.CellHorizontalAlignmentCenter-- ### `Builder`s--Now, we are able to compose the `Builder`s for tables.--A builder for the constants header.--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/constantsHeader.png" width = "50%">--> constantsHeaderBuilder :: Builder ConstantsHeader CellData (Coords, Coords)-> constantsHeaderBuilder = do-> tl <- columnWidth 20 (alignCenter <| colorBlue) hConstant-> columnWidth_ 8 (alignCenter <| colorBlue) hSymbol-> column_ (alignCenter <| colorBlue) hValue-> tr <- columnWidth 13 (alignCenter <| colorBlue) hUnits-> return (unCell tl, unCell tr)--A builder for a constant. We'll use this builder for each constant separately-as each constant produces cells of a specific type.--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/constants.png" width = "50%">--> constantBuilder :: forall a. ToCellData a => Builder (ConstantsData a) CellData (Coords, Cell a)-> constantBuilder = do-> topLeft <- column colorLightBlue name-> column_ colorLightBlue symbol-> value <- column (nf2decimal <| colorLightBlue) value-> column_ colorLightBlue units-> return (unCell topLeft, value)--A builder for values' header.--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/valuesHeader.png" width = "50%">--> valuesHeaderBuilder :: Builder ValuesHeader CellData Coords-> valuesHeaderBuilder = do-> tl <- columnWidth 12 colorGreen hVolume-> columnWidth_ 16 colorGreen hPressure-> return (unCell tl)--A builder for volume & pressure (formulas enabled)--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/valuesFormulas.png" width = "50%">--To pass values in a structured way, we make a helper type.--> data ConstantsValues = ConstantsValues-> { gas :: Cell Double-> , nMoles :: Cell Double-> , temperature :: Cell Double-> }--Next, we define a function to produce a builder.--> valuesBuilder :: ConstantsValues -> Builder Volume CellData ()-> valuesBuilder cv = do-> volume' <- column colorMixed volume-> let pressure' = ex cv.gas |*| ex cv.nMoles |*| ex cv.temperature |/| ex volume'-> column_ (nf2decimal <| colorMixed) (const pressure')-- ### `SheetBuilder`--The `SheetBuilder` is used to place builders onto a sheet and glue them together--> full :: SheetBuilder ()-> full = do-> (constantsHeaderTL, constantsHeaderTR) <- placeInput (Coords 2 2) constantsHeader constantsHeaderBuilder-> (gasTL, gas) <- placeInput (overRow (+ 2) constantsHeaderTL) constants.gas constantBuilder-> (nMolesTL, nMoles) <- placeInput (overRow (+ 1) gasTL) constants.nMoles constantBuilder-> temperature <- snd <$> placeInput (overRow (+ 1) nMolesTL) constants.temperature constantBuilder-> valuesHeaderTL <- placeInput (overCol (+ 2) constantsHeaderTR) valuesHeader valuesHeaderBuilder-> placeInputs_ (overRow (+ 2) valuesHeaderTL) volumeData (valuesBuilder $ ConstantsValues{..})-- ### Result--Now, we can write the result and get the spreadsheet images that you've seen at the top of this tutorial.--> writeWorksheet :: SheetBuilder a -> String -> IO ()-> writeWorksheet tb name = do-> ct <- getPOSIXTime-> let-> xlsx = composeXlsx [("List 1", void tb)]-> L.writeFile ("example-" <> name <> ".xlsx") $ X.fromXlsx ct xlsx-> -> writeEx :: IO ()-> writeEx = writeWorksheet full "1"-> -> main :: IO ()-> main = writeEx--Run--< stack run--to get `example-1.xlsx`.--With formulas enabled, `example-1.xlsx` looks like this:--<img src = "https://raw.githubusercontent.com/deemp/clerk/master/README/demoFormulas.png" width = "80%">