balance, register, register-csv: depth 0 shows summary items (#206)

Previously, a depth:0 query produced an empty report (since there are no
level zero accounts). Now, it aggregates all data into one summary item
with account name "...".

This makes it easier to see the kind of data Gwern was looking for from
register-csv (net worth over time). Eg this shows one line per month
summarising the total of assets and liabilities:

hledger register-csv -- -MHE ^assets ^liabilities depth:0

Single and multi-column balance reports behave similarly.
This commit is contained in:
Simon Michael 2014-10-19 17:53:20 -07:00
parent 0620ec9840
commit 7f6cf1f849
7 changed files with 79 additions and 17 deletions

View File

@ -125,7 +125,7 @@ clipAccounts d a = a{asubs=subs}
clipAccountsAndAggregate :: Int -> [Account] -> [Account]
clipAccountsAndAggregate d as = combined
where
clipped = [a{aname=clipAccountName d $ aname a} | a <- as]
clipped = [a{aname=clipOrEllipsifyAccountName d $ aname a} | a <- as]
combined = [a{aebalance=sum (map aebalance same)}
| same@(a:_) <- groupBy (\a1 a2 -> aname a1 == aname a2) clipped]
{-

View File

@ -108,9 +108,17 @@ elideAccountName width s =
| length ss > 1 = elideparts width (done++[take 2 $ head ss]) (tail ss)
| otherwise = done++ss
-- | Keep only the first n components of an account name, where n
-- is a positive integer. If n is 0, returns the empty string.
clipAccountName :: Int -> AccountName -> AccountName
clipAccountName n = accountNameFromComponents . take n . accountNameComponents
-- | Keep only the first n components of an account name, where n
-- is a positive integer. If n is 0, returns "...".
clipOrEllipsifyAccountName :: Int -> AccountName -> AccountName
clipOrEllipsifyAccountName 0 = const "..."
clipOrEllipsifyAccountName n = accountNameFromComponents . take n . accountNameComponents
-- | Convert an account name to a regular expression matching it and its subaccounts.
accountNameToAccountRegex :: String -> String
accountNameToAccountRegex "" = ""

View File

@ -67,6 +67,9 @@ balanceReport opts q j = (items, total)
accts = ledgerRootAccount $ ledgerFromJournal q $ journalSelectingAmountFromOpts opts j
accts' :: [Account]
| queryDepth q == 0 =
dbg "accts" $
take 1 $ clipAccountsAndAggregate (queryDepth q) $ flattenAccounts accts
| flat_ opts = dbg "accts" $
filterzeros $
filterempty $
@ -75,7 +78,8 @@ balanceReport opts q j = (items, total)
filter (not.aboring) $
drop 1 $ flattenAccounts $
markboring $
prunezeros $ clipAccounts (queryDepth q) accts
prunezeros $
clipAccounts (queryDepth q) accts
where
balance = if flat_ opts then aebalance else aibalance
filterzeros = if empty_ opts then id else filter (not . isZeroMixedAmount . balance)
@ -99,14 +103,18 @@ markBoringParentAccounts = tieAccountParents . mapAccounts mark
| otherwise = a
balanceReportItem :: ReportOpts -> Query -> Account -> BalanceReportItem
balanceReportItem opts _ a@Account{aname=name}
balanceReportItem opts q a
| flat_ opts = ((name, name, 0), (if flatShowsExclusiveBalance then aebalance else aibalance) a)
| otherwise = ((name, elidedname, indent), aibalance a)
where
name | queryDepth q > 0 = aname a
| otherwise = "..."
elidedname = accountNameFromComponents (adjacentboringparentnames ++ [accountLeafName name])
adjacentboringparentnames = reverse $ map (accountLeafName.aname) $ takeWhile aboring $ parents
indent = length $ filter (not.aboring) parents
parents = init $ parentAccounts a
-- parents exclude the tree's root node
parents = case parentAccounts a of [] -> []
as -> init as
-- -- the above using the newer multi balance report code:
-- balanceReport' opts q j = (items, total)

View File

@ -129,7 +129,7 @@ multiBalanceReport opts q j = MultiBalanceReport (displayspans, items, totals)
displayedAccts :: [ClippedAccountName] =
dbg "displayedAccts" $
(if tree_ opts then expandAccountNames else id) $
nub $ map (clipAccountName depth) $
nub $ map (clipOrEllipsifyAccountName depth) $
if empty_ opts then nub $ sort $ startAccts ++ postedAccts else postedAccts
acctBalChangesPerSpan :: [[(ClippedAccountName, MixedAmount)]] =
@ -150,7 +150,7 @@ multiBalanceReport opts q j = MultiBalanceReport (displayspans, items, totals)
HistoricalBalance -> drop 1 $ scanl (+) (startingBalanceFor a) changes
CumulativeBalance -> drop 1 $ scanl (+) nullmixedamt changes
_ -> changes
, empty_ opts || any (not . isZeroMixedAmount) displayedBals
, empty_ opts || depth == 0 || any (not . isZeroMixedAmount) displayedBals
]
totals :: [MixedAmount] =
@ -162,6 +162,6 @@ multiBalanceReport opts q j = MultiBalanceReport (displayspans, items, totals)
dbg "highestlevelaccts" $
[a | a <- displayedAccts, not $ any (`elem` displayedAccts) $ init $ expandAccountName a]
dbg s = let p = "multiBalanceReport" in Hledger.Utils.dbg (p++" "++s) -- add prefix in debug output
-- dbg = const id -- exclude from debug output
dbg s = let p = "multiBalanceReport" in Hledger.Utils.dbg (p++" "++s) -- add prefix in this function's debug output
-- dbg = const id -- exclude this function from debug output

View File

@ -85,7 +85,7 @@ postingsReport opts q j = (totallabel, items)
whichdate = whichDateFromOpts opts
itemps | interval == NoInterval = map (,Nothing) reportps
| otherwise = summarisePostingsByInterval interval whichdate depth showempty reportspan reportps
items = postingsReportItems itemps (nullposting,Nothing) whichdate depth startbal runningcalc 1
items = dbg "items" $ postingsReportItems itemps (nullposting,Nothing) whichdate depth startbal runningcalc 1
where
startbal = if balancetype_ opts == HistoricalBalance then sumPostings precedingps else 0
runningcalc | average_ opts = \i avg amt -> avg + (amt - avg) `divideMixedAmount` (fromIntegral i) -- running average
@ -108,7 +108,7 @@ postingsReportItems ((p,menddate):ps) (pprev,menddateprev) wd d b runningcalcfn
isfirstintxn = ptransaction p /= ptransaction pprev
isdifferentdate = case wd of PrimaryDate -> postingDate p /= postingDate pprev
SecondaryDate -> postingDate2 p /= postingDate2 pprev
p' = p{paccount=clipAccountName d $ paccount p}
p' = p{paccount= clipOrEllipsifyAccountName d $ paccount p}
b' = runningcalcfn itemnum b (pamount p)
-- | Generate one postings report line item, containing the posting,
@ -150,8 +150,10 @@ type SummaryPosting = (Posting, Maybe Day)
-- postings within it, aggregate the postings into one summary posting per
-- account.
--
-- When a depth argument is present, postings to accounts of greater depth are
-- also aggregated where possible.
-- When a depth argument is present, postings to accounts of greater
-- depth are also aggregated where possible. If the depth is 0, all
-- postings in the span are aggregated into a single posting with
-- account name "...".
--
-- The showempty flag includes spans with no postings and also postings
-- with 0 amount.
@ -166,8 +168,10 @@ summarisePostingsInDateSpan (DateSpan b e) wd depth showempty ps
b' = fromMaybe (maybe nulldate postingdate $ headMay ps) b
e' = fromMaybe (maybe (addDays 1 nulldate) postingdate $ lastMay ps) e
summaryp = nullposting{pdate=Just b'}
clippedanames = nub $ map (clipAccountName depth) anames
summaryps = [summaryp{paccount=a,pamount=balance a} | a <- clippedanames]
clippedanames | depth > 0 = nub $ map (clipAccountName depth) anames
| otherwise = ["..."]
summaryps | depth > 0 = [summaryp{paccount=a,pamount=balance a} | a <- clippedanames]
| otherwise = [summaryp{paccount="...",pamount=sum $ map pamount ps}]
summarypes = map (, Just e') $ (if showempty then id else filter (not . isZeroMixedAmount . pamount)) summaryps
anames = sort $ nub $ map paccount ps
-- aggregate balances by account, like ledgerFromJournal, then do depth-clipping

View File

@ -1,4 +1,4 @@
# 1
# 1.
hledgerdev -f sample.journal balance --no-total --depth 1
>>>
$-1 assets
@ -7,3 +7,22 @@ hledgerdev -f sample.journal balance --no-total --depth 1
$1 liabilities
>>>=0
# 2. Depth 0 aggregates everything into one line
hledgerdev -f sample.journal balance --no-total --depth 0
>>>
0 ...
>>>=0
# 3. Ditto in a multi-column balance report.
hledgerdev -f sample.journal balance -M -e 2008/4 --depth 0
>>>
Balance changes in 2008/01:
|| 2008/01
=====++==========
... || 0
-----++----------
|| 0
>>>=0

View File

@ -8,7 +8,7 @@ hledgerdev -f - register aa --depth 1
2010/01/01 x a 1 1
>>>=0
# 2. similar to above, postings with same clipped account name are not aggregated
# 2. separate postings remain separate
hledgerdev -f - register aa --depth 2
<<<
2010/1/1 x
@ -28,7 +28,7 @@ hledgerdev -f - register aa --depth 2
2010/01/02 z a:aa 1 3
>>>=0
# 3. as above, but with a reporting interval causing postings to be aggregated
# 3. with a reporting interval, all postings are aggregated under each (clipped) account
hledgerdev -f - register aa --depth 1 --daily
<<<
2010/1/1 x
@ -56,3 +56,26 @@ hledgerdev -f - register a --depth 1 --cleared
2012/01/01 (a) 1 1
>>>2
>>>=0
# 5. depth 0 aggregates everything into a single line
hledgerdev -f - register --depth 0 --daily a b
<<<
2010/1/1 x
a:aa 1
b:bb 2
c:cc
2010/1/1 y
a:aa 1
b:bb 2
c:cc
2010/1/2 z
a:aa 1
b:bb 2
c:cc
>>>
2010/01/01d ... 6 6
2010/01/02d ... 3 9
>>>=0