# Copyright (c) 2009 Google Inc. All rights reserved. # Copyright (c) 2009 Apple Inc. All rights reserved. # Copyright (c) 2010 Research In Motion Limited. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import re from .attachment import Attachment class Bug(object): # FIXME: This class is kinda a hack for now. It exists so we have one # place to hold bug logic, even if much of the code deals with # dictionaries still. def __init__(self, bug_dictionary, bugzilla): self.bug_dictionary = bug_dictionary self._bugzilla = bugzilla def id(self): return self.bug_dictionary["id"] def title(self): # FIXME: Do we need to HTML unescape the title? return self.bug_dictionary["title"] def reporter_email(self): return self.bug_dictionary["reporter_email"] def assigned_to_email(self): return self.bug_dictionary["assigned_to_email"] def cc_emails(self): return self.bug_dictionary["cc_emails"] # FIXME: This information should be stored in some sort of webkit_config.py instead of here. unassigned_emails = frozenset([ "webkit-unassigned@lists.webkit.org", "webkit-qt-unassigned@trolltech.com", ]) def is_unassigned(self): return self.assigned_to_email() in self.unassigned_emails def status(self): return self.bug_dictionary["bug_status"] # Bugzilla has many status states we don't really use in WebKit: # https://bugs.webkit.org/page.cgi?id=fields.html#status _open_states = ["UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED"] _closed_states = ["RESOLVED", "VERIFIED", "CLOSED"] def is_open(self): return self.status() in self._open_states def is_closed(self): return not self.is_open() def duplicate_of(self): return self.bug_dictionary.get('dup_id', None) # Rarely do we actually want obsolete attachments def attachments(self, include_obsolete=False): attachments = self.bug_dictionary["attachments"] if not include_obsolete: attachments = filter(lambda attachment: not attachment["is_obsolete"], attachments) return [Attachment(attachment, self) for attachment in attachments] def patches(self, include_obsolete=False): return [patch for patch in self.attachments(include_obsolete) if patch.is_patch()] def unreviewed_patches(self): return [patch for patch in self.patches() if patch.review() == "?"] def reviewed_patches(self, include_invalid=False): patches = [patch for patch in self.patches() if patch.review() == "+"] if include_invalid: return patches # Checking reviewer() ensures that it was both reviewed and has a valid # reviewer. return filter(lambda patch: patch.reviewer(), patches) def commit_queued_patches(self, include_invalid=False): patches = [patch for patch in self.patches() if patch.commit_queue() == "+"] if include_invalid: return patches # Checking committer() ensures that it was both commit-queue+'d and has # a valid committer. return filter(lambda patch: patch.committer(), patches) def comments(self): return self.bug_dictionary["comments"] def is_in_comments(self, message): for comment in self.comments(): if message in comment["text"]: return True return False def commit_revision(self): # Sort the comments in reverse order as we want the latest committed revision. r = re.compile("Committed r(?P\d+)") for comment in sorted(self.comments(), reverse=True): rev = r.search(comment['text']) if rev: return int(rev.group('svn_revision')) return None