New command 'chart': generate balances pie chart
This commit is contained in:
		
							parent
							
								
									c8c62ef4c2
								
							
						
					
					
						commit
						e96350c3f1
					
				| @ -20,6 +20,9 @@ module Commands.All ( | |||||||
| #endif | #endif | ||||||
| #ifdef WEB | #ifdef WEB | ||||||
|                      module Commands.Web, |                      module Commands.Web, | ||||||
|  | #endif | ||||||
|  | #ifdef CHART | ||||||
|  |                      module Commands.Chart | ||||||
| #endif | #endif | ||||||
|               ) |               ) | ||||||
| where | where | ||||||
| @ -36,3 +39,6 @@ import Commands.UI | |||||||
| #ifdef WEB | #ifdef WEB | ||||||
| import Commands.Web | import Commands.Web | ||||||
| #endif | #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 | #endif | ||||||
| #ifdef WEB | #ifdef WEB | ||||||
|   "  web       - run a simple web-based UI\n" ++ |   "  web       - run a simple web-based UI\n" ++ | ||||||
|  | #endif | ||||||
|  | #ifdef CHART | ||||||
|  |   "  chart     - generate balances pie chart\n" ++ | ||||||
| #endif | #endif | ||||||
|   "  test      - run self-tests\n" ++ |   "  test      - run self-tests\n" ++ | ||||||
|   "\n" ++ |   "\n" ++ | ||||||
| @ -81,6 +84,10 @@ options = [ | |||||||
|  ,Option ""    ["binary-filename"] (NoArg BinaryFilename) "show the download filename for this hledger build" |  ,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"]        (NoArg  Debug)         "show extra debug output; implies verbose" | ||||||
|  ,Option ""    ["debug-no-ui"]  (NoArg  DebugNoUI)     "run ui commands with no output" |  ,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. | -- | An option value from a command-line flag. | ||||||
| @ -109,6 +116,10 @@ data Opt = | |||||||
|     | BinaryFilename |     | BinaryFilename | ||||||
|     | Debug |     | Debug | ||||||
|     | DebugNoUI |     | DebugNoUI | ||||||
|  | #ifdef CHART | ||||||
|  |     | ChartOutput {value::String} | ||||||
|  |     | ChartSize   {value::String} | ||||||
|  | #endif | ||||||
|     deriving (Show,Eq) |     deriving (Show,Eq) | ||||||
| 
 | 
 | ||||||
| -- these make me nervous | -- these make me nervous | ||||||
|  | |||||||
| @ -66,5 +66,8 @@ configflags   = tail ["" | |||||||
| #endif | #endif | ||||||
| #ifdef WEB | #ifdef WEB | ||||||
|   ,"web" |   ,"web" | ||||||
|  | #endif | ||||||
|  | #ifdef CHART | ||||||
|  |   ,"chart" | ||||||
| #endif | #endif | ||||||
|  ] |  ] | ||||||
|  | |||||||
| @ -37,6 +37,10 @@ flag web | |||||||
|   description: enable the web ui |   description: enable the web ui | ||||||
|   default:     False |   default:     False | ||||||
| 
 | 
 | ||||||
|  | flag chart | ||||||
|  |   description: enable the pie chart generation | ||||||
|  |   default:     False | ||||||
|  | 
 | ||||||
| library | library | ||||||
|   exposed-modules: |   exposed-modules: | ||||||
|                   Ledger |                   Ledger | ||||||
| @ -146,6 +150,12 @@ executable hledger | |||||||
|                  ,HTTP >= 4000.0 && < 4000.1 |                  ,HTTP >= 4000.0 && < 4000.1 | ||||||
|                  ,applicative-extras |                  ,applicative-extras | ||||||
| 
 | 
 | ||||||
|  |   if flag(chart) | ||||||
|  |     cpp-options: -DCHART | ||||||
|  |     other-modules:Commands.Chart | ||||||
|  |     build-depends: | ||||||
|  |                   Chart >= 0.11 && < 0.12 | ||||||
|  |                  ,colour | ||||||
| 
 | 
 | ||||||
| -- source-repository head | -- source-repository head | ||||||
| --   type:     darcs | --   type:     darcs | ||||||
|  | |||||||
| @ -67,6 +67,9 @@ main = do | |||||||
| #endif | #endif | ||||||
| #ifdef WEB | #ifdef WEB | ||||||
|        | cmd `isPrefixOf` "web"       = withLedgerDo opts args cmd web |        | cmd `isPrefixOf` "web"       = withLedgerDo opts args cmd web | ||||||
|  | #endif | ||||||
|  | #ifdef CHART | ||||||
|  |        | cmd `isPrefixOf` "chart"       = withLedgerDo opts args cmd chart | ||||||
| #endif | #endif | ||||||
|        | cmd `isPrefixOf` "test"      = runtests opts args >> return () |        | cmd `isPrefixOf` "test"      = runtests opts args >> return () | ||||||
|        | otherwise                    = putStr usage |        | otherwise                    = putStr usage | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user