lib: multiBalanceReport: Break calculateAccountChanges and acctChangesFromPostings separate functions.
This commit is contained in:
		
							parent
							
								
									a72c4f285b
								
							
						
					
					
						commit
						0dcfddd201
					
				| @ -22,8 +22,11 @@ module Hledger.Reports.MultiBalanceReport ( | |||||||
| ) | ) | ||||||
| where | where | ||||||
| 
 | 
 | ||||||
|  | import Data.Foldable (toList) | ||||||
| import Data.List | import Data.List | ||||||
| import Data.List.Extra (nubSort) | import Data.List.Extra (nubSort) | ||||||
|  | import Data.HashMap.Strict (HashMap) | ||||||
|  | import qualified Data.HashMap.Strict as HM | ||||||
| import Data.Map (Map) | import Data.Map (Map) | ||||||
| import qualified Data.Map as M | import qualified Data.Map as M | ||||||
| import Data.Maybe | import Data.Maybe | ||||||
| @ -125,12 +128,9 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle = | |||||||
|       startbals = dbg' "startbals" $ startingBalances ropts q j' reportspan |       startbals = dbg' "startbals" $ startingBalances ropts q j' reportspan | ||||||
|       -- The matched accounts with a starting balance. All of these should appear |       -- The matched accounts with a starting balance. All of these should appear | ||||||
|       -- in the report even if they have no postings during the report period. |       -- in the report even if they have no postings during the report period. | ||||||
|       startaccts = dbg'' "startaccts" $ map fst startbals |       startaccts = dbg'' "startaccts" $ HM.keys startbals | ||||||
|       -- Helpers to look up an account's starting balance. |       -- Helpers to look up an account's starting balance. | ||||||
|       startingBalanceFor a = fromMaybe nullmixedamt $ lookup a startbals |       startingBalanceFor a = HM.lookupDefault nullmixedamt a startbals | ||||||
| 
 |  | ||||||
|       ---------------------------------------------------------------------- |  | ||||||
|       -- 3. Gather postings for each column. |  | ||||||
| 
 | 
 | ||||||
|       -- Postings matching the query within the report period. |       -- Postings matching the query within the report period. | ||||||
|       ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts reportq j' |       ps :: [(Posting, Day)] = dbg'' "ps" $ getPostings ropts reportq j' | ||||||
| @ -142,21 +142,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle = | |||||||
|       -- Group postings into their columns. |       -- Group postings into their columns. | ||||||
|       colps = dbg'' "colps" $ calculateColumns colspans ps |       colps = dbg'' "colps" $ calculateColumns colspans ps | ||||||
| 
 | 
 | ||||||
|       ---------------------------------------------------------------------- |       -- Each account's balance changes across all columns. | ||||||
|       -- 4. Calculate account balance changes in each column. |       acctchanges = dbg'' "acctchanges" $ calculateAccountChanges ropts q startbals colps | ||||||
| 
 |  | ||||||
|       -- In each column, gather the accounts that have postings and their change amount. |  | ||||||
|       acctChangesFromPostings :: [Posting] -> [(ClippedAccountName, MixedAmount)] |  | ||||||
|       acctChangesFromPostings ps = [(aname a, (if tree_ ropts then aibalance else aebalance) a) | a <- as] |  | ||||||
|           where |  | ||||||
|             as = depthLimit $ |  | ||||||
|                  (if tree_ ropts then id else filter ((>0).anumpostings)) $ |  | ||||||
|                  drop 1 $ accountsFromPostings ps |  | ||||||
|             depthLimit |  | ||||||
|                 | tree_ ropts = filter ((depthq `matchesAccount`).aname) -- exclude deeper balances |  | ||||||
|                 | otherwise   = clipAccountsAndAggregate depth -- aggregate deeper balances at the depth limit |  | ||||||
|       colacctchanges :: [[(ClippedAccountName, MixedAmount)]] = |  | ||||||
|           dbg'' "colacctchanges" $ map (acctChangesFromPostings . snd) $ M.toList colps |  | ||||||
| 
 | 
 | ||||||
|       ---------------------------------------------------------------------- |       ---------------------------------------------------------------------- | ||||||
|       -- 5. Gather the account balance changes into a regular matrix including the accounts |       -- 5. Gather the account balance changes into a regular matrix including the accounts | ||||||
| @ -173,16 +160,6 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle = | |||||||
|         where |         where | ||||||
|           allpostedaccts :: [AccountName] = |           allpostedaccts :: [AccountName] = | ||||||
|             dbg'' "allpostedaccts" . sort . accountNamesFromPostings $ map fst ps |             dbg'' "allpostedaccts" . sort . accountNamesFromPostings $ map fst ps | ||||||
|       -- Each column's balance changes for each account, adding zeroes where needed. |  | ||||||
|       colallacctchanges :: [[(ClippedAccountName, MixedAmount)]] = |  | ||||||
|           dbg'' "colallacctchanges" |  | ||||||
|           [ sortOn fst $ unionBy (\(a,_) (a',_) -> a == a') postedacctchanges zeroes |  | ||||||
|              | postedacctchanges <- colacctchanges ] |  | ||||||
|           where zeroes = [(a, nullmixedamt) | a <- displayaccts] |  | ||||||
|       -- Transpose to get each account's balance changes across all columns. |  | ||||||
|       acctchanges :: [(ClippedAccountName, [MixedAmount])] = |  | ||||||
|           dbg'' "acctchanges" |  | ||||||
|           [(a, map snd abs) | abs@((a,_):_) <- transpose colallacctchanges] -- never null, or used when null... |  | ||||||
| 
 | 
 | ||||||
|       ---------------------------------------------------------------------- |       ---------------------------------------------------------------------- | ||||||
|       -- 6. Build the report rows. |       -- 6. Build the report rows. | ||||||
| @ -191,7 +168,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle = | |||||||
|       rows :: [MultiBalanceReportRow] = |       rows :: [MultiBalanceReportRow] = | ||||||
|           dbg'' "rows" $ |           dbg'' "rows" $ | ||||||
|           [ PeriodicReportRow a (accountNameLevel a) valuedrowbals rowtot rowavg |           [ PeriodicReportRow a (accountNameLevel a) valuedrowbals rowtot rowavg | ||||||
|            | (a,changes) <- dbg'' "acctchanges" acctchanges |            | (a,changesMap) <- HM.toList acctchanges | ||||||
|  |            , let changes = toList changesMap | ||||||
|              -- The row amounts to be displayed: per-period changes, |              -- The row amounts to be displayed: per-period changes, | ||||||
|              -- zero-based cumulative totals, or |              -- zero-based cumulative totals, or | ||||||
|              -- starting-balance-based historical balances. |              -- starting-balance-based historical balances. | ||||||
| @ -315,8 +293,8 @@ multiBalanceReportWith ropts@ReportOpts{..} q j priceoracle = | |||||||
| -- | -- | ||||||
| -- Balances at report start date, from all earlier postings which otherwise match the query. | -- Balances at report start date, from all earlier postings which otherwise match the query. | ||||||
| -- These balances are unvalued except maybe converted to cost. | -- These balances are unvalued except maybe converted to cost. | ||||||
| startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> [(AccountName, MixedAmount)] | startingBalances :: ReportOpts -> Query -> Journal -> DateSpan -> HashMap AccountName MixedAmount | ||||||
| startingBalances ropts q j reportspan = map (\(a,_,_,b) -> (a,b)) startbalanceitems | startingBalances ropts q j reportspan = HM.fromList $ map (\(a,_,_,b) -> (a,b)) startbalanceitems | ||||||
|   where |   where | ||||||
|     (startbalanceitems,_) = dbg'' "starting balance report" $ |     (startbalanceitems,_) = dbg'' "starting balance report" $ | ||||||
|         balanceReport ropts''{value_=Nothing, percent_=False} startbalq j |         balanceReport ropts''{value_=Nothing, percent_=False} startbalq j | ||||||
| @ -386,6 +364,37 @@ calculateColumns colspans = foldr addPosting emptyMap | |||||||
|     addPosting (p, d) = maybe id (M.adjust (p:)) $ latestSpanContaining colspans d |     addPosting (p, d) = maybe id (M.adjust (p:)) $ latestSpanContaining colspans d | ||||||
|     emptyMap = M.fromList . zip colspans $ repeat [] |     emptyMap = M.fromList . zip colspans $ repeat [] | ||||||
| 
 | 
 | ||||||
|  | -- | Calculate account balance changes in each column. | ||||||
|  | -- | ||||||
|  | -- In each column, gather the accounts that have postings and their change amount. | ||||||
|  | acctChangesFromPostings :: ReportOpts -> Query -> [Posting] -> HashMap ClippedAccountName MixedAmount | ||||||
|  | acctChangesFromPostings ropts q ps = | ||||||
|  |     HM.fromList [(aname a, (if tree_ ropts then aibalance else aebalance) a) | a <- as] | ||||||
|  |   where | ||||||
|  |     as = depthLimit $ | ||||||
|  |          (if tree_ ropts then id else filter ((>0).anumpostings)) $ | ||||||
|  |          drop 1 $ accountsFromPostings ps | ||||||
|  |     depthLimit | ||||||
|  |       | tree_ ropts = filter ((depthq `matchesAccount`) . aname) -- exclude deeper balances | ||||||
|  |       | otherwise   = clipAccountsAndAggregate $ queryDepth depthq -- aggregate deeper balances at the depth limit | ||||||
|  |     depthq = dbg1 "depthq" $ filterQuery queryIsDepth q | ||||||
|  | 
 | ||||||
|  | -- | Gather the account balance changes into a regular matrix including the accounts | ||||||
|  | -- from all columns | ||||||
|  | calculateAccountChanges :: ReportOpts -> Query | ||||||
|  |                         -> HashMap ClippedAccountName MixedAmount | ||||||
|  |                         -> Map DateSpan [Posting] | ||||||
|  |                         -> HashMap ClippedAccountName (Map DateSpan MixedAmount) | ||||||
|  | calculateAccountChanges ropts q startbals colps = acctchanges | ||||||
|  |   where | ||||||
|  |     -- Transpose to get each account's balance changes across all columns. | ||||||
|  |     acctchanges = transposeMap colacctchanges <> (zeros <$ startbals) | ||||||
|  | 
 | ||||||
|  |     colacctchanges :: Map DateSpan (HashMap ClippedAccountName MixedAmount) = | ||||||
|  |       dbg'' "colacctchanges" $ fmap (acctChangesFromPostings ropts q) colps | ||||||
|  | 
 | ||||||
|  |     zeros = nullmixedamt <$ colacctchanges | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| -- | Generates a simple non-columnar BalanceReport, but using multiBalanceReport, | -- | Generates a simple non-columnar BalanceReport, but using multiBalanceReport, | ||||||
| -- in order to support --historical. Does not support tree-mode boring parent eliding. | -- in order to support --historical. Does not support tree-mode boring parent eliding. | ||||||
| @ -403,6 +412,20 @@ balanceReportFromMultiBalanceReport opts q j = (rows', total) | |||||||
|              ) | PeriodicReportRow a d amts _ _ <- rows] |              ) | PeriodicReportRow a d amts _ _ <- rows] | ||||||
|     total = headDef nullmixedamt totals |     total = headDef nullmixedamt totals | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | -- | Transpose a Map of HashMaps to a HashMap of Maps. | ||||||
|  | transposeMap :: Map DateSpan (HashMap AccountName MixedAmount) | ||||||
|  |              -> HashMap AccountName (Map DateSpan MixedAmount) | ||||||
|  | transposeMap xs = M.foldrWithKey addSpan mempty xs | ||||||
|  |   where | ||||||
|  |     addSpan span acctmap seen = HM.foldrWithKey (addAcctSpan span) seen acctmap | ||||||
|  | 
 | ||||||
|  |     addAcctSpan span acct a = HM.alter f acct | ||||||
|  |       where f = Just . M.insert span a . fromMaybe emptySpanMap | ||||||
|  | 
 | ||||||
|  |     emptySpanMap = nullmixedamt <$ xs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| -- Local debug helper | -- Local debug helper | ||||||
| -- add a prefix to this function's debug output | -- add a prefix to this function's debug output | ||||||
| dbg   s = let p = "multiBalanceReport" in Hledger.Utils.dbg3 (p++" "++s) | dbg   s = let p = "multiBalanceReport" in Hledger.Utils.dbg3 (p++" "++s) | ||||||
| @ -411,7 +434,6 @@ dbg'' s = let p = "multiBalanceReport" in Hledger.Utils.dbg5 (p++" "++s) | |||||||
| -- dbg = const id  -- exclude this function from debug output | -- dbg = const id  -- exclude this function from debug output | ||||||
| 
 | 
 | ||||||
| -- common rendering helper, XXX here for now | -- common rendering helper, XXX here for now | ||||||
| 
 |  | ||||||
| tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String | tableAsText :: ReportOpts -> (a -> String) -> Table String String a -> String | ||||||
| tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell = | tableAsText (ReportOpts{pretty_tables_ = pretty}) showcell = | ||||||
|   unlines |   unlines | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| cabal-version: 1.12 | cabal-version: 1.12 | ||||||
| 
 | 
 | ||||||
| -- This file has been generated from package.yaml by hpack version 0.33.0. | -- This file has been generated from package.yaml by hpack version 0.33.1. | ||||||
| -- | -- | ||||||
| -- see: https://github.com/sol/hpack | -- see: https://github.com/sol/hpack | ||||||
| -- | -- | ||||||
| -- hash: c30491f8c77b1d38a1992455cc9c340cbcb17e95ec5c07085f9987b289747ba1 | -- hash: dd7c200231996bc96dfb65f042843355e9f7db7002d68c953ada6e89cedd5cc5 | ||||||
| 
 | 
 | ||||||
| name:           hledger-lib | name:           hledger-lib | ||||||
| version:        1.18.99 | version:        1.18.99 | ||||||
| @ -149,6 +149,7 @@ library | |||||||
|     , timeit |     , timeit | ||||||
|     , transformers >=0.2 |     , transformers >=0.2 | ||||||
|     , uglymemo |     , uglymemo | ||||||
|  |     , unordered-containers >=0.2 | ||||||
|     , utf8-string >=0.3.5 |     , utf8-string >=0.3.5 | ||||||
|   default-language: Haskell2010 |   default-language: Haskell2010 | ||||||
| 
 | 
 | ||||||
| @ -202,6 +203,7 @@ test-suite doctest | |||||||
|     , timeit |     , timeit | ||||||
|     , transformers >=0.2 |     , transformers >=0.2 | ||||||
|     , uglymemo |     , uglymemo | ||||||
|  |     , unordered-containers >=0.2 | ||||||
|     , utf8-string >=0.3.5 |     , utf8-string >=0.3.5 | ||||||
|   if (impl(ghc < 8.2)) |   if (impl(ghc < 8.2)) | ||||||
|     buildable: False |     buildable: False | ||||||
| @ -257,6 +259,7 @@ test-suite unittest | |||||||
|     , timeit |     , timeit | ||||||
|     , transformers >=0.2 |     , transformers >=0.2 | ||||||
|     , uglymemo |     , uglymemo | ||||||
|  |     , unordered-containers >=0.2 | ||||||
|     , utf8-string >=0.3.5 |     , utf8-string >=0.3.5 | ||||||
|   buildable: True |   buildable: True | ||||||
|   default-language: Haskell2010 |   default-language: Haskell2010 | ||||||
|  | |||||||
| @ -82,6 +82,7 @@ dependencies: | |||||||
| - time >=1.5 | - time >=1.5 | ||||||
| - timeit | - timeit | ||||||
| - transformers >=0.2 | - transformers >=0.2 | ||||||
|  | - unordered-containers >=0.2 | ||||||
| - uglymemo | - uglymemo | ||||||
| - utf8-string >=0.3.5 | - utf8-string >=0.3.5 | ||||||
| - extra >=1.6.3 | - extra >=1.6.3 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user