ttask-0.0.0.2: app/Main.hs
{-# LANGUAGE DataKinds #-}
module Main where
import Control.Monad
import Control.Monad.IO.Class
import Data.Maybe
import Data.Time
import Data.TTask
import Options.Declarative
main :: IO ()
main = do
initDirectory
run_ $ Group "Task management tool for yourself is inspired by scrum."
[ subCmd "project" cmdProject
, subCmd "add" cmdAdd
, subCmd "delete" cmdDelete
, subCmd "move" cmdMove
, subCmd "swap" cmdSwap
, subCmd "active" cmdActive
, subCmd "pbl" cmdShowPbl
, subCmd "sprint" cmdShowSprints
, subCmd "story" cmdShowStorys
, subCmd "run" cmdRun
, subCmd "finish" cmdFinish
, subCmd "wait" cmdWait
, subCmd "nota" cmdNotAchieved
, subCmd "reject" cmdReject
, subCmd "log" cmdLog
]
----
-- project
cmdProject
:: Flag "i" '["ID"] "Project id" "Switch project id" (Def "" String)
-> Cmd "Switch other project. Or list all projects when project id is abbreviated." ()
cmdProject i = liftIO $ do
case get i of
"" -> listupAllProjects
j -> do
res <- setActiveProject j
when (res == Failure)
. putStrLn $ failureMsgWithIdS "Set active project" "project" j
listupAllProjects :: IO ()
listupAllProjects = do
mpn <- activeProjectName
pjs <- findProjects
case mpn of
Just pn -> do
when (notElem pn pjs)
$ putStrLn ("Warning : active project `" ++ pn ++ "` is not found. Try `ttask project -i [project name]`")
listProjects pn pjs
Nothing -> do
putStrLn "Warning : Get active project failed. Try `ttask project -i [project name]`"
where
listProjects :: String -> [String] -> IO ()
listProjects pn = mapM_ $ putStrLn . fmtRow pn
fmtRow :: String -> String -> String
fmtRow apn lpn = (if apn == lpn then (" ["++).(++"]") else (" "++)) lpn
----
-- add
cmdAdd :: Group
cmdAdd = Group "Add contents to active project"
[ subCmd "project" cmdAddProject
, subCmd "sprint" cmdAddSprint
, subCmd "story" cmdAddStory
, subCmd "task" cmdAddTask
]
cmdAddProject
:: Arg "ID" String
-> Flag "n" '["Name"] "Project name" "New projects explanatory name" String
-> Cmd "Add new project and switch to it" ()
cmdAddProject j n = liftIO $ do
initProjectFile (get j) (get n)
cmdAddSprint
:: Arg "DESCRIPTION" String
-> Cmd "Add sprint to active project" ()
cmdAddSprint description = liftIO . modifyActivePjSimple
(addNewSprint $ get description) $ failureMsg "Add sprint"
cmdAddStory
:: Arg "DESCRIPTION" String
-> Flag "i" '["ID"] "Sprint id" "Target Sprint Id" (Def "-1" Int)
-> Cmd "Add story to product backlog, Or designated sprint." ()
cmdAddStory description i = liftIO . modifyActiveProject
(addStory (get i) $ get description) (failureMsg "Add story")
$ failureMsgWithId "Add story" "sprint" (get i)
where
addStory :: Id -> String -> Project -> IO Project
addStory i description pj = case i of
-1 -> addNewStoryToPbl description pj
_ -> addNewStoryToSprints i description pj
cmdAddTask
:: Arg "DESCRIPTION" String
-> Flag "i" '["ID"] "Story id" "Target Story Id" Int
-> Flag "p" '["POINT"] "Estimated work point" "Estimated work point like 1, 2, 3, 5, 8 ..." Int
-> Cmd "Add task to active project" ()
cmdAddTask description i point = liftIO . modifyActiveProject
(addNewTask (get point) (get i) (get description)) (failureMsg "Add task")
$ failureMsgWithId "Add task" "story" (get i)
----
-- delete
cmdDelete :: Group
cmdDelete = Group "Delete contents from active project"
[ subCmd "sprint" cmdDeleteSprint
, subCmd "story" cmdDeleteStory
, subCmd "task" cmdDeleteTask
]
cmdDeleteSprint
:: Flag "i" '["ID"] "Sprint id" "Target Sprint Id" Int
-> Cmd "Delete sprint from active project" ()
cmdDeleteSprint i = liftIO . modifyActivePjSimple
(return . deleteSprint (get i)) $ failureMsg "Delete sprint"
cmdDeleteStory
:: Flag "i" '["ID"] "Story id" "Target Story Id" Int
-> Cmd "Delete sprint from active project" ()
cmdDeleteStory i = liftIO . modifyActivePjSimple
(return . deleteStory (get i)) $ failureMsg "Delete story"
cmdDeleteTask
:: Flag "i" '["ID"] "Task id" "Target Task Id" Int
-> Cmd "Delete sprint from active project" ()
cmdDeleteTask i = liftIO . modifyActivePjSimple
(return . deleteTask (get i)) $ failureMsg "Delete task"
----
-- swap
cmdSwap :: Group
cmdSwap = Group "Swap contents of active project"
[ subCmd "sprint" cmdSwapSprint
, subCmd "story" cmdSwapStory
, subCmd "task" cmdSwapTask
]
cmdSwapSprint
:: Flag "f" '["ID-FROM"] "Sprint id From" "Target Story Id : From" Int
-> Flag "t" '["ID-TO"] "Sprint id To" "Target Story Id : To" Int
-> Cmd "Swap sprint of active project" ()
cmdSwapSprint i j = liftIO . modifyActivePjSimple
(return . swapSprint (get i) (get j)) $ failureMsg "Swap sprint"
cmdSwapStory
:: Flag "f" '["ID-FROM"] "Story id From" "Target Story Id : From" Int
-> Flag "t" '["ID-TO"] "Story id To" "Target Story Id : To" Int
-> Cmd "Swap story of active project" ()
cmdSwapStory i j = liftIO . modifyActivePjSimple
(return . swapStory (get i) (get j)) $ failureMsg "Swap story"
cmdSwapTask
:: Flag "f" '["ID-FROM"] "Task id From" "Target Story Id : From" Int
-> Flag "t" '["ID-TO"] "Task id To" "Target Story Id : To" Int
-> Cmd "Swap task of active project" ()
cmdSwapTask i j = liftIO . modifyActivePjSimple
(return . swapTask (get i) (get j)) $ failureMsg "Swap task"
----
-- move
cmdMove :: Group
cmdMove = Group "Move contents of active project"
[ subCmd "story" cmdMoveStory
, subCmd "task" cmdMoveTask
]
cmdMoveStory
:: Flag "i" '["ID"] "Story id" "Target Story Id" Int
-> Flag "t" '["SPRINT-ID"] "Sprint id" "Destination Srint Id" (Def "-1" Int)
-> Cmd "Move story of active project" ()
cmdMoveStory i s = liftIO . modifyActivePjSimple
(return . moveStory (get i) (get s)) $ failureMsg "Move story"
where
moveStory :: Id -> Id -> Project -> Project
moveStory i s pj = fromMaybe pj $ case s of
-1 -> moveStoryToPbl i pj
_ -> moveStoryToSprints i s pj
cmdMoveTask
:: Flag "i" '["ID"] "Story id" "Target Story Id" Int
-> Flag "t" '["STORY-ID"] "Story id" "Destination Story Id" Int
-> Cmd "Move story of active project" ()
cmdMoveTask i s = liftIO . modifyActivePjSimple
(\pj -> return . fromMaybe pj $ moveTask (get i) (get s) pj) $ failureMsg "Move task"
----
-- update
cmdRun :: Group
cmdRun = Group "Update contents status to `Running`"
[ subCmd "sprint" (cmdUpdStatusSprint StatusRunning)
, subCmd "story" (cmdUpdStatusStory StatusRunning)
, subCmd "task" (cmdUpdStatusTask StatusRunning)
]
cmdFinish :: Group
cmdFinish = Group "Update contents status to `Finish`"
[ subCmd "sprint" (cmdUpdStatusSprint StatusFinished)
, subCmd "story" (cmdUpdStatusStory StatusFinished)
, subCmd "task" (cmdUpdStatusTask StatusFinished)
]
cmdWait :: Group
cmdWait = Group "Update contents status to `Wait`"
[ subCmd "sprint" (cmdUpdStatusSprint StatusWait)
, subCmd "story" (cmdUpdStatusStory StatusWait)
, subCmd "task" (cmdUpdStatusTask StatusWait)
]
cmdNotAchieved :: Group
cmdNotAchieved = Group "Update contents status to `Not Achieved`"
[ subCmd "sprint" (cmdUpdStatusSprint StatusNotAchieved)
, subCmd "story" (cmdUpdStatusStory StatusNotAchieved)
, subCmd "task" (cmdUpdStatusTask StatusNotAchieved)
]
cmdReject :: Group
cmdReject = Group "Update contents status to `Rejected`"
[ subCmd "sprint" (cmdUpdStatusSprint StatusReject)
, subCmd "story" (cmdUpdStatusStory StatusReject)
, subCmd "task" (cmdUpdStatusTask StatusReject)
]
cmdUpdStatusSprint :: (LocalTime -> TStatusRecord)
-> Flag "i" '["ID"] "Sprint id" "Target Sprint Id" Int
-> Cmd "Update sprint of active project" ()
cmdUpdStatusSprint s i = liftIO $ do
lt <- getLocalTime
modifyActivePjSimple
(return . updateSprintStatus (get i) (s lt)) $ failureMsg "Update sprint"
cmdUpdStatusStory :: (LocalTime -> TStatusRecord)
-> Flag "i" '["ID"] "Story id" "Target Story Id" Int
-> Cmd "Update story of active project" ()
cmdUpdStatusStory s i = liftIO $ do
lt <- getLocalTime
modifyActivePjSimple
(return . updateStoryStatus (get i) (s lt)) $ failureMsg "Update story"
cmdUpdStatusTask :: (LocalTime -> TStatusRecord)
-> Flag "i" '["ID"] "Task id" "Target Task Id" Int
-> Cmd "Update task of active project" ()
cmdUpdStatusTask s i = liftIO $ do
lt <- getLocalTime
modifyActivePjSimple
(return . updateTaskStatus (get i) (s lt)) $ failureMsg "Update task"
----
-- show
cmdActive
:: Cmd "Show active projects information" ()
cmdActive = liftIO $ do
pn <- activeProjectName
case pn of
Just n ->
execToActiveProject (putStrLn . ppActive n) $ failureMsg "Show project"
Nothing -> putStrLn $ failureMsg "Show project"
cmdShowPbl
:: Cmd "List all pbl stories" ()
cmdShowPbl = liftIO $ do
execToActiveProject (putStrLn . ppProjectPbl) $ failureMsg "Show product backlog"
cmdShowSprints
:: Flag "i" '["ID"] "Sprint id" "Target Sprint Id" (Def "-1" Int)
-> Flag "s" '["SIMPLE"] "show simple" "Show simple mode when sprint id is designated." Bool
-> Cmd "List all sprint, Or show sprints detail when project id is not abbreviated." ()
cmdShowSprints i s = liftIO $ do
execToActiveProject (showSprint $ get i) $ failureMsg "Show sprint(s)"
where
showSprint :: Id -> Project -> IO ()
showSprint i pj =
showContnt i pj ppProjectSprintList (if get s then ppProjectSprint else ppProjectSprintDetail)
("Error : Show sprint(s) Filure. Check target story (id = " ++ show i ++ ") is exist")
cmdShowStorys
:: Flag "i" '["ID"] "Story id" "Target Story Id" Int
-> Cmd "Show target story information and all tasks" ()
cmdShowStorys i = liftIO $ do
execToActiveProject (showStory $ get i) $ failureMsg "Show story"
where
showStory :: Id -> Project -> IO ()
showStory i pj = showContnt i pj (const "") ppProjectStory
("Error : Show story Filure. Check target story (id = " ++ show i ++ ") is exist")
----
-- log
cmdLog
:: Flag "i" '["ID"] "Sprint id" "Target Sprint Id" Int
-> Cmd "Analysis sprint log" ()
cmdLog i = liftIO $ do
execToActiveProject (showSprintLog $ get i) $ failureMsg "Show sprint log"
where
showSprintLog :: Id -> Project -> IO ()
showSprintLog i pj = showContnt i pj (const "") ppProjectSprintLog
("Error : Show sprint log Filure. Check target sprint (id = " ++ show i ++ ") is exist")
----
-- util
modifyActivePjSimple :: (Project -> IO Project) -> String -> IO ()
modifyActivePjSimple f err = modifyActiveProject f err err
modifyActiveProject :: (Project -> IO Project) -> String -> String -> IO ()
modifyActiveProject f err1 err2 = do
mpj <- readActiveProject
case mpj of
Just pj -> do
res <- writeActiveProject =<< f pj
when (res == Failure)
$ putStrLn err2
Nothing -> putStrLn err1
execToActiveProject :: (Project -> IO ()) -> String -> IO ()
execToActiveProject f err = do
mpj <- readActiveProject
case mpj of
Just pj -> f pj
Nothing -> putStrLn err
showContnt :: Id -> Project -> (Project -> String) -> (Id -> Project -> Maybe String) -> String -> IO ()
showContnt i pj f g err = case i of
-1 -> putStrLn (f pj)
_ ->
let res = g i pj
in case res of
Just r -> putStrLn r
Nothing -> putStrLn err
----
failureMsg :: String -> String
failureMsg s
= "Error : " ++ s ++ " Failure. Check active project is available as `ttask project`"
failureMsgWithId :: String -> String -> Id -> String
failureMsgWithId s t i = failureMsgWithIdS s t $ show i
failureMsgWithIdS :: String -> String -> String -> String
failureMsgWithIdS s t i
= "Error : " ++ s ++ " Failure. Check " ++ t ++ " `" ++ show i ++ "` exist"