prices: allow filtering by query, clarify docs

Query arguments are allowed, prices can be filtered by date, and
postings providing transaction prices can be filtered by anything.
This commit is contained in:
Simon Michael 2018-07-15 08:37:13 +01:00
parent 91e5baf617
commit 92404bb847
2 changed files with 46 additions and 17 deletions

View File

@ -19,6 +19,7 @@ module Hledger.Query (
-- * accessors -- * accessors
queryIsNull, queryIsNull,
queryIsAcct, queryIsAcct,
queryIsAmt,
queryIsDepth, queryIsDepth,
queryIsDate, queryIsDate,
queryIsDate2, queryIsDate2,
@ -41,6 +42,8 @@ module Hledger.Query (
matchesAccount, matchesAccount,
matchesMixedAmount, matchesMixedAmount,
matchesAmount, matchesAmount,
matchesCommodity,
matchesMarketPrice,
words'', words'',
-- * tests -- * tests
tests_Hledger_Query tests_Hledger_Query
@ -481,6 +484,10 @@ queryIsAcct :: Query -> Bool
queryIsAcct (Acct _) = True queryIsAcct (Acct _) = True
queryIsAcct _ = False queryIsAcct _ = False
queryIsAmt :: Query -> Bool
queryIsAmt (Amt _ _) = True
queryIsAmt _ = False
queryIsSym :: Query -> Bool queryIsSym :: Query -> Bool
queryIsSym (Sym _) = True queryIsSym (Sym _) = True
queryIsSym _ = False queryIsSym _ = False
@ -635,6 +642,10 @@ matchesMixedAmount :: Query -> MixedAmount -> Bool
matchesMixedAmount q (Mixed []) = q `matchesAmount` nullamt matchesMixedAmount q (Mixed []) = q `matchesAmount` nullamt
matchesMixedAmount q (Mixed as) = any (q `matchesAmount`) as matchesMixedAmount q (Mixed as) = any (q `matchesAmount`) as
matchesCommodity :: Query -> CommoditySymbol -> Bool
matchesCommodity (Sym r) s = regexMatchesCI ("^" ++ r ++ "$") (T.unpack s)
matchesCommodity _ _ = True
-- | Does the match expression match this (simple) amount ? -- | Does the match expression match this (simple) amount ?
matchesAmount :: Query -> Amount -> Bool matchesAmount :: Query -> Amount -> Bool
matchesAmount (Not q) a = not $ q `matchesAmount` a matchesAmount (Not q) a = not $ q `matchesAmount` a
@ -644,7 +655,7 @@ matchesAmount (Or qs) a = any (`matchesAmount` a) qs
matchesAmount (And qs) a = all (`matchesAmount` a) qs matchesAmount (And qs) a = all (`matchesAmount` a) qs
-- --
matchesAmount (Amt ord n) a = compareAmount ord n a matchesAmount (Amt ord n) a = compareAmount ord n a
matchesAmount (Sym r) a = regexMatchesCI ("^" ++ r ++ "$") $ T.unpack $ acommodity a matchesAmount (Sym r) a = matchesCommodity (Sym r) (acommodity a)
-- --
matchesAmount _ _ = True matchesAmount _ _ = True
@ -688,7 +699,7 @@ matchesPosting q@(Amt _ _) Posting{pamount=amt} = q `matchesMixedAmount` amt
-- matchesPosting (Empty False) Posting{pamount=a} = True -- matchesPosting (Empty False) Posting{pamount=a} = True
-- matchesPosting (Empty True) Posting{pamount=a} = isZeroMixedAmount a -- matchesPosting (Empty True) Posting{pamount=a} = isZeroMixedAmount a
matchesPosting (Empty _) _ = True matchesPosting (Empty _) _ = True
matchesPosting (Sym r) Posting{pamount=Mixed as} = any (regexMatchesCI $ "^" ++ r ++ "$") $ map (T.unpack . acommodity) as matchesPosting (Sym r) Posting{pamount=Mixed as} = any (matchesCommodity (Sym r)) $ map acommodity as
matchesPosting (Tag n v) p = case (n, v) of matchesPosting (Tag n v) p = case (n, v) of
("payee", Just v) -> maybe False (regexMatchesCI v . T.unpack . transactionPayee) $ ptransaction p ("payee", Just v) -> maybe False (regexMatchesCI v . T.unpack . transactionPayee) $ ptransaction p
("note", Just v) -> maybe False (regexMatchesCI v . T.unpack . transactionNote) $ ptransaction p ("note", Just v) -> maybe False (regexMatchesCI v . T.unpack . transactionNote) $ ptransaction p
@ -771,6 +782,18 @@ matchesTags namepat valuepat = not . null . filter (match namepat valuepat)
match npat Nothing (n,_) = regexMatchesCI npat (T.unpack n) -- XXX match npat Nothing (n,_) = regexMatchesCI npat (T.unpack n) -- XXX
match npat (Just vpat) (n,v) = regexMatchesCI npat (T.unpack n) && regexMatchesCI vpat (T.unpack v) match npat (Just vpat) (n,v) = regexMatchesCI npat (T.unpack n) && regexMatchesCI vpat (T.unpack v)
-- | Does the query match this market price ?
matchesMarketPrice :: Query -> MarketPrice -> Bool
matchesMarketPrice (None) _ = False
matchesMarketPrice (Not q) p = not $ matchesMarketPrice q p
matchesMarketPrice (Or qs) p = any (`matchesMarketPrice` p) qs
matchesMarketPrice (And qs) p = all (`matchesMarketPrice` p) qs
matchesMarketPrice q@(Amt _ _) p = matchesAmount q (mpamount p)
matchesMarketPrice q@(Sym _) p = matchesCommodity q (mpcommodity p)
matchesMarketPrice (Date span) p = spanContainsDate span (mpdate p)
matchesMarketPrice _ _ = True
-- tests -- tests
tests_Hledger_Query :: Test tests_Hledger_Query :: Test

View File

@ -17,28 +17,34 @@ import System.Console.CmdArgs.Explicit
pricesmode = hledgerCommandMode pricesmode = hledgerCommandMode
[here| prices [here| prices
Print all market prices from the journal. Print market price directives from the journal.
With --costs, also print synthetic market prices based on transaction prices.
With --inverted-costs, also print inverse prices based on transaction prices.
Prices (and postings providing prices) can be filtered by a query.
|] |]
[flagNone ["costs"] (setboolopt "costs") "print transaction prices from postings" [flagNone ["costs"] (setboolopt "costs") "print transaction prices from postings"
,flagNone ["inverted-costs"] (setboolopt "inverted-costs") "print transaction inverted prices from postings also"] ,flagNone ["inverted-costs"] (setboolopt "inverted-costs") "print transaction inverted prices from postings also"]
[generalflagsgroup1] [generalflagsgroup1]
[] []
([], Nothing) ([], Just $ argsFlag "[QUERY]")
-- XXX the original hledger-prices script always ignored assertions
prices opts j = do prices opts j = do
-- XXX the original hledger-prices script always ignored assertions d <- getCurrentDay
let cprices = concatMap postingCosts . allPostings $ j let
icprices = concatMap postingCosts . mapAmount invertPrice . allPostings $ j q = queryFromOpts d (reportopts_ opts)
printPrices = mapM_ (putStrLn . showPrice) ps = filter (matchesPosting q) $ allPostings j
ifBoolOpt opt | boolopt opt $ rawopts_ opts = id mprices = jmarketprices j
| otherwise = const [] cprices = concatMap postingCosts ps
allPrices = sortOn mpdate . concat $ icprices = concatMap postingCosts . mapAmount invertPrice $ ps
[ jmarketprices j allprices = mprices ++ ifBoolOpt "costs" cprices ++ ifBoolOpt "inverted-costs" icprices
, ifBoolOpt "costs" cprices mapM_ (putStrLn . showPrice) $
, ifBoolOpt "inverted-costs" icprices sortOn mpdate $
] filter (matchesMarketPrice q) $
allprices
printPrices allPrices where
ifBoolOpt opt | boolopt opt $ rawopts_ opts = id
| otherwise = const []
showPrice :: MarketPrice -> String showPrice :: MarketPrice -> String
showPrice mp = unwords ["P", show $ mpdate mp, T.unpack . quoteCommoditySymbolIfNeeded $ mpcommodity mp, showAmountWithZeroCommodity $ mpamount mp] showPrice mp = unwords ["P", show $ mpdate mp, T.unpack . quoteCommoditySymbolIfNeeded $ mpcommodity mp, showAmountWithZeroCommodity $ mpamount mp]