From 474fbe0850ba5dc6a7aa8eb06c60d229af674dcb Mon Sep 17 00:00:00 2001
From: kd From: %s %s %s {} ```>1|ERROR|2020-03-17 15:50:00,018-0400|org.springframework.jms.listener.DefaultMessageListenerContainer#0-3|GenerateEmailNotificationTemplate:106|Unexpected error in processing. Abending. ### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotifications at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77) at com.sun.proxy.$Proxy49.selectList(Unknown Source) at com.acuityads.persistence.dao.SelectQuery.selectList(SelectQuery.java:74)``` some code
+ rich_html_content = MSGraphApi.insertCodeBlocksInMessage(customMessage["text"])
+
+ html_content = "" + \
+ "{}
".format(tok)
+
+ quoted_str = "".join(toks)
+ return quoted_str
+
+ @staticmethod
+ def searchValuesinDict(lib, value):
+ """TODO: Docstring for searchValuesinDict.
+
+ :returns: the key of the dict where the value matches
+
+ """
+ for key in lib.keys():
+ if lib[key] == value:
+ return key
+ return ""
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..3ceef42
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,3 @@
+requests>=2,<3
+msal>=0,<2
+jsonpath-ng>=1.5
diff --git a/slack_parser.py b/slack_parser.py
new file mode 100644
index 0000000..8756992
--- /dev/null
+++ b/slack_parser.py
@@ -0,0 +1,169 @@
+import os
+import json
+import re
+import time
+from jsonpath_ng import jsonpath, parse
+
+
+# jsonpath match will match in order of appearence.
+# This mapping defines which position in the RESULT of match to look for which key.
+# so. eg. at 0th position (of the match) will be the key "text", etc.
+key_orderings = {
+ "text": 0,
+ "ts": 1,
+ "name": 2
+}
+
+class SlackParser(object):
+
+ """Docstring for SlackParser. """
+
+ def __init__(self, messagesDir=None, usersFile=None, keys=None):
+
+ self.messagesDir = ""
+ self.usersFile = ""
+ self.keys = list()
+ if messagesDir is not None:
+ self.messagesDir += messagesDir
+ if usersFile is not None:
+ self.usersFile += usersFile
+ if keys is not None:
+ self.keys += keys
+
+ self.usersLib = None
+
+ def getSortedMessageFiles(self):
+ """Docstring for getSortedMessageFiles.
+ Get the files that contain channel data sorted.
+
+ :returns: a sorted list of json files names for the channel
+
+ """
+ files = sorted(os.listdir( self.messagesDir ))
+ return files
+
+ def getUsers(self):
+ """Docstring for getUsers
+ Get the parsed json that contains the raw user info.
+
+ :returns: a dict with all users infos
+
+ """
+ path = self.usersFile
+ # TODO: check for path validity
+ content = json.load(open(path))
+ return content
+
+
+ def buildUsersLibrary(self):
+ """Docstring for buildUsersLibrary
+ Build a Users dict that keeps only usersid to name mappings.
+
+ :returns: a dict user info
+
+ """
+ userLib = {}
+ for userjson in self.getUsers():
+ if "name" in userjson and "id" in userjson:
+ userLib[userjson["id"]] = userjson["name"]
+ self.setUsersLib(userLib)
+ return self
+
+ def setUsersLib(self, usersLib):
+ """Docstring for buildUsersLibrary
+ A setter method that sets the object lib to the given lib.
+
+ """
+ self.usersLib = usersLib
+
+ def getMessages(self, slackFile):
+ """Docstring for getMessages.
+ Given a file, get the messages in that file.
+
+ :slackFile: the json file name containing messages
+ :returns: a list of all messages in the file
+
+ """
+ path = self.messagesDir + "/" + slackFile
+ # TODO: check for path validity
+ content = json.load(open(path))
+ return content
+
+ def getKeyValues(self, slackMessage):
+ """Docstring for getKeyValues.
+ Get the value for each key (defined during init) from the mssg.
+
+ :slackMessage: the parsed json slack message
+ :returns: on success, a dict with only required keys and values
+ on failure, None
+
+ """
+ data = None
+ # we also check for "subtype" to filter out mssgs like:
+ # "s joined the channel", "x left the channel" etc.
+ if "type" in slackMessage and "subtype" not in slackMessage:
+ if slackMessage["type"] == "message" and slackMessage["text"] != "":
+ data = {}
+ jsonpath_expression = parse("$.." + str(self.keys))
+ # search slackMessage for each key, and add all matches (incl. dups.) in-order
+ match = jsonpath_expression.find(slackMessage)
+ #print(match[0])
+ for key in self.keys:
+ # TODO: validation for matches possible "index out of range" here.
+ data[key] = match[key_orderings[key]].value
+ # get the user id in the text
+ userIds = self.getUserIdsFromMessage(data["text"])
+ if userIds != []:
+ # find corresponding usernames from library
+ userNamesinMessageDict = self.getUserNamesFromIds(self.usersLib, userIds)
+ # inject usernames in the text replace userid
+ data["text"] = self.replaceMessageUserIdsWithNames(userNamesinMessageDict, data["text"])
+ # convert ts to a human readble time
+ data["ts"] = time.strftime( "%H:%M %b %d, %Y", time.localtime(float(data["ts"])) )
+
+ return data
+
+ @staticmethod
+ def getUserIdsFromMessage(text):
+ """Docstring for getUserIdsFromMessage.
+ A helper method that searches a text for Slack user ids. It maps:
+ "<@SLACKID1>adsfasdf <@SLACKID2>" -> ["SLACKID1", "SLACKID2"]
+
+ :text: raw text from Slack message json
+ :returns: a list of userids strings in the text in order of match
+
+ """
+ regex = r"<@(.+?)>"
+ matches = re.findall(regex, text)
+ return matches
+
+ @staticmethod
+ def getUserNamesFromIds(usersDict, userIds):
+ """Docstring for getUserNamesFromIds.
+ A helper method to search a dict for a keys, and get the values.
+
+ :usersDict: a dict library of userid and name mappings
+ :ids: a string list of userids
+ :returns: dict of each userid and its username
+
+ """
+ userMap = {}
+ for userId in userIds:
+ if userId in usersDict:
+ userMap[userId] = usersDict[userId]
+ return userMap
+
+ @staticmethod
+ def replaceMessageUserIdsWithNames(messageUsersDict, text):
+ """Docstring for replaceMessageUserIdsWithNames.
+ A helper method to replace all Slackuser id tags with their @username. So it maps:
+ { "USERID1" : "name1" , "USERID2": "name2" },"<@USERID1> blah blah <@USERID2>." -> "@name1 blah blah @name2."
+
+ :messageUsersDict: the dict containing userid to username mappings for this text
+ :text: the original string contianing Slack userids in it
+ :returns: a string with each Slack userid replaced with mapping value from dict.
+
+ """
+ for userId in messageUsersDict.keys():
+ text = re.sub( "<@{}>".format(userId), "@{}".format(messageUsersDict[userId]), text )
+ return text
diff --git a/test.py b/test.py
new file mode 100644
index 0000000..81daa0b
--- /dev/null
+++ b/test.py
@@ -0,0 +1,96 @@
+import unittest
+
+from msgraph import MSGraphApi
+from slack_parser import SlackParser
+
+class Test(unittest.TestCase):
+ """
+ The basic class that inherits unittest.TestCase
+ """
+ msapi = MSGraphApi()
+ sp = SlackParser()
+
+ # test case function to check the MSGraphApi.insertCodeBlocksInMessage function
+ def test_0_insert_block_quotes(self):
+ input_string = "sdfsdf"
+ expected = "sdfsdf"
+ actual = self.msapi.insertCodeBlocksInMessage(input_string)
+ self.assertEqual( expected, actual )
+
+ def test_1_insert_block_quotes(self):
+ input_string = "``` CODE1 ```"
+ expected = " CODE1
"
+ actual = self.msapi.insertCodeBlocksInMessage(input_string)
+ self.assertEqual( expected, actual )
+
+ def test_2_insert_block_quotes(self):
+ input_string = " there and ``` CODE 1 ``` asdfasdf ``` CODE 2 ``` and then again ``` CODE 3 ``` end."
+ expected = " there and CODE 1
asdfasdf CODE 2
and then again CODE 3
end."
+ actual = self.msapi.insertCodeBlocksInMessage(input_string)
+ self.assertEqual( expected, actual )
+
+ def test_3_insert_long_block_quotes(self):
+ input_string = """<@U5F956SJH> a long running query on the master from job01.....job-consumer-2 logs showing\n```>1|ERROR|2020-03-17 15:50:00,018-0400|org.springframework.jms.listener.DefaultMessageListenerContainer#0-3|GenerateEmailNotificationTemplate:106|Unexpected error in processing. Abending.\norg.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:\n### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotifications\n### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotifications\n at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)\n at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)\n at com.sun.proxy.$Proxy49.selectList(Unknown Source)\n at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)\n at com.acuityads.persistence.dao.SelectQuery.selectList(SelectQuery.java:74)```\n
+ """
+ expected = """<@U5F956SJH> a long running query on the master from job01.....job-consumer-2 logs showing\n>1|ERROR|2020-03-17 15:50:00,018-0400|org.springframework.jms.listener.DefaultMessageListenerContainer#0-3|GenerateEmailNotificationTemplate:106|Unexpected error in processing. Abending.\norg.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:\n### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotifications\n### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotifications\n at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)\n at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)\n at com.sun.proxy.$Proxy49.selectList(Unknown Source)\n at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)\n at com.acuityads.persistence.dao.SelectQuery.selectList(SelectQuery.java:74)
\n
+ """
+
+ actual = self.msapi.insertCodeBlocksInMessage(input_string)
+ self.assertEqual( expected, actual )
+
+ def test_4_insert_block_quotes(self):
+ input_string = """<@U5F956SJH> a long running query on the master from job01.....job-consumer-2 logs showing>1|ERROR|2020-03-17 15:50:00,018-0400|org.springframework.jms.listener.DefaultMessageListenerContainer#0-3|GenerateEmailNotificationTemplate:106|Unexpected error in processing. Abending.
### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotifications
### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.acuityads.notification.dao.NotificationMapper.findNotificationsat org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)at com.sun.proxy.$Proxy49.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)at com.acuityads.persistence.dao.SelectQuery.selectList(SelectQuery.java:74)
+ """ + actual = self.msapi.insertCodeBlocksInMessage(input_string) + self.assertEqual( expected, actual ) + + def test_5_parse_user_ids(self): + input_string = "a long history of thisg" + expected = [] + actual = self.sp.getUserIdsFromMessage(input_string) + self.assertEqual( expected, actual ) + + def test_6_parse_user_ids(self): + input_string = "<@U32498ksjl> a long history of thisg" + expected = ["U32498ksjl"] + actual = self.sp.getUserIdsFromMessage(input_string) + self.assertEqual( expected, actual ) + + def test_7_parse_user_ids(self): + input_string = " what<@U32498ksjl> a long history
of thisg <@kumar12>" + expected = ["U32498ksjl", "kumar12"] + actual = self.sp.getUserIdsFromMessage(input_string) + self.assertEqual( expected, actual ) + + def test_8_get_user_name_from_user_ids(self): + usersDict = { "U32498ksjl" : "kd1" , "UBLAH" : "kd2" } + test_input = ["U32498ksjl"] + expected = { "U32498ksjl" : "kd1" } + actual = self.sp.getUserNamesFromIds(usersDict, test_input) + self.assertEqual( expected, actual ) + + def test_9_get_user_name_from_user_ids(self): + usersDict = { "U32498ksjl" : "kd1" , "UBLAH" : "kd2" } + test_input = ["U32498ksjl", "UBLAH"] + expected = { "U32498ksjl" : "kd1", "UBLAH" : "kd2" } + actual = self.sp.getUserNamesFromIds(usersDict, test_input) + self.assertEqual( expected, actual ) + + def test_10_replace_userids_with_names(self): + usersDict = { "U32498ksjl" : "kd1" } + input_string = "<@U32498ksjl> a long history of thisg" + expected = "@kd1 a long history of thisg" + actual = self.sp.replaceMessageUserIdsWithNames(usersDict, input_string) + self.assertEqual( expected, actual ) + + def test_11_replace_userids_with_names(self): + usersDict = { "U32498ksjl" : "kd1" , "UBLAH" : "kd2" } + input_string = "<@U32498ksjl> a long history of thisg from <@UBLAH> and this <@UBLAH> again." + expected = "@kd1 a long history of thisg from @kd2 and this @kd2 again." + actual = self.sp.replaceMessageUserIdsWithNames(usersDict, input_string) + self.assertEqual( expected, actual ) + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.3