packages feed

concrete-haskell-0.1.0.9: src/Data/Concrete/Utils.hs

{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
module Data.Concrete.Utils
       (
         getUUID
       , createAnnotationMetadata
       , readCommunicationsFromBytes
       , readCommunicationsFromTar
       , readCommunicationsFromZip
       , writeCommunications
       , writeCommunication
       , writeCommunicationToZip
       , showCommunication
       , sendCommunication
       , connectToService
       ) where

import GHC.Generics
import qualified Data.Concrete as C
import Path.IO (resolveFile')
import Data.Concrete (Communication(..), Section(..), UUID(..), default_Communication, read_Communication)
--import Data.Concrete (StoreCommunicationService_Client)
import Data.Text
import Data.Maybe (fromJust, maybeToList, fromMaybe)
import Data.ByteString.Lazy
import Data.Map
import Data.UUID.V4 (nextRandom)
import Data.UUID (toString)
import qualified Data.Text.Lazy as T
import Thrift
import Thrift.Transport.Handle
import Thrift.Transport.Framed
import Thrift.Transport.Empty
import Thrift.Protocol.Compact
import Thrift.Protocol
import Thrift.Transport.IOBuffer
import Thrift.Transport
import qualified Data.List as L
import qualified Data.Map as Map
import qualified Data.ByteString.Lazy as BS
import qualified Codec.Compression.GZip as GZip
import qualified Codec.Compression.BZip as BZip
import qualified Codec.Archive.Tar       as Tar
import qualified Codec.Archive.Zip       as Zip
import qualified Codec.Archive.Tar.Entry as Tar
import Data.Time
import Data.Time.Clock.POSIX
import System.FilePath (takeFileName, (</>), (<.>))
import Data.Either (rights)
import Control.Monad (liftM, join)
import Data.Foldable (foldr)
import System.IO (Handle)
import qualified Data.Vector as V
import qualified Network as Net

getUUID :: IO UUID
getUUID = do
  uuid <- (T.pack . toString) <$> nextRandom
  return $ UUID uuid

readCommunicationsFromTar :: ByteString -> IO [Communication]
readCommunicationsFromTar bs = do
  let es = Tar.read bs
      cs = Tar.foldEntries (\e x -> (Tar.entryContent e):x) [] (\e -> []) es
  sequence $ L.map (\ (Tar.NormalFile bs _) -> stringToComm bs) cs

writeCommunicationsToTar :: [Communication] -> ByteString
writeCommunicationsToTar cs = error "unimplemented"

readCommunicationsFromZip :: String -> IO [Communication]
readCommunicationsFromZip f = do
  f' <- resolveFile' f
  es <- Zip.withArchive f' ((Map.keys <$> Zip.getEntries))
  bss <- Zip.withArchive f' ((sequence . (L.map Zip.getEntry)) es)
  sequence $ L.map (stringToComm . fromStrict) bss
  
writeCommunicationsToZip :: String -> [Communication] -> IO ()
writeCommunicationsToZip f cs = return ()

writeCommunicationToZip :: String -> [Communication] -> IO ()
writeCommunicationToZip f cs = return ()

writeCommunications :: Handle -> [Communication] -> IO ()
writeCommunications out cs = do
  let tarPath = "comms"
  texts <- sequence [commToString c | c <- cs]
  let names = rights [Tar.toTarPath False (tarPath </> ((T.unpack . C.communication_id) c) <.> "comm") | c <- cs]
      entries = [Tar.fileEntry n t|(n, t) <- L.zip names texts]
      t = Tar.write entries
  (BS.hPutStr out . GZip.compress) t

writeCommunication :: Handle -> Communication -> IO ()
writeCommunication out c = do
  t <- commToString c
  BS.hPutStr out t
 -- 
connectToService :: String -> Int -> IO (CompactProtocol (FramedTransport Handle), CompactProtocol (FramedTransport Handle))
connectToService h p = do
  transport <- hOpen (h, Net.PortNumber $ fromIntegral p)
  transport' <- openFramedTransport transport
  let protocol = CompactProtocol transport'
  return (protocol, protocol)

sendCommunication :: String -> Int -> Communication -> IO ()
sendCommunication h p c = do  
  return ()

readCommunicationsFromBytes :: BS.ByteString -> IO [Communication]
readCommunicationsFromBytes t = do
  transport <- newTString
  fillBuf (getRead transport) t
  let iproto = CompactProtocol transport
  c <- read_Communication iproto
  c' <- read_Communication iproto
  return [c']
  where
    readCommunication' pr cs = do
      o <- tIsOpen $ getTransport pr      
      case o of
        True -> do
          c <- read_Communication pr
          readCommunication' pr $ c:cs
        False -> return cs

entryToString :: Tar.EntryContent -> BS.ByteString
entryToString (Tar.NormalFile s _) = s

data TString = TString ReadBuffer WriteBuffer

getWrite :: TString -> WriteBuffer
getWrite (TString r w) = w

getRead :: TString -> ReadBuffer
getRead (TString r w) = r          

newTString = do
  w <- newWriteBuffer
  r <- newReadBuffer
  return $ TString r w

instance Transport TString where
    tIsOpen (TString r w) = do
      p <- peekBuf r
      case p of
        Nothing -> return False
        _ -> return True
    tClose (TString r w) = case peekBuf r of
                             _ -> return ()
    tRead (TString r w) i = readBuf r i
    tPeek (TString r w) = peekBuf r
    tWrite (TString r w) bs = writeBuf w bs
    tFlush (TString r w) = flushBuf w >> return ()
  
commToString :: Communication -> IO BS.ByteString
commToString c = do  
  otransport <- newTString
  let oproto = CompactProtocol otransport
  C.write_Communication oproto c
  flushBuf (getWrite otransport)

stringToComm :: BS.ByteString -> IO Communication
stringToComm s = do
  otransport@(TString r w) <- newTString
  fillBuf r s
  let oproto = CompactProtocol otransport
  C.read_Communication oproto

dropTarSuffix :: String -> String
dropTarSuffix f = (L.reverse . L.drop n . L.reverse) f
  where
    n = if ".tgz" `L.isSuffixOf` f then 4 else if ".tar.gz" `L.isSuffixOf` f then 7 else 0

createAnnotationMetadata :: String -> IO C.AnnotationMetadata
createAnnotationMetadata s = do
  time <- round `fmap` getPOSIXTime
  return C.default_AnnotationMetadata { C.annotationMetadata_tool=T.pack s
                                      , C.annotationMetadata_timestamp=time
                                      }

showSection :: T.Text -> C.Section -> T.Text
showSection t s = T.concat ["    ", ((fromMaybe "*NO LABEL*" . C.section_label) s), " == ", t']
  where
    C.TextSpan s' e' = (fromJust . C.section_textSpan) s
    k = C.section_kind s
    t' = substr t (fromIntegral s') (fromIntegral e')

substr :: T.Text -> Int -> Int -> T.Text
substr t s e = res
  where
    (_, start) = T.splitAt (fromIntegral s) t    
    res = T.take (fromIntegral $ e - s) start

showCommunication :: Communication -> T.Text
showCommunication c = T.concat [C.communication_id c, " ", C.communication_type c, "\n", "  Content:", "\n", T.intercalate "\n" sects, "\n"]
  where
    ss = L.concat $ L.map V.toList (maybeToList (C.communication_sectionList c))
    t = (fromJust . C.communication_text) c
    sects = L.map (showSection t) ((L.filter (\x -> section_kind x == "content")) ss)