OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Get rietveld stats about the review you done, or forgot to do. | 6 """Get rietveld stats about the review you done, or forgot to do. |
7 | 7 |
8 Example: | 8 Example: |
9 - my_reviews.py -r me@chromium.org -Q for stats for last quarter. | 9 - my_reviews.py -r me@chromium.org -Q for stats for last quarter. |
10 """ | 10 """ |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
54 if seconds > 0 and not out: | 54 if seconds > 0 and not out: |
55 # Skip seconds unless there's only seconds. | 55 # Skip seconds unless there's only seconds. |
56 out.append('%02ds' % seconds) | 56 out.append('%02ds' % seconds) |
57 return prefix + ''.join(out) | 57 return prefix + ''.join(out) |
58 | 58 |
59 | 59 |
60 class Stats(object): | 60 class Stats(object): |
61 def __init__(self): | 61 def __init__(self): |
62 self.total = 0 | 62 self.total = 0 |
63 self.actually_reviewed = 0 | 63 self.actually_reviewed = 0 |
64 self.average_latency = 0. | 64 self.latencies = [] |
65 self.number_latency = 0 | |
66 self.lgtms = 0 | 65 self.lgtms = 0 |
67 self.multiple_lgtms = 0 | 66 self.multiple_lgtms = 0 |
68 self.drive_by = 0 | 67 self.drive_by = 0 |
69 self.not_requested = 0 | 68 self.not_requested = 0 |
70 self.self_review = 0 | 69 self.self_review = 0 |
71 | 70 |
72 self.percent_done = 0. | |
73 self.percent_lgtm = 0. | 71 self.percent_lgtm = 0. |
74 self.percent_drive_by = 0. | 72 self.percent_drive_by = 0. |
75 self.percent_not_requested = 0. | 73 self.percent_not_requested = 0. |
76 self.days = 0 | 74 self.days = 0 |
77 self.review_per_day = 0. | |
78 self.review_done_per_day = 0. | |
79 | 75 |
80 def add_latency(self, latency): | 76 @property |
81 self.average_latency = ( | 77 def average_latency(self): |
82 (self.average_latency * self.number_latency + latency) / | 78 if not self.latencies: |
83 (self.number_latency + 1.)) | 79 return 0 |
84 self.number_latency += 1 | 80 return sum(self.latencies) / float(len(self.latencies)) |
| 81 |
| 82 @property |
| 83 def median_latency(self): |
| 84 if not self.latencies: |
| 85 return 0 |
| 86 length = len(self.latencies) |
| 87 latencies = sorted(self.latencies) |
| 88 if (length & 1) == 0: |
| 89 return (latencies[length/2] + latencies[length/2+1]) / 2. |
| 90 else: |
| 91 return latencies[length/2] |
| 92 |
| 93 @property |
| 94 def percent_done(self): |
| 95 if not self.total: |
| 96 return 0 |
| 97 return self.actually_reviewed * 100. / self.total |
| 98 |
| 99 @property |
| 100 def review_per_day(self): |
| 101 if not self.days: |
| 102 return 0 |
| 103 return self.total * 1. / self.days |
| 104 |
| 105 @property |
| 106 def review_done_per_day(self): |
| 107 if not self.days: |
| 108 return 0 |
| 109 return self.actually_reviewed * 1. / self.days |
85 | 110 |
86 def finalize(self, first_day, last_day): | 111 def finalize(self, first_day, last_day): |
87 if self.total: | |
88 self.percent_done = (self.actually_reviewed * 100. / self.total) | |
89 if self.actually_reviewed: | 112 if self.actually_reviewed: |
90 self.percent_lgtm = (self.lgtms * 100. / self.actually_reviewed) | 113 self.percent_lgtm = (self.lgtms * 100. / self.actually_reviewed) |
91 self.percent_drive_by = (self.drive_by * 100. / self.actually_reviewed) | 114 self.percent_drive_by = (self.drive_by * 100. / self.actually_reviewed) |
92 self.percent_not_requested = ( | 115 self.percent_not_requested = ( |
93 self.not_requested * 100. / self.actually_reviewed) | 116 self.not_requested * 100. / self.actually_reviewed) |
94 if first_day and last_day: | 117 if first_day and last_day: |
95 self.days = (to_datetime(last_day) - to_datetime(first_day)).days + 1 | 118 self.days = (to_datetime(last_day) - to_datetime(first_day)).days + 1 |
96 if self.days: | |
97 self.review_per_day = self.total * 1. / self.days | |
98 self.review_done_per_day = self.actually_reviewed * 1. / self.days | |
99 | 119 |
100 | 120 |
101 def _process_issue_lgtms(issue, reviewer, stats): | 121 def _process_issue_lgtms(issue, reviewer, stats): |
102 """Calculates LGTMs stats.""" | 122 """Calculates LGTMs stats.""" |
103 stats.actually_reviewed += 1 | 123 stats.actually_reviewed += 1 |
104 reviewer_lgtms = len([ | 124 reviewer_lgtms = len([ |
105 msg for msg in issue['messages'] | 125 msg for msg in issue['messages'] |
106 if msg['approval'] and msg['sender'] == reviewer]) | 126 if msg['approval'] and msg['sender'] == reviewer]) |
107 if reviewer_lgtms > 1: | 127 if reviewer_lgtms > 1: |
108 stats.multiple_lgtms += 1 | 128 stats.multiple_lgtms += 1 |
(...skipping 29 matching lines...) Expand all Loading... |
138 | 158 |
139 if first_msg_from_owner and msg['sender'] == reviewer: | 159 if first_msg_from_owner and msg['sender'] == reviewer: |
140 delta = msg['date'] - first_msg_from_owner['date'] | 160 delta = msg['date'] - first_msg_from_owner['date'] |
141 latency = delta.seconds + delta.days * 24 * 3600 | 161 latency = delta.seconds + delta.days * 24 * 3600 |
142 break | 162 break |
143 | 163 |
144 if latency is None: | 164 if latency is None: |
145 stats.not_requested += 1 | 165 stats.not_requested += 1 |
146 return '<no rqst sent>' | 166 return '<no rqst sent>' |
147 if latency > 0: | 167 if latency > 0: |
148 stats.add_latency(latency) | 168 stats.latencies.append(latency) |
149 else: | 169 else: |
150 stats.not_requested += 1 | 170 stats.not_requested += 1 |
151 return to_time(latency) | 171 return to_time(latency) |
152 | 172 |
153 | 173 |
154 def _process_issue(issue): | 174 def _process_issue(issue): |
155 """Preprocesses the issue to simplify the remaining code.""" | 175 """Preprocesses the issue to simplify the remaining code.""" |
156 issue['owner_email'] = username(issue['owner_email']) | 176 issue['owner_email'] = username(issue['owner_email']) |
157 issue['reviewers'] = set(username(r) for r in issue['reviewers']) | 177 issue['reviewers'] = set(username(r) for r in issue['reviewers']) |
158 # By default, hide commit-bot. | 178 # By default, hide commit-bot. |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 print >> sys.stderr, ( | 252 print >> sys.stderr, ( |
233 '%4d were requested over IM or irc (%5.1f%% of reviews done).' % ( | 253 '%4d were requested over IM or irc (%5.1f%% of reviews done).' % ( |
234 stats.not_requested, stats.percent_not_requested)) | 254 stats.not_requested, stats.percent_not_requested)) |
235 print >> sys.stderr, ( | 255 print >> sys.stderr, ( |
236 ('%4d issues LGTM\'d (%5.1f%% of reviews done),' | 256 ('%4d issues LGTM\'d (%5.1f%% of reviews done),' |
237 ' gave multiple LGTMs on %d issues.') % ( | 257 ' gave multiple LGTMs on %d issues.') % ( |
238 stats.lgtms, stats.percent_lgtm, stats.multiple_lgtms)) | 258 stats.lgtms, stats.percent_lgtm, stats.multiple_lgtms)) |
239 print >> sys.stderr, ( | 259 print >> sys.stderr, ( |
240 'Average latency from request to first comment is %s.' % | 260 'Average latency from request to first comment is %s.' % |
241 to_time(stats.average_latency)) | 261 to_time(stats.average_latency)) |
| 262 print >> sys.stderr, ( |
| 263 'Median latency from request to first comment is %s.' % |
| 264 to_time(stats.median_latency)) |
242 | 265 |
243 | 266 |
244 def print_count(reviewer, created_after, created_before, instance_url): | 267 def print_count(reviewer, created_after, created_before, instance_url): |
245 remote = rietveld.Rietveld(instance_url, None, None) | 268 remote = rietveld.Rietveld(instance_url, None, None) |
246 print len(list(remote.search( | 269 print len(list(remote.search( |
247 reviewer=reviewer, | 270 reviewer=reviewer, |
248 created_after=created_after, | 271 created_after=created_after, |
249 created_before=created_before, | 272 created_before=created_before, |
250 keys_only=True))) | 273 keys_only=True))) |
251 | 274 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
325 print_reviews( | 348 print_reviews( |
326 options.reviewer, | 349 options.reviewer, |
327 options.begin, | 350 options.begin, |
328 options.end, | 351 options.end, |
329 options.instance_url) | 352 options.instance_url) |
330 return 0 | 353 return 0 |
331 | 354 |
332 | 355 |
333 if __name__ == '__main__': | 356 if __name__ == '__main__': |
334 sys.exit(main()) | 357 sys.exit(main()) |
OLD | NEW |