1 import Control.Applicative( (<$>), (<*>) )
2 import Control.Monad( when, unless )
3 import Data.DateTime( diffSeconds )
4 import Data.List.Split( splitOn )
5 import Data.List( zipWith4, intercalate )
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, utcToZonedTime, midnight, localDay )
11 import Diddo( DiddoEntry(..), DiddoEntry2(..), LogEntry(..), LogLine(..), parseDiddoLogline, formatDiddoEntry, timestamp, logToDiddoEntry )
12 import HMSTime( secondsToHMS )
13 import System.Console.GetOpt
14 import System.Environment( getArgs )
15 import System.Exit( exitSuccess, exitFailure )
16 import System.IO( stdin, stdout, stderr, hPutStr, hPutStrLn )
23 , optInputFiles :: [String]
24 , optOutputFile :: String
25 , optInputFormat :: String
26 , optOutputFormat :: String
27 , optStartDate :: String
28 , optEndDate :: String
38 , optInputFormat = "%FT%T%z"
39 , optOutputFormat = "%FT%T%z"
44 availableOptions :: [OptDescr (Opt -> IO Opt)]
46 [ Option ['h'] ["help"]
47 (NoArg (\_ -> putStrLn (usageInfo "Usage: diddohs [OPTION...]" availableOptions) >> exitSuccess))
48 "Display program help"
49 , Option ['v'] ["verbose"]
50 (NoArg (\opts -> return opts { optVerbose = True }))
51 "More detailed output"
52 , Option ['V'] ["version"]
53 (NoArg (\opts -> return opts { optVersion = True }))
54 "Display program version"
55 , Option ['f'] ["file"]
56 (ReqArg (\arg opts -> return opts { optInputFiles = optInputFiles opts ++ [arg]}) "FILE" )
58 , Option ['w'] ["output"]
59 (ReqArg (\arg opts -> return opts { optOutputFile = arg }) "FILE")
61 , Option ['i'] ["informat"]
62 (ReqArg (\arg opts -> return opts { optInputFormat = arg }) "FORMAT")
63 "Timeformat used in input"
64 , Option ['o'] ["outformat"]
65 (ReqArg (\arg opts -> return opts { optOutputFormat = arg }) "FORMAT")
66 "Timeformat used in output"
67 , Option ['s'] ["start"]
68 (ReqArg (\arg opts -> return opts { optStartDate = arg }) "DATE")
69 "Start of reporting period"
70 , Option ['e'] ["end"]
71 (ReqArg (\arg opts -> return opts { optEndDate = arg }) "DATE")
72 "End of reporting period"
75 -- SECTION: parsing to ZonedTime
76 parseToZonedTime :: String -> String -> ZonedTime
77 parseToZonedTime format string = fromMaybe (error $ "Input data broken: " ++ string) $ parseTime defaultTimeLocale format string
79 parseISOsecondsTime :: String -> ZonedTime
80 parseISOsecondsTime = parseToZonedTime $ iso8601DateFormat $ Just "%T%z"
82 parseRFC822Time :: String -> ZonedTime
83 parseRFC822Time = parseToZonedTime rfc822DateFormat
84 -- SECTION: parsing to ZonedTime
86 -- SECTION: handling ZonedTime
87 formatZonedTime :: String -> ZonedTime -> String
88 formatZonedTime = formatTime defaultTimeLocale
90 startOfDay :: ZonedTime -> ZonedTime
91 startOfDay time = ZonedTime (LocalTime day midnight) $ zonedTimeZone time
93 day = localDay $ zonedTimeToLocalTime time
95 startOfMonth :: ZonedTime -> ZonedTime
96 startOfMonth time = ZonedTime (LocalTime day midnight) $ zonedTimeZone time
98 day = localDay $ zonedTimeToLocalTime time
99 -- SECTION: handling ZonedTime
101 -- SECTION: handling UTCTime
102 utcTimesDeltas :: [UTCTime] -> [Integer]
103 utcTimesDeltas [] = error "Function utcTimesDeltas called with no argument"
104 utcTimesDeltas [_] = error "Function utcTimesDeltas called with bougus argument"
105 utcTimesDeltas (x:y:[]) = [diffSeconds y x]
106 utcTimesDeltas (x:y:xs) = diffSeconds y x : utcTimesDeltas (y:xs)
108 --deltaAndEntry :: Map UTCTime Diddo.LogLine -> DiddoEntry2
109 --deltaAndEntry dddLoglineMap = Map.mapWithKey findDeltaEntry dddLoglineMap
111 -- precEntry = case Map.lookupLT (Diddo.timestamp logentry) dddLoglineMap of
113 -- Nothing -> Diddo.timestamp logentry
114 -- end = utcToZonedTime (Diddo.timezone logentry) (Diddo.timestamp logentry)
115 -- start = utcToZonedTime (Diddo.timezone logentry) (fst precEntry)
116 -- delta = diffSeconds end start
117 -- comment = Diddo.text logentry
119 --findDeltaEntry timestamp logentry = Diddo.DiddoEntry2 start end delta comment
121 mapToDiddoEntries :: Map.Map UTCTime Diddo.LogEntry -> Map.Map UTCTime Diddo.DiddoEntry2
122 mapToDiddoEntries logmap = Map.mapWithKey toDddEntry logmap
124 toDddEntry timestamp logentry = Diddo.logToDiddoEntry (preceedingTimestamp timestamp) logentry
125 preceedingTimestamp x = case Map.lookupLT x logmap of
127 Nothing -> fst $ Map.findMin logmap
128 -- SECTION: handling UTCTime
130 -- SECTION: handling rawinput
131 splitToMapOn :: String -> [String] -> Map.Map String [String]
132 splitToMapOn sep loglines = Map.fromList $ map (listToTuple . splitOn sep) loglines
133 where listToTuple (x:xs) = (x, xs)
134 listToTuple [] = ("",[""])
136 logLinesToDiddohs :: String -> [String] -> [DiddoEntry]
137 logLinesToDiddohs inDateFmt logLines =
139 loglineMap = splitToMapOn ";" logLines
140 zonedtimeEntryMap = Map.mapKeysMonotonic (parseToZonedTime inDateFmt) loglineMap
141 utctimeEntryMap = Map.mapKeys zonedTimeToUTC zonedtimeEntryMap
143 timeStamps = Map.keys loglineMap
144 entryTexts = map (intercalate ";") $ Map.elems loglineMap
145 parsedTimes = Map.keys zonedtimeEntryMap
147 deltasHMS = map secondsToHMS $ utcTimesDeltas ((zonedTimeToUTC $ startOfDay $ head parsedTimes) : Map.keys utctimeEntryMap)
149 zipWith4 DiddoEntry (formatZonedTime inDateFmt (startOfDay $ head parsedTimes) : init timeStamps)
150 timeStamps deltasHMS entryTexts
151 -- SECTION: handling rawinput
155 -- SECTION: option processing
156 (givenOptions,args,errs) <- getArgs >>= return . getOpt Permute availableOptions
158 unless (null errs) $ do
159 mapM_ (hPutStr stderr) errs
162 effectiveOptions <- foldl (>>=) (return defaultOpts) givenOptions
163 -- SECTION: option processing
166 map Diddo.LogLine <$> case optInputFiles effectiveOptions of
167 files@(_:_) -> lines . concat <$> mapM readFile files
168 [] -> lines <$> getContents
171 map Diddo.parseDiddoLogline <$> case optInputFiles effectiveOptions of
172 files@(_:_) -> lines . concat <$> mapM readFile files
173 [] -> lines <$> getContents
176 dddLogEntryMap = Map.fromList $ map (\diddo -> (Diddo.timestamp diddo, diddo)) dddLogEntries
177 diddoEntriesMap = mapToDiddoEntries dddLogEntryMap
178 inDateFmt = optInputFormat effectiveOptions
179 outDateFmt = optOutputFormat effectiveOptions
185 mapM_ print $ logLinesToDiddohs inDateFmt (map show dddLines)
186 -- putStrLn "new code output following"
187 -- mapM_ (print . snd) $ Map.toAscList diddoEntriesMap
188 putStrLn "new code output following"
189 mapM_ (putStrLn . snd) $ Map.toAscList $ Map.map (Diddo.formatDiddoEntry outDateFmt) diddoEntriesMap