diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 6c29c1f..458536d 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -175,6 +175,7 @@ def __getContent(self) -> list: def __parseSections(self) -> list: + # List of all item events sections = [] contentStart = self.__getHeaderBoundary() + 1 @@ -182,18 +183,27 @@ def __parseSections(self) -> list: # Find line numbers where sections start sectionBoundaries = [ {"start": contentStart} ] - - # Parses the entire contents of the message, stores everything before any delimiter as the initial message + + directoryInfo = [] initialMessageContent = [] endInitialMessage = False + + # Parses the entire contents of the message, stores everything before any delimiter as the initial message for lineNumber in range(contentStart, contentEnd + 1): + line = self.__rawItem[lineNumber] + if line.startswith("***") or line.startswith("===") and not line.startswith("===="): + + # Signifies that the inital message has been copletely parsed endInitialMessage = True - #Soters what line every delimeter starts/ends + + # Stores what line every delimeter starts/ends sectionBoundaries.append({"start": lineNumber}) - if endInitialMessage == False: + elif endInitialMessage == False: + + # Delimiter not encountered yet, so append line to initial message list initialMessageContent.append(line) # All possible Directory Items @@ -210,15 +220,16 @@ def __parseSections(self) -> list: " User ECNDB: ", " Host ECNDB: ", " Subject: " - ] - directoryInfo = [] - - # Stores line numbers for directory info that can be removed from initial content + ] - # Directory Info is stored + # Parses the initial message for directory information for lineContents in initialMessageContent: + for itemsindirectory in directoryInfoPattern: + + # Checks if the line starts with any of the directory parameters if lineContents.startswith(itemsindirectory): + directoryInfo.append(lineContents) # Appends Directory Information into the sections array @@ -229,18 +240,25 @@ def __parseSections(self) -> list: # Gets the initial message date from the header initialMessageDateStr = self.__getMostRecentHeaderByType("Date") + + # Formats the initial message date to UTC initialMessageFormattedDate = self.__getFormattedDate(initialMessageDateStr) - # Parses the header looking for CC recipients of the initial message + # Stores list of dictionaries for CC information initialMessageCCSection =[] + + # Parses the header looking for CC recipients of the initial message and stores it in a list of tuples initialMessageCCList = email.utils.getaddresses([self.__getMostRecentHeaderByType("CC")]) + # Parses the CC list and stores the cc recipient information in a list of dictionaries for ccRecipients in initialMessageCCList: + initialMessageCCSection.append( {"name": ccRecipients[0], "email": ccRecipients[1]} ) - # Appends the initial message information to the sections array + + # Appends all initial message information to the sections array sections.append( {"type": "initialMessage", "datetime": initialMessageFormattedDate, @@ -258,19 +276,24 @@ def __parseSections(self) -> list: # 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() # Aissignment_Updated_By signifies the end of the assignment event @@ -291,12 +314,13 @@ def __parseSections(self) -> list: # Set line number where section end for boundaryIndex in range(0, len(sectionBoundaries) - 1): + sectionBoundaries[boundaryIndex]["end"] = sectionBoundaries[boundaryIndex + 1]["start"] # Remove End of File boundary del sectionBoundaries[-1] - # Different delimiters for different sections + # Different delimiters for different message events delimiters = [ {"name": "edit", "pattern": "*** Edited"}, {"name": "status", "pattern": "*** Status"}, @@ -304,146 +328,238 @@ def __parseSections(self) -> list: {"name": "replyFromUser", "pattern": "=== "}, ] - + # Parses through all the boundaries in section boundaries for boundary in sectionBoundaries: + + # Sets line to the first line of the boundary (which is always the delimiter) line = self.__rawItem[boundary["start"]] + sectionType = None + # Looks at the begining of line and determines if it starts with any of the delimiters, + # if so, name it accordingly for delimiter in delimiters: + if line.startswith(delimiter["pattern"]): + sectionType = delimiter["name"] + break + # Returns all of the lines within the current section sectionContent = self.__rawItem[boundary["start"] : boundary["end"]] + # Checks for each section type if sectionType == "edit": - formattedDateTime = "" - editedBy = "" - - # 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() + + # Returns a dictionary with edit information + editInfo = self.__editParsing(line) + + # Appends content of the edit message to the dictionary + editInfo["content"] = sectionContent - # 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() + # Appends the edit dictionary to sections + sections.append(editInfo) - # Attempts to format the date and time into utc format - formattedDateTime = self.__getFormattedDate(dateTimeString) + continue - sections.append( - {"type": sectionType, - "by": editedBy, - "datetime": formattedDateTime, - "content": sectionContent,} - ) - elif sectionType == "replyToUser": - formattedDateTime = "" - repliedBy = "" + + # Returns a dictionary with reply-to information + replyToInfo = self.__replyToParsing(line) - #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() + # Appends content of the reply-to message to the dicionary + replyToInfo['content'] = sectionContent - #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() + # Appends the reply-to to sections + sections.append(replyToInfo) - formattedDateTime = self.__getFormattedDate(dateTimeString) - - sections.append( - {"type": sectionType, - "by": repliedBy, - "datetime": formattedDateTime, - "content": sectionContent} - ) + continue elif sectionType == "status": - formattedDateTime = "" - updatedBy = "" + + # Returns a dictionary with status information + statusInfo = self.__statusParsing(line) - #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() + # 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) - #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() + continue - formattedDateTime = self.__getFormattedDate(dateTimeString) - - sections.append( - {"type": sectionType, - "by": updatedBy, - "datetime": formattedDateTime, - "content": sectionContent} - ) - #sectionContent.remove(line) - - #elif sectionType == "": elif sectionType == "replyFromUser": + + # Returns a dictionary with userReply information + replyFromInfo = self.__userReplyParsing(sectionContent) + + # Appends content to empty content key to avoid passing large amounts of info that isnt used within the function + replyFromInfo['content'] = sectionContent - formattedDateTime = "" - repliedByName = "" - repliedByEmail = "" - subject = "" - ccRecipientsList = [] + # Appends the replyFrom to sections + sections.append(replyFromInfo) + - """ - ccRecipientDict Format: - ccRecipientDict = { - "Name": Name - "Email": Email - } - """ + return sections - newLineCounter = 0 + def __editParsing(self, line: str) -> dict: + """Returns a dictionary with edit information - #Parses the section content looking for any line that starts with a metadata - for items in sectionContent: + Returns: + dictionary: "type": "edit", by, datetime and content + """ + + formattedDateTime = "" + editedBy = "" + + # 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() + + # 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() + + # Attempts to format the date and time into utc format + formattedDateTime = self.__getFormattedDate(dateTimeString) + + editInfo = { + "type": "edit", + "by": editedBy, + "datetime": formattedDateTime, + "content": "" + } + + return editInfo + + def __replyToParsing(self, line: str) -> dict: + """Returns a dictionary with reply to user information + + Returns: + dictionary: "type": "replyToUser", by, datetime and content + """ + + formattedDateTime = "" + repliedBy = "" + + # 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() + + # 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() + + # Formats date to UTC + formattedDateTime = self.__getFormattedDate(dateTimeString) + + replyInfo = { + "type": "replyToUser", + "by": repliedBy, + "datetime": formattedDateTime, + "content": "" + } + + return replyInfo + + def __statusParsing(self, line: str) -> dict: + """Returns a dictionary with status information + + Returns: + dictionary: "type": "status", by, datetime and content + """ + + formattedDateTime = "" + 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() + + # 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() + + # Formats the date to UTC + formattedDateTime = self.__getFormattedDate(dateTimeString) + + statusInfo = { + "type": "status", + "by": updatedBy, + "datetime": formattedDateTime, + "content": "" + } + + return statusInfo + + def __userReplyParsing(self, replyContent: list) -> dict: + """Returns a dictionary with user Reply information information + + Returns: + dictionary: "type": "replyFromUser", datetime, subject, userName, userEmail, content, ccRecipients + """ + formattedDateTime = "" + repliedByName = "" + repliedByEmail = "" + subject = "" + ccRecipientsList = [] + newLineCounter = 0 + + #Parses the section content looking for any line that starts with a metadata + for line in replyContent: - #Checks for a newline and breaks for loop on second occurance of a newline - if items == "\n": - newLineCounter = newLineCounter + 1 - if newLineCounter == 2: - break - else: - continue - #Checks for lines starting with these delimiters - if items.startswith("Subject: "): - subject = (re.search("(?<=Subject: )(.*)", items)).group() - - elif items.startswith("From: "): - emailList = email.utils.getaddresses([items]) - #repliedByName = (re.search("(?<=From: )(.*)(?= <)", items)).group() - repliedByName = emailList[0][0] - #repliedByEmail = (re.search("(?<= <)(.*)(?=>)", items)).group() - repliedByEmail = emailList[0][1] - - elif items.startswith("Date: "): - dateStr = (re.search("(?<=Date: )(.*)", items)).group() - - formattedDateTime = self.__getFormattedDate(dateStr) - - elif items.startswith("Cc: "): - recipientsList = email.utils.getaddresses([items]) - for cc in recipientsList: - ccRecipientsList.append( - {"name":cc[0], - "email":cc[1]} - ) - - #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() - - #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() - sections.append( - {"type": sectionType, - "datetime": formattedDateTime, - "subject": subject, - "userName": repliedByName, - "userEmail": repliedByEmail, - "content": sectionContent, - "ccRecipients": ccRecipientsList} - ) + #Checks for a newline and breaks for loop on second occurance of a newline + if line == "\n": + newLineCounter = newLineCounter + 1 - return sections + if newLineCounter == 2: + break + + # Checks for lines starting with Subject, From, Date and CC + if line.startswith("Subject: "): + + # Matches everything after "Subject: " in the line + subject = (re.search("(?<=Subject: )(.*)", line)).group() + + elif line.startswith("From: "): + + # Returns a list of tuples with name and email information + 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] + + elif line.startswith("Date: "): + + # Matches everything after "Date: " + dateStr = (re.search("(?<=Date: )(.*)", line)).group() + + # Formatts the date to UTC + formattedDateTime = self.__getFormattedDate(dateStr) + + 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( + {"name":cc[0], + "email":cc[1]} + ) + + replyFromInfo = { + "type": "replyFromUser", + "datetime": formattedDateTime, + "subject": subject, + "userName": repliedByName, + "userEmail": repliedByEmail, + "content": "", + "ccRecipients": ccRecipientsList + } + + return replyFromInfo def __isLocked(self) -> Union[str, bool]: """Returns a string info about the lock if true and a bool False if false