def handle_comment(self, message): """ The user is posting a comment to Pivotal Tracker via email. """ (sender, message_body, is_html, html_body, plain_body, subject) = self.parse_message(message) logging.info('is_html = %s', is_html) if is_html == True: # try to clean up the html message_body = self.strip_and_clean(message_body) user = db.Query(Users).filter('pt_emails =', sender).get() if user is None: self.log_and_reply(sender, "Could not find your Pivotal Tracker token. Have you signed up yet? " + "Your comment will not be added.\n\nYour original email:\n%s" % (message_body)) return mytoken = user.pt_token story_id = self.get_story_id(message_body) if story_id == False: self.log_and_reply(sender, "Could not find the story Id. Your comment will not be added.\n\nYour original email:\n%s" % ( message_body)) return project_name = self.get_name_from_subject(subject) project_id = PTUtil.get_project_id(user, project_name, story_id) if project_id != False: logging.info("Using ProjectId: " + project_id + " StoryId: " + story_id) else: self.log_and_reply(sender, "Could not find the project for this story. Your comment will not be added.\n\nYour original email:\n%s" % ( message_body)) return comment = self.get_pt_comment(message_body, user.signatures, is_html) if comment is None: self.log_and_reply(sender, "Could not figure out what your comment was.\n\nYour original email:\n%s" % ( message_body)) return self.post_reply_to_pt(mytoken, project_id, story_id, comment) comment = Comments(user_id = user.user_id, project_id = project_id, story_id = story_id, comment = db.Text(comment)) db.put(comment)
def new_ticket(self, message): """ The user is creating a new ticket in Pivotal Tracker via email. """ (sender, message_body, is_html, html_body, plain_body, subject) = self.parse_message(message) logging.info('is_html = %s', is_html) if is_html == True: # try to clean up the html message_body = self.strip_and_clean(message_body) # clean up subject pattern = re.compile('^\s*re[\s:]+', re.I) subject = pattern.sub(lambda x: '', subject) # clean up message body pattern = re.compile('##### PT REPLY #####.*##### PT REPLY #####', re.I | re.S) message_body = pattern.sub(lambda x: ' ', message_body).strip() user = db.Query(Users).filter('pt_emails =', sender).get() if user is None: self.log_and_reply(sender, "##### PT REPLY #####\n" + "Could not find your Pivotal Tracker token. Have you signed up yet at ptreply.com? \n\n" + "Your story will not be added.\n" + "\nNote: this section will automatically be removed when you reply.\n" + "##### PT REPLY #####\n\n" + message_body) return token = user.pt_token bad_subject = False index = subject.find(':') if index < 0: # error no colon in subject bad_subject = True temp = subject[:index] story_name = subject[index+1:].strip() index = temp.rfind(' ') if index < 0: # error no space in subject bad_subject = True return if bad_subject == True: self.log_and_reply(sender, "##### PT REPLY #####\n" + "Your subject was confusing, please make sure it is in the following format:\n" + " PROJECT_NAME STORY_TYPE: STORY_TITLE\n" + " (Example: PT-MAIL bug: users can't login)\n" + "\nNote: this section will automatically be removed when you reply.\n" + "##### PT REPLY #####\n\n" + message_body, subject=subject, send_from=self.newreply, debug=False) return possible_project = temp[:index] story_type = temp[index+1:].lower() logging.info("possible_project: %s, story_type: %s", possible_project, story_type) projects, distance = self.guess_name_from_subject(user, possible_project) logging.info("projects = %s, distance = %s", projects, distance) if len(projects) == 0: # ERROR couldn't find any projects self.log_and_reply(sender, "##### PT REPLY #####\n" + "We couldn't find any projects in Pivotal Tracker that match your subject. " + "Please double check for typos in your subject and try again.\n" + "\nNote: this section will automatically be removed when you reply.\n" + "##### PT REPLY #####\n\n" + message_body, subject=subject, send_from=self.newreply, debug=False) return if len(projects) > 1 and distance == 0: # FATAL ERROR more than one project name matched EXACTLY, we cannot continue self.log_and_reply(sender, "##### PT REPLY #####\n" + "More than one project in Pivotal Tracker matched your subject EXACTLY.\n" + "\nThis is either because our guessing algorithm is wrong or you are actually a member " + "of more than one project with the same name. If you only have one project with this name, " + "please email us at [email protected] so we can take a look.\n" + "\nNote: this section will automatically be removed when you reply.\n" + "##### PT REPLY #####\n\n" + message_body, subject=subject, send_from=self.newreply, debug=False) return if len(projects) > 1 or distance > 2: # ERROR more than one project matched the same or not well enough, user needs to choose new_subject = "%s %s: %s" % (projects[0], story_type, story_name) self.log_and_reply(sender, "##### PT REPLY #####\n" + "We couldn't guess what project you wanted to add this story to, but we think we have a pretty good idea.\n" + "\nCheck the new subject of this email, and if it looks good, just hit reply and send and we'll take care of it.\n" + "\nHere are some other projects it might be, but you'll have to change the subject yourself:\n " + "\n ".join(projects[1:]) + "\n" + "\nNote: this section will automatically be removed when you reply.\n" + "##### PT REPLY #####\n\n" + message_body, subject=new_subject, send_from=self.newreply, debug=False) return project_id = PTUtil.get_project_id(user, projects[0]) if project_id == False: self.log_and_reply(sender, "##### PT REPLY #####\n" + "Could not find the project in Pivotal Tracker.\n" + "\nPlease visit ptreply.com and verify that your token is still valid.\n" + "##### PT REPLY #####\n\n" + message_body) return # sanity check story type if story_type != 'feature' and story_type != 'bug' and story_type != 'release' and story_type != 'chore': story_type = 'feature' description = self.get_pt_comment(message_body, user.signatures, is_html) payload = """ <story> <story_type>%s</story_type> <name>%s</name> <description>%s</description> </story> """ % (story_type, story_name, description) logging.info("Using project_id %s to post new %s: %s", project_id, story_type, story_name) logging.info("Payload: %s", payload) url = "https://www.pivotaltracker.com/services/v3/projects/%s/stories" % (project_id) result = urlfetch.fetch(url=url, payload=payload, method=urlfetch.POST, headers={'X-TrackerToken': token, 'Content-type': 'application/xml'}) story_dom = minidom.parseString(result.content) story_id = None for node in story_dom.getElementsByTagName('story'): story_id = node.getElementsByTagName('id')[0].firstChild.data if story_id is not None: logging.info("Story Posted") else: logging.info("Failed to Post Story") logging.info(result.content)