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

mozilla / relman-auto-nag / #4472

pending completion
#4472

push

coveralls-python

web-flow
Send reminders on the day specified instead of the day after

640 of 3213 branches covered (19.92%)

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

1817 of 8008 relevant lines covered (22.69%)

0.23 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": ["assigned_to", "whiteboard", "triage_owner", "history"],
34
            "f1": "status_whiteboard",
35
            "o1": "substring",
36
            "v1": "[reminder-",
37
        }
38

39
        return params
×
40

41
    def handle_bug(self, bug, data):
×
42
        bugid = str(bug["id"])
×
43

44
        new_whiteboard = whiteboard = bug["whiteboard"]
×
45
        matches = DATE_MATCH.findall(whiteboard)
×
46

47
        # We support having multiple reminders that expire on a single day.
48
        # (I'm not sure why, but I guess we should.)
49
        reminders = []
×
50

51
        for m in matches:
×
52
            tag, date = m
×
53

54
            # If we can't parse the date, do a little hack to throw it back
55
            # at the user.
56
            try:
×
57
                parsed_date = lmdutils.get_date_ymd(date)
×
58
            except ParserError:
×
59
                replace_string = f"[reminder-{tag} {date}]"
×
60
                new_whiteboard = new_whiteboard.replace(replace_string, "")
×
61
                reminders.append({"full_tag": replace_string, "invalid_date": True})
×
62
                continue
×
63

64
            if parsed_date <= self.today:
×
65
                replace_string = f"[reminder-{tag} {date}]"
×
66
                new_whiteboard = new_whiteboard.replace(replace_string, "")
×
67
                reminders.append({"full_tag": replace_string})
×
68

69
        if new_whiteboard == whiteboard:
×
70
            return
×
71

72
        target_entries = []
×
73
        for entry in bug["history"]:
×
74
            for field in entry["changes"]:
×
75
                if field["field_name"] == "whiteboard":
×
76
                    # Check to see if any of the replace strings appeared in this change.
77
                    for r in reminders:
×
78
                        if (
×
79
                            r["full_tag"] in field["added"]
80
                            and r["full_tag"] not in field["removed"]
81
                        ):
82
                            entry["full_tag"] = r["full_tag"]
×
83
                            target_entries.append(entry)
×
84
                    break
×
85

86
        user_emails_to_names = self._get_user_emails_to_names(target_entries)
×
87

88
        for r in reminders:
×
89
            for entry in target_entries:
×
90
                if r["full_tag"] == entry["full_tag"]:
×
91
                    if user_emails_to_names[entry["who"]] == "Invalid User":
×
92
                        reminders.remove(r)
×
93
                    else:
94
                        r["who"] = user_emails_to_names[entry["who"]]
×
95
                        r["when"] = utils.get_human_lag(entry["when"])
×
96

97
        if not reminders:
×
98
            return
×
99

100
        data[bugid] = {
×
101
            "full_tags": ", ".join([r["full_tag"] for r in reminders]),
102
        }
103

104
        self.autofix_whiteboard[bugid] = {
×
105
            "whiteboard": new_whiteboard,
106
        }
107

108
        self.extra_ni[bugid] = {"reminders": reminders}
×
109

110
        return bug
×
111

112
    def _get_user_emails_to_names(self, target_entries):
×
113
        """
114
        Do a bunch of annoying stuff to translate bugzilla email addresses
115
        to nicknames, and then from a list of nicknames to a nicely formatted
116
        string.
117
        """
118

119
        # emails -> nicks
120
        def user_handler(user, data):
×
121
            data[user["name"]] = "Invalid User"
×
122
            for g in user["groups"]:
×
123
                if g["name"] == "editbugs" or g["name"] == "canconfirm":
×
124
                    data[user["name"]] = (
×
125
                        user["real_name"] or user["nick"] or user["name"]
126
                    )
127

128
        user_emails_to_names = {}
×
129
        BugzillaUser(
×
130
            user_names=[entry["who"] for entry in target_entries],
131
            include_fields=["real_name", "nick", "name", "groups"],
132
            user_handler=user_handler,
133
            user_data=user_emails_to_names,
134
        ).wait()
135

136
        return user_emails_to_names
×
137

138
    def get_extra_for_needinfo_template(self):
×
139
        return self.extra_ni
×
140

141
    def get_autofix_change(self):
×
142
        return self.autofix_whiteboard
×
143

144
    def columns(self):
×
145
        return ["id", "summary", "full_tags"]
×
146

147
    def get_mail_to_auto_ni(self, bug):
×
148
        for field in ["assigned_to", "triage_owner"]:
×
149
            person = bug.get(field, "")
×
150
            if person and not utils.is_no_assignee(person):
×
151
                return {"mail": person, "nickname": bug[f"{field}_detail"]["nick"]}
×
152

153
        return None
×
154

155

156
if __name__ == "__main__":
×
157
    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