예제 #1
0
    def update(self):
        self.instance.update()
        self._history = \
            HistoryWrapper(self, cachedir=self.cachedir, usecache=True)
        if self.is_pullrequest():
            self.pullrequest.update()

            if self.instance.updated_at > self.pullrequest.updated_at:
                logging.error('breakpoint!')
                import epdb; epdb.st()
예제 #2
0
    def update(self):
        self.instance.update()
        self._history = \
            HistoryWrapper(self, cachedir=self.cachedir, usecache=True)
        if self.is_pullrequest():
            self.pullrequest.update()

            if self.instance.updated_at > self.pullrequest.updated_at:
                import epdb
                epdb.st()
예제 #3
0
    def process_history(self, usecache=True):
        self.meta = {}
        today = self.get_current_time()

        # Build the history
        self.debug(msg="Building event history ...")
        self.history = HistoryWrapper(self.issue, usecache=usecache)

        # what was the last commment?
        bot_broken = False
        if self.issue.current_comments:
            for comment in self.issue.current_comments:
                if 'bot_broken' in comment.body:
                    bot_broken = True

        # who made this and when did they last comment?
        submitter = self.issue.get_submitter()
        submitter_last_commented = self.history.last_commented_at(submitter)
        submitter_last_comment = self.history.last_comment(submitter)
        submitter_last_notified = self.history.last_notified(submitter)

        # what did they not provide?
        missing_sections = self.issue.get_missing_sections()
        if 'ansible version' in missing_sections:
            missing_sections.remove('ansible version')

        # DEBUG + FIXME - speeds up bulk triage
        if 'component name' in missing_sections and self.match:
            missing_sections.remove('component name')
        #import epdb; epdb.st()

        # Is this a valid module?
        valid_module = False
        correct_repo = True
        if self.match:
            valid_module = True
            if self.match['repository'] != self.github_repo:
                correct_repo = False
            #import epdb; epdb.st()

        # Do these after evaluating the module
        self.add_desired_labels_by_issue_type()
        if 'ansible version' in missing_sections:
            #self.debug(msg='desired_comments: %s' % self.issue.desired_comments)
            self.add_desired_labels_by_ansible_version()
        #self.debug(msg='desired_comments: %s' % self.issue.desired_comments)
        self.add_desired_labels_by_namespace()
        #self.debug(msg='desired_comments: %s' % self.issue.desired_comments)


        # Who are the maintainers?
        maintainers = [x for x in self.get_module_maintainers()]
        #if 'ansible' in maintainers:
        #    maintainers.remove('ansible')
        #    maintainers += self.ansible_members

        #print("MAINTAINERS: %s" % maintainers)
        if 'ansible' in maintainers:
            maintainers.remove('ansible')
        maintainers.extend(self.ansible_members)
        if 'ansibot' in maintainers:
            maintainers.remove('ansibot')
        if submitter in maintainers:
            maintainers.remove(submitter)
        maintainers = sorted(set(maintainers))

        # Has maintainer been notified? When?
        notification_maintainers = self.get_module_maintainers()
        if 'ansible' in notification_maintainers:
            notification_maintainers.extend(self.ansible_members)
        if 'ansibot' in notification_maintainers:
            notification_maintainers.remove('ansibot')
        maintainer_last_notified = self.history.\
                    last_notified(notification_maintainers)
        #import epdb; epdb.st()

        # Has maintainer viewed issue?
        maintainer_viewed = self.history.has_viewed(maintainers)
        maintainer_last_viewed = self.history.last_viewed_at(maintainers)
        #import epdb; epdb.st()

        # Has maintainer been mentioned?
        maintainer_mentioned = self.history.is_mentioned(maintainers)

        # Has maintainer viewed issue?
        maintainer_viewed = self.history.has_viewed(maintainers)

        # Has the maintainer ever responded?
        maintainer_commented = self.history.has_commented(maintainers)
        maintainer_last_commented = self.history.last_commented_at(maintainers)
        maintainer_last_comment = self.history.last_comment(maintainers)
        maintainer_comments = self.history.get_user_comments(maintainers)
        #import epdb; epdb.st()

        # Was the maintainer the last commentor?
        last_commentor_ismaintainer = False
        last_commentor_issubmitter = False
        last_commentor = self.history.last_commentor()
        if last_commentor in maintainers and last_commentor != self.github_user:
            last_commentor_ismaintainer = True
        elif last_commentor == submitter:
            last_commentor_issubmitter = True

        # Did the maintainer issue a command?
        maintainer_commands = self.history.get_commands(maintainers, 
                                                        self.VALID_COMMANDS)
        #import epdb; epdb.st()
        maintainer_command_close = False
        maintainer_command_needsinfo = False
        maintainer_command_not_needsinfo = False
        maintainer_command_notabug = False
        maintainer_command_wontfix = False
        maintainer_command_resolved_bug = False
        maintainer_command_resolved_pr = False
        maintainer_command_needscontributor = False
        maintainer_command_duplicateof = False
        if maintainer_commented and not maintainer_last_comment:
            print('ERROR: should have a comment from maintainer')
            import epdb; epdb.st()
        elif maintainer_last_comment and last_commentor_ismaintainer:
            maintainer_last_comment = maintainer_last_comment.strip()            
            if 'needs_info' in maintainer_last_comment \
                and not '!needs_info' in maintainer_last_comment:
                maintainer_command_needsinfo = True
            elif '!needs_info' in maintainer_last_comment:
                maintainer_command_not_needsinfo = True
            elif 'notabug' in maintainer_last_comment:
                maintainer_command_notabug = True
                maintainer_command_close = True
            elif 'wontfix' in maintainer_last_comment:
                maintainer_command_wontfix = True
                maintainer_command_close = True
            elif 'bug_resolved' in maintainer_last_comment:
                maintainer_command_resolved_bug = True
                maintainer_command_close = True
            elif 'resolved_by_pr' in maintainer_last_comment:
                maintainer_command_resolved_pr = True
                maintainer_command_close = True
            elif 'needs_contributor' in maintainer_last_comment:
                maintainer_command_needscontributor = True
            elif 'duplicate_of' in maintainer_last_comment:
                maintainer_command_duplicateof = True
                maintainer_command_close = True
        elif maintainer_commands:
            # are there any persistant commands?
            if 'needs_contributor' in maintainer_commands:
                maintainer_command_needscontributor = True
            elif not missing_sections and not submitter_last_commented and maintainer_commands[-1] == 'needs_info':
                maintainer_command_needsinfo = True
            elif not missing_sections and not submitter_last_commented and maintainer_commands[-1] == '!needs_info':
                maintainer_command_not_needsinfo = True
            #import epdb; epdb.st()
        #import epdb; epdb.st()

        # Has the maintainer ever subscribed?
        maintainer_subscribed = self.history.has_subscribed(maintainers)

        # Was it ever needs_info?
        was_needs_info = self.history.was_labeled(label='needs_info')
        needsinfo_last_applied = self.history.label_last_applied('needs_info')
        needsinfo_last_removed = self.history.label_last_removed('needs_info')
        #import epdb; epdb.st()

        # Still needs_info?
        needsinfo_add = False
        needsinfo_remove = False
        if 'needs_info' in self.issue.current_labels:
            if submitter_last_commented and needsinfo_last_applied:
                if submitter_last_commented > needsinfo_last_applied \
                    and not missing_sections:
                    needsinfo_remove = True
        if maintainer_command_needsinfo and maintainer_last_commented:
            if submitter_last_commented and maintainer_last_commented:
                if submitter_last_commented > maintainer_last_commented:
                    needsinfo_add = False
                    needsinfo_remove = True
            else:
                needsinfo_add = True
                needsinfo_remove = False

        # Is needs_info stale or expired?
        needsinfo_age = None
        needsinfo_stale = False
        needsinfo_expired = False
        if 'needs_info' in self.issue.current_labels: 
            time_delta = today - needsinfo_last_applied
            needsinfo_age = time_delta.days
            if needsinfo_age > 14:
                needsinfo_stale = True
            if needsinfo_age > 56:
                needsinfo_expired = True

        # Should we be in waiting_on_maintainer mode?
        maintainer_waiting_on = False
        if (needsinfo_remove or not needsinfo_add) \
            or not was_needs_info \
            and not missing_sections:
            maintainer_waiting_on = True

        # Should we [re]notify the submitter?
        submitter_waiting_on = False
        submitter_to_ping = False
        submitter_to_reping = False
        if not maintainer_waiting_on:
            submitter_waiting_on = True

        if missing_sections:
            submitter_waiting_on = True
            maintainer_waiting_on = False
        else:
            if 'needs_info' in self.issue.current_labels \
                and not maintainer_command_not_needsinfo:
                needsinfo_remove = True                
                submitter_waiting_on = False
                maintainer_waiting_on = True

        if maintainer_command_not_needsinfo:
            submitter_waiting_on = False
            maintainer_waiting_on = True

        if maintainer_command_needsinfo:
            submitter_waiting_on = True
            maintainer_waiting_on = False
            needsinfo_add = True
            needsinfo_remove = False

        # Time to [re]ping maintainer?
        maintainer_to_ping = False
        maintainer_to_reping = False
        if maintainer_waiting_on:
            #import epdb; epdb.st()
            if maintainer_viewed and not maintainer_last_notified:
                time_delta = today - maintainer_last_viewed
                view_age = time_delta.days
                if view_age > 14:
                    maintainer_to_reping = True
            elif maintainer_last_notified:
                time_delta = today - maintainer_last_notified
                ping_age = time_delta.days
                if ping_age > 14:
                    maintainer_to_reping = True
            else:
                maintainer_to_ping = True

        # Time to [re]ping the submitter?
        if submitter_waiting_on:
            if submitter_last_notified:
                time_delta = today - submitter_last_notified
                notification_age = time_delta.days
                if notification_age > 14:
                    submitter_to_reping = True
                else:
                    submitter_to_reping = False
                submitter_to_ping = False
            else:
                submitter_to_ping = True
                submitter_to_reping = False

        issue_type = self.template_data.get('issue type', None)
        issue_type = self.issue_type_to_label(issue_type)
        self.meta['issue_type'] = issue_type

        # new module requests need to disable everything
        #   https://github.com/ansible/ansible-modules-core/issues/4112
        #   https://github.com/ansible/ansible-modules-core/issues/2267
        #   https://github.com/ansible/ansible-modules-core/issues/2626
        #   https://github.com/ansible/ansible-modules-core/issues/645
        new_module_request = False
        if 'feature_idea' in self.issue.desired_labels:
            if self.template_data['component name'] == 'new':
                new_module_request = True

        # reset the maintainers
        maintainers = self.get_module_maintainers()

        #################################################
        # FINAL LOGIC LOOP
        #################################################

        if bot_broken:
            self.debug(msg='broken bot stanza')
            self.issue.add_desired_label('bot_broken')

        elif maintainer_command_close:

            self.debug(msg='maintainer closure stanza')

            # Need to close the issue ...
            self.issue.set_desired_state('closed')


        elif new_module_request:

            self.debug(msg='new module request stanza')
            self.issue.desired_comments = []
            for label in self.issue.current_labels:
                if not label in self.issue.desired_labels:
                    self.issue.desired_labels.append(label)

        elif not correct_repo:

            self.debug(msg='wrong repo stanza')
            self.issue.desired_comments = ['issue_wrong_repo']
            self.actions['close'] = True
            #import epdb; epdb.st()

        elif not valid_module and not maintainer_command_needsinfo:
            self.debug(msg='invalid module stanza')

            #import epdb; epdb.st()
            self.issue.add_desired_label('needs_info')
            if 'issue_invalid_module' not in self.issue.current_bot_comments \
                and not 'issue_needs_info' in self.issue.current_bot_comments:
                self.issue.desired_comments = ['issue_invalid_module']

        elif not maintainers and not maintainer_command_needsinfo:

            self.debug(msg='no maintainer stanza')

            self.issue.add_desired_label('waiting_on_maintainer')
            self.issue.add_desired_comment("issue_module_no_maintainer")

        elif maintainer_command_needscontributor:

            # maintainer can't or won't fix this, but would like someone else to
            self.debug(msg='maintainer needs contributor stanza')
            self.issue.add_desired_label('waiting_on_contributor')
            #import epdb; epdb.st()            

        elif maintainer_waiting_on:

            self.debug(msg='maintainer wait stanza')
            #import epdb; epdb.st()

            self.issue.add_desired_label('waiting_on_maintainer')
            if len(self.issue.current_comments) == 0:
                if issue_type:
                    self.issue.add_desired_comment('issue_new')
            else:
                if maintainers != ['DEPRECATED']:
                    if maintainer_to_ping and maintainers:
                        self.issue.add_desired_comment("issue_notify_maintainer")
                    elif maintainer_to_reping and maintainers:
                        self.issue.add_desired_comment("issue_renotify_maintainer")
                #import epdb; epdb.st()

        elif submitter_waiting_on:

            self.debug(msg='submitter wait stanza')
            #import epdb; epdb.st()

            if 'waiting_on_maintainer' in self.issue.desired_labels:
                self.issue.desired_labels.remove('waiting_on_maintainer')

            if (needsinfo_add or missing_sections) \
                or (not needsinfo_remove and missing_sections) \
                or (needsinfo_add and not missing_sections): 

                self.issue.add_desired_label('needs_info')
                if len(self.issue.current_comments) == 0:
                    self.issue.add_desired_comment("issue_needs_info")

                # needs_info: warn if stale, close if expired
                elif needsinfo_expired:
                    self.issue.add_desired_comment("issue_closure")
                    self.issue.set_desired_state('closed')
                elif needsinfo_stale \
                    and (submitter_to_ping or submitter_to_reping):
                    self.issue.add_desired_comment("issue_pending_closure")
예제 #4
0
 def history(self):
     if self._history is False:
         self._history = \
             HistoryWrapper(self, cachedir=self.cachedir, usecache=True)
     return self._history
예제 #5
0
    def get_facts(self, usecache=True):
        '''Only used by the ansible/ansible triager at the moment'''
        hfacts = {}
        today = self.get_current_time()

        self.history = HistoryWrapper(
                        self.issue,
                        usecache=usecache,
                        cachedir=self.cachedir
                       )

        # what was the last commment?
        bot_broken = False
        if self.issue.current_comments:
            for comment in self.issue.current_comments:
                if 'bot_broken' in comment.body:
                    bot_broken = True

        # did someone from ansible want this issue skipped?
        bot_skip = False
        if self.issue.current_comments:
            for comment in self.issue.current_comments:
                if 'bot_skip' in comment.body and comment.user.login in self.ansible_members:
                    bot_skip = True

        #########################################################
        # Facts for new "triage" workflow
        #########################################################

        # these logins don't count for the next set of facts
        exclude_logins = ['ansibot', 'gregdek', self.issue.instance.user.login]

        # Has a human (not ansibot + not submitter) ever changed labels?
        lchanges = [x for x in self.history.history if x['event'] in ['labeled', 'unlabeled'] and x['actor'] not in exclude_logins]
        if lchanges:
            hfacts['human_labeled'] = True
        else:
            hfacts['human_labeled'] = False

        # Has a human (not ansibot + not submitter) ever changed the title?
        tchanges = [x for x in self.history.history if x['event'] in ['renamed'] and x['actor'] not in exclude_logins]
        if tchanges:
            hfacts['human_retitled'] = True
        else:
            hfacts['human_retitled'] = False

        # Has a human (not ansibot + not submitter) ever changed the description?
        # ... github doesn't record an event for this. Lame.

        # Has a human (not ansibot + not submitter) ever set a milestone?
        mchanges = [x for x in self.history.history if x['event'] in ['milestoned', 'demilestoned'] and x['actor'] not in exclude_logins]
        if mchanges:
            hfacts['human_milestoned'] = True
        else:
            hfacts['human_milestoned'] = False

        # Has a human (not ansibot + not submitter) ever set/unset assignees?
        achanges = [x for x in self.history.history if x['event'] in ['assigned', 'unassigned'] and x['actor'] not in exclude_logins]
        if achanges:
            hfacts['human_assigned'] = True
        else:
            hfacts['human_assigned'] = False

        #########################################################

        # Has the bot been overzealous with comments?
        hfacts['bot_spam'] = False
        bcg = self.history.get_user_comments_groupby('ansibot', groupby='w')
        for k,v in bcg.iteritems():
            if v >= self.MAX_BOT_COMMENTS_PER_WEEK:
                hfacts['bot_spam'] = True

        # Is this a new module?
        hfacts['new_module_request'] = False
        if 'feature_idea' in self.issue.desired_labels:
            if self.template_data['component name'] == 'new':
                hfacts['new_module_request'] = True

        # who made this and when did they last comment?
        submitter = self.issue.get_submitter()
        submitter_last_commented = self.history.last_commented_at(submitter)
        if not submitter_last_commented:
            submitter_last_commented = self.issue.instance.created_at
            #import epdb; epdb.st()
        submitter_last_comment = self.history.last_comment(submitter)
        submitter_last_notified = self.history.last_notified(submitter)

        # what did they not provide?
        missing_sections = self.issue.get_missing_sections()

        # Is this a valid module?
        if self.match:
            self.meta['valid_module'] = True
        else:
            self.meta['valid_module'] = False

        # Filed in the right place?
        if self.meta['valid_module']:
            if self.match['repository'] != self.github_repo:
                hfacts['correct_repo'] = False
            else:
                hfacts['correct_repo'] = True
        else:
            hfacts['correct_repo'] = True

        # DEBUG + FIXME - speeds up bulk triage
        if 'component name' in missing_sections \
            and (self.match or self.github_repo == 'ansible'):
            missing_sections.remove('component name')
        #import epdb; epdb.st()

        # Who are the maintainers?
        maintainers = [x for x in self.get_module_maintainers()]
        #hfacts['maintainers'] = maintainers
        #import epdb; epdb.st()

        # Set a fact to indicate that we know the maintainer
        self.meta['maintainers_known'] = False
        if maintainers:
            self.meta['maintainers_known'] = True

        if 'ansible' in maintainers:
            maintainers.remove('ansible')
        maintainers.extend(self.ansible_members)
        if 'ansibot' in maintainers:
            maintainers.remove('ansibot')
        if submitter in maintainers:
            maintainers.remove(submitter)
        maintainers = sorted(set(maintainers))

        # Has maintainer been notified? When?
        notification_maintainers = [x for x in self.get_module_maintainers()]
        if 'ansible' in notification_maintainers:
            notification_maintainers.extend(self.ansible_members)
        if 'ansibot' in notification_maintainers:
            notification_maintainers.remove('ansibot')
        hfacts['notification_maintainers'] = notification_maintainers
        maintainer_last_notified = self.history.\
                    last_notified(notification_maintainers)

        # Has maintainer viewed issue?
        maintainer_viewed = self.history.has_viewed(maintainers)
        maintainer_last_viewed = self.history.last_viewed_at(maintainers)
        #import epdb; epdb.st()

        # Has maintainer been mentioned?
        maintainer_mentioned = self.history.is_mentioned(maintainers)

        # Has maintainer viewed issue?
        maintainer_viewed = self.history.has_viewed(maintainers)

        # Has the maintainer ever responded?
        maintainer_commented = self.history.has_commented(maintainers)
        maintainer_last_commented = self.history.last_commented_at(maintainers)
        maintainer_last_comment = self.history.last_comment(maintainers)
        maintainer_comments = self.history.get_user_comments(maintainers)
        #import epdb; epdb.st()

        # Was the maintainer the last commentor?
        last_commentor_ismaintainer = False
        last_commentor_issubmitter = False
        last_commentor = self.history.last_commentor()
        if last_commentor in maintainers and last_commentor != self.github_user:
            last_commentor_ismaintainer = True
        elif last_commentor == submitter:
            last_commentor_issubmitter = True

        # Did the maintainer issue a command?
        maintainer_commands = self.history.get_commands(maintainers,
                                                        self.VALID_COMMANDS)

        # Keep all commands
        hfacts['maintainer_commands'] = maintainer_commands
        # Set a bit for the last command given
        if hfacts['maintainer_commands']:
            hfacts['maintainer_command'] = hfacts['maintainer_commands'][-1]
        else:
            hfacts['maintainer_command'] = None

        # Is the last command a closure command?
        if hfacts['maintainer_command'] in self.CLOSURE_COMMANDS:
            hfacts['maintainer_closure'] = True
        else:
            hfacts['maintainer_closure'] = False

        # handle resolved_by_pr ...
        if 'resolved_by_pr' in maintainer_commands:
            maintainer_comments = self.history.get_user_comments(maintainers)
            maintainer_comments = [x for x in reversed(maintainer_comments) \
                                   if 'resolved_by_pr' in x]
            for comment in maintainer_comments:
                pr_number = extract_pr_number_from_comment(comment)
                hfacts['resolved_by_pr'] = {
                    'number': pr_number,
                    'merged': self.is_pr_merged(pr_number),
                }
                if not hfacts['resolved_by_pr']['merged']:
                    hfacts['maintainer_closure'] = False
                break

        # needs_info toggles
        ni_commands = [x for x in maintainer_commands if 'needs_info' in x]

        # Has the maintainer ever subscribed?
        maintainer_subscribed = self.history.has_subscribed(maintainers)

        # Was it ever needs_info?
        was_needs_info = self.history.was_labeled(label='needs_info')
        needsinfo_last_applied = self.history.label_last_applied('needs_info')
        needsinfo_last_removed = self.history.label_last_removed('needs_info')

        # Still needs_info?
        needsinfo_add = False
        needsinfo_remove = False

        if 'needs_info' in self.issue.current_labels:
            if not needsinfo_last_applied or not submitter_last_commented:
                import epdb; epdb.st()
            if submitter_last_commented > needsinfo_last_applied:
                needsinfo_add = False
                needsinfo_remove = True

        #if 'needs_info' in maintainer_commands and maintainer_last_commented:
        if ni_commands and maintainer_last_commented:
            if ni_commands[-1] == 'needs_info':
                #import epdb; epdb.st()
                if submitter_last_commented and maintainer_last_commented:
                    if submitter_last_commented > maintainer_last_commented:
                        needsinfo_add = False
                        needsinfo_remove = True
                else:
                    needsinfo_add = True
                    needsinfo_remove = False
            else:
                needsinfo_add = False
                needsinfo_remove = True

        # Save existing needs_info if not time to remove ...
        if 'needs_info' in self.issue.current_labels \
            and not needsinfo_add \
            and not needsinfo_remove:
            needsinfo_add = True

        if ni_commands and maintainer_last_commented:
            if maintainer_last_commented > submitter_last_commented:
                if ni_commands[-1] == 'needs_info':
                    needsinfo_add = True
                    needsinfo_remove = False
                else:
                    needsinfo_add = False
                    needsinfo_remove = True
        #import epdb; epdb.st()

        # Is needs_info stale or expired?
        needsinfo_age = None
        needsinfo_stale = False
        needsinfo_expired = False
        if 'needs_info' in self.issue.current_labels:
            time_delta = today - needsinfo_last_applied
            needsinfo_age = time_delta.days
            if needsinfo_age > self.RENOTIFY_INTERVAL:
                needsinfo_stale = True
            if needsinfo_age > self.RENOTIFY_EXPIRE:
                needsinfo_expired = True

        # Should we be in waiting_on_maintainer mode?
        maintainer_waiting_on = False
        if (needsinfo_remove or not needsinfo_add) \
            or not was_needs_info \
            and not missing_sections:
            maintainer_waiting_on = True

        # Should we [re]notify the submitter?
        submitter_waiting_on = False
        submitter_to_ping = False
        submitter_to_reping = False
        if not maintainer_waiting_on:
            submitter_waiting_on = True

        if missing_sections:
            submitter_waiting_on = True
            maintainer_waiting_on = False

        # use [!]needs_info to set final state
        if ni_commands:
            if ni_commands[-1] == '!needs_info':
                submitter_waiting_on = False
                maintainer_waiting_on = True
            elif ni_commands[-1] == 'needs_info':
                submitter_waiting_on = True
                maintainer_waiting_on = False

        # Time to [re]ping maintainer?
        maintainer_to_ping = False
        maintainer_to_reping = False
        if maintainer_waiting_on:

            # if feature idea, extend the notification interval
            interval = self.RENOTIFY_INTERVAL
            if self.meta.get('issue_type', None) == 'feature idea' \
                or 'feature_idea' in self.issue.current_labels:

                interval = self.FEATURE_RENOTIFY_INTERVAL

            if maintainer_viewed and not maintainer_last_notified:
                time_delta = today - maintainer_last_viewed
                view_age = time_delta.days
                if view_age > interval:
                    maintainer_to_reping = True
            elif maintainer_last_notified:
                time_delta = today - maintainer_last_notified
                ping_age = time_delta.days
                if ping_age > interval:
                    maintainer_to_reping = True
            else:
                maintainer_to_ping = True

        # Time to [re]ping the submitter?
        if submitter_waiting_on:
            if submitter_last_notified:
                time_delta = today - submitter_last_notified
                notification_age = time_delta.days
                if notification_age > self.RENOTIFY_INTERVAL:
                    submitter_to_reping = True
                else:
                    submitter_to_reping = False
                submitter_to_ping = False
            else:
                submitter_to_ping = True
                submitter_to_reping = False

        # needs_contributor ...
        hfacts['needs_contributor'] = False
        for command in maintainer_commands:
            if command == 'needs_contributor':
                hfacts['needs_contributor'] = True
            elif command == '!needs_contributor':
                hfacts['needs_contributor'] = False

        hfacts['bot_broken'] = bot_broken
        hfacts['bot_skip'] = bot_skip
        hfacts['missing_sections'] = missing_sections
        hfacts['was_needsinfo'] = was_needs_info
        hfacts['needsinfo_age'] = needsinfo_age
        hfacts['needsinfo_stale'] = needsinfo_stale
        hfacts['needsinfo_expired'] = needsinfo_expired
        hfacts['needsinfo_add'] = needsinfo_add
        hfacts['needsinfo_remove'] = needsinfo_remove
        hfacts['notification_maintainers'] = self.get_module_maintainers() or 'ansible'
        hfacts['maintainer_last_notified'] = maintainer_last_notified
        hfacts['maintainer_commented'] = maintainer_commented
        hfacts['maintainer_viewed'] = maintainer_viewed
        hfacts['maintainer_subscribed'] = maintainer_subscribed
        hfacts['maintainer_command_needsinfo'] = 'needs_info' in maintainer_commands
        hfacts['maintainer_command_not_needsinfo'] = '!needs_info' in maintainer_commands
        hfacts['maintainer_waiting_on'] = maintainer_waiting_on
        hfacts['maintainer_to_ping'] = maintainer_to_ping
        hfacts['maintainer_to_reping'] = maintainer_to_reping
        hfacts['submitter'] = submitter
        hfacts['submitter_waiting_on'] = submitter_waiting_on
        hfacts['submitter_to_ping'] = submitter_to_ping
        hfacts['submitter_to_reping'] = submitter_to_reping

        hfacts['last_commentor_ismaintainer'] = last_commentor_ismaintainer
        hfacts['last_commentor_issubmitter'] = last_commentor_issubmitter
        hfacts['last_commentor'] = last_commentor

        return hfacts
예제 #6
0
    def get_history_facts(self, usecache=True):

        hfacts = {}
        today = self.get_current_time()

        self.history = HistoryWrapper(self.issue,
                                      usecache=usecache,
                                      cachedir=self.cachedir)

        # what was the last commment?
        bot_broken = False
        if self.issue.current_comments:
            for comment in self.issue.current_comments:
                if 'bot_broken' in comment.body:
                    bot_broken = True

        # who made this and when did they last comment?
        submitter = self.issue.get_submitter()
        submitter_last_commented = self.history.last_commented_at(submitter)
        if not submitter_last_commented:
            submitter_last_commented = self.issue.instance.created_at
            #import epdb; epdb.st()
        submitter_last_comment = self.history.last_comment(submitter)
        submitter_last_notified = self.history.last_notified(submitter)

        # what did they not provide?
        missing_sections = self.issue.get_missing_sections()

        #if 'ansible version' in missing_sections:
        #    missing_sections.remove('ansible version')

        # DEBUG + FIXME - speeds up bulk triage
        if 'component name' in missing_sections \
            and (self.match or self.github_repo == 'ansible'):
            missing_sections.remove('component name')
        #import epdb; epdb.st()

        # Who are the maintainers?
        maintainers = [x for x in self.get_module_maintainers()]

        if 'ansible' in maintainers:
            maintainers.remove('ansible')
        maintainers.extend(self.ansible_members)
        if 'ansibot' in maintainers:
            maintainers.remove('ansibot')
        if submitter in maintainers:
            maintainers.remove(submitter)
        maintainers = sorted(set(maintainers))

        # Has maintainer been notified? When?
        notification_maintainers = self.get_module_maintainers()
        if 'ansible' in notification_maintainers:
            notification_maintainers.extend(self.ansible_members)
        if 'ansibot' in notification_maintainers:
            notification_maintainers.remove('ansibot')
        maintainer_last_notified = self.history.\
                    last_notified(notification_maintainers)

        # Has maintainer viewed issue?
        maintainer_viewed = self.history.has_viewed(maintainers)
        maintainer_last_viewed = self.history.last_viewed_at(maintainers)
        #import epdb; epdb.st()

        # Has maintainer been mentioned?
        maintainer_mentioned = self.history.is_mentioned(maintainers)

        # Has maintainer viewed issue?
        maintainer_viewed = self.history.has_viewed(maintainers)

        # Has the maintainer ever responded?
        maintainer_commented = self.history.has_commented(maintainers)
        maintainer_last_commented = self.history.last_commented_at(maintainers)
        maintainer_last_comment = self.history.last_comment(maintainers)
        maintainer_comments = self.history.get_user_comments(maintainers)
        #import epdb; epdb.st()

        # Was the maintainer the last commentor?
        last_commentor_ismaintainer = False
        last_commentor_issubmitter = False
        last_commentor = self.history.last_commentor()
        if last_commentor in maintainers and last_commentor != self.github_user:
            last_commentor_ismaintainer = True
        elif last_commentor == submitter:
            last_commentor_issubmitter = True

        # Did the maintainer issue a command?
        maintainer_commands = self.history.get_commands(
            maintainers, self.VALID_COMMANDS)
        # needs_info toggles
        ni_commands = [x for x in maintainer_commands if 'needs_info' in x]

        # Has the maintainer ever subscribed?
        maintainer_subscribed = self.history.has_subscribed(maintainers)

        # Was it ever needs_info?
        was_needs_info = self.history.was_labeled(label='needs_info')
        needsinfo_last_applied = self.history.label_last_applied('needs_info')
        needsinfo_last_removed = self.history.label_last_removed('needs_info')

        # Still needs_info?
        needsinfo_add = False
        needsinfo_remove = False

        if 'needs_info' in self.issue.current_labels:
            if not needsinfo_last_applied or not submitter_last_commented:
                import epdb
                epdb.st()
            if submitter_last_commented > needsinfo_last_applied:
                needsinfo_add = False
                needsinfo_remove = True

        #if 'needs_info' in maintainer_commands and maintainer_last_commented:
        if ni_commands and maintainer_last_commented:
            if ni_commands[-1] == 'needs_info':
                #import epdb; epdb.st()
                if submitter_last_commented and maintainer_last_commented:
                    if submitter_last_commented > maintainer_last_commented:
                        needsinfo_add = False
                        needsinfo_remove = True
                else:
                    needsinfo_add = True
                    needsinfo_remove = False
            else:
                needsinfo_add = False
                needsinfo_remove = True

        # Save existing needs_info if not time to remove ...
        if 'needs_info' in self.issue.current_labels \
            and not needsinfo_add \
            and not needsinfo_remove:
            needsinfo_add = True

        if ni_commands and maintainer_last_commented:
            if maintainer_last_commented > submitter_last_commented:
                if ni_commands[-1] == 'needs_info':
                    needsinfo_add = True
                    needsinfo_remove = False
                else:
                    needsinfo_add = False
                    needsinfo_remove = True
        #import epdb; epdb.st()

        # Is needs_info stale or expired?
        needsinfo_age = None
        needsinfo_stale = False
        needsinfo_expired = False
        if 'needs_info' in self.issue.current_labels:
            time_delta = today - needsinfo_last_applied
            needsinfo_age = time_delta.days
            if needsinfo_age > 14:
                needsinfo_stale = True
            if needsinfo_age > 56:
                needsinfo_expired = True

        # Should we be in waiting_on_maintainer mode?
        maintainer_waiting_on = False
        if (needsinfo_remove or not needsinfo_add) \
            or not was_needs_info \
            and not missing_sections:
            maintainer_waiting_on = True

        # Should we [re]notify the submitter?
        submitter_waiting_on = False
        submitter_to_ping = False
        submitter_to_reping = False
        if not maintainer_waiting_on:
            submitter_waiting_on = True

        if missing_sections:
            submitter_waiting_on = True
            maintainer_waiting_on = False

        # use [!]needs_info to set final state
        if ni_commands:
            if ni_commands[-1] == '!needs_info':
                submitter_waiting_on = False
                maintainer_waiting_on = True
            elif ni_commands[-1] == 'needs_info':
                submitter_waiting_on = True
                maintainer_waiting_on = False

        # Time to [re]ping maintainer?
        maintainer_to_ping = False
        maintainer_to_reping = False
        if maintainer_waiting_on:
            #import epdb; epdb.st()
            if maintainer_viewed and not maintainer_last_notified:
                time_delta = today - maintainer_last_viewed
                view_age = time_delta.days
                if view_age > 14:
                    maintainer_to_reping = True
            elif maintainer_last_notified:
                time_delta = today - maintainer_last_notified
                ping_age = time_delta.days
                if ping_age > 14:
                    maintainer_to_reping = True
            else:
                maintainer_to_ping = True

        # Time to [re]ping the submitter?
        if submitter_waiting_on:
            if submitter_last_notified:
                time_delta = today - submitter_last_notified
                notification_age = time_delta.days
                if notification_age > 14:
                    submitter_to_reping = True
                else:
                    submitter_to_reping = False
                submitter_to_ping = False
            else:
                submitter_to_ping = True
                submitter_to_reping = False

        hfacts['bot_broken'] = bot_broken
        hfacts['missing_sections'] = missing_sections
        hfacts['was_needsinfo'] = was_needs_info
        hfacts['needsinfo_age'] = needsinfo_age
        hfacts['needsinfo_stale'] = needsinfo_stale
        hfacts['needsinfo_expired'] = needsinfo_expired
        hfacts['needsinfo_add'] = needsinfo_add
        hfacts['needsinfo_remove'] = needsinfo_remove
        hfacts['notification_maintainers'] = self.get_module_maintainers(
        ) or 'ansible'
        hfacts['maintainer_last_notified'] = maintainer_last_notified
        hfacts['maintainer_commented'] = maintainer_commented
        hfacts['maintainer_viewed'] = maintainer_viewed
        hfacts['maintainer_subscribed'] = maintainer_subscribed
        hfacts[
            'maintainer_command_needsinfo'] = 'needs_info' in maintainer_commands
        hfacts[
            'maintainer_command_not_needsinfo'] = '!needs_info' in maintainer_commands
        hfacts['maintainer_waiting_on'] = maintainer_waiting_on
        hfacts['maintainer_to_ping'] = maintainer_to_ping
        hfacts['maintainer_to_reping'] = maintainer_to_reping
        hfacts['submitter'] = submitter
        hfacts['submitter_waiting_on'] = submitter_waiting_on
        hfacts['submitter_to_ping'] = submitter_to_ping
        hfacts['submitter_to_reping'] = submitter_to_reping

        hfacts['last_commentor_ismaintainer'] = last_commentor_ismaintainer
        hfacts['last_commentor_issubmitter'] = last_commentor_issubmitter
        hfacts['last_commentor'] = last_commentor

        #import epdb; epdb.st()
        return hfacts