1 import Control.Applicative( (<$>) )
2 import Control.Monad( when )
3 import Data.DateTime( diffSeconds )
4 import Data.List.Split( splitOn )
5 import Data.List( zipWith4 )
6 import qualified Data.Map as Map
7 import Data.Maybe( fromMaybe, fromJust )
8 import Data.Time.Clock( UTCTime(..) )
9 import Data.Time.Format( parseTime, formatTime )
10 import Data.Time.LocalTime( LocalTime(..), ZonedTime(..), TimeZone(..), zonedTimeToUTC, midnight, localDay )
11 import Diddo( DiddoEntry(..), DiddoLogline(..), DiddoParsed(..) )
12 import HMSTime( secondsToHMS )
13 import System.Console.GetOpt
14 import System.Environment( getArgs )
15 import System.Exit( exitSuccess )
16 import System.IO( stderr, hPutStr, hPutStrLn )
22 | InputFile String | OutputFile String
23 | InputFormat String | OutputFormat String
24 | StartDate String | EndDate String
27 options :: [OptDescr Opt]
29 [ Option "v" ["verbose"] (NoArg Verbose) "More detailed output"
30 , Option "V" ["version"] (NoArg Version) "Display program version"
31 , Option "h" ["help"] (NoArg Help) "Display program help"
32 , Option "f" ["file"] (ReqArg InputFile "FILE") "Read from FILE"
33 , Option "w" ["output"] (ReqArg OutputFile "FILE") "Write to FILE"
34 , Option "i" ["informat"] (ReqArg InputFormat "FORMAT") "Timeformat used in input"
35 , Option "o" ["outformat"] (ReqArg OutputFormat "FORMAT") "Timeformat used in output"
36 , Option "s" ["start"] (ReqArg StartDate "DATE") "Start of reporting period"
37 , Option "e" ["end"] (ReqArg EndDate "DATE") "End of reporting period"
40 parseToZonedTime :: String -> String -> ZonedTime
41 parseToZonedTime format string = fromMaybe (error $ "Input data broken: " ++ string) $ parseTime defaultTimeLocale format string
43 parseISOsecondsTime :: String -> ZonedTime
44 parseISOsecondsTime = parseToZonedTime $ iso8601DateFormat $ Just "%T%z"
46 zonedToUTCandTZ :: ZonedTime -> (UTCTime, TimeZone)
47 zonedToUTCandTZ zt = (zonedTimeToUTC zt, zonedTimeZone zt)
49 parseRFC822Time :: String -> ZonedTime
50 parseRFC822Time = parseToZonedTime rfc822DateFormat
52 formatZonedTime :: String -> ZonedTime -> String
53 formatZonedTime = formatTime defaultTimeLocale
55 utcTimesDeltas' :: [UTCTime] -> [Integer]
56 utcTimesDeltas' [] = error "Function utcTimesDeltas' called with no argument"
57 utcTimesDeltas' [_] = error "Function utcTimesDeltas' called with bougus argument"
58 utcTimesDeltas' (x:y:[]) = [diffSeconds y x]
59 utcTimesDeltas' (x:y:xs) = diffSeconds y x : utcTimesDeltas' (y:xs)
61 utcTimesDeltas :: ZonedTime -> [UTCTime] -> [Integer]
62 utcTimesDeltas startTime timestamps =
64 startTimeUTC = zonedTimeToUTC startTime
65 relevantTimestamps = dropWhile (< startTimeUTC) timestamps
67 zipWith diffSeconds relevantTimestamps $ startTimeUTC : init relevantTimestamps
69 startOfDay :: ZonedTime -> ZonedTime
70 startOfDay time = ZonedTime (LocalTime day midnight) $ zonedTimeZone time
72 day = localDay $ zonedTimeToLocalTime time
74 startOfMonth :: ZonedTime -> ZonedTime
75 startOfMonth time = ZonedTime (LocalTime day midnight) $ zonedTimeZone time
77 day = localDay $ zonedTimeToLocalTime time
79 parseToUTCFromZonedString :: String -> String -> UTCTime
80 parseToUTCFromZonedString fmt time = zonedTimeToUTC $ parseToZonedTime fmt time
82 linesFromFiles :: [String] -> IO [String]
83 linesFromFiles filenames = lines . concat <$> mapM readFile filenames
85 splitToMapOn :: String -> [String] -> Map.Map String [String]
86 splitToMapOn sep loglines = Map.fromList $ map (listToTuple . splitOn sep) loglines
87 where listToTuple (x:xs) = (x, xs)
88 listToTuple [] = ("",[""])
90 logLinesToDiddohs :: String -> [String] -> [DiddoEntry]
91 logLinesToDiddohs inDateFmt logLines =
93 loglineMap = splitToMapOn ";" logLines
94 zonedtimeEntryMap = Map.mapKeysMonotonic (parseToZonedTime inDateFmt) loglineMap
95 utctimeEntryMap = Map.mapKeys zonedTimeToUTC zonedtimeEntryMap
97 timeStamps = Map.keys loglineMap
98 entryTexts = map head $ Map.elems loglineMap
99 parsedTimes = Map.keys zonedtimeEntryMap
101 deltasHMS = map secondsToHMS $ utcTimesDeltas (startOfDay $ head parsedTimes) $ Map.keys utctimeEntryMap
103 zipWith4 DiddoEntry (formatZonedTime inDateFmt (startOfDay $ head parsedTimes) : init timeStamps)
104 timeStamps deltasHMS entryTexts
106 --parsedLinesToDiddohs :: String -> Map.Map UTCTime DiddoParsed -> [DiddoEntry]
107 --parsedLinesToDiddohs inDateFmt parsedLines =
108 -- Map.foldrWithKey (\t e ts -> )
110 parseDddLog :: String -> Map.Map UTCTime DiddoParsed
112 Map.singleton timestamp $ DiddoParsed timestamp zt entry
114 (timestring:entry:_) = splitOn ";" line
115 (timestamp,zt) = zonedToUTCandTZ $ parseISOsecondsTime timestring
121 case getOpt Permute options argv of
123 when (Help `elem` opts) $
124 (putStrLn $ usageInfo "Usage: diddohs [OPTION...]" options) >> exitSuccess
127 logFileNames = [file | InputFile file <- opts]
129 logLines <- linesFromFiles logFileNames
132 inDateFmt = head [fmt | InputFormat fmt <- opts]
133 -- reportPeriod = ( head [time | StartDate time <- opts]
134 -- , head [time | EndDate time <- opts]
139 -- putStrLn $ show reportPeriod
143 loglineMap = Map.unions $ map parseDddLog logLines
144 deltasHMS = map secondsToHMS $ utcTimesDeltas' $ fst (Map.findMin loglineMap) : Map.keys loglineMap
146 -- loglineMapReduced = Map.filterWithKey (\t _ -> t > startDay) loglineMap
147 -- diddoEntries = Map.foldrWithKey
149 mapM_ print $ logLinesToDiddohs inDateFmt logLines
150 -- mapM_ print deltasHMS
153 hPutStr stderr $ usageInfo header options
154 ioError (userError ('\n' : concat errs))
155 where header = "Usage: diddohs [OPTION...]"