1 {-# LANGUAGE NamedFieldPuns #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ScopedTypeVariables #-}
4 {-# LANGUAGE TupleSections #-}
5 module Hcompta.CLI.Command.Journal where
7 -- import Control.Applicative ((<$>))
8 import Control.Monad (foldM, liftM)
9 import Control.Monad.IO.Class (liftIO)
10 import Control.Monad.Trans.Except (runExceptT)
11 import qualified Data.Either
12 import Data.Foldable ()
13 import Data.Functor.Compose (Compose(..))
14 import qualified Data.List
15 import qualified Data.Map.Strict as Data.Map
16 import System.Console.GetOpt
20 import Data.Monoid ((<>))
21 import Prelude hiding (foldr)
22 import System.Environment as Env (getProgName)
23 import System.Exit (exitWith, ExitCode(..))
24 import qualified System.IO as IO
26 import qualified Hcompta.CLI.Args as Args
27 import qualified Hcompta.CLI.Context as Context
28 import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
29 import qualified Hcompta.CLI.Write as Write
30 import qualified Hcompta.Date as Date
31 import qualified Hcompta.Filter as Filter
32 import qualified Hcompta.Filter.Read as Filter.Read
33 import qualified Hcompta.Filter.Reduce as Filter.Reduce
34 import qualified Hcompta.Format.Ledger as Ledger
35 import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
36 import qualified Hcompta.Format.Ledger.Read as Ledger.Read
37 import qualified Hcompta.Format.Ledger.Write as Ledger.Write
38 import qualified Hcompta.Lib.Leijen as W
39 import qualified Hcompta.Lib.Interval as Interval
43 { ctx_input :: [FilePath]
45 , ctx_reduce_date :: Bool
46 , ctx_filter_transaction :: Filter.Simplified
48 (Filter.Test_Transaction
57 , ctx_reduce_date = True
58 , ctx_filter_transaction = mempty
63 bin <- Env.getProgName
66 , " "++bin++" journal"
67 , " [-t TRANSACTION_FILTER]"
68 , " JOURNAL_FILE [...]"
70 , usageInfo "OPTIONS" options
73 options :: Args.Options Ctx
76 (NoArg (\_context _ctx -> do
77 usage >>= IO.hPutStr IO.stderr
78 exitWith ExitSuccess))
80 , Option "i" ["input"]
81 (ReqArg (\s _context ctx -> do
82 return $ ctx{ctx_input=s:ctx_input ctx}) "FILE")
83 "read data from given file, multiple uses merge the data as would a concatenation do"
85 (OptArg (\arg context ctx -> do
86 ctx_align <- case arg of
87 Nothing -> return $ True
88 Just "yes" -> return $ True
89 Just "no" -> return $ False
90 Just _ -> Write.fatal context $
91 W.text "--align option expects \"yes\", or \"no\" as value"
92 return $ ctx{ctx_align})
95 , Option "" ["reduce-date"]
96 (OptArg (\arg context ctx -> do
97 ctx_reduce_date <- case arg of
98 Nothing -> return $ True
99 Just "yes" -> return $ True
100 Just "no" -> return $ False
101 Just _ -> Write.fatal context $
102 W.text "--reduce-date option expects \"yes\", or \"no\" as value"
103 return $ ctx{ctx_reduce_date})
105 "use advanced date reducer to speed up filtering"
106 , Option "t" ["transaction-filter"]
107 (ReqArg (\s context ctx -> do
108 ctx_filter_transaction <-
109 liftM (\t -> (<>) (ctx_filter_transaction ctx)
110 (Filter.simplify t (Nothing::Maybe Ledger.Transaction))) $
111 liftIO $ Filter.Read.read Filter.Read.test_transaction s
113 Left ko -> Write.fatal context $ ko
114 Right ok -> return ok
115 return $ ctx{ctx_filter_transaction}) "FILTER")
116 "filter at transaction level, multiple uses are merged with a logical AND"
119 run :: Context.Context -> [String] -> IO ()
120 run context args = do
121 (ctx, inputs) <- Args.parse context usage options (nil, args)
122 (read_journals, read_bench) <- Date.bench $ do
123 CLI.Ledger.paths context $ ctx_input ctx ++ inputs
126 liftIO $ runExceptT $ Ledger.Read.file path
128 Left ko -> return $ Left (path, ko)
129 Right ok -> return $ Right ok
130 >>= return . Data.Either.partitionEithers
131 Write.debug context $ "benchmark: input:" ++ show read_bench
132 case read_journals of
133 (errs@(_:_), _journals) ->
134 (flip mapM_) errs $ \(_path, err) -> do
135 Write.fatal context $ err
137 Write.debug context $ "filter: transaction: " ++ show (ctx_filter_transaction ctx)
139 if ctx_reduce_date ctx
140 then Filter.Reduce.bool_date <$> ctx_filter_transaction ctx
142 Write.debug context $ "filter: transaction: reducer: " ++ show reducer_date
143 style_color <- Write.with_color context IO.stdout
144 let sty = Ledger.Write.Style
145 { Ledger.Write.style_align = ctx_align ctx
146 , Ledger.Write.style_color
150 (flip (Ledger.Journal.foldM
152 let ts = Ledger.journal_transactions j
154 (Data.Map.unionsWith (++) . (:) j_ts) $
155 case Filter.simplified $ ctx_filter_transaction ctx of
156 Right True -> return $ ts:[]
157 Right False -> return $ []
163 case Data.List.filter (Filter.test flt) lt of
167 case Filter.simplified reducer_date of
169 let (ts_reduced, date_sieve) = Filter.Reduce.map_date reducer ts
170 Write.debug context $ "filter: transaction: sieve: "
171 ++ "journal=" ++ (show $ Ledger.journal_file j)
172 ++ ": " ++ show (Interval.Pretty date_sieve)
174 Right True -> return $ ts:[]
175 Right False -> return $ []
179 Ledger.Write.put sty IO.stdout $ do
180 Ledger.Write.transactions (Compose transactions)