{-# LANGUAGE ScopedTypeVariables, PatternGuards #-}
{-# OPTIONS -fno-warn-orphans #-}
module BuildBox.Data.Schedule
(
second, minute, hour, day
, When (..)
, WhenModifier (..)
, EventName
, Event (..)
, earliestEventToStartAt
, eventCouldStartAt
, Schedule (..)
, makeSchedule
, lookupEventOfSchedule
, lookupCommandOfSchedule
, adjustEventOfSchedule
, eventsOfSchedule)
where
import Data.Time
import Data.List
import Data.Function
import Data.Maybe
import Control.Monad
import Data.Map (Map)
import qualified Data.Map as Map
instance Read NominalDiffTime where
readsPrec n str
= let [(secs :: Double, rest)] = readsPrec n str
in case rest of
's' : rest' -> [(fromRational $ toRational secs, rest')]
_ -> []
second, minute, hour, day :: NominalDiffTime
second = 1
minute = 60
hour = 60 * minute
day = 24 * hour
data When
= Always
| Never
| Every NominalDiffTime
| After NominalDiffTime
| Daily TimeOfDay
deriving (Read, Show, Eq)
data WhenModifier
= Immediate
| WaitUntil UTCTime
deriving (Read, Show, Eq)
type EventName = String
data Event
= Event
{
eventName :: EventName
, eventWhen :: When
, eventWhenModifier :: Maybe WhenModifier
, eventLastStarted :: Maybe UTCTime
, eventLastEnded :: Maybe UTCTime }
deriving (Read, Show, Eq)
earliestEventToStartAt :: UTCTime -> [Event] -> Maybe Event
earliestEventToStartAt curTime events
= let eventsStartable = filter (eventCouldStartAt curTime) events
eventsSorted = sortBy (compare `on` eventLastStarted) eventsStartable
in listToMaybe eventsSorted
eventCouldStartAt :: UTCTime -> Event -> Bool
eventCouldStartAt curTime event
| Nothing <- eventLastStarted event
, Nothing <- eventLastEnded event
, Just Immediate <- eventWhenModifier event
= True
| Just lastStarted <- eventLastStarted event
, Just lastEnded <- eventLastEnded event
, lastEnded < lastStarted
= False
| Just (WaitUntil waitTime) <- eventWhenModifier event
, curTime < waitTime
= False
| otherwise
= case eventWhen event of
Always -> True
Never -> False
Every diffTime
-> maybe True
(\lastTime -> (curTime `diffUTCTime` lastTime ) > diffTime)
(eventLastStarted event)
After diffTime
-> maybe True
(\lastTime -> (curTime `diffUTCTime` lastTime ) > diffTime)
(eventLastEnded event)
Daily timeOfDay
| Just lastStarted <- eventLastStarted event
, (curTime `diffUTCTime` lastStarted) < day
-> False
| otherwise
-> let
startTimeToday
= curTime
{ utctDayTime = timeOfDayToTime timeOfDay }
in curTime > startTimeToday
data Schedule cmd
= Schedule (Map EventName (Event, cmd))
eventsOfSchedule :: Schedule cmd -> [Event]
eventsOfSchedule (Schedule sched)
= map fst $ Map.elems sched
makeSchedule :: [(EventName, When, Maybe WhenModifier, cmd)] -> Schedule cmd
makeSchedule tuples
= let makeSched (name, whn, mMod, cmd)
= (name, (Event name whn mMod Nothing Nothing, cmd))
in Schedule $ Map.fromList $ map makeSched tuples
lookupEventOfSchedule :: EventName -> Schedule cmd -> Maybe Event
lookupEventOfSchedule name (Schedule sched)
= liftM fst $ Map.lookup name sched
lookupCommandOfSchedule :: EventName -> Schedule cmd -> Maybe cmd
lookupCommandOfSchedule name (Schedule sched)
= liftM snd $ Map.lookup name sched
adjustEventOfSchedule :: Event -> Schedule cmd -> Schedule cmd
adjustEventOfSchedule event (Schedule sched)
= Schedule
$ Map.adjust
(\(_, build) -> (event, build))
(eventName event)
sched