module Penny.Brenner.Import (mode) where
import Control.Monad.Exception.Synchronous as Ex
import Data.Maybe (mapMaybe)
import qualified System.Console.MultiArg as MA
import qualified Penny.Brenner.Types as Y
import qualified Penny.Brenner.Util as U
data Arg
= AFitFile String
| AAllowNew
deriving (Eq, Show)
toFitFile :: Arg -> Maybe String
toFitFile a = case a of
AFitFile s -> Just s
_ -> Nothing
data ImportOpts = ImportOpts
{ fitFile :: Y.FitFileLocation
, allowNew :: Y.AllowNew
, parser :: Y.FitFileLocation
-> IO (Ex.Exceptional String [Y.Posting])
}
mode
:: Maybe Y.FitAcct
-> MA.Mode (IO ())
mode mayFa = MA.Mode
{ MA.mName = "import"
, MA.mIntersperse = MA.Intersperse
, MA.mOpts =
[ MA.OptSpec ["new"] "n" (MA.NoArg AAllowNew) ]
, MA.mPosArgs = AFitFile
, MA.mProcess = processor mayFa
, MA.mHelp = help
}
processor
:: Maybe Y.FitAcct
-> [Arg]
-> IO ()
processor mayFa as = do
(dbLoc, prsr) <- case mayFa of
Nothing -> fail $ "no financial institution account provided"
++ " on command line, and no default financial institution"
++ " account is configured."
Just fa -> return (Y.dbLocation fa, snd . Y.parser $ fa)
loc <- case mapMaybe toFitFile as of
[] -> fail "you must provide a postings file to read"
x:[] -> return (Y.FitFileLocation x)
_ -> fail "you cannot provide more than one postings file to read"
let aNew = Y.AllowNew $ any (== AAllowNew) as
doImport dbLoc (ImportOpts loc aNew prsr)
appendNew
:: [(Y.UNumber, Y.Posting)]
-> [Y.Posting]
-> ([(Y.UNumber, Y.Posting)], Int)
appendNew db new = (db ++ newWithU, length newWithU)
where
nextUNum = if null db
then 0
else (Y.unUNumber . maximum . map fst $ db) + 1
currFitIds = map (Y.fitId . snd) db
isNew p = not (any (== Y.fitId p) currFitIds)
newPstgs = filter isNew new
mkPair i p = (Y.UNumber i, p)
newWithU = zipWith mkPair [nextUNum..] newPstgs
doImport :: Y.DbLocation -> ImportOpts -> IO ()
doImport dbLoc os = do
txnsOld <- U.loadDb (allowNew os) dbLoc
parseResult <- parser os (fitFile os)
ins <- case parseResult of
Ex.Exception e -> fail e
Ex.Success g -> return g
let (new, len) = appendNew txnsOld ins
U.saveDb dbLoc new
putStrLn $ "imported " ++ show len ++ " new transactions."
help :: String -> String
help pn = unlines
[ "usage: " ++ pn ++ " [global-options] import [local-options] FIT_FILE"
, "where FIT_FILE is the file downloaded from the financial"
, "institution."
, ""
, "Local Options:"
, ""
, "-n, --new - Allows creation of new database. Without this option,"
, "if the database file is not found, quits with an error."
, ""
, "-h, --help - Show this help."
, ""
]