summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2025-11-21 03:18:01 -0500
committerBen Sima <ben@bsima.me>2025-11-21 03:18:01 -0500
commit65c0b02b23a8b3125b0c10112d48c1a637f01cf9 (patch)
tree1cc29929539b717854bb15ecf39e02324c4af738
parent1a118b071ee82f28818413a50b913bca76758f14 (diff)
feat: implement t-rWacMb1av
-rw-r--r--.tasks/tasks.jsonl.lock0
-rw-r--r--Omni/Task.hs11
-rw-r--r--Omni/Task/Core.hs54
3 files changed, 41 insertions, 24 deletions
diff --git a/.tasks/tasks.jsonl.lock b/.tasks/tasks.jsonl.lock
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.tasks/tasks.jsonl.lock
diff --git a/Omni/Task.hs b/Omni/Task.hs
index 24e528b..4a36dcf 100644
--- a/Omni/Task.hs
+++ b/Omni/Task.hs
@@ -352,7 +352,16 @@ unitTests =
-- Create a new child, it should get .4, not .2
child4 <- createTask "Child 4" WorkTask (Just (taskId parent)) Nothing P2 []
- taskId child4 Test.@?= taskId parent <> ".4"
+ taskId child4 Test.@?= taskId parent <> ".4",
+ Test.unit "task lookup is case insensitive" <| do
+ task <- createTask "Case sensitive" WorkTask Nothing Nothing P2 []
+ let tid = taskId task
+ upperTid = T.toUpper tid
+ tasks <- loadTasks
+ let found = findTask upperTid tasks
+ case found of
+ Just t -> taskId t Test.@?= tid
+ Nothing -> Test.assertFailure "Could not find task with upper case ID"
]
-- | Test CLI argument parsing to ensure docopt string matches actual usage
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index e4f1086..2b00bca 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -81,6 +81,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 tasks = List.find (\t -> matchesId (taskId t) tid) tasks
+
-- Get the tasks database file path (use test file if TASK_TEST_MODE is set)
getTasksFilePath :: IO FilePath
getTasksFilePath = do
@@ -246,7 +254,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)
@@ -299,29 +307,29 @@ 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
-- Show dependency tree for a task
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)
@@ -335,13 +343,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 (\t -> maybe False (`matchesId` taskId task) (taskParent t)) allTasks
in task : concatMap (collectChildren allTasks) children
-- Show task tree (epic with children, or all epics if no ID given)
@@ -357,9 +365,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
@@ -369,7 +377,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 (\t -> maybe False (`matchesId` taskId task) (taskParent t)) allTasks
-- Build tree prefix using box-drawing characters
prefix =
if indent == 0
@@ -418,7 +426,7 @@ printTask t = do
let progressInfo =
if taskType t == Epic
then
- let children = filter (\child -> taskParent child == Just (taskId t)) tasks
+ let children = filter (\child -> maybe False (`matchesId` taskId t) (taskParent child)) tasks
total = length children
completed = length <| filter (\child -> taskStatus child == Done) children
in " [" <> T.pack (show completed) <> "/" <> T.pack (show total) <> "]"
@@ -456,7 +464,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 (\child -> maybe False (`matchesId` taskId t) (taskParent child)) tasks
total = length children
completed = length <| filter (\child -> taskStatus child == Done) children
percentage = if total == 0 then 0 else (completed * 100) `div` total
@@ -610,7 +618,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
@@ -630,9 +638,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