File: buganizer.py

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (121 lines) | stat: -rw-r--r-- 4,487 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Code for interacting with Buganizer."""

import datetime
from typing import Iterable, Set

from blinkpy.w3c import buganizer

from bad_machine_finder import detection

_AUTOMATED_COMMENT_START = 'Automated Report Of Bad Machines'


class BuganizerException(Exception):
  """A general exception for Buganizer-related errors."""


class ClientNotAvailableException(BuganizerException):
  """Indicates that a Buganzier client could not be created."""


class BugNotAccessibleException(BuganizerException):
  """Indicates that the specified bug could not be accessed."""


def UpdateBug(bug_id: int,
              mixin_grouped_bad_machines: detection.MixinGroupedBadMachines,
              grace_period: int) -> None:
  """Updates the given |bug_id| with bad machine results.

  Will automatically omit bad machines that have previously been reported in the
  past |grace_period| days.

  Args:
    bug_id: The Buganizer bug ID to update.
    mixin_groupd_bad_machines: A MixinGroupedBadMachines object containing all
        of the bad machine data to use when updating the bug.
    grace_period: The minimum number of days between when a bad machine was
        reported and when it can be reported again.
  """
  client = _GetBuganizerClient()

  bad_machine_names = mixin_grouped_bad_machines.GetAllBadMachineNames()
  recently_reported_bots = _GetRecentlyReportedBots(bug_id, client,
                                                    bad_machine_names,
                                                    grace_period)

  markdown_components = [
      _AUTOMATED_COMMENT_START,
  ]
  mixin_report_markdown = mixin_grouped_bad_machines.GenerateMarkdown(
      bots_to_skip=recently_reported_bots)
  if mixin_report_markdown:
    markdown_components.append(mixin_report_markdown)
  else:
    markdown_components.append('No new bad machines detected')
  markdown_comment = '\n\n'.join(markdown_components)

  client.NewComment(bug_id, markdown_comment, use_markdown=True)


def _GetRecentlyReportedBots(bug_id: int, client: buganizer.BuganizerClient,
                             bad_machine_names: Iterable[str],
                             grace_period: int) -> Set[str]:
  """Retrieves the subset of |bad_machine_names| which were reported recently.

  Args:
    bug_id: The Buganizer bug ID to check.
    client: The BuganizerClient to use for interacting with Buganizer.
    bad_machine_names: All machine names that should be looked for within
        recent comments on the bug.
    grace_period: The minimum number of days since the last mention of a machine
        on the bug for it not to be considered recently reported.

  Returns:
    A set of machine names which were reported on |bug_id| within the past
    |grace_period| days. Guaranteed to be a subset of |bad_machine_names|.
  """
  comment_list = client.GetIssueComments(bug_id)
  # GetIssueComments currently returns a dict if something goes wrong instead of
  # raising an exception.
  # TODO(crbug.com/361602059): Switch to catching exceptions once those are
  # raised.
  if isinstance(comment_list, dict):
    raise BugNotAccessibleException(
        f'Failed to get comments from {bug_id}: '
        f'{comment_list.get("error", "error not provided")}')

  recent_comment_bodies = []
  for c in comment_list:
    comment_iso_timestamp = c['timestamp']
    # Z indicates the UTC timezone, but is not supported until Python 3.11.
    if comment_iso_timestamp.endswith(('z', 'Z')):
      comment_iso_timestamp = comment_iso_timestamp[:-1]
    comment_date = datetime.datetime.fromisoformat(comment_iso_timestamp)
    n_days_ago = (datetime.datetime.now(comment_date.tzinfo) -
                  datetime.timedelta(days=grace_period))
    if comment_date < n_days_ago:
      continue
    if _AUTOMATED_COMMENT_START not in c['comment']:
      continue
    recent_comment_bodies.append(c['comment'])

  recently_reported_bots = set()
  for bot_id in bad_machine_names:
    for cb in recent_comment_bodies:
      if bot_id in cb:
        recently_reported_bots.add(bot_id)
        break

  return recently_reported_bots


def _GetBuganizerClient() -> buganizer.BuganizerClient:
  try:
    return buganizer.BuganizerClient()
  except Exception as e:  # pylint: disable=broad-except
    raise ClientNotAvailableException(
        'Failed to create Buganizer client') from e