You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by kn...@apache.org on 2021/05/19 08:09:47 UTC

[flink-jira-bot] 25/47: [hotfix] give rule classes more descriptive names and refactor to separate files

This is an automated email from the ASF dual-hosted git repository.

knaufk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-jira-bot.git

commit 425f75f6f56e35e3fe91a7f2804c02e26fd895f3
Author: Konstantin Knauf <kn...@gmail.com>
AuthorDate: Wed Apr 21 11:16:15 2021 +0200

    [hotfix] give rule classes more descriptive names and refactor to separate files
---
 .gitignore             |   1 +
 flink_jira_bot.py      | 253 ++-----------------------------------------------
 flink_jira_rule.py     | 103 ++++++++++++++++++++
 stale_assigned_rule.py | 101 ++++++++++++++++++++
 stale_minor_rule.py    | 101 ++++++++++++++++++++
 5 files changed, 312 insertions(+), 247 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4b8ee1e..2d85626 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@
 ################################################################################
 
 venv
+__pycache__
diff --git a/flink_jira_bot.py b/flink_jira_bot.py
index 8b46132..211f855 100644
--- a/flink_jira_bot.py
+++ b/flink_jira_bot.py
@@ -20,252 +20,11 @@ from atlassian import Jira
 import logging
 import confuse
 import os
-import abc
 from argparse import ArgumentParser
 from pathlib import Path
 
-
-class FlinkJiraRule:
-    __metaclass__ = abc.ABCMeta
-
-    def __init__(self, jira_client, config, is_dry_run):
-        self.jira_client = jira_client
-        self.config = config
-        self.is_dry_run = is_dry_run
-
-    def get_issues(self, jql_query):
-        """Queries the JIRA PI for all issues that match the given JQL Query
-
-        This method is necessary as requests tend to time out if the number of results reaches a certain number.
-        So, this method requests the results in multiple queries and returns a final list of all issues.
-        :param jql_query: the search query
-        :return: a list of issues matching the query
-        """
-        limit = 200
-        current = 0
-        total = 1
-        issues = []
-        while current < total:
-            response = self.jira_client.jql(jql_query, limit=limit, start=current)
-            total = response["total"]
-            issues = issues + response["issues"]
-            current = len(issues)
-        logging.info(f'"{jql_query}" returned {len(issues)} issues')
-        return issues
-
-    def has_recently_updated_subtask(self, parent, updated_within_days):
-        find_subtasks_updated_within = (
-            f"parent = {parent}  AND updated > startOfDay(-{updated_within_days}d)"
-        )
-        issues = self.get_issues(find_subtasks_updated_within)
-        return len(issues) > 0
-
-    def add_label(self, issue, label):
-        labels = issue["fields"]["labels"] + [label]
-        fields = {"labels": labels}
-        key = issue["key"]
-
-        if not self.is_dry_run:
-            self.jira_client.update_issue_field(key, fields)
-        else:
-            logging.info(f'DRY RUN ({key}): Adding label "{label}".')
-
-    def replace_label(self, issue, old_label, new_label):
-        labels = issue["fields"]["labels"] + [new_label]
-        labels.remove(old_label)
-        fields = {"labels": labels}
-        key = issue["key"]
-
-        if not self.is_dry_run:
-            self.jira_client.update_issue_field(key, fields)
-        else:
-            logging.info(
-                f'DRY RUN ({key}): Replace label "{old_label}" for "{new_label}".'
-            )
-
-    def add_comment(self, key, comment):
-        if not self.is_dry_run:
-            self.jira_client.issue_add_comment(key, comment)
-        else:
-            logging.info(f'DRY_RUN ({key}): Adding comment "{comment}".')
-
-    def close_issue(self, key):
-        if not self.is_dry_run:
-            self.jira_client.set_issue_status(
-                key, "Closed", fields={"resolution": {"name": "Auto Closed"}}
-            )
-        else:
-            logging.info(f"DRY_RUN (({key})): Closing.")
-
-    def unassign(self, key):
-        if not self.is_dry_run:
-            self.jira_client.assign_issue(key, None)
-        else:
-            logging.info(f"DRY_RUN (({key})): Unassigning.")
-
-    @abc.abstractmethod
-    def run(self):
-        return
-
-
-class Rule3(FlinkJiraRule):
-    """
-    An unresolved Minor ticket without an update for {stale_minor.stale_days} is closed after a warning period of
-    {stale_minor.warning_days} with a comment that encourages users to watch, comment and simply reopen with a higher
-    priority if the problem insists.
-    """
-
-    def __init__(self, jira_client, config, is_dry_run):
-        super().__init__(jira_client, config, is_dry_run)
-        self.stale_days = config["stale_minor"]["stale_days"].get()
-        self.warning_days = config["stale_minor"]["warning_days"].get()
-        self.warning_label = config["stale_minor"]["warning_label"].get()
-        self.done_label = config["stale_minor"]["done_label"].get()
-        self.done_comment = config["stale_minor"]["done_comment"].get()
-        self.warning_comment = config["stale_minor"]["warning_comment"].get()
-
-    def run(self):
-        self.close_tickets_marked_stale()
-        self.mark_stale_tickets_stale()
-
-    def close_tickets_marked_stale(self):
-
-        minor_tickets_marked_stale = (
-            f"project=FLINK AND Priority = Minor AND resolution = Unresolved AND labels in "
-            f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)'
-        )
-        logging.info(
-            f"Looking for minor tickets, which were previously marked as {self.warning_label}."
-        )
-        issues = self.get_issues(minor_tickets_marked_stale)
-
-        for issue in issues:
-            key = issue["key"]
-            logging.info(
-                f"Found https://issues.apache.org/jira/browse/{key}. It is now closed due to inactivity."
-            )
-
-            formatted_comment = self.done_comment.format(
-                warning_days=self.warning_days,
-                warning_label=self.warning_label,
-                done_label=self.done_label,
-            )
-
-            self.add_comment(key, formatted_comment)
-            self.replace_label(issue, self.warning_label, self.done_label)
-            self.close_issue(key)
-
-    def mark_stale_tickets_stale(self):
-
-        stale_minor_tickets = (
-            f"project = FLINK AND Priority = Minor AND resolution = Unresolved AND updated < "
-            f"startOfDay(-{self.stale_days}d)"
-        )
-        logging.info(f"Looking for minor tickets, which are stale.")
-        issues = self.get_issues(stale_minor_tickets)
-
-        for issue in issues:
-            key = issue["key"]
-            issue = self.jira_client.get_issue(key)
-
-            if not self.has_recently_updated_subtask(key, self.stale_days):
-                logging.info(
-                    f"Found https://issues.apache.org/jira/browse/{key}. It is marked stale now."
-                )
-                formatted_comment = self.warning_comment.format(
-                    stale_days=self.stale_days,
-                    warning_days=self.warning_days,
-                    warning_label=self.warning_label,
-                )
-
-                self.add_label(issue, self.warning_label)
-                self.add_comment(key, formatted_comment)
-
-            else:
-                logging.info(
-                    f"Found https://issues.apache.org/jira/browse/{key}, but is has recently updated Subtasks. "
-                    f"Ignoring for now."
-                )
-
-
-class Rule2(FlinkJiraRule):
-    """
-    Assigned tickets without an update for {stale_assigned.stale_days} are unassigned after a warning period of
-    {stale_assigned.warning_days}. Before this happens the assignee is notified that this is about to happen and
-    asked for an update on the status of her contribution.
-    """
-
-    def __init__(self, jira_client, config, is_dry_run):
-        super().__init__(jira_client, config, is_dry_run)
-        self.stale_days = config["stale_assigned"]["stale_days"].get()
-        self.warning_days = config["stale_assigned"]["warning_days"].get()
-        self.warning_label = config["stale_assigned"]["warning_label"].get()
-        self.done_label = config["stale_assigned"]["done_label"].get()
-        self.done_comment = config["stale_assigned"]["done_comment"].get()
-        self.warning_comment = config["stale_assigned"]["warning_comment"].get()
-
-    def run(self):
-        self.unassign_tickets_marked_stale()
-        self.mark_stale_tickets_stale()
-
-    def unassign_tickets_marked_stale(self):
-
-        assigned_tickets_marked_stale = (
-            f"project=FLINK AND resolution = Unresolved AND labels in "
-            f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)'
-        )
-        logging.info(
-            f"Looking for assigned tickets, which were previously marked as {self.warning_label}."
-        )
-        issues = self.get_issues(assigned_tickets_marked_stale)
-
-        for issue in issues:
-            key = issue["key"]
-            logging.info(
-                f"Found https://issues.apache.org/jira/browse/{key}. It is now unassigned due to inactivity."
-            )
-
-            formatted_comment = self.done_comment.format(
-                warning_days=self.warning_days,
-                warning_label=self.warning_label,
-                done_label=self.done_label,
-            )
-
-            self.add_comment(key, formatted_comment)
-            self.replace_label(issue, self.warning_label, self.done_label)
-            self.unassign(key)
-
-    def mark_stale_tickets_stale(self):
-
-        stale_assigned_tickets = (
-            f"project = FLINK AND resolution = Unresolved AND assignee is not EMPTY AND updated < "
-            f"startOfDay(-{self.stale_days}d)"
-        )
-        logging.info(f"Looking for assigned tickets, which are stale.")
-        issues = self.get_issues(stale_assigned_tickets)
-
-        for issue in issues:
-            key = issue["key"]
-            issue = self.jira_client.get_issue(key)
-
-            if not self.has_recently_updated_subtask(key, self.stale_days):
-                logging.info(
-                    f"Found https://issues.apache.org/jira/browse/{key}. It is marked stale now."
-                )
-                formatted_comment = self.warning_comment.format(
-                    stale_days=self.stale_days,
-                    warning_days=self.warning_days,
-                    warning_label=self.warning_label,
-                )
-
-                self.add_label(issue, self.warning_label)
-                self.add_comment(key, formatted_comment)
-
-            else:
-                logging.info(
-                    f"Found https://issues.apache.org/jira/browse/{key}, but is has recently updated Subtasks. "
-                    f"Ignoring for now."
-                )
+from stale_assigned_rule import StaleAssignedRule
+from stale_minor_rule import StaleMinorRule
 
 
 def get_args():
@@ -302,7 +61,7 @@ if __name__ == "__main__":
         password=os.environ["JIRA_PASSWORD"],
     )
 
-    rule_2 = Rule2(jira, jira_bot_config, args.dryrun)
-    rule_3 = Rule3(jira, jira_bot_config, args.dryrun)
-    rule_2.run()
-    rule_3.run()
+    stale_assigned_rule = StaleAssignedRule(jira, jira_bot_config, args.dryrun)
+    stale_minor_rule = StaleMinorRule(jira, jira_bot_config, args.dryrun)
+    stale_assigned_rule.run()
+    stale_minor_rule.run()
diff --git a/flink_jira_rule.py b/flink_jira_rule.py
new file mode 100644
index 0000000..9960aca
--- /dev/null
+++ b/flink_jira_rule.py
@@ -0,0 +1,103 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+import abc
+import logging
+
+
+class FlinkJiraRule:
+    __metaclass__ = abc.ABCMeta
+
+    def __init__(self, jira_client, config, is_dry_run):
+        self.jira_client = jira_client
+        self.config = config
+        self.is_dry_run = is_dry_run
+
+    def get_issues(self, jql_query):
+        """Queries the JIRA PI for all issues that match the given JQL Query
+
+        This method is necessary as requests tend to time out if the number of results reaches a certain number.
+        So, this method requests the results in multiple queries and returns a final list of all issues.
+        :param jql_query: the search query
+        :return: a list of issues matching the query
+        """
+        limit = 200
+        current = 0
+        total = 1
+        issues = []
+        while current < total:
+            response = self.jira_client.jql(jql_query, limit=limit, start=current)
+            total = response["total"]
+            issues = issues + response["issues"]
+            current = len(issues)
+        logging.info(f'"{jql_query}" returned {len(issues)} issues')
+        return issues
+
+    def has_recently_updated_subtask(self, parent, updated_within_days):
+        find_subtasks_updated_within = (
+            f"parent = {parent}  AND updated > startOfDay(-{updated_within_days}d)"
+        )
+        issues = self.get_issues(find_subtasks_updated_within)
+        return len(issues) > 0
+
+    def add_label(self, issue, label):
+        labels = issue["fields"]["labels"] + [label]
+        fields = {"labels": labels}
+        key = issue["key"]
+
+        if not self.is_dry_run:
+            self.jira_client.update_issue_field(key, fields)
+        else:
+            logging.info(f'DRY RUN ({key}): Adding label "{label}".')
+
+    def replace_label(self, issue, old_label, new_label):
+        labels = issue["fields"]["labels"] + [new_label]
+        labels.remove(old_label)
+        fields = {"labels": labels}
+        key = issue["key"]
+
+        if not self.is_dry_run:
+            self.jira_client.update_issue_field(key, fields)
+        else:
+            logging.info(
+                f'DRY RUN ({key}): Replace label "{old_label}" for "{new_label}".'
+            )
+
+    def add_comment(self, key, comment):
+        if not self.is_dry_run:
+            self.jira_client.issue_add_comment(key, comment)
+        else:
+            logging.info(f'DRY_RUN ({key}): Adding comment "{comment}".')
+
+    def close_issue(self, key):
+        if not self.is_dry_run:
+            self.jira_client.set_issue_status(
+                key, "Closed", fields={"resolution": {"name": "Auto Closed"}}
+            )
+        else:
+            logging.info(f"DRY_RUN (({key})): Closing.")
+
+    def unassign(self, key):
+        if not self.is_dry_run:
+            self.jira_client.assign_issue(key, None)
+        else:
+            logging.info(f"DRY_RUN (({key})): Unassigning.")
+
+    @abc.abstractmethod
+    def run(self):
+        return
diff --git a/stale_assigned_rule.py b/stale_assigned_rule.py
new file mode 100644
index 0000000..e781a9d
--- /dev/null
+++ b/stale_assigned_rule.py
@@ -0,0 +1,101 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+import logging
+
+from flink_jira_rule import FlinkJiraRule
+
+
+class StaleAssignedRule(FlinkJiraRule):
+    """
+    Assigned tickets without an update for {stale_assigned.stale_days} are unassigned after a warning period of
+    {stale_assigned.warning_days}. Before this happens the assignee is notified that this is about to happen and
+    asked for an update on the status of her contribution.
+    """
+
+    def __init__(self, jira_client, config, is_dry_run):
+        super().__init__(jira_client, config, is_dry_run)
+        self.stale_days = config["stale_assigned"]["stale_days"].get()
+        self.warning_days = config["stale_assigned"]["warning_days"].get()
+        self.warning_label = config["stale_assigned"]["warning_label"].get()
+        self.done_label = config["stale_assigned"]["done_label"].get()
+        self.done_comment = config["stale_assigned"]["done_comment"].get()
+        self.warning_comment = config["stale_assigned"]["warning_comment"].get()
+
+    def run(self):
+        self.unassign_tickets_marked_stale()
+        self.mark_stale_tickets_stale()
+
+    def unassign_tickets_marked_stale(self):
+
+        assigned_tickets_marked_stale = (
+            f"project=FLINK AND resolution = Unresolved AND labels in "
+            f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)'
+        )
+        logging.info(
+            f"Looking for assigned tickets, which were previously marked as {self.warning_label}."
+        )
+        issues = self.get_issues(assigned_tickets_marked_stale)
+
+        for issue in issues:
+            key = issue["key"]
+            logging.info(
+                f"Found https://issues.apache.org/jira/browse/{key}. It is now unassigned due to inactivity."
+            )
+
+            formatted_comment = self.done_comment.format(
+                warning_days=self.warning_days,
+                warning_label=self.warning_label,
+                done_label=self.done_label,
+            )
+
+            self.add_comment(key, formatted_comment)
+            self.replace_label(issue, self.warning_label, self.done_label)
+            self.unassign(key)
+
+    def mark_stale_tickets_stale(self):
+
+        stale_assigned_tickets = (
+            f"project = FLINK AND resolution = Unresolved AND assignee is not EMPTY AND updated < "
+            f"startOfDay(-{self.stale_days}d)"
+        )
+        logging.info(f"Looking for assigned tickets, which are stale.")
+        issues = self.get_issues(stale_assigned_tickets)
+
+        for issue in issues:
+            key = issue["key"]
+            issue = self.jira_client.get_issue(key)
+
+            if not self.has_recently_updated_subtask(key, self.stale_days):
+                logging.info(
+                    f"Found https://issues.apache.org/jira/browse/{key}. It is marked stale now."
+                )
+                formatted_comment = self.warning_comment.format(
+                    stale_days=self.stale_days,
+                    warning_days=self.warning_days,
+                    warning_label=self.warning_label,
+                )
+
+                self.add_label(issue, self.warning_label)
+                self.add_comment(key, formatted_comment)
+
+            else:
+                logging.info(
+                    f"Found https://issues.apache.org/jira/browse/{key}, but is has recently updated Subtasks. "
+                    f"Ignoring for now."
+                )
diff --git a/stale_minor_rule.py b/stale_minor_rule.py
new file mode 100644
index 0000000..78f7b8d
--- /dev/null
+++ b/stale_minor_rule.py
@@ -0,0 +1,101 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+import logging
+
+from flink_jira_rule import FlinkJiraRule
+
+
+class StaleMinorRule(FlinkJiraRule):
+    """
+    An unresolved Minor ticket without an update for {stale_minor.stale_days} is closed after a warning period of
+    {stale_minor.warning_days} with a comment that encourages users to watch, comment and simply reopen with a higher
+    priority if the problem insists.
+    """
+
+    def __init__(self, jira_client, config, is_dry_run):
+        super().__init__(jira_client, config, is_dry_run)
+        self.stale_days = config["stale_minor"]["stale_days"].get()
+        self.warning_days = config["stale_minor"]["warning_days"].get()
+        self.warning_label = config["stale_minor"]["warning_label"].get()
+        self.done_label = config["stale_minor"]["done_label"].get()
+        self.done_comment = config["stale_minor"]["done_comment"].get()
+        self.warning_comment = config["stale_minor"]["warning_comment"].get()
+
+    def run(self):
+        self.close_tickets_marked_stale()
+        self.mark_stale_tickets_stale()
+
+    def close_tickets_marked_stale(self):
+
+        minor_tickets_marked_stale = (
+            f"project=FLINK AND Priority = Minor AND resolution = Unresolved AND labels in "
+            f'("{self.warning_label}") AND updated < startOfDay(-{self.warning_days}d)'
+        )
+        logging.info(
+            f"Looking for minor tickets, which were previously marked as {self.warning_label}."
+        )
+        issues = self.get_issues(minor_tickets_marked_stale)
+
+        for issue in issues:
+            key = issue["key"]
+            logging.info(
+                f"Found https://issues.apache.org/jira/browse/{key}. It is now closed due to inactivity."
+            )
+
+            formatted_comment = self.done_comment.format(
+                warning_days=self.warning_days,
+                warning_label=self.warning_label,
+                done_label=self.done_label,
+            )
+
+            self.add_comment(key, formatted_comment)
+            self.replace_label(issue, self.warning_label, self.done_label)
+            self.close_issue(key)
+
+    def mark_stale_tickets_stale(self):
+
+        stale_minor_tickets = (
+            f"project = FLINK AND Priority = Minor AND resolution = Unresolved AND updated < "
+            f"startOfDay(-{self.stale_days}d)"
+        )
+        logging.info(f"Looking for minor tickets, which are stale.")
+        issues = self.get_issues(stale_minor_tickets)
+
+        for issue in issues:
+            key = issue["key"]
+            issue = self.jira_client.get_issue(key)
+
+            if not self.has_recently_updated_subtask(key, self.stale_days):
+                logging.info(
+                    f"Found https://issues.apache.org/jira/browse/{key}. It is marked stale now."
+                )
+                formatted_comment = self.warning_comment.format(
+                    stale_days=self.stale_days,
+                    warning_days=self.warning_days,
+                    warning_label=self.warning_label,
+                )
+
+                self.add_label(issue, self.warning_label)
+                self.add_comment(key, formatted_comment)
+
+            else:
+                logging.info(
+                    f"Found https://issues.apache.org/jira/browse/{key}, but is has recently updated Subtasks. "
+                    f"Ignoring for now."
+                )