module Development.Bake.Server.Memory(
ClientInfo(..), Memory(..),
newMemory, stateFailure,
notify, notifyAdmins, summary,
Shower(..), shower,
) where
import Development.Bake.Server.Store
import qualified Data.Map as Map
import Development.Bake.Core.Type
import Data.Time
import Development.Bake.Core.Message
import Control.DeepSeq
import qualified Data.Set as Set
import Control.Exception.Extra
import Data.Tuple.Extra
import Data.List.Extra
import Data.Maybe
import General.HTML
import Control.Monad
import General.Extra
import Data.Monoid
import Prelude
stateFailure = toState ""
data ClientInfo = ClientInfo
{ciPingTime :: UTCTime
,ciPing :: Ping
,ciAlive :: Bool
,ciTests :: Map.Map (Point, Maybe Test) Bool
} deriving (Eq,Show)
data Memory = Memory
{simulated :: Bool
,oven :: Oven State Patch Test
,prettys :: Prettys
,admins :: [Author]
,store :: Store
,fatal :: [String]
,clients :: Map.Map Client ClientInfo
,running :: [(UTCTime, Question)]
,paused :: Bool
,active :: Point
}
newMemory :: Oven State Patch Test -> Prettys -> Store -> (State, Answer) -> IO Memory
newMemory oven prettys store (state, answer) = do
store <- storeUpdate store [IUState state answer Nothing]
let ps = map fst $ sortOn (paQueued . snd) $
filter (isJust . paStart . snd) $
map (id &&& storePatch store) $ Set.toList $ storeAlive store
return $ Memory False oven prettys [] store [] Map.empty [] False (state, ps)
instance NFData Memory where
rnf Memory{..} = ()
notify :: Memory -> String -> [(Author, HTML)] -> IO (Memory -> Memory)
notify mem subject messages = do
messages <- return $ concat [(a,b) : map (,b) (admins mem) | (a,b) <- messages]
res <- try_ $ forM_ (groupSort messages) $ \(author, body) -> do
let nl = br_ <> str_ "\n"
ovenNotify (oven mem) author subject $ renderHTML $ mconcat $ intersperse (nl <> nl) $ nubOrd body
return $ \mem -> mem{fatal = ["Notification failure: " ++ show e | Left e <- [res]] ++ fatal mem}
notifyAdmins :: Memory -> String -> HTML -> IO (Memory -> Memory)
notifyAdmins mem subject message = notify mem subject $ map (,message) $ admins mem
summary :: String -> HTML
summary x | length x < 10000 = str_ x
| otherwise = str_ (take 5000 x) <> br_ <> str_ "..." <> br_ <> str_ (takeEnd 5000 x)
data Shower = Shower
{showLink :: String -> HTML -> HTML
,showPatch :: Patch -> HTML
,showExtra :: Either State Patch -> HTML
,showTest :: Maybe Test -> HTML
,showTestAt :: (State, [Patch]) -> Maybe Test -> HTML
,showQuestion :: Question -> HTML
,showClient :: Client -> HTML
,showState :: State -> HTML
,showCandidate :: (State, [Patch]) -> HTML
,showTime :: UTCTime -> HTML
,showThreads :: Int -> HTML
}
shower :: Memory -> Bool -> IO Shower
shower Memory{prettys=Prettys{..},..} argsAdmin = do
showRel <- showRelativeTime
let shwState s | s == toState "" = span__ [class_ "bad" ] $ str_ $ "invalid state"
shwState s = shwLink ("state=" ++ fromState s) $ str_ $ prettyState s
let shwPatch p = shwLink ("patch=" ++ fromPatch p) $ str_ $ prettyPatch p
return $ Shower
{showLink = shwLink
,showPatch = shwPatch
,showState = shwState
,showCandidate = \(s,ps) -> do
shwState s
when (not $ null ps) $ str_ " plus " <> commas_ (map shwPatch ps)
,showExtra = \e -> raw_ $ maybe "" fst $ storeExtra store e
,showClient = \c -> shwLink ("client=" ++ url_ (fromClient c)) $ str_ $ fromClient c
,showTest = f Nothing Nothing []
,showTestAt = \(s,ps) -> f Nothing (Just s) ps
,showQuestion = \Question{..} -> f (Just qClient) (Just $ fst qCandidate) (snd qCandidate) qTest
,showTime = \x -> span__ [class_ "nobr"] $ str_ $ showUTCTime "%H:%M" x ++ " (" ++ showRel x ++ ")"
,showThreads = \i -> str_ $ show i ++ " thread" ++ ['s' | i /= 1]
}
where
shwLink url = a__ [href_ $ (if argsAdmin then "?admin=&" else "?") ++ url]
f c s ps t =
shwLink (intercalate "&" parts) $ str_ $
maybe "Preparing" prettyTest t
where parts = ["client=" ++ url_ (fromClient c) | Just c <- [c]] ++
["state=" ++ url_ (fromState s) | Just s <- [s]] ++
["patch=" ++ url_ (fromPatch p) | p <- ps] ++
["test=" ++ url_ (maybe "" fromTest t)]