packages feed

itemfield-1.2.3.0: examples/workreport.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE RankNTypes #-}

{- This example application uses the ItemField widget to show the status
of a number of asynchronously executing workers as they complete their
work.
-}

module Main where

import Prelude hiding (length)
import Compat
import Data.List (intercalate)
import Data.Monoid
import Data.Default
import Data.String (IsString)
import Brick
import Brick.Widgets.Center (hCenter)
import Graphics.Vty (Event(..), Key(..), mkVty, outputIface, supportsMode, Mode(..), setMode, Vty)
import Graphics.Vty.Attributes
import TextUI.ItemField
import Brick.Widgets.Border
import Lens.Micro ((^.), Lens', (.~), (&))
import Lens.Micro.TH (makeLenses)
import TextUI.ItemField.BrickHelper (BChan, newBChan, writeBChan)
import Control.Concurrent
import Control.Monad
import Control.Monad.IO.Class
import System.Random


data WorkEvent = WorkerFinished Int ItemState


data WorkerTeams n = WorkerTeams { _workers :: ItemFieldWidget n
                                 , _reqChannel :: BChan WorkEvent
                                 }

makeLenses ''WorkerTeams


setupFactory :: n -> BChan WorkEvent -> WorkerTeams n
setupFactory n =
    let teams = [ ItemGroup "Team 1" (Items 28)
                , ItemGroup "Team 2" (Items 238)
                , ItemGroup "Team 3" $ ItemGroup "Group 1" $ Items 93
                , ItemGroup "Team 3" $ ItemGroup "Group 2" $ Items 127
                , ItemGroup "Team 3" $ ItemGroup "Group 3" $ Items 56
                , ItemGroup "Team 3" $ Items 596
                , ItemGroup "Team 4" $ Items 77
                , ItemGroup "Team 5" $ Items 0
                , ItemGroup "Team 6" $ Items 3
                ]
    in WorkerTeams (ItemFieldWidget n $ newItemField teams Nothing)


workerstate :: IsString s => ItemState -> s
workerstate s = case s of
                  Good -> "done"
                  Bad -> "error"
                  Pending -> "delayed"
                  _ -> ""

-- KWQ: add a mapper that will map through and provide index, state, and [groups]
-- KWQ: add a mapper that will map through groups, with sub-mapping for index, state
showWorkers :: WorkerTeams n -> String
showWorkers teams =
    let ws = itemField $ teams^.workers
        st8s = itemst8 ws
        numSt8 st8 = length . filter (st8 ==)
        showNum = show . flip numSt8 st8s
        summary s = showNum s <> " " <> workerstate s
    in intercalate ", " [ show (length st8s) <> " workers"
                        , summary Good, summary Pending, summary Bad
                        ]


data WorkerTeamsName = WorkerTeamsName deriving (Eq, Ord, Show)


drawWorkers :: (Show n, Ord n) => WorkerTeams n -> [Widget n]
drawWorkers teams =
    [ vBox [ hCenter $ str "Workers"
           , itemFieldWidget $ teams^.workers
           , hBorder
           , str "             Movement: arrows, or '<' and '>' to jump."
           , str "Toggle item selection: space = single item, L = line, G = group, A = all"
           , str "                       right or left arrow with shift extends selection"
           , str "                       !, ~, or + selects all corresponding items"
           , str "                       s, f select only successes or failures"
           , str "                 Misc: Q/q = quit, r = run workers"
           , str ""
           , str "When run, workers will asynchronously \"do some work\" and then"
           , str "set their state to good or bad."
           ]
    ]



workEvent :: Ord n => WorkerTeams n -> BrickEvent n WorkEvent -> EventM n (Next (WorkerTeams n))
workEvent s (VtyEvent ve) = workVtyEvt ve
    where
      workVtyEvt (EvResize _ _) = continue s
      workVtyEvt (EvKey (KChar 'r') []) = runWork s
    -- workVtyEvt e@(EvKey (KChar 'r') []) = continue =<< handleEventLensed s workers (runWork (s^. e
    -- workVtyEvt e@(EvKey (KChar 'l') []) = continue =<< handleEventLensed s shelves (setBooks Pending) e
    -- workVtyEvt e@(EvKey (KChar 'm') []) = continue =<< handleEventLensed s shelves (setBooks Bad) e
      workVtyEvt (EvKey (KChar 'Q') []) = halt s
      workVtyEvt (EvKey (KChar 'q') []) = halt s
      workVtyEvt _ = continue =<< handleEventLensed s workers handleItemFieldEvent ve
workEvent s (AppEvent e@(WorkerFinished wnum result)) =
    continue =<< handleEventL s workers (workDone wnum result) e
workEvent s _ = continue s

handleEventL :: a
                  -- ^ The state value.
                  -> Lens' a b
                  -- ^ The lens to use to extract and store the target
                  -- of the event.
                  -> (e -> b -> EventM n b)
                  -- ^ The event handler.
                  -> e
                  -- ^ The event to handle.
                  -> EventM n a
handleEventL v target handleEvent ev = do
    newB <- handleEvent ev (v^.target)
    return $ v & target .~ newB

runWork :: WorkerTeams n -> EventM n (Next (WorkerTeams n))
runWork wt =
    let chan = wt^.reqChannel
        marked = getMarkedItems $ wt^.workers
        startwork c i = forkIO $ doWork c i
    in liftIO (mapM_ (startwork chan) marked) >> continue wt


doWork :: BChan WorkEvent -> Int -> IO ()
doWork reportChan myId =
    do r <- randomRIO (100000,3000000)
       threadDelay r
       s <- ([Good, Bad, Pending] !!) <$> randomRIO (0,2)
       writeBChan reportChan $ WorkerFinished myId s
       when (s == Pending) $ doWork reportChan myId

workDone :: Int -> ItemState -> t -> ItemFieldWidget n -> EventM n (ItemFieldWidget n)
workDone workerNum toState _ fieldw =
    return $ setItemState toState fieldw workerNum


workAttrs :: AttrMap
workAttrs =
    applyAttrMappings [ (itemFieldAttr, bg brightBlack)
                      , (itemFreeAttr, defAttr `withStyle` dim)
                      , (itemBadAttr, brightYellow `on` red `withStyle` bold)
                      , (itemHeaderAttr, bg blue `withStyle` underline)
                      ] $
    applyAttrMappings itemDefaultAttrs $
    setDefault (white `on` black) def


enableMouse :: Graphics.Vty.Vty -> IO ()
enableMouse v = let output = outputIface v
                in when (supportsMode output Mouse) $ setMode output Mouse True

main :: IO ()
main = do
  chan <- newBChan 100
  let allworkers = setupFactory WorkerTeamsName chan
      app = App { appDraw = drawWorkers
                , appHandleEvent = workEvent
                , appStartEvent = return
                , appAttrMap = const workAttrs
                , appChooseCursor = showFirstCursor
                }
      vty = do v <- mkVty def
               enableMouse v
               return v
  mapM_ (forkIO . doWork chan) [8 .. 12]  -- initial sample work
  putStrLn . ("Final results: " <>) . showWorkers =<< customMain vty (Just chan) app allworkers