summaryrefslogtreecommitdiff
path: root/Omni/Task/Core.hs
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-11-29 23:18:57 -0500
committerBen Sima <ben@bensima.com>2025-11-29 23:18:57 -0500
commitb5f3b9027aa0e96cd792f036a61d6b4418b39487 (patch)
tree2a29248e3a421ce989d75d6f8813ce2dd9d616f5 /Omni/Task/Core.hs
parent82a2d55775eee2e34a26972774aaa6c5c0946743 (diff)
Sort /blocked page by blocking impact (transitive dependents)
All tests pass. The implementation is complete: **Summary of changes:** 1. **Omni/Task/Core.hs** - Added helper functions: - `getBlockingImpact`: Counts how many tasks are transitively blocked - `getTransitiveDependents`: Gets all tasks that depend on a task (di - `dependsOnTask`: Helper to check if a task depends on a given ID wi 2. **Omni/Jr/Web.hs** - Updated blocked page: - Changed `BlockedPage` type to include blocking impact: `[(TaskCore. - Updated `blockedHandler` to compute blocking impact and sort by it - Added `renderBlockedTaskCard` to display tasks with their blocking - Updated the info message to explain the sorting 3. **Omni/Jr/Web/Style.hs** - Added CSS: - `.blocking-impact` badge style (light mode) - `.blocking-impact` dark mode style Task-Id: t-189
Diffstat (limited to 'Omni/Task/Core.hs')
-rw-r--r--Omni/Task/Core.hs26
1 files changed, 26 insertions, 0 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index d64d607..e4986c1 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -11,6 +11,7 @@ import Data.Aeson (FromJSON, ToJSON, decode, encode)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy.Char8 as BLC
import qualified Data.List as List
+import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Time (UTCTime, diffUTCTime, getCurrentTime)
@@ -1395,6 +1396,31 @@ getBlockedTasks = do
`notElem` doneIds
pure [t | t <- allTasks, isBlocked t]
+-- | Count how many tasks are transitively blocked by this task
+getBlockingImpact :: [Task] -> Task -> Int
+getBlockingImpact allTasks task =
+ length (getTransitiveDependents allTasks (taskId task))
+
+-- | Get all tasks that depend on this task (directly or transitively)
+-- Uses a Set to track visited nodes and avoid infinite loops from circular deps
+getTransitiveDependents :: [Task] -> Text -> [Task]
+getTransitiveDependents allTasks tid = go Set.empty [tid]
+ where
+ go :: Set.Set Text -> [Text] -> [Task]
+ go _ [] = []
+ go visited (current : rest)
+ | Set.member current visited = go visited rest
+ | otherwise =
+ let directDeps = [t | t <- allTasks, dependsOnTask current t]
+ newIds = [taskId t | t <- directDeps, not (Set.member (taskId t) visited)]
+ visited' = Set.insert current visited
+ in directDeps ++ go visited' (newIds ++ rest)
+
+-- | Check if task depends on given ID with Blocks dependency type
+dependsOnTask :: Text -> Task -> Bool
+dependsOnTask tid task =
+ any (\d -> matchesId (depId d) tid && depType d == Blocks) (taskDependencies task)
+
-- | Get tasks that have failed 3+ times and need human intervention
getInterventionTasks :: IO [Task]
getInterventionTasks = do