@@ -9,6 +9,8 @@ module Lib
99 , processTickets
1010 , fetchTickets
1111 , showStatistics
12+
13+ , listAndSortTickets
1214 ) where
1315
1416import Universum
@@ -23,10 +25,11 @@ import LogAnalysis.Classifier (extractErrorCodes, extractIssuesFromLog
2325import LogAnalysis.KnowledgeCSVParser (parseKnowLedgeBase )
2426import LogAnalysis.Types (ErrorCode (.. ), Knowledge , renderErrorCode , setupAnalysis )
2527import Util (extractLogsFromZip )
26- import Zendesk (App , Attachment (.. ), Comment (.. ), Config (.. ), RequestType (.. ),
27- TicketId , TicketInfo (.. ), TicketTag (.. ), ZendeskLayer (.. ),
28- ZendeskResponse (.. ), asksZendeskLayer , assignToPath , defaultConfig ,
29- knowledgebasePath , renderTicketStatus , runApp , tokenPath )
28+ import Zendesk (App , Attachment (.. ), Comment (.. ), Config (.. ), IOLayer (.. ),
29+ RequestType (.. ), TicketId , TicketInfo (.. ), TicketTag (.. ),
30+ ZendeskLayer (.. ), ZendeskResponse (.. ), asksIOLayer , asksZendeskLayer ,
31+ assignToPath , defaultConfig , knowledgebasePath , renderTicketStatus ,
32+ runApp , tokenPath )
3033
3134------------------------------------------------------------
3235-- Functions
@@ -53,8 +56,8 @@ runZendeskMain = do
5356 -- At this point, the configuration is set up and there is no point in using a pure IO.
5457 case args of
5558 CollectEmails -> runApp collectEmails cfg
56- (ProcessTicket ticketId) -> runApp (processTicket ticketId) cfg
57- ProcessTickets -> runApp processTickets cfg
59+ (ProcessTicket ticketId) -> void $ runApp (processTicket ticketId) cfg
60+ ProcessTickets -> void $ runApp processTickets cfg
5861 FetchTickets -> runApp fetchTickets cfg
5962 ShowStatistics -> runApp showStatistics cfg
6063
@@ -70,65 +73,80 @@ collectEmails = do
7073 let ticketIds = foldr (\ TicketInfo {.. } acc -> ticketId : acc) [] tickets
7174 mapM_ extractEmailAddress ticketIds
7275
73- processTicket :: TicketId -> App ()
76+
77+ processTicket :: TicketId -> App [ZendeskResponse ]
7478processTicket ticketId = do
79+
7580 -- We first fetch the function from the configuration
76- getTicketInfo <- asksZendeskLayer zlGetTicketInfo
77- putTextLn " Processing single ticket"
78- ticketInfo <- getTicketInfo ticketId
79- processTicketAndId ticketInfo
80- putTextLn " Process finished, please see the following url"
81- putTextLn $ " https://iohk.zendesk.com/agent/tickets/" <> show ticketId
81+ getTicketInfo <- asksZendeskLayer zlGetTicketInfo
82+ printText <- asksIOLayer iolPrintText
83+
84+ printText " Processing single ticket"
85+
86+ ticketInfo <- getTicketInfo ticketId
87+ attachments <- getTicketAttachments ticketInfo
88+
89+ zendeskResponse <- mapM (inspectAttachment ticketInfo) attachments
90+
91+ postTicketComment <- asksZendeskLayer zlPostTicketComment
92+ _ <- mapM postTicketComment zendeskResponse
93+
94+ printText " Process finished, please see the following url"
95+ printText $ " https://iohk.zendesk.com/agent/tickets/" <> show ticketId
96+
97+ pure zendeskResponse
98+
8299
83100processTickets :: App ()
84101processTickets = do
85- cfg <- ask
86- sortedTicketIds <- processBatchTickets cfg
87- mapM_ processTicketAndId sortedTicketIds
102+ sortedTicketIds <- listAndSortTickets
103+
104+ _ <- mapM (processTicket . ticketId) sortedTicketIds
105+
88106 putTextLn " All the tickets has been processed."
89107
108+
90109fetchTickets :: App ()
91110fetchTickets = do
92- cfg <- ask
93- sortedTicketIds <- processBatchTickets cfg
111+ sortedTicketIds <- listAndSortTickets
94112 mapM_ (putTextLn . show ) sortedTicketIds
95113 putTextLn " All the tickets has been processed."
96114
115+
97116showStatistics :: App ()
98117showStatistics = do
99118 cfg <- ask
100119 -- We first fetch the function from the configuration
101120 listTickets <- asksZendeskLayer zlListTickets
102121
103122 putTextLn $ " Classifier is going to gather ticket information assigned to: " <> cfgEmail cfg
104- liftIO printWarning
105- tickets <- listTickets Assigned
123+
124+ tickets <- listTickets Assigned
106125 liftIO $ printTicketCountMessage tickets (cfgEmail cfg)
107126
108127
109- processBatchTickets :: Config -> App [TicketInfo ]
110- processBatchTickets cfg = do
128+ listAndSortTickets :: App [TicketInfo ]
129+ listAndSortTickets = do
130+
131+ Config {.. } <- ask
111132
112133 -- We first fetch the function from the configuration
113134 listTickets <- asksZendeskLayer zlListTickets
135+ printText <- asksIOLayer iolPrintText
136+
137+ printText $ " Classifier is going to process tickets assign to: " <> cfgEmail
114138
115- putTextLn $ " Classifier is going to process tickets assign to: " <> cfgEmail cfg
116- liftIO printWarning
117- tickets <- listTickets Assigned
139+ tickets <- listTickets Assigned
118140
119141 let filteredTicketIds = filterAnalyzedTickets tickets
120142 let sortedTicketIds = sortBy compare filteredTicketIds
121143
122- putTextLn $ " There are " <> show (length sortedTicketIds) <> " unanalyzed tickets."
123- putTextLn " Processing tickets, this may take hours to finish."
144+ printText $ " There are " <> show (length sortedTicketIds) <> " unanalyzed tickets."
145+ printText " Processing tickets, this may take hours to finish."
124146
125147 pure sortedTicketIds
126148
127149
128- -- | Warning
129- printWarning :: IO ()
130- printWarning = putTextLn " Note that this process may take a while. Please do not kill the process"
131-
132150-- | Print how many tickets are assinged, analyzed, and unanalyzed
133151printTicketCountMessage :: [TicketInfo ] -> Text -> IO ()
134152printTicketCountMessage tickets email = do
@@ -177,36 +195,42 @@ extractEmailAddress ticketId = do
177195 liftIO $ appendFile " emailAddress.txt" (emailAddress <> " \n " )
178196 liftIO $ putTextLn emailAddress
179197
198+
180199-- | Process specifig ticket id (can be used for testing) only inspects the one's with logs
181- processTicketAndId :: TicketInfo -> App ()
182- processTicketAndId ticketInfo@ TicketInfo {.. } = do
200+ -- TODO(ks): Switch to `(MonadReader Config m)`, pure function?
201+ getTicketAttachments :: TicketInfo -> App [Attachment ]
202+ getTicketAttachments TicketInfo {.. } = do
183203
184- getTicketComments <- asksZendeskLayer zlGetTicketComments
204+ -- Get the function from the configuration
205+ getTicketComments <- asksZendeskLayer zlGetTicketComments
206+ comments <- getTicketComments ticketId
207+
208+ -- However, if we want this to be more composable...
209+ pure $ getAttachmentsFromComment comments
185210
186- comments <- getTicketComments ticketId
187211
212+ -- | A pure function for fetching @Attachment@ from @Comment@.
213+ getAttachmentsFromComment :: [Comment ] -> [Attachment ]
214+ getAttachmentsFromComment comments = do
188215 -- Filter tickets without logs
189- -- Could analyze the comments but I don't see it useful..
190216 let commentsWithAttachments :: [Comment ]
191- commentsWithAttachments = filter ( \ x -> length (cAttachments x) > 0 ) comments
217+ commentsWithAttachments = filter commentHasAttachment comments
192218
193219 -- Filter out ticket without logs
194220 let attachments :: [Attachment ]
195221 attachments = concatMap cAttachments commentsWithAttachments
196222
197- let justLogs :: [ Attachment ]
198- justLogs = filter ( \ x -> " application/zip " == aContentType x) attachments
223+ -- Filter out non-logs
224+ filter isAttachmentZip attachments
199225
200- mapM_ (inspectAttachmentAndPostComment ticketInfo) justLogs
226+ where
227+ commentHasAttachment :: Comment -> Bool
228+ commentHasAttachment comment = length (cAttachments comment) > 0
201229
202- -- | Inspect attachment then post comment to the ticket
203- inspectAttachmentAndPostComment :: TicketInfo -> Attachment -> App ()
204- inspectAttachmentAndPostComment ticketInfo attachment = do
205- liftIO $ putTextLn $ " Analyzing ticket: " <> show ticketInfo
206- zendeskResponse <- inspectAttachment ticketInfo attachment
230+ -- Readability
231+ isAttachmentZip :: Attachment -> Bool
232+ isAttachmentZip attachment = " application/zip" == aContentType attachment
207233
208- postTicketComment <- asksZendeskLayer zlPostTicketComment
209- postTicketComment zendeskResponse
210234
211235-- | Given number of file of inspect, knowledgebase and attachment,
212236-- analyze the logs and return the results.
@@ -217,17 +241,18 @@ inspectAttachmentAndPostComment ticketInfo attachment = do
217241inspectAttachment :: TicketInfo -> Attachment -> App ZendeskResponse
218242inspectAttachment ticketInfo@ TicketInfo {.. } att = do
219243
220- Config {.. } <- ask
244+ Config {.. } <- ask
221245
222- getAttachment <- asksZendeskLayer zlGetAttachment
246+ getAttachment <- asksZendeskLayer zlGetAttachment
247+ printText <- asksIOLayer iolPrintText
223248
224249 rawlog <- getAttachment att -- Get attachment
225250 let results = extractLogsFromZip cfgNumOfLogsToAnalyze rawlog
226251
227252 case results of
228253 Left _ -> do
229254
230- liftIO . putStrLn . renderErrorCode $ SentLogCorrupted
255+ printText . renderErrorCode $ SentLogCorrupted
231256
232257 pure ZendeskResponse
233258 { zrTicketId = ticketId
@@ -246,7 +271,7 @@ inspectAttachment ticketInfo@TicketInfo{..} att = do
246271
247272 let fErrorCode = foldr (\ errorCode acc -> errorCode <> " ;" <> acc) " " errorCodes
248273
249- liftIO . putTextLn $ fErrorCode
274+ printText fErrorCode
250275
251276 pure ZendeskResponse
252277 { zrTicketId = ticketId
@@ -257,7 +282,7 @@ inspectAttachment ticketInfo@TicketInfo{..} att = do
257282
258283 Left _ -> do
259284
260- liftIO . putStrLn . renderTicketStatus $ NoKnownIssue
285+ printText . renderTicketStatus $ NoKnownIssue
261286
262287 pure ZendeskResponse
263288 { zrTicketId = ticketId
0 commit comments