OLD | NEW |
| (Empty) |
1 # Copyright (c) 2009 Google Inc. All rights reserved. | |
2 # Copyright (c) 2009 Apple Inc. All rights reserved. | |
3 # Copyright (c) 2010 Research In Motion Limited. All rights reserved. | |
4 # | |
5 # Redistribution and use in source and binary forms, with or without | |
6 # modification, are permitted provided that the following conditions are | |
7 # met: | |
8 # | |
9 # * Redistributions of source code must retain the above copyright | |
10 # notice, this list of conditions and the following disclaimer. | |
11 # * Redistributions in binary form must reproduce the above | |
12 # copyright notice, this list of conditions and the following disclaimer | |
13 # in the documentation and/or other materials provided with the | |
14 # distribution. | |
15 # * Neither the name of Google Inc. nor the names of its | |
16 # contributors may be used to endorse or promote products derived from | |
17 # this software without specific prior written permission. | |
18 # | |
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | |
31 import re | |
32 | |
33 from .attachment import Attachment | |
34 | |
35 | |
36 class Bug(object): | |
37 # FIXME: This class is kinda a hack for now. It exists so we have one | |
38 # place to hold bug logic, even if much of the code deals with | |
39 # dictionaries still. | |
40 | |
41 def __init__(self, bug_dictionary, bugzilla): | |
42 self.bug_dictionary = bug_dictionary | |
43 self._bugzilla = bugzilla | |
44 | |
45 def id(self): | |
46 return self.bug_dictionary["id"] | |
47 | |
48 def title(self): | |
49 # FIXME: Do we need to HTML unescape the title? | |
50 return self.bug_dictionary["title"] | |
51 | |
52 def reporter_email(self): | |
53 return self.bug_dictionary["reporter_email"] | |
54 | |
55 def assigned_to_email(self): | |
56 return self.bug_dictionary["assigned_to_email"] | |
57 | |
58 def cc_emails(self): | |
59 return self.bug_dictionary["cc_emails"] | |
60 | |
61 # FIXME: This information should be stored in some sort of webkit_config.py
instead of here. | |
62 unassigned_emails = frozenset([ | |
63 "webkit-unassigned@lists.webkit.org", | |
64 "webkit-qt-unassigned@trolltech.com", | |
65 ]) | |
66 | |
67 def is_unassigned(self): | |
68 return self.assigned_to_email() in self.unassigned_emails | |
69 | |
70 def status(self): | |
71 return self.bug_dictionary["bug_status"] | |
72 | |
73 # Bugzilla has many status states we don't really use in WebKit: | |
74 # https://bugs.webkit.org/page.cgi?id=fields.html#status | |
75 _open_states = ["UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED"] | |
76 _closed_states = ["RESOLVED", "VERIFIED", "CLOSED"] | |
77 | |
78 def is_open(self): | |
79 return self.status() in self._open_states | |
80 | |
81 def is_closed(self): | |
82 return not self.is_open() | |
83 | |
84 def duplicate_of(self): | |
85 return self.bug_dictionary.get('dup_id', None) | |
86 | |
87 # Rarely do we actually want obsolete attachments | |
88 def attachments(self, include_obsolete=False): | |
89 attachments = self.bug_dictionary["attachments"] | |
90 if not include_obsolete: | |
91 attachments = filter(lambda attachment: | |
92 not attachment["is_obsolete"], attachments) | |
93 return [Attachment(attachment, self) for attachment in attachments] | |
94 | |
95 def patches(self, include_obsolete=False): | |
96 return [patch for patch in self.attachments(include_obsolete) | |
97 if patch.is_patch()] | |
98 | |
99 def unreviewed_patches(self): | |
100 return [patch for patch in self.patches() if patch.review() == "?"] | |
101 | |
102 def reviewed_patches(self, include_invalid=False): | |
103 patches = [patch for patch in self.patches() if patch.review() == "+"] | |
104 if include_invalid: | |
105 return patches | |
106 # Checking reviewer() ensures that it was both reviewed and has a valid | |
107 # reviewer. | |
108 return filter(lambda patch: patch.reviewer(), patches) | |
109 | |
110 def commit_queued_patches(self, include_invalid=False): | |
111 patches = [patch for patch in self.patches() | |
112 if patch.commit_queue() == "+"] | |
113 if include_invalid: | |
114 return patches | |
115 # Checking committer() ensures that it was both commit-queue+'d and has | |
116 # a valid committer. | |
117 return filter(lambda patch: patch.committer(), patches) | |
118 | |
119 def comments(self): | |
120 return self.bug_dictionary["comments"] | |
121 | |
122 def is_in_comments(self, message): | |
123 for comment in self.comments(): | |
124 if message in comment["text"]: | |
125 return True | |
126 return False | |
127 | |
128 def commit_revision(self): | |
129 # Sort the comments in reverse order as we want the latest committed rev
ision. | |
130 r = re.compile("Committed r(?P<svn_revision>\d+)") | |
131 for comment in sorted(self.comments(), reverse=True): | |
132 rev = r.search(comment['text']) | |
133 if rev: | |
134 return int(rev.group('svn_revision')) | |
135 | |
136 return None | |
OLD | NEW |