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

mozilla / relman-auto-nag / #4993

09 May 2024 02:52PM CUT coverage: 21.817% (-0.008%) from 21.825%
#4993

push

coveralls-python

benjaminmah
Moved the email sending action in function `_update_triage_owners`

716 of 3592 branches covered (19.93%)

0 of 11 new or added lines in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

1928 of 8837 relevant lines covered (21.82%)

0.22 hits per line

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

0.0
/bugbot/rules/triage_owner_rotations.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

6
from typing import List, Set
×
7
from urllib.parse import quote_plus
×
8

9
from jinja2 import Environment, FileSystemLoader
×
10
from libmozdata.bugzilla import BugzillaComponent
×
11
from requests import HTTPError
×
12
from tenacity import (
×
13
    RetryError,
14
    retry,
15
    retry_if_exception_message,
16
    stop_after_attempt,
17
    wait_exponential,
18
)
19

20
from bugbot import logger, mail
×
21
from bugbot.bzcleaner import BzCleaner
×
22
from bugbot.component_triagers import ComponentName, ComponentTriagers, TriageOwner
×
23

24

25
class TriageOwnerRotations(BzCleaner):
×
26
    def __init__(
×
27
        self,
28
        excluded_teams: List[str] = [
29
            "Layout",
30
            "GFX",
31
        ],
32
    ) -> None:
33
        """Constructor
34

35
        Args:
36
            excluded_teams: teams to excluded all of their components when
37
                performing the triage owner rotation.
38
        """
39
        super().__init__()
×
40
        self.component_triagers = ComponentTriagers(excluded_teams=excluded_teams)
×
41
        self.query_url = None
×
42
        self.has_put_errors = False
×
43

44
    def description(self) -> str:
×
45
        return "Triage owners that got updated"
×
46

47
    def get_extra_for_template(self):
×
48
        return {"has_put_errors": self.has_put_errors}
×
49

50
    def _update_triage_owners(
×
51
        self, new_triage_owners: List[TriageOwner]
52
    ) -> Set[ComponentName]:
53
        failures = set()
×
54
        for new_triager in new_triage_owners:
×
55
            logger.info(
×
56
                "The triage owner for '%s' will be: '%s'",
57
                new_triager.component,
58
                new_triager.bugzilla_email,
59
            )
NEW
60
            error_occurred = False
×
61

62
            if not self.dryrun and not self.test_mode:
×
63
                try:
×
64
                    self._put_new_triage_owner(new_triager)
×
65
                except (HTTPError, RetryError) as err:
×
66
                    failures.add(new_triager.component)
×
NEW
67
                    error_occurred = True
×
UNCOV
68
                    logger.exception(
×
69
                        "Cannot update the triage owner for '%s' to be '%s': %s",
70
                        new_triager.component,
71
                        new_triager.bugzilla_email,
72
                        err,
73
                    )
74

NEW
75
            old_owner = self.component_triagers.get_current_triage_owner(
×
76
                new_triager.component
77
            )
NEW
78
            details_url = self.convert_to_url(str(new_triager.component))
×
79

NEW
80
            self.send_email_to_triage_owners(
×
81
                old_owner,
82
                new_triager.bugzilla_email,
83
                new_triager.component,
84
                details_url,
85
                error_occurred,
86
            )
87

UNCOV
88
        return failures
×
89

90
    @retry(
×
91
        retry=retry_if_exception_message(match=r"^\d{3} Server Error"),
92
        wait=wait_exponential(),
93
        stop=stop_after_attempt(3),
94
    )
95
    def _put_new_triage_owner(self, new_triager: TriageOwner) -> None:
×
96
        change = {"triage_owner": new_triager.bugzilla_email}
×
97
        BugzillaComponent(
×
98
            new_triager.component.product,
99
            new_triager.component.name,
100
        ).put(change)
101

102
    def get_email_data(self, date: str) -> List[dict]:
×
103
        new_triagers = self.component_triagers.get_new_triage_owners()
×
104
        failures = self._update_triage_owners(new_triagers)
×
105
        self.has_put_errors = len(failures) > 0
×
106

107
        email_data = []
×
108

109
        for new_triager in new_triagers:
×
110
            old_owner = self.component_triagers.get_current_triage_owner(
×
111
                new_triager.component
112
            )
113

114
            email_data.append(
×
115
                {
116
                    "component": new_triager.component,
117
                    "old_triage_owner": old_owner,
118
                    "new_triage_owner": new_triager.bugzilla_email,
119
                    "has_put_error": new_triager.component in failures,
120
                }
121
            )
122

123
        return email_data
×
124

NEW
125
    def send_email_to_triage_owners(
×
126
        self, old_email, new_email, component, details_url, error_occurred
127
    ):
128
        """Send an email to the old and new triage owners about the switch."""
129
        env = Environment(loader=FileSystemLoader("templates"))
×
130
        template = env.get_template("triage_owner_rotations_2.html")
×
131

132
        body = template.render(
×
133
            preamble="Triage Owner Update Notification",
134
            data=[
135
                {
136
                    "component": component,
137
                    "old_triage_owner": old_email,
138
                    "new_triage_owner": new_email,
139
                    "details_url": details_url,
140
                    "has_put_error": error_occurred,
141
                }
142
            ],
143
            table_attrs="",
144
        )
145

146
        subject = "Triage Owner Update"
×
147

148
        mail.send(
×
149
            From="xxx@xxxx.xxx",
150
            To=[old_email, new_email],
151
            Subject=subject,
152
            Body=body,
153
            html=True,
154
            dryrun=True,
155
        )
156

NEW
157
    def convert_to_url(self, component: str) -> str:
×
158
        # replace double colons with a single colon
NEW
159
        component = component.replace("::", ":")
×
160

NEW
161
        encoded = quote_plus(component)
×
162

NEW
163
        url = "https://bugdash.moz.tools/?component=" + encoded
×
164

NEW
165
        return url
×
166

167

168
if __name__ == "__main__":
×
169
    TriageOwnerRotations().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