{-# LANGUAGE ScopedTypeVariables #-} -------------------------------------------------------------------- -- | -- Module : Generate gnuplot graphs of hackage activity -- Copyright : (c) Don Stewart 2008-2013 -- License : BSD3 -- -- Maintainer: Don Stewart -- Stability : provisional -- Portability: -- -------------------------------------------------------------------- import Data.List import Data.Maybe import System.Directory import System.FilePath import System.Locale import System.Time import System.Time.Parse -- import Text.HTML.Download import Graphics.Gnuplot.Simple import qualified Data.IntMap as I import Text.Printf import Network.Curl.Download import Data.ByteString.Char8 (unpack) url = "http://hackage.haskell.org/packages/archive/log" main = do xs <- gettime let ys = [ (i,d) | ((_,d), i) <- zip xs [1..] ] zs = sliding ys [1.. length ys] writeFile "/tmp/data1" (unlines . map (\((x,y) :: (Int,Int)) -> printf "%d %d" x y ) $ ys) writeFile "/tmp/data2" (unlines . map (\((x,y) :: (Int,Double)) -> printf "%d %f" x y) $ zs) plotList [Key Nothing ,YRange (0,maximum (map snd zs) + 1) ,XLabel "Days since launch of Hackage" ,YLabel "Unique uploads each day" ,Title "Daily uploads (180 day moving average) to http://hackage.haskell.org" -- ,SVG "/tmp/hackage-daily-graph.svg" ,Custom "grid" [] ,Custom "terminal svg;set output \"/tmp/hackage-daily-graph.svg\""[] ,Custom "style line" ["3","lc","3","lw","3"] ] (map snd zs) print "Output written to: /tmp/hackage-daily-graph.svg" -- sliding average window window :: Int window = 180 -- -- compute the 7-day sliding average, starting with a sparse sequence -- -- sliding :: [(Int,Int)] -> [Int] -> [(Int,Double)] sliding [] _ = [] sliding xs days = [ (i,slidingAv i) | i <- days' ] where m = window `div` 2 -- 1/2 n days' = drop m . take (max 0 (length days - window+m+1)) $ days table = I.fromList xs :: I.IntMap Int -- to handle missing elems easily slidingAv :: Int -> Double slidingAv i = fromIntegral (sum seqN) / fromIntegral window where seqN = map (\j -> I.findWithDefault 0 j table) [i-m..i+(window-m-1)] gettime :: IO [(CalendarTime, Int)] gettime = do pwd <- getCurrentDirectory -- src <- openURL url e <- openURI url let src = case e of Left err -> error err Right s -> unpack s let dates = catMaybes . sort . map parse . lines $ src let days = groupBy day $ dates return [ (head d, length d) | d <- days ] where parse = parseCalendarTime defaultTimeLocale "%c" month a b = ctYear a == ctYear b && ctMonth a == ctMonth b day a b = month a b && ctDay a == ctDay b