1 {-# LANGUAGE BangPatterns #-}
2 {-# LANGUAGE DeriveDataTypeable #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE FlexibleInstances #-}
5 {-# LANGUAGE MultiParamTypeClasses #-}
6 {-# LANGUAGE NamedFieldPuns #-}
7 {-# LANGUAGE StandaloneDeriving #-}
8 {-# LANGUAGE TupleSections #-}
9 {-# LANGUAGE TypeFamilies #-}
10 {-# OPTIONS_GHC -fno-warn-deprecations #-} -- FIXME: to be removed when dropping GHC-7.6 support
11 {-# OPTIONS_GHC -fno-warn-orphans #-}
12 module Hcompta.Stats where
14 import Control.DeepSeq (NFData(..))
15 -- import Control.Applicative (Const(..))
17 import Data.Eq (Eq(..))
18 import Data.Ord (Ord(..))
19 import Data.Foldable (Foldable(..))
20 import Data.Functor ((<$>))
21 import Data.Map.Strict (Map)
22 import qualified Data.Map.Strict as Map
23 import Data.Maybe (Maybe(..), maybe)
24 import Data.Monoid (Monoid(..))
25 import Data.Text (Text)
26 import Data.Typeable ()
27 import Prelude (($), (.), Int, Integer, Num(..), Show(..), const, flip, id, seq)
29 import qualified Hcompta.Account as Account
30 import Hcompta.Account (Account(..))
31 import Hcompta.Date (Date)
32 import Hcompta.Lib.Consable (Consable(..))
33 import Hcompta.Lib.Interval (Interval)
34 import qualified Hcompta.Lib.Interval as Interval
35 import qualified Hcompta.Tag as Tag
36 import Hcompta.Tag (Tags(..))
37 import Hcompta.Transaction (Transaction_Tags(..))
39 -- * Requirements' interface
44 ( Account (Posting_Account p)
45 , Data (Posting_Unit p)
46 , NFData (Posting_Account p)
47 , NFData (Posting_Unit p)
48 , Ord (Posting_Unit p)
49 , Show (Posting_Unit p)
51 type Posting_Account p
53 type Posting_Quantity p
54 posting_account :: p -> Posting_Account p
55 posting_amounts :: p -> Map (Posting_Unit p)
58 -- ** Class 'Transaction'
61 ( Posting (Transaction_Posting t)
62 , Foldable (Transaction_Postings t)
63 ) => Transaction t where
64 type Transaction_Posting t
65 type Transaction_Postings t :: * -> *
66 transaction_date :: t -> Date
67 transaction_postings :: t -> Transaction_Postings t (Transaction_Posting t)
68 transaction_postings_size :: t -> Int
69 transaction_postings_size = foldr (const $ (+) 1) 0 . transaction_postings
70 transaction_tags :: t -> Transaction_Tags
77 { stats_accounts :: Map (Posting_Account (Transaction_Posting t)) ()
78 , stats_tags :: Map Tag.Path (Map Text Integer)
79 , stats_transactions :: Integer
80 , stats_transactions_span :: Maybe (Interval Date)
81 , stats_units :: Map (Posting_Unit (Transaction_Posting t)) ()
83 deriving instance ( Transaction transaction
85 ) => Data (Stats transaction)
86 deriving instance ( Transaction transaction
88 ) => Eq (Stats transaction)
89 deriving instance ( Transaction transaction
91 ) => Show (Stats transaction)
92 deriving instance Typeable1 Stats
96 ) => NFData (Stats t) where
102 , stats_transactions_span
105 rnf stats_accounts `seq`
107 rnf stats_transactions `seq`
108 rnf stats_transactions_span `seq`
111 empty :: Transaction t => Stats t
114 { stats_accounts = mempty
115 , stats_tags = mempty
116 , stats_transactions = 0
117 , stats_transactions_span = Nothing
118 , stats_units = mempty
121 stats_accounts_depths :: Transaction t => Stats t -> Interval Integer
122 stats_accounts_depths s =
123 case Map.keys $ stats_accounts s of
124 [] -> Interval.point 0
127 (Interval.span . Interval.point . Account.depth)
128 (Interval.point $ Account.depth a) as
130 -- | Return the given 'Stats'
131 -- updated by the given 'Transaction'.
133 -- NOTE: to reduce memory consumption when 'cons'ing iteratively,
134 -- the given 'Stats' is matched strictly.
135 cons :: Transaction t => t -> Stats t -> Stats t
140 (flip $ (\p -> Map.insert (posting_account p) ()))
142 (transaction_postings t)
146 Map.unionWith (+) x1 $
147 Map.fromListWith (+) $ (, 1) <$> x2)
148 id ((Map.fromListWith (+) . ((, 1) <$>)) <$>)
149 (stats_tags s) -- Map Text (Map Text Integer)
150 (let Transaction_Tags (Tags tags) = transaction_tags t in tags) -- Map Text [Text]
151 , stats_transactions = 1 + (stats_transactions s)
152 , stats_transactions_span =
153 let i = Interval.point $ transaction_date t in
154 maybe (Just i) (Just . Interval.span i)
155 (stats_transactions_span s)
158 Map.foldlWithKey -- TODO: merge rather than insert
159 (\acc unit _qty -> Map.insert unit () acc)
160 su . posting_amounts)
162 (transaction_postings t)
165 union :: Transaction t => Stats t -> Stats t -> Stats t
178 , stats_transactions =
180 (stats_transactions s0)
181 (stats_transactions s1)
182 , stats_transactions_span =
184 ( stats_transactions_span s0
185 , stats_transactions_span s1
187 (Nothing, Nothing) -> Nothing
188 (Just i0, Nothing) -> Just i0
189 (Nothing, Just i1) -> Just i1
190 (Just i0, Just i1) -> Just $ Interval.span i0 i1
198 instance Transaction t => Monoid (Stats t) where
201 instance Transaction t => Consable t (Stats t) where