diff options
Diffstat (limited to 'Omni/Task/Core.hs')
| -rw-r--r-- | Omni/Task/Core.hs | 62 |
1 files changed, 35 insertions, 27 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index 622e8de..3f665da 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -89,6 +89,14 @@ instance ToJSON Task instance FromJSON Task +-- | Case-insensitive ID comparison +matchesId :: Text -> Text -> Bool +matchesId id1 id2 = T.toLower id1 == T.toLower id2 + +-- | Find a task by ID (case-insensitive) +findTask :: Text -> [Task] -> Maybe Task +findTask tid = List.find (\t -> matchesId (taskId t) tid) + instance ToJSON TaskProgress instance FromJSON TaskProgress @@ -258,7 +266,7 @@ updateTaskStatus tid newStatus = do now <- getCurrentTime let updatedTasks = map updateIfMatch tasks updateIfMatch t = - if taskId t == tid + if matchesId (taskId t) tid then t {taskStatus = newStatus, taskUpdatedAt = now} else t -- Rewrite the entire file (simple approach for MVP) @@ -311,14 +319,14 @@ getReadyTasks = do getDependencyTree :: Text -> IO [Task] getDependencyTree tid = do tasks <- loadTasks - case filter (\t -> taskId t == tid) tasks of - [] -> pure [] - (task : _) -> pure <| collectDeps tasks task + case findTask tid tasks of + Nothing -> pure [] + Just task -> pure <| collectDeps tasks task where collectDeps :: [Task] -> Task -> [Task] collectDeps allTasks task = let depIds = map depId (taskDependencies task) - deps = filter (\t -> taskId t `elem` depIds) allTasks + deps = filter (\t -> any (matchesId (taskId t)) depIds) allTasks in task : concatMap (collectDeps allTasks) deps -- Get task progress @@ -351,15 +359,15 @@ showTaskProgress tid = do showDependencyTree :: Text -> IO () showDependencyTree tid = do tasks <- loadTasks - case filter (\t -> taskId t == tid) tasks of - [] -> putText "Task not found" - (task : _) -> printTree tasks task 0 + case findTask tid tasks of + Nothing -> putText "Task not found" + Just task -> printTree tasks task 0 where printTree :: [Task] -> Task -> Int -> IO () printTree allTasks task indent = do putText <| T.pack (replicate (indent * 2) ' ') <> taskId task <> ": " <> taskTitle task let depIds = map depId (taskDependencies task) - deps = filter (\t -> taskId t `elem` depIds) allTasks + deps = filter (\t -> any (matchesId (taskId t)) depIds) allTasks traverse_ (\dep -> printTree allTasks dep (indent + 1)) deps -- Get task tree (returns tasks hierarchically) @@ -373,13 +381,13 @@ getTaskTree maybeId = do in pure <| concatMap (collectChildren tasks) epics Just tid -> do -- Return specific task/epic with its children - case filter (\t -> taskId t == tid) tasks of - [] -> pure [] - (task : _) -> pure <| collectChildren tasks task + case findTask tid tasks of + Nothing -> pure [] + Just task -> pure <| collectChildren tasks task where collectChildren :: [Task] -> Task -> [Task] collectChildren allTasks task = - let children = filter (\t -> taskParent t == Just (taskId task)) 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) @@ -395,9 +403,9 @@ showTaskTree maybeId = do else traverse_ (printEpicTree tasks) epics Just tid -> do -- Show specific task/epic with its children - case filter (\t -> taskId t == tid) tasks of - [] -> putText "Task not found" - (task : _) -> printEpicTree tasks task + case findTask tid tasks of + Nothing -> putText "Task not found" + Just task -> printEpicTree tasks task where printEpicTree :: [Task] -> Task -> IO () printEpicTree allTasks task = printTreeNode allTasks task 0 @@ -407,7 +415,7 @@ showTaskTree maybeId = do printTreeNode' :: [Task] -> Task -> Int -> [Bool] -> IO () printTreeNode' allTasks task indent ancestry = do - let children = filter (\t -> taskParent t == Just (taskId task)) allTasks + let children = filter (maybe False (`matchesId` taskId task) <. taskParent) allTasks -- Build tree prefix using box-drawing characters prefix = if indent == 0 @@ -456,7 +464,7 @@ printTask t = do let progressInfo = if taskType t == Epic then - let children = filter (\child -> taskParent child == Just (taskId t)) 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) <> "]" @@ -494,7 +502,7 @@ showTaskDetailed t = do -- Show epic progress if this is an epic when (taskType t == Epic) <| do - let children = filter (\child -> taskParent child == Just (taskId t)) 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 @@ -563,9 +571,9 @@ getTaskStats maybeEpicId = do targetTasks <- case maybeEpicId of Nothing -> pure allTasks Just epicId -> - case filter (\t -> taskId t == epicId) allTasks of - [] -> panic "Epic not found" - _ -> pure <| getAllDescendants allTasks 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 @@ -610,7 +618,7 @@ getTaskStats maybeEpicId = do -- Helper to get all descendants of a task (recursive) getAllDescendants :: [Task] -> Text -> [Task] getAllDescendants allTasks parentId = - let children = filter (\t -> taskParent t == Just parentId) allTasks + let children = filter (maybe False (`matchesId` parentId) <. taskParent) allTasks in children ++ concatMap (getAllDescendants allTasks <. taskId) children -- Show task statistics (human-readable) @@ -669,7 +677,7 @@ importTasks filePath = do -- Create a map of existing task IDs for quick lookup let existingIds = map taskId existingTasks -- Filter to only new tasks (not already in our database) - newTasks = filter (\t -> taskId t `notElem` existingIds) importedTasks + newTasks = filter (\t -> not (any (`matchesId` taskId t) existingIds)) importedTasks -- For tasks that exist, update them with imported data updatedTasks = map (updateWithImported importedTasks) existingTasks -- Combine: updated existing tasks + new tasks @@ -689,9 +697,9 @@ importTasks filePath = do -- Update an existing task if there's a newer version in imported tasks updateWithImported :: [Task] -> Task -> Task updateWithImported imported existing = - case filter (\t -> taskId t == taskId existing) imported of - [] -> existing -- No imported version, keep existing - (importedTask : _) -> + case findTask (taskId existing) imported of + Nothing -> existing -- No imported version, keep existing + Just importedTask -> -- Use imported version if it's newer (based on updatedAt) if taskUpdatedAt importedTask > taskUpdatedAt existing then importedTask |
