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]")
prices opts j = do
-- XXX the original hledger-prices script always ignored assertions -- XXX the original hledger-prices script always ignored assertions
let cprices = concatMap postingCosts . allPostings $ j prices opts j = do
icprices = concatMap postingCosts . mapAmount invertPrice . allPostings $ j d <- getCurrentDay
printPrices = mapM_ (putStrLn . showPrice) let
q = queryFromOpts d (reportopts_ opts)
ps = filter (matchesPosting q) $ allPostings j
mprices = jmarketprices j
cprices = concatMap postingCosts ps
icprices = concatMap postingCosts . mapAmount invertPrice $ ps
allprices = mprices ++ ifBoolOpt "costs" cprices ++ ifBoolOpt "inverted-costs" icprices
mapM_ (putStrLn . showPrice) $
sortOn mpdate $
filter (matchesMarketPrice q) $
allprices
where
ifBoolOpt opt | boolopt opt $ rawopts_ opts = id ifBoolOpt opt | boolopt opt $ rawopts_ opts = id
| otherwise = const [] | otherwise = const []
allPrices = sortOn mpdate . concat $
[ jmarketprices j
, ifBoolOpt "costs" cprices
, ifBoolOpt "inverted-costs" icprices
]
printPrices allPrices
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]