more tree support, properly filter balance report by (one) account regexp
This commit is contained in:
		
							parent
							
								
									453ca1206e
								
							
						
					
					
						commit
						1e1c819f4e
					
				
							
								
								
									
										40
									
								
								Account.hs
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								Account.hs
									
									
									
									
									
								
							| @ -49,8 +49,8 @@ aggregateTransactionsInAccountNamed l a = | |||||||
| addDataToAccountNameTree :: Ledger -> Tree AccountName -> Tree Account | addDataToAccountNameTree :: Ledger -> Tree AccountName -> Tree Account | ||||||
| addDataToAccountNameTree l ant =  | addDataToAccountNameTree l ant =  | ||||||
|     Node  |     Node  | ||||||
|     (mkAccount l $ rootLabel ant)  |     (mkAccount l $ root ant)  | ||||||
|     (map (addDataToAccountNameTree l) $ subForest ant) |     (map (addDataToAccountNameTree l) $ branches ant) | ||||||
| 
 | 
 | ||||||
| -- would be straightforward except we want to elide boring accounts when | -- would be straightforward except we want to elide boring accounts when | ||||||
| -- displaying account trees: | -- displaying account trees: | ||||||
| @ -65,7 +65,7 @@ showAccountTree :: Ledger -> Int -> Int -> Tree Account -> String | |||||||
| showAccountTree _ 0 _ _ = "" | showAccountTree _ 0 _ _ = "" | ||||||
| showAccountTree l maxdepth indentlevel t | showAccountTree l maxdepth indentlevel t | ||||||
|     -- if this acct is boring, don't show it (unless this is as deep as we're going) |     -- if this acct is boring, don't show it (unless this is as deep as we're going) | ||||||
|     | (boringacct && (maxdepth > 1)) = subacctsindented 0 | --     | (boringacct && (maxdepth > 1)) = subacctsindented 0 | ||||||
| 
 | 
 | ||||||
|     -- otherwise show normal indented account name with balance |     -- otherwise show normal indented account name with balance | ||||||
|     -- if this acct has one or more boring parents, prepend their names |     -- if this acct has one or more boring parents, prepend their names | ||||||
| @ -75,24 +75,24 @@ showAccountTree l maxdepth indentlevel t | |||||||
|     where |     where | ||||||
|       boringacct = isBoringAccount2 l name |       boringacct = isBoringAccount2 l name | ||||||
|       boringparents = takeWhile (isBoringAccount2 l) $ parentAccountNames name |       boringparents = takeWhile (isBoringAccount2 l) $ parentAccountNames name | ||||||
|       bal = printf "%20s" $ show $ abalance $ rootLabel t |       bal = printf "%20s" $ show $ abalance $ root t | ||||||
|       indent = replicate (indentlevel * 2) ' ' |       indent = replicate (indentlevel * 2) ' ' | ||||||
|       parentnames = concatMap (++ ":") $ map accountLeafName boringparents |       parentnames = concatMap (++ ":") $ map accountLeafName boringparents | ||||||
|       leafname = accountLeafName name |       leafname = accountLeafName name | ||||||
|       name = aname $ rootLabel t |       name = aname $ root t | ||||||
|       subacctsindented i =  |       subacctsindented i =  | ||||||
|           case maxdepth > 1 of |           case maxdepth > 1 of | ||||||
|             True -> concatMap (showAccountTree l (maxdepth-1) (indentlevel+i)) $ subForest t |             True -> concatMap (showAccountTree l (maxdepth-1) (indentlevel+i)) $ branches t | ||||||
|             False -> "" |             False -> "" | ||||||
| 
 | 
 | ||||||
| isBoringAccount :: Tree Account -> Bool | isBoringAccount :: Tree Account -> Bool | ||||||
| isBoringAccount at =  | isBoringAccount at =  | ||||||
|     (length txns == 0) && ((length subaccts) == 1) && (not $ name == "top") |     (length txns == 0) && ((length subaccts) == 1) && (not $ name == "top") | ||||||
|         where |         where | ||||||
|           a = rootLabel at |           a = root at | ||||||
|           name = aname a |           name = aname a | ||||||
|           txns = atransactions a |           txns = atransactions a | ||||||
|           subaccts = subForest at |           subaccts = branches at | ||||||
| 
 | 
 | ||||||
| isBoringAccount2 :: Ledger -> AccountName -> Bool | isBoringAccount2 :: Ledger -> AccountName -> Bool | ||||||
| isBoringAccount2 l a | isBoringAccount2 l a | ||||||
| @ -103,15 +103,17 @@ isBoringAccount2 l a | |||||||
|       txns = transactionsInAccountNamed l a |       txns = transactionsInAccountNamed l a | ||||||
|       subs = subAccountNamesFrom (ledgerAccountNames l) a |       subs = subAccountNamesFrom (ledgerAccountNames l) a | ||||||
| 
 | 
 | ||||||
| ledgerAccountTree :: Ledger -> Tree Account | ledgerAccountTreeMatching :: Ledger -> Bool -> [String] -> Tree Account | ||||||
| ledgerAccountTree l = addDataToAccountNameTree l (ledgerAccountNameTree l) | ledgerAccountTreeMatching l showsubs [] =  | ||||||
|  |     ledgerAccountTreeMatching l showsubs [".*"] | ||||||
|  | ledgerAccountTreeMatching l showsubs acctpats =  | ||||||
|  |     addDataToAccountNameTree l $  | ||||||
|  |     filterAccountNameTree acctpat $  | ||||||
|  |     ledgerAccountNameTree l | ||||||
|  |         where acctpat = head acctpats | ||||||
| 
 | 
 | ||||||
| -- ledgerAccountTreeForAccount :: Ledger -> AccountName -> Tree Account | showLedgerAccounts :: Ledger -> Bool -> [String] -> String | ||||||
| -- ledgerAccountTreeForAccount l a = addDataToAccountNameTree l (ledgerAccountNameTree l) | showLedgerAccounts l showsubs acctpats =  | ||||||
| 
 |     concatMap  | ||||||
| ledgerAccountsMatching :: Ledger -> [String] -> [Account] |     (showAccountTree l 999 0)  | ||||||
| ledgerAccountsMatching l acctpats = undefined |     (branches (ledgerAccountTreeMatching l showsubs acctpats)) | ||||||
| 
 |  | ||||||
| showLedgerAccounts :: Ledger -> Int -> String |  | ||||||
| showLedgerAccounts l maxdepth =  |  | ||||||
|     concatMap (showAccountTree l maxdepth 0) (subForest (ledgerAccountTree l)) |  | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ accountNameFromComponents :: [String] -> AccountName | |||||||
| accountNameFromComponents = concat . intersperse ":" | accountNameFromComponents = concat . intersperse ":" | ||||||
| 
 | 
 | ||||||
| accountLeafName :: AccountName -> String | accountLeafName :: AccountName -> String | ||||||
| accountLeafName = rhead . accountNameComponents | accountLeafName = last . accountNameComponents | ||||||
| 
 | 
 | ||||||
| accountNameLevel :: AccountName -> Int | accountNameLevel :: AccountName -> Int | ||||||
| accountNameLevel = length . accountNameComponents | accountNameLevel = length . accountNameComponents | ||||||
| @ -29,8 +29,7 @@ topAccountNames :: [AccountName] -> [AccountName] | |||||||
| topAccountNames as = [a | a <- expandAccountNames as, accountNameLevel a == 1] | topAccountNames as = [a | a <- expandAccountNames as, accountNameLevel a == 1] | ||||||
| 
 | 
 | ||||||
| parentAccountName :: AccountName -> AccountName | parentAccountName :: AccountName -> AccountName | ||||||
| parentAccountName a =  | parentAccountName a = accountNameFromComponents $ init $ accountNameComponents a | ||||||
|     accountNameFromComponents $ rtail $ accountNameComponents a |  | ||||||
| 
 | 
 | ||||||
| parentAccountNames :: AccountName -> [AccountName] | parentAccountNames :: AccountName -> [AccountName] | ||||||
| parentAccountNames a = parentAccountNames' $ parentAccountName a | parentAccountNames a = parentAccountNames' $ parentAccountName a | ||||||
| @ -79,7 +78,11 @@ accountNameTreeFrom accts = | |||||||
| 
 | 
 | ||||||
| showAccountNameTree :: Tree AccountName -> String | showAccountNameTree :: Tree AccountName -> String | ||||||
| showAccountNameTree t = | showAccountNameTree t = | ||||||
|     topacct  ++ "\n" ++ concatMap showAccountNameTree (subForest t) |     topacct  ++ "\n" ++ concatMap showAccountNameTree (branches t) | ||||||
|         where |         where | ||||||
|           topacct = indentAccountName 0 $ rootLabel t |           topacct = indentAccountName 0 $ root t | ||||||
|  | 
 | ||||||
|  | filterAccountNameTree :: String -> Tree AccountName -> Tree AccountName | ||||||
|  | filterAccountNameTree s =  treefilter ((matchAccountName s) . accountLeafName) | ||||||
|  | --any (flip matchAccountName . accountLeafName) acctpats | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										111
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								TODO
									
									
									
									
									
								
							| @ -1,17 +1,111 @@ | |||||||
| make it fast | * feature: balance report account matching | ||||||
|  profile, refactor | 
 | ||||||
|   * CookedLedger caching acct txns, boring status etc. | sample account tree: | ||||||
|    refactor apis | 
 | ||||||
|  | assets | ||||||
|  |  cash | ||||||
|  |  checking | ||||||
|  |  saving | ||||||
|  | equity | ||||||
|  | expenses | ||||||
|  |  food | ||||||
|  |  shelter | ||||||
|  | income | ||||||
|  |  salary | ||||||
|  | liabilities | ||||||
|  |  debts | ||||||
|  | 
 | ||||||
|  | standard balance command shows all top-level accounts: | ||||||
|  | 
 | ||||||
|  | > ledger bal | ||||||
|  | $ assets       | ||||||
|  | $ equity | ||||||
|  | $ expenses     | ||||||
|  | $ income       | ||||||
|  | $ liabilities  | ||||||
|  | 
 | ||||||
|  | with an account pattern, show only the ones with matching names: | ||||||
|  | 
 | ||||||
|  | > ledger bal asset | ||||||
|  | $ assets       | ||||||
|  | 
 | ||||||
|  | with -s, show all subaccounts of matched accounts: | ||||||
|  | 
 | ||||||
|  | > ledger -s bal asset | ||||||
|  | $ assets       | ||||||
|  | $  cash        | ||||||
|  | $  checking    | ||||||
|  | $  saving | ||||||
|  | 
 | ||||||
|  | again: | ||||||
|  | 
 | ||||||
|  | > ledger bal a | ||||||
|  | $ assets       | ||||||
|  | $  cash        | ||||||
|  | $  saving | ||||||
|  | $ income       | ||||||
|  | $  salary      | ||||||
|  | $ liabilities  | ||||||
|  | 
 | ||||||
|  | and including subaccounts: | ||||||
|  | 
 | ||||||
|  | > ledger -s bal a | ||||||
|  | $ assets       | ||||||
|  | $  cash        | ||||||
|  | $  checking    | ||||||
|  | $  saving | ||||||
|  | $ income       | ||||||
|  | $  salary      | ||||||
|  | $ liabilities  | ||||||
|  | $  debts | ||||||
|  | 
 | ||||||
|  | but also, elide boring accounts whenever possible, so if savings is 0 and | ||||||
|  | income/liabilities have no transactions the above would be displayed as: | ||||||
|  | 
 | ||||||
|  | > ledger -s bal a | ||||||
|  | $ assets       | ||||||
|  | $  cash        | ||||||
|  | $  checking    | ||||||
|  | $ income:salary | ||||||
|  | $ liabilities:debts | ||||||
|  | 
 | ||||||
|  | algorithm: | ||||||
|  | 
 | ||||||
|  | 1 filter account tree by name, keeping any necessary parents | ||||||
|  | 2 add subaccounts if -s | ||||||
|  | 3 display account tree, eliding boring accounts | ||||||
|  | 
 | ||||||
|  | * include subaccounts | ||||||
|  | elide boring accounts | ||||||
|  | handle multiple patterns | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | optimization: add CookedLedger caching acct txns, boring status etc. | ||||||
|  |  refactor apis | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | speed | ||||||
|  |  profile, refactor, optimize | ||||||
| 
 | 
 | ||||||
| basic features | basic features | ||||||
|  * balance report account matching |  | ||||||
|  -f - |  -f - | ||||||
|  print |  print | ||||||
|  -j and -J graph data output |  -j and -J graph data output | ||||||
|  !include |  !include | ||||||
|  read timelog files |  read timelog files | ||||||
| 
 | 
 | ||||||
| more features | advanced features | ||||||
|  handle mixed amounts |  handle mixed amounts | ||||||
|  3.0-style elision |  3.0-style elision | ||||||
|  -p period expressions |  -p period expressions | ||||||
| @ -26,15 +120,16 @@ new features | |||||||
|  timelog simple amount entries |  timelog simple amount entries | ||||||
|  better layout |  better layout | ||||||
| 
 | 
 | ||||||
| tests | testing | ||||||
|  better use of quickcheck/smallcheck |  better use of quickcheck/smallcheck | ||||||
|   http://blog.codersbase.com/2006/09/01/simple-unit-testing-in-haskell/ |   http://blog.codersbase.com/2006/09/01/simple-unit-testing-in-haskell/ | ||||||
|  ledger compatibility tests |  ledger compatibility tests | ||||||
| 
 | 
 | ||||||
| docs | documentation | ||||||
|  literate docs |  literate docs | ||||||
|  better use of haddock |  better use of haddock | ||||||
| 
 | 
 | ||||||
| marketing | marketing | ||||||
|  set up as a cabal/hackage project following wiki howto ? |  set up as a cabal/hackage project following wiki howto ? | ||||||
|  |   http://en.wikibooks.org/wiki/Haskell/Packaging | ||||||
|  announce on haskell list, wiki |  announce on haskell list, wiki | ||||||
|  | |||||||
							
								
								
									
										26
									
								
								Utils.hs
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								Utils.hs
									
									
									
									
									
								
							| @ -17,9 +17,6 @@ import Text.Printf | |||||||
| import Text.Regex | import Text.Regex | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| rhead = head . reverse  |  | ||||||
| rtail = reverse . tail . reverse  |  | ||||||
| 
 |  | ||||||
| splitAtElement :: Eq a => a -> [a] -> [[a]] | splitAtElement :: Eq a => a -> [a] -> [[a]] | ||||||
| splitAtElement e l =  | splitAtElement e l =  | ||||||
|     case dropWhile (e==) l of |     case dropWhile (e==) l of | ||||||
| @ -39,3 +36,26 @@ tildeExpand ('~':'/':xs) =  getHomeDirectory >>= return . (++ ('/':xs)) | |||||||
| --                                return (homeDirectory pw ++ path) | --                                return (homeDirectory pw ++ path) | ||||||
| tildeExpand xs           =  return xs | tildeExpand xs           =  return xs | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | -- tree tools | ||||||
|  | 
 | ||||||
|  | root = rootLabel | ||||||
|  | branches = subForest | ||||||
|  | 
 | ||||||
|  | -- apply f to all tree nodes | ||||||
|  | treemap :: (a -> b) -> Tree a -> Tree b | ||||||
|  | treemap f t = Node (f $ root t) (map (treemap f) $ branches t) | ||||||
|  | 
 | ||||||
|  | -- remove all subtrees whose nodes do not fulfill predicate | ||||||
|  | treefilter :: (a -> Bool) -> Tree a -> Tree a | ||||||
|  | treefilter f t = Node  | ||||||
|  |                  (root t)  | ||||||
|  |                  (map (treefilter f) $ filter (treeany f) $ branches t) | ||||||
|  |      | ||||||
|  | -- is predicate true in any node of tree ? | ||||||
|  | treeany :: (a -> Bool) -> Tree a -> Bool | ||||||
|  | treeany f t = (f $ root t) || (any (treeany f) $ branches t) | ||||||
|  |      | ||||||
|  | -- treedrop -- remove the leaves which do fulfill predicate.  | ||||||
|  | -- treedropall -- do this repeatedly. | ||||||
|  | 
 | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								hledger.hs
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								hledger.hs
									
									
									
									
									
								
							| @ -30,6 +30,7 @@ import System.Environment (withArgs) -- for testing in old hugs | |||||||
| import Test.HUnit (runTestTT) | import Test.HUnit (runTestTT) | ||||||
| import Test.QuickCheck (quickCheck) | import Test.QuickCheck (quickCheck) | ||||||
| import Text.ParserCombinators.Parsec (ParseError) | import Text.ParserCombinators.Parsec (ParseError) | ||||||
|  | import Debug.Trace | ||||||
| 
 | 
 | ||||||
| import Options | import Options | ||||||
| import Models | import Models | ||||||
| @ -72,7 +73,7 @@ balance opts args = do | |||||||
| -- doWithLedgerFile = | -- doWithLedgerFile = | ||||||
| --     getLedgerFilePath >>= parseLedgerFile >>= doWithParsed | --     getLedgerFilePath >>= parseLedgerFile >>= doWithParsed | ||||||
| 
 | 
 | ||||||
| doWithParsed :: (a -> IO ()) -> (Either ParseError a) -> IO () | doWithParsed :: Show a => (a -> IO ()) -> (Either ParseError a) -> IO () | ||||||
| doWithParsed a p = do | doWithParsed a p = do | ||||||
|   case p of Left e -> parseError e |   case p of Left e -> parseError e | ||||||
|             Right v -> a v |             Right v -> a v | ||||||
| @ -86,9 +87,9 @@ printRegister opts args ledger = do | |||||||
| 
 | 
 | ||||||
| printBalance :: [Flag] -> [String] -> Ledger -> IO () | printBalance :: [Flag] -> [String] -> Ledger -> IO () | ||||||
| printBalance opts args ledger = do | printBalance opts args ledger = do | ||||||
|   putStr $ case showsubs of |   putStr $ showLedgerAccounts ledger showsubs acctpats | ||||||
|              True -> showLedgerAccounts ledger 999 |     where  | ||||||
|              False -> showLedgerAccounts ledger 1 |       showsubs = (ShowSubs `elem` opts) | ||||||
|       where  |       (acctpats,_) = ledgerPatternArgs args | ||||||
|         showsubs = (ShowSubs `elem` opts) | 
 | ||||||
|         (acctpats,_) = ledgerPatternArgs args | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user