{-# LANGUAGE CPP #-} {-# LANGUAGE DoAndIfThenElse #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-} module XSaiga.CGI where import qualified XSaiga.SolarmanTriplestore as App import Network.FastCGI import qualified Data.List as List import Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.ByteString.Lazy.Char8 as BLIO import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as BS import Data.Text.Encoding as E import Data.Text.Lazy.Encoding as EL #ifdef INSTORE import qualified XSaiga.LocalData as Local #endif #ifndef INSTORE --For caching name lookup import qualified Network.Socket as Net #endif import qualified XSaiga.Getts as Getts import qualified XSaiga.TypeAg2 as TypeAg2 import qualified Control.Monad.State.Strict as State import qualified Data.Map.Strict as Map import qualified Control.Monad as M import qualified XSaiga.ShowText as ShowText import qualified Data.Aeson as Aeson import qualified Data.Aeson.Text as AesonText import Data.Maybe import GHC.Generics data XSaigaResult = XSaigaResult { res :: T.Text, syntax :: T.Text } deriving (Generic, Show) data XSaigaConversationResult = XSaigaConversationResult { resConversation :: T.Text } deriving (Generic, Show) data XSaigaParseError = XSaigaParseError { resError :: T.Text } deriving (Generic, Show) instance Aeson.ToJSON XSaigaResult where toEncoding = Aeson.genericToEncoding Aeson.defaultOptions instance Aeson.ToJSON XSaigaConversationResult where toEncoding = Aeson.genericToEncoding Aeson.defaultOptions instance Aeson.ToJSON XSaigaParseError where toEncoding = Aeson.genericToEncoding Aeson.defaultOptions cgiMain :: (Getts.TripleStore m) => m -> CGI CGIResult cgiMain dataStore = do query <- getInputFPS "query" case query of Nothing -> outputFPS $ EL.encodeUtf8 "error" Just input -> do out <- liftIO $ interpret' dataStore (E.decodeUtf8 $ BL.toStrict input) setHeader "Content-type" "text/plain; charset=utf-8" outputFPS out namespace_uri = "http://solarman.richard.myweb.cs.uwindsor.ca#" endpoint_uri = "speechweb2.cs.uwindsor.ca/sparql" --Asterius requires https asteriusRemoteData = Getts.SPARQL ("https://" ++ endpoint_uri) namespace_uri --CGI requires http (Network.FastCGI limitation) cgiRemoteData = Getts.SPARQL ("http://" ++ endpoint_uri) namespace_uri --Inside an #ifdef to avoid the network-based dependencies, helps keep size down for completely offline builds main :: IO () #ifdef INSTORE main = do runFastCGIorCGI (handleErrors $ cgiMain Local.localData) --No need to resolve anything #else main = do --For CGI, not Asterius resolved_endpoint <- resolveEndpoint cgiRemoteData runFastCGIorCGI (handleErrors $ cgiMain resolved_endpoint) where resolveEndpoint (Getts.SPARQL url namespace_uri) = do x <- Net.getAddrInfo Nothing (Just $ getServer url) (Just "http") --Network.FastCGI requires plain HTTP :( return $ Getts.SPARQL (newURL (showAddress x) (getURLPath url)) namespace_uri where getServer = List.takeWhile (\x -> not $ x `elem` ['/']) . List.drop 7 --drop the "http://" part and take until the first "/" character showAddress = show . Net.addrAddress . List.head getURLPath xs = List.drop (7 + (List.length $ getServer xs)) xs newURL server path = "http://" ++ server ++ path #endif interpret "ask them to be quiet" = Just $ "Hello. Quiet please. My " `T.append` "masters are going to talk. Quiet please." interpret "introduce yourself solar man" = Just $ "Hello. Hello. My name is Solar man." `T.append` " Thank you for coming to my party." `T.append` " I am very pleased to meet you." interpret "what can i say" = Just $ "You can say. hello there. what is your name." `T.append` " you can ask me about the moons and the planets." `T.append` " such as, who discovered a moon." `T.append` " who discovered two moons." `T.append` " which moons were discovered by kuiper." `T.append` " who discovered phobos." `T.append` " which planet is orbited by miranda." `T.append` " how many moons orbit saturn." `T.append` " and other similar questions." `T.append` " who are you. where do you live." `T.append` " tell me a joke. who made you." `T.append` " who do you know. what is your favorite band." `T.append` " who is the vice president at the university of windsor." `T.append` " who is the president at the university of windsor." `T.append` " who is the dean of science at the university of windsor." interpret "hi" = Just $ "Hi there. My name is solar man" interpret "hello" = Just $ "hello. My name is solar man." interpret "hello there" = Just $ "Good day to you" interpret "hello solar man" = Just $ "hello. How are you" interpret "goodbye" = Just $ "goodbye. See you in the stars." interpret "goodbye solar man" = interpret "goodbye" interpret "fine thanks" = Just $ "Good, so am I. Except for a bit of back ache" interpret "thanks" = Just $ "you are welcome" interpret "thanks solar man" = Just $ "you are most welcome" interpret "yes please" = Just $ "yes please? What did you want? My memory is getting bad" interpret "what is your name" = Just $ "My name is solar man." interpret "who are you" = Just $ "My name is solar man. I know about the planets and the" `T.append` " moons, and the people who discovered them" interpret "where do you live" = Just $ "I live in a dark cold computer. " `T.append` "The center of my universe is Lambton Tower, at the University of Windsor." interpret "what do you know" = Just $ "Not much I am afraid. I am just beginning to learn. I know a bit about " `T.append` "the planets, the moons, and the people who discovered them. " `T.append` "My master will teach me some more when he gets another grant " interpret "how old are you" = Just $ "older than you think. And much older than my friends Judy and Monty." interpret "who made you" = Just $ "I. B. M. and Opera Software made my ears and vocal chords. William Ma connected my " `T.append` "ears to my brain, and Doctor Frost, master of the universe, made " `T.append` "my brain" interpret "what is your favorite band" = Just $ "Pink Floyd. I love, Dark Side of the Moon" interpret "who is the vice president at the university of windsor" =Just $ "Douglas Kneale" interpret "who is the president at the university of windsor" = Just $ "Doctor Robert Gordon." interpret "who is the dean of science at the university of windsor" = Just $ "Doctor Chris Houser" interpret "tell me a poem" = Just $ "do not know any poems. But my friend, Judy, does" interpret "know any poems" = Just $ "no but my friend, Judy does;" interpret "tell me a joke" = Just $ "did you hear about the Computer Scientist who thought his computer" `T.append` "was a car. He had a hard drive home every day" interpret "know any jokes" = Just $ "just one. My friend Monty knows one too." interpret "who is judy" = Just $ "She is my friend. She knows about poetry" interpret "who is monty" = Just $ "Monty is my friend. He is a student" `T.append` " at the university of Windsor." {- interpret "can I talk to judy" ="yes. here she is" `T.append` "http://cs.uwindsor.ca/~speechweb/p_d_speechweb/judy/judy.xml" interpret "can I talk to monty" ="yes. here he is" `T.append` "http://cs.uwindsor.ca/~speechweb/p_d_speechweb/monty/monty.xml" interpret "can I talk to solar man" ="yes. here he is" `T.append` "http://cs.uwindsor.ca/~speechweb/p_d_speechweb/solarman/solarman.xml" -} interpret "who do you know" = Just $ "i only know three people. Judy, Monty, and Solarman." interpret _ = Nothing interpret'' = TypeAg2.getQUVAL . List.head . App.parse --TODO: multiple interpretations! need to optimize these! interpret' dataStore input = do let firstpass = interpret input case firstpass of Nothing -> do let attTrees = App.parseTree input let atts = Prelude.map fst attTrees let trees = Prelude.map snd attTrees let sems = List.map TypeAg2.getQUVAL atts --outs <- mapM evaluate interpretations --TODO: this is a code smell -- needs to be abstracted -- looks like SemFunc let flatQueries = Prelude.foldr mergeFlat ([],[],[]) sems let optQueries = TypeAg2.flatOptimize flatQueries rtriples <- TypeAg2.getReducedTriplestore dataStore optQueries (outs, _) <- M.foldM (nextInterp rtriples) ([], Map.empty) sems --TODO: save the state for later? paper opportunity if List.null attTrees then return $ Aeson.encode $ XSaigaParseError "Do not know that one yet, will work on it tonight" else return $ Aeson.encode $ List.zipWith XSaigaResult outs trees Just result -> return $ Aeson.encode $ XSaigaConversationResult result where mergeFlat interp flatGetts = let g = TypeAg2.getGetts interp in TypeAg2.merge (TypeAg2.flattenGetts g) flatGetts nextInterp rtriples (txt, state) interp = do --TODO: improves by about 2 seconds on heavy workloads -- could be better! (out, nState) <- evaluate rtriples interp state return (txt ++ [out], nState) evaluate rtriples interp startState = do return $ State.runState (TypeAg2.getSem interp rtriples) startState --TODO: needs to use resolved_endpoint URI? or refactored as LocalData/SPARQL/etc runQuery dataStore interp = do let g = TypeAg2.getGetts interp let flatQueries = TypeAg2.flattenGetts g let optQueries = TypeAg2.flatOptimize flatQueries rtriples <- TypeAg2.getReducedTriplestore dataStore optQueries (out, _) <- evaluate rtriples interp Map.empty return out interpret''' dataStore input = interpret' dataStore input >>= BLIO.putStrLn