{-# LANGUAGE OverloadedStrings #-}

module Poseidon.CLI.List (runList, ListOptions(..), ListEntity(..), RepoLocationSpec(..)) where

import           Poseidon.BibFile       (authorAbbrvString, parseAuthors)
import           Poseidon.Contributor   (ContributorSpec (..), renderORCID)
import           Poseidon.EntityTypes   (HasNameAndVersion (..))
import           Poseidon.Package       (PackageReadOptions (..),
                                         defaultPackageReadOptions,
                                         getAllGroupInfo, getBibliographyInfo,
                                         getExtendedIndividualInfo,
                                         packagesToPackageInfos,
                                         readPoseidonPackageCollection)
import           Poseidon.ServerClient  (AddColSpec (..), ApiReturnData (..),
                                         ArchiveEndpoint (..),
                                         BibliographyInfo (..),
                                         ExtendedIndividualInfo (..),
                                         GroupInfo (..), PackageInfo (..),
                                         processApiResponse, qDefault)
import           Poseidon.Utils         (PoseidonIO, logInfo, logWarning)

import           Control.Monad          (forM_, when)
import           Control.Monad.IO.Class (liftIO)
import           Data.List              (intercalate, nub, sortOn)
import           Data.Maybe             (catMaybes, fromMaybe)
import qualified Data.Text              as T
import           Data.Version           (Version, showVersion)
import           Text.Layout.Table      (asciiRoundS, column, def, expandUntil,
                                         rowsG, tableString, titlesH)

-- | A datatype representing command line options for the list command
data ListOptions = ListOptions
    { ListOptions -> RepoLocationSpec
_listRepoLocation :: RepoLocationSpec -- ^ the list of base directories to search for packages
    , ListOptions -> ListEntity
_listListEntity   :: ListEntity -- ^ what to list
    , ListOptions -> Bool
_listRawOutput    :: Bool -- ^ whether to output raw TSV instead of a nicely formatted table
    , ListOptions -> Bool
_listOnlyLatest   :: Bool -- ^ whether to show only latest versions of packages
    }

data RepoLocationSpec = RepoLocal [FilePath] | RepoRemote ArchiveEndpoint

-- | A datatype to represent the options what to list
data ListEntity = ListPackages Bool -- an option to list all YAML columns as well
    | ListGroups
    | ListIndividuals AddColSpec
    | ListBibliography AddColSpec

-- | The main function running the list command
runList :: ListOptions -> PoseidonIO ()
runList :: ListOptions -> PoseidonIO ()
runList (ListOptions RepoLocationSpec
repoLocation ListEntity
listEntity Bool
rawOutput Bool
onlyLatest) = do
    let pacReadOpts :: PackageReadOptions
pacReadOpts = PackageReadOptions
defaultPackageReadOptions {
          _readOptIgnoreChecksums      = True
        , _readOptGenoCheck            = False
        , _readOptIgnoreGeno           = True
        , _readOptOnlyLatest           = onlyLatest
    }
    -- build tables
    ([[Char]]
tableH, [[[Char]]]
tableB) <- case ListEntity
listEntity of
        ListPackages Bool
fullOutput -> do
            [PackageInfo]
packageInfos <- case RepoLocationSpec
repoLocation of
                RepoRemote (ArchiveEndpoint [Char]
remoteURL Maybe [Char]
archive) -> do
                    [Char] -> PoseidonIO ()
logInfo [Char]
"Downloading package data from server"
                    ApiReturnData
apiReturn <- [Char] -> Bool -> PoseidonIO ApiReturnData
processApiResponse ([Char]
remoteURL [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"/packages" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Maybe [Char] -> [Char]
qDefault Maybe [Char]
archive) Bool
False
                    case ApiReturnData
apiReturn of
                        ApiReturnPackageInfo [PackageInfo]
pacInfo -> [PackageInfo] -> ReaderT Env IO [PackageInfo]
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [PackageInfo]
pacInfo
                        ApiReturnData
_ -> [Char] -> ReaderT Env IO [PackageInfo]
forall a. HasCallStack => [Char] -> a
error [Char]
"should not happen"
                RepoLocal [[Char]]
baseDirs -> do
                    [PoseidonPackage]
pacCollection <- PackageReadOptions -> [[Char]] -> PoseidonIO [PoseidonPackage]
readPoseidonPackageCollection PackageReadOptions
pacReadOpts [[Char]]
baseDirs
                    Bool -> [PoseidonPackage] -> ReaderT Env IO [PackageInfo]
forall (m :: * -> *).
MonadThrow m =>
Bool -> [PoseidonPackage] -> m [PackageInfo]
packagesToPackageInfos Bool
True [PoseidonPackage]
pacCollection
            let tableH :: [[Char]]
tableH =
                    let baseColumnH :: [[Char]]
baseColumnH = [[Char]
"Package", [Char]
"Package Version", [Char]
"Is Latest", [Char]
"Poseidon Version", [Char]
"Description", [Char]
"Last modified", [Char]
"Nr Individuals"]
                    in  if Bool
fullOutput then
                            [[Char]]
baseColumnH [[Char]] -> [[Char]] -> [[Char]]
forall a. [a] -> [a] -> [a]
++ [[Char]
"Contributors", [Char]
"Base Directory", [Char]
"GenotypeFiles", [Char]
"JannoFile", [Char]
"SeqSourceFile", [Char]
"BibFile", [Char]
"ReadmeFile", [Char]
"ChangelogFile"]
                        else
                            [[Char]]
baseColumnH
                tableB :: [[[Char]]]
tableB = ([[Char]] -> [Char]) -> [[[Char]]] -> [[[Char]]]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn [[Char]] -> [Char]
forall a. HasCallStack => [a] -> a
head ([[[Char]]] -> [[[Char]]]) -> [[[Char]]] -> [[[Char]]]
forall a b. (a -> b) -> a -> b
$ do
                    PackageInfo
pInf <- [PackageInfo]
packageInfos
                    -- for the locally read packages this doesn't do anything,
                    -- because the dataset is already reduced to the latest packages
                    -- in the reading process
                    Bool
True <- Bool -> [Bool]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Bool
not Bool
onlyLatest Bool -> Bool -> Bool
|| PackageInfo -> Bool
pIsLatest PackageInfo
pInf)
                    let baseCols :: [[Char]]
baseCols = [PackageInfo -> [Char]
forall a. HasNameAndVersion a => a -> [Char]
getPacName PackageInfo
pInf, Maybe Version -> [Char]
showMaybeVersion (PackageInfo -> Maybe Version
forall a. HasNameAndVersion a => a -> Maybe Version
getPacVersion PackageInfo
pInf), Bool -> [Char]
forall a. Show a => a -> [Char]
show (Bool -> [Char]) -> Bool -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Bool
pIsLatest PackageInfo
pInf,
                            Version -> [Char]
showVersion (Version -> [Char]) -> Version -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Version
pPosVersion PackageInfo
pInf, Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pDescription PackageInfo
pInf, Maybe [Char] -> [Char]
showMaybe (Day -> [Char]
forall a. Show a => a -> [Char]
show (Day -> [Char]) -> Maybe Day -> Maybe [Char]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> PackageInfo -> Maybe Day
pLastModified PackageInfo
pInf), Int -> [Char]
forall a. Show a => a -> [Char]
show (Int -> [Char]) -> Int -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Int
pNrIndividuals PackageInfo
pInf]
                    if Bool
fullOutput then
                        [[Char]] -> [[[Char]]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]] -> [[[Char]]]) -> [[Char]] -> [[[Char]]]
forall a b. (a -> b) -> a -> b
$ [[Char]]
baseCols [[Char]] -> [[Char]] -> [[Char]]
forall a. [a] -> [a] -> [a]
++ [[Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
";" ([[Char]] -> [Char])
-> (PackageInfo -> [[Char]]) -> PackageInfo -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ContributorSpec -> [Char]) -> [ContributorSpec] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ContributorSpec -> [Char]
showContributor ([ContributorSpec] -> [[Char]])
-> (PackageInfo -> [ContributorSpec]) -> PackageInfo -> [[Char]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageInfo -> [ContributorSpec]
pContributors (PackageInfo -> [Char]) -> PackageInfo -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo
pInf, Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pBaseDir PackageInfo
pInf, [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"," ([[Char]] -> [Char])
-> (PackageInfo -> [[Char]]) -> PackageInfo -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageInfo -> [[Char]]
pGenotypeFiles (PackageInfo -> [Char]) -> PackageInfo -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo
pInf,
                            Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pJannoFile PackageInfo
pInf, Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pSeqSourceFile PackageInfo
pInf, Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pBibFile PackageInfo
pInf, Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pReadmeFile PackageInfo
pInf,
                            Maybe [Char] -> [Char]
showMaybe (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ PackageInfo -> Maybe [Char]
pChangelogFile PackageInfo
pInf]
                    else
                        [[Char]] -> [[[Char]]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [[Char]]
baseCols
            ([[Char]], [[[Char]]]) -> ReaderT Env IO ([[Char]], [[[Char]]])
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]]
tableH, [[[Char]]]
tableB)
        ListEntity
ListGroups -> do
            [GroupInfo]
groupInfos <- case RepoLocationSpec
repoLocation of
                RepoRemote (ArchiveEndpoint [Char]
remoteURL Maybe [Char]
archive) -> do
                    [Char] -> PoseidonIO ()
logInfo [Char]
"Downloading group data from server"
                    ApiReturnData
apiReturn <- [Char] -> Bool -> PoseidonIO ApiReturnData
processApiResponse ([Char]
remoteURL [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"/groups" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Maybe [Char] -> [Char]
qDefault Maybe [Char]
archive) Bool
False
                    case ApiReturnData
apiReturn of
                        ApiReturnGroupInfo [GroupInfo]
groupInfo -> [GroupInfo] -> ReaderT Env IO [GroupInfo]
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [GroupInfo]
groupInfo
                        ApiReturnData
_ -> [Char] -> ReaderT Env IO [GroupInfo]
forall a. HasCallStack => [Char] -> a
error [Char]
"should not happen"
                RepoLocal [[Char]]
baseDirs -> do
                    [PoseidonPackage]
pacCollection <- PackageReadOptions -> [[Char]] -> PoseidonIO [PoseidonPackage]
readPoseidonPackageCollection PackageReadOptions
pacReadOpts [[Char]]
baseDirs
                    [PoseidonPackage] -> ReaderT Env IO [GroupInfo]
forall (m :: * -> *).
MonadThrow m =>
[PoseidonPackage] -> m [GroupInfo]
getAllGroupInfo [PoseidonPackage]
pacCollection
            let tableH :: [[Char]]
tableH = [[Char]
"Group", [Char]
"Package", [Char]
"Package Version", [Char]
"Is Latest", [Char]
"Nr Individuals"]
                tableB :: [[[Char]]]
tableB = do
                    gi :: GroupInfo
gi@(GroupInfo [Char]
groupName PacNameAndVersion
_ Bool
isLatest Int
nrInds) <- [GroupInfo]
groupInfos
                    Bool
True <- Bool -> [Bool]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Bool
not Bool
onlyLatest Bool -> Bool -> Bool
|| Bool
isLatest)
                    [[Char]] -> [[[Char]]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [[Char]
groupName, GroupInfo -> [Char]
forall a. HasNameAndVersion a => a -> [Char]
getPacName GroupInfo
gi, Maybe Version -> [Char]
showMaybeVersion (GroupInfo -> Maybe Version
forall a. HasNameAndVersion a => a -> Maybe Version
getPacVersion GroupInfo
gi), Bool -> [Char]
forall a. Show a => a -> [Char]
show Bool
isLatest, Int -> [Char]
forall a. Show a => a -> [Char]
show Int
nrInds]
            ([[Char]], [[[Char]]]) -> ReaderT Env IO ([[Char]], [[[Char]]])
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]]
tableH, [[[Char]]]
tableB)
        ListIndividuals AddColSpec
addJannoColSpec -> do
            [ExtendedIndividualInfo]
extIndInfos <- case RepoLocationSpec
repoLocation of
                RepoRemote (ArchiveEndpoint [Char]
remoteURL Maybe [Char]
archive) -> do
                    [Char] -> PoseidonIO ()
logInfo [Char]
"Downloading individual data from server"
                    let addJannoColFlag :: [Char]
addJannoColFlag = case AddColSpec
addJannoColSpec of
                            AddColSpec
AddColAll -> [Char]
"&additionalJannoColumns=ALL"
                            AddColList [] -> [Char]
""
                            AddColList [[Char]]
moreJannoColumns -> [Char]
"&additionalJannoColumns=" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"," [[Char]]
moreJannoColumns
                    ApiReturnData
apiReturn <- [Char] -> Bool -> PoseidonIO ApiReturnData
processApiResponse ([Char]
remoteURL [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"/individuals" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Maybe [Char] -> [Char]
qDefault Maybe [Char]
archive [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
addJannoColFlag) Bool
False
                    case ApiReturnData
apiReturn of
                        ApiReturnExtIndividualInfo [ExtendedIndividualInfo]
indInfo -> [ExtendedIndividualInfo] -> ReaderT Env IO [ExtendedIndividualInfo]
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [ExtendedIndividualInfo]
indInfo
                        ApiReturnData
_ -> [Char] -> ReaderT Env IO [ExtendedIndividualInfo]
forall a. HasCallStack => [Char] -> a
error [Char]
"should not happen"
                RepoLocal [[Char]]
baseDirs -> do
                    [PoseidonPackage]
pacCollection <- PackageReadOptions -> [[Char]] -> PoseidonIO [PoseidonPackage]
readPoseidonPackageCollection PackageReadOptions
pacReadOpts [[Char]]
baseDirs
                    [PoseidonPackage]
-> AddColSpec -> ReaderT Env IO [ExtendedIndividualInfo]
forall (m :: * -> *).
MonadThrow m =>
[PoseidonPackage] -> AddColSpec -> m [ExtendedIndividualInfo]
getExtendedIndividualInfo [PoseidonPackage]
pacCollection AddColSpec
addJannoColSpec

            let addJannoCols :: [[Char]]
addJannoCols = case [ExtendedIndividualInfo]
extIndInfos of -- get all add-column names from first extIndInfo
                    []    -> []
                    (ExtendedIndividualInfo
e:[ExtendedIndividualInfo]
_) -> (([Char], Maybe [Char]) -> [Char])
-> [([Char], Maybe [Char])] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ([Char], Maybe [Char]) -> [Char]
forall a b. (a, b) -> a
fst ([([Char], Maybe [Char])] -> [[Char]])
-> (ExtendedIndividualInfo -> [([Char], Maybe [Char])])
-> ExtendedIndividualInfo
-> [[Char]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExtendedIndividualInfo -> [([Char], Maybe [Char])]
extIndInfoAddCols (ExtendedIndividualInfo -> [[Char]])
-> ExtendedIndividualInfo -> [[Char]]
forall a b. (a -> b) -> a -> b
$ ExtendedIndividualInfo
e

            -- warning in case the additional Columns do not exist in the entire janno dataset,
            -- we only output this warning if the columns were requested explicitly. Not if
            -- all columns were requested. We consider such an "all" request to mean "all columns that are present".
            case AddColSpec
addJannoColSpec of
                AddColList ([Char]
_:[[Char]]
_) -> do
                    [(Int, [Char])]
-> ((Int, [Char]) -> PoseidonIO ()) -> PoseidonIO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ ([Int] -> [[Char]] -> [(Int, [Char])]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
0..] [[Char]]
addJannoCols) (((Int, [Char]) -> PoseidonIO ()) -> PoseidonIO ())
-> ((Int, [Char]) -> PoseidonIO ()) -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$ \(Int
i, [Char]
columnKey) -> do
                        -- check entries in all individuals for that key
                        let nonEmptyEntries :: [[Char]]
nonEmptyEntries = [Maybe [Char]] -> [[Char]]
forall a. [Maybe a] -> [a]
catMaybes [([Char], Maybe [Char]) -> Maybe [Char]
forall a b. (a, b) -> b
snd ([([Char], Maybe [Char])]
entries [([Char], Maybe [Char])] -> Int -> ([Char], Maybe [Char])
forall a. HasCallStack => [a] -> Int -> a
!! Int
i) | ExtendedIndividualInfo [Char]
_ [[Char]]
_ PacNameAndVersion
_ Bool
_ [([Char], Maybe [Char])]
entries <- [ExtendedIndividualInfo]
extIndInfos]
                        Bool -> PoseidonIO () -> PoseidonIO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([[Char]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
nonEmptyEntries) (PoseidonIO () -> PoseidonIO ())
-> ([Char] -> PoseidonIO ()) -> [Char] -> PoseidonIO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> PoseidonIO ()
logWarning ([Char] -> PoseidonIO ()) -> [Char] -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$ [Char]
"Column Name " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
columnKey [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" not present in any individual"
                AddColSpec
_ -> () -> PoseidonIO ()
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

            let tableH :: [[Char]]
tableH = [[Char]
"Individual", [Char]
"Group", [Char]
"Package", [Char]
"PackageVersion", [Char]
"Is Latest"] [[Char]] -> [[Char]] -> [[Char]]
forall a. [a] -> [a] -> [a]
++ [[Char]]
addJannoCols
                tableB :: [[[Char]]]
tableB = do
                    i :: ExtendedIndividualInfo
i@(ExtendedIndividualInfo [Char]
name [[Char]]
groups PacNameAndVersion
_ Bool
isLatest [([Char], Maybe [Char])]
addColumnEntries) <- [ExtendedIndividualInfo]
extIndInfos
                    Bool
True <- Bool -> [Bool]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Bool
not Bool
onlyLatest Bool -> Bool -> Bool
|| Bool
isLatest)
                    [[Char]] -> [[[Char]]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]] -> [[[Char]]]) -> [[Char]] -> [[[Char]]]
forall a b. (a -> b) -> a -> b
$ [[Char]
name, [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
", " [[Char]]
groups, ExtendedIndividualInfo -> [Char]
forall a. HasNameAndVersion a => a -> [Char]
getPacName ExtendedIndividualInfo
i,
                              Maybe Version -> [Char]
showMaybeVersion (ExtendedIndividualInfo -> Maybe Version
forall a. HasNameAndVersion a => a -> Maybe Version
getPacVersion ExtendedIndividualInfo
i), Bool -> [Char]
forall a. Show a => a -> [Char]
show  Bool
isLatest] [[Char]] -> [[Char]] -> [[Char]]
forall a. [a] -> [a] -> [a]
++
                              (([Char], Maybe [Char]) -> [Char])
-> [([Char], Maybe [Char])] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ([Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
"n/a" (Maybe [Char] -> [Char])
-> (([Char], Maybe [Char]) -> Maybe [Char])
-> ([Char], Maybe [Char])
-> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Char], Maybe [Char]) -> Maybe [Char]
forall a b. (a, b) -> b
snd) [([Char], Maybe [Char])]
addColumnEntries
            ([[Char]], [[[Char]]]) -> ReaderT Env IO ([[Char]], [[[Char]]])
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]]
tableH, [[[Char]]]
tableB)
        ListBibliography AddColSpec
addColSpec -> do
            [BibliographyInfo]
bibInfos <- case RepoLocationSpec
repoLocation of
                RepoRemote (ArchiveEndpoint [Char]
remoteURL Maybe [Char]
archive) -> do
                    [Char] -> PoseidonIO ()
logInfo [Char]
"Downloading bibliography data from server"
                    let addJannoColFlag :: [Char]
addJannoColFlag = case AddColSpec
addColSpec of
                            AddColSpec
AddColAll -> [Char]
"&additionalBibColumns=ALL"
                            AddColList [] -> [Char]
""
                            AddColList [[Char]]
moreBibFields -> [Char]
"&additionalBibColumns=" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"," [[Char]]
moreBibFields
                    ApiReturnData
apiReturn <- [Char] -> Bool -> PoseidonIO ApiReturnData
processApiResponse ([Char]
remoteURL [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"/bibliography" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Maybe [Char] -> [Char]
qDefault Maybe [Char]
archive [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
addJannoColFlag) Bool
False
                    case ApiReturnData
apiReturn of
                        ApiReturnBibInfo [BibliographyInfo]
bibInfo -> [BibliographyInfo] -> ReaderT Env IO [BibliographyInfo]
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [BibliographyInfo]
bibInfo
                        ApiReturnData
_                        -> [Char] -> ReaderT Env IO [BibliographyInfo]
forall a. HasCallStack => [Char] -> a
error [Char]
"should not happen"
                RepoLocal [[Char]]
baseDirs -> do
                    [PoseidonPackage]
pacCollection <- PackageReadOptions -> [[Char]] -> PoseidonIO [PoseidonPackage]
readPoseidonPackageCollection PackageReadOptions
pacReadOpts [[Char]]
baseDirs
                    [PoseidonPackage]
-> AddColSpec -> ReaderT Env IO [BibliographyInfo]
forall (m :: * -> *).
MonadThrow m =>
[PoseidonPackage] -> AddColSpec -> m [BibliographyInfo]
getBibliographyInfo [PoseidonPackage]
pacCollection AddColSpec
addColSpec

            let addBibFieldNames :: [[Char]]
addBibFieldNames = case AddColSpec
addColSpec of
                    AddColSpec
AddColAll -> [[Char]] -> [[Char]]
forall a. Eq a => [a] -> [a]
nub ([[Char]] -> [[Char]])
-> ([BibliographyInfo] -> [[Char]])
-> [BibliographyInfo]
-> [[Char]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (BibliographyInfo -> [[Char]]) -> [BibliographyInfo] -> [[Char]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((([Char], Maybe [Char]) -> [Char])
-> [([Char], Maybe [Char])] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ([Char], Maybe [Char]) -> [Char]
forall a b. (a, b) -> a
fst ([([Char], Maybe [Char])] -> [[Char]])
-> (BibliographyInfo -> [([Char], Maybe [Char])])
-> BibliographyInfo
-> [[Char]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BibliographyInfo -> [([Char], Maybe [Char])]
bibInfoAddCols) ([BibliographyInfo] -> [[Char]]) -> [BibliographyInfo] -> [[Char]]
forall a b. (a -> b) -> a -> b
$ [BibliographyInfo]
bibInfos
                    AddColList [[Char]]
names -> [[Char]]
names

            -- warning in case the additional Columns do not exist in the entire janno dataset,
            -- we only output this warning if the columns were requested explicitly. Not if
            -- all columns were requested. We consider such an "all" request to mean "all columns that are present".
            case AddColSpec
addColSpec of
                AddColList ([Char]
_:[[Char]]
_) -> do
                    [[Char]] -> ([Char] -> PoseidonIO ()) -> PoseidonIO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [[Char]]
addBibFieldNames (([Char] -> PoseidonIO ()) -> PoseidonIO ())
-> ([Char] -> PoseidonIO ()) -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$ \[Char]
bibFieldKey -> do
                        -- check entries in all individuals for that key
                        let nonEmptyEntries :: [()]
nonEmptyEntries = do
                                BibliographyInfo
bibInfo <- [BibliographyInfo]
bibInfos
                                Just (Just [Char]
_) <- Maybe (Maybe [Char]) -> [Maybe (Maybe [Char])]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Maybe [Char]) -> [Maybe (Maybe [Char])])
-> Maybe (Maybe [Char]) -> [Maybe (Maybe [Char])]
forall a b. (a -> b) -> a -> b
$ [Char]
bibFieldKey [Char] -> [([Char], Maybe [Char])] -> Maybe (Maybe [Char])
forall a b. Eq a => a -> [(a, b)] -> Maybe b
`lookup` BibliographyInfo -> [([Char], Maybe [Char])]
bibInfoAddCols BibliographyInfo
bibInfo
                                () -> [()]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ()
                        Bool -> PoseidonIO () -> PoseidonIO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([()] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [()]
nonEmptyEntries) (PoseidonIO () -> PoseidonIO ())
-> ([Char] -> PoseidonIO ()) -> [Char] -> PoseidonIO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> PoseidonIO ()
logWarning ([Char] -> PoseidonIO ()) -> [Char] -> PoseidonIO ()
forall a b. (a -> b) -> a -> b
$
                            [Char]
"Bibliography field " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
bibFieldKey [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"is not present in any bibliography entry"
                AddColSpec
_ -> () -> PoseidonIO ()
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

            let tableH :: [[Char]]
tableH = [[Char]
"BibKey", [Char]
"Title", [Char]
"Author", [Char]
"Year", [Char]
"DOI",
                          [Char]
"Nr of samples"] [[Char]] -> [[Char]] -> [[Char]]
forall a. [a] -> [a] -> [a]
++ [[Char]]
addBibFieldNames
                tableB :: [[[Char]]]
tableB = do
                    BibliographyInfo
bibInfo <- [BibliographyInfo]
bibInfos
                    let addBibFieldColumns :: [[Char]]
addBibFieldColumns = do
                            [Char]
bibFieldName <- [[Char]]
addBibFieldNames
                            case [Char]
bibFieldName [Char] -> [([Char], Maybe [Char])] -> Maybe (Maybe [Char])
forall a b. Eq a => a -> [(a, b)] -> Maybe b
`lookup` BibliographyInfo -> [([Char], Maybe [Char])]
bibInfoAddCols BibliographyInfo
bibInfo of
                                Just (Just [Char]
v) -> [Char] -> [[Char]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
v
                                Maybe (Maybe [Char])
_             -> [Char] -> [[Char]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
""
                    [([Char], [Char])]
authors <- [Char] -> [[([Char], [Char])]]
forall (m :: * -> *).
MonadThrow m =>
[Char] -> m [([Char], [Char])]
parseAuthors ([Char] -> [[([Char], [Char])]])
-> (BibliographyInfo -> [Char])
-> BibliographyInfo
-> [[([Char], [Char])]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe [Char] -> [Char]
curateBibField (Maybe [Char] -> [Char])
-> (BibliographyInfo -> Maybe [Char]) -> BibliographyInfo -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BibliographyInfo -> Maybe [Char]
bibInfoAuthor (BibliographyInfo -> [[([Char], [Char])]])
-> BibliographyInfo -> [[([Char], [Char])]]
forall a b. (a -> b) -> a -> b
$ BibliographyInfo
bibInfo
                    [[Char]] -> [[[Char]]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]] -> [[[Char]]]) -> [[Char]] -> [[[Char]]]
forall a b. (a -> b) -> a -> b
$ [BibliographyInfo -> [Char]
bibInfoKey BibliographyInfo
bibInfo, Maybe [Char] -> [Char]
curateBibField (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ BibliographyInfo -> Maybe [Char]
bibInfoTitle BibliographyInfo
bibInfo, [([Char], [Char])] -> [Char]
authorAbbrvString [([Char], [Char])]
authors,
                              Maybe [Char] -> [Char]
curateBibField (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ BibliographyInfo -> Maybe [Char]
bibInfoYear BibliographyInfo
bibInfo,
                              Maybe [Char] -> [Char]
curateBibField (Maybe [Char] -> [Char]) -> Maybe [Char] -> [Char]
forall a b. (a -> b) -> a -> b
$ BibliographyInfo -> Maybe [Char]
bibInfoDoi BibliographyInfo
bibInfo, Int -> [Char]
forall a. Show a => a -> [Char]
show (BibliographyInfo -> Int
bibInfoNrSamples BibliographyInfo
bibInfo)] [[Char]] -> [[Char]] -> [[Char]]
forall a. [a] -> [a] -> [a]
++
                              [[Char]]
addBibFieldColumns
            ([[Char]], [[[Char]]]) -> ReaderT Env IO ([[Char]], [[[Char]]])
forall a. a -> ReaderT Env IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([[Char]]
tableH, [[[Char]]]
tableB)

    if Bool
rawOutput then
        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
$ [Char] -> IO ()
putStrLn ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"\n" [[Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"\t" [[Char]]
row | [[Char]]
row <- [[Char]]
tableH[[Char]] -> [[[Char]]] -> [[[Char]]]
forall a. a -> [a] -> [a]
:[[[Char]]]
tableB]
    else do
        let colSpecs :: [ColSpec]
colSpecs = Int -> ColSpec -> [ColSpec]
forall a. Int -> a -> [a]
replicate ([[Char]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[Char]]
tableH) (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)
        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
$ [Char] -> IO ()
putStrLn ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [ColSpec]
-> TableStyle -> HeaderSpec -> [RowGroup [Char]] -> [Char]
forall a.
Cell a =>
[ColSpec] -> TableStyle -> HeaderSpec -> [RowGroup a] -> [Char]
tableString [ColSpec]
colSpecs TableStyle
asciiRoundS ([[Char]] -> HeaderSpec
titlesH [[Char]]
tableH) [[[[Char]]] -> RowGroup [Char]
forall a. [Row a] -> RowGroup a
rowsG [[[Char]]]
tableB]
  where
    showMaybe :: Maybe String -> String
    showMaybe :: Maybe [Char] -> [Char]
showMaybe = [Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
"n/a"
    showMaybeVersion :: Maybe Version -> String
    showMaybeVersion :: Maybe Version -> [Char]
showMaybeVersion = [Char] -> (Version -> [Char]) -> Maybe Version -> [Char]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [Char]
"n/a" Version -> [Char]
showVersion

    -- this function is necessary, as BibTeX sometimes has arbitrary line breaks within fields,
    -- which we need to get rid of to avoid down-stream problems
    curateBibField :: Maybe String -> String
    curateBibField :: Maybe [Char] -> [Char]
curateBibField = Text -> [Char]
T.unpack (Text -> [Char])
-> (Maybe [Char] -> Text) -> Maybe [Char] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text] -> Text
T.intercalate Text
" " ([Text] -> Text)
-> (Maybe [Char] -> [Text]) -> Maybe [Char] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Text
T.strip ([Text] -> [Text])
-> (Maybe [Char] -> [Text]) -> Maybe [Char] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text]
T.lines (Text -> [Text])
-> (Maybe [Char] -> Text) -> Maybe [Char] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Text
T.pack ([Char] -> Text)
-> (Maybe [Char] -> [Char]) -> Maybe [Char] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
""
    showContributor :: ContributorSpec -> String
    showContributor :: ContributorSpec -> [Char]
showContributor (ContributorSpec [Char]
n [Char]
e Maybe ORCID
o) = [Char]
n [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" (" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
e [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
") ORCID: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char] -> (ORCID -> [Char]) -> Maybe ORCID -> [Char]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [Char]
"n/a" ORCID -> [Char]
renderORCID Maybe ORCID
o