From 08bbb832d08f06a103dacba60d96a5f6e6fe5942 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Wed, 16 May 2012 07:50:22 +0000 Subject: [PATCH] more query cleanup --- hledger-lib/Hledger/Data/Query.hs | 120 ++++++++++++++++-------------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/hledger-lib/Hledger/Data/Query.hs b/hledger-lib/Hledger/Data/Query.hs index 881b68d94..383814072 100644 --- a/hledger-lib/Hledger/Data/Query.hs +++ b/hledger-lib/Hledger/Data/Query.hs @@ -1,36 +1,35 @@ {-| -More generic matching, done in one step, unlike FilterSpec and filterJournal*. -Currently used only by hledger-web. +A general query system for matching items by standard criteria, in one +step unlike FilterSpec and filterJournal*. Currently used by hledger-web. -} module Hledger.Data.Query ( + -- * Query and QueryOpt Query(..), + QueryOpt(..), queryIsNull, - queryIsStartDateOnly, queryStartDate, - matchesTransaction, - matchesPosting, + queryIsStartDateOnly, inAccount, inAccountQuery, + -- * parsing + parseQuery, + -- * matching + matchesTransaction, + matchesPosting, + -- * tests tests_Hledger_Data_Query ) where import Data.Either import Data.List --- import Data.Map (findWithDefault, (!)) import Data.Maybe --- import Data.Ord import Data.Time.Calendar --- import Data.Time.LocalTime --- import Data.Tree import Safe (readDef, headDef) --- import System.Time (ClockTime(TOD)) import Test.HUnit import Text.ParserCombinators.Parsec --- import Text.Printf --- import qualified Data.Map as Map import Hledger.Utils import Hledger.Data.Types @@ -42,6 +41,7 @@ import Hledger.Data.Posting import Hledger.Data.Transaction -- import Hledger.Data.TimeLog + -- | A query is a composition of search criteria, which can be used to -- match postings, transactions, accounts and more. data Query = Any -- ^ always match @@ -69,6 +69,49 @@ data QueryOpt = QueryOptInAcctOnly AccountName -- ^ show an account register fo -- | QueryOptEffectiveDate -- ^ show effective dates instead of actual dates deriving (Show, Eq) +-- | Does this query match everything ? +queryIsNull Any = True +queryIsNull (And []) = True +queryIsNull (Not (Or [])) = True +queryIsNull _ = False + +-- | What start date does this query specify, if any ? +-- If the query is an OR expression, returns the earliest of the alternatives. +-- When the flag is true, look for a starting effective date instead. +queryStartDate :: Bool -> Query -> Maybe Day +queryStartDate effective (Or ms) = earliestMaybeDate $ map (queryStartDate effective) ms +queryStartDate effective (And ms) = latestMaybeDate $ map (queryStartDate effective) ms +queryStartDate False (Date (DateSpan (Just d) _)) = Just d +queryStartDate True (EDate (DateSpan (Just d) _)) = Just d +queryStartDate _ _ = Nothing + +-- | Does this query specify a start date and nothing else (that would +-- filter postings prior to the date) ? +-- When the flag is true, look for a starting effective date instead. +queryIsStartDateOnly :: Bool -> Query -> Bool +queryIsStartDateOnly _ Any = False +queryIsStartDateOnly _ None = False +queryIsStartDateOnly effective (Or ms) = and $ map (queryIsStartDateOnly effective) ms +queryIsStartDateOnly effective (And ms) = and $ map (queryIsStartDateOnly effective) ms +queryIsStartDateOnly False (Date (DateSpan (Just _) _)) = True +queryIsStartDateOnly True (EDate (DateSpan (Just _) _)) = True +queryIsStartDateOnly _ _ = False + +-- | What is the earliest of these dates, where Nothing is earliest ? +earliestMaybeDate :: [Maybe Day] -> Maybe Day +earliestMaybeDate = headDef Nothing . sortBy compareMaybeDates + +-- | What is the latest of these dates, where Nothing is earliest ? +latestMaybeDate :: [Maybe Day] -> Maybe Day +latestMaybeDate = headDef Nothing . sortBy (flip compareMaybeDates) + +-- | Compare two maybe dates, Nothing is earliest. +compareMaybeDates :: Maybe Day -> Maybe Day -> Ordering +compareMaybeDates Nothing Nothing = EQ +compareMaybeDates Nothing (Just _) = LT +compareMaybeDates (Just _) Nothing = GT +compareMaybeDates (Just a) (Just b) = compare a b + -- | The account we are currently focussed on, if any, and whether subaccounts are included. -- Just looks at the first query option. inAccount :: [QueryOpt] -> Maybe (AccountName,Bool) @@ -83,6 +126,12 @@ inAccountQuery [] = Nothing inAccountQuery (QueryOptInAcctOnly a:_) = Just $ Acct $ accountNameToAccountOnlyRegex a inAccountQuery (QueryOptInAcct a:_) = Just $ Acct $ accountNameToAccountRegex a +-- -- | Convert a query to its inverse. +-- negateQuery :: Query -> Query +-- negateQuery = Not + +-- query parsing + -- -- | A query restricting the account(s) to be shown in the sidebar, if any. -- -- Just looks at the first query option. -- showAccountMatcher :: [QueryOpt] -> Maybe Query @@ -185,9 +234,7 @@ parseBool s = s `elem` truestrings truestrings :: [String] truestrings = ["1","t","true"] --- -- | Convert a query to its inverse. --- negateQuery :: Query -> Query --- negateQuery = Not +-- query matching -- | Does the match expression match this posting ? matchesPosting :: Query -> Posting -> Bool @@ -239,48 +286,7 @@ matchesAccount (And ms) a = all (`matchesAccount` a) ms matchesAccount (Acct r) a = regexMatchesCI r a matchesAccount _ _ = False --- | What start date does this query specify, if any ? --- If the query is an OR expression, returns the earliest of the alternatives. --- When the flag is true, look for a starting effective date instead. -queryStartDate :: Bool -> Query -> Maybe Day -queryStartDate effective (Or ms) = earliestMaybeDate $ map (queryStartDate effective) ms -queryStartDate effective (And ms) = latestMaybeDate $ map (queryStartDate effective) ms -queryStartDate False (Date (DateSpan (Just d) _)) = Just d -queryStartDate True (EDate (DateSpan (Just d) _)) = Just d -queryStartDate _ _ = Nothing - --- | Does this query specify a start date and nothing else (that would --- filter postings prior to the date) ? --- When the flag is true, look for a starting effective date instead. -queryIsStartDateOnly :: Bool -> Query -> Bool -queryIsStartDateOnly _ Any = False -queryIsStartDateOnly _ None = False -queryIsStartDateOnly effective (Or ms) = and $ map (queryIsStartDateOnly effective) ms -queryIsStartDateOnly effective (And ms) = and $ map (queryIsStartDateOnly effective) ms -queryIsStartDateOnly False (Date (DateSpan (Just _) _)) = True -queryIsStartDateOnly True (EDate (DateSpan (Just _) _)) = True -queryIsStartDateOnly _ _ = False - --- | Does this query match everything ? -queryIsNull Any = True -queryIsNull (And []) = True -queryIsNull (Not (Or [])) = True -queryIsNull _ = False - --- | What is the earliest of these dates, where Nothing is earliest ? -earliestMaybeDate :: [Maybe Day] -> Maybe Day -earliestMaybeDate = headDef Nothing . sortBy compareMaybeDates - --- | What is the latest of these dates, where Nothing is earliest ? -latestMaybeDate :: [Maybe Day] -> Maybe Day -latestMaybeDate = headDef Nothing . sortBy (flip compareMaybeDates) - --- | Compare two maybe dates, Nothing is earliest. -compareMaybeDates :: Maybe Day -> Maybe Day -> Ordering -compareMaybeDates Nothing Nothing = EQ -compareMaybeDates Nothing (Just _) = LT -compareMaybeDates (Just _) Nothing = GT -compareMaybeDates (Just a) (Just b) = compare a b +-- tests tests_Hledger_Data_Query :: Test tests_Hledger_Data_Query = TestList