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 return sum(self.latencies) / float(len(self.latencies)) |
83 (self.number_latency + 1.)) | 79 |
84 self.number_latency += 1 | 80 @property |
81 def median_latency(self): | |
82 length = len(self.latencies) | |
83 latencies = sorted(self.latencies) | |
84 if length & 1: | |
Dirk Pranke
2012/03/21 00:54:14
are you sure this isn't backwards?
M-A Ruel
2012/03/21 01:01:16
Thanks, indeed I had it backward.
| |
85 return (latencies[length/2] + latencies[length/2+1]) / 2. | |
86 else: | |
87 return latencies[length/2] | |
88 | |
89 @property | |
90 def percent_done(self): | |
91 if not self.total: | |
92 return 0 | |
93 return self.actually_reviewed * 100. / self.total | |
94 | |
95 @property | |
96 def review_per_day(self): | |
97 if not self.days: | |
98 return 0 | |
99 return self.total * 1. / self.days | |
100 | |
101 @property | |
102 def review_done_per_day(self): | |
103 if not self.days: | |
104 return 0 | |
105 return self.actually_reviewed * 1. / self.days | |
85 | 106 |
86 def finalize(self, first_day, last_day): | 107 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: | 108 if self.actually_reviewed: |
90 self.percent_lgtm = (self.lgtms * 100. / self.actually_reviewed) | 109 self.percent_lgtm = (self.lgtms * 100. / self.actually_reviewed) |
91 self.percent_drive_by = (self.drive_by * 100. / self.actually_reviewed) | 110 self.percent_drive_by = (self.drive_by * 100. / self.actually_reviewed) |
92 self.percent_not_requested = ( | 111 self.percent_not_requested = ( |
93 self.not_requested * 100. / self.actually_reviewed) | 112 self.not_requested * 100. / self.actually_reviewed) |
94 if first_day and last_day: | 113 if first_day and last_day: |
95 self.days = (to_datetime(last_day) - to_datetime(first_day)).days + 1 | 114 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 | 115 |
100 | 116 |
101 def _process_issue_lgtms(issue, reviewer, stats): | 117 def _process_issue_lgtms(issue, reviewer, stats): |
102 """Calculates LGTMs stats.""" | 118 """Calculates LGTMs stats.""" |
103 stats.actually_reviewed += 1 | 119 stats.actually_reviewed += 1 |
104 reviewer_lgtms = len([ | 120 reviewer_lgtms = len([ |
105 msg for msg in issue['messages'] | 121 msg for msg in issue['messages'] |
106 if msg['approval'] and msg['sender'] == reviewer]) | 122 if msg['approval'] and msg['sender'] == reviewer]) |
107 if reviewer_lgtms > 1: | 123 if reviewer_lgtms > 1: |
108 stats.multiple_lgtms += 1 | 124 stats.multiple_lgtms += 1 |
(...skipping 29 matching lines...) Expand all Loading... | |
138 | 154 |
139 if first_msg_from_owner and msg['sender'] == reviewer: | 155 if first_msg_from_owner and msg['sender'] == reviewer: |
140 delta = msg['date'] - first_msg_from_owner['date'] | 156 delta = msg['date'] - first_msg_from_owner['date'] |
141 latency = delta.seconds + delta.days * 24 * 3600 | 157 latency = delta.seconds + delta.days * 24 * 3600 |
142 break | 158 break |
143 | 159 |
144 if latency is None: | 160 if latency is None: |
145 stats.not_requested += 1 | 161 stats.not_requested += 1 |
146 return '<no rqst sent>' | 162 return '<no rqst sent>' |
147 if latency > 0: | 163 if latency > 0: |
148 stats.add_latency(latency) | 164 stats.latencies.append(latency) |
149 else: | 165 else: |
150 stats.not_requested += 1 | 166 stats.not_requested += 1 |
151 return to_time(latency) | 167 return to_time(latency) |
152 | 168 |
153 | 169 |
154 def _process_issue(issue): | 170 def _process_issue(issue): |
155 """Preprocesses the issue to simplify the remaining code.""" | 171 """Preprocesses the issue to simplify the remaining code.""" |
156 issue['owner_email'] = username(issue['owner_email']) | 172 issue['owner_email'] = username(issue['owner_email']) |
157 issue['reviewers'] = set(username(r) for r in issue['reviewers']) | 173 issue['reviewers'] = set(username(r) for r in issue['reviewers']) |
158 # By default, hide commit-bot. | 174 # By default, hide commit-bot. |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
232 print >> sys.stderr, ( | 248 print >> sys.stderr, ( |
233 '%4d were requested over IM or irc (%5.1f%% of reviews done).' % ( | 249 '%4d were requested over IM or irc (%5.1f%% of reviews done).' % ( |
234 stats.not_requested, stats.percent_not_requested)) | 250 stats.not_requested, stats.percent_not_requested)) |
235 print >> sys.stderr, ( | 251 print >> sys.stderr, ( |
236 ('%4d issues LGTM\'d (%5.1f%% of reviews done),' | 252 ('%4d issues LGTM\'d (%5.1f%% of reviews done),' |
237 ' gave multiple LGTMs on %d issues.') % ( | 253 ' gave multiple LGTMs on %d issues.') % ( |
238 stats.lgtms, stats.percent_lgtm, stats.multiple_lgtms)) | 254 stats.lgtms, stats.percent_lgtm, stats.multiple_lgtms)) |
239 print >> sys.stderr, ( | 255 print >> sys.stderr, ( |
240 'Average latency from request to first comment is %s.' % | 256 'Average latency from request to first comment is %s.' % |
241 to_time(stats.average_latency)) | 257 to_time(stats.average_latency)) |
258 print >> sys.stderr, ( | |
259 'Median latency from request to first comment is %s.' % | |
260 to_time(stats.median_latency)) | |
242 | 261 |
243 | 262 |
244 def print_count(reviewer, created_after, created_before, instance_url): | 263 def print_count(reviewer, created_after, created_before, instance_url): |
245 remote = rietveld.Rietveld(instance_url, None, None) | 264 remote = rietveld.Rietveld(instance_url, None, None) |
246 print len(list(remote.search( | 265 print len(list(remote.search( |
247 reviewer=reviewer, | 266 reviewer=reviewer, |
248 created_after=created_after, | 267 created_after=created_after, |
249 created_before=created_before, | 268 created_before=created_before, |
250 keys_only=True))) | 269 keys_only=True))) |
251 | 270 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
325 print_reviews( | 344 print_reviews( |
326 options.reviewer, | 345 options.reviewer, |
327 options.begin, | 346 options.begin, |
328 options.end, | 347 options.end, |
329 options.instance_url) | 348 options.instance_url) |
330 return 0 | 349 return 0 |
331 | 350 |
332 | 351 |
333 if __name__ == '__main__': | 352 if __name__ == '__main__': |
334 sys.exit(main()) | 353 sys.exit(main()) |
OLD | NEW |