diff options
| author | Ben Sima <ben@bensima.com> | 2025-11-29 23:18:57 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bensima.com> | 2025-11-29 23:18:57 -0500 |
| commit | b5f3b9027aa0e96cd792f036a61d6b4418b39487 (patch) | |
| tree | 2a29248e3a421ce989d75d6f8813ce2dd9d616f5 /Omni/Task/Core.hs | |
| parent | 82a2d55775eee2e34a26972774aaa6c5c0946743 (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.hs | 26 |
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 |
