packages feed

convert-annotation-0.2.0.0: app/Main.hs

{- convert-annotation
Gregory W. Schwartz

Converts an unknown annotation to Ensembl's annotation, or other annotation.
-}

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators     #-}

module Main where

-- Standard
import Data.Maybe
import Data.Char
import Data.List
import Control.Monad
import GHC.Generics

-- Cabal
import qualified Data.Text as T
import qualified Data.Csv as CSV
import qualified Control.Lens as L
import Pipes
import qualified Pipes.Prelude as P
import qualified Pipes.ByteString as PB
import Pipes.Csv
import Options.Generic

-- Local
import Types
import EnsemblConvert
import HUGOConvert
import UniProtConvert

-- | Command line arguments
data Options = Info { delimiter        :: Maybe String
                                      <?> "([,] | CHAR) The delimiter of the CSV file."
                    , database         :: String
                                      <?> "(Ensembl | HUGO TYPE | UniProt) Which database to convert with. TYPE is the type of the original gene symbol. The compatible list is in http://www.genenames.org/help/rest-web-service-help. HUGO is only supported for Annotation."
                    , descriptionField :: String
                                      <?> "(Other TEXT | Description | Synonyms) The info to retrieve about the identifier. Description provides information about the identifier while synonyms provides alternate identifiers for the same entity. Returns a list of information (delimited by '/') for each match to Ensembl's cross references. For UniProt, enter a valid column (http://www.uniprot.org/help/programmatic_access)."
                    , column           :: T.Text
                                      <?> "(COLUMN) The column containing the identifier. Must be a valid id for info."
                    , remove           :: Bool
                                      <?> "Whether to remove empty results (no matches to the database)."
                    }
             | Annotation { delimiter :: Maybe String
                                     <?> "([,] | CHAR) The delimiter of the CSV file."
                          , database  :: String
                                     <?> "(Ensembl | HUGO TYPE | UniProt) Which database to convert with. TYPE is the type of the original gene symbol. The compatible list is in http://www.genenames.org/help/rest-web-service-help. HUGO is only supported for Annotation."
                          , column    :: T.Text
                                     <?> "(COLUMN) The column containing the identifier. Must be a valid id for info."
                          , remove    :: Bool
                                     <?> "Whether to remove empty results (no matches to the database)."
                          }
               deriving (Generic)

instance ParseRecord Options

-- | Map the header column to the rest of the file for converting that
-- column.
pipeConvert :: Options -> Pipe [T.Text] [T.Text] IO ()
pipeConvert opts = do
    h <- await

    let c = col opts h

    yield h

    forever $ do
        x    <- await
        newX <- lift . convert opts . (!! c) $ x
        unless
            ((unHelpful . remove $ opts) && T.null newX)
            (yield . L.set (L.ix c) newX $ x)
        return ()

    return ()

-- | Get the index of the column.
col :: Options -> [T.Text] -> Int
col opts =
    fromMaybe (error "Column not found.") . elemIndex (unHelpful $ column opts)

-- | The conversion process.
convert :: Options -> T.Text -> IO T.Text
convert opts@(Info { descriptionField = df }) =
    fmap (fromMaybe "" . fmap unDesc)
        . whichDesc (read . unHelpful . database $ opts)
        . UnknownAnn
  where
    whichDesc Ensembl  = toEnsemblDesc (read . unHelpful $ df)
    whichDesc (HUGO _) = error "HUGO description not yet supported."
    whichDesc UniProt  = toUniProtDesc (read . unHelpful $ df)
convert opts@(Annotation {})                  =
    fmap (fromMaybe "" . fmap unAnn)
        . whichAnn (read . unHelpful . database $ opts)
        . UnknownAnn
  where
    whichAnn Ensembl          = toEnsemblAnn
    whichAnn (HUGO queryType) = toHUGOAnn . HUGOType $ queryType
    whichAnn UniProt          = toUniProtAnn

main :: IO ()
main = do
    opts <- getRecord "convert-annotation, Gregory W. Schwartz.\
                      \ Converts an unknown annotation to some other\
                      \ annotation."

    let delim = case unHelpful . delimiter $ opts of
                    Nothing         -> ','
                    (Just "\\t")    -> '\t'
                    (Just [x])      -> x
                    (Just [])       -> error "No delimiter set"
                    _         -> error "Delimiter is one character"
        csvOpts = CSV.defaultDecodeOptions
                    { CSV.decDelimiter = fromIntegral (ord delim) }

    runEffect $ decodeWith csvOpts NoHeader PB.stdin
            >-> P.concat
            >-> (pipeConvert opts)
            >-> encode
            >-> PB.stdout

    return ()