Skip to content

Commit

Permalink
cli: support for new layout bare-wide
Browse files Browse the repository at this point in the history
  • Loading branch information
thielema committed Oct 18, 2024
1 parent a758eb0 commit 5d02eaf
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 34 deletions.
4 changes: 3 additions & 1 deletion hledger-lib/Hledger/Reports/ReportOptions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ instance Default AccountListMode where def = ALFlat
data Layout = LayoutWide (Maybe Int)
| LayoutTall
| LayoutBare
| LayoutBareWide
| LayoutTidy
deriving (Eq, Show)

Expand Down Expand Up @@ -373,6 +374,7 @@ layoutopt rawopts = fromMaybe (LayoutWide Nothing) $ layout <|> column
, ("tall", LayoutTall)
, ("bare", LayoutBare)
, ("tidy", LayoutTidy)
, ("bare-wide", LayoutBareWide)
]
-- For `--layout=elided,n`, elide to the given width
(s,n) = break (==',') $ map toLower opt
Expand All @@ -381,7 +383,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\", \"bare\", or \"tidy\""
err = usageError "--layout's argument should be \"wide[,WIDTH]\", \"tall\", \"bare\", \"bare-wide\", or \"tidy\""

-- Get the period specified by any -b/--begin, -e/--end and/or -p/--period
-- options appearing in the command line.
Expand Down
10 changes: 10 additions & 0 deletions hledger-lib/Hledger/Write/Spreadsheet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module Hledger.Write.Spreadsheet (
rawTableContent,
cellFromMixedAmount,
cellsFromMixedAmount,
cellFromAmount,
) where

import qualified Hledger.Data.Amount as Amt
Expand Down Expand Up @@ -235,6 +236,15 @@ cellsFromMixedAmount bopts (cls, mixedAmt) =
})
(Amt.showMixedAmountLinesPartsB bopts mixedAmt)

cellFromAmount ::
(Lines border) =>
AmountFormat -> (Class, (wb, Amount)) -> Cell border wb
cellFromAmount bopts (cls, (str,amt)) =
(defaultCell str) {
cellClass = cls,
cellType = amountType bopts amt
}

amountType :: AmountFormat -> Amount -> Type
amountType bopts amt =
TypeAmount $
Expand Down
89 changes: 67 additions & 22 deletions hledger/Hledger/Cli/Commands/Balance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ import Hledger.Write.Ods (printFods)
import Hledger.Write.Html.Lucid (printHtml)
import Hledger.Write.Spreadsheet (rawTableContent, headerCell,
addHeaderBorders, addRowSpanHeader,
cellFromMixedAmount, cellsFromMixedAmount)
cellFromMixedAmount, cellsFromMixedAmount, cellFromAmount)
import qualified Hledger.Write.Spreadsheet as Ods


Expand Down Expand Up @@ -594,6 +594,9 @@ renderComponent topaligned oneline opts (acctname, dep, total) (FormatField ljus
}


headerWithoutBorders :: [Ods.Cell () text] -> [Ods.Cell Ods.NumLines text]
headerWithoutBorders = map (\c -> c {Ods.cellBorder = Ods.noBorder})

simpleDateSpanCell :: DateSpan -> Ods.Cell Ods.NumLines Text
simpleDateSpanCell = Ods.defaultCell . showDateSpan

Expand Down Expand Up @@ -622,8 +625,11 @@ balanceReportAsSpreadsheet opts (items, total) =
headers =
addHeaderBorders $ map headerCell $
"account" : case layout_ opts of
LayoutBareWide -> allCommodities
LayoutBare -> ["commodity", "balance"]
_ -> ["balance"]
allCommodities =
S.toAscList $ foldMap (\(_,_,_,ma) -> maCommodities ma) items
rows ::
RowClass -> BalanceReportItem ->
[[Ods.Cell Ods.NumLines Text]]
Expand All @@ -635,6 +641,15 @@ balanceReportAsSpreadsheet opts (items, total) =
cell $ renderBalanceAcct opts nbsp (name, dispName, dep) in
addRowSpanHeader accountCell $
case layout_ opts of
LayoutBareWide ->
let bopts =
machineFmt {
displayCommodity = False,
displayCommodityOrder = Just allCommodities
} in
[map (\bldAmt ->
fmap wbToText $ cellFromAmount bopts (amountClass rc, bldAmt)) $
showMixedAmountLinesPartsB bopts ma]
LayoutBare ->
map (\a -> [cell $ acommodity a, renderAmount rc $ mixedAmount a])
. amounts $ mixedAmountStripCosts ma
Expand All @@ -658,29 +673,41 @@ balanceReportAsSpreadsheet opts (items, total) =
multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> CSV
multiBalanceReportAsCsv opts@ReportOpts{..} report =
(if transpose_ then transpose else id) $
rawTableContent $ header : body ++ totals
rawTableContent $ header ++ body ++ totals
where
(header, body, totals) =
multiBalanceReportAsSpreadsheetParts machineFmt opts report
multiBalanceReportAsSpreadsheetParts machineFmt opts
(allCommoditiesFromPeriodicReport $ prRows report) report

-- | Render the Spreadsheet table rows (CSV, ODS, HTML) for a MultiBalanceReport.
-- Returns the heading row, 0 or more body rows, and the totals row if enabled.
multiBalanceReportAsSpreadsheetParts ::
AmountFormat -> ReportOpts -> MultiBalanceReport ->
([Ods.Cell Ods.NumLines Text],
AmountFormat -> ReportOpts ->
[CommoditySymbol] -> MultiBalanceReport ->
([[Ods.Cell Ods.NumLines Text]],
[[Ods.Cell Ods.NumLines Text]],
[[Ods.Cell Ods.NumLines Text]])
multiBalanceReportAsSpreadsheetParts fmt opts@ReportOpts{..} (PeriodicReport colspans items tr) =
(headers, concatMap fullRowAsTexts items, addTotalBorders totalrows)
multiBalanceReportAsSpreadsheetParts fmt opts@ReportOpts{..}
allCommodities (PeriodicReport colspans items tr) =
(allHeaders, concatMap fullRowAsTexts items, addTotalBorders totalrows)
where
accountCell label =
(Ods.defaultCell label) {Ods.cellClass = Ods.Class "account"}
hCell cls label = (headerCell label) {Ods.cellClass = Ods.Class cls}
allHeaders =
case layout_ of
LayoutBareWide ->
[headerWithoutBorders $
Ods.emptyCell :
concatMap (Ods.horizontalSpan allCommodities) dateHeaders,
headers]
_ -> [headers]
headers =
addHeaderBorders $
hCell "account" "account" :
case layout_ of
LayoutTidy -> map headerCell tidyColumnLabels
LayoutBareWide -> dateHeaders >> map headerCell allCommodities
LayoutBare -> headerCell "commodity" : dateHeaders
_ -> dateHeaders
dateHeaders =
Expand All @@ -701,7 +728,7 @@ multiBalanceReportAsSpreadsheetParts fmt opts@ReportOpts{..} (PeriodicReport col
rowAsText Total simpleDateSpanCell tr
rowAsText rc dsCell =
map (map (fmap wbToText)) .
multiBalanceRowAsCellBuilders fmt opts colspans rc dsCell
multiBalanceRowAsCellBuilders fmt opts colspans allCommodities rc dsCell

tidyColumnLabels :: [Text]
tidyColumnLabels =
Expand All @@ -721,10 +748,12 @@ multiBalanceReportAsSpreadsheet ::
((Maybe Int, Maybe Int), [[Ods.Cell Ods.NumLines Text]])
multiBalanceReportAsSpreadsheet ropts mbr =
let (header,body,total) =
multiBalanceReportAsSpreadsheetParts oneLineNoCostFmt ropts mbr
multiBalanceReportAsSpreadsheetParts oneLineNoCostFmt ropts
(allCommoditiesFromPeriodicReport $ prRows mbr) mbr
in (if transpose_ ropts then swap *** Ods.transpose else id) $
((Just 1, case layout_ ropts of LayoutWide _ -> Just 1; _ -> Nothing),
header : body ++ total)
((Just $ case layout_ ropts of LayoutBareWide -> 2; _ -> 1,
case layout_ ropts of LayoutWide _ -> Just 1; _ -> Nothing),
header ++ body ++ total)


-- | Render a multi-column balance report as plain text suitable for console output.
Expand Down Expand Up @@ -795,19 +824,24 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, balanceaccum_
(concat rows)
where
colheadings = ["Commodity" | layout_ opts == LayoutBare]
++ (if not summary_only_ then map (reportPeriodName balanceaccum_ spans) spans else [])
++ (if not summary_only_
then case layout_ opts of
LayoutBareWide -> spans >> allCommodities
_ -> map (reportPeriodName balanceaccum_ spans) spans
else [])
++ [" Total" | multiBalanceHasTotalsColumn opts]
++ ["Average" | average_]
allCommodities = allCommoditiesFromPeriodicReport items
(accts, rows) = unzip $ fmap fullRowAsTexts items
where
fullRowAsTexts row = (replicate (length rs) (renderacct row), rs)
where
rs = multiBalanceRowAsText opts row
rs = multiBalanceRowAsText opts allCommodities row
renderacct row' = T.replicate (prrIndent row' * 2) " " <> prrDisplayName row'
addtotalrow
| no_total_ opts = id
| otherwise =
let totalrows = multiBalanceRowAsText opts tr
let totalrows = multiBalanceRowAsText opts allCommodities tr
rowhdrs = Group NoLine $ map Header $ totalRowHeadingText : replicate (length totalrows - 1) ""
colhdrs = Header [] -- unused, concatTables will discard
in (flip (concatTables SingleLine) $ Table rowhdrs colhdrs totalrows)
Expand All @@ -816,12 +850,17 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, balanceaccum_
multiColumnTableInterRowBorder = NoLine
multiColumnTableInterColumnBorder = if pretty_ opts then SingleLine else NoLine

allCommoditiesFromPeriodicReport ::
[PeriodicReportRow a MixedAmount] -> [CommoditySymbol]
allCommoditiesFromPeriodicReport =
S.toAscList . foldMap (foldMap maCommodities . prrAmounts)

multiBalanceRowAsCellBuilders ::
AmountFormat -> ReportOpts -> [DateSpan] ->
AmountFormat -> ReportOpts -> [DateSpan] -> [CommoditySymbol] ->
RowClass -> (DateSpan -> Ods.Cell Ods.NumLines Text) ->
PeriodicReportRow a MixedAmount ->
[[Ods.Cell Ods.NumLines WideBuilder]]
multiBalanceRowAsCellBuilders bopts ropts@ReportOpts{..} colspans
multiBalanceRowAsCellBuilders bopts ropts@ReportOpts{..} colspans allCommodities
rc renderDateSpanCell (PeriodicReportRow _acct as rowtot rowavg) =
case layout_ of
LayoutWide width -> [fmap (cellFromMixedAmount bopts{displayMaxWidth=width}) clsamts]
Expand All @@ -832,6 +871,8 @@ multiBalanceRowAsCellBuilders bopts ropts@ReportOpts{..} colspans
. transpose -- each row becomes a list of Text quantities
. map (cellsFromMixedAmount bopts{displayCommodity=False, displayCommodityOrder=Just cs, displayMinWidth=Nothing})
$ clsamts
LayoutBareWide -> [concatMap (cellsFromMixedAmount bopts{displayCommodity=False, displayCommodityOrder=Just allCommodities, displayMinWidth=Nothing})
$ clsamts]
LayoutTidy -> concat
. zipWith (map . addDateColumns) colspans
. map ( zipWith (\c a -> [wbCell c, a]) cs
Expand Down Expand Up @@ -874,16 +915,20 @@ multiBalanceHasTotalsColumn :: ReportOpts -> Bool
multiBalanceHasTotalsColumn ropts =
row_total_ ropts && balanceaccum_ ropts `notElem` [Cumulative, Historical]

multiBalanceRowAsText :: ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
multiBalanceRowAsText opts =
multiBalanceRowAsText ::
ReportOpts -> [CommoditySymbol] -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
multiBalanceRowAsText opts allCommodities =
rawTableContent .
multiBalanceRowAsCellBuilders oneLineNoCostFmt{displayColour=color_ opts} opts []
multiBalanceRowAsCellBuilders oneLineNoCostFmt{displayColour=color_ opts}
opts [] allCommodities
Value simpleDateSpanCell

multiBalanceRowAsCsvText :: ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[T.Text]]
multiBalanceRowAsCsvText opts colspans =
multiBalanceRowAsCsvText ::
ReportOpts -> [DateSpan] -> [CommoditySymbol] ->
PeriodicReportRow a MixedAmount -> [[T.Text]]
multiBalanceRowAsCsvText opts colspans allCommodities =
map (map (wbToText . Ods.cellContent)) .
multiBalanceRowAsCellBuilders machineFmt opts colspans
multiBalanceRowAsCellBuilders machineFmt opts colspans allCommodities
Value simpleDateSpanCell


Expand Down
43 changes: 32 additions & 11 deletions hledger/Hledger/Cli/CompoundBalanceCommand.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TB
import qualified Data.Set as Set
import Data.Time.Calendar (Day, addDays)
import System.Console.CmdArgs.Explicit as C (Mode, flagNone, flagReq)
import qualified System.IO as IO
Expand Down Expand Up @@ -285,12 +286,14 @@ compoundBalanceReportAsText ropts (CompoundPeriodicReport title _colspans subrep
-- [COL1LINE1, COL2LINE1]
-- [COL1LINE2, COL2LINE2]
-- ]
coltotalslines = multiBalanceRowAsText ropts totalsrow
coltotalslines = multiBalanceRowAsText ropts allCommodities totalsrow
totalstable = Table
(Group NoLine $ map Header $ "Net:" : replicate (length coltotalslines - 1) "") -- row headers
(Header []) -- column headers, concatTables will discard these
coltotalslines -- cell values

allCommodities = allCommoditiesFromSubreports subreports

-- | Convert a named multi balance report to a table suitable for
-- concatenating with others to make a compound balance report table.
subreportAsTable ropts1 (title1, r, _) = tablewithtitle
Expand Down Expand Up @@ -355,14 +358,21 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
_ -> []
dataHeaders =
(guard (layout_ ropts /= LayoutTidy) >>) $
map (Spr.headerCell . reportPeriodName (balanceaccum_ ropts) colspans)
map (dataHeaderCell . reportPeriodName (balanceaccum_ ropts) colspans)
colspans ++
(guard (multiBalanceHasTotalsColumn ropts) >> [Spr.headerCell "Total"]) ++
(guard (average_ ropts) >> [Spr.headerCell "Average"])
(guard (multiBalanceHasTotalsColumn ropts) >> [dataHeaderCell "Total"]) ++
(guard (average_ ropts) >> [dataHeaderCell "Average"])
dataHeaderCell label =
(Spr.headerCell label) {Spr.cellSpan = Spr.SpanHorizontal numSubColumns}
headerrow = leadingHeaders ++ dataHeaders

blankrow =
fmap (Spr.horizontalSpan headerrow . Spr.defaultCell) maybeBlank
numSubColumns =
case layout_ ropts of
LayoutBareWide -> length allCommodities
_ -> 1
allCommodities = allCommoditiesFromSubreports subreports

-- Make rows for a subreport: its title row, not the headings row,
-- the data rows, any totals row, and a blank row for whitespace.
Expand All @@ -371,14 +381,18 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
subreportrows (subreporttitle, mbr, _increasestotal) =
let
(_, bodyrows, mtotalsrows) =
multiBalanceReportAsSpreadsheetParts fmt ropts mbr

in
Spr.horizontalSpan headerrow
((Spr.defaultCell subreporttitle){
multiBalanceReportAsSpreadsheetParts fmt ropts allCommodities mbr
accountCell =
(Spr.defaultCell subreporttitle) {
Spr.cellStyle = Spr.Body Spr.Total,
Spr.cellClass = Spr.Class "account"
}) :
}

in
(case layout_ ropts of
LayoutBareWide ->
accountCell : map Spr.headerCell (dataHeaders >> allCommodities)
_ -> Spr.horizontalSpan headerrow accountCell) :
bodyrows ++
mtotalsrows ++
maybeToList blankrow ++
Expand All @@ -387,7 +401,7 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
totalrows =
if no_total_ ropts || length subreports == 1 then []
else
multiBalanceRowAsCellBuilders fmt ropts colspans
multiBalanceRowAsCellBuilders fmt ropts colspans allCommodities
Total simpleDateSpanCell totalrow
-- make a table of rendered lines of the report totals row
& map (map (fmap wbToText))
Expand All @@ -399,3 +413,10 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
in (title,
((Just 1, Just 1),
headerrow :| concatMap subreportrows subreports ++ totalrows))

allCommoditiesFromSubreports ::
[(text, PeriodicReport a MixedAmount, bool)] -> [CommoditySymbol]
allCommoditiesFromSubreports =
Set.toAscList .
foldMap (\(_,mbr,_) ->
foldMap (foldMap maCommodities . prrAmounts) $ prRows mbr)

0 comments on commit 5d02eaf

Please sign in to comment.