• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / relman-auto-nag / #4634

pending completion
#4634

push

coveralls-python

web-flow
[reminder] Support adding reminders at the time of filing the bug (#2141)

646 of 3448 branches covered (18.74%)

2 of 2 new or added lines in 1 file covered. (100.0%)

1828 of 8554 relevant lines covered (21.37%)

0.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/bugbot/rules/reminder.py
1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
# You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
import re
×
6

7
from dateutil.parser import ParserError
×
8
from libmozdata import utils as lmdutils
×
9
from libmozdata.bugzilla import BugzillaUser
×
10

11
from bugbot import utils
×
12
from bugbot.bzcleaner import BzCleaner
×
13

14
REMINDER_TYPES = ["test", "pref", "disclosure", "deprecation"]
×
15
DATE_MATCH = re.compile(
×
16
    r"\[reminder\-(%s) ([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})\]" % "|".join(REMINDER_TYPES)
17
)
18

19

20
class Reminder(BzCleaner):
×
21
    def __init__(self):
×
22
        super(Reminder, self).__init__()
×
23
        self.extra_ni = {}
×
24
        self.autofix_whiteboard = {}
×
25

26
    def description(self):
×
27
        return "Bugs with whiteboard reminders"
×
28

29
    def get_bz_params(self, date):
×
30
        self.today = lmdutils.get_date_ymd(date)
×
31

32
        params = {
×
33
            "include_fields": [
34
                "assigned_to",
35
                "whiteboard",
36
                "triage_owner",
37
                "history",
38
                "creator",
39
                "creation_time",
40
            ],
41
            "f1": "status_whiteboard",
42
            "o1": "substring",
43
            "v1": "[reminder-",
44
        }
45

46
        return params
×
47

48
    def handle_bug(self, bug, data):
×
49
        bugid = str(bug["id"])
×
50

51
        new_whiteboard = whiteboard = bug["whiteboard"]
×
52
        matches = DATE_MATCH.findall(whiteboard)
×
53

54
        # We support having multiple reminders that expire on a single day.
55
        # (I'm not sure why, but I guess we should.)
56
        reminders = []
×
57

58
        for m in matches:
×
59
            tag, date = m
×
60

61
            # If we can't parse the date, do a little hack to throw it back
62
            # at the user.
63
            try:
×
64
                parsed_date = lmdutils.get_date_ymd(date)
×
65
            except ParserError:
×
66
                replace_string = f"[reminder-{tag} {date}]"
×
67
                new_whiteboard = new_whiteboard.replace(replace_string, "")
×
68
                reminders.append({"full_tag": replace_string, "invalid_date": True})
×
69
                continue
×
70

71
            if parsed_date <= self.today:
×
72
                replace_string = f"[reminder-{tag} {date}]"
×
73
                new_whiteboard = new_whiteboard.replace(replace_string, "")
×
74
                reminders.append({"full_tag": replace_string})
×
75

76
        if new_whiteboard == whiteboard:
×
77
            return
×
78

79
        target_entries = []
×
80
        for entry in bug["history"]:
×
81
            for field in entry["changes"]:
×
82
                if field["field_name"] == "whiteboard":
×
83
                    # Check to see if any of the replace strings appeared in this change.
84
                    for r in reminders:
×
85
                        if (
×
86
                            r["full_tag"] in field["added"]
87
                            and r["full_tag"] not in field["removed"]
88
                        ):
89
                            entry["full_tag"] = r["full_tag"]
×
90
                            target_entries.append(entry)
×
91
                    break
×
92

93
        if not target_entries:
×
94
            # If the history shows no changes, it indicates that the reminders
95
            # were added when the bug was filed.
96
            target_entries.extend(
×
97
                {
98
                    "who": bug["creator"],
99
                    "when": bug["creation_time"],
100
                    "full_tag": reminder["full_tag"],
101
                }
102
                for reminder in reminders
103
            )
104

105
        user_emails_to_names = self._get_user_emails_to_names(target_entries)
×
106

107
        for r in reminders:
×
108
            for entry in target_entries:
×
109
                if r["full_tag"] == entry["full_tag"]:
×
110
                    if user_emails_to_names[entry["who"]] == "Invalid User":
×
111
                        reminders.remove(r)
×
112
                    else:
113
                        r["who"] = user_emails_to_names[entry["who"]]
×
114
                        r["when"] = utils.get_human_lag(entry["when"])
×
115

116
        if not reminders:
×
117
            return
×
118

119
        data[bugid] = {
×
120
            "full_tags": ", ".join([r["full_tag"] for r in reminders]),
121
        }
122

123
        self.autofix_whiteboard[bugid] = {
×
124
            "whiteboard": new_whiteboard,
125
        }
126

127
        self.extra_ni[bugid] = {"reminders": reminders}
×
128

129
        return bug
×
130

131
    def _get_user_emails_to_names(self, target_entries):
×
132
        """
133
        Do a bunch of annoying stuff to translate bugzilla email addresses
134
        to nicknames, and then from a list of nicknames to a nicely formatted
135
        string.
136
        """
137

138
        # emails -> nicks
139
        def user_handler(user, data):
×
140
            data[user["name"]] = "Invalid User"
×
141
            for g in user["groups"]:
×
142
                if g["name"] == "editbugs" or g["name"] == "canconfirm":
×
143
                    data[user["name"]] = (
×
144
                        user["real_name"] or user["nick"] or user["name"]
145
                    )
146

147
        user_emails_to_names = {}
×
148
        BugzillaUser(
×
149
            user_names=[entry["who"] for entry in target_entries],
150
            include_fields=["real_name", "nick", "name", "groups"],
151
            user_handler=user_handler,
152
            user_data=user_emails_to_names,
153
        ).wait()
154

155
        return user_emails_to_names
×
156

157
    def get_extra_for_needinfo_template(self):
×
158
        return self.extra_ni
×
159

160
    def get_autofix_change(self):
×
161
        return self.autofix_whiteboard
×
162

163
    def columns(self):
×
164
        return ["id", "summary", "full_tags"]
×
165

166
    def get_mail_to_auto_ni(self, bug):
×
167
        for field in ["assigned_to", "triage_owner"]:
×
168
            person = bug.get(field, "")
×
169
            if person and not utils.is_no_assignee(person):
×
170
                return {"mail": person, "nickname": bug[f"{field}_detail"]["nick"]}
×
171

172
        return None
×
173

174

175
if __name__ == "__main__":
×
176
    Reminder().run()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc