Compare commits
No commits in common. "f9c1e36ee8985f15fc6fe62ff831fdf60f6b5b1f" and "0faea1161c60da3e9b845c27ec08c54c32298564" have entirely different histories.
f9c1e36ee8
...
0faea1161c
19
app/Main.hs
19
app/Main.hs
|
@ -157,12 +157,17 @@ runDumpAccountKeys c = do
|
||||||
runSync :: MonadUnliftIO m => FilePath -> m ()
|
runSync :: MonadUnliftIO m => FilePath -> m ()
|
||||||
runSync c = do
|
runSync c = do
|
||||||
config <- readConfig c
|
config <- readConfig c
|
||||||
migrate_ (sqlConfig config) $ do
|
catch (sync_ config) $ \case
|
||||||
s <- getDBState config
|
InsertException _ -> liftIO $ putStrLn "insert error"
|
||||||
flip runReaderT (s $ takeDirectory c) $ do
|
where
|
||||||
es1 <- insertBudget $ budget config
|
sync_ config = migrate_ (sqlConfig config) $ do
|
||||||
es2 <- insertStatements config
|
res <- getDBState config
|
||||||
let es = es1 ++ es2
|
case res of
|
||||||
unless (null es) $ throwIO $ InsertException es
|
Left es -> throwIO $ InsertException es
|
||||||
|
Right s -> flip runReaderT (s $ takeDirectory c) $ do
|
||||||
|
es1 <- insertBudget $ budget config
|
||||||
|
es2 <- insertStatements config
|
||||||
|
let es = es1 ++ es2
|
||||||
|
unless (null es) $ throwIO $ InsertException es
|
||||||
|
|
||||||
-- showBalances
|
-- showBalances
|
||||||
|
|
|
@ -306,19 +306,23 @@ indexAcntRoot r =
|
||||||
getDBState
|
getDBState
|
||||||
:: MonadUnliftIO m
|
:: MonadUnliftIO m
|
||||||
=> Config
|
=> Config
|
||||||
-> SqlPersistT m (FilePath -> DBState)
|
-> SqlPersistT m (EitherErrs (FilePath -> DBState))
|
||||||
getDBState c = do
|
getDBState c = mapM (uncurry go) $ concatEithers2 bi si (,)
|
||||||
am <- updateAccounts $ accounts c
|
where
|
||||||
cm <- updateCurrencies $ currencies c
|
bi = intervalMaybeBounds $ budgetInterval $ global c
|
||||||
hs <- updateHashes c
|
si = intervalMaybeBounds $ statementInterval $ global c
|
||||||
-- TODO not sure how I feel about this, probably will change this struct alot
|
go budgetInt statementInt = do
|
||||||
-- in the future so whatever...for now
|
am <- updateAccounts $ accounts c
|
||||||
return $ \f ->
|
cm <- updateCurrencies $ currencies c
|
||||||
DBState
|
hs <- updateHashes c
|
||||||
{ kmCurrency = cm
|
-- TODO not sure how I feel about this, probably will change this struct alot
|
||||||
, kmAccount = am
|
-- in the future so whatever...for now
|
||||||
, kmBudgetInterval = intervalMaybeBounds $ budgetInterval $ global c
|
return $ \f ->
|
||||||
, kmStatementInterval = intervalMaybeBounds $ statementInterval $ global c
|
DBState
|
||||||
, kmNewCommits = hs
|
{ kmCurrency = cm
|
||||||
, kmConfigDir = f
|
, kmAccount = am
|
||||||
}
|
, kmBudgetInterval = budgetInt
|
||||||
|
, kmStatementInterval = statementInt
|
||||||
|
, kmNewCommits = hs
|
||||||
|
, kmConfigDir = f
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ module Internal.Insert
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Data.Bitraversable
|
||||||
import Data.Hashable
|
import Data.Hashable
|
||||||
import Database.Persist.Class
|
import Database.Persist.Class
|
||||||
import Database.Persist.Sql hiding (Single, Statement)
|
import Database.Persist.Sql hiding (Single, Statement)
|
||||||
|
@ -48,19 +49,21 @@ lookupCurrency c = do
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- intervals
|
-- intervals
|
||||||
|
|
||||||
expandDatePat :: Bounds -> DatePat -> [Day]
|
expandDatePat :: Bounds -> DatePat -> EitherErr [Day]
|
||||||
expandDatePat (a, b) (Cron cp) = filter (cronPatternMatches cp) [a .. b]
|
expandDatePat (a, b) (Cron cp) = return $ filter (cronPatternMatches cp) [a .. b]
|
||||||
expandDatePat i (Mod mp) = expandModPat mp i
|
expandDatePat i (Mod mp) = expandModPat mp i
|
||||||
|
|
||||||
expandModPat :: ModPat -> Bounds -> [Day]
|
expandModPat :: ModPat -> Bounds -> EitherErr [Day]
|
||||||
expandModPat
|
expandModPat
|
||||||
ModPat {mpStart = s, mpBy = b, mpUnit = u, mpRepeats = r}
|
ModPat {mpStart = s, mpBy = b, mpUnit = u, mpRepeats = r}
|
||||||
(lower, upper) =
|
(lower, upper) = do
|
||||||
takeWhile (<= upper) $
|
start <- maybe (return lower) fromGregorian' s
|
||||||
(`addFun` start) . (* b')
|
return $
|
||||||
<$> maybe id (take . fromIntegral) r [0 ..]
|
takeWhile (<= upper) $
|
||||||
|
(`addFun` start) . (* b')
|
||||||
|
<$> maybe id (take . fromIntegral) r [0 ..]
|
||||||
where
|
where
|
||||||
start = maybe lower fromGregorian' s
|
-- start = maybe lower fromGregorian' s
|
||||||
b' = fromIntegral b
|
b' = fromIntegral b
|
||||||
addFun = case u of
|
addFun = case u of
|
||||||
Day -> addDays
|
Day -> addDays
|
||||||
|
@ -118,8 +121,9 @@ mdyPatternMatches check x p = case p of
|
||||||
|
|
||||||
insertBudget :: MonadUnliftIO m => Budget -> MappingT m [InsertError]
|
insertBudget :: MonadUnliftIO m => Budget -> MappingT m [InsertError]
|
||||||
insertBudget Budget {income = is, expenses = es} = do
|
insertBudget Budget {income = is, expenses = es} = do
|
||||||
mapM_ insertExpense es
|
es1 <- mapM insertIncome is
|
||||||
concat <$> mapM insertIncome is
|
es2 <- mapM insertExpense es
|
||||||
|
return $ concat $ es1 ++ es2
|
||||||
|
|
||||||
-- TODO this hashes twice (not that it really matters)
|
-- TODO this hashes twice (not that it really matters)
|
||||||
whenHash
|
whenHash
|
||||||
|
@ -144,21 +148,23 @@ insertIncome
|
||||||
} =
|
} =
|
||||||
whenHash CTIncome i [] $ \c -> do
|
whenHash CTIncome i [] $ \c -> do
|
||||||
bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval
|
bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval
|
||||||
unlessLeft (balanceIncome i) $ \balanced -> do
|
case (balanceIncome i, expandDatePat bounds dp) of
|
||||||
forM_ (expandDatePat bounds dp) $ \day -> do
|
(Right balanced, Right days) -> do
|
||||||
alloTx <- concat <$> mapM (allocationToTx from day) balanced
|
forM_ days $ \day -> do
|
||||||
taxTx <- fmap (,Fixed) <$> mapM (taxToTx from day cur) ts
|
alloTx <- concat <$> mapM (allocationToTx from day) balanced
|
||||||
lift $ mapM_ (\(t, b) -> insertTxBucket (Just b) c t) $ alloTx ++ taxTx
|
taxTx <- fmap (,Fixed) <$> mapM (taxToTx from day cur) ts
|
||||||
|
lift $ mapM_ (\(t, b) -> insertTxBucket (Just b) c t) $ alloTx ++ taxTx
|
||||||
|
return []
|
||||||
|
(a, b) -> return $ catMaybes [leftToMaybe a, leftToMaybe b]
|
||||||
|
|
||||||
balanceIncome :: Income -> EitherErr [BalAllocation]
|
balanceIncome :: Income -> EitherErr [BalAllocation]
|
||||||
balanceIncome
|
balanceIncome
|
||||||
Income
|
Income
|
||||||
{ incGross = g
|
{ incGross = g
|
||||||
, incWhen = dp
|
|
||||||
, incPretax = pre
|
, incPretax = pre
|
||||||
, incTaxes = tax
|
, incTaxes = tax
|
||||||
, incPosttax = post
|
, incPosttax = post
|
||||||
} = (preRat ++) <$> balancePostTax dp bal postRat
|
} = (preRat ++) <$> balancePostTax bal postRat
|
||||||
where
|
where
|
||||||
preRat = mapAlloAmts dec2Rat <$> pre
|
preRat = mapAlloAmts dec2Rat <$> pre
|
||||||
postRat = mapAlloAmts (fmap dec2Rat) <$> post
|
postRat = mapAlloAmts (fmap dec2Rat) <$> post
|
||||||
|
@ -229,7 +235,7 @@ transferToTx
|
||||||
transferToTx day from to cur Amount {amtValue = v, amtDesc = d} =
|
transferToTx day from to cur Amount {amtValue = v, amtDesc = d} =
|
||||||
txPair day from to cur v d
|
txPair day from to cur v d
|
||||||
|
|
||||||
insertExpense :: MonadUnliftIO m => Expense -> MappingT m ()
|
insertExpense :: MonadUnliftIO m => Expense -> MappingT m [InsertError]
|
||||||
insertExpense
|
insertExpense
|
||||||
e@Expense
|
e@Expense
|
||||||
{ expFrom = from
|
{ expFrom = from
|
||||||
|
@ -238,11 +244,12 @@ insertExpense
|
||||||
, expBucket = buc
|
, expBucket = buc
|
||||||
, expAmounts = as
|
, expAmounts = as
|
||||||
} = do
|
} = do
|
||||||
whenHash CTExpense e () $ \key -> mapM_ (go key) as
|
whenHash CTExpense e [] $ \key -> concat <$> mapM (go key) as
|
||||||
where
|
where
|
||||||
go key amt = do
|
go key amt = do
|
||||||
keys <- timeAmountToTx from to cur amt
|
res <- timeAmountToTx from to cur amt
|
||||||
lift $ mapM_ (insertTxBucket (Just buc) key) keys
|
unlessLeft res $
|
||||||
|
lift . mapM_ (insertTxBucket (Just buc) key)
|
||||||
|
|
||||||
timeAmountToTx
|
timeAmountToTx
|
||||||
:: MonadUnliftIO m
|
:: MonadUnliftIO m
|
||||||
|
@ -250,7 +257,7 @@ timeAmountToTx
|
||||||
-> AcntID
|
-> AcntID
|
||||||
-> CurID
|
-> CurID
|
||||||
-> TimeAmount
|
-> TimeAmount
|
||||||
-> MappingT m [KeyTx]
|
-> MappingT m (EitherErr [KeyTx])
|
||||||
timeAmountToTx
|
timeAmountToTx
|
||||||
from
|
from
|
||||||
to
|
to
|
||||||
|
@ -264,7 +271,7 @@ timeAmountToTx
|
||||||
}
|
}
|
||||||
} = do
|
} = do
|
||||||
bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval
|
bounds <- (liftIO . resolveBounds) =<< asks kmBudgetInterval
|
||||||
mapM tx $ expandDatePat bounds dp
|
bimapM return (mapM tx) $ expandDatePat bounds dp
|
||||||
where
|
where
|
||||||
tx day = txPair day from to cur (dec2Rat v) d
|
tx day = txPair day from to cur (dec2Rat v) d
|
||||||
|
|
||||||
|
@ -277,10 +284,10 @@ insertStatements conf = concat <$> mapM insertStatement (statements conf)
|
||||||
-- unless (null es) $ throwIO $ InsertException es
|
-- unless (null es) $ throwIO $ InsertException es
|
||||||
|
|
||||||
insertStatement :: MonadUnliftIO m => Statement -> MappingT m [InsertError]
|
insertStatement :: MonadUnliftIO m => Statement -> MappingT m [InsertError]
|
||||||
insertStatement (StmtManual m) = insertManual m >> return []
|
insertStatement (StmtManual m) = insertManual m
|
||||||
insertStatement (StmtImport i) = insertImport i
|
insertStatement (StmtImport i) = insertImport i
|
||||||
|
|
||||||
insertManual :: MonadUnliftIO m => Manual -> MappingT m ()
|
insertManual :: MonadUnliftIO m => Manual -> MappingT m [InsertError]
|
||||||
insertManual
|
insertManual
|
||||||
m@Manual
|
m@Manual
|
||||||
{ manualDate = dp
|
{ manualDate = dp
|
||||||
|
@ -290,10 +297,11 @@ insertManual
|
||||||
, manualCurrency = u
|
, manualCurrency = u
|
||||||
, manualDesc = e
|
, manualDesc = e
|
||||||
} = do
|
} = do
|
||||||
whenHash CTManual m () $ \c -> do
|
whenHash CTManual m [] $ \c -> do
|
||||||
bounds <- (liftIO . resolveBounds) =<< asks kmStatementInterval
|
bounds <- (liftIO . resolveBounds) =<< asks kmStatementInterval
|
||||||
ts <- mapM tx $ expandDatePat bounds dp
|
unlessLeft (expandDatePat bounds dp) $ \days -> do
|
||||||
lift $ mapM_ (insertTx c) ts
|
ts <- mapM tx days
|
||||||
|
lift $ mapM_ (insertTx c) ts
|
||||||
where
|
where
|
||||||
tx day = txPair day from to u (dec2Rat v) e
|
tx day = txPair day from to u (dec2Rat v) e
|
||||||
|
|
||||||
|
|
|
@ -102,24 +102,27 @@ recoverZipper :: Zipped a -> [a]
|
||||||
recoverZipper (Zipped as bs) = reverse as ++ bs
|
recoverZipper (Zipped as bs) = reverse as ++ bs
|
||||||
|
|
||||||
zipperSlice
|
zipperSlice
|
||||||
:: (a -> b -> Ordering)
|
:: Monad m
|
||||||
|
=> (a -> b -> m Ordering)
|
||||||
-> b
|
-> b
|
||||||
-> Zipped a
|
-> Zipped a
|
||||||
-> Either (Zipped a) (Unzipped a)
|
-> m (Either (Zipped a) (Unzipped a))
|
||||||
zipperSlice f x = go
|
zipperSlice f x = go
|
||||||
where
|
where
|
||||||
go z@(Zipped _ []) = Left z
|
go z@(Zipped _ []) = return $ Left z
|
||||||
go z@(Zipped bs (a : as)) =
|
go z@(Zipped bs (a : as)) = do
|
||||||
case f a x of
|
res <- f a x
|
||||||
|
case res of
|
||||||
GT -> go $ Zipped (a : bs) as
|
GT -> go $ Zipped (a : bs) as
|
||||||
EQ -> Right $ goEq (Unzipped bs [a] as)
|
EQ -> Right <$> goEq (Unzipped bs [a] as)
|
||||||
LT -> Left z
|
LT -> return $ Left z
|
||||||
goEq z@(Unzipped _ _ []) = z
|
goEq z@(Unzipped _ _ []) = return z
|
||||||
goEq z@(Unzipped bs cs (a : as)) =
|
goEq z@(Unzipped bs cs (a : as)) = do
|
||||||
case f a x of
|
res <- f a x
|
||||||
|
case res of
|
||||||
GT -> goEq $ Unzipped (a : bs) cs as
|
GT -> goEq $ Unzipped (a : bs) cs as
|
||||||
EQ -> goEq $ Unzipped bs (a : cs) as
|
EQ -> goEq $ Unzipped bs (a : cs) as
|
||||||
LT -> z
|
LT -> return z
|
||||||
|
|
||||||
zipperMatch :: Unzipped Match -> TxRecord -> EitherErrs (Zipped Match, MatchRes RawTx)
|
zipperMatch :: Unzipped Match -> TxRecord -> EitherErrs (Zipped Match, MatchRes RawTx)
|
||||||
zipperMatch (Unzipped bs cs as) x = go [] cs
|
zipperMatch (Unzipped bs cs as) x = go [] cs
|
||||||
|
@ -175,8 +178,9 @@ matchDates ms = go ([], [], initZipper ms)
|
||||||
, reverse unmatched
|
, reverse unmatched
|
||||||
, recoverZipper z
|
, recoverZipper z
|
||||||
)
|
)
|
||||||
go (matched, unmatched, z) (r : rs) =
|
go (matched, unmatched, z) (r : rs) = do
|
||||||
case zipperSlice findDate r z of
|
sliced <- zipperSlice findDate r z
|
||||||
|
case sliced of
|
||||||
Left zipped -> go (matched, r : unmatched, zipped) rs
|
Left zipped -> go (matched, r : unmatched, zipped) rs
|
||||||
Right unzipped -> do
|
Right unzipped -> do
|
||||||
(z', res) <- zipperMatch unzipped r
|
(z', res) <- zipperMatch unzipped r
|
||||||
|
@ -185,7 +189,7 @@ matchDates ms = go ([], [], initZipper ms)
|
||||||
MatchSkip -> (Nothing : matched, unmatched)
|
MatchSkip -> (Nothing : matched, unmatched)
|
||||||
MatchFail -> (matched, r : unmatched)
|
MatchFail -> (matched, r : unmatched)
|
||||||
go (m, u, z') rs
|
go (m, u, z') rs
|
||||||
findDate m r = maybe EQ (`compareDate` trDate r) $ mDate m
|
findDate m r = maybe (Right EQ) (first (: []) . (`compareDate` trDate r)) $ mDate m
|
||||||
|
|
||||||
matchNonDates :: [Match] -> [TxRecord] -> EitherErrs ([RawTx], [TxRecord], [Match])
|
matchNonDates :: [Match] -> [TxRecord] -> EitherErrs ([RawTx], [TxRecord], [Match])
|
||||||
matchNonDates ms = go ([], [], initZipper ms)
|
matchNonDates ms = go ([], [], initZipper ms)
|
||||||
|
|
|
@ -540,8 +540,8 @@ data AllocationSuberr
|
||||||
|
|
||||||
data InsertError
|
data InsertError
|
||||||
= RegexError T.Text
|
= RegexError T.Text
|
||||||
| -- | YearError MatchYMD
|
| YearError Natural
|
||||||
ConversionError T.Text
|
| ConversionError T.Text
|
||||||
| LookupError LookupSuberr T.Text
|
| LookupError LookupSuberr T.Text
|
||||||
| BalanceError BalanceType CurID [RawSplit]
|
| BalanceError BalanceType CurID [RawSplit]
|
||||||
| AllocationError AllocationSuberr DatePat
|
| AllocationError AllocationSuberr DatePat
|
||||||
|
|
|
@ -2,25 +2,7 @@
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
module Internal.Utils
|
module Internal.Utils where
|
||||||
( compareDate
|
|
||||||
, intervalMaybeBounds
|
|
||||||
, fmtRational
|
|
||||||
, matches
|
|
||||||
, fromGregorian'
|
|
||||||
, resolveBounds
|
|
||||||
, leftToMaybe
|
|
||||||
, dec2Rat
|
|
||||||
, concatEithers2
|
|
||||||
, concatEither2
|
|
||||||
, parseRational
|
|
||||||
, showError
|
|
||||||
, unlessLeft
|
|
||||||
, unlessLefts
|
|
||||||
, inMaybeBounds
|
|
||||||
, acntPath2Text
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Time.Format.ISO8601
|
import Data.Time.Format.ISO8601
|
||||||
import GHC.Real
|
import GHC.Real
|
||||||
|
@ -33,39 +15,49 @@ import RIO.Time
|
||||||
import Text.Regex.TDFA
|
import Text.Regex.TDFA
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- dates
|
-- gregorian
|
||||||
|
|
||||||
gregTup :: Gregorian -> (Integer, Int, Int)
|
gregTup :: Gregorian -> EitherErr (Integer, Int, Int)
|
||||||
gregTup Gregorian {..} =
|
gregTup Gregorian {..}
|
||||||
( fromIntegral gYear
|
| gYear > 99 = Left $ YearError gYear
|
||||||
, fromIntegral gMonth
|
| otherwise =
|
||||||
, fromIntegral gDay
|
return
|
||||||
)
|
( fromIntegral gYear + 2000
|
||||||
|
, fromIntegral gMonth
|
||||||
|
, fromIntegral gDay
|
||||||
|
)
|
||||||
|
|
||||||
gregMTup :: GregorianM -> (Integer, Int)
|
gregMTup :: GregorianM -> EitherErr (Integer, Int)
|
||||||
gregMTup GregorianM {..} =
|
gregMTup GregorianM {..}
|
||||||
( fromIntegral gmYear
|
| gmYear > 99 = Left $ YearError gmYear
|
||||||
, fromIntegral gmMonth
|
| otherwise =
|
||||||
)
|
return
|
||||||
|
( fromIntegral gmYear + 2000
|
||||||
|
, fromIntegral gmMonth
|
||||||
|
)
|
||||||
|
|
||||||
data YMD_ = Y_ Integer | YM_ Integer Int | YMD_ Integer Int Int
|
data MDY_ = Y_ Integer | YM_ Integer Int | YMD_ Integer Int Int
|
||||||
|
|
||||||
fromMatchYMD :: MatchYMD -> YMD_
|
fromMatchYMD :: MatchYMD -> EitherErr MDY_
|
||||||
fromMatchYMD m = case m of
|
fromMatchYMD m = case m of
|
||||||
Y y -> Y_ $ fromIntegral y
|
Y y
|
||||||
YM g -> uncurry YM_ $ gregMTup g
|
| y > 99 -> Left $ YearError y
|
||||||
YMD g -> uncurry3 YMD_ $ gregTup g
|
| otherwise -> Right $ Y_ $ fromIntegral y + 2000
|
||||||
|
YM g -> uncurry YM_ <$> gregMTup g
|
||||||
|
YMD g -> uncurry3 YMD_ <$> gregTup g
|
||||||
|
|
||||||
compareDate :: MatchDate -> Day -> Ordering
|
compareDate :: MatchDate -> Day -> EitherErr Ordering
|
||||||
compareDate (On md) x =
|
compareDate (On md) x = do
|
||||||
case fromMatchYMD md of
|
res <- fromMatchYMD md
|
||||||
|
return $ case res of
|
||||||
Y_ y' -> compare y y'
|
Y_ y' -> compare y y'
|
||||||
YM_ y' m' -> compare (y, m) (y', m')
|
YM_ y' m' -> compare (y, m) (y', m')
|
||||||
YMD_ y' m' d' -> compare (y, m, d) (y', m', d')
|
YMD_ y' m' d' -> compare (y, m, d) (y', m', d')
|
||||||
where
|
where
|
||||||
(y, m, d) = toGregorian x
|
(y, m, d) = toGregorian x
|
||||||
compareDate (In md offset) x = do
|
compareDate (In md offset) x = do
|
||||||
case fromMatchYMD md of
|
res <- fromMatchYMD md
|
||||||
|
return $ case res of
|
||||||
Y_ y' -> compareRange y' y
|
Y_ y' -> compareRange y' y
|
||||||
YM_ y' m' -> compareRange (toMonth y' m') $ toMonth y m
|
YM_ y' m' -> compareRange (toMonth y' m') $ toMonth y m
|
||||||
YMD_ y' m' d' ->
|
YMD_ y' m' d' ->
|
||||||
|
@ -78,21 +70,26 @@ compareDate (In md offset) x = do
|
||||||
| otherwise = if (start + fromIntegral offset - 1) < z then GT else EQ
|
| otherwise = if (start + fromIntegral offset - 1) < z then GT else EQ
|
||||||
toMonth year month = (year * 12) + fromIntegral month
|
toMonth year month = (year * 12) + fromIntegral month
|
||||||
|
|
||||||
-- boundsFromGregorian :: (Gregorian, Gregorian) -> Bounds
|
boundsFromGregorian :: (Gregorian, Gregorian) -> EitherErrs Bounds
|
||||||
-- boundsFromGregorian (a, b) = (fromGregorian' a, fromGregorian' b)
|
boundsFromGregorian (a, b) = concatEither2 a_ b_ (,)
|
||||||
|
where
|
||||||
|
a_ = fromGregorian' a
|
||||||
|
b_ = fromGregorian' b
|
||||||
|
|
||||||
fromGregorian' :: Gregorian -> Day
|
fromGregorian' :: Gregorian -> EitherErr Day
|
||||||
fromGregorian' = uncurry3 fromGregorian . gregTup
|
fromGregorian' = fmap (uncurry3 fromGregorian) . gregTup
|
||||||
|
|
||||||
-- inBounds :: Bounds -> Day -> Bool
|
inBounds :: Bounds -> Day -> Bool
|
||||||
-- inBounds (d0, d1) x = d0 <= x && x <= d1
|
inBounds (d0, d1) x = d0 <= x && x <= d1
|
||||||
|
|
||||||
inMaybeBounds :: MaybeBounds -> Day -> Bool
|
inMaybeBounds :: MaybeBounds -> Day -> Bool
|
||||||
inMaybeBounds (d0, d1) x = maybe True (x >=) d0 && maybe True (x <=) d1
|
inMaybeBounds (d0, d1) x = maybe True (x >=) d0 && maybe True (x <=) d1
|
||||||
|
|
||||||
intervalMaybeBounds :: Interval -> MaybeBounds
|
intervalMaybeBounds :: Interval -> EitherErrs MaybeBounds
|
||||||
intervalMaybeBounds Interval {intStart = s, intEnd = e} =
|
intervalMaybeBounds Interval {intStart = s, intEnd = e} = concatEither2 s_ e_ (,)
|
||||||
(fmap fromGregorian' s, fmap fromGregorian' e)
|
where
|
||||||
|
s_ = mapM fromGregorian' s
|
||||||
|
e_ = mapM fromGregorian' e
|
||||||
|
|
||||||
resolveBounds :: MonadUnliftIO m => MaybeBounds -> m Bounds
|
resolveBounds :: MonadUnliftIO m => MaybeBounds -> m Bounds
|
||||||
resolveBounds (s, e) = do
|
resolveBounds (s, e) = do
|
||||||
|
@ -107,13 +104,13 @@ resolveBounds (s, e) = do
|
||||||
|
|
||||||
matches :: Match -> TxRecord -> EitherErrs (MatchRes RawTx)
|
matches :: Match -> TxRecord -> EitherErrs (MatchRes RawTx)
|
||||||
matches Match {..} r@TxRecord {..} = do
|
matches Match {..} r@TxRecord {..} = do
|
||||||
res <- concatEither2 other desc (&&)
|
res <- concatEither3 date other desc (\x y z -> x && y && z)
|
||||||
if date && val && res
|
if val && res
|
||||||
then maybe (Right MatchSkip) (fmap MatchPass . convert) mTx
|
then maybe (Right MatchSkip) (fmap MatchPass . convert) mTx
|
||||||
else Right MatchFail
|
else Right MatchFail
|
||||||
where
|
where
|
||||||
val = valMatches mVal trAmount
|
val = valMatches mVal trAmount
|
||||||
date = maybe True (`dateMatches` trDate) mDate
|
date = maybe (Right True) (`dateMatches` trDate) mDate
|
||||||
other = foldM (\a o -> (a &&) <$> otherMatches trOther o) True mOther
|
other = foldM (\a o -> (a &&) <$> otherMatches trOther o) True mOther
|
||||||
desc = maybe (return True) (matchMaybe trDesc) mDesc
|
desc = maybe (return True) (matchMaybe trDesc) mDesc
|
||||||
convert (ToTx cur a ss) = toTx cur a ss r
|
convert (ToTx cur a ss) = toTx cur a ss r
|
||||||
|
@ -149,8 +146,8 @@ valMatches MatchVal {..} x =
|
||||||
s = signum x >= 0
|
s = signum x >= 0
|
||||||
checkMaybe = maybe True
|
checkMaybe = maybe True
|
||||||
|
|
||||||
dateMatches :: MatchDate -> Day -> Bool
|
dateMatches :: MatchDate -> Day -> EitherErr Bool
|
||||||
dateMatches md = (EQ ==) . compareDate md
|
dateMatches md = fmap (EQ ==) . compareDate md
|
||||||
|
|
||||||
otherMatches :: M.Map T.Text T.Text -> MatchOther -> EitherErr Bool
|
otherMatches :: M.Map T.Text T.Text -> MatchOther -> EitherErr Bool
|
||||||
otherMatches dict m = case m of
|
otherMatches dict m = case m of
|
||||||
|
@ -246,8 +243,8 @@ readRational s = case T.split (== '.') s of
|
||||||
err = Left $ ConversionError s
|
err = Left $ ConversionError s
|
||||||
|
|
||||||
-- TODO smells like a lens
|
-- TODO smells like a lens
|
||||||
-- mapTxSplits :: (a -> b) -> Tx a -> Tx b
|
mapTxSplits :: (a -> b) -> Tx a -> Tx b
|
||||||
-- mapTxSplits f t@Tx {txSplits = ss} = t {txSplits = fmap f ss}
|
mapTxSplits f t@Tx {txSplits = ss} = t {txSplits = fmap f ss}
|
||||||
|
|
||||||
fmtRational :: Natural -> Rational -> T.Text
|
fmtRational :: Natural -> Rational -> T.Text
|
||||||
fmtRational precision x = T.concat [s, txt n', ".", pad 2 "0" $ txt d']
|
fmtRational precision x = T.concat [s, txt n', ".", pad 2 "0" $ txt d']
|
||||||
|
@ -275,6 +272,8 @@ acntPath2Text (AcntPath t cs) = T.intercalate "/" (atName t : cs)
|
||||||
showError :: InsertError -> [T.Text]
|
showError :: InsertError -> [T.Text]
|
||||||
showError (StatementError ts ms) = (showTx <$> ts) ++ (showMatch <$> ms)
|
showError (StatementError ts ms) = (showTx <$> ts) ++ (showMatch <$> ms)
|
||||||
showError other = (: []) $ case other of
|
showError other = (: []) $ case other of
|
||||||
|
-- TODO show whole date here since this is kinda useless
|
||||||
|
(YearError y) -> T.append "Year must be two digits: " $ showT y
|
||||||
(RegexError re) -> T.append "could not make regex from pattern: " re
|
(RegexError re) -> T.append "could not make regex from pattern: " re
|
||||||
(ConversionError x) -> T.append "Could not convert to rational number: " x
|
(ConversionError x) -> T.append "Could not convert to rational number: " x
|
||||||
-- TODO use the field indicator
|
-- TODO use the field indicator
|
||||||
|
@ -347,50 +346,24 @@ showMatch Match {mDate = d, mVal = v, mDesc = e, mOther = o, mTimes = n, mPriori
|
||||||
[] -> Nothing
|
[] -> Nothing
|
||||||
xs -> Just $ T.concat $ showMatchOther <$> xs
|
xs -> Just $ T.concat $ showMatchOther <$> xs
|
||||||
|
|
||||||
-- | Convert match date to text
|
|
||||||
-- Single date matches will just show the single date, and ranged matches will
|
|
||||||
-- show an interval like [YY-MM-DD, YY-MM-DD)
|
|
||||||
showMatchDate :: MatchDate -> T.Text
|
showMatchDate :: MatchDate -> T.Text
|
||||||
showMatchDate md = case md of
|
showMatchDate md = case md of
|
||||||
(On x) -> showMatchYMD x
|
(On x) ->
|
||||||
(In start n) -> T.concat ["[", showMatchYMD start, " ", showYMD_ end, ")"]
|
let ys = case x of
|
||||||
where
|
Y y -> [y]
|
||||||
-- TODO not DRY (this shifting thing happens during the comparison
|
YM (GregorianM {..}) -> [gmYear, gmMonth]
|
||||||
-- function (kinda)
|
YMD (Gregorian {..}) -> [gYear, gMonth, gDay]
|
||||||
end = case fromMatchYMD start of
|
in T.intercalate "-" $ L.take 3 (fmap showT ys ++ L.repeat "*")
|
||||||
Y_ y -> Y_ $ y + fromIntegral n
|
(In _ _) -> undefined
|
||||||
YM_ y m ->
|
|
||||||
let (y_, m_) = divMod (m + fromIntegral n - 1) 12
|
|
||||||
in YM_ (y + fromIntegral y_) (m + m_ + 1)
|
|
||||||
YMD_ y m d ->
|
|
||||||
uncurry3 YMD_ $
|
|
||||||
toGregorian $
|
|
||||||
addDays (fromIntegral n) $
|
|
||||||
fromGregorian y m d
|
|
||||||
|
|
||||||
-- | convert YMD match to text
|
-- let ys = case x of
|
||||||
showMatchYMD :: MatchYMD -> T.Text
|
-- Y y -> [y]
|
||||||
showMatchYMD = showYMD_ . fromMatchYMD
|
-- YM (GregorianM {..}) -> [gmYear, gmMonth]
|
||||||
|
-- YMD (Gregorian {..}) -> [gYear, gMonth, gDay]
|
||||||
|
-- in T.intercalate "-" $ L.take 3 (fmap showT ys ++ L.repeat "*")
|
||||||
|
|
||||||
showYMD_ :: YMD_ -> T.Text
|
|
||||||
showYMD_ md =
|
|
||||||
T.intercalate "-" $ L.take 3 (fmap showT digits ++ L.repeat "*")
|
|
||||||
where
|
|
||||||
digits = case md of
|
|
||||||
Y_ y -> [fromIntegral y]
|
|
||||||
YM_ y m -> [fromIntegral y, m]
|
|
||||||
YMD_ y m d -> [fromIntegral y, m, d]
|
|
||||||
|
|
||||||
-- TODO there are errors that can be thrown here
|
|
||||||
showMatchVal :: MatchVal -> Maybe T.Text
|
showMatchVal :: MatchVal -> Maybe T.Text
|
||||||
showMatchVal MatchVal {mvSign = Nothing, mvNum = Nothing, mvDen = Nothing} = Nothing
|
showMatchVal = undefined
|
||||||
showMatchVal MatchVal {..} = Just $ T.concat [sign, num, ".", den]
|
|
||||||
where
|
|
||||||
sign = case mvSign of
|
|
||||||
Nothing -> "+/-"
|
|
||||||
Just s -> if s then "+" else "-"
|
|
||||||
num = maybe "*" showT mvNum
|
|
||||||
den = maybe "*" (lpadT '0' (fromIntegral mvPrec) . showT) mvDen
|
|
||||||
|
|
||||||
showMatchOther :: MatchOther -> T.Text
|
showMatchOther :: MatchOther -> T.Text
|
||||||
showMatchOther = undefined
|
showMatchOther = undefined
|
||||||
|
@ -427,21 +400,21 @@ concatEither2 a b fun = case (a, b) of
|
||||||
(Right a_, Right b_) -> Right $ fun a_ b_
|
(Right a_, Right b_) -> Right $ fun a_ b_
|
||||||
_ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b]
|
_ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b]
|
||||||
|
|
||||||
-- concatEither3 :: Either x a -> Either x b -> Either x c -> (a -> b -> c -> d) -> Either [x] d
|
concatEither3 :: Either x a -> Either x b -> Either x c -> (a -> b -> c -> d) -> Either [x] d
|
||||||
-- concatEither3 a b c fun = case (a, b, c) of
|
concatEither3 a b c fun = case (a, b, c) of
|
||||||
-- (Right a_, Right b_, Right c_) -> Right $ fun a_ b_ c_
|
(Right a_, Right b_, Right c_) -> Right $ fun a_ b_ c_
|
||||||
-- _ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b, leftToMaybe c]
|
_ -> Left $ catMaybes [leftToMaybe a, leftToMaybe b, leftToMaybe c]
|
||||||
|
|
||||||
concatEithers2 :: Either [x] a -> Either [x] b -> (a -> b -> c) -> Either [x] c
|
concatEithers2 :: Either [x] a -> Either [x] b -> (a -> b -> c) -> Either [x] c
|
||||||
concatEithers2 a b = merge . concatEither2 a b
|
concatEithers2 a b = merge . concatEither2 a b
|
||||||
|
|
||||||
-- concatEithers3
|
concatEithers3
|
||||||
-- :: Either [x] a
|
:: Either [x] a
|
||||||
-- -> Either [x] b
|
-> Either [x] b
|
||||||
-- -> Either [x] c
|
-> Either [x] c
|
||||||
-- -> (a -> b -> c -> d)
|
-> (a -> b -> c -> d)
|
||||||
-- -> Either [x] d
|
-> Either [x] d
|
||||||
-- concatEithers3 a b c = merge . concatEither3 a b c
|
concatEithers3 a b c = merge . concatEither3 a b c
|
||||||
|
|
||||||
concatEitherL :: [Either x a] -> Either [x] [a]
|
concatEitherL :: [Either x a] -> Either [x] [a]
|
||||||
concatEitherL as = case partitionEithers as of
|
concatEitherL as = case partitionEithers as of
|
||||||
|
@ -473,20 +446,17 @@ merge = first concat
|
||||||
-- random functions
|
-- random functions
|
||||||
|
|
||||||
-- when bifunctor fails...
|
-- when bifunctor fails...
|
||||||
-- thrice :: (a -> d) -> (b -> e) -> (c -> f) -> (a, b, c) -> (d, e, f)
|
thrice :: (a -> d) -> (b -> e) -> (c -> f) -> (a, b, c) -> (d, e, f)
|
||||||
-- thrice f1 f2 f3 (x, y, z) = (f1 x, f2 y, f3 z)
|
thrice f1 f2 f3 (x, y, z) = (f1 x, f2 y, f3 z)
|
||||||
|
|
||||||
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
|
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
|
||||||
uncurry3 f (a, b, c) = f a b c
|
uncurry3 f (a, b, c) = f a b c
|
||||||
|
|
||||||
-- lpad :: a -> Int -> [a] -> [a]
|
lpad :: a -> Int -> [a] -> [a]
|
||||||
-- lpad c n s = replicate (n - length s) c ++ s
|
lpad c n s = replicate (n - length s) c ++ s
|
||||||
|
|
||||||
-- rpad :: a -> Int -> [a] -> [a]
|
rpad :: a -> Int -> [a] -> [a]
|
||||||
-- rpad c n s = s ++ replicate (n - length s) c
|
rpad c n s = s ++ replicate (n - length s) c
|
||||||
|
|
||||||
lpadT :: Char -> Int -> T.Text -> T.Text
|
|
||||||
lpadT c n s = T.append (T.replicate (n - T.length s) (T.singleton c)) s
|
|
||||||
|
|
||||||
matchMaybe :: RegexContext Regex query b => query -> T.Text -> EitherErr b
|
matchMaybe :: RegexContext Regex query b => query -> T.Text -> EitherErr b
|
||||||
matchMaybe q re = first (const $ RegexError re) $ pureTry $ q =~ re
|
matchMaybe q re = first (const $ RegexError re) $ pureTry $ q =~ re
|
||||||
|
|
Loading…
Reference in New Issue