imp: csv: Allow for generating tidy csv with --layout=tidy.

This puts every date in a separate row, which is more suitable for
many graphing programs.
This commit is contained in:
Stephen Morgan 2021-12-03 22:28:23 +11:00 committed by Simon Michael
parent 3884f90cc6
commit 7ccf7430d0
4 changed files with 74 additions and 14 deletions

View File

@ -112,6 +112,7 @@ instance Default AccountListMode where def = ALFlat
data Layout = LayoutWide (Maybe Int)
| LayoutTall
| LayoutBare
| LayoutTidy
deriving (Eq, Show)
-- | Standard options for customising report filtering and output.
@ -348,6 +349,7 @@ layoutopt rawopts = fromMaybe (LayoutWide Nothing) $ layout <|> column
checkNames = [ ("wide", LayoutWide w)
, ("tall", LayoutTall)
, ("bare", LayoutBare)
, ("tidy", LayoutTidy)
]
-- For `--layout=elided,n`, elide to the given width
(s,n) = break (==',') $ map toLower opt
@ -356,7 +358,7 @@ layoutopt rawopts = fromMaybe (LayoutWide Nothing) $ layout <|> column
c | Just w <- readMay c -> Just w
_ -> usageError "width in --layout=wide,WIDTH must be an integer"
err = usageError "--layout's argument should be \"wide[,WIDTH]\", \"tall\", or \"bare\""
err = usageError "--layout's argument should be \"wide[,WIDTH]\", \"tall\", \"bare\", or \"tidy\""
-- Get the period specified by any -b/--begin, -e/--end and/or -p/--period
-- options appearing in the command line.

View File

@ -525,13 +525,17 @@ multiBalanceReportAsCsv opts@ReportOpts{..} =
multiBalanceReportAsCsv' :: ReportOpts -> MultiBalanceReport -> (CSV, CSV)
multiBalanceReportAsCsv' opts@ReportOpts{..} (PeriodicReport colspans items tr) =
( ("account" : ["commodity" | layout_ == LayoutBare] ++ map showDateSpan colspans
++ ["total" | row_total_]
++ ["average" | average_]
) : concatMap (fullRowAsTexts (accountNameDrop drop_ . prrFullName)) items
, totalrows)
( headers : concatMap (fullRowAsTexts (accountNameDrop drop_ . prrFullName)) items
, totalrows
)
where
fullRowAsTexts render row = (render row :) <$> multiBalanceRowAsCsvText opts row
headers = "account" : case layout_ of
LayoutTidy -> ["date", "commodity", "value"]
LayoutBare -> "commodity" : dateHeaders
_ -> dateHeaders
dateHeaders = map showDateSpan colspans ++ ["total" | row_total_] ++ ["average" | average_]
fullRowAsTexts render row = map (render row :) $ multiBalanceRowAsCsvText opts colspans row
totalrows
| no_total_ = mempty
| otherwise = fullRowAsTexts (const "total") tr
@ -692,8 +696,8 @@ balanceReportAsTable opts@ReportOpts{average_, row_total_, balanceaccum_}
maybetranspose | transpose_ opts = \(Table rh ch vals) -> Table ch rh (transpose vals)
| otherwise = id
multiBalanceRowAsWbs :: AmountDisplayOpts -> ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
multiBalanceRowAsWbs bopts ReportOpts{..} (PeriodicReportRow _ as rowtot rowavg) =
multiBalanceRowAsWbs :: AmountDisplayOpts -> ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
multiBalanceRowAsWbs bopts ReportOpts{..} colspans (PeriodicReportRow _ as rowtot rowavg) =
case layout_ of
LayoutWide width -> [fmap (showMixedAmountB bopts{displayMaxWidth=width}) all]
LayoutTall -> paddedTranspose mempty
@ -703,12 +707,20 @@ multiBalanceRowAsWbs bopts ReportOpts{..} (PeriodicReportRow _ as rowtot rowavg)
. transpose -- each row becomes a list of Text quantities
. fmap (showMixedAmountLinesB bopts{displayOrder=Just cs, displayMinWidth=Nothing})
$ all
LayoutTidy -> concat
. zipWith (\d -> map (wbFromText d :)) dates
. fmap ( zipWith (\c a -> [wbFromText c, a]) cs
. showMixedAmountLinesB bopts{displayOrder=Just cs, displayMinWidth=Nothing})
$ all
where
totalscolumn = row_total_ && balanceaccum_ `notElem` [Cumulative, Historical]
cs = S.toList . foldl' S.union mempty $ fmap maCommodities all
all = as
++ [rowtot | totalscolumn && not (null as)]
++ [rowavg | average_ && not (null as)]
dates = map showDateSpan colspans
++ ["Total" | totalscolumn && not (null as)]
++ ["Average" | average_ && not (null as)]
paddedTranspose :: a -> [[a]] -> [[a]]
paddedTranspose _ [] = [[]]
@ -724,11 +736,11 @@ multiBalanceRowAsWbs bopts ReportOpts{..} (PeriodicReportRow _ as rowtot rowavg)
m (x:xs) = x:xs
m [] = [n]
multiBalanceRowAsCsvText :: ReportOpts -> PeriodicReportRow a MixedAmount -> [[T.Text]]
multiBalanceRowAsCsvText opts = fmap (fmap wbToText) . multiBalanceRowAsWbs (balanceOpts False opts) opts
multiBalanceRowAsCsvText :: ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[T.Text]]
multiBalanceRowAsCsvText opts colspans = fmap (fmap wbToText) . multiBalanceRowAsWbs (balanceOpts False opts) opts colspans
multiBalanceRowAsTableText :: ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
multiBalanceRowAsTableText opts = multiBalanceRowAsWbs (balanceOpts True opts) opts
multiBalanceRowAsTableText opts = multiBalanceRowAsWbs (balanceOpts True opts) opts []
-- | Amount display options to use for balance reports
balanceOpts :: Bool -> ReportOpts -> AmountDisplayOpts

View File

@ -272,7 +272,7 @@ compoundBalanceReportAsCsv ropts (CompoundPeriodicReport title colspans subrepor
map (length . prDates . second3) subreports
addtotals
| no_total_ ropts || length subreports == 1 = id
| otherwise = (++ fmap ("Net:" : ) (multiBalanceRowAsCsvText ropts netrow))
| otherwise = (++ fmap ("Net:" : ) (multiBalanceRowAsCsvText ropts colspans netrow))
-- | Render a compound balance report as HTML.
compoundBalanceReportAsHtml :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> Html ()
@ -309,7 +309,7 @@ compoundBalanceReportAsHtml ropts cbr =
++ [blankrow]
totalrows | no_total_ ropts || length subreports == 1 = []
| otherwise = multiBalanceReportHtmlFootRow ropts <$> (("Net:" :) <$> multiBalanceRowAsCsvText ropts netrow)
| otherwise = multiBalanceReportHtmlFootRow ropts <$> (("Net:" :) <$> multiBalanceRowAsCsvText ropts colspans netrow)
in do
style_ (T.unlines [""
,"td { padding:0 0.5em; }"

View File

@ -235,3 +235,49 @@ Balance Sheet 2014-10-11
|| VEA 36.00
|| VHT 294.00
>=0
# 13. Multicolumn balance report csv output with --layout=tidy
$ hledger -f bcexample.hledger bal -T -Y assets.*etrade -3 -O csv --layout=tidy
>
"account","date","commodity","value"
"Assets:US:ETrade","2012","GLD","0"
"Assets:US:ETrade","2012","ITOT","10.00"
"Assets:US:ETrade","2012","USD","337.18"
"Assets:US:ETrade","2012","VEA","12.00"
"Assets:US:ETrade","2012","VHT","106.00"
"Assets:US:ETrade","2013","GLD","70.00"
"Assets:US:ETrade","2013","ITOT","18.00"
"Assets:US:ETrade","2013","USD","-98.12"
"Assets:US:ETrade","2013","VEA","10.00"
"Assets:US:ETrade","2013","VHT","18.00"
"Assets:US:ETrade","2014","GLD","0"
"Assets:US:ETrade","2014","ITOT","-11.00"
"Assets:US:ETrade","2014","USD","4881.44"
"Assets:US:ETrade","2014","VEA","14.00"
"Assets:US:ETrade","2014","VHT","170.00"
"Assets:US:ETrade","Total","GLD","70.00"
"Assets:US:ETrade","Total","ITOT","17.00"
"Assets:US:ETrade","Total","USD","5120.50"
"Assets:US:ETrade","Total","VEA","36.00"
"Assets:US:ETrade","Total","VHT","294.00"
"total","2012","GLD","0"
"total","2012","ITOT","10.00"
"total","2012","USD","337.18"
"total","2012","VEA","12.00"
"total","2012","VHT","106.00"
"total","2013","GLD","70.00"
"total","2013","ITOT","18.00"
"total","2013","USD","-98.12"
"total","2013","VEA","10.00"
"total","2013","VHT","18.00"
"total","2014","GLD","0"
"total","2014","ITOT","-11.00"
"total","2014","USD","4881.44"
"total","2014","VEA","14.00"
"total","2014","VHT","170.00"
"total","Total","GLD","70.00"
"total","Total","ITOT","17.00"
"total","Total","USD","5120.50"
"total","Total","VEA","36.00"
"total","Total","VHT","294.00"
>=0