summaryrefslogtreecommitdiff
path: root/diddohs.hs
blob: 3f9235972490cec084a10c6bab1e63d220ded5e8 (plain)
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...]"