packages feed

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!"