loadCats :: IO (Map (FunName,TaskGroup,Text) String)
loadCats =
  fmap (Map.fromList . concat . concat . concat) $
  do let top = "queues" </> "solved"
     cats <- list top
     forM cats $ \cat ->
       do let catPath = top </> cat
          funs <- list catPath
          forM funs $ \fun ->
            do let funPath = catPath </> fun
               tgs <- list funPath
               forM tgs $ \tgName ->
                 do tg <- case parseTaskGroup (Text.pack tgName) of
                            Just tg -> return tg
                            Nothing -> fail $ "Failed to parse task group: "
                                                              ++ show tgName
                    let tgPath = funPath </> tgName
                    tasks <- list tgPath
                    forM tasks $ \t ->
                       return ( (rawFunName fun, tg, Text.pack t), cat )
  where
  list d = do fs <- getDirectoryContents d
              return (filter ((/= ".") . take 1) fs)
loadCats :: IO (Map (FunName,TaskGroup,Text) String)
loadCats =
  fmap (Map.fromList . concat . concat . concat) $
  do let top = "queues" </> "solved"
     cats <- list top
     forM cats $ \cat ->
       do let catPath = top </> cat
          funs <- list catPath
          forM funs $ \fun ->
            do let funPath = catPath </> fun
               tgs <- list funPath
               forM tgs $ \tgName ->
                 do tg <- case parseTaskGroup (Text.pack tgName) of
                            Just tg -> return tg
                            Nothing -> fail $ "Failed to parse task group: "
                                                              ++ show tgName
                    let tgPath = funPath </> tgName
                    tasks <- list tgPath
                    forM tasks $ \t ->
                       return ( (rawFunName fun, tg, Text.pack t), cat )
  where
  list d = do fs <- getDirectoryContents d
              return (filter ((/= ".") . take 1) fs)
loadCats =
  fmap (Map.fromList . concat . concat . concat) $
  do let top = "queues" </> "solved"
     cats <- list top
     forM cats $ \cat ->
       do let catPath = top </> cat
          funs <- list catPath
          forM funs $ \fun ->
            do let funPath = catPath </> fun
               tgs <- list funPath
               forM tgs $ \tgName ->
                 do tg <- case parseTaskGroup (Text.pack tgName) of
                            Just tg -> return tg
                            Nothing -> fail $ "Failed to parse task group: "
                                                              ++ show tgName
                    let tgPath = funPath </> tgName
                    tasks <- list tgPath
                    forM tasks $ \t ->
                       return ( (rawFunName fun, tg, Text.pack t), cat )
  where
  list d = do fs <- getDirectoryContents d
              return (filter ((/= ".") . take 1) fs)