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