import App.Clk.Entry
import Data.Maybe
import qualified Data.Map as Map
import Text.Printf

main = do
    entries <- mostRecentMonthEntries
    let reportable    = filter isReportable entries
    let totalDuration = sum $ map (fromJust.dur) $ reportable
    let f = \s e -> Map.insertWith (+) (client e) (maybe 0 id $ dur e) s
    let byClient = foldl f Map.empty reportable
    putStrLn $ show byClient
    putStrLn $ printf "%.2f hours" (realToFrac (totalDuration/3600) ::Float)

isClockedOut :: Entry -> Bool
isClockedOut = (=="out") . msg

isClockedIn :: Entry -> Bool
isClockedIn = not . isClockedOut

hasDuration :: Entry -> Bool
hasDuration = isJust . dur

isReportable :: Entry -> Bool
isReportable e = (hasDuration e) && (isClockedIn e)

type Client = String
client :: Entry -> Client
client e = maybe "" id $ listToMaybe $ filter isClientTag $ tags e

type Tag = String
isClientTag :: Tag -> Bool
isClientTag t = any (t==) ["gsg","jjgames","ndrix","scs","vgpc"]

import App.Clk.Entry
import Data.Maybe
import qualified Data.Map as Map
import Text.Printf

main = do
    entries <- mostRecentMonthEntries
    let reportable    = filter isReportable entries
    let f = \s e -> Map.insertWith (+) (client e) (maybe 0 id $ dur e) s
    let byClient = foldl f Map.empty reportable
    let totalDuration = sum $ Map.elems byClient
    putStrLn $ show byClient
    putStrLn $ printf "%.2f hours" (realToFrac (totalDuration/3600) ::Float)

isClockedOut :: Entry -> Bool
isClockedOut = (=="out") . msg

isClockedIn :: Entry -> Bool
isClockedIn = not . isClockedOut

hasDuration :: Entry -> Bool
hasDuration = isJust . dur

isReportable :: Entry -> Bool
isReportable e = (hasDuration e) && (isClockedIn e)

type Client = String
client :: Entry -> Client
client e = maybe "" id $ listToMaybe $ filter isClientTag $ tags e

type Tag = String
isClientTag :: Tag -> Bool
isClientTag t = any (t==) ["gsg","jjgames","ndrix","scs","vgpc"]

Simplify calculation of the total duration
Simplify calculation of the total duration

Since we've already traversed the list of entries and summed the
durations by client, we don't need to traverse the entire list of
entries again.  One more summing stage finishes the job.
