module OrgStat.Helpers
( convertRange
, resolveInputOrg
, resolveScope
, resolveReport
, resolveOutput
) where
import Universum
import Control.Lens (at, views, (.=))
import qualified Data.List.NonEmpty as NE
import Data.Time (LocalTime (..), TimeOfDay (..), addDays,
getZonedTime, toGregorian,
zonedTimeToLocalTime)
import Data.Time.Calendar (addGregorianMonthsRollOver)
import Data.Time.Calendar.WeekDate (toWeekDate)
import OrgStat.Ast (Org (..), cutFromTo, mergeClocks, orgTitle)
import OrgStat.Config (ConfDate (..), ConfOutput (..),
ConfRange (..), ConfReport (..),
ConfScope (..), ConfigException (..),
OrgStatConfig (..))
import OrgStat.IO (readOrgFile)
import OrgStat.Scope (applyModifiers)
import OrgStat.WorkMonad (WorkM, wcConfig, wdReadFiles,
wdResolvedReports, wdResolvedScopes)
convertRange :: (MonadIO m) => ConfRange -> m (LocalTime, LocalTime)
convertRange range = case range of
(ConfFromTo f t) -> (,) <$> fromConfDate f <*> fromConfDate t
(ConfBlockDay i) | i < 0 -> error $ "ConfBlockDay i is <0: " <> show i
(ConfBlockDay 0) -> (,) <$> (localFromDay <$> startOfDay) <*> curTime
(ConfBlockDay i) -> do
d <- (negate (i 1) `addDays`) <$> startOfDay
pure $ localFromDayPair ((negate 1) `addDays` d, d)
(ConfBlockWeek i) | i < 0 -> error $ "ConfBlockWeek i is <0: " <> show i
(ConfBlockWeek 0) -> (,) <$> (localFromDay <$> startOfWeek) <*> curTime
(ConfBlockWeek i) -> do
d <- (negate (i 1) `addWeeks`) <$> startOfWeek
pure $ localFromDayPair ((negate 1) `addWeeks` d, d)
(ConfBlockMonth i) | i < 0 -> error $ "ConfBlockMonth i is <0: " <> show i
(ConfBlockMonth 0) -> (,) <$> (localFromDay <$> startOfMonth) <*> curTime
(ConfBlockMonth i) -> do
d <- addGregorianMonthsRollOver (negate $ i1) <$> startOfMonth
pure $ localFromDayPair ((negate 1) `addGregorianMonthsRollOver` d, d)
where
localFromDay d = LocalTime d $ TimeOfDay 0 0 0
localFromDayPair = bimap localFromDay localFromDay
curTime = liftIO $ zonedTimeToLocalTime <$> getZonedTime
curDay = localDay <$> curTime
addWeeks i d = (i*7) `addDays` d
startOfDay = curDay
startOfWeek = do
d <- curDay
let weekDay = pred $ view _3 $ toWeekDate d
pure $ fromIntegral (negate weekDay) `addDays` d
startOfMonth = do
d <- curDay
let monthDate = pred $ view _3 $ toGregorian d
pure $ fromIntegral (negate monthDate) `addDays` d
fromConfDate ConfNow = curTime
fromConfDate (ConfLocal x) = pure x
resolveInputOrg :: FilePath -> WorkM (Text, Org)
resolveInputOrg fp = use (wdReadFiles . at fp) >>= \case
Just x -> pure x
Nothing -> do
todoKeywords <- views wcConfig confTodoKeywords
o <- readOrgFile todoKeywords fp
wdReadFiles . at fp .= Just o
pure o
resolveScope :: Text -> WorkM Org
resolveScope scopeName = use (wdResolvedScopes . at scopeName) >>= \case
Just x -> pure x
Nothing -> constructScope
where
constructScope = do
let filterScopes = filter (\x -> csName x == scopeName)
views wcConfig (filterScopes . confScopes) >>= \case
[] ->
throwM $ ConfigLogicException $
"Scope "<> scopeName <> " is not declared"
[sc] -> resolveFoundScope sc
scopes ->
throwM $ ConfigLogicException $
"Multple scopes with name "<> scopeName <>
" are declared " <> show scopes
resolveFoundScope ConfScope{..} = do
orgs <- NE.toList <$> forM csPaths resolveInputOrg
let orgTop = Org "/" [] [] $ map (\(fn,o) -> o & orgTitle .~ fn) orgs
wdResolvedScopes . at scopeName .= Just orgTop
pure orgTop
resolveReport :: Text -> WorkM Org
resolveReport reportName = use (wdResolvedReports . at reportName) >>= \case
Just x -> pure x
Nothing -> constructReport
where
constructReport = do
let filterReports = filter (\x -> crName x == reportName)
views wcConfig (filterReports . confReports) >>= \case
[] ->
throwM $ ConfigLogicException $
"Report " <> reportName <> " is not declared"
[rep] -> resolveFoundReport rep
reports ->
throwM $ ConfigLogicException $
"Multple reports with name "<> reportName <>
" are declared " <> show reports
resolveFoundReport ConfReport{..} = do
orgTop <- resolveScope crScope
fromto <- convertRange crRange
withModifiers <-
either throwM pure $
applyModifiers orgTop crModifiers
let finalOrg = cutFromTo fromto $ mergeClocks withModifiers
wdResolvedReports . at reportName .= Just finalOrg
pure finalOrg
resolveOutput :: Text -> WorkM ConfOutput
resolveOutput outputName =
views wcConfig (filterOutputs . confOutputs) >>= \case
[] ->
throwM $ ConfigLogicException $
"Report " <> outputName <> " is not declared"
[rep] -> pure rep
reports ->
throwM $ ConfigLogicException $
"Multple outputs with name "<> outputName <>
" are declared " <> show reports
where
filterOutputs = filter (\x -> coName x == outputName)