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
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
module BasicTypes
|
module BasicTypes
|
||||||
where
|
where
|
||||||
import Utils
|
import Utils
|
||||||
|
|
||||||
|
|||||||
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