1 {-# LANGUAGE BangPatterns #-}
2 {-# LANGUAGE DeriveDataTypeable #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE FlexibleInstances #-}
5 {-# LANGUAGE MultiParamTypeClasses #-}
6 {-# LANGUAGE StandaloneDeriving #-}
7 {-# LANGUAGE TupleSections #-}
8 {-# LANGUAGE TypeFamilies #-}
9 {-# OPTIONS_GHC -fno-warn-deprecations #-} -- FIXME: to be removed when dropping GHC-7.6 support
10 {-# OPTIONS_GHC -fno-warn-orphans #-}
11 module Hcompta.Stats where
13 -- import Control.Applicative (Const(..))
15 import qualified Data.Foldable
16 import qualified Data.Map.Strict as Data.Map
17 import Data.Map.Strict (Map)
18 import Data.Text (Text)
19 import Data.Typeable ()
21 import qualified Hcompta.Account as Account
22 import Hcompta.Account (Account)
23 import qualified Hcompta.Amount as Amount
24 import qualified Hcompta.Amount.Unit as Amount.Unit
25 import Hcompta.Date (Date)
26 import Hcompta.Lib.Consable (Consable(..))
27 import qualified Hcompta.Lib.Interval as Interval
28 import Hcompta.Lib.Interval (Interval)
29 import qualified Hcompta.Tag as Tag
31 -- * Requirements' interface
36 unit_text :: a -> Text
38 instance Unit Amount.Unit where
39 unit_text = Amount.Unit.text
44 ( Data (Amount_Unit a)
46 , Show (Amount_Unit a)
47 , Unit (Amount_Unit a)
51 amount_unit :: a -> Amount_Unit a
53 instance Amount Amount.Amount where
54 type Amount_Unit Amount.Amount = Amount.Unit
55 amount_unit = Amount.unit
59 class Amount (Posting_Amount p)
62 posting_account :: p -> Account
63 posting_amounts :: p -> Map (Amount_Unit (Posting_Amount p)) (Posting_Amount p)
65 -- ** Class 'Transaction'
68 ( Posting (Transaction_Posting t)
69 , Foldable (Transaction_Postings t)
71 => Transaction t where
72 type Transaction_Posting t
73 type Transaction_Postings t :: * -> *
74 transaction_date :: t -> Date
75 transaction_postings :: t -> Transaction_Postings t (Transaction_Posting t)
76 transaction_postings_size :: t -> Int
77 transaction_postings_size = foldr (const $ (+) 1) 0 . transaction_postings
78 transaction_tags :: t -> Map Tag.Path [Tag.Value]
82 data Transaction t => Stats t
84 { stats_accounts :: !(Map Account ())
85 , stats_tags :: !(Map Tag.Path (Map Text Integer))
86 , stats_transactions :: !Integer
87 , stats_transactions_span :: !(Maybe (Interval Date))
88 , stats_units :: !(Map (Amount_Unit (Posting_Amount (Transaction_Posting t))) ())
90 deriving instance ( Transaction transaction
92 ) => Data (Stats transaction)
93 deriving instance ( Transaction transaction
95 ) => Eq (Stats transaction)
96 deriving instance ( Transaction transaction
98 ) => Show (Stats transaction)
99 deriving instance Typeable1 Stats
101 empty :: Transaction t => Stats t
104 { stats_accounts = mempty
105 , stats_tags = mempty
106 , stats_transactions = 0
107 , stats_transactions_span = Nothing
108 , stats_units = mempty
111 stats_accounts_depths :: Transaction t => Stats t -> Interval Integer
112 stats_accounts_depths s =
113 case Data.Map.keys $ stats_accounts s of
114 [] -> Interval.point 0
117 (Interval.span . Interval.point . Account.depth)
118 (Interval.point $ Account.depth a) as
120 -- | Return the given 'Stats'
121 -- updated by the given 'Transaction'.
123 -- NOTE: to reduce memory consumption when 'cons'ing iteratively,
124 -- the given 'Stats' is matched strictly.
125 cons :: Transaction t => t -> Stats t -> Stats t
130 (flip $ (\p -> Data.Map.insert (posting_account p) ()))
132 (transaction_postings t)
134 Data.Map.mergeWithKey
136 Data.Map.unionWith (+) x1 $
137 Data.Map.fromListWith (+) $ (, 1) <$> x2)
138 id ((Data.Map.fromListWith (+) . ((, 1) <$>)) <$>)
139 (stats_tags s) -- Map Text (Map Text Integer)
140 (transaction_tags t) -- Map Text [Text]
141 , stats_transactions = 1 + (stats_transactions s)
142 , stats_transactions_span =
143 let i = Interval.point $ transaction_date t in
144 maybe (Just i) (Just . Interval.span i)
145 (stats_transactions_span s)
150 (flip $ (\a -> Data.Map.insert (amount_unit a) ()))
151 su . posting_amounts)
153 (transaction_postings t)
156 union :: Transaction t => Stats t -> Stats t -> Stats t
166 (Data.Map.unionWith (+))
169 , stats_transactions =
171 (stats_transactions s0)
172 (stats_transactions s1)
173 , stats_transactions_span = do
175 ( stats_transactions_span s0
176 , stats_transactions_span s1
178 (Nothing, Nothing) -> Nothing
179 (Just i0, Nothing) -> Just i0
180 (Nothing, Just i1) -> Just i1
181 (Just i0, Just i1) -> Just $ Interval.span i0 i1
189 instance Transaction t => Monoid (Stats t) where
193 instance Transaction t => Consable () (Stats) t where
194 mcons () t !s = cons t s