diff --git a/api/ECNQueue.py b/api/ECNQueue.py index 7c2ae1d..f5e7c47 100644 --- a/api/ECNQueue.py +++ b/api/ECNQueue.py @@ -1,4 +1,32 @@ -# TODO: Add ECNQueue module documentation +"""A library for interacting with Purdue ECN's ticketing system. + +This library allows interacting with queue Items (called Items) and collections +of items (called Queues). + +Example: + # Create a single Item (ce100) + >>> item = Item("ce", 100) + # Get the sender's email address from an Item + >>> item = Item("ce", 100) + >>> item.userEmail + + # Create an entire Queue (ce) + >>> queue = Queue("ce") + # Get the number of items in a Queue + >>> queue = Queue("ce") + >>> numItems = len(queue) + + # Get all queues (and their items) + >>> queues = getQueues() + +Attributes: + queueDirectory: The directory to load queues from. + queuesToIgnore: Queues that will not be loaded when running getQueues() + +Raises: + # TODO: Add description(s) of when a ValueError is raised. + ValueError: [description] +""" #------------------------------------------------------------------------------# # Imports @@ -20,14 +48,9 @@ currentFileDirectory = os.path.dirname(currentFilePath) currentFileDirectoryParent = os.path.dirname(currentFileDirectory) queueDirectory = os.path.join(currentFileDirectoryParent, "q-snapshot") -#queueDirectory = "/usr/site/uds/qcopy/11" # Queues to not load in getQueues() -queuesToIgnore = ["archives", "drafts", "inbox"] - -# B/c some items don't have a From field -# See coral259 -queuesToIgnore.append("coral") +queuesToIgnore = ["archives", "drafts", "inbox", "coral"] @@ -35,16 +58,35 @@ # Classes #------------------------------------------------------------------------------# class Item: - # TODO: Add Item class documentation + """A single issue. + + Example: + # Create an Item (ce100) + >>> item = Item("ce", 100) + + Attributes: + lastUpdated: An ISO 8601 formatted time string showing the last time the file was updated according to the filesystem. + headers: A list of dictionaries containing header keys and values. + content: A list of section dictionaries. + isLocked: A boolean showing whether or not a lockfile for the item is present. + userEmail: The email address of the person who this item is from. + userName: The real name of the person who this item is from. + userAlias: The Purdue career account alias of the person this item is from. + assignedTo: The Purdue career account alias of the person this item is assigned to + subject: The subject of the original message for this item. + status: The most recent status update for the item. + priority: The most recent priority for this item. + department: The most recent department for this item. + dateReceived: The date this item was created. + jsonData: A JSON serializable representation of the Item. + """ def __init__(self, queue: str, number: int) -> None: - self.queue = queue try: self.number = int(number) except ValueError: - raise ValueError("Could not convert \"" + number + "\" to an integer") - #self.number = number + raise ValueError(" Could not convert \"" + number + "\" to an integer") self.__path = "/".join([queueDirectory, self.queue, str(self.number)]) self.lastUpdated = self.__getLastUpdated() @@ -55,7 +97,7 @@ def __init__(self, queue: str, number: int) -> None: self.userEmail = self.__parseFromData(data="userEmail") self.userName = self.__parseFromData(data="userName") self.userAlias = self.__getUserAlias() - self.assignedTo = self.__getAssignedTo() + self.assignedTo = self.__getMostRecentHeaderByType("Assigned-To") self.subject = self.__getMostRecentHeaderByType("Subject") self.status = self.__getMostRecentHeaderByType("Status") self.priority = self.__getMostRecentHeaderByType("Priority") @@ -63,6 +105,7 @@ def __init__(self, queue: str, number: int) -> None: self.building = self.__getMostRecentHeaderByType("Building") self.dateReceived = self.__getFormattedDate(self.__getMostRecentHeaderByType("Date")) + # TODO: Autopopulate jsonData w/ __dir__() command. Exclude `^_` and `jsonData`. self.jsonData = { "queue": self.queue, "number": self.number, @@ -91,6 +134,7 @@ def __getLastUpdated(self) -> str: Returns: str: last modified time of item reported by the filesystem in mm-dd-yy hh:mm am/pm format. """ + # TODO: Simplify this code block by allowing __getFormattedDate to accept milliseconds since the epoch. unixTime = os.path.getmtime(self.__path) formattedTime = time.strftime('%m-%d-%y %I:%M %p', time.localtime(unixTime)) return self.__getFormattedDate(formattedTime) @@ -151,7 +195,8 @@ def __parseHeaders(self) -> list: headerString += line - message = email.message_from_string(headerString + "".join(self.__getContent())) + # message = email.message_from_string(headerString + "".join(self.__getContent())) + message = email.message_from_string(headerString) headers = [] for key in message.keys(): @@ -161,26 +206,6 @@ def __parseHeaders(self) -> list: # TODO: Implement attachment parsing - def __getContent(self) -> list: - """Returns a dictionary of lines of the item body. - - Example: - "Hello. I need help.\\n\\n*** Status updated by: campb303 at: 6/23/2020 13:26:55 ***\\nDont Delete" becomes - [ - "Hello. I need help.\\n", - "\\n", - "*** Status updated by: campb303 at: 6/23/2020 13:26:55 ***\\n", - "Don't Delete", - ] - - Returns: - list: Lines of the body item. - """ - contentStart = self.__getHeaderBoundary() + 1 - contentEnd = len(self.__rawItem) - 1 - return self.__rawItem[ contentStart : contentEnd ] - - def __parseSections(self) -> list: # List of all item events sections = [] @@ -1008,7 +1033,6 @@ def __getSortedSections(self, sectionsList: list) -> list: return sortedSections - def __isLocked(self) -> Union[str, bool]: """Returns a string info about the lock if true and a bool False if false @@ -1089,16 +1113,6 @@ def __getUserAlias(self) -> str: """ emailUser, emailDomain = self.userEmail.split("@") return emailUser if emailDomain.endswith("purdue.edu") else "" - - def __getAssignedTo(self) -> str: - """Returns the alias of the person this item was most recently assigned to. - Returns empty string if this item isn't assigned. - - Returns: - str: Alias of the person item is assigned to or empty string. - """ - assignedTo = self.__getMostRecentHeaderByType("Assigned-To") - return assignedTo def __getFormattedDate(self, date: str) -> str: """Returns the date/time formatted as RFC 8601 YYYY-MM-DDTHH:MM:SS+00:00. @@ -1109,8 +1123,8 @@ def __getFormattedDate(self, date: str) -> str: str: Properly formatted date/time recieved or empty string. """ try: - parsedDate = parse(date, default= datetime.datetime(2017, 10, 13, tzinfo=tz.gettz('EDT'))) - #parsedDate = parse(date, default= datetime.datetime(2017, 10, 13, tzinfo=datetime.timezone.) + # This date is never meant to be used. The default attribute is just to set timezone. + parsedDate = parse(date, default=datetime.datetime(1970, 0, 1, tzinfo=tz.gettz('EDT'))) except: return "" @@ -1129,8 +1143,19 @@ def toJson(self) -> dict: def __repr__(self) -> str: return self.queue + str(self.number) +# TODO: Make Queue iterable using __iter__. See: https://thispointer.com/python-how-to-make-a-class-iterable-create-iterator-class-for-it/ class Queue: - # TODO: Add Queue class documentation + """A collection of items. + + Example: + # Create a queue (ce) + >>> queue = Queue("ce") + + Attributes: + name: The name of the queue. + items: A list of Items in the queue. + jsonData: A JSON serializable representation of the Queue. + """ def __init__(self, name: str) -> None: self.name = name @@ -1198,4 +1223,4 @@ def getQueues() -> list: if isDirectory and isValid: queues.append(Queue(file)) - return queues + return queues \ No newline at end of file