stan-0.0.0.0: src/Stan/Analysis/Summary.hs
{- |
Copyright: (c) 2020 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>
Static analysis summary.
-}
module Stan.Analysis.Summary
( Summary (..)
, createSummary
) where
import Stan.Analysis (Analysis (..))
import Stan.Category (Category)
import Stan.Core.Id (Id)
import Stan.Core.ModuleName (ModuleName)
import Stan.Inspection (Inspection (..))
import Stan.Inspection.All (getInspectionById)
import Stan.Observation (Observation (..))
import Stan.Severity (Severity)
import qualified Data.HashMap.Strict as HM
-- | Short info about analysis.
data Summary = Summary
{ summaryInspectionId :: !(Id Inspection) -- ^ The most popular 'Inspection'
, summaryCategory :: !Category -- ^ The most popular 'Category'
, summaryModule :: !ModuleName -- ^ Module with the biggest number of observations
, summarySeverity :: !Severity -- ^ The highest 'Severity'
}
data SummaryData = SummaryData
{ sdInspections :: !(HashMap (Id Inspection) Int) -- ^ Count of each inspection
, sdCategories :: !(HashMap Category Int) -- ^ Count of each category
, sdModules :: !(HashMap ModuleName Int) -- ^ Count of observations per module
, sdSeverity :: !(Maybe Severity) -- ^ Highest severity
}
emptySummaryData :: SummaryData
emptySummaryData = SummaryData
{ sdInspections = mempty
, sdCategories = mempty
, sdModules = mempty
, sdSeverity = Nothing
}
summaryFromData :: SummaryData -> Maybe Summary
summaryFromData SummaryData{..} = do
summaryInspectionId <- maxOnSnd $ HM.toList sdInspections
summaryCategory <- maxOnSnd $ HM.toList sdCategories
summaryModule <- maxOnSnd $ HM.toList sdModules
summarySeverity <- sdSeverity
pure Summary{..}
where
maxOnSnd :: Ord b => [(a, b)] -> Maybe a
maxOnSnd = viaNonEmpty (fst . maximumOn1 snd)
{- | Assemble 'Summary' after analysis. Returns 'Nothing' when there's
no 'Observations'. Otherwise, there's at least one observation, which
means that we can find the most popular 'Inspection', 'Category' and
the highest 'Severity'.
-}
createSummary :: Analysis -> Maybe Summary
createSummary Analysis{..} = summaryFromData
$ foldl' handleObservation emptySummaryData analysisObservations
where
handleObservation :: SummaryData -> Observation -> SummaryData
handleObservation SummaryData{..} Observation{..} =
let Inspection{..} = getInspectionById observationInspectionId
in SummaryData
-- increase count for the current observations
{ sdInspections = HM.insertWith (+) observationInspectionId 1 sdInspections
-- increase count for each category
, sdCategories = flipfoldl'
(\cat -> HM.insertWith (+) cat 1)
sdCategories
inspectionCategory
-- increase count the module
, sdModules = HM.insertWith (+) observationModuleName 1 sdModules
-- choose the highest severity
, sdSeverity = max sdSeverity (Just inspectionSeverity)
}
-- | Fast, strict maximum
maximumOn1 :: forall b a . Ord b => (a -> b) -> NonEmpty a -> a
maximumOn1 f (x :| xs) = foldl' cmp x xs
where
cmp :: a -> a -> a
cmp a b = case compare (f a) (f b) of
LT -> b
EQ -> a
GT -> a