spacepaste

  1.  
  2. #!/usr/bin/env stack
  3. -- stack --resolver lts-8.0 script
  4. {-# LANGUAGE OverloadedStrings #-}
  5. {-# LANGUAGE ScopedTypeVariables #-}
  6. {-# LANGUAGE DeriveGeneric #-}
  7. import Graphics.Rendering.Chart.Easy
  8. import Graphics.Rendering.Chart.Backend.Cairo
  9. import System.Environment
  10. import Data.Colour (opaque)
  11. import Data.Colour.SRGB (sRGB24read)
  12. import GHC.Generics (Generic)
  13. import qualified Data.HashTable.IO as H
  14. import qualified Data.Csv as C
  15. import qualified Data.ByteString.Lazy as BL
  16. import qualified Data.ByteString.Char8 as B
  17. import Control.Monad (forM_, when)
  18. import Data.Hashable
  19. import Data.List (sortBy)
  20. type HashTable k v = H.BasicHashTable k v
  21. data Girl = Blake
  22. | Cinder
  23. | Deerie
  24. | Emerald
  25. | Ilia
  26. | Neo
  27. | Nora
  28. | Penny
  29. | Pyrrha
  30. | Ruby
  31. | Shadowperson
  32. | Weiss
  33. | Winter
  34. | Yang deriving (Show, Read, Eq, Enum, Ord, Generic)
  35. instance Hashable Girl
  36. data Other = Geist
  37. | Ironwood
  38. | Jaune
  39. | Mercury
  40. | Nonanswer
  41. | Nuckelavee
  42. | Other
  43. | Port
  44. | Qrow
  45. | Ren
  46. | Roman
  47. | Shopkeep
  48. | Tyrian
  49. | Zwei
  50. | Bad deriving (Show, Read, Eq, Enum, Ord, Generic)
  51. instance Hashable Other
  52. data Character = IsGirl Girl
  53. | NotGirl Other deriving (Show, Read, Eq, Generic, Ord)
  54. simpleShow :: Character -> String
  55. simpleShow (IsGirl g) = show g
  56. simpleShow (NotGirl o) = show o
  57. isGirl :: Character -> Bool
  58. isGirl (IsGirl _) = True
  59. isGirl _ = False
  60. instance C.FromField Character where
  61. parseField s = case filter ((`B.isInfixOf` s) . B.pack . show) [Blake .. ] of
  62. x:_ -> pure $ IsGirl x
  63. [] -> case filter ((`B.isInfixOf` s) . B.pack . show) [Geist .. ] of
  64. x:_ -> pure $ NotGirl x
  65. [] -> mempty
  66. instance C.ToField Character where
  67. toField = C.toField . simpleShow
  68. instance Hashable Character
  69. chartColors :: [AlphaColour Double]
  70. chartColors = map (opaque . sRGB24read)
  71. [ "#4f894a"
  72. , "#a540c5"
  73. , "#70d65d"
  74. , "#6158bb"
  75. , "#cbd352"
  76. , "#472b53"
  77. , "#d28b3b"
  78. , "#799fc2"
  79. , "#d54c3d"
  80. , "#85d4b7"
  81. , "#c5447e"
  82. , "#877e3b"
  83. , "#c88bc1"
  84. , "#3e4b3d"
  85. , "#d1b39a"
  86. , "#814238"
  87. , "#6a92be"
  88. , "#c8d63d"
  89. , "#8b4acb"
  90. , "#6ad258"
  91. , "#cd478f"
  92. , "#6dcdb5"
  93. , "#d54938"
  94. , "#483068"
  95. , "#bed184"
  96. , "#ba85c5"
  97. , "#cd993b"
  98. , "#424439"
  99. , "#c6b4af"
  100. , "#762e39"
  101. , "#577a3b"
  102. , "#b77253"
  103. , "#cdcf50"
  104. , "#653bba"
  105. , "#77d545"
  106. , "#cb4dc5"
  107. , "#7ad78c"
  108. , "#bc4577"
  109. , "#59803e"
  110. , "#726cbe"
  111. , "#c38339"
  112. , "#48284a"
  113. , "#7dcbbe"
  114. , "#c34839"
  115. , "#6c93b4"
  116. , "#524732"
  117. , "#ca94b7"
  118. , "#ccb491"
  119. , "#af7958"
  120. , "#673ebd"
  121. , "#c8d845"
  122. , "#cb4dc4"
  123. , "#66cc5b"
  124. , "#c55384"
  125. , "#71cdb1"
  126. , "#cf4a3a"
  127. , "#6495b3"
  128. , "#d3973a"
  129. , "#9080cf"
  130. , "#abb86d"
  131. , "#48315d"
  132. , "#ceb7bc"
  133. , "#612f2c"
  134. , "#445c38"
  135. ]
  136. barFrom :: String -> String -> FileOptions -> Int -> Int
  137. -> [(Character, Int)] -> IO ()
  138. barFrom baseName title fo upper lower rawDat =
  139. toFile fo (baseName ++ "-bar-" ++ (show lower) ++ "-" ++ (show upper) ++ ".png") $ do
  140. let titleSuffix = if lower == 0 then "Top " ++ show upper else show (lower+1) ++ " to " ++ show upper
  141. dat = drop lower $ take upper rawDat
  142. toBar (k, v) (ks, vs) = ((simpleShow k):ks, v:vs)
  143. (titles, barData) = foldr toBar ([], []) dat :: ([String], [Int])
  144. indexAndOffset = map (\(n, l) -> (n, replicate n 0 ++ l)) . zip [0..] . map (:[])
  145. mkstyle c = (solidFillStyle c, Nothing)
  146. bPlot = do
  147. (b :: PlotBars Int Int) <- (bars [] (indexAndOffset barData))
  148. pure $ b & plot_bars_style .~ BarsStacked
  149. & plot_bars_spacing .~ BarsFixGap 5 5
  150. & plot_bars_item_styles .~ map mkstyle (cycle $ drop lower $ take upper chartColors)
  151. layout_title .= title ++ titleSuffix
  152. layout_x_axis . laxis_generate .= autoIndexAxis titles
  153. layout_x_axis . laxis_override .= (\x -> x & axis_labels .~ [zip [0..] titles])
  154. plot $ plotBars <$> bPlot
  155. main :: IO ()
  156. main = do
  157. rawDat <- getData
  158. outFile <- head <$> tail <$> getArgs
  159. case rawDat of
  160. (Left s) -> print s
  161. (Right (inc, girls)) -> do
  162. let bigDef = def & fo_size .~ (1280, 720)
  163. & fo_format .~ PNG
  164. titleInc = "r/RWBY Moderator Application Best Girl Responses (Inclusive) "
  165. titleGirl = "r/RWBY Moderator Application Best Girl Responses (Only actual girls from RWBY) "
  166. sortFst = sortBy (\l r -> compare (fst l) (fst r))
  167. merge [] [] acc = acc
  168. merge ((k1, v1):as) [] acc = merge as [] $ (k1, v1, 0):acc
  169. merge [] ((k2, v2):bs) acc = merge [] bs $ (k2, 0, v2):acc
  170. merge a@((k1, v1):as) b@((k2, v2):bs) acc =
  171. case compare k1 k2 of
  172. EQ -> merge as bs $ (k1, v1, v2):acc
  173. LT -> merge as b $ (k1, v1, 0):acc
  174. GT -> merge a bs $ (k2, 0, v2):acc
  175. BL.writeFile (outFile ++ "-dump.csv") $ C.encode $ merge (sortFst inc) (sortFst girls) []
  176. barFrom outFile titleGirl bigDef 14 0 girls
  177. barFrom outFile titleInc bigDef 15 0 inc
  178. barFrom outFile titleInc bigDef 29 15 inc
  179. getData :: IO (Either String ([(Character, Int)], [(Character, Int)]))
  180. getData = do
  181. csvName <- head <$> getArgs
  182. csv <- BL.readFile csvName
  183. inclusive <- H.new :: IO (HashTable Character Int)
  184. girls <- H.new :: IO (HashTable Character Int)
  185. let dat = C.decode C.NoHeader csv
  186. update h v = do
  187. check <- H.lookup h v
  188. case check of
  189. (Just n) -> H.insert h v (n + 1)
  190. Nothing -> H.insert h v 1
  191. case dat of
  192. (Left s) -> pure $ Left s
  193. (Right d) -> do
  194. forM_ d $ \(inc, exc) -> do
  195. update inclusive inc
  196. when (isGirl exc) (update girls exc)
  197. let sortSnd = sortBy (\l r -> compare (snd r) (snd l))
  198. lhs <- fmap sortSnd $ H.toList inclusive
  199. rhs <- fmap sortSnd $ H.toList girls
  200. pure $ Right (lhs, rhs)
  201.