From 7ccf7430d0043563618e341461754922f015e37f Mon Sep 17 00:00:00 2001 From: Stephen Morgan Date: Fri, 3 Dec 2021 22:28:23 +1100 Subject: [PATCH] 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. --- hledger-lib/Hledger/Reports/ReportOptions.hs | 4 +- hledger/Hledger/Cli/Commands/Balance.hs | 34 +++++++++----- hledger/Hledger/Cli/CompoundBalanceCommand.hs | 4 +- hledger/test/balance/commodity-column.test | 46 +++++++++++++++++++ 4 files changed, 74 insertions(+), 14 deletions(-) diff --git a/hledger-lib/Hledger/Reports/ReportOptions.hs b/hledger-lib/Hledger/Reports/ReportOptions.hs index a001f9a9b..c92fbde36 100644 --- a/hledger-lib/Hledger/Reports/ReportOptions.hs +++ b/hledger-lib/Hledger/Reports/ReportOptions.hs @@ -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. diff --git a/hledger/Hledger/Cli/Commands/Balance.hs b/hledger/Hledger/Cli/Commands/Balance.hs index f563361f7..abcd2b797 100644 --- a/hledger/Hledger/Cli/Commands/Balance.hs +++ b/hledger/Hledger/Cli/Commands/Balance.hs @@ -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 diff --git a/hledger/Hledger/Cli/CompoundBalanceCommand.hs b/hledger/Hledger/Cli/CompoundBalanceCommand.hs index e11c0f460..796b0a1f9 100644 --- a/hledger/Hledger/Cli/CompoundBalanceCommand.hs +++ b/hledger/Hledger/Cli/CompoundBalanceCommand.hs @@ -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; }" diff --git a/hledger/test/balance/commodity-column.test b/hledger/test/balance/commodity-column.test index 2c1ac491e..5ad47096c 100644 --- a/hledger/test/balance/commodity-column.test +++ b/hledger/test/balance/commodity-column.test @@ -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