1 {-# LANGUAGE NamedFieldPuns #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ScopedTypeVariables #-}
4 {-# LANGUAGE TupleSections #-}
5 module Hcompta.CLI.Command.Stats where
7 import Control.Monad (liftM, forM_)
8 import Control.Monad.IO.Class (liftIO)
9 import Control.Monad.Trans.Except (runExceptT)
10 import qualified Data.Either
11 import qualified Data.Foldable
12 import qualified Data.Map.Strict as Data.Map
13 import Data.Monoid ((<>))
14 import System.Console.GetOpt
18 import System.Environment as Env (getProgName)
19 import System.Exit (exitSuccess)
20 import qualified System.IO as IO
22 import qualified Hcompta.CLI.Args as Args
23 import qualified Hcompta.CLI.Context as Context
24 import Hcompta.CLI.Context (Context)
25 import qualified Hcompta.CLI.Format.Ledger as CLI.Ledger
26 import qualified Hcompta.CLI.Lang as Lang
27 import qualified Hcompta.CLI.Write as Write
28 -- import qualified Hcompta.Date as Date
29 import qualified Hcompta.Amount.Unit as Amount.Unit
30 import qualified Hcompta.Filter as Filter
31 import qualified Hcompta.Filter.Read as Filter.Read
32 -- import qualified Hcompta.Filter.Reduce as Filter.Reduce
33 import qualified Hcompta.Format.Ledger as Ledger
34 import qualified Hcompta.Format.Ledger.Journal as Ledger.Journal
35 import qualified Hcompta.Format.Ledger.Read as Ledger.Read
36 -- import qualified Hcompta.Format.Ledger.Write as Ledger.Write
37 import qualified Hcompta.Lib.Interval as Interval
38 import qualified Hcompta.Lib.Leijen as W
39 import qualified Hcompta.Stats as Stats
43 { ctx_input :: [FilePath]
44 , ctx_filter_transaction :: Filter.Simplified
46 (Filter.Filter_Transaction
54 , ctx_filter_transaction = mempty
59 bin <- Env.getProgName
60 let pad = replicate (length bin) ' '
63 , " "++bin++" stats [-i JOURNAL_FILE]"
64 , " "++pad++" [-t TRANSACTION_FILTER]"
65 , " "++pad++" [JOURNAL_FILE] [...]"
67 , usageInfo "OPTIONS" options
70 options :: Args.Options Ctx
73 (NoArg (\_context _ctx -> do
74 usage >>= IO.hPutStr IO.stderr
77 , Option "i" ["input"]
78 (ReqArg (\s _context ctx -> do
79 return $ ctx{ctx_input=s:ctx_input ctx}) "FILE")
80 "read data from given file, multiple uses merge the data as would a concatenation do"
81 , Option "t" ["transaction-filter"]
82 (ReqArg (\s context ctx -> do
83 ctx_filter_transaction <-
84 liftM ((ctx_filter_transaction ctx <>) . Filter.simplify) $
85 liftIO $ Filter.Read.read Filter.Read.filter_transaction s
87 Left ko -> Write.fatal context $ ko
89 Write.debug context $ "filter: transaction: " ++ show ok
91 return $ ctx{ctx_filter_transaction}) "FILTER")
92 "filter at transaction level, multiple uses are merged with a logical AND"
95 run :: Context.Context -> [String] -> IO ()
97 (ctx, inputs) <- Args.parse context usage options (nil, args)
99 liftM Data.Either.partitionEithers $ do
100 CLI.Ledger.paths context $ ctx_input ctx ++ inputs
103 liftIO $ runExceptT $ Ledger.Read.file
104 (Ledger.Read.context (ctx_filter_transaction ctx) Ledger.journal)
107 Left ko -> return $ Left (path, ko)
108 Right ok -> return $ Right ok
109 case read_journals of
110 (errs@(_:_), _journals) ->
111 forM_ errs $ \(_path, err) -> do
112 Write.fatal context $ err
114 Write.debug context $ "filter: transaction: " ++ show (ctx_filter_transaction ctx)
115 let (files, stats) = ledger_stats ctx journals
116 style_color <- Write.with_color context IO.stdout
117 W.displayIO IO.stdout $ do
118 W.renderPretty style_color 1.0 maxBound $ do
119 doc_stats context ctx (files, stats)
123 -> [ Ledger.Journal (Stats.Stats Ledger.Transaction) ]
124 -> ([FilePath], Stats.Stats Ledger.Transaction)
127 (flip $ Ledger.Journal.fold
129 { Ledger.journal_transactions=s
130 , Ledger.journal_file=f
131 } -> mappend ([f], s)))
137 -> ([FilePath], Stats.Stats Ledger.Transaction)
139 doc_stats context _ctx (files, stats) =
140 let lang = Context.lang context in
141 W.toDoc lang Lang.Message_Accounts <> " (" <>
142 (W.toDoc () $ Data.Map.size $ Stats.stats_accounts stats) <> ")" <>
144 let depth = Stats.stats_accounts_depths stats in
145 W.line <> W.toDoc lang Lang.Message_Depths <>
147 W.toDoc () (Interval.limit $ Interval.low depth) <>
149 W.toDoc () (Interval.limit $ Interval.high depth) <>
152 W.toDoc lang Lang.Message_Transactions <> " (" <> (W.toDoc () $ Stats.stats_transactions stats) <> ")" <>
154 case Stats.stats_transactions_span stats of
159 W.toDoc () (Interval.limit $ Interval.low date) <>
161 W.toDoc () (Interval.limit $ Interval.high date) <>
164 W.toDoc lang Lang.Message_Units <> " (" <>
165 (W.toDoc () (Data.Map.size $ Data.Map.delete Amount.Unit.nil $ Stats.stats_units stats)) <> ")" <> W.line <>
166 W.toDoc lang Lang.Message_Journals <> " (" <> (W.toDoc () (length $ files)) <> ")" <> W.line <>
167 W.toDoc lang Lang.Message_Tags <> " (" <>
168 (W.toDoc () (foldr (flip $ foldr (+)) 0 $ Stats.stats_tags stats)) <>
171 "Distincts" <> " (" <> (W.toDoc () (Data.Map.size $ Stats.stats_tags stats)) <> ")"