packages feed

fathead-util-0.1.0.0: src/DDG/FatHead/Util/DB.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module DDG.FatHead.Util.DB
  ( Entry
  , Title
  , title
  , redirect
  , article
  , disambiguation
  , writeOutput
  ) where


import Data.Char (ord)
import Data.Csv
import qualified Data.Text as DT
import qualified Data.ByteString.Lazy as BSZ
import qualified Data.ByteString as BS
import Data.Text (Text)
import Network.URI (URI)
import Data.Monoid ((<>))
import Data.List (intercalate)


type FieldText = String


-- | The title of an entry.
newtype Title = Title { getTitle :: String } deriving (ToField)


-- | Construct an 'Entry' title from a 'String'.
title :: String -> Title
title = Title


type Abstract = String


newtype Disambiguation = Disambiguation { getDisambiguation :: [(Entry, String)] }


instance ToField Disambiguation where
  toField (Disambiguation xs) = toField . intercalate "\\n"
    $ fmap (\(e,d) -> concat ["*[[", getTitle (entryTitle e), "]], ", d, "."]) xs


newtype Categories = Categories { getCategories :: [Text] }
  deriving (Monoid)


instance ToField Categories where
  toField (Categories { getCategories = cs }) = toField (DT.unlines cs)


instance ToField URI where
  toField = toField . show


data Entry =
  EntryArticle { articleTitle :: Title
               , articleCategories :: Maybe Categories
               , articleAbstract :: Abstract
               , articleUrl :: URI
               }
  | EntryRedirect { redirectFrom :: Title
                  , redirectTo   :: Title
                  }
  | EntryDisambiguation { disambiguationTitle :: Title
                        , disambiguationD     :: Disambiguation
                        }


entryTitle :: Entry -> Title
entryTitle a@(EntryArticle{}) = articleTitle a
entryTitle r@(EntryRedirect{}) = redirectFrom r
entryTitle d@(EntryDisambiguation{}) = disambiguationTitle d


emptyField :: Field
emptyField = toField ("" :: FieldText)


typeArticle, typeDisambiguation, typeRedirect :: (Name, Field)
typeArticle        = ("type", "A")
typeDisambiguation = ("type", "D")
typeRedirect       = ("type", "R")


instance ToNamedRecord Entry where
  toNamedRecord (EntryRedirect { redirectFrom = from, redirectTo = to }) =
    namedRecord ([ "title" .= toField from
                 , "redirect" .= toField to
                 , typeRedirect ] ++
      emptyFieldsExcept ["title", "type", "redirect"])
  toNamedRecord (EntryDisambiguation { disambiguationTitle = title
                                     , disambiguationD = disambiguation }) =
    namedRecord ([ "title" .= toField title
                 , "disambiguation" .= toField disambiguation
                 , typeDisambiguation ] ++
      emptyFieldsExcept ["title", "type", "disambiguation"])
  toNamedRecord (EntryArticle { articleTitle = title
                              , articleCategories = cs
                              , articleAbstract = a
                              , articleUrl = u }) =
    namedRecord ([ "title"      .= toField title
                 , "categories" .= toField cs
                 , "abstract"   .= toField a
                 , "source_url"  .= toField u
                 , typeArticle ] ++
      emptyFieldsExcept ["title", "type", "categories", "abstract", "source_url"])


encodeOptions :: EncodeOptions
encodeOptions = defaultEncodeOptions { encDelimiter = tab
                                     , encIncludeHeader = True
                                     , encUseCrLf = False
                                     , encQuoting = QuoteNone
                                     }
  where tab = fromIntegral . ord $ '\t'


outputFields :: [BS.ByteString]
outputFields = [ "title" , "type", "redirect", "null1"
               , "categories", "null2", "see_also", "null3"
               , "external_links", "disambiguation"
               , "images", "abstract", "source_url"
               ]


emptyFieldsExcept :: [BS.ByteString] -> [(Name, Field)]
emptyFieldsExcept xs = fmap (`namedField` emptyField) $ filter (`notElem` xs) outputFields
  where bar = length (1,1)


outputHeader :: Header
outputHeader = header outputFields


article :: Title -> Abstract -> URI -> Categories -> Entry
article t a u cs = EntryArticle { articleTitle =  t
                             , articleCategories = Just cs
                             , articleAbstract = a
                             , articleUrl = u
                             }


-- | @redirect from to@ creates an 'Entry' that redirects
-- queries for @from@ to the entry at @to@.
redirect :: Title -> Title -> Entry
redirect from to = EntryRedirect { redirectFrom = from
                                 , redirectTo   = to
                                 }


-- | Create a disambiguation page for the given title.
disambiguation :: Title -> Disambiguation -> Entry
disambiguation t d = EntryDisambiguation { disambiguationTitle = t
                                         , disambiguationD = d
                                         }


writeOutput :: [Entry] -> IO ()
writeOutput = BSZ.writeFile "output.txt"
              . encodeByNameWith encodeOptions outputHeader