Skip to content

Instantly share code, notes, and snippets.

@jcla1
Created July 4, 2014 21:27
Show Gist options
  • Save jcla1/addb4ab8a20aef2fb862 to your computer and use it in GitHub Desktop.
Save jcla1/addb4ab8a20aef2fb862 to your computer and use it in GitHub Desktop.

Revisions

  1. jcla1 created this gist Jul 4, 2014.
    94 changes: 94 additions & 0 deletions activityLog.hs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    import Data.Function (on)
    import Data.List (sort, sortBy, groupBy, nub)
    import Data.List.Utils (replace)
    import Data.Ord (comparing)
    import Data.Either (rights)

    import Control.Monad (liftM)
    import System.Environment (getArgs)

    import Text.Parsec.Error (ParseError)
    import qualified Text.CSV as CSV

    validPrograms = ["Google Chrome", "Sublime Text 2", "iTerm", "Finder", "Activity Monitor"]
    -- Maximum time difference between two usages of a program,
    -- so that these usages would be counted in the same interval, in seconds.
    maxIntervalDiff = 360
    -- Minimum duration of usage of a program to be accounted for in the line-up
    minUsage = 720

    main = do
    activityRecords <- liftM (concatMap init . rights) getCSVs
    putStrLn $ processActivityRecords activityRecords

    getCSVs :: IO [Either ParseError CSV.CSV]
    getCSVs = mapM CSV.parseCSVFromFile =<< getArgs

    processActivityRecords :: CSV.CSV -> String
    processActivityRecords rs = concatRecords otherPrograms allPrograms
    where
    concatRecords = (++) `on` flip (++) "\n" . postpareCSV

    allPrograms = intervalsToCSV "All Programs" allProgramIntervals
    allProgramIntervals =
    timesToInterval .
    sort .
    nub $
    concatMap snd programTimes

    otherPrograms =
    concatMap (uncurry intervalsToCSV) .
    filterPrograms $
    mapTimesToInterval programTimes

    programTimes =
    cleanGroups .
    groupBy ((==) `on` fst) .
    sortBy (comparing fst) $
    toProgramTime rs

    filterPrograms = filterMinUsage . filterValidPrograms

    postpareCSV :: CSV.CSV -> String
    postpareCSV = removeQuotes . CSV.printCSV

    intervalsToCSV :: String -> [(Int, Int)] -> CSV.CSV
    intervalsToCSV name = map (uncurry (intervalToCSV name))

    intervalToCSV :: String -> Int -> Int -> CSV.Record
    intervalToCSV name s e = [show s, show e, show (e - s), name]

    mapTimesToInterval :: [(String, [Int])] -> [(String, [(Int, Int)])]
    mapTimesToInterval = map (\ x -> (fst x, timesToInterval $ snd x))

    timesToInterval :: [Int] -> [(Int, Int)]
    timesToInterval (x:xs) = foldl concatIntervals [(x, x)] xs
    where
    concatIntervals (i:is) t = makeInterval i t ++ is
    makeInterval (start, end) t
    | t > end && t - end <= maxIntervalDiff = [(start, t)]
    | start /= end = [(t, t), (start, end)]
    | otherwise = [(t, t)]

    filterValidPrograms :: [(String, a)] -> [(String, a)]
    filterValidPrograms = filter (\ x -> fst x `elem` validPrograms)

    filterMinUsage :: [(a, [(Int, Int)])] -> [(a, [(Int, Int)])]
    filterMinUsage = filter (\ (_, is) -> intervalsToDuration is >= minUsage)

    intervalsToDuration :: [(Int, Int)] -> Int
    intervalsToDuration = foldl (\ acc (a, b) -> acc + b - a) 0

    toProgramTime :: CSV.CSV -> [(String, Int)]
    toProgramTime = map (\ r -> (r !! 3, adjustTime r))

    adjustTime :: CSV.Record -> Int
    adjustTime (t:tz:_) = (read t :: Int) + case tz of
    "CEST" -> 7200
    "CET" -> 3600

    removeQuotes :: String -> String
    removeQuotes = replace "\"" ""

    cleanGroups :: [[(a, b)]] -> [(a, [b])]
    cleanGroups = map (\ x -> (fst $ head x, map snd x))
    20 changes: 20 additions & 0 deletions example_log.csv
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    1404508051,CEST,0,Google Chrome,1,1,15,24.4
    1404508067,CEST,1,iTerm,1,0,15,24.3
    1404508080,CEST,0,iTerm,1,0,15,24.3
    1404508095,CEST,0,iTerm,1,0,15,24.3
    1404508111,CEST,0,Google Chrome,1,1,15,24.3
    1404508126,CEST,0,iTerm,1,0,14,24.3
    1404508140,CEST,0,iTerm,1,0,14,24.3
    1404508155,CEST,0,Google Chrome,1,1,14,24.3
    1404508170,CEST,0,iTerm,1,0,14,24.3
    1404508186,CEST,1,iTerm,1,0,14,24.1
    1404508200,CEST,0,iTerm,1,0,14,24.1
    1404508216,CEST,0,iTerm,1,0,14,24.1
    1404508231,CEST,0,iTerm,1,0,14,24.1
    1404508247,CEST,0,iTerm,1,0,14,23.9
    1404508260,CEST,0,iTerm,1,0,14,23.9
    1404508275,CEST,3,iTerm,1,0,14,23.9
    1404508291,CEST,0,Google Chrome,1,1,14,23.9
    1404508306,CEST,0,iTerm,1,0,14,23.8
    1404508320,CEST,0,iTerm,1,0,14,23.8
    1404508335,CEST,0,iTerm,1,0,14,23.8