Skip to content

Instantly share code, notes, and snippets.

@jcla1
Created July 4, 2014 21:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcla1/addb4ab8a20aef2fb862 to your computer and use it in GitHub Desktop.
Save jcla1/addb4ab8a20aef2fb862 to your computer and use it in GitHub Desktop.
Haskell activity log script
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))
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment