diff options
Diffstat (limited to 'Omni/Task')
| -rw-r--r-- | Omni/Task/Core.hs | 93 |
1 files changed, 76 insertions, 17 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 2b00bca..3f665da 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -57,6 +57,14 @@ data DependencyType | Related -- Soft relationship, doesn't block deriving (Show, Eq, Generic) +data TaskProgress = TaskProgress + { progressTaskId :: Text, + progressTotal :: Int, + progressCompleted :: Int, + progressPercentage :: Int + } + deriving (Show, Eq, Generic) + instance ToJSON TaskType instance FromJSON TaskType @@ -87,7 +95,11 @@ matchesId id1 id2 = T.toLower id1 == T.toLower id2 -- | Find a task by ID (case-insensitive) findTask :: Text -> [Task] -> Maybe Task -findTask tid tasks = List.find (\t -> matchesId (taskId t) tid) tasks +findTask tid = List.find (\t -> matchesId (taskId t) tid) + +instance ToJSON TaskProgress + +instance FromJSON TaskProgress -- Get the tasks database file path (use test file if TASK_TEST_MODE is set) getTasksFilePath :: IO FilePath @@ -317,6 +329,32 @@ getDependencyTree tid = do deps = filter (\t -> any (matchesId (taskId t)) depIds) allTasks in task : concatMap (collectDeps allTasks) deps +-- Get task progress +getTaskProgress :: Text -> IO TaskProgress +getTaskProgress tid = do + tasks <- loadTasks + -- Verify task exists (optional, but good for error handling) + case filter (\t -> taskId t == tid) tasks of + [] -> panic "Task not found" + _ -> do + let children = filter (\child -> taskParent child == Just tid) tasks + total = length children + completed = length <| filter (\child -> taskStatus child == Done) children + percentage = if total == 0 then 0 else (completed * 100) `div` total + pure + TaskProgress + { progressTaskId = tid, + progressTotal = total, + progressCompleted = completed, + progressPercentage = percentage + } + +-- Show task progress +showTaskProgress :: Text -> IO () +showTaskProgress tid = do + progress <- getTaskProgress tid + putText <| "Progress for " <> tid <> ": " <> T.pack (show (progressCompleted progress)) <> "/" <> T.pack (show (progressTotal progress)) <> " (" <> T.pack (show (progressPercentage progress)) <> "%)" + -- Show dependency tree for a task showDependencyTree :: Text -> IO () showDependencyTree tid = do @@ -349,7 +387,7 @@ getTaskTree maybeId = do where collectChildren :: [Task] -> Task -> [Task] collectChildren allTasks task = - let children = filter (\t -> maybe False (`matchesId` taskId task) (taskParent t)) allTasks + let children = filter (maybe False (`matchesId` taskId task) <. taskParent) allTasks in task : concatMap (collectChildren allTasks) children -- Show task tree (epic with children, or all epics if no ID given) @@ -377,7 +415,7 @@ showTaskTree maybeId = do printTreeNode' :: [Task] -> Task -> Int -> [Bool] -> IO () printTreeNode' allTasks task indent ancestry = do - let children = filter (\t -> maybe False (`matchesId` taskId task) (taskParent t)) allTasks + let children = filter (maybe False (`matchesId` taskId task) <. taskParent) allTasks -- Build tree prefix using box-drawing characters prefix = if indent == 0 @@ -426,7 +464,7 @@ printTask t = do let progressInfo = if taskType t == Epic then - let children = filter (\child -> maybe False (`matchesId` taskId t) (taskParent child)) tasks + let children = filter (maybe False (`matchesId` taskId t) <. taskParent) tasks total = length children completed = length <| filter (\child -> taskStatus child == Done) children in " [" <> T.pack (show completed) <> "/" <> T.pack (show total) <> "]" @@ -464,7 +502,7 @@ showTaskDetailed t = do -- Show epic progress if this is an epic when (taskType t == Epic) <| do - let children = filter (\child -> maybe False (`matchesId` taskId t) (taskParent child)) tasks + let children = filter (maybe False (`matchesId` taskId t) <. taskParent) tasks total = length children completed = length <| filter (\child -> taskStatus child == Done) children percentage = if total == 0 then 0 else (completed * 100) `div` total @@ -526,18 +564,31 @@ instance ToJSON TaskStats instance FromJSON TaskStats -- Get task statistics -getTaskStats :: IO TaskStats -getTaskStats = do - tasks <- loadTasks - ready <- getReadyTasks - let total = length tasks +getTaskStats :: Maybe Text -> IO TaskStats +getTaskStats maybeEpicId = do + allTasks <- loadTasks + + targetTasks <- case maybeEpicId of + Nothing -> pure allTasks + Just epicId -> + case findTask epicId allTasks of + Nothing -> panic "Epic not found" + Just task -> pure <| getAllDescendants allTasks (taskId task) + + globalReady <- getReadyTasks + let readyIds = map taskId globalReady + -- Filter ready tasks to only include those in our target set + readyCount = length <| filter (\t -> taskId t `elem` readyIds) targetTasks + + tasks = targetTasks + total = length tasks open = length <| filter (\t -> taskStatus t == Open) tasks inProg = length <| filter (\t -> taskStatus t == InProgress) tasks review = length <| filter (\t -> taskStatus t == Review) tasks done = length <| filter (\t -> taskStatus t == Done) tasks epics = length <| filter (\t -> taskType t == Epic) tasks - readyCount = length ready - blockedCount = total - readyCount - done + readyCount' = readyCount + blockedCount = total - readyCount' - done -- Count tasks by priority byPriority = [ (P0, length <| filter (\t -> taskPriority t == P0) tasks), @@ -558,18 +609,26 @@ getTaskStats = do reviewTasks = review, doneTasks = done, totalEpics = epics, - readyTasks = readyCount, + readyTasks = readyCount', blockedTasks = blockedCount, tasksByPriority = byPriority, tasksByNamespace = byNamespace } +-- Helper to get all descendants of a task (recursive) +getAllDescendants :: [Task] -> Text -> [Task] +getAllDescendants allTasks parentId = + let children = filter (maybe False (`matchesId` parentId) <. taskParent) allTasks + in children ++ concatMap (getAllDescendants allTasks <. taskId) children + -- Show task statistics (human-readable) -showTaskStats :: IO () -showTaskStats = do - stats <- getTaskStats +showTaskStats :: Maybe Text -> IO () +showTaskStats maybeEpicId = do + stats <- getTaskStats maybeEpicId putText "" - putText "Task Statistics" + case maybeEpicId of + Nothing -> putText "Task Statistics" + Just epicId -> putText <| "Task Statistics for Epic " <> epicId putText "" putText <| "Total tasks: " <> T.pack (show (totalTasks stats)) putText <| " Open: " <> T.pack (show (openTasks stats)) |
