|
| 1 | +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} |
| 2 | +{-# LANGUAGE OverloadedStrings #-} |
| 3 | + |
| 4 | +module Main where |
| 5 | + |
| 6 | +import Data.List (sortBy) |
| 7 | +import Data.List.Split (splitOn) |
| 8 | +import Data.Maybe (catMaybes) |
| 9 | +import Data.Ord (Down (Down), comparing) |
| 10 | +import qualified Data.Text as T |
| 11 | +import Data.Time (fromGregorian) |
| 12 | +import Data.Time.Calendar (Day, addGregorianMonthsClip, |
| 13 | + toGregorian) |
| 14 | +import Data.Time.Clock (getCurrentTime, utctDay) |
| 15 | +import System.Directory (listDirectory) |
| 16 | +import Text.Pandoc (Inline (Space, Str), |
| 17 | + Pandoc (Pandoc), readMarkdown, |
| 18 | + runIO) |
| 19 | +import Text.Pandoc.Options |
| 20 | +import Text.Pandoc.Writers.Shared (lookupMetaInlines) |
| 21 | + |
| 22 | +parseDateFromString :: String -> Day |
| 23 | +parseDateFromString filePath = |
| 24 | + let [year, month, day] = map read . take 3 . splitOn "-" $ filePath |
| 25 | + in fromGregorian (toInteger year) month day |
| 26 | + |
| 27 | +data AlbumPostSummary = AlbumPostSummary { |
| 28 | + score :: Float, |
| 29 | + released :: Day, |
| 30 | + title :: String |
| 31 | +} deriving (Eq, Ord) |
| 32 | + |
| 33 | +instance Show AlbumPostSummary where |
| 34 | + show summary = |
| 35 | + title summary ++ " (score: " ++ show (score summary) ++ ", release date: " ++ show (released summary) ++ ")" |
| 36 | + |
| 37 | +getAlbumPostSummary :: String -> IO (Maybe AlbumPostSummary) |
| 38 | +getAlbumPostSummary filePath = do |
| 39 | + md <- T.pack <$> readFile ("posts/" ++ filePath) |
| 40 | + pandoc <- runIO $ |
| 41 | + readMarkdown |
| 42 | + (def { |
| 43 | + readerStandalone = True, |
| 44 | + readerExtensions = |
| 45 | + enableExtension Ext_yaml_metadata_block (readerExtensions def) } ) |
| 46 | + md |
| 47 | + case pandoc of |
| 48 | + Left _ -> return Nothing |
| 49 | + Right (Pandoc meta _) -> do |
| 50 | + if not . hasMusicTag $ lookupMetaInlines "tags" meta |
| 51 | + then do |
| 52 | + return Nothing |
| 53 | + else return $ Just AlbumPostSummary { |
| 54 | + released = parseDateFromString (getValue "released"), |
| 55 | + score = read (getValue "score") :: Float, |
| 56 | + title = getValue "title" |
| 57 | + } |
| 58 | + where |
| 59 | + hasMusicTag inlines = any (`elem` inlines) [Str "music", Str "music,"] |
| 60 | + getValue key = T.unpack . T.concat . map stringify . lookupMetaInlines key $ meta |
| 61 | + stringify (Str value) = value |
| 62 | + stringify Space = " " |
| 63 | + stringify _ = "" |
| 64 | + |
| 65 | +data What = Good | Wack | Ok deriving (Eq) |
| 66 | +instance Show What where |
| 67 | + show Good = "====== good ======" |
| 68 | + show Wack = "====== wack ======" |
| 69 | + show Ok = "======= ok =======" |
| 70 | + |
| 71 | +data Bucket = Bucket { |
| 72 | + summaries :: [AlbumPostSummary], |
| 73 | + what :: What |
| 74 | +} |
| 75 | + |
| 76 | + |
| 77 | +summariesToBuckets :: [AlbumPostSummary] -> ([AlbumPostSummary], [AlbumPostSummary], [AlbumPostSummary]) |
| 78 | +summariesToBuckets = foldr f ([], [], []) |
| 79 | + where |
| 80 | + f s (hi, mid, lo) | score s >= 8.0 = (s:hi, mid, lo) |
| 81 | + f s (hi, mid, lo) | score s < 6.0 = (hi, mid, s:lo) |
| 82 | + f s (hi, mid, lo) = (hi, s:mid, lo) |
| 83 | + |
| 84 | +printBucket :: Bucket -> IO () |
| 85 | +printBucket bucket = |
| 86 | + if null (summaries bucket) |
| 87 | + then return () |
| 88 | + else do |
| 89 | + putStrLn "" |
| 90 | + print (what bucket) |
| 91 | + mapM_ print $ summaries bucket |
| 92 | + putStrLn "" |
| 93 | + |
| 94 | +main :: IO () |
| 95 | +main = do |
| 96 | + files <- listDirectory "posts/" |
| 97 | + (year, month, _) <- toGregorian . utctDay <$> getCurrentTime |
| 98 | + let endDate = fromGregorian year month 1 |
| 99 | + startDate = addGregorianMonthsClip (-1) endDate |
| 100 | + filteredFiles = filter ((\fileDate -> fileDate >= startDate && fileDate < endDate) . parseDateFromString) files |
| 101 | + allSummaries <- sortBy (comparing Down) . catMaybes <$> mapM getAlbumPostSummary filteredFiles |
| 102 | + let (hiScores, midScores, lowScores) = summariesToBuckets allSummaries |
| 103 | + mapM_ printBucket |
| 104 | + [ Bucket hiScores Good |
| 105 | + , Bucket midScores Ok |
| 106 | + , Bucket lowScores Wack |
| 107 | + ] |
| 108 | + -- TODO: send this as an email? save it as a new post? just run it every month and then collect it somehow? |
0 commit comments