module Poseidon.CLI.Summarise where

import           Poseidon.Janno         (JannoDateBCADMedian (..),
                                         JannoRow (..), JannoRows (..),
                                         ListColumn (..))
import           Poseidon.MathHelpers   (meanAndSdInteger, meanAndSdRoundTo)
import           Poseidon.Package       (PackageReadOptions (..),
                                         PoseidonPackage (..),
                                         defaultPackageReadOptions,
                                         readPoseidonPackageCollection)
import           Poseidon.Utils         (PoseidonIO, logInfo, uniquePO)

import           Control.Monad.IO.Class (liftIO)
import           Data.List              (group, intercalate, sort, sortBy)
import           Data.Maybe             (mapMaybe)
import           Poseidon.ColumnTypes   (JannoCoverageOnTargets (..),
                                         JannoEndogenous (JannoEndogenous),
                                         JannoNrSNPs (..))
import           Text.Layout.Table      (asciiRoundS, column, def, expandUntil,
                                         rowsG, tableString, titlesH)

-- | A datatype representing command line options for the summarise command
data SummariseOptions = SummariseOptions
    { SummariseOptions -> [String]
_summariseBaseDirs  :: [FilePath]
    , SummariseOptions -> Bool
_summariseRawOutput :: Bool
    }

pacReadOpts :: PackageReadOptions
pacReadOpts :: PackageReadOptions
pacReadOpts = PackageReadOptions
defaultPackageReadOptions {
      _readOptIgnoreChecksums :: Bool
_readOptIgnoreChecksums      = Bool
True
    , _readOptIgnoreGeno :: Bool
_readOptIgnoreGeno           = Bool
True
    , _readOptGenoCheck :: Bool
_readOptGenoCheck            = Bool
False
    , _readOptOnlyLatest :: Bool
_readOptOnlyLatest           = Bool
True
    }

-- | The main function running the janno command
runSummarise :: SummariseOptions -> PoseidonIO ()
runSummarise :: SummariseOptions -> PoseidonIO ()
runSummarise (SummariseOptions [String]
baseDirs Bool
rawOutput) = do
    [PoseidonPackage]
allPackages <- PackageReadOptions -> [String] -> PoseidonIO [PoseidonPackage]
readPoseidonPackageCollection PackageReadOptions
pacReadOpts [String]
baseDirs
    let jannos :: [JannoRows]
jannos = (PoseidonPackage -> JannoRows) -> [PoseidonPackage] -> [JannoRows]
forall a b. (a -> b) -> [a] -> [b]
map PoseidonPackage -> JannoRows
posPacJanno [PoseidonPackage]
allPackages
    String -> PoseidonIO ()
logInfo String
"Note that only the latest versions of packages are included in the summary"
    IO () -> PoseidonIO ()
forall a. IO a -> ReaderT Env IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PoseidonIO ()) -> IO () -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$ JannoRows -> Bool -> IO ()
summariseJannoRows ([JannoRows] -> JannoRows
forall a. Monoid a => [a] -> a
mconcat [JannoRows]
jannos) Bool
rawOutput

-- | A function to print meaningful summary information for a list of poseidon samples
summariseJannoRows :: JannoRows -> Bool -> IO ()
summariseJannoRows :: JannoRows -> Bool -> IO ()
summariseJannoRows (JannoRows [JannoRow]
rows) Bool
rawOutput = do
    ([String]
tableH, [[String]]
tableB) <- do
        let tableH :: [String]
tableH = [String
"Summary", String
"Value"]
            tableB :: [[String]]
tableB = [
                [String
"Nr Samples"
                , Int -> String
forall a. Show a => a -> String
show ([JannoRow] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [JannoRow]
rows)],
                [String
"Samples"
                , [String] -> String
paste ([String] -> String)
-> ([JannoRow] -> [String]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> [String]
forall a. Ord a => [a] -> [a]
sort ([String] -> [String])
-> ([JannoRow] -> [String]) -> [JannoRow] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> String) -> [JannoRow] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map JannoRow -> String
jPoseidonID ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Nr Primary Groups"
                , [GroupName] -> String
forall a. Ord a => [a] -> String
uniqueNumber ([GroupName] -> String)
-> ([JannoRow] -> [GroupName]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> GroupName) -> [JannoRow] -> [GroupName]
forall a b. (a -> b) -> [a] -> [b]
map ([GroupName] -> GroupName
forall a. HasCallStack => [a] -> a
head ([GroupName] -> GroupName)
-> (JannoRow -> [GroupName]) -> JannoRow -> GroupName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ListColumn GroupName -> [GroupName]
forall a. ListColumn a -> [a]
getListColumn (ListColumn GroupName -> [GroupName])
-> (JannoRow -> ListColumn GroupName) -> JannoRow -> [GroupName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JannoRow -> ListColumn GroupName
jGroupName) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Primary Groups"
                , String -> [(String, Int)] -> String
printFrequencyString String
", " ([(String, Int)] -> String)
-> ([JannoRow] -> [(String, Int)]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> [(String, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([String] -> [(String, Int)])
-> ([JannoRow] -> [String]) -> [JannoRow] -> [(String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> String) -> [JannoRow] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (GroupName -> String
forall a. Show a => a -> String
show (GroupName -> String)
-> (JannoRow -> GroupName) -> JannoRow -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [GroupName] -> GroupName
forall a. HasCallStack => [a] -> a
head ([GroupName] -> GroupName)
-> (JannoRow -> [GroupName]) -> JannoRow -> GroupName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ListColumn GroupName -> [GroupName]
forall a. ListColumn a -> [a]
getListColumn (ListColumn GroupName -> [GroupName])
-> (JannoRow -> ListColumn GroupName) -> JannoRow -> [GroupName]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JannoRow -> ListColumn GroupName
jGroupName) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Nr Publications"
                , [JannoPublication] -> String
forall a. Ord a => [a] -> String
uniqueNumber ([JannoPublication] -> String)
-> ([JannoRow] -> [JannoPublication]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ListColumn JannoPublication -> [JannoPublication])
-> [ListColumn JannoPublication] -> [JannoPublication]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ListColumn JannoPublication -> [JannoPublication]
forall a. ListColumn a -> [a]
getListColumn ([ListColumn JannoPublication] -> [JannoPublication])
-> ([JannoRow] -> [ListColumn JannoPublication])
-> [JannoRow]
-> [JannoPublication]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe (ListColumn JannoPublication))
-> [JannoRow] -> [ListColumn JannoPublication]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe JannoRow -> Maybe (ListColumn JannoPublication)
jPublication ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Publications"
                , [String] -> String
paste ([String] -> String)
-> ([JannoRow] -> [String]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoPublication -> String) -> [JannoPublication] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map JannoPublication -> String
forall a. Show a => a -> String
show ([JannoPublication] -> [String])
-> ([JannoRow] -> [JannoPublication]) -> [JannoRow] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [JannoPublication] -> [JannoPublication]
forall a. Ord a => [a] -> [a]
uniquePO ([JannoPublication] -> [JannoPublication])
-> ([JannoRow] -> [JannoPublication])
-> [JannoRow]
-> [JannoPublication]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ListColumn JannoPublication -> [JannoPublication])
-> [ListColumn JannoPublication] -> [JannoPublication]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ListColumn JannoPublication -> [JannoPublication]
forall a. ListColumn a -> [a]
getListColumn ([ListColumn JannoPublication] -> [JannoPublication])
-> ([JannoRow] -> [ListColumn JannoPublication])
-> [JannoRow]
-> [JannoPublication]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe (ListColumn JannoPublication))
-> [JannoRow] -> [ListColumn JannoPublication]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe JannoRow -> Maybe (ListColumn JannoPublication)
jPublication ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Nr Countries"
                , [JannoCountry] -> String
forall a. Ord a => [a] -> String
uniqueNumber ([JannoCountry] -> String)
-> ([JannoRow] -> [JannoCountry]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe JannoCountry) -> [JannoRow] -> [JannoCountry]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe JannoRow -> Maybe JannoCountry
jCountry ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Countries"
                , String -> [(Maybe String, Int)] -> String
printFrequencyMaybeString String
", " ([(Maybe String, Int)] -> String)
-> ([JannoRow] -> [(Maybe String, Int)]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe String] -> [(Maybe String, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([Maybe String] -> [(Maybe String, Int)])
-> ([JannoRow] -> [Maybe String])
-> [JannoRow]
-> [(Maybe String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe String) -> [JannoRow] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map ((JannoCountry -> String) -> Maybe JannoCountry -> Maybe String
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap JannoCountry -> String
forall a. Show a => a -> String
show (Maybe JannoCountry -> Maybe String)
-> (JannoRow -> Maybe JannoCountry) -> JannoRow -> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JannoRow -> Maybe JannoCountry
jCountry) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows
                ],
                [String
"Mean age BC/AD"
                , [Double] -> String
meanAndSdInteger ([Double] -> String)
-> ([JannoRow] -> [Double]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoDateBCADMedian -> Double)
-> [JannoDateBCADMedian] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (\(JannoDateBCADMedian Int
x) -> Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
x) ([JannoDateBCADMedian] -> [Double])
-> ([JannoRow] -> [JannoDateBCADMedian]) -> [JannoRow] -> [Double]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe JannoDateBCADMedian)
-> [JannoRow] -> [JannoDateBCADMedian]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe JannoRow -> Maybe JannoDateBCADMedian
jDateBCADMedian ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Dating type"
                , String -> [(Maybe JannoDateType, Int)] -> String
forall a. Show a => String -> [(Maybe a, Int)] -> String
printFrequencyMaybe String
", " ([(Maybe JannoDateType, Int)] -> String)
-> ([JannoRow] -> [(Maybe JannoDateType, Int)])
-> [JannoRow]
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe JannoDateType] -> [(Maybe JannoDateType, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([Maybe JannoDateType] -> [(Maybe JannoDateType, Int)])
-> ([JannoRow] -> [Maybe JannoDateType])
-> [JannoRow]
-> [(Maybe JannoDateType, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe JannoDateType)
-> [JannoRow] -> [Maybe JannoDateType]
forall a b. (a -> b) -> [a] -> [b]
map JannoRow -> Maybe JannoDateType
jDateType ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Sex distribution"
                , String -> [(GeneticSex, Int)] -> String
forall a. Show a => String -> [(a, Int)] -> String
printFrequency String
", " ([(GeneticSex, Int)] -> String)
-> ([JannoRow] -> [(GeneticSex, Int)]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [GeneticSex] -> [(GeneticSex, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([GeneticSex] -> [(GeneticSex, Int)])
-> ([JannoRow] -> [GeneticSex])
-> [JannoRow]
-> [(GeneticSex, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> GeneticSex) -> [JannoRow] -> [GeneticSex]
forall a b. (a -> b) -> [a] -> [b]
map JannoRow -> GeneticSex
jGeneticSex ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"MT haplogroups"
                , String -> [(Maybe String, Int)] -> String
printFrequencyMaybeString String
", " ([(Maybe String, Int)] -> String)
-> ([JannoRow] -> [(Maybe String, Int)]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe String] -> [(Maybe String, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([Maybe String] -> [(Maybe String, Int)])
-> ([JannoRow] -> [Maybe String])
-> [JannoRow]
-> [(Maybe String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe String) -> [JannoRow] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map ((JannoMTHaplogroup -> String)
-> Maybe JannoMTHaplogroup -> Maybe String
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap JannoMTHaplogroup -> String
forall a. Show a => a -> String
show (Maybe JannoMTHaplogroup -> Maybe String)
-> (JannoRow -> Maybe JannoMTHaplogroup)
-> JannoRow
-> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JannoRow -> Maybe JannoMTHaplogroup
jMTHaplogroup) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Y haplogroups"
                , String -> [(Maybe String, Int)] -> String
printFrequencyMaybeString String
", " ([(Maybe String, Int)] -> String)
-> ([JannoRow] -> [(Maybe String, Int)]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe String] -> [(Maybe String, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([Maybe String] -> [(Maybe String, Int)])
-> ([JannoRow] -> [Maybe String])
-> [JannoRow]
-> [(Maybe String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe String) -> [JannoRow] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map ((JannoYHaplogroup -> String)
-> Maybe JannoYHaplogroup -> Maybe String
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap JannoYHaplogroup -> String
forall a. Show a => a -> String
show (Maybe JannoYHaplogroup -> Maybe String)
-> (JannoRow -> Maybe JannoYHaplogroup) -> JannoRow -> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JannoRow -> Maybe JannoYHaplogroup
jYHaplogroup) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"% endogenous DNA"
                , Int -> [Double] -> String
meanAndSdRoundTo Int
2 ([Double] -> String)
-> ([JannoRow] -> [Double]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoEndogenous -> Double) -> [JannoEndogenous] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (\(JannoEndogenous Double
x) -> Double
x) ([JannoEndogenous] -> [Double])
-> ([JannoRow] -> [JannoEndogenous]) -> [JannoRow] -> [Double]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe JannoEndogenous)
-> [JannoRow] -> [JannoEndogenous]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe JannoRow -> Maybe JannoEndogenous
jEndogenous ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Nr of SNPs"
                , [Double] -> String
meanAndSdInteger ([Double] -> String)
-> ([JannoRow] -> [Double]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Double) -> [Int] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([Int] -> [Double])
-> ([JannoRow] -> [Int]) -> [JannoRow] -> [Double]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe Int) -> [JannoRow] -> [Int]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((JannoNrSNPs -> Int) -> Maybe JannoNrSNPs -> Maybe Int
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(JannoNrSNPs Int
x) -> Int
x) (Maybe JannoNrSNPs -> Maybe Int)
-> (JannoRow -> Maybe JannoNrSNPs) -> JannoRow -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JannoRow -> Maybe JannoNrSNPs
jNrSNPs) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Coverage on target"
                , Int -> [Double] -> String
meanAndSdRoundTo Int
2 ([Double] -> String)
-> ([JannoRow] -> [Double]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe Double) -> [JannoRow] -> [Double]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((JannoCoverageOnTargets -> Double)
-> Maybe JannoCoverageOnTargets -> Maybe Double
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(JannoCoverageOnTargets Double
x) -> Double
x) (Maybe JannoCoverageOnTargets -> Maybe Double)
-> (JannoRow -> Maybe JannoCoverageOnTargets)
-> JannoRow
-> Maybe Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
.  JannoRow -> Maybe JannoCoverageOnTargets
jCoverageOnTargets) ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"Library type"
                , String -> [(Maybe JannoLibraryBuilt, Int)] -> String
forall a. Show a => String -> [(Maybe a, Int)] -> String
printFrequencyMaybe String
", " ([(Maybe JannoLibraryBuilt, Int)] -> String)
-> ([JannoRow] -> [(Maybe JannoLibraryBuilt, Int)])
-> [JannoRow]
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe JannoLibraryBuilt] -> [(Maybe JannoLibraryBuilt, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([Maybe JannoLibraryBuilt] -> [(Maybe JannoLibraryBuilt, Int)])
-> ([JannoRow] -> [Maybe JannoLibraryBuilt])
-> [JannoRow]
-> [(Maybe JannoLibraryBuilt, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe JannoLibraryBuilt)
-> [JannoRow] -> [Maybe JannoLibraryBuilt]
forall a b. (a -> b) -> [a] -> [b]
map JannoRow -> Maybe JannoLibraryBuilt
jLibraryBuilt ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows],
                [String
"UDG treatment"
                , String -> [(Maybe JannoUDG, Int)] -> String
forall a. Show a => String -> [(Maybe a, Int)] -> String
printFrequencyMaybe String
", " ([(Maybe JannoUDG, Int)] -> String)
-> ([JannoRow] -> [(Maybe JannoUDG, Int)]) -> [JannoRow] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe JannoUDG] -> [(Maybe JannoUDG, Int)]
forall a. Ord a => [a] -> [(a, Int)]
frequency ([Maybe JannoUDG] -> [(Maybe JannoUDG, Int)])
-> ([JannoRow] -> [Maybe JannoUDG])
-> [JannoRow]
-> [(Maybe JannoUDG, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (JannoRow -> Maybe JannoUDG) -> [JannoRow] -> [Maybe JannoUDG]
forall a b. (a -> b) -> [a] -> [b]
map JannoRow -> Maybe JannoUDG
jUDG ([JannoRow] -> String) -> [JannoRow] -> String
forall a b. (a -> b) -> a -> b
$ [JannoRow]
rows]
                ]
        ([String], [[String]]) -> IO ([String], [[String]])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([String]
tableH, [[String]]
tableB)
    let colSpecs :: [ColSpec]
colSpecs = Int -> ColSpec -> [ColSpec]
forall a. Int -> a -> [a]
replicate Int
2 (LenSpec -> Position H -> AlignSpec -> CutMark -> ColSpec
column (Int -> LenSpec
expandUntil Int
60) Position H
forall a. Default a => a
def AlignSpec
forall a. Default a => a
def CutMark
forall a. Default a => a
def)
    if Bool
rawOutput
    then String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" [String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"\t" [String]
row | [String]
row <- [[String]]
tableB]
    else String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [ColSpec]
-> TableStyle -> HeaderSpec -> [RowGroup String] -> String
forall a.
Cell a =>
[ColSpec] -> TableStyle -> HeaderSpec -> [RowGroup a] -> String
tableString [ColSpec]
colSpecs TableStyle
asciiRoundS ([String] -> HeaderSpec
titlesH [String]
tableH) [[[String]] -> RowGroup String
forall a. [Row a] -> RowGroup a
rowsG [[String]]
tableB]

-- | A helper function to concat the first N elements of a string list in a nice way
paste :: [String] -> String
paste :: [String] -> String
paste [] = String
"no values"
paste [String]
xs = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
xs

uniqueNumber :: Ord a => [a] -> String
uniqueNumber :: forall a. Ord a => [a] -> String
uniqueNumber = Int -> String
forall a. Show a => a -> String
show (Int -> String) -> ([a] -> Int) -> [a] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([a] -> Int) -> ([a] -> [a]) -> [a] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [a]
forall a. Ord a => [a] -> [a]
uniquePO

-- | A helper function to determine the frequency of objects in a list
-- (similar to the table function in R)
frequency :: Ord a => [a] -> [(a,Int)]
frequency :: forall a. Ord a => [a] -> [(a, Int)]
frequency [a]
list = ((a, Int) -> (a, Int) -> Ordering) -> [(a, Int)] -> [(a, Int)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (a, Int) -> (a, Int) -> Ordering
forall a b. (Ord a, Ord b) => (a, b) -> (a, b) -> Ordering
sortTupelsBySndDesc ([(a, Int)] -> [(a, Int)]) -> [(a, Int)] -> [(a, Int)]
forall a b. (a -> b) -> a -> b
$ ([a] -> (a, Int)) -> [[a]] -> [(a, Int)]
forall a b. (a -> b) -> [a] -> [b]
map (\[a]
l -> ([a] -> a
forall a. HasCallStack => [a] -> a
head [a]
l, [a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
l)) ([a] -> [[a]]
forall a. Eq a => [a] -> [[a]]
group ([a] -> [a]
forall a. Ord a => [a] -> [a]
sort [a]
list))

sortTupelsBySndDesc :: (Ord a, Ord b) => (a, b) -> (a, b) -> Ordering
sortTupelsBySndDesc :: forall a b. (Ord a, Ord b) => (a, b) -> (a, b) -> Ordering
sortTupelsBySndDesc (a
a1, b
b1) (a
a2, b
b2)
  | b
b1 b -> b -> Bool
forall a. Ord a => a -> a -> Bool
< b
b2   = Ordering
GT
  | b
b1 b -> b -> Bool
forall a. Ord a => a -> a -> Bool
> b
b2   = Ordering
LT
  | b
b1 b -> b -> Bool
forall a. Eq a => a -> a -> Bool
== b
b2  = a -> a -> Ordering
forall a. Ord a => a -> a -> Ordering
compare a
a1 a
a2
  | Bool
otherwise = String -> Ordering
forall a. HasCallStack => String -> a
error String
"sortTuplesBySndDesc: should never happen"

-- | A helper function to print the output of frequency nicely
printFrequency :: Show a => String -> [(a,Int)] -> String
printFrequency :: forall a. Show a => String -> [(a, Int)] -> String
printFrequency String
_ [] = String
"no values"
printFrequency String
_ [(a, Int)
x] = a -> String
forall a. Show a => a -> String
show ((a, Int) -> a
forall a b. (a, b) -> a
fst (a, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((a, Int) -> Int
forall a b. (a, b) -> b
snd (a, Int)
x)
printFrequency String
sep ((a, Int)
x:[(a, Int)]
xs) = a -> String
forall a. Show a => a -> String
show ((a, Int) -> a
forall a b. (a, b) -> a
fst (a, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((a, Int) -> Int
forall a b. (a, b) -> b
snd (a, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
sep String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [(a, Int)] -> String
forall a. Show a => String -> [(a, Int)] -> String
printFrequency String
sep [(a, Int)]
xs

-- | A helper function to print the output of frequency over Maybe values nicely
printFrequencyMaybe :: Show a => String -> [(Maybe a,Int)] -> String
printFrequencyMaybe :: forall a. Show a => String -> [(Maybe a, Int)] -> String
printFrequencyMaybe String
_ [] = String
"no values"
printFrequencyMaybe String
_ [(Maybe a, Int)
x] = Maybe a -> String
forall a. Show a => Maybe a -> String
maybeShow ((Maybe a, Int) -> Maybe a
forall a b. (a, b) -> a
fst (Maybe a, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((Maybe a, Int) -> Int
forall a b. (a, b) -> b
snd (Maybe a, Int)
x)
printFrequencyMaybe String
sep ((Maybe a, Int)
x:[(Maybe a, Int)]
xs) = Maybe a -> String
forall a. Show a => Maybe a -> String
maybeShow ((Maybe a, Int) -> Maybe a
forall a b. (a, b) -> a
fst (Maybe a, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((Maybe a, Int) -> Int
forall a b. (a, b) -> b
snd (Maybe a, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
sep String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [(Maybe a, Int)] -> String
forall a. Show a => String -> [(Maybe a, Int)] -> String
printFrequencyMaybe String
sep [(Maybe a, Int)]
xs

-- | A helper function to unwrap a maybe
maybeShow :: Show a => Maybe a -> String
maybeShow :: forall a. Show a => Maybe a -> String
maybeShow (Just a
x) = a -> String
forall a. Show a => a -> String
show a
x
maybeShow Maybe a
Nothing  = String
"n/a"

-- | As printFrequency, but without additional quoting of strings
printFrequencyString :: String -> [(String,Int)] -> String
printFrequencyString :: String -> [(String, Int)] -> String
printFrequencyString String
_ [] = String
"no values"
printFrequencyString String
_ [(String, Int)
x] = (String, Int) -> String
forall a b. (a, b) -> a
fst (String, Int)
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((String, Int) -> Int
forall a b. (a, b) -> b
snd (String, Int)
x)
printFrequencyString String
sep ((String, Int)
x:[(String, Int)]
xs) = (String, Int) -> String
forall a b. (a, b) -> a
fst (String, Int)
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((String, Int) -> Int
forall a b. (a, b) -> b
snd (String, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
sep String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [(String, Int)] -> String
printFrequencyString String
sep [(String, Int)]
xs

-- | As printFrequencyMaybe, but without additional quoting of strings
printFrequencyMaybeString :: String -> [(Maybe String,Int)] -> String
printFrequencyMaybeString :: String -> [(Maybe String, Int)] -> String
printFrequencyMaybeString String
_ [] = String
"no values"
printFrequencyMaybeString String
_ [(Maybe String, Int)
x] = Maybe String -> String
maybeShowString ((Maybe String, Int) -> Maybe String
forall a b. (a, b) -> a
fst (Maybe String, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((Maybe String, Int) -> Int
forall a b. (a, b) -> b
snd (Maybe String, Int)
x)
printFrequencyMaybeString String
sep ((Maybe String, Int)
x:[(Maybe String, Int)]
xs) = Maybe String -> String
maybeShowString ((Maybe String, Int) -> Maybe String
forall a b. (a, b) -> a
fst (Maybe String, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ((Maybe String, Int) -> Int
forall a b. (a, b) -> b
snd (Maybe String, Int)
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
sep String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [(Maybe String, Int)] -> String
printFrequencyMaybeString String
sep [(Maybe String, Int)]
xs

-- | As maybeShow, but without additional quoting of strings
maybeShowString :: Maybe String -> String
maybeShowString :: Maybe String -> String
maybeShowString (Just String
x) = String
x
maybeShowString Maybe String
Nothing  = String
"n/a"