Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: commit-queue/tools/count.py

Issue 135363007: Delete public commit queue to avoid confusion after move to internal repo (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « commit-queue/threadpool.py ('k') | commit-queue/tools/gather_stats.sh » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5 """Count commits by the commit queue."""
6
7 import datetime
8 import json
9 import logging
10 import optparse
11 import os
12 import re
13 import sys
14 from xml.etree import ElementTree
15
16 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
17
18 import find_depot_tools # pylint: disable=W0611
19 import subprocess2
20
21
22 def log(repo, args):
23 """If extra is True, grab one revision before and one after."""
24 args = args or []
25 out = subprocess2.check_output(
26 ['svn', 'log', '--with-all-revprops', '--xml', repo] + args)
27 data = {}
28 for logentry in ElementTree.XML(out).findall('logentry'):
29 date_str = logentry.find('date').text
30 date = datetime.datetime(*map(int, re.split('[^\d]', date_str)[:-1]))
31 entry = {
32 'author': logentry.find('author').text,
33 'date': date,
34 'msg': logentry.find('msg').text,
35 'revprops': {},
36 'commit-bot': False,
37 }
38 revprops = logentry.find('revprops')
39 if revprops is not None:
40 for revprop in revprops.findall('property'):
41 entry['revprops'][revprop.attrib['name']] = revprop.text
42 if revprop.attrib['name'] == 'commit-bot':
43 entry['commit-bot'] = True
44 data[logentry.attrib['revision']] = entry
45 return data
46
47
48 def log_dates(repo, start_date, days):
49 """Formats dates so 'svn log' does the right thing. Fetches everything in UTC.
50 """
51 # http://svnbook.red-bean.com/nightly/en/svn-book.html#svn.tour.revs.dates
52 if not days:
53 end_inclusive = datetime.date.today()
54 else:
55 end_inclusive = start_date + datetime.timedelta(days=days)
56 actual_days = (end_inclusive - start_date).days
57 print('Getting data from %s for %s days' % (start_date, actual_days))
58 range_str = (
59 '{%s 00:00:00 +0000}:{%s 00:00:00 +0000}' % (start_date, end_inclusive))
60 data = log(repo, ['-r', range_str])
61 # Strip off everything outside the range.
62 start_date_time = datetime.datetime(*start_date.timetuple()[:6])
63 if data:
64 first = sorted(data.keys())[0]
65 if data[first]['date'] < start_date_time:
66 del data[first]
67 # Strip the commit message to save space.
68 for item in data.itervalues():
69 del item['msg']
70 return data
71
72
73 def monday_last_week():
74 """Returns Monday in 'date' object."""
75 today = datetime.date.today()
76 last_week = today - datetime.timedelta(days=7)
77 return last_week - datetime.timedelta(days=(last_week.isoweekday() - 1))
78
79
80 class JSONEncoder(json.JSONEncoder):
81 def default(self, o): # pylint: disable=E0202
82 if isinstance(o, datetime.datetime):
83 return str(o)
84 return super(JSONEncoder, self)
85
86
87 def print_aligned(zipped_list):
88 max_len = max(len(i[0]) for i in zipped_list)
89 for author, count in zipped_list:
90 print('%*s: %d' % (max_len, author, count))
91
92
93 def print_data(log_data, stats_only, top):
94 # Calculate stats.
95 num_commit_bot = len([True for v in log_data.itervalues() if v['commit-bot']])
96 num_total_commits = len(log_data)
97 pourcent = 0.
98 if num_total_commits:
99 pourcent = float(num_commit_bot) * 100. / float(num_total_commits)
100 users = {}
101 for i in log_data.itervalues():
102 if i['commit-bot']:
103 users.setdefault(i['author'], 0)
104 users[i['author']] += 1
105
106 if not stats_only:
107 max_author_len = max(len(i['author']) for i in log_data.itervalues())
108 for revision in sorted(log_data.keys()):
109 entry = log_data[revision]
110 commit_bot = ' '
111 if entry['commit-bot']:
112 commit_bot = 'c'
113 print('%s %s %s %*s' % (
114 ('r%s' % revision).rjust(6),
115 commit_bot,
116 entry['date'].strftime('%Y-%m-%d %H:%M UTC'),
117 max_author_len,
118 entry['author']))
119 print('')
120
121 if top:
122 top_users = sorted(
123 users.iteritems(), key=lambda x: x[1], reverse=True)[:top]
124 top_commits = sum(x[1] for x in top_users)
125 p_u = 100. * len(top_users) / len(users)
126 p_c = 100. * top_commits / num_commit_bot
127 print(
128 'Top users: %6d out of %6d total users %6.2f%%' %
129 (len(top_users), len(users), p_u))
130 print(
131 ' Committed %6d out of %6d CQ\'ed commits %5.2f%%' %
132 (top_commits, num_commit_bot, p_c))
133 if not stats_only:
134 print_aligned(top_users)
135
136 non_committers = sorted(
137 ( (u, c) for u, c in users.iteritems()
138 if not u.endswith('@chromium.org')),
139 key=lambda x: x[1], reverse=True)
140 if non_committers:
141 print('')
142 n_c_commits = sum(x[1] for x in non_committers)
143 p_u = 100. * len(non_committers) / len(users)
144 p_c = 100. * n_c_commits / num_commit_bot
145 print(
146 'Non-committers: %6d out of %6d total users %6.2f%%' %
147 (len(non_committers), len(users), p_u))
148 print(
149 ' Committed %6d out of %6d CQ\'ed commits %5.2f%%' %
150 (n_c_commits, num_commit_bot, p_c))
151 print('')
152 print('Top domains')
153 domains = {}
154 for user, count in non_committers:
155 domain = user.split('@', 1)[1]
156 domains.setdefault(domain, 0)
157 domains[domain] += count
158 domains_stats = sorted(
159 ((k, v) for k, v in domains.iteritems()),
160 key=lambda x: x[1], reverse=True)
161 print_aligned(domains_stats)
162 if not stats_only:
163 print_aligned(non_committers)
164
165 print('')
166 print('Total commits: %6d' % num_total_commits)
167 print(
168 'Total commits by commit bot: %6d (%6.1f%%)' % (num_commit_bot, pourcent))
169
170
171 def main():
172 parser = optparse.OptionParser(
173 description=sys.modules['__main__'].__doc__)
174 parser.add_option('-v', '--verbose', action='store_true')
175 parser.add_option(
176 '-r', '--repo', default='http://src.chromium.org/svn/trunk')
177 parser.add_option('-s', '--since', action='store')
178 parser.add_option('-d', '--days', type=int, default=7)
179 parser.add_option('--all', action='store_true', help='Get ALL the revisions!')
180 parser.add_option('--dump', help='Dump json in file')
181 parser.add_option('--read', help='Read the data from a file')
182 parser.add_option('-o', '--stats_only', action='store_true')
183 parser.add_option('--top', default=20, type='int')
184 options, args = parser.parse_args(None)
185 if args:
186 parser.error('Unsupported args: %s' % args)
187 logging.basicConfig(
188 level=(logging.DEBUG if options.verbose else logging.ERROR))
189
190 # By default, grab stats for last week.
191 if not options.since:
192 options.since = monday_last_week()
193 else:
194 options.since = datetime.date(*map(int, re.split('[^\d]', options.since)))
195
196 if options.read:
197 if options.dump:
198 parser.error('Can\'t use --dump and --read simultaneously')
199 log_data = json.load(open(options.read, 'r'))
200 for entry in log_data.itervalues():
201 # Convert strings like "2012-09-04 01:14:43.785581" to a datetime object.
202 entry['date'] = datetime.datetime.strptime(
203 entry['date'], '%Y-%m-%d %H:%M:%S.%f')
204 else:
205 if options.all:
206 log_data = log(options.repo, [])
207 else:
208 log_data = log_dates(options.repo, options.since, options.days)
209 if options.dump:
210 json.dump(log_data, open(options.dump, 'w'), cls=JSONEncoder)
211
212 print_data(log_data, options.stats_only, options.top)
213 return 0
214
215
216 if __name__ == '__main__':
217 sys.exit(main())
OLDNEW
« no previous file with comments | « commit-queue/threadpool.py ('k') | commit-queue/tools/gather_stats.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698