add: code cleanups

This commit is contained in:
Simon Michael 2014-02-21 09:07:52 -08:00
parent e35614c88d
commit 56abdb2c8c

View File

@ -53,8 +53,8 @@ add opts j
] ]
today <- showDate `fmap` getCurrentDay today <- showDate `fmap` getCurrentDay
let args = words' $ query_ $ reportopts_ opts let args = words' $ query_ $ reportopts_ opts
(defdate, moredefs) = headTailDef today args (defdate, defs) = headTailDef today args
getAndAddTransactionsLoop j opts defdate moredefs getAndAddTransactionsLoop j opts defdate defs
`E.catch` (\e -> if isEOFError e then putStr "\n" else ioError e) `E.catch` (\e -> if isEOFError e then putStr "\n" else ioError e)
where f = journalFilePath j where f = journalFilePath j
@ -65,9 +65,9 @@ add opts j
-- first transaction; otherwise defaults come from the most similar -- first transaction; otherwise defaults come from the most similar
-- recent transaction in the journal. -- recent transaction in the journal.
getAndAddTransactionsLoop :: Journal -> CliOpts -> String -> [String] -> IO () getAndAddTransactionsLoop :: Journal -> CliOpts -> String -> [String] -> IO ()
getAndAddTransactionsLoop j opts defdate moredefs = do getAndAddTransactionsLoop j opts defdate defs = do
hPrintf stderr "\nStarting a new transaction.\n" hPrintf stderr "\nStarting a new transaction.\n"
t <- getTransaction j opts defdate moredefs t <- getTransaction j opts defdate defs
j' <- journalAddTransaction j opts t j' <- journalAddTransaction j opts t
hPrintf stderr "Added to the journal.\n" hPrintf stderr "Added to the journal.\n"
let defdate' = showDate $ tdate t let defdate' = showDate $ tdate t
@ -77,11 +77,11 @@ getAndAddTransactionsLoop j opts defdate moredefs = do
-- allowing the user to restart and confirm at the end. -- allowing the user to restart and confirm at the end.
-- A default date, and zero or more defaults for subsequent fields, are provided. -- A default date, and zero or more defaults for subsequent fields, are provided.
getTransaction :: Journal -> CliOpts -> String -> [String] -> IO Transaction getTransaction :: Journal -> CliOpts -> String -> [String] -> IO Transaction
getTransaction j opts defdate moredefs = do getTransaction j opts defdate defs = do
mt <- getTransactionOrRestart j opts defdate moredefs mt <- getTransactionOrRestart j opts defdate defs
let restart = do let restart = do
hPrintf stderr "\nRestarting this transaction.\n" hPrintf stderr "\nRestarting this transaction.\n"
getTransaction j opts defdate moredefs getTransaction j opts defdate defs
case mt of case mt of
Nothing -> restart Nothing -> restart
Just t -> do Just t -> do
@ -95,7 +95,7 @@ getTransaction j opts defdate moredefs = do
-- or return nothing indicating that the user wants to restart entering this transaction. -- or return nothing indicating that the user wants to restart entering this transaction.
-- A default date, and zero or more defaults for subsequent fields, are provided. -- A default date, and zero or more defaults for subsequent fields, are provided.
getTransactionOrRestart :: Journal -> CliOpts -> String -> [String] -> IO (Maybe Transaction) getTransactionOrRestart :: Journal -> CliOpts -> String -> [String] -> IO (Maybe Transaction)
getTransactionOrRestart j opts defdate moredefs = do getTransactionOrRestart j opts defdate defs = do
let dateandcodep = do {d <- smartdate; c <- optionMaybe codep; many spacenonewline; eof; return (d, fromMaybe "" c)} let dateandcodep = do {d <- smartdate; c <- optionMaybe codep; many spacenonewline; eof; return (d, fromMaybe "" c)}
datecodestr <- runInteraction $ askFor "date" datecodestr <- runInteraction $ askFor "date"
(Just defdate) (Just defdate)
@ -108,43 +108,58 @@ getTransactionOrRestart j opts defdate moredefs = do
defday = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) defdate defday = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) defdate
datestr = showDate $ fixSmartDate defday sdate datestr = showDate $ fixSmartDate defday sdate
let (defdesc, moredefs') = headTailDef "" moredefs let (defdesc, defs') = headTailDef "" defs
desc <- runInteraction $ askFor "description" (Just defdesc) Nothing desc <- runInteraction $ askFor "description" (Just defdesc) Nothing
if desc == "<" if desc == "<"
then return Nothing then return Nothing
else do else do
let (description,comment) = (strip a, strip $ dropWhile (==';') b) where (a,b) = break (==';') desc let (description,comment) = (strip a, strip $ dropWhile (==';') b) where (a,b) = break (==';') desc
getPostingsForTransactionWithHistory j opts datestr code description comment moredefs' getPostingsForTransactionWithHistory j opts datestr code description comment defs'
data RestartEntryException = RestartEntryException deriving (Typeable,Show) data RestartEntryException = RestartEntryException deriving (Typeable,Show)
instance Exception RestartEntryException instance Exception RestartEntryException
-- | Information used as the basis for suggested account names, amounts etc. in add prompt. -- | State used while entering a single transaction.
data PostingsState = PostingsState { data EntryState = EntryState {
psJournal :: Journal esJournal :: Journal -- ^ the journal we are adding to
,psValidateAccount :: AccountName -> Bool ,esDefaultsRemaining :: [String] -- ^ command line arguments not yet used as defaults
,psSuggestHistoricalAmount :: Bool ,esValidateAccount :: AccountName -> Bool -- ^ validator for entered account names
,psHistory :: Maybe [Posting] ,esSuggestHistoricalAmount :: Bool -- ^ should the amount from a similar past txn be suggested
,esHistoricalPostings :: Maybe [Posting] -- ^ the postings of the most similar past txn
,esEnteredPostings :: [Posting] -- ^ postings entered so far
} }
defEntryState = EntryState {
esJournal = nulljournal
,esDefaultsRemaining = []
,esValidateAccount = const True
,esSuggestHistoricalAmount = True
,esHistoricalPostings = Nothing
,esEnteredPostings = []
}
-- | Loop reading postings from the console, until a valid balanced -- | Loop reading postings from the console, until a valid balanced
-- set of postings has been entered, then return the final transaction, -- set of postings has been entered, then return the final transaction,
-- or nothing indicating that the user wants to restart entering this transaction. -- or nothing indicating that the user wants to restart entering this transaction.
getPostingsForTransactionWithHistory :: Journal -> CliOpts -> String -> String -> String -> String -> [String] -> IO (Maybe Transaction) getPostingsForTransactionWithHistory :: Journal -> CliOpts -> String -> String -> String -> String -> [String] -> IO (Maybe Transaction)
getPostingsForTransactionWithHistory j opts datestr code description comment defargs = do getPostingsForTransactionWithHistory j opts datestr code description comment defs = do
today <- getCurrentDay today <- getCurrentDay
let historymatches = transactionsSimilarTo j (queryFromOpts today $ reportopts_ opts) description let historymatches = transactionsSimilarTo j (queryFromOpts today $ reportopts_ opts) description
bestmatch | not (null defargs) || null historymatches = Nothing bestmatch | not (null defs) || null historymatches = Nothing
| otherwise = Just $ snd $ head historymatches | otherwise = Just $ snd $ head historymatches
bestmatchpostings = maybe Nothing (Just . tpostings) bestmatch bestmatchpostings = maybe Nothing (Just . tpostings) bestmatch
date = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) datestr date = fixSmartDate today $ fromparse $ (parse smartdate "" . lowercase) datestr
validateaccount x = x == "." || (not . null) x && validateaccount x = x == "." || (not . null) x &&
if no_new_accounts_ opts if no_new_accounts_ opts
then x `elem` existingaccts then x `elem` journalAccountNames j
else True else True
existingaccts = journalAccountNames j
getvalidpostings = do getvalidpostings = do
ps <- getPostingsLoop (PostingsState j validateaccount True bestmatchpostings) [] defargs let st = defEntryState{esJournal=j
,esDefaultsRemaining=defs
,esValidateAccount=validateaccount
,esHistoricalPostings=bestmatchpostings
}
ps <- getPostingsLoop st
let t = nulltransaction{tdate=date let t = nulltransaction{tdate=date
,tstatus=False ,tstatus=False
,tcode=code ,tcode=code
@ -161,15 +176,15 @@ getPostingsForTransactionWithHistory j opts datestr code description comment def
-- | Read postings from the command line until . is entered, generating -- | Read postings from the command line until . is entered, generating
-- useful defaults based on historical context and postings entered so far. -- useful defaults based on historical context and postings entered so far.
getPostingsLoop :: PostingsState -> [Posting] -> [String] -> IO [Posting] getPostingsLoop :: EntryState -> IO [Posting]
getPostingsLoop st enteredps defargs = do getPostingsLoop st = do
let bestmatch | isNothing historicalps = Nothing let 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 = historicalps where Just ps = historicalps
bestmatchacct = maybe Nothing (Just . showacctname) bestmatch bestmatchacct = maybe Nothing (Just . showacctname) bestmatch
defacct = maybe bestmatchacct Just $ headMay defargs defs = esDefaultsRemaining st
defargs' = tailDef [] defargs (defacct, defs') = (maybe bestmatchacct Just $ headMay defs, tailDef [] defs)
ordot | null enteredps || length enteredrealps == 1 = "" :: String ordot | null enteredps || length enteredrealps == 1 = "" :: String
| otherwise = " (or . to complete this transaction)" | otherwise = " (or . to complete this transaction)"
account <- runInteractionWithAccountCompletion j $ askFor (printf "account %d%s" n ordot) defacct (Just validateaccount) account <- runInteractionWithAccountCompletion j $ askFor (printf "account %d%s" n ordot) defacct (Just validateaccount)
@ -178,21 +193,21 @@ getPostingsLoop st enteredps defargs = do
then then
if null enteredps if null enteredps
then do hPutStrLn stderr $ "\nPlease enter some postings first." then do hPutStrLn stderr $ "\nPlease enter some postings first."
getPostingsLoop st enteredps defargs getPostingsLoop st
else return enteredps else return enteredps
else do else do
let defacctused = Just account == defacct let defacctaccepted = Just account == defacct
historicalps' = if defacctused then historicalps else Nothing historicalps' = if defacctaccepted then historicalps else Nothing
bestmatch' | isNothing historicalps' = Nothing 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 = historicalps' where Just ps = historicalps'
defamountstr | isJust commandlineamt = commandlineamt (amtfromdefs, defs'') = (headMay defs', tailDef [] defs')
defamountstr | isJust amtfromdefs = amtfromdefs
| isJust bestmatch' && suggesthistorical = Just historicalamountstr | isJust bestmatch' && suggesthistorical = Just historicalamountstr
| n > 1 = Just balancingamountstr | n > 1 = Just balancingamountstr
| otherwise = Nothing | otherwise = Nothing
where where
commandlineamt = headMay defargs'
historicalamountstr = showMixedAmountWithPrecision p $ pamount $ fromJust bestmatch' historicalamountstr = showMixedAmountWithPrecision p $ pamount $ fromJust bestmatch'
balancingamountstr = showMixedAmountWithPrecision p $ negate $ sum $ map pamount enteredrealps balancingamountstr = showMixedAmountWithPrecision p $ negate $ sum $ map pamount enteredrealps
-- what should this be ? -- what should this be ?
@ -203,32 +218,35 @@ getPostingsLoop st enteredps defargs = do
-- 5 3 or 4, whichever would show the most decimal places ? -- 5 3 or 4, whichever would show the most decimal places ?
-- I think 1 or 4, whichever would show the most decimal places -- I think 1 or 4, whichever would show the most decimal places
p = maxprecisionwithpoint p = maxprecisionwithpoint
defargs'' = tailDef [] defargs'
amt <- runInteraction $ askFor (printf "amount %d" n) defamountstr validateamount amt <- runInteraction $ askFor (printf "amount %d" n) defamountstr validateamount
when (amt=="<") $ throwIO RestartEntryException when (amt=="<") $ throwIO RestartEntryException
let (amountstr,comment) = (strip a, strip $ dropWhile (==';') b) where (a,b) = break (==';') amt let (amountstr,comment) = (strip a, strip $ dropWhile (==';') b) where (a,b) = break (==';') amt
let a = fromparse $ runParser (amountp <|> return missingamt) ctx "" amountstr let a = fromparse $ runParser (amountp <|> return missingamt) ctx "" amountstr
a' = fromparse $ runParser (amountp <|> return missingamt) nullctx "" amountstr a' = fromparse $ runParser (amountp <|> return missingamt) nullctx "" amountstr
wasdefamtused = Just (showAmount a) == defamountstr defamtaccepted = Just (showAmount a) == defamountstr
defcommodityadded | acommodity a == acommodity a' = Nothing defcommodityadded | acommodity a == acommodity a' = Nothing
| otherwise = Just $ acommodity a | otherwise = Just $ acommodity a
p = nullposting{paccount=stripbrackets account p = nullposting{paccount=stripbrackets account
,pamount=mixed a ,pamount=mixed a
,pcomment=comment ,pcomment=comment
,ptype=postingtype account ,ptype=postingtype account
} }
st' = if wasdefamtused st' = st{esEnteredPostings=esEnteredPostings st ++ [p]
then st ,esDefaultsRemaining=defs''
else st{psHistory=historicalps', psSuggestHistoricalAmount=False} }
st'' = if defamtaccepted
then st'
else st'{esHistoricalPostings=historicalps', esSuggestHistoricalAmount=False}
when (isJust defcommodityadded) $ when (isJust defcommodityadded) $
liftIO $ hPutStrLn stderr $ printf "using default commodity (%s)" (fromJust defcommodityadded) liftIO $ hPutStrLn stderr $ printf "using default commodity (%s)" (fromJust defcommodityadded)
getPostingsLoop st' (enteredps ++ [p]) defargs'' getPostingsLoop st''
where where
j = psJournal st j = esJournal st
historicalps = psHistory st historicalps = esHistoricalPostings st
ctx = jContext j ctx = jContext j
validateaccount = psValidateAccount st validateaccount = esValidateAccount st
suggesthistorical = psSuggestHistoricalAmount st suggesthistorical = esSuggestHistoricalAmount st
enteredps = esEnteredPostings st
n = length enteredps + 1 n = length enteredps + 1
enteredrealps = filter isReal enteredps enteredrealps = filter isReal enteredps
showacctname p = showAccountName Nothing (ptype p) $ paccount p showacctname p = showAccountName Nothing (ptype p) $ paccount p