hasklepias-0.20.0: src/Hasklepias/MakeApp.hs
{-|
Module : Hasklepias.MakeApp
Description : Functions for creating a cohort application
Copyright : (c) NoviSci, Inc 2020
License : BSD3
Maintainer : bsaul@novisci.com
-}
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Hasklepias.MakeApp
( makeCohortApp
) where
import Control.Applicative ( Applicative )
import Control.Monad ( Functor(fmap)
, Monad(return)
)
import Data.Aeson ( FromJSON
, ToJSON(..)
, encode
)
import Data.Bifunctor ( Bifunctor(second) )
import qualified Data.ByteString.Lazy as B
import Data.ByteString.Lazy.Char8 as C
( putStrLn )
import Data.Function ( ($)
, (.)
)
import Data.List ( (++) )
import Data.Map.Strict ( fromList
, toList
)
import Data.Maybe ( Maybe )
import Data.Monoid ( Monoid(mconcat) )
import Data.String ( String )
import Data.Text ( Text
, pack
)
import Data.Tuple ( fst
, snd
)
import GHC.IO ( IO )
import GHC.Show ( Show(show) )
import Cohort
import EventData ( Events )
import IntervalAlgebra ( IntervalSizeable )
import Colog ( (<&)
, (>$)
, HasLog(..)
, LogAction(..)
, Message
, WithLog
, log
, logError
, logInfo
, logPrint
, logPrintStderr
, logStringStderr
, logStringStdout
, logText
, richMessageAction
, withLog
)
import Control.Monad.IO.Class ( MonadIO
, liftIO
)
import Control.Monad.Reader ( MonadReader(..)
, ReaderT(..)
)
import System.Console.CmdArgs ( (&=)
, Data
, Typeable
, cmdArgs
, help
, summary
)
import System.Environment ( getArgs )
-- a stub to add more arguments to later
data MakeCohort = MakeCohort
deriving (Show, Data, Typeable)
makeAppArgs
:: String -- ^ name of the application
-> String -- ^ version of the application
-> MakeCohort
makeAppArgs name version =
MakeCohort{} &= help "Pass event data via stdin." &= summary
(name ++ " " ++ version)
makeCohortBuilder
:: ( FromJSON a
, Show a
, IntervalSizeable a b
, ToJSON d0
, ShapeCohort d0
, Monad m
)
=> CohortSetSpec (Events a) d0
-> m (B.ByteString -> m ([ParseError], CohortSet d0))
makeCohortBuilder specs =
return (return . second (evalCohortSet specs) . parsePopulationLines)
reshapeCohortSet :: (Cohort d0 -> CohortJSON) -> CohortSet d0 -> CohortSetJSON
reshapeCohortSet g x =
MkCohortSetJSON $ fromList $ fmap (fmap g) (toList $ getCohortSet x)
shapeOutput
:: (Monad m, ShapeCohort d0)
=> (Cohort d0 -> CohortJSON)
-> m ([ParseError], CohortSet d0)
-> m ([ParseError], CohortSetJSON)
shapeOutput shape = fmap (fmap (reshapeCohortSet shape))
-- logging based on example here:
-- https://github.com/kowainik/co-log/blob/main/co-log/tutorials/Main.hs
parseErrorL :: LogAction IO ParseError
parseErrorL = logPrintStderr
logParseErrors :: [ParseError] -> IO ()
logParseErrors x = mconcat $ fmap (parseErrorL <&) x
-- | Make a command line cohort building application.
makeCohortApp
:: (FromJSON a, Show a, IntervalSizeable a b, ToJSON d0, ShapeCohort d0)
=> String -- ^ cohort name
-> String -- ^ app version
-> (Cohort d0 -> CohortJSON) -- ^ a function which specifies the output shape
-> CohortSetSpec (Events a) d0 -- ^ a list of cohort specifications
-> IO ()
makeCohortApp name version shape spec = do
args <- cmdArgs (makeAppArgs name version)
-- let logger = logStringStdout
let errLog = logStringStderr
errLog <& "Creating cohort builder..."
app <- makeCohortBuilder spec
errLog <& "Reading data from stdin..."
-- TODO: give error if no contents within some amount of time
dat <- B.getContents
errLog <& "Bulding cohort..."
res <- shapeOutput shape (app dat)
logParseErrors (fst res)
errLog <& "Encoding cohort(s) output and writing to stdout..."
C.putStrLn (encode (toJSON (snd res)))
errLog <& "Cohort build complete!"