support negative patterns and ledger's special balance report account matching rule

This commit is contained in:
Simon Michael 2008-10-15 17:04:47 +00:00
parent 8306c2f6b3
commit 5fcab59414
7 changed files with 51 additions and 30 deletions

View File

@ -66,13 +66,10 @@ topAccounts :: Ledger -> [Account]
topAccounts l = map root $ branches $ ledgerAccountTree 9999 l
-- | Accounts in ledger whose name matches the pattern, in tree order.
-- Like ledger (I think), if the pattern contains : we match the full
-- name, otherwise just the leaf name.
-- We apply ledger's special rules for balance report account matching
-- (see 'matchLedgerPatterns').
accountsMatching :: [String] -> Ledger -> [Account]
accountsMatching pats l = filter (containsRegex (regexFor pats) . name) $ accounts l
where name = if any (elem ':') pats
then aname
else accountLeafName . aname
accountsMatching pats l = filter (matchLedgerPatterns True pats . aname) $ accounts l
-- | List a ledger account's immediate subaccounts
subAccounts :: Ledger -> Account -> [Account]

View File

@ -15,6 +15,8 @@ import Ledger.Entry
import Ledger.Transaction
negativepatternchar = '-'
instance Show RawLedger where
show l = printf "RawLedger with %d entries, %d accounts: %s"
((length $ entries l) +
@ -42,20 +44,18 @@ rawLedgerAccountNameTree l = accountNameTreeFrom $ rawLedgerAccountNames l
-- | Remove ledger entries we are not interested in.
-- Keep only those which fall between the begin and end dates, and match
-- the description pattern.
filterRawLedger :: String -> String -> Regex -> RawLedger -> RawLedger
filterRawLedger begin end descpat =
filterRawLedger :: String -> String -> [String] -> RawLedger -> RawLedger
filterRawLedger begin end pats =
filterRawLedgerEntriesByDate begin end .
filterRawLedgerEntriesByDescription descpat
filterRawLedgerEntriesByDescription pats
-- | Keep only entries whose description matches the description pattern.
filterRawLedgerEntriesByDescription :: Regex -> RawLedger -> RawLedger
filterRawLedgerEntriesByDescription descpat (RawLedger ms ps es f) =
filterRawLedgerEntriesByDescription :: [String] -> RawLedger -> RawLedger
filterRawLedgerEntriesByDescription pats (RawLedger ms ps es f) =
RawLedger ms ps (filter matchdesc es) f
where
matchdesc :: Entry -> Bool
matchdesc e = case matchRegex descpat (edescription e) of
Nothing -> False
otherwise -> True
matchdesc = matchLedgerPatterns False pats . edescription
-- | Keep only entries which fall between begin and end dates.
-- We include entries on the begin date and exclude entries on the end
@ -73,6 +73,28 @@ filterRawLedgerEntriesByDate begin end (RawLedger ms ps es f) =
entrydate = parsedate $ edate e
-- | Check if a set of ledger account/description patterns matches the
-- given account name or entry description, applying ledger's special
-- cases.
--
-- Patterns are regular expression strings, and those beginning with - are
-- negative patterns. The special case is that account patterns match the
-- full account name except in balance reports when the pattern does not
-- contain : and is a positive pattern, where it matches only the leaf
-- name.
matchLedgerPatterns :: Bool -> [String] -> String -> Bool
matchLedgerPatterns forbalancereport pats str =
(null positives || any ismatch positives) && (null negatives || (not $ any ismatch negatives))
where
isnegative = (== negativepatternchar) . head
(negatives,positives) = partition isnegative pats
ismatch pat = containsRegex (mkRegex pat') matchee
where
pat' = if isnegative pat then drop 1 pat else pat
matchee = if forbalancereport && (not $ ':' `elem` pat) && (not $ isnegative pat)
then accountLeafName str
else str
-- | Give amounts the display settings of the first one detected in each commodity.
normaliseRawLedgerAmounts :: RawLedger -> RawLedger
normaliseRawLedgerAmounts l@(RawLedger ms ps es f) = RawLedger ms ps es' f

View File

@ -44,20 +44,19 @@ import Text.ParserCombinators.Parsec (parse)
instance Show Regex where show r = "a Regex"
-- | convert a list of strings to a regular expression matching any of them,
-- or a wildcard if there are none.
regexFor :: [String] -> Regex
regexFor [] = wildcard
regexFor ss = mkRegex $ concat $ ["("] ++ (intersperse "|" ss) ++ [")"]
wildcard :: Regex
wildcard = mkRegex ".*"
containsRegex :: Regex -> String -> Bool
containsRegex r s = case matchRegex r s of
Just _ -> True
otherwise -> False
-- | Convert a list of strings (possibly with regular expression syntax)
-- to a regular expression matching any of them, or a wildcard if there
-- are none.
combinedRegex :: [String] -> Regex
combinedRegex [] = mkRegex ".*"
combinedRegex args = mkRegex $ concat $ ["("] ++ intersperse "|" args ++ [")"]
-- time
-- | Parse a date-time string to a time type, or raise an error.

View File

@ -19,7 +19,7 @@ showTransactionsWithBalances opts args l =
unlines $ showTransactionsWithBalances' ts nulltxn startingbalance
where
ts = filter matchtxn $ ledgerTransactions l
matchtxn (Transaction _ _ desc acct _) = (containsRegex (regexFor apats) acct)
matchtxn (Transaction _ _ desc acct _) = matchLedgerPatterns False apats acct
pats@(apats,dpats) = parseAccountDescriptionArgs args
startingbalance = nullamt
showTransactionsWithBalances' :: [Transaction] -> Transaction -> Amount -> [String]

View File

@ -177,6 +177,11 @@ balancecommandtests =
\ $1\n\
\" --"
$ showBalanceReport [] ["expenses:food"] l
,
"balance report negative account pattern always matches full name" ~:
do
l <- ledgerfromfile "sample.ledger"
assertequal "" $ showBalanceReport [] ["-e"] l
]
-- | Assert a parsed thing equals some expected thing, or print a parse error.

View File

@ -21,7 +21,7 @@ rawledgerfromfile f = do
ledgerfromfile :: FilePath -> IO Ledger
ledgerfromfile f = do
l <- rawledgerfromfile f
return $ cacheLedger $ filterRawLedger "" "" wildcard l
return $ cacheLedger $ filterRawLedger "" "" [] l
-- | get a RawLedger from the file your LEDGER environment variable
-- variable points to or (WARNING) an empty one if there was a problem.
@ -35,7 +35,7 @@ myrawledger = do
myledger :: IO Ledger
myledger = do
l <- myrawledger
return $ cacheLedger $ filterRawLedger "" "" wildcard l
return $ cacheLedger $ filterRawLedger "" "" [] l
-- | get a named account from your ledger file
myaccount :: AccountName -> IO Account

View File

@ -70,10 +70,8 @@ parseLedgerAndDo :: [Opt] -> [String] -> ([Opt] -> [String] -> Ledger -> IO ())
parseLedgerAndDo opts args cmd =
ledgerFilePathFromOpts opts >>= parseLedgerFile >>= either printParseError runthecommand
where
runthecommand = cmd opts args . cacheLedger . normaliseRawLedgerAmounts . filterRawLedger begin end descpat
runthecommand = cmd opts args . cacheLedger . normaliseRawLedgerAmounts . filterRawLedger begin end descpats
begin = beginDateFromOpts opts
end = endDateFromOpts opts
acctpat = regexFor acctpats
descpat = regexFor descpats
(acctpats,descpats) = parseAccountDescriptionArgs args
descpats = snd $ parseAccountDescriptionArgs args