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 9, 2024
1 parent 8ac51eb commit a2799a0
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 49 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
128 changes: 95 additions & 33 deletions hledger/Hledger/Cli/Commands/Balance.hs
Original file line number Diff line number Diff line change
Expand Up @@ -583,15 +583,8 @@ renderComponent topaligned oneline opts (acctname, dep, total) (FormatField ljus
}


headerCell :: Text -> Ods.Cell Ods.NumLines Text
headerCell text =
let deflt = Ods.defaultCell text
in
deflt {
Ods.cellStyle = Ods.Head,
Ods.cellBorder =
(Ods.cellBorder deflt) {Ods.borderBottom = Ods.DoubleLine}
}
headerCell :: (Ods.Lines borders) => Text -> Ods.Cell borders Text
headerCell text = (Ods.defaultCell text) {Ods.cellStyle = Ods.Head}

registerQueryUrl :: [Text] -> Text
registerQueryUrl query =
Expand Down Expand Up @@ -633,13 +626,30 @@ replaceDate :: Text -> [Text] -> [Text]
replaceDate prd query = "date:"<>prd : removeDates query

headerDateSpanCell ::
Maybe Text -> [Text] -> DateSpan -> Ods.Cell Ods.NumLines Text
Maybe Text -> [Text] -> DateSpan -> Ods.Cell () Text
headerDateSpanCell base query spn =
let prd = showDateSpan spn in
(headerCell prd) {
Ods.cellAnchor = composeAnchor base $ replaceDate prd query
}

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

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

groupHeaderCells ::
(Ods.Lines border, Monoid text) =>
[a] -> Ods.Cell border text -> [Ods.Cell border text]
groupHeaderCells subCells cell =
zipWith const
(cell{Ods.cellSpan = Ods.SpanHorizontal $ length subCells}
: repeat (Ods.emptyCell {Ods.cellSpan = Ods.Covered}))
subCells

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

Expand Down Expand Up @@ -697,10 +707,13 @@ balanceReportAsSpreadsheet opts (items, total) =
where
cell = Ods.defaultCell
headers =
map headerCell $
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 @@ -712,6 +725,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 Down Expand Up @@ -749,6 +771,15 @@ cellsFromMixedAmount bopts (cls, mixedAmt) =
})
(showMixedAmountLinesPartsB bopts mixedAmt)

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

amountType :: AmountFormat -> Amount -> Ods.Type
amountType bopts amt =
Ods.TypeAmount $
Expand All @@ -771,30 +802,43 @@ multiBalanceReportAsCsv opts@ReportOpts{..} report = maybeTranspose allRows
case layout_ of
LayoutTidy -> rows -- tidy csv should not include totals or averages
_ -> rows ++ totals
rows = header:body
rows = header++body
(header, body, totals) =
multiBalanceReportAsSpreadsheetParts machineFmt opts report
multiBalanceReportAsSpreadsheetParts machineFmt opts
(allCommoditiesFromPeriodicReport $ prRows report) report
maybeTranspose = if transpose_ then transpose else id

-- | 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 (groupHeaderCells allCommodities) dateHeaders,
headers]
_ -> [headers]
headers =
addHeaderBorders $
hCell "account" "account" :
case layout_ of
LayoutTidy ->
map headerCell
["period", "start_date", "end_date", "commodity", "value"]
LayoutBareWide -> dateHeaders >> map headerCell allCommodities
LayoutBare -> headerCell "commodity" : dateHeaders
_ -> dateHeaders
dateHeaders =
Expand All @@ -815,7 +859,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


-- | Render a multi-column balance report as HTML.
Expand All @@ -833,10 +877,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 @@ -908,19 +954,24 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, row_total_, b
where
totalscolumn = row_total_ && balanceaccum_ `notElem` [Cumulative, Historical]
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" | totalscolumn]
++ ["Average" | average_]
allCommodities = allCommoditiesFromPeriodicReport items
fullRowAsTexts row =
let rs = multiBalanceRowAsText opts row
let rs = multiBalanceRowAsText opts allCommodities row
in (replicate (length rs) (renderacct row), rs)
(accts, rows) = unzip $ fmap fullRowAsTexts items
renderacct row =
T.replicate ((prrDepth row - 1) * 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 @@ -929,12 +980,17 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, row_total_, b
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 ReportOpts{..} colspans
multiBalanceRowAsCellBuilders bopts ReportOpts{..} colspans allCommodities
rc renderDateSpanCell (PeriodicReportRow _acct as rowtot rowavg) =
case layout_ of
LayoutWide width -> [fmap (cellFromMixedAmount bopts{displayMaxWidth=width}) clsamts]
Expand All @@ -945,6 +1001,8 @@ multiBalanceRowAsCellBuilders bopts 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 @@ -983,16 +1041,20 @@ multiBalanceRowAsCellBuilders bopts ReportOpts{..} colspans
m [] = [n]


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 Expand Up @@ -1254,7 +1316,7 @@ budgetReportAsSpreadsheet
= (if transpose_ then Ods.transpose else id) $

-- heading row
(map headerCell $
(addHeaderBorders $ map headerCell $
"Account" :
["Commodity" | layout_ == LayoutBare ]
++ concatMap (\spn -> [showDateSpan spn, "budget"]) colspans
Expand Down
Loading

0 comments on commit a2799a0

Please sign in to comment.