-- | Provides a low level view of the log file.
module Edits ( -- * AST
Edits, Edit(..),
-- ** Parsing
editsFromFile,
-- * Grouping
groupWith , fileGroup,
-- ** Predicates
Pred,
-- * Calendars
Calendar(..), flatten,
-- ** Calendar of Edits
calendarE, CalendarE
) where
import Char
import Maybe (mapMaybe)
import Control.Applicative
import Data.List as L (groupBy)
import qualified Data.Map as D hiding (map, filter, mapMaybe)
import System.FilePath
-- | Abstract syntax for an editor log entry.
--
data Edit = Edit { year :: Int
, month :: Int
, day :: Int
, hour :: Int
, minute :: Int
, seconds :: Int
, dow :: Int -- ^ Day of the week
, doy :: Int -- ^ Day of the year
, file :: FilePath } deriving (Show, Eq)
type Edits = [Edit]
-- | Parses log files generated by timechartd.py
-- daemon or editor.
--
editsFromFile :: FilePath -> IO Edits
editsFromFile f = parseEdits <$> readFile f
parseEdits :: String -> Edits
parseEdit :: String -> Maybe Edit
parseEdits = mapMaybe parseEdit . lines
parseEdit s = if a then Just $ Edit y m d h mi se dw dy (unwords (drop 9 w))
else Nothing
where w = words s
i = take 8 w
a = length w >= 9 && all (all isDigit) i && w !! 8 == "EDIT"
(y:m:d:h:mi:se:dw:dy:[]) = map read i
-- | A simple abstraction for predicates
--
type Pred a = a -> Bool
-- | Use a function's result to group by.
--
groupWith :: Eq a => (b -> a) -> [b] -> [[b]]
groupWith y = groupBy (\f g -> y f == y g)
-- | Group Edits on year.
--
yearGroup :: Edits -> [Edits]
yearGroup = groupWith year
-- | Group Edits on month.
--
monthGroup :: Edits -> [Edits]
monthGroup = groupWith month
-- | Group Edits on day of the month.
--
dayGroup :: Edits -> [Edits]
dayGroup = groupWith day
-- | Group Edits on file name.
--
fileGroup :: Edits -> [Edits]
fileGroup = groupWith file
-- | The Calendar type is an abstraction over lists of days in
-- lists of months in lists of years:
--
-- > [ [ [ a ] ] ]
-- > | | + years
-- > | + months
-- > + days
--
newtype Calendar a = Calendar [[[a]]] deriving (Eq, Show)
instance Functor Calendar where
fmap f (Calendar as) = Calendar (map (map (map f)) as)
-- Getting items out of the Calendar container
--
fromCalendar :: Calendar a -> [[[a]]]
flatten :: Calendar a -> [a]
fromCalendar (Calendar as) = as
flatten = concat . concat . fromCalendar
-- Calendar of Edits
-- Using the grouping functions to build it.
-- Expects a chronological list of edits.
--
type CalendarE = Calendar Edits
calendarE :: Edits -> CalendarE
calendarE = Calendar . map (map dayGroup . monthGroup) . yearGroup