Skip to content

Commit 9d1ba5c

Browse files
thielemasimonmichael
authored andcommitted
cli: CompoundBalanceCommand.compoundBalanceReportAsSpreadsheet: common function for CSV, HTML, FODS export
1 parent 71a7879 commit 9d1ba5c

File tree

3 files changed

+78
-72
lines changed

3 files changed

+78
-72
lines changed

hledger-lib/Hledger/Write/Spreadsheet.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Hledger.Write.Spreadsheet (
1818
emptyCell,
1919
transposeCell,
2020
transpose,
21+
horizontalSpan,
2122
addRowSpanHeader,
2223
rawTableContent,
2324
) where
@@ -171,6 +172,15 @@ transpose :: [[Cell border text]] -> [[Cell border text]]
171172
transpose = List.transpose . map (map transposeCell)
172173

173174

175+
horizontalSpan ::
176+
(Lines border, Monoid text) =>
177+
[a] -> Cell border text -> [Cell border text]
178+
horizontalSpan subCells cell =
179+
zipWith const
180+
(cell{cellSpan = SpanHorizontal $ length subCells}
181+
: repeat (emptyCell {cellSpan = Covered}))
182+
subCells
183+
174184
addRowSpanHeader ::
175185
Cell border text ->
176186
[[Cell border text]] -> [[Cell border text]]

hledger/Hledger/Cli/Commands/Balance.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ module Hledger.Cli.Commands.Balance (
266266
,multiBalanceHasTotalsColumn
267267
,addTotalBorders
268268
,simpleDateSpanCell
269+
,nbsp
269270
,RowClass(..)
270271
-- ** Tests
271272
,tests_Balance

hledger/Hledger/Cli/CompoundBalanceCommand.hs

Lines changed: 67 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,24 @@ module Hledger.Cli.CompoundBalanceCommand (
1414
,compoundBalanceCommand
1515
) where
1616

17+
import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
18+
import Data.List.NonEmpty (NonEmpty((:|)))
19+
import Data.Bifunctor (second)
20+
import qualified Data.Map as Map
1721
import qualified Data.List as List
18-
import Data.Maybe (fromMaybe, mapMaybe)
22+
import qualified Data.List.NonEmpty as NonEmpty
1923
import qualified Data.Text as T
2024
import qualified Data.Text.Lazy as TL
2125
import qualified Data.Text.Lazy.Builder as TB
2226
import Data.Time.Calendar (Day, addDays)
2327
import System.Console.CmdArgs.Explicit as C (Mode, flagNone, flagReq)
28+
import qualified System.IO as IO
29+
import Hledger.Write.Ods (printFods)
2430
import Hledger.Write.Csv (CSV, printCSV, printTSV)
25-
import Hledger.Write.Html.Attribute (stylesheet, tableStyle, alignleft, alignright)
26-
import qualified Hledger.Write.Html.Lucid as Html
31+
import Hledger.Write.Html.Lucid (printHtml)
32+
import Hledger.Write.Html.Attribute (stylesheet, tableStyle, alignleft)
2733
import qualified Hledger.Write.Spreadsheet as Spr
2834
import Lucid as L hiding (value_)
29-
import Safe (tailDef)
3035
import Text.Tabular.AsciiWide as Tabular hiding (render)
3136

3237
import Hledger
@@ -197,6 +202,10 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportspec_=r
197202
"csv" -> printCSV . compoundBalanceReportAsCsv ropts'
198203
"tsv" -> printTSV . compoundBalanceReportAsCsv ropts'
199204
"html" -> L.renderText . compoundBalanceReportAsHtml ropts'
205+
"fods" -> printFods IO.localeEncoding .
206+
fmap (second NonEmpty.toList) . uncurry Map.singleton .
207+
compoundBalanceReportAsSpreadsheet
208+
oneLineNoCostFmt "Account" (Just "") ropts'
200209
"json" -> toJsonText
201210
x -> error' $ unsupportedOutputFormatError x
202211

@@ -302,99 +311,85 @@ compoundBalanceReportAsText ropts (CompoundPeriodicReport title _colspans subrep
302311
-- subreport title row, and an overall title row, one headings row, and an
303312
-- optional overall totals row is added.
304313
compoundBalanceReportAsCsv :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
305-
compoundBalanceReportAsCsv ropts (CompoundPeriodicReport title colspans subreports totalrow) =
306-
addtotals $
307-
padRow title
308-
: ( "Account"
309-
: ["Commodity" | layout_ ropts == LayoutBare]
310-
++ map (reportPeriodName (balanceaccum_ ropts) colspans) colspans
311-
++ (if multiBalanceHasTotalsColumn ropts then ["Total"] else [])
312-
++ (if average_ ropts then ["Average"] else [])
313-
)
314-
: concatMap (subreportAsCsv ropts) subreports
315-
where
316-
-- | Add a subreport title row and drop the heading row.
317-
subreportAsCsv ropts1 (subreporttitle, multibalreport, _) =
318-
padRow subreporttitle :
319-
tailDef [] (multiBalanceReportAsCsv ropts1 multibalreport)
320-
padRow s = take numcols $ s : repeat ""
321-
where
322-
numcols
323-
| null subreports = 1
324-
| otherwise =
325-
(1 +) $ -- account name column
326-
(if layout_ ropts == LayoutBare then (1+) else id) $
327-
(if multiBalanceHasTotalsColumn ropts then (1+) else id) $
328-
(if average_ ropts then (1+) else id) $
329-
maximum $ -- depends on non-null subreports
330-
map (length . prDates . second3) subreports
331-
addtotals
332-
| no_total_ ropts || length subreports == 1 = id
333-
| otherwise = (++ map ("Net:" : ) (multiBalanceRowAsCsvText ropts colspans totalrow))
314+
compoundBalanceReportAsCsv ropts cbr =
315+
let spreadsheet =
316+
snd $ snd $
317+
compoundBalanceReportAsSpreadsheet
318+
machineFmt "Account" Nothing ropts cbr
319+
in Spr.rawTableContent $
320+
Spr.horizontalSpan (NonEmpty.head spreadsheet)
321+
(Spr.headerCell (cbrTitle cbr)) :
322+
NonEmpty.toList spreadsheet
334323

335324
-- | Render a compound balance report as HTML.
336325
compoundBalanceReportAsHtml :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> Html ()
337326
compoundBalanceReportAsHtml ropts cbr =
327+
let (title, (_fixed, cells)) =
328+
compoundBalanceReportAsSpreadsheet
329+
oneLineNoCostFmt "" (Just nbsp) ropts cbr
330+
colspanattr = colspan_ $ T.pack $ show $ length $ NonEmpty.head cells
331+
in do
332+
link_ [rel_ "stylesheet", href_ "hledger.css"]
333+
style_ $ stylesheet $
334+
tableStyle ++ [
335+
("td:nth-child(1)", "white-space:nowrap"),
336+
("tr:nth-child(odd) td", "background-color:#eee")
337+
]
338+
table_ $ do
339+
tr_ $ th_ [colspanattr, style_ alignleft] $ h2_ $ toHtml title
340+
printHtml $ NonEmpty.toList $ fmap (map (fmap L.toHtml)) cells
341+
342+
-- | Render a compound balance report as Spreadsheet.
343+
compoundBalanceReportAsSpreadsheet ::
344+
AmountFormat -> T.Text -> Maybe T.Text ->
345+
ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount ->
346+
(T.Text, ((Maybe Int, Maybe Int), NonEmpty [Spr.Cell Spr.NumLines T.Text]))
347+
compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
338348
let
339349
CompoundPeriodicReport title colspans subreports totalrow = cbr
340350
headerrow =
341-
th_ "" :
342-
(guard (layout_ ropts == LayoutBare) >> [th_ "Commodity"]) ++
343-
map (th_ [style_ alignright] . toHtml .
344-
reportPeriodName (balanceaccum_ ropts) colspans)
351+
Spr.headerCell accountLabel :
352+
(guard (layout_ ropts == LayoutBare) >> [Spr.headerCell "Commodity"]) ++
353+
map (Spr.headerCell . reportPeriodName (balanceaccum_ ropts) colspans)
345354
colspans ++
346-
(guard (multiBalanceHasTotalsColumn ropts) >> [th_ "Total"]) ++
347-
(guard (average_ ropts) >> [th_ "Average"])
355+
(guard (multiBalanceHasTotalsColumn ropts) >> [Spr.headerCell "Total"]) ++
356+
(guard (average_ ropts) >> [Spr.headerCell "Average"])
348357

349-
colspanattr = colspan_ $ T.pack $ show $ length headerrow
350-
leftattr = style_ alignleft
351-
blankrow = tr_ $ td_ [colspanattr] $ toHtmlRaw (" "::String)
352-
353-
titlerows =
354-
[tr_ $ th_ [colspanattr, leftattr] $ h2_ $ toHtml title
355-
,tr_ $ mconcat headerrow
356-
]
358+
blankrow =
359+
fmap (Spr.horizontalSpan headerrow . Spr.defaultCell) maybeBlank
357360

358361
-- Make rows for a subreport: its title row, not the headings row,
359362
-- the data rows, any totals row, and a blank row for whitespace.
360-
subreportrows :: (T.Text, MultiBalanceReport, Bool) -> [Html ()]
363+
subreportrows ::
364+
(T.Text, MultiBalanceReport, Bool) -> [[Spr.Cell Spr.NumLines T.Text]]
361365
subreportrows (subreporttitle, mbr, _increasestotal) =
362366
let
363-
-- TODO: should the commodity_column be displayed as a subaccount in this case as well?
364367
(_, bodyrows, mtotalsrows) =
365-
multiBalanceReportAsSpreadsheetParts oneLineNoCostFmt ropts mbr
366-
formatRow = Html.formatRow . map (fmap L.toHtml)
368+
multiBalanceReportAsSpreadsheetParts fmt ropts mbr
367369

368370
in
369-
[tr_ $ th_ [colspanattr, leftattr, class_ "account"] $ toHtml subreporttitle]
370-
++ map formatRow bodyrows
371-
++ map formatRow mtotalsrows
372-
++ [blankrow]
371+
Spr.horizontalSpan headerrow
372+
((Spr.defaultCell subreporttitle){
373+
Spr.cellStyle = Spr.Body Spr.Total,
374+
Spr.cellClass = Spr.Class "account"
375+
}) :
376+
bodyrows ++
377+
mtotalsrows ++
378+
maybeToList blankrow ++
379+
[]
373380

374381
totalrows =
375382
if no_total_ ropts || length subreports == 1 then []
376383
else
377-
multiBalanceRowAsCellBuilders oneLineNoCostFmt ropts colspans
384+
multiBalanceRowAsCellBuilders fmt ropts colspans
378385
Total simpleDateSpanCell totalrow
379386
-- make a table of rendered lines of the report totals row
380387
& map (map (fmap wbToText))
381388
& Spr.addRowSpanHeader
382389
((Spr.defaultCell "Net:") {Spr.cellClass = Spr.Class "account"})
383390
-- insert a headings column, with Net: on the first line only
384-
& addTotalBorders -- marking the first for special styling
385-
& map (Html.formatRow . map (fmap L.toHtml))
386-
-- convert to a list of HTML totals rows
387-
388-
in do
389-
link_ [rel_ "stylesheet", href_ "hledger.css"]
390-
style_ $ stylesheet $
391-
tableStyle ++ [
392-
("td:nth-child(1)", "white-space:nowrap"),
393-
("tr:nth-child(even) td", "background-color:#eee")
394-
]
395-
table_ $ mconcat $
396-
titlerows
397-
++ [blankrow]
398-
++ concatMap subreportrows subreports
399-
++ totalrows
391+
& addTotalBorders -- marking the first row for special styling
400392

393+
in (title,
394+
((Just 1, Just 1),
395+
headerrow :| concatMap subreportrows subreports ++ totalrows))

0 commit comments

Comments
 (0)