packages feed

liblastfm-0.6.1: example/sort-friends.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{- This example shows how to extract all
 - user friends, get their similarity score and
 - then sort them based on it outputting top 5
 -
 - User friends of which are searched is 'target'
 -
 - Most of hard work there is on 'aeson-lens' actually :)
 -
 - Sample output:
 -
 - % ./examples/sort-friends.hs
 - Artvart: 0.9969767332077
 - smpcln: 0.9965825676918
 - nv0id: 0.86804795265198
 - QueenrXy: 0.5932799577713
 - GhostOsaka: 0.2875879406929
 -
 - Notice: you may want to adjust maximum open files limit
 - if testing on users with relatively large friends count
 -}
import           Control.Concurrent.Async          -- async
import           Control.Lens                      -- lens
import           Data.Aeson.Lens                   -- lens-aeson
import           Data.Aeson (Value)                -- aeson
import           Data.Function (on)                -- base
import           Data.List (sortBy)                -- base
import           Data.Maybe (fromMaybe)            -- base
import           Data.Monoid ((<>))                -- base
import           Data.Text (Text)                  -- text
import           Data.Text.Lens (unpacked)         -- lens
import qualified Data.Text.IO as Text              -- text
import           Lastfm hiding (to)                -- liblastfm
import qualified Lastfm.User as User               -- liblastfm
import qualified Lastfm.Tasteometer as Tasteometer -- liblastfm
import           Text.Read (readMaybe)             -- base


type Score = Text


main :: IO ()
main = withConnection $ \conn ->
  pages conn >>= scores conn . names >>= pretty
 where
  names x = x ^.. folded.folded.key "friends".key "user"._Array.folded.key "name"._String

pages :: Connection -> IO [Either LastfmError Value]
pages conn = do
  first <- query conn (User.getFriends <*> user target)
  let ps = fromMaybe 1 (preview total first)
  (first :) <$> forConcurrently [2..ps] (\p -> query conn (User.getFriends <*> user target <* page p))
 where
  total = folded.key "friends".key "@attr".key "totalPages"._String.unpacked.to readMaybe.folded

scores :: Connection -> [Text] -> IO [(Text, Score)]
scores conn xs = zip xs <$> forConcurrently xs (\x -> do
  r <- query conn (Tasteometer.compare (user target) (user x))
  return (fromMaybe "0" (preview score r)))
 where
  score = folded.key "comparison".key "result".key "score"._String

pretty :: [(Text, Score)] -> IO ()
pretty = mapM_ (\(n,s) -> Text.putStrLn $ n <> ": " <> s) . take 5 . sortBy (flip compare `on` snd)

query :: Connection -> Request 'JSON (APIKey -> Ready) -> IO (Either LastfmError Value)
query conn r = lastfm conn (r <*> apiKey "234fc6e0f41f6ef99b7bd62ebaf8d318" <* json)

target :: Text
target = "MCDOOMDESTROYER"