more convenient timelog querying when invoked as "hours"

This commit is contained in:
Simon Michael 2009-01-23 02:04:31 +00:00
parent f733a3de80
commit a26a56eafe
2 changed files with 72 additions and 20 deletions

View File

@ -3,7 +3,9 @@ where
import System import System
import System.Console.GetOpt import System.Console.GetOpt
import System.Directory import System.Directory
import System.Environment
import Text.Printf import Text.Printf
import Data.Char (toLower)
import Ledger.Parse import Ledger.Parse
import Ledger.Utils import Ledger.Utils
import Ledger.Types import Ledger.Types
@ -12,9 +14,13 @@ import Ledger.Dates
versionno = "0.3" versionno = "0.3"
version = printf "hledger version %s \n" versionno :: String version = printf "hledger version %s \n" versionno :: String
defaultfile = "~/.ledger" ledgerdefault = "~/.ledger"
fileenvvar = "LEDGER" ledgerenvvar = "LEDGER"
usagehdr = "Usage: hledger [OPTS] COMMAND [ACCTPATTERNS] [-- DESCPATTERNS]\n" ++ timelogdefault = "~/.timelog"
timelogenvvar = "TIMELOG"
timeprogname = "hours"
usagehdr = "Usage: hledger [OPTION] COMMAND [ACCTPATTERNS] [-- DESCPATTERNS]\n" ++
"or: hours [OPTIONS] [PERIOD [COMMAND]]\n" ++
"\n" ++ "\n" ++
"Options (before command, unless using --options-anywhere):" "Options (before command, unless using --options-anywhere):"
usageftr = "\n" ++ usageftr = "\n" ++
@ -27,7 +33,7 @@ usageftr = "\n" ++
"Account and description patterns are regular expressions which filter by\n" ++ "Account and description patterns are regular expressions which filter by\n" ++
"account name and entry description. Prefix a pattern with - to negate it,\n" ++ "account name and entry description. Prefix a pattern with - to negate it,\n" ++
"and separate account and description patterns with --.\n" ++ "and separate account and description patterns with --.\n" ++
"(With --options-anywhere, use ^ and ^^.)\n" ++ "(With --options-anywhere, use ^ and ^^. \"hours\" implies --options-anywhere.)\n" ++
"\n" ++ "\n" ++
"Also: hledger [-v] test [TESTPATTERNS] to run self-tests.\n" ++ "Also: hledger [-v] test [TESTPATTERNS] to run self-tests.\n" ++
"\n" "\n"
@ -61,7 +67,7 @@ options = [
] ]
where where
filehelp = printf "ledger file; - means use standard input. Defaults\nto the %s environment variable or %s" filehelp = printf "ledger file; - means use standard input. Defaults\nto the %s environment variable or %s"
fileenvvar defaultfile ledgerenvvar ledgerdefault
-- | An option value from a command-line flag. -- | An option value from a command-line flag.
data Opt = data Opt =
@ -97,18 +103,33 @@ optValuesForConstructor f opts = concatMap get opts
optValuesForConstructors fs opts = concatMap get opts optValuesForConstructors fs opts = concatMap get opts
where get o = if any (\f -> f v == o) fs then [v] else [] where v = value o where get o = if any (\f -> f v == o) fs then [v] else [] where v = value o
-- | Parse the command-line arguments into ledger options, ledger command -- | Parse the command-line arguments into options, command name, and
-- name, and ledger command arguments. Also any dates in the options are -- command arguments. Any dates in the options are converted to full
-- converted to full YYYY/MM/DD format, while we are in the IO monad -- YYYY/MM/DD format, while we are in the IO monad and can get the current
-- and can get the current time. -- time. Arguments are parsed differently if the program was invoked as
-- "hours".
parseArguments :: IO ([Opt], String, [String]) parseArguments :: IO ([Opt], String, [String])
parseArguments = do parseArguments = do
args <- getArgs args <- getArgs
let order = if "--options-anywhere" `elem` args then Permute else RequireOrder istimequery <- usingTimeProgramName
case (getOpt order options args) of let order = if "--options-anywhere" `elem` args || istimequery
(opts,cmd:args,[]) -> do {opts' <- fixOptDates opts; return (opts',cmd,args)} then Permute
(opts,[],[]) -> do {opts' <- fixOptDates opts; return (opts',[],[])} else RequireOrder
let (os,as,es) = getOpt order options args
os' <- fixOptDates os
case istimequery of
False ->
case (os,as,es) of
(opts,cmd:args,[]) -> return (os',cmd,args)
(opts,[],[]) -> return (os',"",[])
(opts,_,errs) -> ioError (userError (concat errs ++ usage)) (opts,_,errs) -> ioError (userError (concat errs ++ usage))
True ->
case (os,as,es) of
(opts,p:cmd:args,[]) -> return (os' ++ [Period p],cmd,args)
(opts,p:args,[]) -> return ([Period p,SubTotal] ++ os',"balance",args)
(opts,[],[]) -> return ([Period "today",SubTotal] ++ os',"balance",[])
(opts,_,errs) -> ioError (userError (concat errs ++ usage))
-- | Convert any fuzzy dates within these option values to explicit ones, -- | Convert any fuzzy dates within these option values to explicit ones,
-- based on today's date. -- based on today's date.
@ -170,10 +191,20 @@ displayFromOpts opts = listtomaybe $ optValuesForConstructor Display opts
listtomaybe [] = Nothing listtomaybe [] = Nothing
listtomaybe vs = Just $ last vs listtomaybe vs = Just $ last vs
-- | Was the program invoked via the \"hours\" alias ?
usingTimeProgramName :: IO Bool
usingTimeProgramName = do
progname <- getProgName
return $ map toLower progname == timeprogname
-- | Get the ledger file path from options, an environment variable, or a default -- | Get the ledger file path from options, an environment variable, or a default
ledgerFilePathFromOpts :: [Opt] -> IO String ledgerFilePathFromOpts :: [Opt] -> IO String
ledgerFilePathFromOpts opts = do ledgerFilePathFromOpts opts = do
envordefault <- getEnv fileenvvar `catch` \_ -> return defaultfile istimequery <- usingTimeProgramName
let (e,d) = if istimequery
then (timelogenvvar,timelogdefault)
else (ledgerenvvar,ledgerdefault)
envordefault <- getEnv e `catch` \_ -> return d
paths <- mapM tildeExpand $ [envordefault] ++ optValuesForConstructor File opts paths <- mapM tildeExpand $ [envordefault] ++ optValuesForConstructor File opts
return $ last paths return $ last paths

31
README
View File

@ -37,9 +37,12 @@ To get the latest development code do::
darcs get http://joyful.com/repos/hledger darcs get http://joyful.com/repos/hledger
Examples Usage
-------- -----
Here are some commands to try:: hledger looks for your ledger file at ~/.ledger by default. To use a
different file, specify it with the LEDGER environment variable or -f
option (which may be - for standard input). Here are some commands to
try::
hledger --help hledger --help
hledger -f sample.ledger balance hledger -f sample.ledger balance
@ -50,9 +53,27 @@ Here are some commands to try::
hledger reg -- shop hledger reg -- shop
hledger ui hledger ui
hledger looks for your ledger file at ~/.ledger by default. To use a different file,
specify it with -f or the LEDGER environment variable.
Time reporting
--------------
hledger can parse a timelog file in timeclock.el's format, treating time
categories as accounts. If hledger is invoked by the "hours" alias it
looks for your timelog, and parses arguments slightly differently for
convenient querying:
hours [OPTIONS] [PERIOD [COMMAND]]
PERIOD and COMMAND default to "today" and "balance --subtotal"
respectively, and --options-anywhere is assumed. The timelog is found in
the same way as your ledger: ~/.timelog or the file specified by $TIMELOG
or an -f option. Examples:
hours # today's balances
hours today # the same
hours 'this week' # so far this week
hours week # the same
hours lastmonth # last month, the space is optional
hours 'monthly in 2008' reg --depth 1 # monthly register, top-level only
Features Features
-------- --------