query parser fixes, add date: and edate: matching
This commit is contained in:
		
							parent
							
								
									48f208be8f
								
							
						
					
					
						commit
						d2e6b8cbae
					
				| @ -47,22 +47,35 @@ data Matcher = MatchAny                   -- ^ always match | |||||||
|              | MatchEmpty Bool Bool       -- ^ match if "emptiness" (amount is zero ?) has this value |              | MatchEmpty Bool Bool       -- ^ match if "emptiness" (amount is zero ?) has this value | ||||||
|              | MatchDepth Bool Int        -- ^ match if account depth is less than or equal to this value |              | MatchDepth Bool Int        -- ^ match if account depth is less than or equal to this value | ||||||
|              -- XXX not sure if this belongs here |              -- XXX not sure if this belongs here | ||||||
|              | MatchInAcct Bool String    -- ^ match postings whose transaction contains a posting to an account matching this regexp |              | MatchInAcct Bool String    -- ^ a flag indicating account register mode | ||||||
|     deriving (Show, Eq) |     deriving (Show, Eq) | ||||||
| 
 | 
 | ||||||
| -- | Parse a query expression string as a list of match patterns OR'd together. | -- | Parse a query expression string as a list of match patterns OR'd together. | ||||||
| -- The current date is required to interpret relative dates. | -- The current date is required to interpret relative dates. | ||||||
| parseMatcher :: Day -> String -> Matcher | parseMatcher :: Day -> String -> Matcher | ||||||
| parseMatcher refdate s = MatchAnd $ map parseword $ words'' matcherprefixes s | parseMatcher refdate s = m | ||||||
|   where |   where | ||||||
|  |     m = case ms of []     -> MatchAny | ||||||
|  |                    (m:[]) -> m | ||||||
|  |                    ms     -> MatchAnd ms | ||||||
|  |     ms = map parseword $ words'' matcherprefixes s | ||||||
|  | 
 | ||||||
|  |     -- keep synced with patterns below | ||||||
|  |     matcherprefixes = map (++":") [ | ||||||
|  |                        "desc","acct","inacct","in","date","edate","status","real","empty","depth"] | ||||||
|  | 
 | ||||||
|     parseword :: String -> Matcher |     parseword :: String -> Matcher | ||||||
|     parseword ('n':'o':'t':':':s) = negateMatch $ parseMatcher refdate s |     parseword ('n':'o':'t':':':s) = negateMatch $ parseMatcher refdate $ quoteIfSpaced s | ||||||
|     parseword ('d':'e':'s':'c':':':s) = MatchDesc True s |     parseword ('d':'e':'s':'c':':':s) = MatchDesc True s | ||||||
|     parseword ('a':'c':'c':'t':':':s) = MatchAcct True s |     parseword ('a':'c':'c':'t':':':s) = MatchAcct True s | ||||||
|     parseword ('i':'n':'a':'c':'c':'t':':':s) = MatchInAcct True s |     parseword ('i':'n':'a':'c':'c':'t':':':s) = MatchInAcct True s | ||||||
|     parseword ('i':'n':':':s)                 = MatchInAcct True s |     parseword ('i':'n':':':s)                 = MatchInAcct True s | ||||||
|     parseword ('d':'a':'t':'e':':':s) = MatchDate True $ spanFromSmartDateString refdate s |     parseword ('d':'a':'t':'e':':':s) = | ||||||
|     parseword ('e':'d':'a':'t':'e':':':s) = MatchEDate True $ spanFromSmartDateString refdate s |         case parsePeriodExpr refdate s of Left _ -> MatchAnd [] -- XXX warn | ||||||
|  |                                           Right (_,span) -> MatchDate True span | ||||||
|  |     parseword ('e':'d':'a':'t':'e':':':s) = | ||||||
|  |         case parsePeriodExpr refdate s of Left _ -> MatchAnd [] -- XXX warn | ||||||
|  |                                           Right (_,span) -> MatchEDate True span | ||||||
|     parseword ('s':'t':'a':'t':'u':'s':':':s) = MatchStatus True $ parseStatus s |     parseword ('s':'t':'a':'t':'u':'s':':':s) = MatchStatus True $ parseStatus s | ||||||
|     parseword ('r':'e':'a':'l':':':s) = MatchReal True $ parseBool s |     parseword ('r':'e':'a':'l':':':s) = MatchReal True $ parseBool s | ||||||
|     parseword ('e':'m':'p':'t':'y':':':s) = MatchEmpty True $ parseBool s |     parseword ('e':'m':'p':'t':'y':':':s) = MatchEmpty True $ parseBool s | ||||||
| @ -70,9 +83,6 @@ parseMatcher refdate s = MatchAnd $ map parseword $ words'' matcherprefixes s | |||||||
|     parseword "" = MatchAny |     parseword "" = MatchAny | ||||||
|     parseword s = parseword $ "acct:"++s |     parseword s = parseword $ "acct:"++s | ||||||
| 
 | 
 | ||||||
|     -- keep synced with patterns above |  | ||||||
|     matcherprefixes = map (++":") ["not","desc","acct","inacct","in","date","edate","status","real","empty","depth"] |  | ||||||
| 
 |  | ||||||
|     parseStatus "*" = True |     parseStatus "*" = True | ||||||
|     parseStatus _ = False |     parseStatus _ = False | ||||||
| 
 | 
 | ||||||
| @ -80,15 +90,16 @@ parseMatcher refdate s = MatchAnd $ map parseword $ words'' matcherprefixes s | |||||||
| 
 | 
 | ||||||
| -- | Quote-and-prefix-aware version of words - don't split on spaces which | -- | Quote-and-prefix-aware version of words - don't split on spaces which | ||||||
| -- are inside quotes, including quotes which may have one of the specified | -- are inside quotes, including quotes which may have one of the specified | ||||||
| -- prefixes in front. | -- prefixes in front, and maybe an additional not: prefix in front of that. | ||||||
| words'' :: [String] -> String -> [String] | words'' :: [String] -> String -> [String] | ||||||
| words'' prefixes = fromparse . parsewith maybeprefixedquotedphrases | words'' prefixes = fromparse . parsewith maybeprefixedquotedphrases | ||||||
|     where |     where | ||||||
|       maybeprefixedquotedphrases = choice' [prefixedQuotedPattern, quotedPattern, pattern] `sepBy` many1 spacenonewline |       maybeprefixedquotedphrases = choice' [prefixedQuotedPattern, quotedPattern, pattern] `sepBy` many1 spacenonewline | ||||||
|       prefixedQuotedPattern = do |       prefixedQuotedPattern = do | ||||||
|  |         not' <- optionMaybe $ string "not:" | ||||||
|         prefix <- choice' $ map string prefixes |         prefix <- choice' $ map string prefixes | ||||||
|         p <- quotedPattern |         p <- quotedPattern | ||||||
|         return $ prefix ++ stripquotes p |         return $ fromMaybe "" not' ++ prefix ++ stripquotes p | ||||||
|       quotedPattern = do |       quotedPattern = do | ||||||
|         p <- between (oneOf "'\"") (oneOf "'\"") $ many $ noneOf "'\"" |         p <- between (oneOf "'\"") (oneOf "'\"") $ many $ noneOf "'\"" | ||||||
|         return $ stripquotes p |         return $ stripquotes p | ||||||
| @ -128,11 +139,21 @@ matchesPosting (MatchDesc True r) p = regexMatchesCI r $ maybe "" tdescription $ | |||||||
| matchesPosting (MatchDesc False r) p = not $ (MatchDesc True r) `matchesPosting` p | matchesPosting (MatchDesc False r) p = not $ (MatchDesc True r) `matchesPosting` p | ||||||
| matchesPosting (MatchAcct True r) p = regexMatchesCI r $ paccount p | matchesPosting (MatchAcct True r) p = regexMatchesCI r $ paccount p | ||||||
| matchesPosting (MatchAcct False r) p = not $ (MatchAcct True r) `matchesPosting` p | matchesPosting (MatchAcct False r) p = not $ (MatchAcct True r) `matchesPosting` p | ||||||
| matchesPosting (MatchInAcct True r) p = True | matchesPosting (MatchInAcct True _) _ = True | ||||||
|     -- case ptransaction p of |     -- case ptransaction p of | ||||||
|     --     Just t -> (MatchAcct True r) `matchesTransaction` t && (MatchAcct False r) `matchesPosting` p |     --     Just t -> (MatchAcct True r) `matchesTransaction` t && (MatchAcct False r) `matchesPosting` p | ||||||
|     --     Nothing -> False |     --     Nothing -> False | ||||||
| -- matchesPosting (MatchInAcct False r) p = not $ (MatchInAcct True r) `matchesPosting` p | -- matchesPosting (MatchInAcct False r) p = not $ (MatchInAcct True r) `matchesPosting` p | ||||||
|  | matchesPosting (MatchDate True span) p = | ||||||
|  |     case d of Just d'  -> spanContainsDate span d' | ||||||
|  |               Nothing -> False | ||||||
|  |     where d = maybe Nothing (Just . tdate) $ ptransaction p | ||||||
|  | matchesPosting (MatchDate False span) p = not $ (MatchDate True span) `matchesPosting` p | ||||||
|  | matchesPosting (MatchEDate True span) p = | ||||||
|  |     case d of Just d  -> spanContainsDate span d | ||||||
|  |               Nothing -> False | ||||||
|  |     where d = maybe Nothing teffectivedate $ ptransaction p | ||||||
|  | matchesPosting (MatchEDate False span) p = not $ (MatchEDate True span) `matchesPosting` p | ||||||
| matchesPosting _ _ = False | matchesPosting _ _ = False | ||||||
| 
 | 
 | ||||||
| -- | Does the match expression match this transaction ? | -- | Does the match expression match this transaction ? | ||||||
| @ -145,6 +166,11 @@ matchesTransaction (MatchDesc False r) t = not $ (MatchDesc True r) `matchesTran | |||||||
| matchesTransaction m@(MatchAcct True _) t = any (m `matchesPosting`) $ tpostings t | matchesTransaction m@(MatchAcct True _) t = any (m `matchesPosting`) $ tpostings t | ||||||
| matchesTransaction (MatchAcct False r) t = not $ (MatchAcct True r) `matchesTransaction` t | matchesTransaction (MatchAcct False r) t = not $ (MatchAcct True r) `matchesTransaction` t | ||||||
| matchesTransaction (MatchInAcct sense r) t = (MatchAcct sense r) `matchesTransaction` t | matchesTransaction (MatchInAcct sense r) t = (MatchAcct sense r) `matchesTransaction` t | ||||||
|  | matchesTransaction (MatchDate True span) t = spanContainsDate span $ tdate t | ||||||
|  | matchesTransaction (MatchDate False span) t = not $ (MatchDate True span) `matchesTransaction` t | ||||||
|  | matchesTransaction (MatchEDate True span) Transaction{teffectivedate=Just d} = spanContainsDate span d | ||||||
|  | matchesTransaction _ Transaction{teffectivedate=Nothing} = False | ||||||
|  | matchesTransaction (MatchEDate False span) t = not $ (MatchEDate True span) `matchesTransaction` t | ||||||
| matchesTransaction _ _ = False | matchesTransaction _ _ = False | ||||||
| 
 | 
 | ||||||
| -- | Does the match expression match this account ? | -- | Does the match expression match this account ? | ||||||
| @ -208,8 +234,11 @@ tests_Hledger_Data_Matching = TestList | |||||||
| 
 | 
 | ||||||
|   "parseMatcher" ~: do |   "parseMatcher" ~: do | ||||||
|     let d = parsedate "2011/1/1" |     let d = parsedate "2011/1/1" | ||||||
|     parseMatcher d "in:'expenses:autres d\233penses'" `is` |     parseMatcher d "a" `is` (MatchAcct True "a") | ||||||
|      (MatchAnd [MatchInAcct True "expenses:autres d\233penses"]) |     parseMatcher d "acct:a" `is` (MatchAcct True "a") | ||||||
|  |     parseMatcher d "acct:a desc:b" `is` (MatchAnd [MatchAcct True "a", MatchDesc True "b"]) | ||||||
|  |     parseMatcher d "inacct:'expenses:autres d\233penses'" `is` (MatchInAcct True "expenses:autres d\233penses") | ||||||
|  |     parseMatcher d "not:desc:'a b'" `is` (MatchDesc False "a b") | ||||||
| 
 | 
 | ||||||
|   ,"matchesAccount" ~: do |   ,"matchesAccount" ~: do | ||||||
|     assertBool "positive acct match" $ matchesAccount (MatchAcct True "b:c") "a:bb:c:d" |     assertBool "positive acct match" $ matchesAccount (MatchAcct True "b:c") "a:bb:c:d" | ||||||
|  | |||||||
| @ -17,5 +17,6 @@ | |||||||
|     <td |     <td | ||||||
|      leave blank to see general journal (all postings)<br> |      leave blank to see general journal (all postings)<br> | ||||||
|      acct:REGEXP to see postings to matched accounts, desc:REGEXP to search by description<br> |      acct:REGEXP to see postings to matched accounts, desc:REGEXP to search by description<br> | ||||||
|  |      date:PERIODEXP or edate:PERIODEXP to match by date or effective date<br> | ||||||
|      inacct:FULLACCTNAME or click an account to see transactions and accurate balance in a single account<br> |      inacct:FULLACCTNAME or click an account to see transactions and accurate balance in a single account<br> | ||||||
|      not: to negate, single or double quotes to include spaces, multiple patterns are AND'ed |      not: to negate, single or double quotes to include spaces, multiple patterns are AND'ed | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user