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

mozilla / relman-auto-nag / #4077

pending completion
#4077

push

coveralls-python

suhaibmujahid
Merge remote-tracking branch 'upstream/master' into wiki-missed

549 of 3109 branches covered (17.66%)

615 of 615 new or added lines in 27 files covered. (100.0%)

1773 of 8016 relevant lines covered (22.12%)

0.22 hits per line

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

0.0
/auto_nag/scripts/vacant_triage_owner.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
×
7

8
from libmozdata.bugzilla import Bugzilla, BugzillaProduct
×
9

10
from auto_nag import utils
×
11
from auto_nag.bzcleaner import BzCleaner
×
12
from auto_nag.nag_me import Nag
×
13
from auto_nag.team_managers import TeamManagers
×
14
from auto_nag.user_activity import UserActivity
×
15

16

17
class TriageOwnerVacant(BzCleaner, Nag):
×
18
    def __init__(
×
19
        self,
20
        activity_weeks_count: int = 8,
21
        absent_weeks_count: int = 4,
22
    ):
23
        """Components with triage owner need to be assigned.
24

25
        Args:
26
            activity_weeks_count: the number of weeks of not doing any activity
27
                on Bugzilla before considering a triage owner as inactive.
28
            absent_weeks_count: number of weeks of not visiting Bugzilla before
29
                considering a triage owner as inactive.
30
        """
31
        super(TriageOwnerVacant, self).__init__()
×
32
        self.query_url = None
×
33
        self.activity_weeks_count = activity_weeks_count
×
34
        self.absent_weeks_count = absent_weeks_count
×
35

36
    def description(self):
×
37
        return "Components with triage owner need to be assigned"
×
38

39
    def fetch_products(self):
×
40
        data = []
×
41
        include_fields = [
×
42
            "name",
43
            "is_active",
44
            "components.id",
45
            "components.name",
46
            "components.team_name",
47
            "components.triage_owner",
48
            "components.is_active",
49
        ]
50

51
        def product_handler(product, data):
×
52
            data.append(product)
×
53

54
        BugzillaProduct(
×
55
            product_names=self.get_products(),
56
            include_fields=include_fields,
57
            product_handler=product_handler,
58
            product_data=data,
59
        ).wait()
60

61
        return data
×
62

63
    def nag_template(self):
×
64
        return self.template()
×
65

66
    def identify_vacant_components(self):
×
67
        # Filter out products and components that are not active
68
        products = [
×
69
            {
70
                **product,
71
                "components": [
72
                    component
73
                    for component in product["components"]
74
                    if component["is_active"]
75
                ],
76
            }
77
            for product in self.fetch_products()
78
            if product["is_active"]
79
        ]
80

81
        triage_owners = set()
×
82
        for product in products:
×
83
            for component in product["components"]:
×
84
                triage_owners.add(component["triage_owner"])
×
85

86
        user_activity = UserActivity(self.activity_weeks_count, self.absent_weeks_count)
×
87
        inactive_users = user_activity.check_users(triage_owners)
×
88

89
        team_managers = TeamManagers()
×
90
        vacant_components = []
×
91
        for product in products:
×
92
            for component in product["components"]:
×
93
                triage_owner = component["triage_owner"]
×
94
                if triage_owner not in inactive_users:
×
95
                    continue
×
96

97
                manager = team_managers.get_team_manager(component["team_name"])
×
98

99
                info = {
×
100
                    "id": component["id"],
101
                    "manager": manager["name"],
102
                    "team": component["team_name"],
103
                    "product": product["name"],
104
                    "component": component["name"],
105
                    "triage_owner": triage_owner,
106
                    "status": user_activity.get_string_status(
107
                        inactive_users[triage_owner]["status"]
108
                    ),
109
                }
110

111
                vacant_components.append(info)
×
112
                self.add(manager["mozilla_email"], info)
×
113

114
        return vacant_components
×
115

116
    def populate_num_untriaged_bugs(self, components: List[dict]) -> None:
×
117
        """Add the number of untriaged bugs and the URL to its search query to
118
        the provided components.
119

120
        The method will mutate the provided dictionaries to add the results.
121
        """
122

123
        def handler(bug, data):
×
124
            data["untriaged_bugs"] += 1
×
125

126
        queries = [None] * len(components)
×
127
        for i, component in enumerate(components):
×
128
            params = {
×
129
                "resolution": "---",
130
                "severity": "--",
131
                "bug_type": "defect",
132
                "product": component["product"],
133
                "component": component["component"],
134
            }
135
            component["untriaged_bugs"] = 0
×
136
            component["untriaged_bugs_url"] = utils.get_bz_search_url(params)
×
137
            query = Bugzilla(
×
138
                params,
139
                bughandler=handler,
140
                include_fields="-",
141
                bugdata=component,
142
            ).get_data()
143

144
            queries[i] = query
×
145

146
        # Since we query Bugzilla concurrently, we need to wait for the results
147
        # form all queries.
148
        for query in queries:
×
149
            query.wait()
×
150

151
    def get_email_data(self, date: str) -> List[dict]:
×
152
        components = self.identify_vacant_components()
×
153
        self.populate_num_untriaged_bugs(components)
×
154
        return components
×
155

156
    def organize_nag(self, data):
×
157
        return data
×
158

159
    def get_cc(self):
×
160
        return set()
×
161

162

163
if __name__ == "__main__":
×
164
    TriageOwnerVacant().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