packages feed

hackage-server-0.5.0: Distribution/Server/Features/BuildReports/Render.hs

{-# LANGUAGE RecordWildCards #-}

module Distribution.Server.Features.BuildReports.Render
    ( renderBuildStatus
    ) where

import Distribution.Server.Framework

import Distribution.Server.Features.BuildReports
import Distribution.Server.Features.BuildReports.BuildReports (BuildReportId)
import Distribution.Server.Features.BuildReports.BuildReport (BuildReport(..), InstallOutcome(..), Outcome(..))
import Distribution.Server.Features.Documentation

import Distribution.Package

import Text.XHtml.Strict

import Control.Arrow ((&&&))
import Data.Foldable (foldMap)
import Data.List (sortBy, maximumBy)
import Data.Ord (comparing)
import Data.Time (UTCTime(utctDay), showGregorian)


data BuildStatus = BuildStatus {
    rendDocsReport :: Maybe (BuildReportId, BuildReport),
    rendBuildability :: Maybe (Bool, Maybe UTCTime),
    rendNumReports :: Int
} deriving (Show)


renderBuildStatus :: MonadIO m => DocumentationFeature -> ReportsFeature -> PackageId -> m Html
renderBuildStatus DocumentationFeature{..} ReportsFeature{..} pkgid = do
    hasDocs <- queryHasDocumentation pkgid
    reports <- queryPackageReports pkgid
    return $ render reportsResource pkgid hasDocs (summarize reports)


summarize :: [(BuildReportId, BuildReport)] -> BuildStatus
summarize unsortedReports = BuildStatus{..}
  where
    -- Order the reports from oldest to newest. As maximumBy favors later
    -- elements, this will ensure we pick the most recent report.
    allReports = sortBy (comparing fst) unsortedReports

    -- When determining the doc status, only use reports generated by the server
    docReports = filter (docBuilder . snd) allReports

    rendDocsReport
      | null docReports = Nothing
      | otherwise = Just $ maximumBy (comparing (docsOutcome . snd)) docReports

    rendBuildability
      | null allReports = Nothing
      | otherwise = Just $ (isSuccess &&& time) . snd $
            maximumBy (comparing (isSuccess . snd)) allReports
      where
        isSuccess BuildReport{..}
            = installOutcome == InstallOk && testsOutcome /= Failed

    rendNumReports = length allReports


render :: ReportsResource -> PackageId -> Bool -> BuildStatus -> Html
render ReportsResource{..} pkgid hasDocs BuildStatus{..}
    = mconcat
        [ docsStatus
        , reportLink
        , br
        , buildStatus
        , reportsLink
        ]
  where

    docsStatus = toHtml $ case hasDocs of
        False -> case rendDocsReport of
            Nothing -> "Docs pending"
            Just _ -> "Docs not available"
        True -> case rendDocsReport of
            Just (_, BuildReport{ docsOutcome = Ok }) -> "Docs available"
            _ -> "Docs uploaded by user"

    reportLink = foldMap link rendDocsReport
      where
        link (repid, _) = (" " +++) . enclose $
            anchor ! [href $ reportsPageUri "" pkgid repid] << "build log"

    buildStatus = toHtml $ case rendBuildability of
        Nothing -> "Build status unknown"
        Just (False, mtime) -> "All reported builds failed"
            ++ foldMap (\tm -> " as of " ++ showDate tm) mtime
        Just (True, Nothing) -> "Successful builds reported"
        Just (True, Just tm) -> "Last success reported on " ++ showDate tm
      where
        showDate = showGregorian . utctDay

    reportsLink = (" " +++) . enclose $ anchor ! [href $ reportsListUri "" pkgid]
        << if rendNumReports > 0
            then "all " ++ show rendNumReports ++ " reports"
            else "no reports yet"

    enclose item = thespan ! [thestyle "font-size: small"]
        << ("[" +++ item +++ "]")