def __init__(self, cal, fallback, team_name, people=None): super(Calendar, self).__init__() self.cal = cal self.people = People() if people is None else people self.fallback = fallback self.fb_bzmail = self.people.get_bzmail_from_name(self.fallback) self.fb_mozmail = self.people.get_moz_mail(self.fb_bzmail) self.team_name = team_name self.cache = {}
def __init__(self): super(Nag, self).__init__() self.people = People() self.send_nag_mail = True self.data = {} self.nag_date = None self.white_list = [] self.black_list = [] self.escalation = Escalation(self.people) self.triage_owners = {} self.all_owners = None self.query_params = {}
def __init__(self, rr=None, people=None, teams=None): self.people = People.get_instance() if people is None else people self.components_by_triager = {} self.all_calendars = [] self.feed(teams, rr=rr) self.nicks = {} self.erroneous_bzmail = {} utils.init_random()
def __init__(self): super(CodeFreezeWeek, self).__init__() self.versions = utils.get_checked_versions() if not self.versions: return self.people = People() self.nightly = self.versions['central'] self.beta = self.versions['beta'] self.release = self.versions['release'] self.status_nightly = utils.get_flag(self.nightly, 'status', 'central') self.status_beta = utils.get_flag(self.beta, 'status', 'beta') self.status_release = utils.get_flag(self.release, 'status', 'release') self.tracking_nightly = utils.get_flag(self.nightly, 'tracking', 'central') self.tracking_beta = utils.get_flag(self.beta, 'tracking', 'beta') self.tracking_release = utils.get_flag(self.release, 'tracking', 'release')
class LeaveOpenNoActivity(BzCleaner): def __init__(self): super(LeaveOpenNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.blacklist = set(utils.get_config(self.name(), 'blacklist', [])) def description(self): return 'Bugs with leave-open keyword and no activity for the last {} months'.format( self.nmonths ) def get_extra_for_needinfo_template(self): return self.get_extra_for_template() def get_extra_for_template(self): return {'nmonths': self.nmonths} def get_auto_ni_blacklist(self): return self.blacklist def get_max_ni(self): return self.max_ni def get_mail_to_auto_ni(self, bug): for f in ['assigned_to', 'triage_owner']: person = bug.get(f, '') if person and self.people.is_mozilla(person): return {'mail': person, 'nickname': bug[f + '_detail']['nick']} return None def get_bz_params(self, date): fields = ['assigned_to', 'triage_owner'] params = { 'include_fields': fields, 'resolution': '---', 'f1': 'keywords', 'o1': 'casesubstring', 'v1': 'leave-open', 'f2': 'keywords', 'o2': 'notsubstring', 'v2': 'intermittent', 'f3': 'status_whiteboard', 'o3': 'notregexp', 'v3': r'\[(test|stockwell) disabled.*\]', 'f4': 'days_elapsed', 'o4': 'greaterthan', 'v4': self.nmonths * 30, } return params
class LeaveOpenNoActivity(BzCleaner): def __init__(self): super(LeaveOpenNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.skiplist = set(utils.get_config(self.name(), 'skiplist', [])) def description(self): return 'Bugs with leave-open keyword and no activity for the last {} months'.format( self.nmonths) def get_extra_for_needinfo_template(self): return self.get_extra_for_template() def get_extra_for_template(self): return {'nmonths': self.nmonths} def get_auto_ni_skiplist(self): return self.skiplist def get_max_ni(self): return self.max_ni def get_mail_to_auto_ni(self, bug): for f in ['assigned_to', 'triage_owner']: person = bug.get(f, '') if person and self.people.is_mozilla(person): return {'mail': person, 'nickname': bug[f + '_detail']['nick']} return None def get_bz_params(self, date): fields = ['assigned_to', 'triage_owner'] params = { 'include_fields': fields, 'resolution': '---', 'f1': 'keywords', 'o1': 'casesubstring', 'v1': 'leave-open', 'f2': 'keywords', 'o2': 'notsubstring', 'v2': 'intermittent', 'f3': 'status_whiteboard', 'o3': 'notregexp', 'v3': r'\[(test|stockwell) disabled.*\]', 'f4': 'days_elapsed', 'o4': 'greaterthan', 'v4': self.nmonths * 30, } return params
class MetaNoDepsNoActivity(BzCleaner): def __init__(self): super(MetaNoDepsNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.blacklist = set(utils.get_config(self.name(), 'blacklist', [])) def description(self): return 'Bugs with meta keyword, not depending on bugs and no activity for the last {} months'.format( self.nmonths ) def get_extra_for_needinfo_template(self): return self.get_extra_for_template() def get_extra_for_template(self): return {'nmonths': self.nmonths} def get_auto_ni_blacklist(self): return self.blacklist def get_max_ni(self): return self.max_ni def get_mail_to_auto_ni(self, bug): for f in ['assigned_to', 'triage_owner']: person = bug.get(f, '') if person and self.people.is_mozilla(person): return {'mail': person, 'nickname': bug[f + '_detail']['nick']} return None def get_bz_params(self, date): fields = ['assigned_to', 'triage_owner'] params = { 'include_fields': fields, 'resolution': '---', 'f1': 'keywords', 'o1': 'casesubstring', 'v1': 'meta', 'f2': 'days_elapsed', 'o2': 'greaterthan', 'v2': self.nmonths * 30, 'f3': 'dependson', 'o3': 'isempty', } return params
class MetaNoDepsNoActivity(BzCleaner): def __init__(self): super(MetaNoDepsNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.skiplist = set(utils.get_config(self.name(), 'skiplist', [])) def description(self): return 'Bugs with meta keyword, not depending on bugs and no activity for the last {} months'.format( self.nmonths) def get_extra_for_needinfo_template(self): return self.get_extra_for_template() def get_extra_for_template(self): return {'nmonths': self.nmonths} def get_auto_ni_skiplist(self): return self.skiplist def get_max_ni(self): return self.max_ni def get_mail_to_auto_ni(self, bug): for f in ['assigned_to', 'triage_owner']: person = bug.get(f, '') if person and self.people.is_mozilla(person): return {'mail': person, 'nickname': bug[f + '_detail']['nick']} return None def get_bz_params(self, date): fields = ['assigned_to', 'triage_owner'] params = { 'include_fields': fields, 'resolution': '---', 'f1': 'keywords', 'o1': 'casesubstring', 'v1': 'meta', 'f2': 'days_elapsed', 'o2': 'greaterthan', 'v2': self.nmonths * 30, 'f3': 'dependson', 'o3': 'isempty', } return params
def __init__(self): super(CodeFreezeWeek, self).__init__() if not self.init_versions(): return self.people = People.get_instance() self.nightly = self.versions["central"] self.beta = self.versions["beta"] self.release = self.versions["release"] self.status_nightly = utils.get_flag(self.nightly, "status", "central") self.status_beta = utils.get_flag(self.beta, "status", "beta") self.status_release = utils.get_flag(self.release, "status", "release") self.tracking_nightly = utils.get_flag(self.nightly, "tracking", "central") self.tracking_beta = utils.get_flag(self.beta, "tracking", "beta") self.tracking_release = utils.get_flag(self.release, "tracking", "release")
class Nag(object): def __init__(self): super(Nag, self).__init__() self.people = People() self.send_nag_mail = True self.data = {} self.nag_date = None self.white_list = [] self.black_list = [] self.escalation = Escalation(self.people) self.triage_owners = {} self.all_owners = None self.query_params = {} @staticmethod def get_from(): return utils.get_config('auto_nag', 'from', '*****@*****.**') def get_cc(self): cc = self.get_config('cc', None) if cc is None: cc = utils.get_config('auto_nag', 'cc', []) return set(cc) def get_priority(self, bug): tracking = bug[self.tracking] if tracking == 'blocking': return 'high' return 'normal' def filter_bug(self, priority): days = (utils.get_next_release_date() - self.nag_date).days weekday = self.nag_date.weekday() return self.escalation.filter(priority, days, weekday) def get_people(self): return self.people def set_people_to_nag(self, bug, buginfo): return bug def escalate(self, person, priority, **kwargs): days = (utils.get_next_release_date() - self.nag_date).days return self.escalation.get_supervisor(priority, days, person, **kwargs) def add(self, person, bug_data, priority='default', **kwargs): if not self.people.is_mozilla(person): return False manager = self.escalate(person, priority, **kwargs) return self.add_couple(person, manager, bug_data) def add_couple(self, person, manager, bug_data): person = self.people.get_moz_mail(person) if manager in self.data: data = self.data[manager] else: self.data[manager] = data = {} if person in data: data[person].append(bug_data) else: data[person] = [bug_data] return True def nag_template(self): return self.name() + '_nag.html' def get_extra_for_nag_template(self): return {} def columns_nag(self): return None def sort_columns_nag(self): return None def _is_in_list(self, mail, _list): for manager in _list: if self.people.is_under(mail, manager): return True return False def is_under(self, mail): if not self.white_list: if not self.black_list: return True return not self._is_in_list(mail, self.black_list) if not self.black_list: return self._is_in_list(mail, self.white_list) return self._is_in_list(mail, self.white_list) and not self._is_in_list( mail, self.black_list ) def add_triage_owner(self, owner, real_owner=None): if owner not in self.triage_owners: to = real_owner if real_owner is not None else owner self.triage_owners[owner] = self.get_query_url_for_triage_owner(to) def get_query_url_for_triage_owner(self, owner): if self.all_owners is None: self.all_owners = utils.get_triage_owners() params = copy.deepcopy(self.query_params) if 'include_fields' in params: del params['include_fields'] comps = self.all_owners[owner] comps = set(comps) params['component'] = sorted(comps) url = utils.get_bz_search_url(params) return url def organize_nag(self, bugs): columns = self.columns_nag() if columns is None: columns = self.columns() key = self.sort_columns_nag() if key is None: key = self.sort_columns() return utils.organize(bugs, columns, key=key) def send_mails(self, title, dryrun=False): if not self.send_nag_mail: return env = Environment(loader=FileSystemLoader('templates')) common = env.get_template('common.html') login_info = utils.get_login_info() From = Nag.get_from() Default_Cc = self.get_cc() mails = self.prepare_mails() for m in mails: Cc = Default_Cc.copy() if m['manager']: Cc.add(m['manager']) body = common.render(message=m['body'], query_url=None) receivers = set(m['to']) | set(Cc) status = 'Success' try: mail.send( From, sorted(m['to']), title, body, Cc=sorted(Cc), html=True, login=login_info, dryrun=dryrun, ) except: # NOQA logger.exception('Tool {}'.format(self.name())) status = 'Failure' db.Email.add(self.name(), receivers, 'individual', status) def prepare_mails(self): if not self.data: return [] template = self.nag_template() if not template: return [] extra = self.get_extra_for_nag_template() env = Environment(loader=FileSystemLoader('templates')) template = env.get_template(template) mails = [] for manager, info in self.data.items(): data = [] To = sorted(info.keys()) for person in To: bug_data = info[person] data += bug_data if len(To) == 1 and To[0] in self.triage_owners: query_url = self.triage_owners[To[0]] else: query_url = None body = template.render( date=self.nag_date, extra=extra, plural=utils.plural, enumerate=enumerate, data=self.organize_nag(data), nag=True, query_url_nag=query_url, table_attrs=self.get_config('table_attrs'), ) m = {'manager': manager, 'to': set(info.keys()), 'body': body} mails.append(m) return mails def reorganize_to_bag(self, data): return data
class Calendar(object): def __init__(self, cal, fallback, team_name, people=None): super(Calendar, self).__init__() self.cal = cal self.people = People() if people is None else people self.fallback = fallback self.fb_bzmail = self.people.get_bzmail_from_name(self.fallback) self.fb_mozmail = self.people.get_moz_mail(self.fb_bzmail) self.team_name = team_name self.cache = {} def get_fallback(self): return self.fallback def get_fallback_bzmail(self): if not self.fb_bzmail: raise BadFallback('\'{}\' is an invalid fallback'.format( self.fallback)) return self.fb_bzmail def get_fallback_mozmail(self): if not self.fb_mozmail: raise BadFallback('\'{}\' is an invalid fallback'.format( self.fallback)) return self.fb_mozmail def get_team_name(self): return self.team_name def get_persons(self, date): return [] def set_team(self, team, triagers): self.team = [] for p in team: if p in triagers and 'bzmail' in triagers[p]: bzmail = triagers[p]['bzmail'] else: bzmail = self.people.get_bzmail_from_name(p) self.team.append((p, bzmail)) @staticmethod def get(url, fallback, team_name, people=None): data = None if url.startswith('private://'): name = url.split('//', 1)[1] url = utils.get_private()[name] if url.startswith('http'): r = requests.get(url) data = r.text elif os.path.isfile(url): with open(url, 'r') as In: data = In.read() else: data = url if data is None: raise InvalidCalendar('Cannot read calendar: {}'.format(url)) try: cal = json.loads(data) return JSONCalendar(cal, fallback, team_name, people=people) except JSONDecodeError: try: # there is an issue with dateutil.rrule parser when until doesn't have a tz # so a workaround is to add a Z at the end of the string. pat = re.compile(r'^RRULE:(.*)UNTIL=([0-9Z]+)', re.MULTILINE | re.I) def sub(m): date = m.group(1) if date.lower().endswith('z'): return date return date + 'Z' data = pat.sub(sub, data) return ICSCalendar(data, fallback, team_name, people=people) except ValueError: raise InvalidCalendar('Cannot decode calendar: {}'.format(url))
def __init__(self, rr=None, people=None): self.feed(rr=rr) self.nicks = {} self.people = People() if people is None else people
class RoundRobin(object): def __init__(self, rr=None, people=None): self.feed(rr=rr) self.nicks = {} self.people = People() if people is None else people def feed(self, rr=None): self.data = {} filenames = {} if rr is None: rr = {} for team, path in utils.get_config('round-robin', "teams", default={}).items(): with open('./auto_nag/scripts/configs/{}'.format(path), 'r') as In: rr[team] = json.load(In) filenames[team] = path # rr is dictionary: # - doc -> documentation # - triagers -> dictionary: Real Name -> {bzmail: bugzilla email, nick: bugzilla nickname} # - components -> dictionary: Product::Component -> strategy name # - strategies: dictionay: {duty en date -> Real Name} # Get all the strategies for each team for team, data in rr.items(): if 'doc' in data: del data['doc'] strategies = {} triagers = data['triagers'] fallback_bzmail = triagers['Fallback']['bzmail'] path = filenames.get(team, '') # collect strategies for pc, strategy in data['components'].items(): strategy_data = data[strategy] if strategy not in strategies: strategies[strategy] = strategy_data # rewrite strategy to have a sorted list of end dates for strat_name, strategy in strategies.items(): if 'doc' in strategy: del strategy['doc'] date_name = [] # end date and real name of the triager for date, name in strategy.items(): # collect the tuple (end_date, bzmail) date = lmdutils.get_date_ymd(date) bzmail = triagers[name]['bzmail'] date_name.append((date, bzmail)) # we sort the list to use bisection to find the triager date_name = sorted(date_name) strategies[strat_name] = { 'dates': [d for d, _ in date_name], 'mails': [m for _, m in date_name], 'fallback': fallback_bzmail, 'filename': path, } # finally self.data is a dictionary: # - Product::Component -> dictionary {dates: sorted list of end date # mails: list # fallback: who to nag when we've nobody # filename: the file containing strategy} for pc, strategy in data['components'].items(): self.data[pc] = strategies[strategy] def get_nick(self, bzmail): if bzmail not in self.nicks: def handler(user): self.nicks[bzmail] = user['nick'] BugzillaUser(user_names=[bzmail], user_handler=handler).wait() return self.nicks[bzmail] def is_mozilla(self, bzmail): return self.people.is_mozilla(bzmail) def get(self, bug, date): pc = '{}::{}'.format(bug['product'], bug['component']) if pc not in self.data: mail = bug['triage_owner'] nick = bug['triage_owner_detail']['nick'] return mail, nick date = lmdutils.get_date_ymd(date) strategy = self.data[pc] dates = strategy['dates'] i = bisect.bisect_left(strategy['dates'], date) if i == len(dates): bzmail = strategy['fallback'] else: bzmail = strategy['mails'][i] nick = self.get_nick(bzmail) return bzmail, nick def get_who_to_nag(self, date): fallbacks = {} date = lmdutils.get_date_ymd(date) days = utils.get_config('round-robin', 'days_to_nag', 7) for pc, strategy in self.data.items(): last_date = strategy['dates'][-1] if (last_date - date ).days <= days and strategy['filename'] not in fallbacks: fallbacks[strategy['filename']] = strategy['fallback'] # create a dict: mozmail -> list of filenames to check res = {} for fn, fb in fallbacks.items(): if not self.is_mozilla(fb): raise BadFallback() mozmail = self.people.get_moz_mail(fb) if mozmail not in res: res[mozmail] = [] res[mozmail].append(fn) res = {fb: sorted(fn) for fb, fn in res.items()} return res
class Calendar(object): def __init__(self, cal, fallback, team_name, people=None): super(Calendar, self).__init__() self.cal = cal self.people = People() if people is None else people self.fallback = fallback self.fb_bzmail = self.people.get_bzmail_from_name(self.fallback) self.fb_mozmail = self.people.get_moz_mail(self.fb_bzmail) self.team_name = team_name self.cache = {} def get_fallback(self): return self.fallback def get_fallback_bzmail(self): if not self.fb_bzmail: raise BadFallback('\'{}\' is an invalid fallback'.format(self.fallback)) return self.fb_bzmail def get_fallback_mozmail(self): if not self.fb_mozmail: raise BadFallback('\'{}\' is an invalid fallback'.format(self.fallback)) return self.fb_mozmail def get_team_name(self): return self.team_name def get_persons(self, date): return [] def set_team(self, team, triagers): self.team = [] for p in team: if p in triagers and 'bzmail' in triagers[p]: bzmail = triagers[p]['bzmail'] else: bzmail = self.people.get_bzmail_from_name(p) self.team.append((p, bzmail)) @staticmethod def get(url, fallback, team_name, people=None): data = None if url.startswith('private://'): name = url.split('//', 1)[1] url = utils.get_private()[name] if url.startswith('http'): r = requests.get(url) data = r.text elif os.path.isfile(url): with open(url, 'r') as In: data = In.read() else: data = url if data is None: raise InvalidCalendar('Cannot read calendar: {}'.format(url)) try: cal = json.loads(data) return JSONCalendar(cal, fallback, team_name, people=people) except JSONDecodeError: try: # there is an issue with dateutil.rrule parser when until doesn't have a tz # so a workaround is to add a Z at the end of the string. pat = re.compile(r'^RRULE:(.*)UNTIL=([0-9Z]+)', re.MULTILINE | re.I) def sub(m): date = m.group(1) if date.lower().endswith('z'): return date return date + 'Z' data = pat.sub(sub, data) return ICSCalendar(data, fallback, team_name, people=people) except ValueError: raise InvalidCalendar('Cannot decode calendar: {}'.format(url))
def __init__(self): super(NewbieWithNI, self).__init__() self.people = People() self.ndays = utils.get_config(self.name(), 'number_of_days', 14) self.ncomments = utils.get_config(self.name(), 'number_of_comments', 2) self.autofix_reporters = {}
def __init__(self): super(MetaNoDepsNoActivity, self).__init__() self.people = People.get_instance() self.nmonths = utils.get_config(self.name(), "months_lookup") self.max_ni = utils.get_config(self.name(), "max_ni") self.skiplist = set(utils.get_config(self.name(), "skiplist", []))
def test_escalation(self): people = [ { "mail": "*****@*****.**", "cn": "A B", "ismanager": "FALSE", "manager": { "dn": "[email protected],o=org,dc=mozilla" }, "title": "nothing", }, { "mail": "*****@*****.**", "cn": "C D", "ismanager": "TRUE", "manager": { "dn": "[email protected],o=org,dc=mozilla" }, "title": "manager", }, { "mail": "*****@*****.**", "cn": "E F", "ismanager": "TRUE", "manager": { "dn": "[email protected],o=org,dc=mozilla" }, "title": "super manager", }, { "mail": "*****@*****.**", "cn": "G H", "ismanager": "TRUE", "manager": { "dn": "[email protected],o=org,dc=mozilla" }, "title": "super manager", }, { "mail": "*****@*****.**", "cn": "I J", "ismanager": "TRUE", "manager": { "dn": "[email protected],o=org,dc=mozilla" }, "title": "director", }, { "mail": "*****@*****.**", "cn": "K L", "ismanager": "TRUE", "title": "vice president", }, ] e = Escalation(People(people), data=TestEscalation.config) assert (e.get_supervisor( "high", 35, "*****@*****.**", foobar="*****@*****.**") == "*****@*****.**") assert e.get_supervisor("high", 25, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("high", 20, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("high", 18, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("high", 7, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("high", 1, "*****@*****.**") == "*****@*****.**" assert e.filter("high", 25, 0) is False assert e.filter("high", 25, 3) is True assert e.filter("high", 18, 0) is True assert e.filter("high", 18, 1) is False assert e.filter("high", 18, 3) is True assert e.filter("high", 18, 5) is False assert e.filter("high", 7, 1) is False assert e.filter("high", 7, 3) is True assert e.filter("high", 7, 5) is False assert e.filter("high", 1, 1) is True assert e.filter("high", 1, 3) is True assert e.filter("high", 1, 4) is True assert e.filter("high", 7, 5) is False assert e.get_supervisor("normal", 17, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("normal", 15, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("normal", 12, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("normal", 7, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("normal", 1, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("default", 17, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("default", 7, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("default", 1, "*****@*****.**") == "*****@*****.**" assert e.get_supervisor("default", 0, "*****@*****.**") == "*****@*****.**"
class CodeFreezeWeek(BzCleaner): def __init__(self): super(CodeFreezeWeek, self).__init__() self.versions = utils.get_checked_versions() if not self.versions: return self.people = People() self.nightly = self.versions['central'] self.beta = self.versions['beta'] self.release = self.versions['release'] self.status_nightly = utils.get_flag(self.nightly, 'status', 'central') self.status_beta = utils.get_flag(self.beta, 'status', 'beta') self.status_release = utils.get_flag(self.release, 'status', 'release') self.tracking_nightly = utils.get_flag(self.nightly, 'tracking', 'central') self.tracking_beta = utils.get_flag(self.beta, 'tracking', 'beta') self.tracking_release = utils.get_flag(self.release, 'tracking', 'release') def description(self): return 'Bugs with patches which landed during the soft freeze week' def name(self): return 'code-freeze-week' def template(self): return 'code_freeze_week.html' def subject(self): return self.description() def get_extra_for_template(self): return { 'nightly': self.nightly, 'beta': self.beta, 'release': self.release, 'date': lmdutils.get_date_str(self.date), } def filter_no_nag_keyword(self): return False def has_enough_data(self): return bool(self.versions) def must_run(self, date): for c in get_calendar(): # if freeze is the 2019-03-11, then the tool must run the day after # until 2019-03-2018 (a week after) freeze = c['soft freeze'] if freeze <= date <= freeze + relativedelta(days=6): return True return False def has_product_component(self): return True def columns(self): return [ 'id', 'summary', 'product', 'component', 'assignee', 'landed_patches', 'addlines', 'rmlines', 'size', 'test_size', 'priority', 'severity', 'tracking', 'status', 'status_flags', 'crash', 'keywords', ] def sort_columns(self): return lambda p: (p[2], p[3], -p[5], -p[8], -p[9], -int(p[0])) def handle_bug(self, bug, data): bugid = str(bug['id']) assignee = bug.get('assigned_to', '') if assignee: info = self.people.get_info(assignee) if info: assignee = info['cn'] else: name = bug.get('assigned_to_detail', {}).get('real_name', '') if name: assignee = utils.get_better_name(name) else: assignee = 'Nobody' isacrash = len(utils.get_signatures(bug.get('cf_crash_signature', ''))) != 0 data[bugid] = { 'land': {}, 'assignee': assignee, 'crash': 'Yes' if isacrash else 'No', 'priority': bug['priority'], 'severity': bug['severity'], 'tracking': bug[self.tracking_nightly], 'status': bug['status'].lower(), 'status_flags': { self.nightly: bug[self.status_nightly], self.beta: bug[self.status_beta], self.release: bug[self.status_release], }, 'keywords': ','.join(bug['keywords']), } return bug def filter_bugs(self, bugs): invalids = set() def comment_handler(bug, bugid, data): r = Bugzilla.get_landing_comments(bug['comments'], [], NIGHTLY_PAT) if not r: invalids.add(bugid) return data[bugid]['land'] = { i['revision']: { 'date': None, 'backedout': False, 'bugid': bugid } for i in r } bugids = list(bugs.keys()) Bugzilla( bugids=bugids, commenthandler=comment_handler, commentdata=bugs, comment_include_fields=['text'], ).get_data().wait() for bugid in invalids: del bugs[bugid] def patch_analysis(self, patch): info = {'size': 0, 'test_size': 0, 'addlines': 0, 'rmlines': 0} for diff in whatthepatch.parse_patch(patch): if diff.header and diff.changes: h = diff.header new_path = h.new_path[2:] if h.new_path.startswith( 'b/') else h.new_path # Calc changes additions & deletions counts = [(old is None and new is not None, new is None and old is not None) for old, new, _ in diff.changes] counts = list(zip(*counts)) # inverse zip info['addlines'] += sum(counts[0]) info['rmlines'] += sum(counts[1]) if utils.is_test_file(new_path): info['test_size'] += len(diff.changes) else: info['size'] += len(diff.changes) return info def get_hg_patches(self, bugs): url = hgmozilla.RawRevision.get_url('nightly') queries = [] def handler(patch, data): info = self.patch_analysis(patch) if 'addlines' not in data: data.update(info) else: for k, v in info.items(): data[k] += v for info in bugs.values(): for rev, i in info['land'].items(): if not i['backedout']: queries.append(Query(url, {'node': rev}, handler, info)) if queries: hgmozilla.Revision(queries=queries).wait() torm = [] for bug, info in bugs.items(): landed_patches = [v['backedout'] for v in info['land'].values()].count(False) # bug with only backouts if landed_patches == 0: torm.append(bug) else: info['landed_patches'] = landed_patches # Remove bugs that we don't want to show for bug in torm: del bugs[bug] def get_hg(self, bugs): url = hgmozilla.Revision.get_url('nightly') queries = [] def handler_rev(json, data): push = json['pushdate'][0] push = datetime.datetime.utcfromtimestamp(push) push = lmdutils.as_utc(push) data['date'] = lmdutils.get_date_str(push) data['backedout'] = utils.is_backout(json) m = BUG_PAT.search(json['desc']) if not m or m.group(1) != data['bugid']: data['bugid'] = '' for info in bugs.values(): for rev, i in info['land'].items(): queries.append(Query(url, {'node': rev}, handler_rev, i)) if queries: hgmozilla.Revision(queries=queries).wait() # clean bug_torm = [] for bug, info in bugs.items(): torm = [] for rev, i in info['land'].items(): if not i['bugid'] or not (self.date <= lmdutils.get_date_ymd( i['date']) < self.tomorrow): torm.append(rev) for x in torm: del info['land'][x] if not info['land']: bug_torm.append(bug) for x in bug_torm: del bugs[x] self.get_hg_patches(bugs) def get_bz_params(self, date): self.date = lmdutils.get_date_ymd(date) self.tomorrow = self.date + relativedelta(days=1) bugs = utils.get_bugs_from_pushlog(self.date, self.tomorrow) assignee_blacklist = self.get_config('assignee_blacklist', default=[]) assignee_blacklist = ','.join(assignee_blacklist) fields = [ 'assigned_to', 'assigned_to_detail', 'status', 'resolution', 'priority', 'severity', 'keywords', 'cf_crash_signature', ] fields += [self.status_nightly, self.status_beta, self.status_release] fields += [self.tracking_nightly] params = { 'include_fields': fields, 'bug_id': ','.join(bugs), 'f1': 'assigned_to', 'o1': 'nowords', 'v1': assignee_blacklist, } return params def get_bugs(self, date='today', bug_ids=[]): bugs = super(CodeFreezeWeek, self).get_bugs(date=date, bug_ids=bug_ids) self.filter_bugs(bugs) self.get_hg(bugs) return bugs
class TestRoundRobin(unittest.TestCase): config = { 'doc': 'The triagers need to have a \'Fallback\' entry.', 'triagers': { 'A B': { 'bzmail': '*****@*****.**' }, 'C D': { 'bzmail': '*****@*****.**' }, 'E F': { 'bzmail': '*****@*****.**' }, 'Fallback': { 'bzmail': '*****@*****.**' }, }, 'components': { 'P1::C1': 'default', 'P2::C2': 'default', 'P3::C3': 'special' }, 'default': { 'doc': 'All the dates are the duty end dates.', '2019-02-21': 'A B', '2019-02-28': 'C D', '2019-03-07': 'E F', }, 'special': { 'doc': 'All the dates are the duty end dates.', '2019-02-21': 'E F', '2019-02-28': 'A B', '2019-03-07': 'C D', }, } people = People([{ 'mail': '*****@*****.**', 'cn': 'G H', 'ismanager': 'FALSE', 'title': 'nothing', }]) def mk_bug(self, pc): p, c = pc.split('::') return { 'product': p, 'component': c, 'triage_owner': '*****@*****.**', 'triage_owner_detail': { 'nick': 'ij' }, } @staticmethod def _get_nick(x, bzmail): return bzmail.split('@')[0] def test_get(self): with patch.object(RoundRobin, 'get_nick', new=TestRoundRobin._get_nick): rr = RoundRobin(rr={'team': TestRoundRobin.config}, people=TestRoundRobin.people) assert rr.get(self.mk_bug('P1::C1'), '2019-02-17') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-17') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P3::C3'), '2019-02-17') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P1::C1'), '2019-02-24') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-24') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P3::C3'), '2019-02-24') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P1::C1'), '2019-02-28') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-28') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P3::C3'), '2019-02-28') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P1::C1'), '2019-03-05') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P2::C2'), '2019-03-05') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P3::C3'), '2019-03-05') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P1::C1'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('P2::C2'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('P3::C3'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('Foo::Bar'), '2019-03-01') == ( '*****@*****.**', 'ij', ) def test_get_who_to_nag(self): rr = RoundRobin(rr={'team': TestRoundRobin.config}, people=TestRoundRobin.people) assert rr.get_who_to_nag('2019-02-25') == {} assert rr.get_who_to_nag('2019-02-28') == {'*****@*****.**': ['']} assert rr.get_who_to_nag('2019-03-05') == {'*****@*****.**': ['']} assert rr.get_who_to_nag('2019-03-07') == {'*****@*****.**': ['']} assert rr.get_who_to_nag('2019-03-10') == {'*****@*****.**': ['']} with patch.object(RoundRobin, 'is_mozilla', return_value=False): rr = RoundRobin(rr={'team': TestRoundRobin.config}, people=TestRoundRobin.people) self.assertRaises(BadFallback, rr.get_who_to_nag, '2019-03-01')
def __init__(self): super(LeaveOpenNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.blacklist = set(utils.get_config(self.name(), 'blacklist', []))
def __init__(self, rr=None, people=None, teams=None): self.people = People() if people is None else people self.all_calendars = [] self.feed(teams, rr=rr) self.nicks = {}
def __init__(self): super(MetaNoDepsNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.blacklist = set(utils.get_config(self.name(), 'blacklist', []))
class NewbieWithNI(BzCleaner): def __init__(self): super(NewbieWithNI, self).__init__() self.people = People() self.ndays = utils.get_config(self.name(), 'number_of_days', 14) self.ncomments = utils.get_config(self.name(), 'number_of_comments', 2) self.autofix_reporters = {} def description(self): return 'Bugs where the reporter has a needinfo and no activity for the last {} weeks'.format( self.ndays ) def get_extra_for_template(self): return {'ndays': self.ndays, 'ncomments': self.ncomments} def get_autofix_change(self): return self.autofix_reporters def set_autofix(self, bugs): for bugid, v in bugs.items(): nick = v['creator_nick'] self.autofix_reporters[bugid] = { 'comment': { 'body': 'Closing as INCOMPLETE because no answer to the needinfo.\n:{}, feel free to reopen the bug if you\'ve more information to provide.'.format( nick ) }, 'status': 'RESOLVED', 'resolution': 'INCOMPLETE', } def filter_interesting_bugs(self, bugs): """Get the bugs with number of comments less than self.ncommments """ def comment_handler(bug, bugid, data): if len(bug['comments']) <= self.ncomments: data.append(bugid) bugids = list(bugs.keys()) data = [] Bugzilla( bugids=bugids, commenthandler=comment_handler, commentdata=data, comment_include_fields=['count'], ).get_data().wait() bugs = {bugid: bugs[bugid] for bugid in data} return bugs def get_bz_params(self, date): fields = ['creator', 'flags'] params = { 'include_fields': fields, 'resolution': '---', 'f1': 'flagtypes.name', 'o1': 'substring', 'v1': 'needinfo?', 'f2': 'days_elapsed', 'o2': 'greaterthan', 'v2': self.ndays, } return params def handle_bug(self, bug, data): creator = bug['creator'] if self.people.is_mozilla(creator): return None for flag in utils.get_needinfo(bug): if flag.get('requestee', '') == creator: bugid = str(bug['id']) nick = bug['creator_detail']['nick'] data[bugid] = {'creator_nick': nick} return bug return None def get_bugs(self, date='today', bug_ids=[]): bugs = super(NewbieWithNI, self).get_bugs(date=date, bug_ids=bug_ids) bugs = self.filter_interesting_bugs(bugs) self.set_autofix(bugs) return bugs
def __init__(self): super(LeaveOpenNoActivity, self).__init__() self.people = People() self.nmonths = utils.get_config(self.name(), 'months_lookup') self.max_ni = utils.get_config(self.name(), 'max_ni') self.skiplist = set(utils.get_config(self.name(), 'skiplist', []))
class Nag(object): def __init__(self): super(Nag, self).__init__() self.people = People() self.send_nag_mail = True self.data = {} self.nag_date = None self.white_list = [] self.black_list = [] self.escalation = Escalation(self.people) self.triage_owners = {} self.all_owners = None self.query_params = {} @staticmethod def get_from(): return utils.get_config('auto_nag', 'from', '*****@*****.**') @staticmethod def get_cc(): return set(utils.get_config('auto_nag', 'cc', [])) def get_priority(self, bug): tracking = bug[self.tracking] if tracking == 'blocking': return 'high' return 'normal' def filter_bug(self, priority): days = (utils.get_next_release_date() - self.nag_date).days weekday = self.nag_date.weekday() return self.escalation.filter(priority, days, weekday) def get_people(self): return self.people def set_people_to_nag(self, bug, buginfo): return bug def escalate(self, person, priority, **kwargs): days = (utils.get_next_release_date() - self.nag_date).days return self.escalation.get_supervisor(priority, days, person, **kwargs) def add(self, person, bug_data, priority='default', **kwargs): if not self.people.is_mozilla(person): return False manager = self.escalate(person, priority, **kwargs) return self.add_couple(person, manager, bug_data) def add_couple(self, person, manager, bug_data): person = self.people.get_moz_mail(person) if manager in self.data: data = self.data[manager] else: self.data[manager] = data = {} if person in data: data[person].append(bug_data) else: data[person] = [bug_data] return True def nag_template(self): return self.name() + '_nag.html' def get_extra_for_nag_template(self): return {} def columns_nag(self): return None def sort_columns_nag(self): return None def _is_in_list(self, mail, _list): for manager in _list: if self.people.is_under(mail, manager): return True return False def is_under(self, mail): if not self.white_list: if not self.black_list: return True return not self._is_in_list(mail, self.black_list) if not self.black_list: return self._is_in_list(mail, self.white_list) return self._is_in_list(mail, self.white_list) and not self._is_in_list( mail, self.black_list ) def add_triage_owner(self, owner, real_owner=None): if owner not in self.triage_owners: to = real_owner if real_owner is not None else owner self.triage_owners[owner] = self.get_query_url_for_triage_owner(to) def get_query_url_for_triage_owner(self, owner): if self.all_owners is None: self.all_owners = utils.get_triage_owners() params = copy.deepcopy(self.query_params) if 'include_fields' in params: del params['include_fields'] comps = self.all_owners[owner] comps = set(comps) params['component'] = sorted(comps) url = utils.get_bz_search_url(params) return url def organize_nag(self, bugs): columns = self.columns_nag() if columns is None: columns = self.columns() key = self.sort_columns_nag() if key is None: key = self.sort_columns() return utils.organize(bugs, columns, key=key) def send_mails(self, title, dryrun=False): if not self.send_nag_mail: return env = Environment(loader=FileSystemLoader('templates')) common = env.get_template('common.html') login_info = utils.get_login_info() From = Nag.get_from() Default_Cc = Nag.get_cc() mails = self.prepare_mails() for m in mails: Cc = Default_Cc.copy() if m['manager']: Cc.add(m['manager']) body = common.render(message=m['body'], query_url=None) receivers = set(m['to']) | set(Cc) status = 'Success' try: mail.send( From, sorted(m['to']), title, body, Cc=sorted(Cc), html=True, login=login_info, dryrun=dryrun, ) except: # NOQA logger.exception('Tool {}'.format(self.name())) status = 'Failure' db.Email.add(self.name(), receivers, 'individual', status) def prepare_mails(self): if not self.data: return [] template = self.nag_template() if not template: return [] extra = self.get_extra_for_nag_template() env = Environment(loader=FileSystemLoader('templates')) template = env.get_template(template) mails = [] for manager, info in self.data.items(): data = [] To = sorted(info.keys()) for person in To: bug_data = info[person] data += bug_data if len(To) == 1 and To[0] in self.triage_owners: query_url = self.triage_owners[To[0]] else: query_url = None body = template.render( date=self.nag_date, extra=extra, plural=utils.plural, enumerate=enumerate, data=self.organize_nag(data), nag=True, query_url_nag=query_url, table_attrs=self.get_config('table_attrs'), ) m = {'manager': manager, 'to': set(info.keys()), 'body': body} mails.append(m) return mails def reorganize_to_bag(self, data): return data
class CodeFreezeWeek(BzCleaner): def __init__(self): super(CodeFreezeWeek, self).__init__() self.versions = utils.get_checked_versions() if not self.versions: return self.people = People() self.nightly = self.versions['central'] self.beta = self.versions['beta'] self.release = self.versions['release'] self.status_nightly = utils.get_flag(self.nightly, 'status', 'central') self.status_beta = utils.get_flag(self.beta, 'status', 'beta') self.status_release = utils.get_flag(self.release, 'status', 'release') self.tracking_nightly = utils.get_flag(self.nightly, 'tracking', 'central') self.tracking_beta = utils.get_flag(self.beta, 'tracking', 'beta') self.tracking_release = utils.get_flag(self.release, 'tracking', 'release') def description(self): return 'Bugs with patches which landed during the soft freeze week' def get_extra_for_template(self): return { 'nightly': self.nightly, 'beta': self.beta, 'release': self.release, 'date': lmdutils.get_date_str(self.date), } def filter_no_nag_keyword(self): return False def has_enough_data(self): return bool(self.versions) def must_run(self, date): for c in get_calendar(): # if freeze is the 2019-03-11, then the tool must run the day after # until 2019-03-2018 (a week after) freeze = c['soft freeze'] if freeze <= date <= freeze + relativedelta(days=6): return True return False def has_product_component(self): return True def columns(self): return [ 'id', 'summary', 'product', 'component', 'assignee', 'landed_patches', 'addlines', 'rmlines', 'size', 'test_size', 'priority', 'severity', 'tracking', 'status', 'status_flags', 'crash', 'keywords', ] def sort_columns(self): return lambda p: (p[2], p[3], -p[5], -p[8], -p[9], -int(p[0])) def handle_bug(self, bug, data): bugid = str(bug['id']) assignee = bug.get('assigned_to', '') if assignee: info = self.people.get_info(assignee) if info: assignee = info['cn'] else: name = bug.get('assigned_to_detail', {}).get('real_name', '') if name: assignee = utils.get_better_name(name) else: assignee = 'Nobody' isacrash = len(utils.get_signatures(bug.get('cf_crash_signature', ''))) != 0 data[bugid] = { 'land': {}, 'assignee': assignee, 'crash': 'Yes' if isacrash else 'No', 'priority': bug['priority'], 'severity': bug['severity'], 'tracking': bug[self.tracking_nightly], 'status': bug['status'].lower(), 'status_flags': { self.nightly: bug[self.status_nightly], self.beta: bug[self.status_beta], self.release: bug[self.status_release], }, 'keywords': ','.join(bug['keywords']), } return bug def filter_bugs(self, bugs): invalids = set() def comment_handler(bug, bugid, data): r = Bugzilla.get_landing_comments(bug['comments'], [], NIGHTLY_PAT) if not r: invalids.add(bugid) return data[bugid]['land'] = { i['revision']: {'date': None, 'backedout': False, 'bugid': bugid} for i in r } bugids = list(bugs.keys()) Bugzilla( bugids=bugids, commenthandler=comment_handler, commentdata=bugs, comment_include_fields=['text'], ).get_data().wait() for bugid in invalids: del bugs[bugid] def patch_analysis(self, patch): info = {'size': 0, 'test_size': 0, 'addlines': 0, 'rmlines': 0} for diff in whatthepatch.parse_patch(patch): if diff.header and diff.changes: h = diff.header new_path = h.new_path[2:] if h.new_path.startswith('b/') else h.new_path # Calc changes additions & deletions counts = [ (old is None and new is not None, new is None and old is not None) for old, new, _ in diff.changes ] counts = list(zip(*counts)) # inverse zip info['addlines'] += sum(counts[0]) info['rmlines'] += sum(counts[1]) if utils.is_test_file(new_path): info['test_size'] += len(diff.changes) else: info['size'] += len(diff.changes) return info def get_hg_patches(self, bugs): url = hgmozilla.RawRevision.get_url('nightly') queries = [] def handler(patch, data): info = self.patch_analysis(patch) if 'addlines' not in data: data.update(info) else: for k, v in info.items(): data[k] += v for info in bugs.values(): for rev, i in info['land'].items(): if not i['backedout']: queries.append(Query(url, {'node': rev}, handler, info)) if queries: hgmozilla.Revision(queries=queries).wait() torm = [] for bug, info in bugs.items(): landed_patches = [v['backedout'] for v in info['land'].values()].count( False ) # bug with only backouts if landed_patches == 0: torm.append(bug) else: info['landed_patches'] = landed_patches # Remove bugs that we don't want to show for bug in torm: del bugs[bug] def get_hg(self, bugs): url = hgmozilla.Revision.get_url('nightly') queries = [] def handler_rev(json, data): push = json['pushdate'][0] push = datetime.datetime.utcfromtimestamp(push) push = lmdutils.as_utc(push) data['date'] = lmdutils.get_date_str(push) data['backedout'] = utils.is_backout(json) m = BUG_PAT.search(json['desc']) if not m or m.group(1) != data['bugid']: data['bugid'] = '' for info in bugs.values(): for rev, i in info['land'].items(): queries.append(Query(url, {'node': rev}, handler_rev, i)) if queries: hgmozilla.Revision(queries=queries).wait() # clean bug_torm = [] for bug, info in bugs.items(): torm = [] for rev, i in info['land'].items(): if not i['bugid'] or not ( self.date <= lmdutils.get_date_ymd(i['date']) < self.tomorrow ): torm.append(rev) for x in torm: del info['land'][x] if not info['land']: bug_torm.append(bug) for x in bug_torm: del bugs[x] self.get_hg_patches(bugs) def get_bz_params(self, date): self.date = lmdutils.get_date_ymd(date) self.tomorrow = self.date + relativedelta(days=1) bugs = utils.get_bugs_from_pushlog(self.date, self.tomorrow) assignee_blacklist = self.get_config('assignee_blacklist', default=[]) assignee_blacklist = ','.join(assignee_blacklist) fields = [ 'assigned_to', 'assigned_to_detail', 'status', 'resolution', 'priority', 'severity', 'keywords', 'cf_crash_signature', ] fields += [self.status_nightly, self.status_beta, self.status_release] fields += [self.tracking_nightly] params = { 'include_fields': fields, 'bug_id': ','.join(bugs), 'f1': 'assigned_to', 'o1': 'nowords', 'v1': assignee_blacklist, } return params def get_bugs(self, date='today', bug_ids=[]): bugs = super(CodeFreezeWeek, self).get_bugs(date=date, bug_ids=bug_ids) self.filter_bugs(bugs) self.get_hg(bugs) return bugs
class TestRoundRobin(unittest.TestCase): default = { "duty-start-dates": { "2019-02-14": "A B", "2019-02-21": "C D", "2019-02-28": "E F", } } special = { "duty-start-dates": { "2019-02-14": "E F", "2019-02-21": "A B", "2019-02-28": "C D", } } config = { "fallback": "G H", "components": {"P1::C1": "default", "P2::C2": "default", "P3::C3": "special"}, "default": {"calendar": json.dumps(default)}, "special": {"calendar": json.dumps(special)}, } config_ics = { "fallback": "G H", "components": {"P1::C1": "default", "P2::C2": "default"}, "default": {"calendar": "auto_nag/tests/calendar.ics"}, } people = People( [ { "mail": "{}{}@mozilla.com".format(x, y), "cn": "{} {}".format(x.upper(), y.upper()), "ismanager": "FALSE", "title": "nothing", } for x, y in zip("aceg", "bdfh") ] ) def mk_bug(self, pc): p, c = pc.split("::") return { "product": p, "component": c, "triage_owner": "*****@*****.**", "triage_owner_detail": {"nick": "ij"}, } @staticmethod def _get_nick(x, bzmail, pc, cal): return bzmail.split("@")[0] def test_get(self): with patch.object(RoundRobin, "get_nick", new=TestRoundRobin._get_nick): rr = RoundRobin( rr={"team": TestRoundRobin.config}, people=TestRoundRobin.people ) assert rr.get(self.mk_bug("P1::C1"), "2019-02-17") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P2::C2"), "2019-02-17") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P3::C3"), "2019-02-17") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P1::C1"), "2019-02-24") == ( "*****@*****.**", "cd", ) assert rr.get(self.mk_bug("P2::C2"), "2019-02-24") == ( "*****@*****.**", "cd", ) assert rr.get(self.mk_bug("P3::C3"), "2019-02-24") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P1::C1"), "2019-02-28") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P2::C2"), "2019-02-28") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P3::C3"), "2019-02-28") == ( "*****@*****.**", "cd", ) assert rr.get(self.mk_bug("P1::C1"), "2019-03-05") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P2::C2"), "2019-03-05") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P3::C3"), "2019-03-05") == ( "*****@*****.**", "cd", ) assert rr.get(self.mk_bug("P1::C1"), "2019-03-08") == ( "*****@*****.**", "gh", ) assert rr.get(self.mk_bug("P2::C2"), "2019-03-08") == ( "*****@*****.**", "gh", ) assert rr.get(self.mk_bug("P3::C3"), "2019-03-08") == ( "*****@*****.**", "gh", ) assert rr.get(self.mk_bug("Foo::Bar"), "2019-03-01") == ( "*****@*****.**", "ij", ) def test_get_ics(self): with patch.object(RoundRobin, "get_nick", new=TestRoundRobin._get_nick): rr = RoundRobin( rr={"team": TestRoundRobin.config_ics}, people=TestRoundRobin.people ) assert rr.get(self.mk_bug("P1::C1"), "2019-02-17") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P2::C2"), "2019-02-17") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P1::C1"), "2019-02-24") == ( "*****@*****.**", "cd", ) assert rr.get(self.mk_bug("P2::C2"), "2019-02-24") == ( "*****@*****.**", "cd", ) assert rr.get(self.mk_bug("P1::C1"), "2019-02-28") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P2::C2"), "2019-02-28") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P1::C1"), "2019-03-05") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P2::C2"), "2019-03-05") == ( "*****@*****.**", "ef", ) assert rr.get(self.mk_bug("P1::C1"), "2019-03-08") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P2::C2"), "2019-03-08") == ( "*****@*****.**", "ab", ) assert rr.get(self.mk_bug("P1::C1"), "2019-03-15") == ( "*****@*****.**", "gh", ) assert rr.get(self.mk_bug("P2::C2"), "2019-03-15") == ( "*****@*****.**", "gh", ) assert rr.get(self.mk_bug("Foo::Bar"), "2019-03-01") == ( "*****@*****.**", "ij", ) def test_get_who_to_nag(self): rr = RoundRobin( rr={"team": TestRoundRobin.config}, people=TestRoundRobin.people ) empty = {"team": {"nobody": True, "persons": []}} assert rr.get_who_to_nag("2019-02-25") == {} assert rr.get_who_to_nag("2019-03-01") == {"*****@*****.**": empty} assert rr.get_who_to_nag("2019-03-05") == {"*****@*****.**": empty} assert rr.get_who_to_nag("2019-03-07") == {"*****@*****.**": empty} assert rr.get_who_to_nag("2019-03-10") == {"*****@*****.**": empty} with patch.object(People, "get_moz_mail", return_value=None): rr = RoundRobin( rr={"team": TestRoundRobin.config}, people=TestRoundRobin.people ) self.assertRaises(BadFallback, rr.get_who_to_nag, "2019-03-01")
class TestRoundRobin(unittest.TestCase): default = { 'duty-start-dates': { '2019-02-14': 'A B', '2019-02-21': 'C D', '2019-02-28': 'E F', } } special = { 'duty-start-dates': { '2019-02-14': 'E F', '2019-02-21': 'A B', '2019-02-28': 'C D', } } config = { 'fallback': 'G H', 'components': { 'P1::C1': 'default', 'P2::C2': 'default', 'P3::C3': 'special' }, 'default': { 'calendar': json.dumps(default) }, 'special': { 'calendar': json.dumps(special) }, } config_ics = { 'fallback': 'G H', 'components': { 'P1::C1': 'default', 'P2::C2': 'default' }, 'default': { 'calendar': 'auto_nag/tests/calendar.ics' }, } people = People([{ 'mail': '{}{}@mozilla.com'.format(x, y), 'cn': '{} {}'.format(x.upper(), y.upper()), 'ismanager': 'FALSE', 'title': 'nothing', } for x, y in zip('aceg', 'bdfh')]) def mk_bug(self, pc): p, c = pc.split('::') return { 'product': p, 'component': c, 'triage_owner': '*****@*****.**', 'triage_owner_detail': { 'nick': 'ij' }, } @staticmethod def _get_nick(x, bzmail): return bzmail.split('@')[0] def test_get(self): with patch.object(RoundRobin, 'get_nick', new=TestRoundRobin._get_nick): rr = RoundRobin(rr={'team': TestRoundRobin.config}, people=TestRoundRobin.people) assert rr.get(self.mk_bug('P1::C1'), '2019-02-17') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-17') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P3::C3'), '2019-02-17') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P1::C1'), '2019-02-24') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-24') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P3::C3'), '2019-02-24') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P1::C1'), '2019-02-28') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-28') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P3::C3'), '2019-02-28') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P1::C1'), '2019-03-05') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P2::C2'), '2019-03-05') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P3::C3'), '2019-03-05') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P1::C1'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('P2::C2'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('P3::C3'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('Foo::Bar'), '2019-03-01') == ( '*****@*****.**', 'ij', ) def test_get_ics(self): with patch.object(RoundRobin, 'get_nick', new=TestRoundRobin._get_nick): rr = RoundRobin(rr={'team': TestRoundRobin.config_ics}, people=TestRoundRobin.people) assert rr.get(self.mk_bug('P1::C1'), '2019-02-17') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-17') == ( '*****@*****.**', 'ab', ) assert rr.get(self.mk_bug('P1::C1'), '2019-02-24') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-24') == ( '*****@*****.**', 'cd', ) assert rr.get(self.mk_bug('P1::C1'), '2019-02-28') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P2::C2'), '2019-02-28') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P1::C1'), '2019-03-05') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P2::C2'), '2019-03-05') == ( '*****@*****.**', 'ef', ) assert rr.get(self.mk_bug('P1::C1'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('P2::C2'), '2019-03-08') == ( '*****@*****.**', 'gh', ) assert rr.get(self.mk_bug('Foo::Bar'), '2019-03-01') == ( '*****@*****.**', 'ij', ) def test_get_who_to_nag(self): rr = RoundRobin(rr={'team': TestRoundRobin.config}, people=TestRoundRobin.people) empty = {'team': {'nobody': True, 'persons': []}} assert rr.get_who_to_nag('2019-02-25') == {} assert rr.get_who_to_nag('2019-03-01') == {'*****@*****.**': empty} assert rr.get_who_to_nag('2019-03-05') == {'*****@*****.**': empty} assert rr.get_who_to_nag('2019-03-07') == {'*****@*****.**': empty} assert rr.get_who_to_nag('2019-03-10') == {'*****@*****.**': empty} with patch.object(People, 'get_moz_mail', return_value=None): rr = RoundRobin(rr={'team': TestRoundRobin.config}, people=TestRoundRobin.people) self.assertRaises(BadFallback, rr.get_who_to_nag, '2019-03-01')
def __init__(self): super(HasSTRNoRange, self).__init__() self.people = People.get_instance() self.autofix_reporters = {}
def test_escalation(self): people = [ { 'mail': '*****@*****.**', 'cn': 'A B', 'ismanager': 'FALSE', 'manager': { 'dn': '[email protected],o=org,dc=mozilla' }, 'title': 'nothing', }, { 'mail': '*****@*****.**', 'cn': 'C D', 'ismanager': 'TRUE', 'manager': { 'dn': '[email protected],o=org,dc=mozilla' }, 'title': 'manager', }, { 'mail': '*****@*****.**', 'cn': 'E F', 'ismanager': 'TRUE', 'manager': { 'dn': '[email protected],o=org,dc=mozilla' }, 'title': 'super manager', }, { 'mail': '*****@*****.**', 'cn': 'G H', 'ismanager': 'TRUE', 'manager': { 'dn': '[email protected],o=org,dc=mozilla' }, 'title': 'super manager', }, { 'mail': '*****@*****.**', 'cn': 'I J', 'ismanager': 'TRUE', 'manager': { 'dn': '[email protected],o=org,dc=mozilla' }, 'title': 'director', }, { 'mail': '*****@*****.**', 'cn': 'K L', 'ismanager': 'TRUE', 'title': 'vice president', }, ] e = Escalation(People(people), data=TestEscalation.config) assert e.get_supervisor( 'high', 35, '*****@*****.**', foobar='*****@*****.**') == '*****@*****.**' assert e.get_supervisor('high', 25, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('high', 20, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('high', 18, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('high', 7, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('high', 1, '*****@*****.**') == '*****@*****.**' assert e.filter('high', 25, 0) == False assert e.filter('high', 25, 3) == True assert e.filter('high', 18, 0) == True assert e.filter('high', 18, 1) == False assert e.filter('high', 18, 3) == True assert e.filter('high', 18, 5) == False assert e.filter('high', 7, 1) == False assert e.filter('high', 7, 3) == True assert e.filter('high', 7, 5) == False assert e.filter('high', 1, 1) == True assert e.filter('high', 1, 3) == True assert e.filter('high', 1, 4) == True assert e.filter('high', 7, 5) == False assert e.get_supervisor('normal', 17, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('normal', 15, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('normal', 12, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('normal', 7, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('normal', 1, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('default', 17, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('default', 7, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('default', 1, '*****@*****.**') == '*****@*****.**' assert e.get_supervisor('default', 0, '*****@*****.**') == '*****@*****.**'