solidabis-koodihaaste/src/API.hs

93 lines
3.1 KiB
Haskell
Raw Normal View History

2019-10-15 22:52:03 +03:00
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
2019-10-15 23:47:32 +03:00
{-|
Module : API
Description : The application API and handlers
Copyright : (c) Mats Rauhala, 2019
License : BSD3
Maintainer : mats.rauhala@iki.fi
Stability : experimental
Portability : POSIX
Provides the API and handlers for the application.
Even if this is only one module, I'm using this same style in all of my projects. Each level of APIs provide their own `API` type and a `handler` who is responsible only for that specific subsection of an API.
-}
2019-10-15 21:01:57 +03:00
module API where
2019-10-15 22:52:03 +03:00
import Control.Monad.Reader (MonadReader, asks)
import Control.Monad.Trans (MonadIO)
2019-10-15 23:12:33 +03:00
import Data.Either (partitionEithers)
2019-10-15 22:52:03 +03:00
import Data.Foldable (traverse_)
2019-10-15 23:12:33 +03:00
import Data.List (find, sortOn)
2019-10-15 22:52:03 +03:00
import Data.Text (Text)
2019-10-15 23:12:33 +03:00
import qualified Data.Text as T
2019-10-15 22:52:03 +03:00
import Lucid.Base (HtmlT, ToHtml (..))
import Lucid.Html5
import Servant.API
import Servant.API.Generic
import Servant.HTML.Lucid
import Servant.Server.Generic
2019-10-16 13:26:34 +03:00
import Control.Parallel.Strategies
2019-10-15 22:52:03 +03:00
2019-10-15 23:12:33 +03:00
import Data.Caesar
import Data.Language
2019-10-15 22:52:03 +03:00
import Solidabis.API (HasClientEnv, HasToken, bullshits,
getBullshits, message)
2019-10-15 23:47:32 +03:00
-- | Data for the frontpage
--
-- Our "UI" only cares about the decrypted and non-decryptable sentences
2019-10-15 22:52:03 +03:00
data Index =
Index { converted :: [Text]
, unconverted :: [Text]
}
2019-10-15 23:47:32 +03:00
-- | Wrap a UI within an HTML page
2019-10-15 22:52:03 +03:00
page :: Monad m => HtmlT m () -> HtmlT m ()
page = doctypehtml_ . body_
instance ToHtml Index where
toHtml Index{..} = page $ div_ $ do
div_ [class_ "decrypted"] $ do
h3_ "No bullshit"
ul_ $ traverse_ (li_ . toHtml) converted
div_ [class_ "bullshit"] $ do
h3_ "Bullshit"
ul_ $ traverse_ (li_ . toHtml) unconverted
toHtmlRaw = toHtml
2019-10-15 23:47:32 +03:00
-- | Our API type
--
-- Consists only of the index route
2019-10-15 22:52:03 +03:00
newtype API route
= API { _index :: route :- Get '[HTML] Index }
deriving Generic
2019-10-15 23:47:32 +03:00
-- | API handler
--
-- Compares the sentences given by the solidabis api against the model
2019-10-15 22:52:03 +03:00
handler
:: (HasModel r, MonadReader r m, HasToken r, HasClientEnv r, MonadIO m)
=> API (AsServerT m)
handler =
API {
_index = do
languageModel <- asks getModel
sentences <- map message . bullshits <$> getBullshits
2019-10-16 13:26:34 +03:00
let (bullshit, noBullshit) = partitionEithers best
best = map (findBest languageModel) sentences `using` parBuffer 10 rdeepseq
2019-10-15 22:52:03 +03:00
return $ Index noBullshit bullshit
}
where
findBest :: Model -> Text -> Either Text Text
findBest model sentence =
let caesared = map (\n -> T.map (caesar n) . T.toLower $ sentence) [0..29]
2019-10-15 23:13:34 +03:00
ranked = sortOn fst [(goodness model x, x) | x <- caesared]
2019-10-15 22:52:03 +03:00
found = find (\(rank, _) -> rank > -7.9) (reverse ranked)
2019-10-15 23:12:33 +03:00
in maybe (Left sentence) (Right . snd) found