def __init__(self, parent, bug_watch, external_bugtracker): self.initFromParent(parent) self.bug_watch = bug_watch self.external_bugtracker = external_bugtracker # We save these for the sake of error reporting. self.remote_bug = self.bug_watch.remotebug self.local_bug = self.bug_watch.bug.id self.oops_properties = get_remote_system_oops_properties( self.external_bugtracker) self.oops_properties.extend([('URL', self.bug_watch.url), ('bug_id', self.remote_bug), ('local_ids', str(self.local_bug))]) self.can_import_comments = parent.can_import_comments self.can_push_comments = parent.can_push_comments self.can_back_link = parent.can_back_link
def __init__(self, parent, bug_watch, external_bugtracker): self.initFromParent(parent) self.bug_watch = bug_watch self.external_bugtracker = external_bugtracker # We save these for the sake of error reporting. self.remote_bug = self.bug_watch.remotebug self.local_bug = self.bug_watch.bug.id self.oops_properties = get_remote_system_oops_properties( self.external_bugtracker) self.oops_properties.extend([ ('URL', self.bug_watch.url), ('bug_id', self.remote_bug), ('local_ids', str(self.local_bug))]) self.can_import_comments = parent.can_import_comments self.can_push_comments = parent.can_push_comments self.can_back_link = parent.can_back_link
def importBugComments(self): """Import all the comments from the remote bug.""" with self.transaction: local_bug_id = self.bug_watch.bug.id remote_bug_id = self.bug_watch.remotebug # Construct a list of the comment IDs we want to import; i.e. # those which we haven't already imported. all_comment_ids = self.external_bugtracker.getCommentIds( remote_bug_id) with self.transaction: comment_ids_to_import = [ comment_id for comment_id in all_comment_ids if not self.bug_watch.hasComment(comment_id)] self.external_bugtracker.fetchComments( remote_bug_id, comment_ids_to_import) with self.transaction: previous_imported_comments = ( self.bug_watch.getImportedBugMessages()) is_initial_import = previous_imported_comments.count() == 0 imported_comments = [] for comment_id in comment_ids_to_import: displayname, email = ( self.external_bugtracker.getPosterForComment( remote_bug_id, comment_id)) if displayname is None and email is None: # If we don't have a displayname or an email address # then we can't create a Launchpad Person as the author # of this comment. We raise an OOPS and continue. self.warning( "Unable to import remote comment author. No email " "address or display name found.", get_remote_system_oops_properties( self.external_bugtracker), sys.exc_info()) continue poster = self.bug_watch.bugtracker.ensurePersonForSelf( displayname, email, PersonCreationRationale.BUGIMPORT, "when importing comments for %s." % self.bug_watch.title) comment_message = ( self.external_bugtracker.getMessageForComment( remote_bug_id, comment_id, poster)) bug_message = self.bug_watch.addComment( comment_id, comment_message) imported_comments.append(bug_message) if len(imported_comments) > 0: self.bug_watch_updater = ( getUtility(ILaunchpadCelebrities).bug_watch_updater) if is_initial_import: notification_text = get_email_template( 'bugwatch-initial-comment-import.txt', 'bugs') % dict( num_of_comments=len(imported_comments), bug_watch_url=self.bug_watch.url) comment_text_template = get_email_template( 'bugwatch-comment.txt', 'bugs') for bug_message in imported_comments: comment = bug_message.message notification_text += comment_text_template % dict( comment_date=comment.datecreated.isoformat(), commenter=comment.owner.displayname, comment_text=comment.text_contents, comment_reply_url=canonical_url(comment)) notification_message = getUtility(IMessageSet).fromText( subject=self.bug_watch.bug.followup_subject(), content=notification_text, owner=self.bug_watch_updater) self.bug_watch.bug.addCommentNotification( notification_message) else: for bug_message in imported_comments: notify(ObjectCreatedEvent( bug_message, user=self.bug_watch_updater)) self.logger.info("Imported %(count)i comments for remote bug " "%(remotebug)s on %(bugtracker_url)s into Launchpad bug " "%(bug_id)s." % {'count': len(imported_comments), 'remotebug': remote_bug_id, 'bugtracker_url': self.external_bugtracker.baseurl, 'bug_id': local_bug_id})
def importBugComments(self): """Import all the comments from the remote bug.""" with self.transaction: local_bug_id = self.bug_watch.bug.id remote_bug_id = self.bug_watch.remotebug # Construct a list of the comment IDs we want to import; i.e. # those which we haven't already imported. all_comment_ids = self.external_bugtracker.getCommentIds(remote_bug_id) with self.transaction: comment_ids_to_import = [ comment_id for comment_id in all_comment_ids if not self.bug_watch.hasComment(comment_id) ] self.external_bugtracker.fetchComments(remote_bug_id, comment_ids_to_import) with self.transaction: previous_imported_comments = ( self.bug_watch.getImportedBugMessages()) is_initial_import = previous_imported_comments.count() == 0 imported_comments = [] for comment_id in comment_ids_to_import: displayname, email = ( self.external_bugtracker.getPosterForComment( remote_bug_id, comment_id)) if displayname is None and email is None: # If we don't have a displayname or an email address # then we can't create a Launchpad Person as the author # of this comment. We raise an OOPS and continue. self.warning( "Unable to import remote comment author. No email " "address or display name found.", get_remote_system_oops_properties( self.external_bugtracker), sys.exc_info()) continue poster = self.bug_watch.bugtracker.ensurePersonForSelf( displayname, email, PersonCreationRationale.BUGIMPORT, "when importing comments for %s." % self.bug_watch.title) comment_message = ( self.external_bugtracker.getMessageForComment( remote_bug_id, comment_id, poster)) bug_message = self.bug_watch.addComment( comment_id, comment_message) imported_comments.append(bug_message) if len(imported_comments) > 0: self.bug_watch_updater = ( getUtility(ILaunchpadCelebrities).bug_watch_updater) if is_initial_import: notification_text = get_email_template( 'bugwatch-initial-comment-import.txt', 'bugs') % dict( num_of_comments=len(imported_comments), bug_watch_url=self.bug_watch.url) comment_text_template = get_email_template( 'bugwatch-comment.txt', 'bugs') for bug_message in imported_comments: comment = bug_message.message notification_text += comment_text_template % dict( comment_date=comment.datecreated.isoformat(), commenter=comment.owner.displayname, comment_text=comment.text_contents, comment_reply_url=canonical_url(comment)) notification_message = getUtility(IMessageSet).fromText( subject=self.bug_watch.bug.followup_subject(), content=notification_text, owner=self.bug_watch_updater) self.bug_watch.bug.addCommentNotification( notification_message) else: for bug_message in imported_comments: notify( ObjectCreatedEvent(bug_message, user=self.bug_watch_updater)) self.logger.info( "Imported %(count)i comments for remote bug " "%(remotebug)s on %(bugtracker_url)s into Launchpad bug " "%(bug_id)s." % { 'count': len(imported_comments), 'remotebug': remote_bug_id, 'bugtracker_url': self.external_bugtracker.baseurl, 'bug_id': local_bug_id })
def updateRemoteBug(self): with self.transaction: bug_watches = self._getBugWatchesForRemoteBug() # If there aren't any bug watches for this remote bug, # just log a warning and carry on. if len(bug_watches) == 0: self.warning( "Spurious remote bug ID: No watches found for " "remote bug %s on %s" % (self.remote_bug, self.external_bugtracker.baseurl) ) return # Mark them all as checked. for bug_watch in bug_watches: bug_watch.lastchecked = UTC_NOW bug_watch.next_check = None # Return if this one is definitely unmodified. if self.remote_bug in self.unmodified_remote_ids: return # Save the remote bug URL for error reporting. remote_bug_url = bug_watches[0].url # Save the list of local bug IDs for error reporting. local_ids = ", ".join(str(bug_id) for bug_id in sorted(watch.bug.id for watch in bug_watches)) try: new_remote_status = None new_malone_status = None new_remote_importance = None new_malone_importance = None error = None oops_id = None try: new_remote_status = self.external_bugtracker.getRemoteStatus(self.remote_bug) new_malone_status = self._convertRemoteStatus(new_remote_status) new_remote_importance = self.external_bugtracker.getRemoteImportance(self.remote_bug) new_malone_importance = self._convertRemoteImportance(new_remote_importance) except (InvalidBugId, BugNotFound, PrivateRemoteBug) as ex: error = get_bugwatcherrortype_for_error(ex) message = self.error_type_messages.get(error, self.error_type_message_default) self.logger.info( message % {"bug_id": self.remote_bug, "base_url": self.external_bugtracker.baseurl, "local_ids": local_ids} ) # Set the error and activity on all bug watches with self.transaction: getUtility(IBugWatchSet).bulkSetError(bug_watches, error) getUtility(IBugWatchSet).bulkAddActivity(bug_watches, result=error) else: # Assuming nothing's gone wrong, we can now deal with # each BugWatch in turn. for bug_watch in bug_watches: bug_watch_updater = BugWatchUpdater(self, bug_watch, self.external_bugtracker) bug_watch_updater.updateBugWatch( new_remote_status, new_malone_status, new_remote_importance, new_malone_importance ) except Exception as error: # Send the error to the log. oops_id = self.error( "Failure updating bug %r on %s (local bugs: %s)." % (self.remote_bug, self.bug_tracker_url, local_ids), properties=[("URL", remote_bug_url), ("bug_id", self.remote_bug), ("local_ids", local_ids)] + get_remote_system_oops_properties(self.external_bugtracker), ) # We record errors against the bug watches and update # their lastchecked dates so that we don't try to # re-check them every time checkwatches runs. error_type = get_bugwatcherrortype_for_error(error) with self.transaction: getUtility(IBugWatchSet).bulkSetError(bug_watches, error_type) getUtility(IBugWatchSet).bulkAddActivity(bug_watches, result=error_type, oops_id=oops_id)
def updateRemoteBug(self): with self.transaction: bug_watches = self._getBugWatchesForRemoteBug() # If there aren't any bug watches for this remote bug, # just log a warning and carry on. if len(bug_watches) == 0: self.warning( "Spurious remote bug ID: No watches found for " "remote bug %s on %s" % ( self.remote_bug, self.external_bugtracker.baseurl)) return # Mark them all as checked. for bug_watch in bug_watches: bug_watch.lastchecked = UTC_NOW bug_watch.next_check = None # Return if this one is definitely unmodified. if self.remote_bug in self.unmodified_remote_ids: return # Save the remote bug URL for error reporting. remote_bug_url = bug_watches[0].url # Save the list of local bug IDs for error reporting. local_ids = ", ".join( str(bug_id) for bug_id in sorted( watch.bug.id for watch in bug_watches)) try: new_remote_status = None new_malone_status = None new_remote_importance = None new_malone_importance = None error = None oops_id = None try: new_remote_status = ( self.external_bugtracker.getRemoteStatus( self.remote_bug)) new_malone_status = self._convertRemoteStatus( new_remote_status) new_remote_importance = ( self.external_bugtracker.getRemoteImportance( self.remote_bug)) new_malone_importance = self._convertRemoteImportance( new_remote_importance) except (InvalidBugId, BugNotFound, PrivateRemoteBug) as ex: error = get_bugwatcherrortype_for_error(ex) message = self.error_type_messages.get( error, self.error_type_message_default) self.logger.info( message % { 'bug_id': self.remote_bug, 'base_url': self.external_bugtracker.baseurl, 'local_ids': local_ids, }) # Set the error and activity on all bug watches with self.transaction: getUtility(IBugWatchSet).bulkSetError( bug_watches, error) getUtility(IBugWatchSet).bulkAddActivity( bug_watches, result=error) else: # Assuming nothing's gone wrong, we can now deal with # each BugWatch in turn. for bug_watch in bug_watches: bug_watch_updater = BugWatchUpdater( self, bug_watch, self.external_bugtracker) bug_watch_updater.updateBugWatch( new_remote_status, new_malone_status, new_remote_importance, new_malone_importance) except Exception as error: # Send the error to the log. oops_id = self.error( "Failure updating bug %r on %s (local bugs: %s)." % (self.remote_bug, self.bug_tracker_url, local_ids), properties=[ ('URL', remote_bug_url), ('bug_id', self.remote_bug), ('local_ids', local_ids)] + get_remote_system_oops_properties( self.external_bugtracker)) # We record errors against the bug watches and update # their lastchecked dates so that we don't try to # re-check them every time checkwatches runs. error_type = get_bugwatcherrortype_for_error(error) with self.transaction: getUtility(IBugWatchSet).bulkSetError( bug_watches, error_type) getUtility(IBugWatchSet).bulkAddActivity( bug_watches, result=error_type, oops_id=oops_id)