mirror of
https://github.com/simonmichael/hledger.git
synced 2025-01-01 06:41:55 +03:00
New command 'chart': generate balances pie chart
This commit is contained in:
parent
c8c62ef4c2
commit
e96350c3f1
@ -20,6 +20,9 @@ module Commands.All (
|
||||
#endif
|
||||
#ifdef WEB
|
||||
module Commands.Web,
|
||||
#endif
|
||||
#ifdef CHART
|
||||
module Commands.Chart
|
||||
#endif
|
||||
)
|
||||
where
|
||||
@ -36,3 +39,6 @@ import Commands.UI
|
||||
#ifdef WEB
|
||||
import Commands.Web
|
||||
#endif
|
||||
#ifdef CHART
|
||||
import Commands.Chart
|
||||
#endif
|
||||
|
77
Commands/Chart.hs
Normal file
77
Commands/Chart.hs
Normal file
@ -0,0 +1,77 @@
|
||||
{-|
|
||||
|
||||
Generate balances pie chart
|
||||
|
||||
-}
|
||||
|
||||
module Commands.Chart
|
||||
where
|
||||
import Ledger.Utils
|
||||
import Ledger.Types
|
||||
import Ledger.Amount
|
||||
import Ledger.AccountName
|
||||
import Ledger.Transaction
|
||||
import Ledger.Ledger
|
||||
import Ledger.Commodity
|
||||
import Options
|
||||
|
||||
import Graphics.Rendering.Chart
|
||||
import Data.Colour
|
||||
import Data.Colour.Names
|
||||
import Data.List
|
||||
|
||||
-- | Generate an image with the pie chart and write it to a file
|
||||
chart :: [Opt] -> [String] -> Ledger -> IO ()
|
||||
chart opts args l = renderableToPNGFile (toRenderable chart) w h filename
|
||||
where
|
||||
chart = genPie opts args l
|
||||
filename = getOption opts ChartOutput "hledger.png"
|
||||
(w,h) = parseSize $ getOption opts ChartSize "1024x1024"
|
||||
|
||||
-- | Extract string option value from a list of options or use the default
|
||||
getOption :: [Opt] -> (String->Opt) -> String -> String
|
||||
getOption opts opt def =
|
||||
case reverse $ optValuesForConstructor opt opts of
|
||||
[] -> def
|
||||
x:_ -> x
|
||||
|
||||
-- | Parse image size from a command-line option
|
||||
parseSize :: String -> (Int,Int)
|
||||
parseSize str = (read w, read h)
|
||||
where
|
||||
x = fromMaybe (error "Size should be in WIDTHxHEIGHT format") $ findIndex (=='x') str
|
||||
(w,_:h) = splitAt x str
|
||||
|
||||
-- | Generate pie chart
|
||||
genPie :: [Opt] -> [String] -> Ledger -> PieLayout
|
||||
genPie opts _ l = defaultPieLayout
|
||||
{ pie_background_ = solidFillStyle $ opaque $ white
|
||||
, pie_plot_ = pie_chart }
|
||||
where
|
||||
pie_chart = defaultPieChart { pie_data_ = items }
|
||||
items = mapMaybe (uncurry accountPieItem) $
|
||||
flatten $
|
||||
balances $
|
||||
ledgerAccountTree (depthFromOpts opts) l
|
||||
|
||||
-- | Convert all quantities of MixedAccount to a single commodity
|
||||
amountValue :: MixedAmount -> Double
|
||||
amountValue = quantity . convertMixedAmountTo unknown
|
||||
|
||||
-- | Generate a tree of account names together with their balances.
|
||||
-- The balance of account is decremented by the balance of its subaccounts
|
||||
-- which are drawn on the chart.
|
||||
balances :: Tree Account -> Tree (AccountName, Double)
|
||||
balances (Node rootAcc subAccs) = Node newroot newsubs
|
||||
where
|
||||
newroot = (aname rootAcc,
|
||||
amountValue $
|
||||
abalance rootAcc - (sum . map (abalance . root)) subAccs)
|
||||
newsubs = map balances subAccs
|
||||
|
||||
-- | Build a single pie chart item
|
||||
accountPieItem :: AccountName -> Double -> Maybe PieItem
|
||||
accountPieItem accname balance =
|
||||
if balance == 0
|
||||
then Nothing
|
||||
else Just $ PieItem accname 0 balance
|
11
Options.hs
11
Options.hs
@ -38,6 +38,9 @@ usagehdr =
|
||||
#endif
|
||||
#ifdef WEB
|
||||
" web - run a simple web-based UI\n" ++
|
||||
#endif
|
||||
#ifdef CHART
|
||||
" chart - generate balances pie chart\n" ++
|
||||
#endif
|
||||
" test - run self-tests\n" ++
|
||||
"\n" ++
|
||||
@ -81,6 +84,10 @@ options = [
|
||||
,Option "" ["binary-filename"] (NoArg BinaryFilename) "show the download filename for this hledger build"
|
||||
,Option "" ["debug"] (NoArg Debug) "show extra debug output; implies verbose"
|
||||
,Option "" ["debug-no-ui"] (NoArg DebugNoUI) "run ui commands with no output"
|
||||
#ifdef CHART
|
||||
,Option "o" ["output"] (ReqArg ChartOutput "FILE") "chart: output filename (default: hledger.png)"
|
||||
,Option "" ["size"] (ReqArg ChartSize "WIDTHxHEIGHT") "chart: image size (default: 1024x1024)"
|
||||
#endif
|
||||
]
|
||||
|
||||
-- | An option value from a command-line flag.
|
||||
@ -109,6 +116,10 @@ data Opt =
|
||||
| BinaryFilename
|
||||
| Debug
|
||||
| DebugNoUI
|
||||
#ifdef CHART
|
||||
| ChartOutput {value::String}
|
||||
| ChartSize {value::String}
|
||||
#endif
|
||||
deriving (Show,Eq)
|
||||
|
||||
-- these make me nervous
|
||||
|
@ -66,5 +66,8 @@ configflags = tail [""
|
||||
#endif
|
||||
#ifdef WEB
|
||||
,"web"
|
||||
#endif
|
||||
#ifdef CHART
|
||||
,"chart"
|
||||
#endif
|
||||
]
|
||||
|
@ -37,6 +37,10 @@ flag web
|
||||
description: enable the web ui
|
||||
default: False
|
||||
|
||||
flag chart
|
||||
description: enable the pie chart generation
|
||||
default: False
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
Ledger
|
||||
@ -146,6 +150,12 @@ executable hledger
|
||||
,HTTP >= 4000.0 && < 4000.1
|
||||
,applicative-extras
|
||||
|
||||
if flag(chart)
|
||||
cpp-options: -DCHART
|
||||
other-modules:Commands.Chart
|
||||
build-depends:
|
||||
Chart >= 0.11 && < 0.12
|
||||
,colour
|
||||
|
||||
-- source-repository head
|
||||
-- type: darcs
|
||||
|
@ -67,6 +67,9 @@ main = do
|
||||
#endif
|
||||
#ifdef WEB
|
||||
| cmd `isPrefixOf` "web" = withLedgerDo opts args cmd web
|
||||
#endif
|
||||
#ifdef CHART
|
||||
| cmd `isPrefixOf` "chart" = withLedgerDo opts args cmd chart
|
||||
#endif
|
||||
| cmd `isPrefixOf` "test" = runtests opts args >> return ()
|
||||
| otherwise = putStr usage
|
||||
|
Loading…
Reference in New Issue
Block a user