Changeset 95238 in webkit
- Timestamp:
- Sep 15, 2011 3:10:50 PM (13 years ago)
- Location:
- trunk/Tools
- Files:
-
- 2 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r95234 r95238 1 2011-09-15 Eric Seidel <eric@webkit.org> 2 3 Reviewed by Adam Barth. 4 5 webkit-patch should be able to find users and add them to bugzilla groups 6 https://bugs.webkit.org/show_bug.cgi?id=63351 7 8 These are both very basic commands. But it's now possible to find 9 all users matching a regexp, as well as add all users matching a regexp 10 to a set of groups. 11 12 bugzilla.py already knew how to find users (for validate-committer-lists) 13 but now it has the ability to modify the user records. 14 15 I split some of the logic out into a new EditUsersParser class 16 to try and reduce the amount of code in Bugzilla/BugzillaQueries. 17 18 * Scripts/webkitpy/common/net/bugzilla/bugzilla.py: 19 * Scripts/webkitpy/common/net/bugzilla/bugzilla_unittest.py: 20 * Scripts/webkitpy/tool/commands/__init__.py: 21 * Scripts/webkitpy/tool/commands/adduserstogroups.py: Added. 22 * Scripts/webkitpy/tool/commands/findusers.py: Added. 23 1 24 2011-09-15 Eric Seidel <eric@webkit.org> 2 25 -
trunk/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py
r87413 r95238 1 # Copyright (c) 20 09Google Inc. All rights reserved.1 # Copyright (c) 2011 Google Inc. All rights reserved. 2 2 # Copyright (c) 2009 Apple Inc. All rights reserved. 3 3 # Copyright (c) 2010 Research In Motion Limited. All rights reserved. … … 50 50 51 51 52 class EditUsersParser(object): 53 def __init__(self): 54 self._group_name_to_group_string_cache = {} 55 56 def _login_and_uid_from_row(self, row): 57 first_cell = row.find("td") 58 # The first row is just headers, we skip it. 59 if not first_cell: 60 return None 61 # When there were no results, we have a fake "<none>" entry in the table. 62 if first_cell.find(text="<none>"): 63 return None 64 # Otherwise the <td> contains a single <a> which contains the login name or a single <i> with the string "<none>". 65 anchor_tag = first_cell.find("a") 66 login = unicode(anchor_tag.string).strip() 67 user_id = int(re.search(r"userid=(\d+)", str(anchor_tag['href'])).group(1)) 68 return (login, user_id) 69 70 def login_userid_pairs_from_edit_user_results(self, results_page): 71 soup = BeautifulSoup(results_page, convertEntities=BeautifulStoneSoup.HTML_ENTITIES) 72 results_table = soup.find(id="admin_table") 73 login_userid_pairs = [self._login_and_uid_from_row(row) for row in results_table('tr')] 74 # Filter out None from the logins. 75 return filter(lambda pair: bool(pair), login_userid_pairs) 76 77 def _group_name_and_string_from_row(self, row): 78 label_element = row.find('label') 79 group_string = unicode(label_element['for']) 80 group_name = unicode(label_element.find('strong').string).rstrip(':') 81 return (group_name, group_string) 82 83 def user_dict_from_edit_user_page(self, page): 84 soup = BeautifulSoup(page, convertEntities=BeautifulStoneSoup.HTML_ENTITIES) 85 user_table = soup.find("table", {'class': 'main'}) 86 user_dict = {} 87 for row in user_table('tr'): 88 label_element = row.find('label') 89 if not label_element: 90 continue # This must not be a row we know how to parse. 91 if row.find('table'): 92 continue # Skip the <tr> holding the groups table. 93 94 key = label_element['for'] 95 if "group" in key: 96 key = "groups" 97 value = user_dict.get('groups', set()) 98 # We must be parsing a "tr" inside the inner group table. 99 (group_name, _) = self._group_name_and_string_from_row(row) 100 if row.find('input', {'type': 'checkbox', 'checked': 'checked'}): 101 value.add(group_name) 102 else: 103 value = unicode(row.find('td').string).strip() 104 user_dict[key] = value 105 return user_dict 106 107 def _group_rows_from_edit_user_page(self, edit_user_page): 108 soup = BeautifulSoup(edit_user_page, convertEntities=BeautifulSoup.HTML_ENTITIES) 109 return soup('td', {'class': 'groupname'}) 110 111 def group_string_from_name(self, edit_user_page, group_name): 112 # Bugzilla uses "group_NUMBER" strings, which may be different per install 113 # so we just look them up once and cache them. 114 if not self._group_name_to_group_string_cache: 115 rows = self._group_rows_from_edit_user_page(edit_user_page) 116 name_string_pairs = map(self._group_name_and_string_from_row, rows) 117 self._group_name_to_group_string_cache = dict(name_string_pairs) 118 return self._group_name_to_group_string_cache[group_name] 119 120 52 121 def timestamp(): 53 122 return datetime.now().strftime("%Y%m%d%H%M%S") … … 175 244 return self._fetch_attachment_ids_request_query(review_queue_url) 176 245 177 def _login_from_row(self, row):178 first_cell = row.find("td")179 # The first row is just headers, we skip it.180 if not first_cell:181 return None182 # When there were no results, we have a fake "<none>" entry in the table.183 if first_cell.find(text="<none>"):184 return None185 # Otherwise the <td> contains a single <a> which contains the login name or a single <i> with the string "<none>".186 return str(first_cell.find("a").string).strip()187 188 def _parse_logins_from_editusers_results(self, results_page):189 soup = BeautifulSoup(results_page, convertEntities=BeautifulStoneSoup.HTML_ENTITIES)190 results_table = soup.find(id="admin_table")191 logins = [self._login_from_row(row) for row in results_table('tr')]192 # Filter out None from the logins.193 return filter(lambda login: bool(login), logins)194 195 246 # This only works if your account has edituser privileges. 196 247 # We could easily parse https://bugs.webkit.org/userprefs.cgi?tab=permissions to 197 248 # check permissions, but bugzilla will just return an error if we don't have them. 198 def fetch_login s_matching_substring(self, search_string):249 def fetch_login_userid_pairs_matching_substring(self, search_string): 199 250 review_queue_url = "editusers.cgi?action=list&matchvalue=login_name&matchstr=%s&matchtype=substr" % urllib.quote(search_string) 200 251 results_page = self._load_query(review_queue_url) 201 return self._parse_logins_from_editusers_results(results_page) 252 # We could pull the EditUsersParser off Bugzilla if needed. 253 return EditUsersParser().login_userid_pairs_from_edit_user_results(results_page) 254 255 # FIXME: We should consider adding a BugzillaUser class. 256 def fetch_logins_matching_substring(self, search_string): 257 pairs = self.fetch_login_userid_pairs_matching_substring(search_string) 258 return map(lambda pair: pair[0], pairs) 202 259 203 260 204 261 class Bugzilla(object): 205 206 262 def __init__(self, dryrun=False, committers=committers.CommitterList()): 207 263 self.dryrun = dryrun … … 210 266 self.committers = committers 211 267 self.cached_quips = [] 268 self.edit_user_parser = EditUsersParser() 212 269 213 270 # FIXME: We should use some sort of Browser mock object when in dryrun … … 215 272 from webkitpy.thirdparty.autoinstalled.mechanize import Browser 216 273 self.browser = Browser() 217 # Ignore bugs.webkit.org/robots.txt until we fix it to allow this 218 # script. 274 # Ignore bugs.webkit.org/robots.txt until we fix it to allow this script. 219 275 self.browser.set_handle_robots(False) 276 277 def fetch_user(self, user_id): 278 self.authenticate() 279 edit_user_page = self.browser.open(self.edit_user_url_for_id(user_id)) 280 return self.edit_user_parser.user_dict_from_edit_user_page(edit_user_page) 281 282 def add_user_to_groups(self, user_id, group_names): 283 self.authenticate() 284 user_edit_page = self.browser.open(self.edit_user_url_for_id(user_id)) 285 self.browser.select_form(nr=1) 286 for group_name in group_names: 287 group_string = self.edit_user_parser.group_string_from_name(user_edit_page, group_name) 288 self.browser.find_control(group_string).items[0].selected = True 289 self.browser.submit() 220 290 221 291 def quips(self): … … 249 319 attachment_id, 250 320 action_param) 321 322 def edit_user_url_for_id(self, user_id): 323 return "%seditusers.cgi?action=edit&userid=%s" % (config_urls.bug_server_url, user_id) 251 324 252 325 def _parse_attachment_flag(self, -
trunk/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla_unittest.py
r86288 r95238 1 # Copyright (C) 20 09Google Inc. All rights reserved.1 # Copyright (C) 2011 Google Inc. All rights reserved. 2 2 # 3 3 # Redistribution and use in source and binary forms, with or without … … 31 31 import StringIO 32 32 33 from .bugzilla import Bugzilla, BugzillaQueries 33 from .bugzilla import Bugzilla, BugzillaQueries, EditUsersParser 34 34 35 35 from webkitpy.common.checkout.changelog import parse_bug_id … … 393 393 queries._load_query("request.cgi?action=queue&type=review&group=type") 394 394 395 396 class EditUsersParserTest(unittest.TestCase): 395 397 _example_user_results = """ 396 <div id="bugzilla-body">397 <p>1 user found.</p>398 <table id="admin_table" border="1" cellpadding="4" cellspacing="0">399 <tr bgcolor="#6666FF">400 <th align="left">Edit user...401 </th>402 <th align="left">Real name403 </th>404 <th align="left">Account History405 </th>406 </tr>407 <tr>408 <td >409 <a href="editusers.cgi?action=edit&userid=1234&matchvalue=login_name&groupid=&grouprestrict=&matchtype=substr&matchstr=abarth%40webkit.org">410 abarth@webkit.org411 </a>412 </td>413 <td >414 Adam Barth415 </td>416 <td >417 <a href="editusers.cgi?action=activity&userid=1234&matchvalue=login_name&groupid=&grouprestrict=&matchtype=substr&matchstr=abarth%40webkit.org">418 View419 </a>420 </td>421 </tr>422 </table>423 """398 <div id="bugzilla-body"> 399 <p>1 user found.</p> 400 <table id="admin_table" border="1" cellpadding="4" cellspacing="0"> 401 <tr bgcolor="#6666FF"> 402 <th align="left">Edit user... 403 </th> 404 <th align="left">Real name 405 </th> 406 <th align="left">Account History 407 </th> 408 </tr> 409 <tr> 410 <td > 411 <a href="editusers.cgi?action=edit&userid=1234&matchvalue=login_name&groupid=&grouprestrict=&matchtype=substr&matchstr=abarth%40webkit.org"> 412 abarth@webkit.org 413 </a> 414 </td> 415 <td > 416 Adam Barth 417 </td> 418 <td > 419 <a href="editusers.cgi?action=activity&userid=1234&matchvalue=login_name&groupid=&grouprestrict=&matchtype=substr&matchstr=abarth%40webkit.org"> 420 View 421 </a> 422 </td> 423 </tr> 424 </table> 425 """ 424 426 425 427 _example_empty_user_results = """ … … 439 441 """ 440 442 441 def _assert_ parsed_logins(self, results_page, expected_logins):442 queries = BugzillaQueries(None)443 logins = queries._parse_logins_from_editusers_results(results_page)443 def _assert_login_userid_pairs(self, results_page, expected_logins): 444 parser = EditUsersParser() 445 logins = parser.login_userid_pairs_from_edit_user_results(results_page) 444 446 self.assertEquals(logins, expected_logins) 445 447 446 def test_parse_logins_from_editusers_results(self): 447 self._assert_parsed_logins(self._example_user_results, ["abarth@webkit.org"]) 448 self._assert_parsed_logins(self._example_empty_user_results, []) 448 def test_logins_from_editusers_results(self): 449 self._assert_login_userid_pairs(self._example_user_results, [("abarth@webkit.org", 1234)]) 450 self._assert_login_userid_pairs(self._example_empty_user_results, []) 451 452 _example_user_page = """<table class="main"><tr> 453 <th><label for="login">Login name:</label></th> 454 <td>eric@webkit.org 455 </td> 456 </tr> 457 <tr> 458 <th><label for="name">Real name:</label></th> 459 <td>Eric Seidel 460 </td> 461 </tr> 462 <tr> 463 <th>Group access:</th> 464 <td> 465 <table class="groups"> 466 <tr> 467 </tr> 468 <tr> 469 <th colspan="2">User is a member of these groups</th> 470 </tr> 471 <tr class="direct"> 472 <td class="checkbox"><input type="checkbox" 473 id="group_7" 474 name="group_7" 475 value="1" checked="checked" /></td> 476 <td class="groupname"> 477 <label for="group_7"> 478 <strong>canconfirm:</strong> 479 Can confirm a bug. 480 </label> 481 </td> 482 </tr> 483 <tr class="direct"> 484 <td class="checkbox"><input type="checkbox" 485 id="group_6" 486 name="group_6" 487 value="1" /></td> 488 <td class="groupname"> 489 <label for="group_6"> 490 <strong>editbugs:</strong> 491 Can edit all aspects of any bug. 492 /label> 493 </td> 494 </tr> 495 </table> 496 </td> 497 </tr> 498 499 <tr> 500 <th>Product responsibilities:</th> 501 <td> 502 <em>none</em> 503 </td> 504 </tr> 505 </table>""" 506 507 def test_user_dict_from_edit_user_page(self): 508 parser = EditUsersParser() 509 user_dict = parser.user_dict_from_edit_user_page(self._example_user_page) 510 expected_user_dict = {u'login': u'eric@webkit.org', u'groups': set(['canconfirm']), u'name': u'Eric Seidel'} 511 self.assertEqual(expected_user_dict, user_dict) -
trunk/Tools/Scripts/webkitpy/tool/commands/__init__.py
r92324 r95238 1 1 # Required for Python to search this directory for module files 2 2 3 from webkitpy.tool.commands.adduserstogroups import AddUsersToGroups 3 4 from webkitpy.tool.commands.bugfortest import BugForTest 4 5 from webkitpy.tool.commands.bugsearch import BugSearch … … 6 7 from webkitpy.tool.commands.earlywarningsystem import * 7 8 from webkitpy.tool.commands.expectations import OptimizeExpectations 9 from webkitpy.tool.commands.findusers import FindUsers 8 10 from webkitpy.tool.commands.gardenomatic import GardenOMatic 9 11 from webkitpy.tool.commands.openbugs import OpenBugs
Note: See TracChangeset
for help on using the changeset viewer.