--alias command-line option

This commit is contained in:
Simon Michael 2011-08-05 00:05:39 +00:00
parent 30b7448f45
commit 957c349780
7 changed files with 101 additions and 10 deletions

View File

@ -439,7 +439,8 @@ Included files are also affected, eg:
### Account aliases ### Account aliases
You can define account aliases to rewrite certain account names (and their subaccounts). You can define account aliases to rewrite certain account names (and their subaccounts).
The format is `alias ORIGACCT = ALIAS`. Use `end aliases` to forget all previously defined aliases. The format is `alias ORIG = ALIAS`, where ORIG and ALIAS are full account names.
To forget all aliases defined to this point, use `end aliases`.
Here's an example: say a sole proprietor has a personal.journal: Here's an example: say a sole proprietor has a personal.journal:
@ -475,6 +476,21 @@ giving:
expenses:office supplies $1 expenses:office supplies $1
assets:business checking $-1 assets:business checking $-1
You can also specify aliases on the command line. This could be useful to
rewrite account names when sharing a report with someone else, such as
your accountant:
$ hledger --alias 'my earning=income:business'
Command-line alias options are applied after any alias directives in the
journal. At most one alias directive and one alias option will be applied
to each account name.
Aliases tend to be a little more reliable than post-processing with sed or
similar, as they know about account name syntax, posting type indicators
etc. Note aliases only change the displayed names, not the account
hierarchy - aliasing two accounts to the same name does not merge them
into one account.
## Core commands ## Core commands

View File

@ -242,6 +242,13 @@ journalSelectingDate ActualDate j = j
journalSelectingDate EffectiveDate j = journalSelectingDate EffectiveDate j =
j{jtxns=map (journalTransactionWithDate EffectiveDate) $ jtxns j} j{jtxns=map (journalTransactionWithDate EffectiveDate) $ jtxns j}
-- | Apply additional account aliases (eg from the command-line) to all postings in a journal.
journalApplyAliases :: [(AccountName,AccountName)] -> Journal -> Journal
journalApplyAliases aliases j@Journal{jtxns=ts} = j{jtxns=map fixtransaction ts}
where
fixtransaction t@Transaction{tpostings=ps} = t{tpostings=map fixposting ps}
fixposting p@Posting{paccount=a} = p{paccount=accountNameApplyAliases aliases a}
-- | Do post-parse processing on a journal, to make it ready for use. -- | Do post-parse processing on a journal, to make it ready for use.
journalFinalise :: ClockTime -> LocalTime -> FilePath -> String -> JournalContext -> Journal -> Either String Journal journalFinalise :: ClockTime -> LocalTime -> FilePath -> String -> JournalContext -> Journal -> Either String Journal
journalFinalise tclock tlocal path txt ctx j@Journal{files=fs} = journalFinalise tclock tlocal path txt ctx j@Journal{files=fs} =

View File

@ -99,7 +99,7 @@ postingsDateSpan [] = DateSpan Nothing Nothing
postingsDateSpan ps = DateSpan (Just $ postingDate $ head ps') (Just $ addDays 1 $ postingDate $ last ps') postingsDateSpan ps = DateSpan (Just $ postingDate $ head ps') (Just $ addDays 1 $ postingDate $ last ps')
where ps' = sortBy (comparing postingDate) ps where ps' = sortBy (comparing postingDate) ps
-- balanced/non-balanced posting indicators -- AccountName stuff that depends on PostingType
accountNamePostingType :: AccountName -> PostingType accountNamePostingType :: AccountName -> PostingType
accountNamePostingType a accountNamePostingType a
@ -131,6 +131,15 @@ concatAccountNames :: [AccountName] -> AccountName
concatAccountNames as = accountNameWithPostingType t $ intercalate ":" $ map accountNameWithoutPostingType as concatAccountNames as = accountNameWithPostingType t $ intercalate ":" $ map accountNameWithoutPostingType as
where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as where t = headDef RegularPosting $ filter (/= RegularPosting) $ map accountNamePostingType as
-- | Rewrite an account name using the first applicable alias from the given list, if any.
accountNameApplyAliases :: [(AccountName,AccountName)] -> AccountName -> AccountName
accountNameApplyAliases aliases a = withorigtype
where
(a',t) = (accountNameWithoutPostingType a, accountNamePostingType a)
firstmatchingalias = headDef Nothing $ map Just $ filter (\(orig,_) -> orig == a' || orig `isAccountNamePrefixOf` a') aliases
rewritten = maybe a' (\(orig,alias) -> alias++drop (length orig) a') firstmatchingalias
withorigtype = accountNameWithPostingType t rewritten
tests_Hledger_Data_Posting = TestList [ tests_Hledger_Data_Posting = TestList [
"accountNamePostingType" ~: do "accountNamePostingType" ~: do

View File

@ -482,12 +482,7 @@ modifiedaccountname = do
prefix <- getParentAccount prefix <- getParentAccount
let prefixed = prefix `joinAccountNames` a let prefixed = prefix `joinAccountNames` a
aliases <- getAccountAliases aliases <- getAccountAliases
let t = accountNamePostingType prefixed return $ accountNameApplyAliases aliases prefixed
a' = accountNameWithoutPostingType prefixed
match = headDef Nothing $ map Just $ filter (\(orig,_) -> orig == a' || orig `isAccountNamePrefixOf` a') aliases
rewritten = maybe a' (\(orig,alias) -> alias++drop (length orig) a') match
withtype = accountNameWithPostingType t rewritten
return withtype
-- | Parse an account name. Account names may have single spaces inside -- | Parse an account name. Account names may have single spaces inside
-- them, and are terminated by two or more spaces. They should have one or -- them, and are terminated by two or more spaces. They should have one or

View File

@ -62,6 +62,7 @@ options_cli :: [OptDescr Opt]
options_cli = [ options_cli = [
Option "f" ["file"] (ReqArg File "FILE") "use a different journal/timelog file; - means stdin" Option "f" ["file"] (ReqArg File "FILE") "use a different journal/timelog file; - means stdin"
,Option "" ["no-new-accounts"] (NoArg NoNewAccts) "don't allow to create new accounts" ,Option "" ["no-new-accounts"] (NoArg NoNewAccts) "don't allow to create new accounts"
,Option "" ["alias"] (ReqArg Alias "ACCT=ALIAS") "display ACCT's name as ALIAS instead"
,Option "b" ["begin"] (ReqArg Begin "DATE") "report on transactions on or after this date" ,Option "b" ["begin"] (ReqArg Begin "DATE") "report on transactions on or after this date"
,Option "e" ["end"] (ReqArg End "DATE") "report on transactions before this date" ,Option "e" ["end"] (ReqArg End "DATE") "report on transactions before this date"
,Option "p" ["period"] (ReqArg Period "EXPR") ("report on transactions during the specified period\n" ++ ,Option "p" ["period"] (ReqArg Period "EXPR") ("report on transactions during the specified period\n" ++
@ -96,6 +97,7 @@ options_cli = [
data Opt = data Opt =
File {value::String} File {value::String}
| NoNewAccts | NoNewAccts
| Alias {value::String}
| Begin {value::String} | Begin {value::String}
| End {value::String} | End {value::String}
| Period {value::String} | Period {value::String}
@ -306,6 +308,18 @@ journalFilePathFromOpts opts = do
f <- if istimequery then myTimelogPath else myJournalPath f <- if istimequery then myTimelogPath else myJournalPath
return $ last $ f : optValuesForConstructor File opts return $ last $ f : optValuesForConstructor File opts
aliasesFromOpts :: [Opt] -> [(AccountName,AccountName)]
aliasesFromOpts opts = map parseAlias $ optValuesForConstructor Alias opts
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
-- | Gather filter pattern arguments into a list of account patterns and a -- | Gather filter pattern arguments into a list of account patterns and a
-- list of description patterns. We interpret pattern arguments as -- list of description patterns. We interpret pattern arguments as
-- follows: those prefixed with "desc:" are description patterns, all -- follows: those prefixed with "desc:" are description patterns, all

View File

@ -38,7 +38,7 @@ import System.Time (ClockTime, getClockTime, diffClockTimes, TimeDiff(TimeDiff))
import Test.HUnit import Test.HUnit
import Text.Printf import Text.Printf
import Hledger.Cli.Options (Opt(..),journalFilePathFromOpts,whichDateFromOpts) import Hledger.Cli.Options
import Hledger.Data import Hledger.Data
import Hledger.Read import Hledger.Read
import Hledger.Utils import Hledger.Utils
@ -51,7 +51,8 @@ withJournalDo opts args _ cmd = do
-- We kludgily read the file before parsing to grab the full text, unless -- We kludgily read the file before parsing to grab the full text, unless
-- it's stdin, or it doesn't exist and we are adding. We read it strictly -- it's stdin, or it doesn't exist and we are adding. We read it strictly
-- to let the add command work. -- to let the add command work.
journalFilePathFromOpts opts >>= readJournalFile Nothing >>= either error' (cmd opts args) journalFilePathFromOpts opts >>= readJournalFile Nothing >>=
either error' (cmd opts args . journalApplyAliases (aliasesFromOpts opts))
-- -- | Get a journal from the given string and options, or throw an error. -- -- | Get a journal from the given string and options, or throw an error.
-- readJournalWithOpts :: [Opt] -> String -> IO Journal -- readJournalWithOpts :: [Opt] -> String -> IO Journal

49
tests/aliases.test Normal file
View File

@ -0,0 +1,49 @@
# alias-related tests
# 1. command-line --alias option. Note multiple applicable aliases, but
# only one is applied per account name. Spaces are allowed if quoted.
bin/hledger -f- print --alias 'a a=A' --alias b=B
<<<
2011/01/01
a a 1
c
>>>
2011/01/01
A 1
c -1
>>>=0
# 2. alias directive, and an account with unbalanced posting indicators.
bin/hledger -f- print
<<<
alias b=B
2011/01/01
(b) 1
>>>
2011/01/01
(B) 1
>>>=0
# 3. --alias options run after alias directives. Subaccounts are also
# matched and rewritten. Accounts with an internal part matching the alias
# are ignored.
bin/hledger -f- print --alias a=A --alias B=C
<<<
alias a=B
2011/01/01
[a:x] 1
[x:a:x]
>>>
2011/01/01
[C:x] 1
[x:a:x] -1
>>>2
>>>=0