{-# LANGUAGE DeriveDataTypeable #-} module Main where import System.Console.CmdArgs import Data.Data import Data.List (intercalate) import Data.Maybe (fromMaybe, isNothing) import Control.Applicative( (<*>) ) import Data.Monoid (mconcat) import Trajectory.Private.Config (withKey) import Trajectory.API (getStories, Story(..), Iteration(..)) main = do args <- cmdArgs lsStoryArgDefinition withKey (profileName args) $ \key -> do stories <- getStories key (accountName args) (projectName args) putStrLn $ either (\error -> "Error: " ++ show error) (handle args) stories handle :: LsStoryArg -> ([Story],[Iteration]) -> String handle args (stories,iterations) = let filters = buildFilters args iterations renderer = buildRenderer args in renderer $ filters `pipe` stories where pipe :: [ a -> a ] -> (a -> a) pipe = foldr (.) id buildRenderer args [] = "" buildRenderer args stories | onlyNext args && null nonMilestones = "" | detailedOutput args && onlyNext args = head $ map detailedFormatter nonMilestones | detailedOutput args = intercalate "\n\n" $ map detailedFormatter stories | onlyNext args = head $ map simpleFormatter nonMilestones | otherwise = intercalate "\n" $ map simpleFormatter stories where nonMilestones = skipMilestones stories skipMilestones = filter $ ("Milestone" /=) . storyTaskType detailedFormatter story = title ++ "\n" ++ origins ++ "\n" ++ overall where title = milestonePrefix story ++ storyTitle story overall = intercalate "\t" $ [ "id: " ++ (show $ storyId story) ,"points: " ++ (show $ storyPoints story) ,"comments: " ++ (show $ storyCommentsCount story) ] origins = intercalate "\t" $ [ (fromMaybe "unassigned" $ storyAssigneeName story) ,(maybe "free-floating" ("idea: " ++) $ storyIdeaSubject story) ] simpleFormatter story = milestonePrefix story ++ storyTitle story milestonePrefix story | storyTaskType story == "Milestone" = "^^^ " | otherwise = "" buildFilters :: LsStoryArg -> [Iteration] -> [ [Story] -> [Story] ] buildFilters args iterations = filters <*> [args] where filters = [ ideaFilter ,beforeMilestoneFilter ,estimationFilter ,assignmentFilter ,stateFilter ,neededFilter ,iterationFilter iterations ,deletionsFilter ] ideaFilter args | null summaries && not limitedToNullIdeas = id | null summaries && limitedToNullIdeas = filter (isNothing . storyIdeaSubject) | otherwise = filter ideaElements where summaries = ideaSummaries args limitedToNullIdeas = showNullIdeas args ideaElements story = maybe limitedToNullIdeas (\summary -> summary `elem` summaries) (storyIdeaSubject story) beforeMilestoneFilter args | isNothing $ beforeMilestone args = id | otherwise = takeWhile notTheMilestone where (Just milestoneTitle) = beforeMilestone args notTheMilestone story = (storyTaskType story /= "Milestone") && (storyTitle story /= milestoneTitle) estimationFilter args | null (showEstimate args) && not (showUnestimated args) = id | null $ showEstimate args = filter (\story -> (storyPoints story) == 0) | not $ showUnestimated args = filter (\story -> (storyPoints story) `elem` (showEstimate args)) | otherwise = filter (\story -> (storyPoints story) `elem` [0]++(showEstimate args)) assignmentFilter args | null assignments && not limitedToUnassigned = id | null assignments = filter (isNothing . storyAssigneeName) | otherwise = filter assignmentElements where limitedToUnassigned = showUnassigned args assignments = showAssigned args assignmentElements story = maybe limitedToUnassigned (\assignment -> assignment `elem` assignments) (storyAssigneeName story) stateFilter args = filter (\story -> (storyState story) `elem` states) where allStates = ["unstarted", "started", "finished", "done", "accepted", "rejected"] stateBits = [(showUnstarted args) ,(showStarted args) ,(showFinished args) ,(showDone args) ,(showAccepted args) ,(showRejected args)] states = if or stateBits then map fst $ filter snd $ zip allStates stateBits else allStates neededFilter args | showDesignNeeded args && showDevelopmentNeeded args = filter (\story -> storyDesignNeeded story && storyDevelopmentNeeded story) | showDesignNeeded args = filter storyDesignNeeded | showDevelopmentNeeded args = filter storyDevelopmentNeeded | otherwise = id iterationFilter iterations args | showAllIterations args = id | showCurrentIteration args && (present $ showIteration args) = filter (storyInIterations $ [currentIteration] ++ desiredIterations) | present $ showIteration args = filter (storyInIterations desiredIterations) | otherwise = filter (storyInIterations [currentIteration]) where currentIteration = head $ filter iterationIsCurrent iterations present = not . null desiredIterations = filter (\iteration -> (iterationStartsOn iteration) `elem` (showIteration args) ) iterations storyInIterations its story = (storyIterationId story) `elem` (map iterationId its) deletionsFilter args = filter (not . storyDeleted) data LsStoryArg = LsStoryArg { profileName :: String ,ideaSummaries :: [String] ,showNullIdeas :: Bool ,beforeMilestone :: Maybe String ,showEstimate :: [Int] ,showUnestimated :: Bool ,showAssigned :: [String] ,showUnassigned :: Bool ,showDesignNeeded :: Bool ,showDevelopmentNeeded :: Bool ,showUnstarted :: Bool ,showStarted :: Bool ,showFinished :: Bool ,showDone :: Bool ,showAccepted :: Bool ,showRejected :: Bool ,showAllIterations :: Bool ,showCurrentIteration :: Bool ,showIteration :: [String] ,detailedOutput :: Bool ,onlyNext :: Bool ,accountName :: String ,projectName :: String } deriving (Show, Data, Typeable) lsStoryArgDefinition = LsStoryArg { profileName = "default" &= explicit &= name "profile" &= groupname "Common flags" &= help "The profile name to use [default]" ,ideaSummaries = def &= explicit &= name "idea" &= groupname "Filtering" &= help "Stories matching this idea" ,showNullIdeas = def &= explicit &= name "unset-idea" &= help "Stories without an idea" ,beforeMilestone = def &= explicit &= name "before-milestone" &= help "Stories before the given milestone" ,showUnestimated = def &= explicit &= name "unestimated" &= help "Unestimated stories" ,showEstimate = def &= explicit &= name "estimate" &= help "Stories with the given estimate" ,showUnassigned = def &= explicit &= name "unassigned" &= help "Unassigned stories" ,showAssigned = def &= explicit &= name "assigned" &= help "Stories assigned to the named person" ,showDesignNeeded = def &= explicit &= name "design" &= help "Stories that need design" ,showDevelopmentNeeded = def &= explicit &= name "development" &= help "Stories that need development" ,showUnstarted = def &= explicit &= name "unstarted" &= groupname "Filtering on state" &= help "Unstarted stories" ,showStarted = def &= explicit &= name "started" &= help "Started stories" ,showFinished = def &= explicit &= name "finished" &= help "Finished stories" ,showDone = def &= explicit &= name "done" &= help "Stories in the done state" ,showAccepted = def &= explicit &= name "accepted" &= help "Stories which have been accepted" ,showRejected = def &= explicit &= name "rejected" &= help "Stories which have been rejected" ,showAllIterations = def &= explicit &= name "all-iterations" &= groupname "Iteration filtering" &= help "Stories from all iterations" ,showCurrentIteration = def &= explicit &= name "current-iteration" &= help "Stories from the current iteration" ,showIteration = def &= explicit &= name "iteration" &= help "Stories from the named iteration" ,onlyNext = def &= explicit &= name "next" &= groupname "Formatting" &= help "Only show the next matching story" ,detailedOutput = def &= explicit &= name "long" &= name "l" &= groupname "Formatting" &= help "Show story details" ,accountName = def &= argPos 0 ,projectName = def &= argPos 1 } &= program "lsstory" &= helpArg [groupname "Common flags"]