hledger 0.25.1 → 0.26
raw patch · 16 files changed
+325/−109 lines, 16 filesdep +criteriondep +timeitdep +unordered-containersdep −regexprdep ~hledgerdep ~hledger-libdep ~shakespearePVP ok
version bump matches the API change (PVP)
Dependencies added: criterion, timeit, unordered-containers
Dependencies removed: regexpr
Dependency ranges changed: hledger, hledger-lib, shakespeare
API changes (from Hackage documentation)
- Hledger.Cli.Options: CliOpts :: RawOpts -> String -> Maybe FilePath -> Maybe FilePath -> Maybe FilePath -> Maybe String -> [String] -> Bool -> Int -> Bool -> Maybe String -> Int -> ReportOpts -> CliOpts
+ Hledger.Cli.Options: CliOpts :: RawOpts -> String -> [FilePath] -> Maybe FilePath -> Maybe FilePath -> Maybe String -> [String] -> Bool -> Int -> Bool -> Maybe String -> Int -> ReportOpts -> CliOpts
- Hledger.Cli.Options: aliasesFromOpts :: CliOpts -> [(AccountName, AccountName)]
+ Hledger.Cli.Options: aliasesFromOpts :: CliOpts -> [AccountAlias]
- Hledger.Cli.Options: file_ :: CliOpts -> Maybe FilePath
+ Hledger.Cli.Options: file_ :: CliOpts -> [FilePath]
- Hledger.Cli.Options: journalFilePathFromOpts :: CliOpts -> IO String
+ Hledger.Cli.Options: journalFilePathFromOpts :: CliOpts -> IO [String]
Files
- CHANGES +142/−0
- Hledger/Cli.hs +6/−6
- Hledger/Cli/Accounts.hs +4/−4
- Hledger/Cli/Add.hs +6/−6
- Hledger/Cli/Balance.hs +1/−1
- Hledger/Cli/Histogram.hs +2/−7
- Hledger/Cli/Main.hs +23/−23
- Hledger/Cli/Options.hs +17/−19
- Hledger/Cli/Print.hs +2/−2
- Hledger/Cli/Register.hs +4/−1
- Hledger/Cli/Stats.hs +9/−5
- Hledger/Cli/Utils.hs +1/−1
- bench/bench.hs +70/−0
- hledger.cabal +32/−28
- test/test.hs +6/−0
- tests/suite.hs +0/−6
CHANGES view
@@ -1,9 +1,151 @@ User-visible changes in hledger and hledger-lib. +0.26 (2015/7/12)++Account aliases:++- Account aliases are once again non-regular-expression-based, by default. (#252)+ + The regex account aliases added in 0.24 trip up people switching between+ hledger and Ledger. (Also they are currently slow).+ + This change makes the old non-regex aliases the default; they are+ unsurprising, useful, and pretty close in functionality to Ledger's.+ + The new regex aliases are still available; they must be enclosed+ in forward slashes. (Ledger effectively ignores these.)+ +Journal format:++- We now parse, and also print, journal entries with no postings, as+ proposed on the mail lists. These are not well-formed General+ Journal entries/transactions, but here is my rationale:+ + - Ledger and beancount parse them+ - if they are parsed, they should be printed+ - they provide a convenient way to record (and report) non-transaction events+ - they permit more gradual introduction and learning of the concepts.+ So eg a beginner can keep a simple journal before learning about accounts and postings.++- Trailing whitespace after a `comment` directive is now ignored.++Command-line interface:++- The -f/file option may now be used multiple times. + This is equivalent to concatenating the input files before running hledger.+ The add command adds entries to the first file specified.++Queries:++- real: (no argument) is now a synonym for real:1++- tag: now matches tag names with a regular expression, like most other queries++- empty: is no longer supported, as it overlaps a bit confusingly with+ amt:0. The --empty flag is still available.++- You can now match on pending status (#250)+ + A transaction/posting status of ! (pending) was effectively equivalent+ to * (cleared). Now it's a separate state, not matched by --cleared.+ The new Ledger-compatible --pending flag matches it, and so does+ --uncleared.++ The relevant search query terms are now status:*, status:! and+ status: (the old status:1 and status:0 spellings are deprecated).+ + Since we interpret --uncleared and status: as "any state except cleared",+ it's not currently possible to match things which are neither cleared+ nor pending.++activity:+- activity no longer excludes 0-amount postings by default.++add:+- Don't show quotes around the journal file path in the "Creating..."+ message, for consistency with the subsequent "Adding..." message.++balancesheet:+- Accounts beginning with "debt" or now also recognised as liabilities.++print:+- We now limit the display precision of inferred prices. (#262)+ + When a transaction posts to two commodities without specifying the+ conversion price, we generate a price which makes it balance (cf+ http://hledger.org/manual.html#prices). The print command showed+ this with full precision (so that manual calculations with the+ displayed numbers would look right), but this sometimes meant we+ showed 255 digits (when there are multiple postings in the+ commodity being priced, and the averaged unit price is an+ irrational number). In this case we now set the price's display+ precision to the sum of the (max) display precisions of the+ commodities involved. An example:+ + hledgerdev -f- print+ <<<+ 1/1+ c C 10.00+ c C 11.00+ d D -320.00+ >>>+ 2015/01/01+ c C 10.00 @ D 15.2381+ c C 11.00 @ D 15.2381+ d D -320.00+ + >>>=0+ + There might still be cases where this will show more price decimal+ places than necessary. ++- We now show inferred unit prices with at least 2 decimal places.+ + When inferring prices, if the commodities involved have low+ display precisions, we don't do a good job of rendering+ accurate-looking unit prices. Eg if the journal doesn't use any+ decimal places, any inferred unit prices are also displayed with+ no decimal places, which makes them look wrong to the user. Now,+ we always give inferred unit prices a minimum display precision of+ 2, which helps a bit.++register:+- Postings with no amounts could give a runtime error in some obscure case, now fixed.++stats: +- stats now supports -o/--outputfile, like register/balance/print.+- An O(n^2) performance slowdown has been fixed, it's now much faster on large journals.++ +--------------------------------------++--------+--------++ | || 0.25 | 0.26 |+ +======================================++========+========++ | -f data/100x100x10.journal stats || 0.10 | 0.16 |+ | -f data/1000x1000x10.journal stats || 0.45 | 0.21 |+ | -f data/10000x1000x10.journal stats || 58.92 | 2.16 |+ +--------------------------------------++--------+--------+++Miscellaneous:++- The June 30 day span was not being rendered correctly; fixed. (#272)++- The bench script invoked by "cabal bench" or "stack bench" now runs+ some simple benchmarks.+ + You can get more accurate benchmark times by running with --criterion.+ This will usually give much the same numbers and takes much longer.+ + Or with --simplebench, it benchmarks whatever commands are+ configured in bench/default.bench. This mode uses the first+ "hledger" executable in $PATH.++- The deprecated shakespeare-text dependency has been removed more thoroughly.++ 0.25.1 (2015/4/29) - timelog: support the description field (#247)+ 0.25 (2015/4/7)
Hledger/Cli.hs view
@@ -346,7 +346,7 @@ tsourcepos=nullsourcepos, tdate=parsedate "2007/01/01", tdate2=Nothing,- tstatus=False,+ tstatus=Uncleared, tcode="*", tdescription="opening balance", tcomment="",@@ -362,7 +362,7 @@ tsourcepos=nullsourcepos, tdate=parsedate "2007/02/01", tdate2=Nothing,- tstatus=False,+ tstatus=Uncleared, tcode="*", tdescription="ayres suites", tcomment="",@@ -378,7 +378,7 @@ tsourcepos=nullsourcepos, tdate=parsedate "2007/01/02", tdate2=Nothing,- tstatus=False,+ tstatus=Uncleared, tcode="*", tdescription="auto transfer to savings", tcomment="",@@ -394,7 +394,7 @@ tsourcepos=nullsourcepos, tdate=parsedate "2007/01/03", tdate2=Nothing,- tstatus=False,+ tstatus=Uncleared, tcode="*", tdescription="poquito mas", tcomment="",@@ -410,7 +410,7 @@ tsourcepos=nullsourcepos, tdate=parsedate "2007/01/03", tdate2=Nothing,- tstatus=False,+ tstatus=Uncleared, tcode="*", tdescription="verizon", tcomment="",@@ -426,7 +426,7 @@ tsourcepos=nullsourcepos, tdate=parsedate "2007/01/03", tdate2=Nothing,- tstatus=False,+ tstatus=Uncleared, tcode="*", tdescription="discover", tcomment="",
Hledger/Cli/Accounts.hs view
@@ -49,10 +49,10 @@ accounts CliOpts{reportopts_=ropts} j = do d <- getCurrentDay let q = queryFromOpts d ropts- nodepthq = dbg "nodepthq" $ filterQuery (not . queryIsDepth) q- depth = dbg "depth" $ queryDepth $ filterQuery queryIsDepth q- ps = dbg "ps" $ journalPostings $ filterJournalPostings nodepthq j- as = dbg "as" $ nub $ filter (not . null) $ map (clipAccountName depth) $ sort $ map paccount ps+ nodepthq = dbg1 "nodepthq" $ filterQuery (not . queryIsDepth) q+ depth = dbg1 "depth" $ queryDepth $ filterQuery queryIsDepth q+ ps = dbg1 "ps" $ journalPostings $ filterJournalPostings nodepthq j+ as = dbg1 "as" $ nub $ filter (not . null) $ map (clipAccountName depth) $ sort $ map paccount ps as' | tree_ ropts = expandAccountNames as | otherwise = as render a | tree_ ropts = replicate (2 * (accountNameLevel a - 1)) ' ' ++ accountLeafName a
Hledger/Cli/Add.hs view
@@ -147,7 +147,7 @@ balancedPostingsWizard = do ps <- postingsWizard es2{esPostings=[]} let t = nulltransaction{tdate=date- ,tstatus=False+ ,tstatus=Uncleared ,tcode=code ,tdescription=desc ,tcomment=comment@@ -241,11 +241,11 @@ line $ green $ printf "Account %d%s%s: " pnum (endmsg::String) (showDefault def) where canfinish = not (null esPostings) && postingsBalanced esPostings- parseAccountOrDotOrNull _ _ "." = dbg $ Just "." -- . always signals end of txn- parseAccountOrDotOrNull "" True "" = dbg $ Just "" -- when there's no default and txn is balanced, "" also signals end of txn- parseAccountOrDotOrNull def@(_:_) _ "" = dbg $ Just def -- when there's a default, "" means use that- parseAccountOrDotOrNull _ _ s = dbg $ either (const Nothing) validateAccount $ runParser (accountnamep <* eof) (jContext esJournal) "" s -- otherwise, try to parse the input as an accountname- dbg = id -- strace+ parseAccountOrDotOrNull _ _ "." = dbg1 $ Just "." -- . always signals end of txn+ parseAccountOrDotOrNull "" True "" = dbg1 $ Just "" -- when there's no default and txn is balanced, "" also signals end of txn+ parseAccountOrDotOrNull def@(_:_) _ "" = dbg1 $ Just def -- when there's a default, "" means use that+ parseAccountOrDotOrNull _ _ s = dbg1 $ either (const Nothing) validateAccount $ runParser (accountnamep <* eof) (jContext esJournal) "" s -- otherwise, try to parse the input as an accountname+ dbg1 = id -- strace validateAccount s | no_new_accounts_ esOpts && not (s `elem` journalAccountNames esJournal) = Nothing | otherwise = Just s
Hledger/Cli/Balance.hs view
@@ -434,7 +434,7 @@ ++ (if row_total_ opts then [" Total"] else []) ++ (if average_ opts then ["Average"] else []) items' | empty_ opts = items- | otherwise = items -- dbg "2" $ filter (any (not . isZeroMixedAmount) . snd) $ dbg "1" items+ | otherwise = items -- dbg1 "2" $ filter (any (not . isZeroMixedAmount) . snd) $ dbg1 "1" items accts = map renderacct items' renderacct ((a,a',i),_,_,_) | tree_ opts = replicate ((i-1)*2) ' ' ++ a'
Hledger/Cli/Histogram.hs view
@@ -13,10 +13,8 @@ import System.Console.CmdArgs.Explicit import Text.Printf +import Hledger import Hledger.Cli.Options-import Hledger.Data-import Hledger.Reports-import Hledger.Query import Prelude hiding (putStr) import Hledger.Utils.UTF8IOCompat (putStr) @@ -54,10 +52,7 @@ -- same as Register -- should count transactions, not postings ? -- ps = sortBy (comparing postingDate) $ filterempties $ filter matchapats $ filterdepth $ journalPostings j- ps = sortBy (comparing postingDate) $ filterempties $ filter (q `matchesPosting`) $ journalPostings j- filterempties- | queryEmpty q = id- | otherwise = filter (not . isZeroMixedAmount . pamount)+ ps = sortBy (comparing postingDate) $ filter (q `matchesPosting`) $ journalPostings j printDayWith f (DateSpan b _, ps) = printf "%s %s\n" (show $ fromJust b) (f ps)
Hledger/Cli/Main.hs view
@@ -199,15 +199,15 @@ isNullCommand = null rawcmd (argsbeforecmd, argsaftercmd') = break (==rawcmd) args argsaftercmd = drop 1 argsaftercmd'- dbgM :: Show a => String -> a -> IO ()- dbgM = dbgAtM 2+ dbgIO :: Show a => String -> a -> IO ()+ dbgIO = tracePrettyAtIO 2 - dbgM "running" prognameandversion- dbgM "raw args" args- dbgM "raw args rearranged for cmdargs" args'- dbgM "raw command is probably" rawcmd- dbgM "raw args before command" argsbeforecmd- dbgM "raw args after command" argsaftercmd+ dbgIO "running" prognameandversion+ dbgIO "raw args" args+ dbgIO "raw args rearranged for cmdargs" args'+ dbgIO "raw command is probably" rawcmd+ dbgIO "raw args before command" argsbeforecmd+ dbgIO "raw args after command" argsaftercmd -- Search PATH for add-ons, excluding any that match built-in names. -- The precise addon names (including file extension) are used for command@@ -231,32 +231,32 @@ generalHelp = putStr $ showModeHelp $ mainmode addonDisplayNames badCommandError = error' ("command "++rawcmd++" is not recognized, run with no command to see a list") >> exitFailure f `orShowHelp` mode = if hasHelp args then putStr (showModeHelp mode) else f- dbgM "processed opts" opts- dbgM "command matched" cmd- dbgM "isNullCommand" isNullCommand- dbgM "isInternalCommand" isInternalCommand- dbgM "isExternalCommand" isExternalCommand- dbgM "isBadCommand" isBadCommand+ dbgIO "processed opts" opts+ dbgIO "command matched" cmd+ dbgIO "isNullCommand" isNullCommand+ dbgIO "isInternalCommand" isInternalCommand+ dbgIO "isExternalCommand" isExternalCommand+ dbgIO "isBadCommand" isBadCommand d <- getCurrentDay- dbgM "date span from opts" (dateSpanFromOpts d $ reportopts_ opts)- dbgM "interval from opts" (intervalFromOpts $ reportopts_ opts)- dbgM "query from opts & args" (queryFromOpts d $ reportopts_ opts)+ dbgIO "date span from opts" (dateSpanFromOpts d $ reportopts_ opts)+ dbgIO "interval from opts" (intervalFromOpts $ reportopts_ opts)+ dbgIO "query from opts & args" (queryFromOpts d $ reportopts_ opts) let runHledgerCommand -- high priority flags and situations. --help should be highest priority.- | hasHelp argsbeforecmd = dbgM "" "--help before command, showing general help" >> generalHelp+ | hasHelp argsbeforecmd = dbgIO "" "--help before command, showing general help" >> generalHelp | not (hasHelp argsaftercmd) && (hasVersion argsbeforecmd || (hasVersion argsaftercmd && isInternalCommand)) = putStrLn prognameandversion | not (hasHelp argsaftercmd) && (hasDetailedVersion argsbeforecmd || (hasDetailedVersion argsaftercmd && isInternalCommand)) = putStrLn prognameanddetailedversion -- \| (null externalcmd) && "binary-filename" `inRawOpts` rawopts = putStrLn $ binaryfilename progname -- \| "--browse-args" `elem` args = System.Console.CmdArgs.Helper.execute "cmdargs-browser" mainmode' args >>= (putStr . show)- | isNullCommand = dbgM "" "no command, showing general help" >> generalHelp+ | isNullCommand = dbgIO "" "no command, showing general help" >> generalHelp | isBadCommand = badCommandError -- internal commands | cmd == "activity" = withJournalDo opts histogram `orShowHelp` activitymode- | cmd == "add" = (journalFilePathFromOpts opts >>= ensureJournalFileExists >> withJournalDo opts add) `orShowHelp` addmode+ | cmd == "add" = (journalFilePathFromOpts opts >>= (ensureJournalFileExists . head) >> withJournalDo opts add) `orShowHelp` addmode | cmd == "accounts" = withJournalDo opts accounts `orShowHelp` accountsmode | cmd == "balance" = withJournalDo opts balance `orShowHelp` balancemode | cmd == "balancesheet" = withJournalDo opts balancesheet `orShowHelp` balancesheetmode@@ -271,9 +271,9 @@ | isExternalCommand = do let externalargs = argsbeforecmd ++ filter (not.(=="--")) argsaftercmd let shellcmd = printf "%s-%s %s" progname cmd (unwords' externalargs) :: String- dbgM "external command selected" cmd- dbgM "external command arguments" (map quoteIfNeeded externalargs)- dbgM "running shell command" shellcmd+ dbgIO "external command selected" cmd+ dbgIO "external command arguments" (map quoteIfNeeded externalargs)+ dbgIO "running shell command" shellcmd system shellcmd >>= exitWith -- deprecated commands
Hledger/Cli/Options.hs view
@@ -64,6 +64,7 @@ import qualified Control.Exception as C import Control.Monad (when) import Data.List.Compat+import Data.List.Split (splitOneOf) import Data.Maybe import Safe import System.Console.CmdArgs@@ -121,8 +122,9 @@ ,flagReq ["period","p"] (\s opts -> Right $ setopt "period" s opts) "PERIODEXP" "set start date, end date, and/or reporting interval all at once (overrides the flags above)" ,flagNone ["date2","aux-date"] (setboolopt "date2") "use postings/txns' secondary dates instead" - ,flagNone ["cleared","C"] (setboolopt "cleared") "include only pending/cleared postings/txns"- ,flagNone ["uncleared","U"] (setboolopt "uncleared") "include only uncleared postings/txns"+ ,flagNone ["cleared","C"] (setboolopt "cleared") "include only cleared postings/txns"+ ,flagNone ["pending"] (setboolopt "pending") "include only pending postings/txns"+ ,flagNone ["uncleared","U"] (setboolopt "uncleared") "include only uncleared (and pending) postings/txns" ,flagNone ["real","R"] (setboolopt "real") "include only non-virtual postings" ,flagReq ["depth"] (\s opts -> Right $ setopt "depth" s opts) "N" "hide accounts/postings deeper than N" ,flagNone ["empty","E"] (setboolopt "empty") "show empty/zero things which are normally omitted"@@ -248,7 +250,7 @@ data CliOpts = CliOpts { rawopts_ :: RawOpts ,command_ :: String- ,file_ :: Maybe FilePath+ ,file_ :: [FilePath] ,rules_file_ :: Maybe FilePath ,output_file_ :: Maybe FilePath ,output_format_ :: Maybe String@@ -309,7 +311,7 @@ return defcliopts { rawopts_ = rawopts ,command_ = stringopt "command" rawopts- ,file_ = maybestringopt "file" rawopts+ ,file_ = map stripquotes $ listofstringopt "file" rawopts ,rules_file_ = maybestringopt "rules-file" rawopts ,output_file_ = maybestringopt "output-file" rawopts ,output_format_ = maybestringopt "output-format" rawopts@@ -360,26 +362,22 @@ -- CliOpts accessors -- | Get the account name aliases from options, if any.-aliasesFromOpts :: CliOpts -> [(AccountName,AccountName)]-aliasesFromOpts = map parseAlias . alias_- where- -- similar to ledgerAlias- parseAlias :: String -> (AccountName,AccountName)- parseAlias s = (accountNameWithoutPostingType $ strip orig- ,accountNameWithoutPostingType $ strip alias')- where- (orig, alias) = break (=='=') s- alias' = case alias of ('=':rest) -> rest- _ -> orig+aliasesFromOpts :: CliOpts -> [AccountAlias]+aliasesFromOpts = map (\a -> fromparse $ runParser accountaliasp () ("--alias "++quoteIfNeeded a) a)+ . alias_ -- | Get the (tilde-expanded, absolute) journal file path from -- 1. options, 2. an environment variable, or 3. the default.-journalFilePathFromOpts :: CliOpts -> IO String+-- Actually, returns one or more file paths. There will be more+-- than one if multiple -f options were provided.+journalFilePathFromOpts :: CliOpts -> IO [String] journalFilePathFromOpts opts = do f <- defaultJournalPath d <- getCurrentDirectory- expandPath d $ fromMaybe f $ file_ opts-+ mapM (expandPath d) $ ifEmpty (file_ opts) [f]+ where+ ifEmpty [] d = d+ ifEmpty l _ = l -- | Get the expanded, absolute output file path from options, -- or the default (-, meaning stdout).@@ -521,7 +519,7 @@ -- directory) or whether it has execute permission. hledgerExecutablesInPath :: IO [String] hledgerExecutablesInPath = do- pathdirs <- regexSplit "[:;]" `fmap` getEnvSafe "PATH"+ pathdirs <- splitOneOf "[:;]" `fmap` getEnvSafe "PATH" pathfiles <- concat `fmap` mapM getDirectoryContentsSafe pathdirs return $ nub $ sort $ filter isHledgerExeName pathfiles -- XXX should exclude directories and files without execute permission.
Hledger/Cli/Print.hs view
@@ -100,7 +100,7 @@ description = tdescription t date = showDate (tdate t) date2 = maybe "" showDate (tdate2 t)- status = if tstatus t then "*" else ""+ status = show $ tstatus t code = tcode t comment = chomp $ strip $ tcomment t @@ -116,7 +116,7 @@ amounts where Mixed amounts = pamount p- status = if pstatus p then "*" else ""+ status = show $ pstatus p account = showAccountName Nothing (ptype p) (paccount p) comment = chomp $ strip $ pcomment p
Hledger/Cli/Register.hs view
@@ -149,9 +149,12 @@ _ -> (id,acctwidth) amt = showMixedAmountWithoutPrice $ pamount p bal = showMixedAmountWithoutPrice b+ -- alternate behaviour, show null amounts as 0 instead of blank+ -- amt = if null amt' then "0" else amt'+ -- bal = if null bal' then "0" else bal' (amtlines, ballines) = (lines amt, lines bal) (amtlen, ballen) = (length amtlines, length ballines)- numlines = max amtlen ballen+ numlines = max 1 (max amtlen ballen) (amtfirstline:amtrest) = take numlines $ amtlines ++ repeat "" -- posting amount is top-aligned (balfirstline:balrest) = take numlines $ replicate (numlines - ballen) "" ++ ballines -- balance amount is bottom-aligned spacer = replicate (totalwidth - (amtwidth + 2 + balwidth)) ' '
Hledger/Cli/Stats.hs view
@@ -13,6 +13,8 @@ import Data.List import Data.Maybe import Data.Ord+import Data.HashSet (size, fromList)+import Data.Text (pack) import Data.Time.Calendar import System.Console.CmdArgs.Explicit import Text.Printf@@ -21,13 +23,15 @@ import Hledger import Hledger.Cli.Options import Prelude hiding (putStr)-import Hledger.Utils.UTF8IOCompat (putStr)+import Hledger.Cli.Utils (writeOutput) statsmode = (defCommandMode $ ["stats"] ++ aliases) { modeHelp = "show some journal statistics" `withAliases` aliases ,modeGroupFlags = Group {- groupUnnamed = []+ groupUnnamed = [+ flagReq ["output-file","o"] (\s opts -> Right $ setopt "output-file" s opts) "FILE[.FMT]" "write output to FILE instead of stdout. A recognised FMT suffix influences the format."+ ] ,groupHidden = [] ,groupNamed = [generalflagsgroup1] }@@ -37,7 +41,7 @@ -- like Register.summarisePostings -- | Print various statistics for the journal. stats :: CliOpts -> Journal -> IO ()-stats CliOpts{reportopts_=reportopts_} j = do+stats opts@CliOpts{reportopts_=reportopts_} j = do d <- getCurrentDay let q = queryFromOpts d reportopts_ l = ledgerFromJournal q j@@ -45,7 +49,7 @@ intervalspans = splitSpan (intervalFromOpts reportopts_) reportspan showstats = showLedgerStats l d s = intercalate "\n" $ map showstats intervalspans- putStr s+ writeOutput opts s showLedgerStats :: Ledger -> Day -> DateSpan -> String showLedgerStats l today span =@@ -63,7 +67,7 @@ ,("Transactions", printf "%d (%0.1f per day)" tnum txnrate) ,("Transactions last 30 days", printf "%d (%0.1f per day)" tnum30 txnrate30) ,("Transactions last 7 days", printf "%d (%0.1f per day)" tnum7 txnrate7)- ,("Payees/descriptions", show $ length $ nub $ map tdescription ts)+ ,("Payees/descriptions", show $ size $ fromList $ map (pack . tdescription) ts) ,("Accounts", printf "%d (depth %d)" acctnum acctdepth) ,("Commodities", printf "%s (%s)" (show $ length cs) (intercalate ", " cs)) -- Transactions this month : %(monthtxns)s (last month in the same period: %(lastmonthtxns)s)
Hledger/Cli/Utils.hs view
@@ -68,7 +68,7 @@ -- to let the add command work. rulespath <- rulesFilePathFromOpts opts journalpath <- journalFilePathFromOpts opts- ej <- readJournalFile Nothing rulespath (not $ ignore_assertions_ opts) journalpath+ ej <- readJournalFiles Nothing rulespath (not $ ignore_assertions_ opts) journalpath either error' (cmd opts . journalApplyAliases (aliasesFromOpts opts)) ej -- | Write some output to stdout or to a file selected by --output-file.
+ bench/bench.hs view
@@ -0,0 +1,70 @@+-- bench+-- By default, show approximate times for some standard hledger operations on a sample journal.+-- With --criterion, show accurate times (slow).+-- With --simplebench, show approximate times for the commands in default.bench, using the first hledger executable on $PATH.++import Criterion.Main (defaultMainWith, defaultConfig, bench, nfIO)+import SimpleBench (defaultMain)+import System.Directory (getCurrentDirectory)+import System.Environment (getArgs, withArgs)+import System.Info (os)+import System.Process (readProcess)+import System.TimeIt (timeItT)+import Text.Printf+import Hledger.Cli++-- sample journal file to use for benchmarks+inputfile = "bench/10000x1000x10.journal"++outputfile = "/dev/null" -- hide output of benchmarked commands (XXX unixism)+-- outputfile = "-" -- show output of benchmarked commands++main = do+ -- withArgs ["--simplebench"] $ do+ -- withArgs ["--criterion"] $ do+ args <- getArgs+ if "--criterion" `elem` args+ then withArgs [] benchWithCriterion+ else if "--simplebench" `elem` args+ then benchWithSimplebench+ else benchWithTimeit++benchWithTimeit = do+ getCurrentDirectory >>= printf "Benchmarking hledger in %s with timeit\n"+ let opts = defcliopts{output_file_=Just outputfile}+ (t0,j) <- timeit ("read "++inputfile) $ either error id <$> readJournalFile Nothing Nothing True inputfile+ (t1,_) <- timeit ("print") $ print' opts j+ (t2,_) <- timeit ("register") $ register opts j+ (t3,_) <- timeit ("balance") $ balance opts j+ (t4,_) <- timeit ("stats") $ stats opts j+ printf "Total: %0.2fs\n" (sum [t0,t1,t2,t3,t4])++timeit :: String -> IO a -> IO (Double, a)+timeit name action = do+ printf "%s%s" name (replicate (40 - length name) ' ')+ (t,a) <- timeItT action+ printf "[%.2fs]\n" t+ return (t,a)++benchWithCriterion = do+ getCurrentDirectory >>= printf "Benchmarking hledger in %s with criterion\n"+ let opts = defcliopts{output_file_=Just "/dev/null"}+ j <- either error id <$> readJournalFile Nothing Nothing True inputfile+ Criterion.Main.defaultMainWith defaultConfig $ [+ bench ("read "++inputfile) $ nfIO $ (either error const <$> readJournalFile Nothing Nothing True inputfile),+ bench ("print") $ nfIO $ print' opts j,+ bench ("register") $ nfIO $ register opts j,+ bench ("balance") $ nfIO $ balance opts j,+ bench ("stats") $ nfIO $ stats opts j+ ]++benchWithSimplebench = do+ let whichcmd = if os == "mingw32" then "where" else "which"+ exe <- init <$> readProcess whichcmd ["hledger"] ""+ pwd <- getCurrentDirectory+ printf "Benchmarking %s in %s with simplebench and shell\n" exe pwd+ flip withArgs SimpleBench.defaultMain [+ "-fbench/default.bench"+ ,"-v"+ ,"hledger"+ ]
hledger.cabal view
@@ -1,5 +1,5 @@ name: hledger-version: 0.25.1+version: 0.26 stability: stable category: Finance, Console synopsis: The main command-line interface for the hledger accounting tool.@@ -17,22 +17,16 @@ maintainer: Simon Michael <simon@joyful.com> homepage: http://hledger.org bug-reports: http://hledger.org/bugs-tested-with: GHC==7.8.2, GHC==7.10.1+tested-with: GHC==7.8.4, GHC==7.10.1 cabal-version: >= 1.10 build-type: Simple -- data-dir: data -- data-files: extra-tmp-files: extra-source-files: - tests/suite.hs+ test/test.hs CHANGES --- Cabal-Version: >= 1.9.2--- Test-Suite test-hledger--- type: exitcode-stdio-1.0--- main-is: test-hledger.hs--- build-depends: base- source-repository head type: git location: https://github.com/simonmichael/hledger@@ -53,7 +47,7 @@ library- cpp-options: -DVERSION="0.25.1"+ cpp-options: -DVERSION="0.26" ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures ghc-options: -fno-warn-type-defaults -fno-warn-orphans default-language: Haskell2010@@ -75,11 +69,12 @@ Hledger.Cli.Register Hledger.Cli.Stats build-depends:- hledger-lib == 0.25.1+ hledger-lib == 0.26 ,base >= 4.3 && < 5 ,base-compat >= 0.8.1 -- ,cabal-file-th ,containers+ ,unordered-containers ,cmdargs >= 0.10 && < 0.11 ,csv -- ,data-pprint >= 0.2.1 && < 0.3@@ -93,7 +88,6 @@ ,parsec >= 3 ,process ,regex-tdfa- ,regexpr >= 0.5.1 ,safe >= 0.2 ,split >= 0.1 && < 0.3 ,text >= 0.11@@ -104,7 +98,7 @@ -- ghc 7.10 requires shakespeare 2.0.2.2+ build-depends: shakespeare >= 2.0.2.2 && < 2.1 else- -- for older ghcs, allow shakespeare 2.x or 1.x (which also requires shakespeare-text)+ -- for older ghcs, allow shakespeare 1.x (which also requires shakespeare-text) -- http://www.yesodweb.com/blog/2014/04/consolidation-progress build-depends: shakespeare >= 1.0 && < 2.1@@ -123,18 +117,19 @@ main-is: hledger-cli.hs hs-source-dirs: app default-language: Haskell2010- cpp-options: -DVERSION="0.25.1"+ cpp-options: -DVERSION="0.26" ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures ghc-options: -fno-warn-type-defaults -fno-warn-orphans if flag(threaded) ghc-options: -threaded -- same as above: build-depends:- hledger-lib == 0.25.1- ,hledger == 0.25.1+ hledger-lib == 0.26+ ,hledger == 0.26 ,base >= 4.3 && < 5 ,base-compat >= 0.8.1 ,containers+ ,unordered-containers ,cmdargs >= 0.10 && < 0.11 ,csv -- ,data-pprint >= 0.2.1 && < 0.3@@ -148,15 +143,19 @@ ,parsec >= 3 ,process ,regex-tdfa- ,regexpr >= 0.5.1 ,safe >= 0.2- ,shakespeare-text >= 1.0 && < 1.2- ,shakespeare >= 1.0 && < 2.1 ,split >= 0.1 && < 0.3 ,tabular >= 0.2 && < 0.3 ,text >= 0.11 ,utf8-string >= 0.3.5 && < 1.1 ,wizards == 1.0.*+ -- as above+ if impl(ghc >= 7.10)+ build-depends: shakespeare >= 2.0.2.2 && < 2.1+ else+ build-depends:+ shakespeare >= 1.0 && < 2.1+ ,shakespeare-text >= 1.0 && < 1.2 if flag(old-locale) build-depends: time < 1.5, old-locale else@@ -165,10 +164,10 @@ build-depends: pretty-show >= 1.6.4 -test-suite tests+test-suite test type: exitcode-stdio-1.0- main-is: suite.hs- hs-source-dirs: tests+ main-is: test.hs+ hs-source-dirs: test default-language: Haskell2010 ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures ghc-options: -fno-warn-type-defaults -fno-warn-orphans@@ -191,10 +190,7 @@ , parsec >= 3 , process , regex-tdfa- , regexpr , safe- , shakespeare-text >= 1.0 && < 1.2- , shakespeare >= 1.0 && < 2.1 , split ,tabular >= 0.2 && < 0.3 , test-framework@@ -202,6 +198,13 @@ , text , transformers , wizards == 1.0.*+ -- as above+ if impl(ghc >= 7.10)+ build-depends: shakespeare >= 2.0.2.2 && < 2.1+ else+ build-depends:+ shakespeare >= 1.0 && < 2.1+ ,shakespeare-text >= 1.0 && < 1.2 if impl(ghc >= 7.4) build-depends: pretty-show >= 1.6.4 if flag(old-locale)@@ -210,11 +213,10 @@ build-depends: time >= 1.5 --- not a standard cabal bench test, I think benchmark bench type: exitcode-stdio-1.0- -- hs-source-dirs: - main-is: ../tools/simplebench.hs+ hs-source-dirs: bench+ main-is: bench.hs ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures ghc-options: -fno-warn-type-defaults -fno-warn-orphans default-language: Haskell2010@@ -222,8 +224,10 @@ hledger, base >= 4.3 && < 5, base-compat >= 0.8.1,+ criterion, html, tabular >= 0.2 && < 0.3,+ timeit, process, filepath, directory
+ test/test.hs view
@@ -0,0 +1,6 @@+import Hledger.Cli (tests_Hledger_Cli)+import Test.Framework.Providers.HUnit (hUnitTestToTests)+import Test.Framework.Runners.Console (defaultMain)++main :: IO ()+main = defaultMain $ hUnitTestToTests tests_Hledger_Cli
− tests/suite.hs
@@ -1,6 +0,0 @@-import Hledger.Cli (tests_Hledger_Cli)-import Test.Framework.Providers.HUnit (hUnitTestToTests)-import Test.Framework.Runners.Console (defaultMain)--main :: IO ()-main = defaultMain $ hUnitTestToTests tests_Hledger_Cli