From dd8399f2016889827c39da70b61a6644698f56fc Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 14 Oct 2020 14:05:47 -0400 Subject: [PATCH 01/18] More generalized error statements, refactored error parsing dictionary to include got, expected, file_path, and line_num keys --- api/ECNQueue.py | 196 +++++++++++++++++++----------------------------- 1 file changed, 76 insertions(+), 120 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index c42942d..1093734 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -508,28 +508,20 @@ def __editParsing(self, line: str, lineNum: int) -> dict: formattedDateTime = "" editedBy = "" - if not line.endswith(" ***\n"): - - columnNum = len(line) - 1 - errorMessage = "Expected the delimiter to end with \" ***\n\"" - - return self.__errorParsing(line, lineNum, columnNum, errorMessage) - # Parses for the author of the edit, which is located between the "*** Edited by: " and " at:" substrings - editedBy = (re.search("(?<=\*{3} Edited by: )(.*)(?= at:)", line)).group() - - # ece23 + try: + editedBy = (re.search("(?<=\*{3} Edited by: )(.*)(?= at:)", line)).group() + except: + errorMessage = "*** Edited by: [username] at: [date and time] ***\n" + return self.__errorParsing(line, lineNum, errorMessage) + try: # Parses for the date and time of the edit, which is located between the " at: " and "***\n" substrings dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", line)).group() - except: # Returns an error message if there is no space after "at:" - - columnNum = line.find("at:") + 3 - errorMessage = "Expected a space after \"at:\" followed by the date which is followed by \" ***\n\"" - - return self.__errorParsing(line, lineNum, columnNum, errorMessage) + errorMessage = "*** Edited by: [username] at: [date and time] ***\n" + return self.__errorParsing(line, lineNum, errorMessage) # Attempts to format the date and time into utc format formattedDateTime = self.__getFormattedDate(dateTimeString) @@ -554,19 +546,20 @@ def __replyToParsing(self, line: str, lineNum: int) -> dict: repliedBy = "" #tech112 - # Checks for malformed delimiter - if not line.endswith(" ***\n"): - - columnNum = len(line) - 1 - errorMessage = "Expected the delimiter to end with \" ***\n\"" - - return self.__errorParsing(line, lineNum, columnNum, errorMessage) - # Parses for the author of the reply, which is located between the "*** Replied by: " and " at:" substrings - repliedBy = (re.search("(?<=\*{3} Replied by: )(.*)(?= at:)", line)).group() + try: + # Parses for the author of the reply, which is located between the "*** Replied by: " and " at:" substrings + repliedBy = (re.search("(?<=\*{3} Replied by: )(.*)(?= at:)", line)).group() + except: + errorMessage = "*** Replied by: [username] at: [date and time] ***\n" + return self.__errorParsing(line, lineNum, errorMessage) # Parses for the date and time of the reply, which is located between the " at: " and "***\n" substrings - dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", line)).group() + try: + dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", line)).group() + except: + errorMessage = "*** Replied by: [username] at: [date and time] ***\n" + return self.__errorParsing(line, lineNum, errorMessage) # Formats date to UTC formattedDateTime = self.__getFormattedDate(dateTimeString) @@ -591,18 +584,20 @@ def __statusParsing(self, line: str, lineNum: int) -> dict: updatedBy = "" # Parses for the author of the status change, which is located between the "*** Status updated by: " and " at:" substrings - updatedBy = (re.search("(?<=\*{3} Status updated by: )(.*)(?= at:)", line)).group() - - # tech 56 - if not line.endswith(" ***\n"): - - columnNum = len(line) - 1 - errorMessage = "Expected the delimiter to end with \" ***\n\"" + try: + updatedBy = (re.search("(?<=\*{3} Status updated by: )(.*)(?= at:)", line)).group() + except: + errorMessage = "*** Status updated by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, columnNum, errorMessage) + return self.__errorParsing(line, lineNum, errorMessage) # Parses for the date and time of the status change, which is located between the " at: " and "***\n" substrings - dateTimeString = re.search("(?<= at: )(.*)(?= \*\*\*\n)", line).group() + try: + dateTimeString = re.search("(?<= at: )(.*)(?= \*\*\*\n)", line).group() + except: + errorMessage = "*** Status updated by: [username] at: [date and time] ***\n" + + return self.__errorParsing(line, lineNum, errorMessage) # Formats the date to UTC formattedDateTime = self.__getFormattedDate(dateTimeString) @@ -622,85 +617,64 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: Returns: dictionary: "type": "replyFromUser", datetime, subject, userName, userEmail, content, ccRecipients """ - formattedDateTime = "" - repliedByName = "" - repliedByEmail = "" - subject = "" - ccRecipientsList = [] + replyFromInfo = { + "type": "reply_from_user", + "datetime": "", + "from_name": "", + "from_email": "", + "cc": [], + "content": [] + } + newLineCounter = 0 endingDelimiterCount = 0 + # Delimiter information line numbers to remove from reply from user linesToRemove =[] - # Parses the section content looking for any line that starts with a metadata, also tracks the line # number with the enumerate function for lineNum, line in enumerate(replyContent): - + #Checks for a newline and breaks for loop on second occurance of a newline if line == "\n": - newLineCounter = newLineCounter + 1 - - if line.startswith("===="): - - endingDelimiterCount = endingDelimiterCount + 1 - - if endingDelimiterCount > 1: - - errorMessage = "Encountered two reply-from-user ending delimiters and expected only one" - - return self.__errorParsing(line, lineNumber + lineNum + 1, 0, errorMessage) + if newLineCounter == 2: + break elif endingDelimiterCount == 0 and lineNum == len(replyContent) - 1: - errorMessage = "Did not encounter a reply-from-user ending delimiter" + return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) - return self.__errorParsing(line, lineNumber + lineNum + 1, 0, errorMessage) - - # Checks for lines starting with Subject, From, Date and CC - elif line.startswith("Subject: ") and newLineCounter < 2: - - # Matches everything after "Subject: " in the line - subject = (re.search("(?<=Subject: )(.*)", line)).group() - - linesToRemove.append(lineNum) - - elif line.startswith("From: ") and newLineCounter < 2: - - # Returns a list of tuples with name and email information + elif line.startswith("From: "): + # Returns a list of one tuples with a name stored in the first index of the tuple and an email stored in the second index of the tuple emailList = email.utils.getaddresses([line]) - - # The name in stored in the first index of the tuple - repliedByName = emailList[0][0] - - # The email is stored in the second index of the tuple - repliedByEmail = emailList[0][1] + replyFromInfo["from_name"] = emailList[0][0] + replyFromInfo["from_email"] = emailList[0][1] linesToRemove.append(lineNum) - elif line.startswith("Date: ") and newLineCounter < 2: - + elif line.startswith("Date: "): # Matches everything after "Date: " + try: + dateStr = (re.search("(?<=Date: )(.*)", line)).group() + except: + errorMessage = "\"Date: [datetime]\"" + return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) - dateStr = (re.search("(?<=Date: )(.*)", line)).group() - - dateStr = "" # Formatts the date to UTC - formattedDateTime = self.__getFormattedDate(dateStr) + replyFromInfo["datetime"] = self.__getFormattedDate(dateStr) linesToRemove.append(lineNum) - elif line.startswith("Cc: ") and newLineCounter < 2: - + elif line.startswith("Cc: "): # Returns a list of tuples with email information recipientsList = email.utils.getaddresses([line]) # Parses through the cc tuple list for cc in recipientsList: - # Stores the cc information in a dictionary and appends it to the ccRecipientsList - ccRecipientsList.append( + replyFromInfo["cc"].append( {"name":cc[0], "email":cc[1]} ) @@ -712,16 +686,7 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: replyContent.pop(lineNum) # Strips any unnecessary newlines or any delimiters frm the message content - replyContent = self.__getFormattedMessageContent(replyContent) - - replyFromInfo = { - "type": "reply_from_user", - "datetime": formattedDateTime, - "from_name": repliedByName, - "from_email": repliedByEmail, - "cc": ccRecipientsList, - "content": replyContent - } + replyFromInfo["content"] = self.__getFormattedMessageContent(replyContent) return replyFromInfo @@ -731,38 +696,29 @@ def __getFormattedMessageContent(self, messageContent: list) -> list: Returns: list: formattedMessageContent """ - # Parses looking for the reply-from-user ending delimiter adn removes the first line that contains the ending delimiter. - - - # Continually loops while looking at the first line of messageContent, removing it from messageContent - # if its a newline. Doesn't run if there is only one line in message content. + # Continually removes the first line of messageContent if it is a newline or delimiter in each iteration while len(messageContent) > 1: if messageContent[0] == "\n" or messageContent[0].startswith("***") or messageContent[0].startswith("===") : messageContent.pop(0) - else: - # Breaks the loop if the first line isn't a newline + # Breaks the loop if the first line isn't a newline or delimiter break - + + # Continually removes the last line of messageContent if it is a newline or delimiter in each iteration while len(messageContent) > 1: - # Initializes the Length of messageContent each iteration of the loop messagecontentLength = len(messageContent) - # Checks if the last line is a newline if messageContent[messagecontentLength -1] == "\n" or messageContent[messagecontentLength -1].startswith("===="): - - # Deletes the last line if it is a newline messageContent.pop(messagecontentLength - 1) - else: - # Breaks the loop if the last line isn't a newline + # Breaks the loop if the last line isn't a newline or delimiter break return messageContent - def __errorParsing(self, line: str, lineNum: int, lineColumn: int, errorMessage: str) -> dict: + def __errorParsing(self, line: str, lineNum: int, expectedSyntax: str) -> dict: """Returns a dictionary with error parse information Returns: @@ -779,17 +735,23 @@ def __errorParsing(self, line: str, lineNum: int, lineColumn: int, errorMessage: errorDictionary = { "type": "parse_error", "datetime": self.__getFormattedDate(str(datetime.datetime.now())), - "content": [] + "file_path": "", + "expected": "", + "got": "", + "line_num": 0 } - # Error message with itemm line and column numbers - errorMessage = errorMessage + " at " + str(lineNum) + ":" + str(lineColumn) + # Filepath + errorDictionary["file_path"] = self.__path - # Appends the error message to the content list in the error dictionary - errorDictionary["content"].append(errorMessage) + # Error message with itemm line and column numbers + errorDictionary["expected"] = expectedSyntax # Appends the item line to the content list in the error dictionary - errorDictionary["content"].append(line) + errorDictionary["got"] = line + + # Apeends error num to the dictonary + errorDictionary["line_num"] = lineNum # returns the error dictionary return errorDictionary @@ -983,9 +945,3 @@ def getQueues() -> list: queues.append(Queue(file)) return queues -if __name__ == "__main__": - item = Item("aae", 2) - print() -# for queue in getQueues(): -# for item in queue.items: -# print(f"${item.queue} ${item.number}") \ No newline at end of file From 92e11f24daeb2b05ced1823071d3d5ce24ca735e Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Fri, 16 Oct 2020 09:14:39 -0400 Subject: [PATCH 02/18] Error parsing detects ending reply-from-user delimters --- api/ECNQueue.py | 152 +++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 79 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 1093734..8740614 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -293,7 +293,7 @@ def __parseSections(self) -> list: elif boundary["type"] == "edit": # Returns a dictionary with edit information - editInfo = self.__editParsing(line, boundary["start"]) + editInfo = self.__editParsing(sectionContent, boundary["start"]) # Checks for a parse error and appends it to sections and exits the function if editInfo["type"] == "parse_error": @@ -302,21 +302,13 @@ def __parseSections(self) -> list: return sections - # Remove the delimiter String and unecessary newlines - sectionContent = self.__getFormattedMessageContent(sectionContent) - - # Appends content of the edit message to the dictionary - editInfo["content"] = sectionContent - # Appends the edit dictionary to sections sections.append(editInfo) - continue - elif boundary["type"] == "replyToUser": # Returns a dictionary with reply-to information - replyToInfo = self.__replyToParsing(line, boundary["start"]) + replyToInfo = self.__replyToParsing(sectionContent, boundary["start"]) if replyToInfo["type"] == "parse_error": @@ -324,21 +316,13 @@ def __parseSections(self) -> list: return sections - # Removes the begining delimiter - sectionContent = self.__getFormattedMessageContent(sectionContent) - - # Appends content of the reply-to message to the dicionary - replyToInfo['content'] = sectionContent - # Appends the reply-to to sections sections.append(replyToInfo) - continue - elif boundary["type"] == "status": # Returns a dictionary with status information - statusInfo = self.__statusParsing(line, boundary["start"]) + statusInfo = self.__statusParsing(sectionContent, boundary["start"]) if statusInfo["type"] == "parse_error": @@ -346,17 +330,9 @@ def __parseSections(self) -> list: return sections - # Removes the begining delimiter - sectionContent = self.__getFormattedMessageContent(sectionContent) - - # Appends content to empty content key to avoid passing large amounts of info that isnt used within the function - statusInfo['content'] = sectionContent - # Appends the status to sections sections.append(statusInfo) - continue - elif boundary["type"] == "replyFromUser": # Returns a dictionary with userReply information @@ -498,7 +474,7 @@ def __initialMessageParsing(self, content: list) -> dict: return initialMessageDictionary - def __editParsing(self, line: str, lineNum: int) -> dict: + def __editParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with edit information Returns: @@ -507,35 +483,38 @@ def __editParsing(self, line: str, lineNum: int) -> dict: formattedDateTime = "" editedBy = "" - + delimiterLine = content[0] # Parses for the author of the edit, which is located between the "*** Edited by: " and " at:" substrings try: - editedBy = (re.search("(?<=\*{3} Edited by: )(.*)(?= at:)", line)).group() + editedBy = (re.search("(?<=\*{3} Edited by: )(.*)(?= at:)", delimiterLine)).group() except: errorMessage = "*** Edited by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, errorMessage) + return self.__errorParsing(delimiterLine, lineNum, errorMessage) try: # Parses for the date and time of the edit, which is located between the " at: " and "***\n" substrings - dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", line)).group() + dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", delimiterLine)).group() except: # Returns an error message if there is no space after "at:" errorMessage = "*** Edited by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, errorMessage) + return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Attempts to format the date and time into utc format formattedDateTime = self.__getFormattedDate(dateTimeString) + # Remove the delimiter String and unecessary newlines + formattedContent = self.__getFormattedMessageContent(content) + editInfo = { "type": "edit", "datetime": formattedDateTime, "by": editedBy, - "content": "" + "content": formattedContent } return editInfo - def __replyToParsing(self, line: str, lineNum: int) -> dict: + def __replyToParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with reply to user information Returns: @@ -544,36 +523,43 @@ def __replyToParsing(self, line: str, lineNum: int) -> dict: formattedDateTime = "" repliedBy = "" + delimiterLine = content[0] + for count, line in enumerate(content): + if line.startswith("===="): + errorMessage = "Reply-from-user ending delimter encountered without Reply-from-user starting delimter" + return self.__errorParsing(line, lineNum + count + 1, errorMessage) #tech112 try: # Parses for the author of the reply, which is located between the "*** Replied by: " and " at:" substrings - repliedBy = (re.search("(?<=\*{3} Replied by: )(.*)(?= at:)", line)).group() + repliedBy = (re.search("(?<=\*{3} Replied by: )(.*)(?= at:)", delimiterLine)).group() except: errorMessage = "*** Replied by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, errorMessage) + return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Parses for the date and time of the reply, which is located between the " at: " and "***\n" substrings try: - dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", line)).group() + dateTimeString = (re.search("(?<= at: )(.*)(?= \*\*\*\n)", delimiterLine)).group() except: errorMessage = "*** Replied by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, errorMessage) + return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Formats date to UTC formattedDateTime = self.__getFormattedDate(dateTimeString) + formattedContent = self.__getFormattedMessageContent(content) + replyInfo = { "type": "reply_to_user", "datetime": formattedDateTime, "by": repliedBy, - "content": "" + "content": formattedContent } return replyInfo - def __statusParsing(self, line: str, lineNum: int) -> dict: + def __statusParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with status information Returns: @@ -582,32 +568,42 @@ def __statusParsing(self, line: str, lineNum: int) -> dict: formattedDateTime = "" updatedBy = "" + delimiterLine = content[0] + + for count, line in enumerate(content): + if line.startswith("===="): + errorMessage = "Reply-from-user ending delimter encountered without Reply-from-user starting delimter" + return self.__errorParsing(line, lineNum + count + 1, errorMessage) # Parses for the author of the status change, which is located between the "*** Status updated by: " and " at:" substrings try: - updatedBy = (re.search("(?<=\*{3} Status updated by: )(.*)(?= at:)", line)).group() + updatedBy = (re.search("(?<=\*{3} Status updated by: )(.*)(?= at:)", delimiterLine)).group() except: errorMessage = "*** Status updated by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, errorMessage) + return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Parses for the date and time of the status change, which is located between the " at: " and "***\n" substrings try: - dateTimeString = re.search("(?<= at: )(.*)(?= \*\*\*\n)", line).group() + dateTimeString = re.search("(?<= at: )(.*)(?= \*\*\*\n)", delimiterLine).group() except: errorMessage = "*** Status updated by: [username] at: [date and time] ***\n" - return self.__errorParsing(line, lineNum, errorMessage) + return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Formats the date to UTC formattedDateTime = self.__getFormattedDate(dateTimeString) - + + # Remove the delimiter String and unecessary newlines + formattedContent = self.__getFormattedMessageContent(content) + statusInfo = { "type": "status", "datetime": formattedDateTime, "by": updatedBy, - "content": "" - } + "content": formattedContent + } + return statusInfo @@ -617,14 +613,9 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: Returns: dictionary: "type": "replyFromUser", datetime, subject, userName, userEmail, content, ccRecipients """ - replyFromInfo = { - "type": "reply_from_user", - "datetime": "", - "from_name": "", - "from_email": "", - "cc": [], - "content": [] - } + replyFromInfo = {} + + replyFromInfo["type"] = "reply_from_user" newLineCounter = 0 endingDelimiterCount = 0 @@ -636,17 +627,18 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: # number with the enumerate function for lineNum, line in enumerate(replyContent): + if endingDelimiterCount == 0 and lineNum == len(replyContent) - 1: + errorMessage = "Did not encounter a reply-from-user ending delimiter" + return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) + #Checks for a newline and breaks for loop on second occurance of a newline if line == "\n": newLineCounter = newLineCounter + 1 - if newLineCounter == 2: - break - elif endingDelimiterCount == 0 and lineNum == len(replyContent) - 1: - errorMessage = "Did not encounter a reply-from-user ending delimiter" - return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) + elif line.startswith("===="): + endingDelimiterCount = endingDelimiterCount + 1 - elif line.startswith("From: "): + elif line.startswith("From: ") and newLineCounter == 1: # Returns a list of one tuples with a name stored in the first index of the tuple and an email stored in the second index of the tuple emailList = email.utils.getaddresses([line]) replyFromInfo["from_name"] = emailList[0][0] @@ -654,7 +646,7 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: linesToRemove.append(lineNum) - elif line.startswith("Date: "): + elif line.startswith("Date: ") and newLineCounter == 1: # Matches everything after "Date: " try: dateStr = (re.search("(?<=Date: )(.*)", line)).group() @@ -667,7 +659,10 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: linesToRemove.append(lineNum) - elif line.startswith("Cc: "): + elif line.startswith("Cc: ") and newLineCounter == 1: + + replyFromInfo["cc"] = [] + # Returns a list of tuples with email information recipientsList = email.utils.getaddresses([line]) @@ -675,8 +670,8 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: for cc in recipientsList: # Stores the cc information in a dictionary and appends it to the ccRecipientsList replyFromInfo["cc"].append( - {"name":cc[0], - "email":cc[1]} + {"name": cc[0], + "email": cc[1]} ) linesToRemove.append(lineNum) @@ -732,25 +727,24 @@ def __errorParsing(self, line: str, lineNum: int, expectedSyntax: str) -> dict: } """ - errorDictionary = { - "type": "parse_error", - "datetime": self.__getFormattedDate(str(datetime.datetime.now())), - "file_path": "", - "expected": "", - "got": "", - "line_num": 0 - } + errorDictionary = {} + + # Type + errorDictionary["type"] = "parse_error" + + # Dateime of the parse error + errorDictionary["datetime"] = self.__getFormattedDate(str(datetime.datetime.now())) - # Filepath + # Item filepath errorDictionary["file_path"] = self.__path - # Error message with itemm line and column numbers + # Expected value errorDictionary["expected"] = expectedSyntax - # Appends the item line to the content list in the error dictionary + # line that threw error errorDictionary["got"] = line - # Apeends error num to the dictonary + # line number that threw error errorDictionary["line_num"] = lineNum # returns the error dictionary @@ -944,4 +938,4 @@ def getQueues() -> list: if isDirectory and isValid: queues.append(Queue(file)) - return queues + return queues \ No newline at end of file From d2ad5bb3162551c718115e11409ff8b764916c7f Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Fri, 16 Oct 2020 09:36:06 -0400 Subject: [PATCH 03/18] Subject key added to the initial message --- api/ECNQueue.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 8740614..e5afa24 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -182,7 +182,6 @@ def __parseSections(self) -> list: contentEnd = len(self.__rawItem) - 1 #directoryInfo = {"type": "directoryInformation"} - initialMessageContent = [] initialMessageSection = True # Delimiter info @@ -419,10 +418,11 @@ def __initialMessageParsing(self, content: list) -> dict: Returns: dictionary: "type": "initial_message", "datetime": utcdate, - "from_name": fromName, - "from_email": userEmail, + "from_name": from_name, + "from_email": user_email, "to": [{email, name}], "cc": [{email, name}], + "subject": initial_message_subject "content": ["message_content"] """ initialMessageDictionary = {} @@ -467,7 +467,7 @@ def __initialMessageParsing(self, content: list) -> dict: "email": ccRecipients[1]} ) - #rawMessageContent = self.__rawItem[startLine : endLine] + initialMessageDictionary["subject"] = self.__getMostRecentHeaderByType("Subject") # Removes unecessary newlines from the begining and the end of the initial message initialMessageDictionary["content"] = self.__getFormattedMessageContent(content) @@ -938,4 +938,10 @@ def getQueues() -> list: if isDirectory and isValid: queues.append(Queue(file)) - return queues \ No newline at end of file + return queues +if __name__ == "__main__": + item = Item("ce", 100) + print() +# for queue in getQueues(): +# for item in queue.items: +# print(f"${item.queue} ${item.number}") \ No newline at end of file From f389afb2172992e2c8d05c267ded419d1345562c Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Fri, 16 Oct 2020 09:49:13 -0400 Subject: [PATCH 04/18] Reply-from-user subject key added to the reply from user dictionary --- api/ECNQueue.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index e5afa24..51a8139 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -617,6 +617,7 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: replyFromInfo["type"] = "reply_from_user" + replyFromHeaders = [] newLineCounter = 0 endingDelimiterCount = 0 @@ -644,6 +645,18 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: replyFromInfo["from_name"] = emailList[0][0] replyFromInfo["from_email"] = emailList[0][1] + linesToRemove.append(lineNum) + elif line.startswith("Subject: ") and newLineCounter == 1: + # Matches everything after "Subject: " + try: + subjectStr = (re.search("(?<=Subject: )(.*)", line)).group() + except: + errorMessage = "Expeted syntax of \"Subject: [subject]\"" + return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) + + # Formatts the date to UTC + replyFromInfo["subject"] = subjectStr + linesToRemove.append(lineNum) elif line.startswith("Date: ") and newLineCounter == 1: @@ -938,10 +951,4 @@ def getQueues() -> list: if isDirectory and isValid: queues.append(Queue(file)) - return queues -if __name__ == "__main__": - item = Item("ce", 100) - print() -# for queue in getQueues(): -# for item in queue.items: -# print(f"${item.queue} ${item.number}") \ No newline at end of file + return queues \ No newline at end of file From 1b734b76d6e993d82d8d522c95dea4b3f1ab9f2c Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Fri, 16 Oct 2020 10:11:16 -0400 Subject: [PATCH 05/18] Reply-from-user dictionary includes a list of headers passed in the reply-from-user section --- api/ECNQueue.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 51a8139..b3abb35 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -632,6 +632,17 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: errorMessage = "Did not encounter a reply-from-user ending delimiter" return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) + if newLineCounter == 1 and line != "\n": + + # Append header information for each headr line + headerType, content = line.split(": ", 1) + replyFromHeaders.append( + {"type": headerType, + "content": content + } + ) + + linesToRemove.append(lineNum) #Checks for a newline and breaks for loop on second occurance of a newline if line == "\n": newLineCounter = newLineCounter + 1 @@ -645,7 +656,6 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: replyFromInfo["from_name"] = emailList[0][0] replyFromInfo["from_email"] = emailList[0][1] - linesToRemove.append(lineNum) elif line.startswith("Subject: ") and newLineCounter == 1: # Matches everything after "Subject: " try: @@ -657,8 +667,6 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: # Formatts the date to UTC replyFromInfo["subject"] = subjectStr - linesToRemove.append(lineNum) - elif line.startswith("Date: ") and newLineCounter == 1: # Matches everything after "Date: " try: @@ -670,8 +678,6 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: # Formatts the date to UTC replyFromInfo["datetime"] = self.__getFormattedDate(dateStr) - linesToRemove.append(lineNum) - elif line.startswith("Cc: ") and newLineCounter == 1: replyFromInfo["cc"] = [] @@ -685,9 +691,7 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: replyFromInfo["cc"].append( {"name": cc[0], "email": cc[1]} - ) - - linesToRemove.append(lineNum) + ) # Deletes reduntant lines from the message content in reverse order for lineNum in sorted(linesToRemove, reverse = True): @@ -696,6 +700,8 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: # Strips any unnecessary newlines or any delimiters frm the message content replyFromInfo["content"] = self.__getFormattedMessageContent(replyContent) + replyFromInfo["headers"] = replyFromHeaders + return replyFromInfo def __getFormattedMessageContent(self, messageContent: list) -> list: From b7834978b805848a5eb77b991533a8e6a0795c61 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Mon, 19 Oct 2020 09:35:16 -0400 Subject: [PATCH 06/18] Items stored as numbers and exception handeling for Value Errors --- api/ECNQueue.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index b3abb35..af982fe 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -37,7 +37,11 @@ class Item: def __init__(self, queue: str, number: int) -> None: self.queue = queue - self.number = number + try: + self.number = int(number) + except ValueError: + raise ValueError("Could not convert \"" + number + "\" to an integer") + #self.number = number self.__path = "/".join([queueDirectory, self.queue, str(self.number)]) self.lastUpdated = self.__getLastUpdated() From acd0733df5f1a899376a7ef360821e8b24dc0e5d Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Tue, 20 Oct 2020 14:06:38 -0400 Subject: [PATCH 07/18] Added assignment parsing helper function --- api/ECNQueue.py | 124 +++++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index af982fe..eb6e78d 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -185,22 +185,20 @@ def __parseSections(self) -> list: contentStart = self.__getHeaderBoundary() + 1 contentEnd = len(self.__rawItem) - 1 - #directoryInfo = {"type": "directoryInformation"} - initialMessageSection = True - - # Delimiter info - delimiters = [ - {"name": "edit", "pattern": "*** Edited"}, - {"name": "status", "pattern": "*** Status"}, - {"name": "replyToUser", "pattern": "*** Replied"}, - {"name": "replyFromUser", "pattern": "=== "}, - ] + # List of assignments for the item + assignementLsit = self.__assignmentParsing(contentStart) + # Appends each assignment individually to sections + for assignment in assignementLsit: + sections.append(assignment) + # Checks for Directory Identifiers if self.__rawItem[contentStart] == "\n" and self.__rawItem[contentStart + 1].startswith("\t"): + directoryStartLine = contentStart + 1 + # Parses the directory information and returns a dictionary of directory values - directoryInfo = self.__directoryParsing(contentStart + 1) + directoryInfo = self.__directoryParsing(directoryStartLine) # Appends Directory Information into the sections array sections.append(directoryInfo) @@ -208,67 +206,45 @@ def __parseSections(self) -> list: # Sets the initial message start to the next line after all directory lines and newlines contentStart = contentStart + len(directoryInfo) + 1 + # The start line, type, and end line for item events sectionBoundaries = [] + # Delimiter info + delimiters = [ + {"name": "edit", "pattern": "*** Edited"}, + {"name": "status", "pattern": "*** Status"}, + {"name": "replyToUser", "pattern": "*** Replied"}, + {"name": "replyFromUser", "pattern": "=== "}, + ] + + # Signifies that there is an initial message to parse + initialMessageSection = True + # Parses the entire contents of the message, stores everything before any delimiter as the initial message - # and the line number of any delimiters + # and the line number of any delimiters as well as the type for lineNumber in range(contentStart, contentEnd + 1): line = self.__rawItem[lineNumber] + # Looks for a starting delimiter and explicity excludes the reply-from-user ending delimiter if line.startswith("***") or line.startswith("===") and not line.startswith("===="): + + # Sets the delimiter type based on the pattern within the delimiters list for delimiter in delimiters: if line.startswith(delimiter["pattern"]): - sectionBoundaries.append({"start": lineNumber, "type": delimiter["name"]}) break - # Signifies that the inital message has been completely parsed - initialMessageSection = False + # If a starting delimiter was encountered, then there is no initial message + if initialMessageSection: + initialMessageSection = False elif initialMessageSection == True: - - # Delimiter not encountered yet, so append line to initial message list + # Delimiter not encountered yet, so append initial message starting line as the current lin number sectionBoundaries.append({"start": lineNumber, "type": "initial_message"}) - initialMessageSection = False - # Assignment Information - assignedBy = "" - assignedDateTime = "" - assignedTo = "" - - # Parses the header looking for assignment delimeters and stores info into their respective variables - for headerContent in range(0, contentStart): - - line = self.__rawItem[headerContent] - - # Gets who the Item was assigned to - if line.startswith("Assigned-To: "): - - assignedTo = (re.search("(?<=Assigned-To: )(.*)", line)).group() - - # Gets the date the Item was assigned - elif line.startswith("Assigned-To-Updated-Time: "): - - dateFromLine = (re.search("(?<=Assigned-To-Updated-Time: )(.*)", line)).group() - - assignedDateTime = self.__getFormattedDate(dateFromLine) - - # Gets who assigned the Item - elif line.startswith("Assigned-To-Updated-By: "): - - assignedBy = (re.search("(?<=Assigned-To-Updated-By: )(.*)", line)).group() - - # Appends the assignment to the sections list - sections.append( - {"type": "assignment", - "datetime": assignedDateTime, - "by": assignedBy, - "to": assignedTo} - ) - sectionBoundaries.append({"start": contentEnd + 1}) # Sets the end of the section boundary to the begining of the next section boundary @@ -415,7 +391,47 @@ def __directoryParsing(self, directoryStartLine: int) -> dict: # Returns the directory information dictionary return directoryInformation - + + def __assignmentParsing(self, contentStart) -> list: + assignmentList =[] + + # Assignment Information + assignedBy = "" + assignedDateTime = "" + assignedTo = "" + + # Parses the header looking for assignment delimeters and stores info into their respective variables + for headerContent in range(0, contentStart): + + line = self.__rawItem[headerContent] + + # Gets who the Item was assigned to + if line.startswith("Assigned-To: "): + + assignedTo = (re.search("(?<=Assigned-To: )(.*)", line)).group() + + # Gets the date the Item was assigned + elif line.startswith("Assigned-To-Updated-Time: "): + + dateFromLine = (re.search("(?<=Assigned-To-Updated-Time: )(.*)", line)).group() + + assignedDateTime = self.__getFormattedDate(dateFromLine) + + # Gets who assigned the Item + elif line.startswith("Assigned-To-Updated-By: "): + + assignedBy = (re.search("(?<=Assigned-To-Updated-By: )(.*)", line)).group() + + # Appends the assignment to the sections list + assignmentList.append( + {"type": "assignment", + "datetime": assignedDateTime, + "by": assignedBy, + "to": assignedTo} + ) + + return assignmentList + def __initialMessageParsing(self, content: list) -> dict: """Returns a dictionary with initial message information From a5a7f9f22ec6566952fb75d36bdf5c6b84909cef Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Tue, 20 Oct 2020 14:42:57 -0400 Subject: [PATCH 08/18] Updated in line commenting and code readability --- api/ECNQueue.py | 98 ++++++++++++++++++++----------------------------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index eb6e78d..bb82f73 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -245,6 +245,7 @@ def __parseSections(self) -> list: sectionBoundaries.append({"start": lineNumber, "type": "initial_message"}) initialMessageSection = False + # Used to set the end line of the last delimiter sectionBoundaries.append({"start": contentEnd + 1}) # Sets the end of the section boundary to the begining of the next section boundary @@ -252,7 +253,7 @@ def __parseSections(self) -> list: sectionBoundaries[boundaryIndex]["end"] = sectionBoundaries[boundaryIndex + 1]["start"] - # Remove End of File boundary + # Remove End of File boundary since the line number has been assigned to the last delimiter del sectionBoundaries[-1] # Parses through all the boundaries in section boundaries @@ -264,69 +265,57 @@ def __parseSections(self) -> list: # Returns all of the lines within the current section sectionContent = self.__rawItem[boundary["start"] : boundary["end"]] + # Appends an initial message dictionary to sections if boundary["type"] == "initial_message": initialMessageDictionary = self.__initialMessageParsing(sectionContent) sections.append(initialMessageDictionary) - - # Checks for each section type - elif boundary["type"] == "edit": - + + elif boundary["type"] == "edit": # Returns a dictionary with edit information editInfo = self.__editParsing(sectionContent, boundary["start"]) - # Checks for a parse error and appends it to sections and exits the function + # Checks for a parse error and appends it, returning the sections list which stops the parsing if editInfo["type"] == "parse_error": - sections.append(editInfo) - return sections # Appends the edit dictionary to sections sections.append(editInfo) elif boundary["type"] == "replyToUser": - # Returns a dictionary with reply-to information replyToInfo = self.__replyToParsing(sectionContent, boundary["start"]) - + + # Checks for a parse error and appends it, returning the sections list which stops the parsing if replyToInfo["type"] == "parse_error": - sections.append(replyToInfo) - return sections # Appends the reply-to to sections sections.append(replyToInfo) elif boundary["type"] == "status": - # Returns a dictionary with status information statusInfo = self.__statusParsing(sectionContent, boundary["start"]) if statusInfo["type"] == "parse_error": - sections.append(statusInfo) - return sections # Appends the status to sections sections.append(statusInfo) elif boundary["type"] == "replyFromUser": - # Returns a dictionary with userReply information replyFromInfo = self.__userReplyParsing(sectionContent, boundary["start"]) if replyFromInfo["type"] == "parse_error": - sections.append(replyFromInfo) - return sections # Appends the replyFrom to sections sections.append(replyFromInfo) - return sections def __directoryParsing(self, directoryStartLine: int) -> dict: @@ -393,6 +382,10 @@ def __directoryParsing(self, directoryStartLine: int) -> dict: return directoryInformation def __assignmentParsing(self, contentStart) -> list: + """Returns a list with assignment information dictionaries + + Returns: + """ assignmentList =[] # Assignment Information @@ -436,7 +429,8 @@ def __initialMessageParsing(self, content: list) -> dict: """Returns a dictionary with initial message information Returns: - dictionary: "type": "initial_message", + dictionary: + "type": "initial_message", "datetime": utcdate, "from_name": from_name, "from_email": user_email, @@ -500,13 +494,20 @@ def __editParsing(self, content: list, lineNum: int) -> dict: Returns: dictionary: "type": "edit", by, datetime and content """ + # Edit Info dictionary + editInfo = {} - formattedDateTime = "" - editedBy = "" + for count, line in enumerate(content): + if line.startswith("===="): + errorMessage = "Reply-from-user ending delimter encountered without Reply-from-user starting delimter" + return self.__errorParsing(line, lineNum + count + 1, errorMessage) + + editInfo["type"] = "edit" + delimiterLine = content[0] # Parses for the author of the edit, which is located between the "*** Edited by: " and " at:" substrings try: - editedBy = (re.search("(?<=\*{3} Edited by: )(.*)(?= at:)", delimiterLine)).group() + editInfo["by"] = (re.search("(?<=\*{3} Edited by: )(.*)(?= at:)", delimiterLine)).group() except: errorMessage = "*** Edited by: [username] at: [date and time] ***\n" return self.__errorParsing(delimiterLine, lineNum, errorMessage) @@ -520,17 +521,10 @@ def __editParsing(self, content: list, lineNum: int) -> dict: return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Attempts to format the date and time into utc format - formattedDateTime = self.__getFormattedDate(dateTimeString) + editInfo["datetime"] = self.__getFormattedDate(dateTimeString) # Remove the delimiter String and unecessary newlines - formattedContent = self.__getFormattedMessageContent(content) - - editInfo = { - "type": "edit", - "datetime": formattedDateTime, - "by": editedBy, - "content": formattedContent - } + editInfo["content"] = self.__getFormattedMessageContent(content) return editInfo @@ -541,19 +535,20 @@ def __replyToParsing(self, content: list, lineNum: int) -> dict: dictionary: "type": "replyToUser", by, datetime and content """ - formattedDateTime = "" - repliedBy = "" + replyInfo = {} + + replyInfo["type"] = "reply_to_user" + delimiterLine = content[0] for count, line in enumerate(content): if line.startswith("===="): errorMessage = "Reply-from-user ending delimter encountered without Reply-from-user starting delimter" return self.__errorParsing(line, lineNum + count + 1, errorMessage) - #tech112 try: # Parses for the author of the reply, which is located between the "*** Replied by: " and " at:" substrings - repliedBy = (re.search("(?<=\*{3} Replied by: )(.*)(?= at:)", delimiterLine)).group() + replyInfo["by"] = (re.search("(?<=\*{3} Replied by: )(.*)(?= at:)", delimiterLine)).group() except: errorMessage = "*** Replied by: [username] at: [date and time] ***\n" return self.__errorParsing(delimiterLine, lineNum, errorMessage) @@ -566,16 +561,9 @@ def __replyToParsing(self, content: list, lineNum: int) -> dict: return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Formats date to UTC - formattedDateTime = self.__getFormattedDate(dateTimeString) + replyInfo["datetime"] = self.__getFormattedDate(dateTimeString) - formattedContent = self.__getFormattedMessageContent(content) - - replyInfo = { - "type": "reply_to_user", - "datetime": formattedDateTime, - "by": repliedBy, - "content": formattedContent - } + replyInfo["content"] = self.__getFormattedMessageContent(content) return replyInfo @@ -586,8 +574,10 @@ def __statusParsing(self, content: list, lineNum: int) -> dict: dictionary: "type": "status", by, datetime and content """ - formattedDateTime = "" - updatedBy = "" + statusInfo = {} + + statusInfo["type"] = "status" + delimiterLine = content[0] for count, line in enumerate(content): @@ -597,7 +587,7 @@ def __statusParsing(self, content: list, lineNum: int) -> dict: # Parses for the author of the status change, which is located between the "*** Status updated by: " and " at:" substrings try: - updatedBy = (re.search("(?<=\*{3} Status updated by: )(.*)(?= at:)", delimiterLine)).group() + statusInfo["by"] = (re.search("(?<=\*{3} Status updated by: )(.*)(?= at:)", delimiterLine)).group() except: errorMessage = "*** Status updated by: [username] at: [date and time] ***\n" @@ -612,18 +602,10 @@ def __statusParsing(self, content: list, lineNum: int) -> dict: return self.__errorParsing(delimiterLine, lineNum, errorMessage) # Formats the date to UTC - formattedDateTime = self.__getFormattedDate(dateTimeString) + statusInfo["datetime"] = self.__getFormattedDate(dateTimeString) # Remove the delimiter String and unecessary newlines - formattedContent = self.__getFormattedMessageContent(content) - - statusInfo = { - "type": "status", - "datetime": formattedDateTime, - "by": updatedBy, - "content": formattedContent - } - + statusInfo["content"] = self.__getFormattedMessageContent(content) return statusInfo From 6dc675af51bb5b5b6d21d6a4d04e8e9422af50a9 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 13:24:01 -0400 Subject: [PATCH 09/18] Implementation of workaround for extreneous new lines in directory information and reply-from-user headers --- api/ECNQueue.py | 80 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index bb82f73..a716fa8 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -338,11 +338,22 @@ def __directoryParsing(self, directoryStartLine: int) -> dict: """ directoryInformation = {"type": "directory_information"} - # Assumes a full directory with 12 items including the starting line - directoryEndingLine = directoryStartLine + 11 - + directoryPossibleKeys=[ + "Name", + "Login", + "Computer", + "Location", + "Email", + "Phone", + "Office", + "UNIX Dir", + "Zero Dir", + "User ECNDB", + "Host ECNDB", + "Subject" + ] # Executies until the directory start line is greater than the directory ending line - while directoryStartLine <= directoryEndingLine: + while True: # Returns the line number at directory start line info = self.__rawItem[directoryStartLine] @@ -364,17 +375,47 @@ def __directoryParsing(self, directoryStartLine: int) -> dict: # swt1 key, value = strippedInfo.split(": ", 1) - # Adds the key value pair to the directory info dictionary - directoryInformation[key] = value - + if key in directoryPossibleKeys: + # Adds the key value pair to the directory info dictionary + directoryInformation[key] = value + else: + # Casts the list type on to a dictionary + dictionaryList = list(directoryInformation) + # Length of dictionary list + lenDictionaryList = len(dictionaryList) + # The last key appended to the directory dictionary + lastKeyAppended = dictionaryList[lenDictionaryList - 1] + + directoryInformation[lastKeyAppended] = directoryInformation[lastKeyAppended] + " " + strippedInfo + elif ":" in strippedInfo: # Seperates the directory info line into two variables, the first variable being the key, the second being the value key, value = strippedInfo.split(":", 1) - # Adds the key value pair to the directory info dictionary - directoryInformation[key] = value - + if key in directoryPossibleKeys: + # Adds the key value pair to the directory info dictionary + directoryInformation[key] = value + else: + # Casts the list type on to a dictionary + dictionaryList = list(directoryInformation) + # Length of dictionary list + lenDictionaryList = len(dictionaryList) + # The last key appended to the directory dictionary + lastKeyAppended = dictionaryList[lenDictionaryList - 1] + + directoryInformation[lastKeyAppended] = directoryInformation[lastKeyAppended] + " " + strippedInfo + + # Signifies that this line belongs to the most previous line + elif ": " not in strippedInfo and ":" not in strippedInfo: + # Casts the list type on to a dictionary + dictionaryList = list(directoryInformation) + # Length of dictionary list + lenDictionaryList = len(dictionaryList) + # The last key appended to the directory dictionary + lastKeyAppended = dictionaryList[lenDictionaryList - 1] + + directoryInformation[lastKeyAppended] = directoryInformation[lastKeyAppended] + " " + strippedInfo # Counter to denote the end of the directory directoryStartLine = directoryStartLine + 1 @@ -635,14 +676,19 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: return self.__errorParsing(line, lineNumber + lineNum + 1, errorMessage) if newLineCounter == 1 and line != "\n": + + try: + # Append header information for each headr line + headerType, content = line.split(": ", 1) + replyFromHeaders.append( + {"type": headerType, + "content": content + } + ) + except: + lenReplyFromHeaders = len(replyFromHeaders) - # Append header information for each headr line - headerType, content = line.split(": ", 1) - replyFromHeaders.append( - {"type": headerType, - "content": content - } - ) + replyFromHeaders[lenReplyFromHeaders - 1]["content"] = replyFromHeaders[lenReplyFromHeaders - 1]["content"] + " " + line linesToRemove.append(lineNum) #Checks for a newline and breaks for loop on second occurance of a newline From 8e720f5a348869af8ac3afe612c3c66273e86891 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 22:23:18 -0400 Subject: [PATCH 10/18] Updated documentation for error parsing helper function --- api/ECNQueue.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index a716fa8..8230d9f 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -36,6 +36,7 @@ class Item: # TODO: Add Item class documentation def __init__(self, queue: str, number: int) -> None: + self.queue = queue try: self.number = int(number) @@ -781,19 +782,25 @@ def __getFormattedMessageContent(self, messageContent: list) -> list: return messageContent def __errorParsing(self, line: str, lineNum: int, expectedSyntax: str) -> dict: - """Returns a dictionary with error parse information + """Returns a dictionary with error parse information when a line is malformed + + Example: + "*** Status updated by: ewhile at: 5/7/2020 10:59:11 *** sharing between\n" + + Args: + line (str): line of that threw error + lineNum (int): line number in the item that threw error + expectedSyntax (str): a message stating the syntax the line should follow Returns: - { - dictionary: "type": "parse_error", - datetime: time_of_execution, - content: [ - 'expected value item_row_num:item_column_num', - 'current line in item' - ] - } + dict: a dictionary with these keys, + "type": "parse_error", + "datetime": time the error was encountered, + "file_path": path of the item with erroneos line, + "expected": expectedSyntax, + "got": line, + "line_num": lineNum """ - errorDictionary = {} # Type From f7560e89da2006d8458d238e9fec491e17f3c450 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 22:51:36 -0400 Subject: [PATCH 11/18] Renamed getFormattedMessageContent helper function to getFormattedSectionContent and updated documentation for getFormattedSectionContent --- api/ECNQueue.py | 51 +++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 8230d9f..ae6c424 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -526,7 +526,7 @@ def __initialMessageParsing(self, content: list) -> dict: initialMessageDictionary["subject"] = self.__getMostRecentHeaderByType("Subject") # Removes unecessary newlines from the begining and the end of the initial message - initialMessageDictionary["content"] = self.__getFormattedMessageContent(content) + initialMessageDictionary["content"] = self.__getFormattedSectionContent(content) return initialMessageDictionary @@ -566,7 +566,7 @@ def __editParsing(self, content: list, lineNum: int) -> dict: editInfo["datetime"] = self.__getFormattedDate(dateTimeString) # Remove the delimiter String and unecessary newlines - editInfo["content"] = self.__getFormattedMessageContent(content) + editInfo["content"] = self.__getFormattedSectionContent(content) return editInfo @@ -605,7 +605,7 @@ def __replyToParsing(self, content: list, lineNum: int) -> dict: # Formats date to UTC replyInfo["datetime"] = self.__getFormattedDate(dateTimeString) - replyInfo["content"] = self.__getFormattedMessageContent(content) + replyInfo["content"] = self.__getFormattedSectionContent(content) return replyInfo @@ -647,7 +647,7 @@ def __statusParsing(self, content: list, lineNum: int) -> dict: statusInfo["datetime"] = self.__getFormattedDate(dateTimeString) # Remove the delimiter String and unecessary newlines - statusInfo["content"] = self.__getFormattedMessageContent(content) + statusInfo["content"] = self.__getFormattedSectionContent(content) return statusInfo @@ -747,39 +747,52 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: replyContent.pop(lineNum) # Strips any unnecessary newlines or any delimiters frm the message content - replyFromInfo["content"] = self.__getFormattedMessageContent(replyContent) + replyFromInfo["content"] = self.__getFormattedSectionContent(replyContent) replyFromInfo["headers"] = replyFromHeaders return replyFromInfo - def __getFormattedMessageContent(self, messageContent: list) -> list: + def __getFormattedSectionContent(self, sectionContent: list) -> list: """Returns a list with message content that is stripped of unnecessary newlines and begining delimiters + Example: + *** Edited by: mph at: 02/21/20 10:27:16 ***\n + \n + Still need to rename machines - but the networking issue now seems to \n + be resolved via another ticket.\n + \n + \n + \n + \n + \n + + Args: + sectionContent (list): The section content of a parsed section + Returns: - list: formattedMessageContent + list: the section content of a parsed section without any delimiters and unnecessary newlines """ - - # Continually removes the first line of messageContent if it is a newline or delimiter in each iteration - while len(messageContent) > 1: - if messageContent[0] == "\n" or messageContent[0].startswith("***") or messageContent[0].startswith("===") : - messageContent.pop(0) + # Continually removes the first line of sectionContent if it is a newline or delimiter in each iteration + while len(sectionContent) > 1: + if sectionContent[0] == "\n" or sectionContent[0].startswith("***") or sectionContent[0].startswith("===") : + sectionContent.pop(0) else: # Breaks the loop if the first line isn't a newline or delimiter break - # Continually removes the last line of messageContent if it is a newline or delimiter in each iteration - while len(messageContent) > 1: - # Initializes the Length of messageContent each iteration of the loop - messagecontentLength = len(messageContent) + # Continually removes the last line of sectionContent if it is a newline or delimiter in each iteration + while len(sectionContent) > 1: + # Initializes the Length of sectionContent each iteration of the loop + sectionContentLength = len(sectionContent) - if messageContent[messagecontentLength -1] == "\n" or messageContent[messagecontentLength -1].startswith("===="): - messageContent.pop(messagecontentLength - 1) + if sectionContent[sectionContentLength -1] == "\n" or sectionContent[sectionContentLength -1].startswith("===="): + sectionContent.pop(sectionContentLength - 1) else: # Breaks the loop if the last line isn't a newline or delimiter break - return messageContent + return sectionContent def __errorParsing(self, line: str, lineNum: int, expectedSyntax: str) -> dict: """Returns a dictionary with error parse information when a line is malformed From 2af7da57b17198de40066844f44b4348f01d91e2 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:13:22 -0400 Subject: [PATCH 12/18] Updated documentation for userReplyParsing --- api/ECNQueue.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index ae6c424..d9c1b45 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -652,10 +652,43 @@ def __statusParsing(self, content: list, lineNum: int) -> dict: return statusInfo def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: - """Returns a dictionary with user Reply information information + """Returns a dictionary with user reply information + + Example: + === Additional information supplied by user ===\n + \n + Subject: Re: Beepboop\n + From: Justin Campbell \n + Date: Tue, 23 Jun 2020 13:30:45 -0400\n + X-ECN-Queue-Original-Path: /home/pier/e/queue/Attachments/inbox/2020-06-23/212-original.txt\n + X-ECN-Queue-Original-URL: https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/212-original.txt\n + \n + Huzzah!\n + \n + ===============================================\n + \n + Args: + replyContent (list): The entire section of a reply-from-user + lineNumber (int): The line number of the begining of a reply-from-user section within and item Returns: - dictionary: "type": "replyFromUser", datetime, subject, userName, userEmail, content, ccRecipients + dict: a dictionary with these keys, + "type": "reply_from_user", + "from_name": name of the user that sent the reply, + "from_email": email of the user that sent the reply, + "subject": subject of the reply, + "datetime": the datetime of the reply, + "cc": [ + {"name": name of the carbon copied recipient, + "email": email of the carbon copied recipient + }, + ] + "content": content of the reply + "headers": [ + {"type": headerType, + "content": content + }, + ] """ replyFromInfo = {} From 90b0da898711ab5ecf08574b6a87fd139cf62387 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:18:29 -0400 Subject: [PATCH 13/18] Updated documentation for statusParsing --- api/ECNQueue.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index d9c1b45..846bfaa 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -612,10 +612,21 @@ def __replyToParsing(self, content: list, lineNum: int) -> dict: def __statusParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with status information + Example: + *** Status updated by: campb303 at: 6/23/2020 13:26:55 ***\n + Dont Delete\n + + Args: + content (list): The content of a status update + lineNum (int): The line number of a status update in an item + Returns: - dictionary: "type": "status", by, datetime and content + dict: a dictionary with these keys, + "type": "status", + "by": initiator of the status update, + "datetime": datetime of the status update, + "content": content of the status update """ - statusInfo = {} statusInfo["type"] = "status" From ce7c4d5d93a320e6900190cfcae6b0bf237e6be8 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:24:39 -0400 Subject: [PATCH 14/18] Updated documentation for replyToParsing --- api/ECNQueue.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 846bfaa..95e62d8 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -573,10 +573,26 @@ def __editParsing(self, content: list, lineNum: int) -> dict: def __replyToParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with reply to user information + Example: + *** Replied by: campb303 at: 06/23/20 13:28:18 ***\n + \n + This be a reply my son\n + \n + Justin\n + ECN\n + \n + + Args: + content (list): content of a reply to user + lineNum (int): line number of a reply to user in an item + Returns: - dictionary: "type": "replyToUser", by, datetime and content + dict: a dictionary with these keys, + "type": "status", + "by": initiator of the reply to user, + "datetime": datetime of the reply to user, + "content": content of the reply to user """ - replyInfo = {} replyInfo["type"] = "reply_to_user" From 96b2e05716b94f520d4a54216d0dd38da5559b3d Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:27:53 -0400 Subject: [PATCH 15/18] Updated documentation for editParsing --- api/ECNQueue.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 95e62d8..092a102 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -533,9 +533,26 @@ def __initialMessageParsing(self, content: list) -> dict: def __editParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with edit information + Example: + *** Edited by: campb303 at: 06/23/20 13:27:56 ***\n + \n + This be an edit my boy\n + \n + \n + \n + + Args: + content (list): content of an edit + lineNum (int): line number of an edit within an item + Returns: - dictionary: "type": "edit", by, datetime and content + dict: a dictionary with these keys, + "type": "edi", + "by": initiator of the edit, + "datetime": datetime of the edit, + "content": content of the edit """ + # Edit Info dictionary editInfo = {} @@ -588,7 +605,7 @@ def __replyToParsing(self, content: list, lineNum: int) -> dict: Returns: dict: a dictionary with these keys, - "type": "status", + "type": "reply_to_user", "by": initiator of the reply to user, "datetime": datetime of the reply to user, "content": content of the reply to user From b9e7800b3b3c11fa06c5d85c60bbc2aea777d1b3 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:31:28 -0400 Subject: [PATCH 16/18] Updated documentation for initialMessageParsing --- api/ECNQueue.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 092a102..49454ba 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -470,16 +470,24 @@ def __assignmentParsing(self, contentStart) -> list: def __initialMessageParsing(self, content: list) -> dict: """Returns a dictionary with initial message information + Example: + \n + Testtest\n + \n + + Args: + content (list): content of the initial message + Returns: - dictionary: + dict: "type": "initial_message", - "datetime": utcdate, + "datetime": datetime the initial message was sent, "from_name": from_name, "from_email": user_email, "to": [{email, name}], "cc": [{email, name}], - "subject": initial_message_subject - "content": ["message_content"] + "subject": initial message subject + "content": content of the initial message """ initialMessageDictionary = {} From a5979b477b8b76005576508da1ae50a776a5bb69 Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:35:30 -0400 Subject: [PATCH 17/18] updated assignmentParsing documentation --- api/ECNQueue.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 49454ba..8418770 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -423,11 +423,30 @@ def __directoryParsing(self, directoryStartLine: int) -> dict: # Returns the directory information dictionary return directoryInformation - def __assignmentParsing(self, contentStart) -> list: + def __assignmentParsing(self, contentStart: int) -> list: """Returns a list with assignment information dictionaries + Example: + Assigned-To: campb303 + Assigned-To-Updated-Time: Tue, 23 Jun 2020 13:27:00 EDT + Assigned-To-Updated-By: campb303 + + Args: + contentStart (int): line number where the content starts + Returns: + list: [ + {"type": "assignment", + "datetime": datetime of the assignment, + "by": user who initiated the assignment, + "to": user who was assigned + }, + ] """ + #Returns a list with assignment information dictionaries + + #Returns: + assignmentList =[] # Assignment Information From a174623d2b955a4bb29ac7a91e5ecc17efb8d33e Mon Sep 17 00:00:00 2001 From: Jacob Daniel Bennett Date: Wed, 21 Oct 2020 23:44:21 -0400 Subject: [PATCH 18/18] Updated documentation for directoryParsing --- api/ECNQueue.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 8418770..a80dce7 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -322,20 +322,25 @@ def __parseSections(self) -> list: def __directoryParsing(self, directoryStartLine: int) -> dict: """Returns a dictionary with directory information - Returns: dictionary: - "type": "directoryInformation" - "Name": name, - "Login": login, - "Computer": computer, - "Location": location, - "Email": email, - "Phone": phone, - "Office": office, - "UNIX Dir": unix_dir, - "Zero Dir": zero_dir, - "User ECNDB": user_ecndb, - "Host ECNDB": host_ecdbn, - "Subject": subject + Example: + Name: Nestor Fabian Rodriguez Buitrago + Login: rodri563 + Computer: ce-205-38 (128.46.205.67) + Location: HAMP G230 + Email: rodri563@purdue.edu + Phone: 7654766893 + Office: HAMP G230 + UNIX Dir: /home/bridge/b/rodri563 + Zero Dir: U=\\bridge.ecn.purdue.edu\rodri563 + User ECNDB: http://eng.purdue.edu/jump/2e8399a + Host ECNDB: http://eng.purdue.edu/jump/2e83999 + Subject: Autocad installation + + Args: + directoryStartLine (int): line number within the item that the directory starts on + + Returns: + dict: dictionary that splits each line within the directory into a key and a value """ directoryInformation = {"type": "directory_information"} @@ -443,10 +448,6 @@ def __assignmentParsing(self, contentStart: int) -> list: }, ] """ - #Returns a list with assignment information dictionaries - - #Returns: - assignmentList =[] # Assignment Information