add: simplify code
This commit is contained in:
parent
49e66477fb
commit
2373429b64
124
Commands/Add.hs
124
Commands/Add.hs
@ -1,6 +1,6 @@
|
|||||||
{-|
|
{-|
|
||||||
|
|
||||||
An add command to help with data entry.
|
A history-aware add command to help with data entry.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
@ -26,125 +26,101 @@ add opts args l
|
|||||||
| otherwise = do
|
| otherwise = do
|
||||||
hPutStrLn stderr
|
hPutStrLn stderr
|
||||||
"Enter one or more transactions, which will be added to your ledger file.\n\
|
"Enter one or more transactions, which will be added to your ledger file.\n\
|
||||||
\To complete a transaction, enter . as account name.\n\
|
\To complete a transaction, enter . as account name. To quit, enter control-d."
|
||||||
\To finish input, enter control-d (discards any transaction in progress)."
|
getAndAddTransactions l args `catch` (\e -> if isEOFError e then return () else ioError e)
|
||||||
getAndAddTransactions l args
|
|
||||||
return ()
|
|
||||||
|
|
||||||
-- | Read a number of ledger transactions from the command line,
|
-- | Read a number of ledger transactions from the command line,
|
||||||
-- prompting, validating, displaying and appending them to the ledger
|
-- prompting, validating, displaying and appending them to the ledger
|
||||||
-- file, until end of input. Any command-line arguments are used as the
|
-- file, until end of input (then raise an EOF exception). Any
|
||||||
-- first transaction's description.
|
-- command-line arguments are used as the first transaction's description.
|
||||||
getAndAddTransactions :: Ledger -> [String] -> IO [LedgerTransaction]
|
getAndAddTransactions :: Ledger -> [String] -> IO ()
|
||||||
getAndAddTransactions l args = do
|
getAndAddTransactions l args = do
|
||||||
-- for now, thread the eoi flag throughout rather than muck about with monads
|
l <- getTransaction l args >>= addTransaction l
|
||||||
(t, eoi) <- getTransaction l args
|
getAndAddTransactions l []
|
||||||
l <- if isJust t then addTransaction l (fromJust t) else return l
|
|
||||||
if eoi then return $ maybe [] (:[]) t
|
|
||||||
else liftM (fromJust t:) (getAndAddTransactions l [])
|
|
||||||
|
|
||||||
-- | Get a transaction from the command line, if possible, and a flag
|
-- | Read a transaction from the command line, with history-aware prompting.
|
||||||
-- indicating end of input.
|
getTransaction :: Ledger -> [String] -> IO LedgerTransaction
|
||||||
getTransaction :: Ledger -> [String] -> IO (Maybe LedgerTransaction, Bool)
|
|
||||||
getTransaction l args = do
|
getTransaction l args = do
|
||||||
today <- getCurrentDay
|
today <- getCurrentDay
|
||||||
(datestr, eoi) <- askFor "date"
|
datestr <- askFor "date"
|
||||||
(Just $ showDate today)
|
(Just $ showDate today)
|
||||||
(Just $ \s -> null s ||
|
(Just $ \s -> null s ||
|
||||||
(isRight $ parse (smartdate >> many spacenonewline >> eof) "" $ lowercase s))
|
(isRight $ parse (smartdate >> many spacenonewline >> eof) "" $ lowercase s))
|
||||||
if eoi
|
description <- if null args
|
||||||
then return (Nothing, True)
|
|
||||||
else do
|
|
||||||
(description, eoi) <- if null args
|
|
||||||
then askFor "description" Nothing (Just $ not . null)
|
then askFor "description" Nothing (Just $ not . null)
|
||||||
else (do
|
else do
|
||||||
let description = unwords args
|
let description = unwords args
|
||||||
hPutStrLn stderr $ "description: " ++ description
|
hPutStrLn stderr $ "description: " ++ description
|
||||||
return (description, False))
|
return description
|
||||||
if eoi
|
|
||||||
then return (Nothing, True)
|
|
||||||
else do
|
|
||||||
let historymatches = transactionsSimilarTo l description
|
let historymatches = transactionsSimilarTo l description
|
||||||
when (not $ null historymatches) (do
|
bestmatch | null historymatches = Nothing
|
||||||
hPutStrLn stderr "Similar past transactions found:"
|
|
||||||
hPutStr stderr $ concatMap (\(n,t) -> printf "[%3d%%] %s" (round $ n*100 :: Int) (show t)) $ take 3 historymatches)
|
|
||||||
let bestmatch | null historymatches = Nothing
|
|
||||||
| otherwise = Just $ snd $ head $ historymatches
|
| otherwise = Just $ snd $ head $ historymatches
|
||||||
bestmatchpostings = maybe Nothing (Just . ltpostings) bestmatch
|
bestmatchpostings = maybe Nothing (Just . ltpostings) bestmatch
|
||||||
date = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) datestr
|
date = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) datestr
|
||||||
getpostingsandvalidate = do
|
getpostingsandvalidate = do
|
||||||
(ps, eoi) <- getPostings bestmatchpostings []
|
ps <- getPostings bestmatchpostings []
|
||||||
let t = nullledgertxn{ltdate=date
|
let t = nullledgertxn{ltdate=date
|
||||||
,ltstatus=False
|
,ltstatus=False
|
||||||
,ltdescription=description
|
,ltdescription=description
|
||||||
,ltpostings=ps
|
,ltpostings=ps
|
||||||
}
|
}
|
||||||
if eoi && null ps
|
|
||||||
then return (Nothing, eoi)
|
|
||||||
else either (const retry) (return . flip (,) eoi . Just) $ balanceLedgerTransaction t
|
|
||||||
retry = do
|
retry = do
|
||||||
hPutStrLn stderr $ "\n" ++ nonzerobalanceerror ++ ". Re-enter:"
|
hPutStrLn stderr $ "\n" ++ nonzerobalanceerror ++ ". Re-enter:"
|
||||||
getpostingsandvalidate
|
getpostingsandvalidate
|
||||||
|
either (const retry) return $ balanceLedgerTransaction t
|
||||||
|
when (not $ null historymatches)
|
||||||
|
(do
|
||||||
|
hPutStrLn stderr "Similar transactions found, using the first for defaults:\n"
|
||||||
|
hPutStr stderr $ concatMap (\(n,t) -> printf "[%3d%%] %s" (round $ n*100 :: Int) (show t)) $ take 3 historymatches)
|
||||||
getpostingsandvalidate
|
getpostingsandvalidate
|
||||||
|
|
||||||
-- | Get two or more postings from the command line, if possible, and a
|
-- | Read postings from the command line until . is entered, using the
|
||||||
-- flag indicating end of input.
|
-- provided historical postings, if any, to guess defaults.
|
||||||
getPostings :: Maybe [Posting] -> [Posting] -> IO ([Posting], Bool)
|
getPostings :: Maybe [Posting] -> [Posting] -> IO [Posting]
|
||||||
getPostings bestmatchps enteredps = do
|
getPostings historicalps enteredps = do
|
||||||
(account, eoi) <- askFor (printf "account %d" n) defaultaccount validateaccount
|
account <- askFor (printf "account %d" n) defaultaccount (Just $ not . null)
|
||||||
if account=="." || eoi
|
if account=="."
|
||||||
then return (enteredps, eoi)
|
then return enteredps
|
||||||
else do
|
else do
|
||||||
(amountstr, eoi) <- askFor (printf "amount %d" n) defaultamount validateamount
|
amountstr <- askFor (printf "amount %d" n) defaultamount validateamount
|
||||||
let amount = fromparse $ parse (someamount <|> return missingamt) "" amountstr
|
let amount = fromparse $ parse (someamount <|> return missingamt) "" amountstr
|
||||||
let p = nullrawposting{paccount=stripbrackets account,pamount=amount,ptype=postingaccounttype account}
|
let p = nullrawposting{paccount=stripbrackets account,
|
||||||
if eoi
|
pamount=amount,
|
||||||
then if null enteredps
|
ptype=postingtype account}
|
||||||
then return ([], True)
|
getPostings historicalps $ enteredps ++ [p]
|
||||||
else return (enteredps ++ [p], True)
|
|
||||||
else if amount == missingamt
|
|
||||||
then return $ (enteredps ++ [p], eoi)
|
|
||||||
else getPostings bestmatchps $ enteredps ++ [p]
|
|
||||||
where
|
where
|
||||||
n = length enteredps + 1
|
n = length enteredps + 1
|
||||||
realn = length enteredrealps + 1
|
realn = length enteredrealps + 1
|
||||||
bestmatch | isNothing bestmatchps = Nothing
|
enteredrealps = filter isReal enteredps
|
||||||
|
bestmatch | isNothing historicalps = Nothing
|
||||||
| n <= length ps = Just $ ps !! (n-1)
|
| n <= length ps = Just $ ps !! (n-1)
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
where Just ps = bestmatchps
|
where Just ps = historicalps
|
||||||
defaultaccount = maybe Nothing (Just . showacctname) bestmatch
|
defaultaccount = maybe Nothing (Just . showacctname) bestmatch
|
||||||
showacctname p = showAccountName Nothing (ptype p) $ paccount p
|
showacctname p = showAccountName Nothing (ptype p) $ paccount p
|
||||||
validateaccount = Just $ \s -> not $ null s
|
|
||||||
defaultamount = maybe balancingamount (Just . show . pamount) bestmatch
|
defaultamount = maybe balancingamount (Just . show . pamount) bestmatch
|
||||||
where balancingamount = Just $ show $ negate $ sum $ map pamount enteredrealps
|
where balancingamount = Just $ show $ negate $ sum $ map pamount enteredrealps
|
||||||
enteredrealps = filter isReal enteredps
|
postingtype ('[':_) = BalancedVirtualPosting
|
||||||
postingaccounttype ('[':_) = BalancedVirtualPosting
|
postingtype ('(':_) = VirtualPosting
|
||||||
postingaccounttype ('(':_) = VirtualPosting
|
postingtype _ = RegularPosting
|
||||||
postingaccounttype _ = RegularPosting
|
|
||||||
stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse
|
stripbrackets = dropWhile (`elem` "([") . reverse . dropWhile (`elem` "])") . reverse
|
||||||
validateamount = Just $ \s ->
|
validateamount = Just $ \s -> (null s && (not $ null enteredrealps))
|
||||||
(null s && (not $ null enteredps)) ||
|
|| (isRight $ parse (someamount>>many spacenonewline>>eof) "" s)
|
||||||
(isRight $ parse (someamount>>many spacenonewline>>eof) "" s)
|
|
||||||
|
|
||||||
|
-- | Prompt for and read a string value, optionally with a default value
|
||||||
-- | Prompt for and read a string value and a flag indicating whether
|
-- and a validator. A validator causes the prompt to repeat until the
|
||||||
-- input has ended (control-d was pressed), optionally with a default
|
-- input is valid. May also raise an EOF exception if control-d is pressed.
|
||||||
-- value and a validator. A validator will cause the prompt to repeat
|
askFor :: String -> Maybe String -> Maybe (String -> Bool) -> IO String
|
||||||
-- until the input is valid (unless the input is just ctrl-d).
|
|
||||||
askFor :: String -> Maybe String -> Maybe (String -> Bool) -> IO (String, Bool)
|
|
||||||
askFor prompt def validator = do
|
askFor prompt def validator = do
|
||||||
hPutStr stderr $ prompt ++ (maybe "" showdef def) ++ ": "
|
hPutStr stderr $ prompt ++ (maybe "" showdef def) ++ ": "
|
||||||
hFlush stderr
|
hFlush stderr
|
||||||
-- ugly
|
l <- getLine
|
||||||
l <- getLine `catch` (\e -> if isEOFError e then return "*EOF*" else ioError e)
|
let input = if null l then fromMaybe l def else l
|
||||||
let (l', eoi) = case l of "*EOF*" -> ("", True)
|
|
||||||
_ -> (l, False)
|
|
||||||
let input = if null l' then fromMaybe l' def else l'
|
|
||||||
case validator of
|
case validator of
|
||||||
Just valid -> if valid input || (null input && eoi)
|
Just valid -> if valid input
|
||||||
then return (input, eoi)
|
then return input
|
||||||
else askFor prompt def validator
|
else askFor prompt def validator
|
||||||
Nothing -> return (input, eoi)
|
Nothing -> return input
|
||||||
where showdef s = " [" ++ s ++ "]"
|
where showdef s = " [" ++ s ++ "]"
|
||||||
|
|
||||||
-- | Append this transaction to the ledger's file. Also, to the ledger's
|
-- | Append this transaction to the ledger's file. Also, to the ledger's
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user