1*1fa4b3daSHector Dearman#!/usr/bin/env python 2*1fa4b3daSHector Dearman# Copyright 2015 The Chromium Authors. All rights reserved. 3*1fa4b3daSHector Dearman# Use of this source code is governed by a BSD-style license that can be 4*1fa4b3daSHector Dearman# found in the LICENSE file. 5*1fa4b3daSHector Dearman 6*1fa4b3daSHector Dearman"""Print statistics about the rate of commits to a repository.""" 7*1fa4b3daSHector Dearman 8*1fa4b3daSHector Dearmanimport datetime 9*1fa4b3daSHector Dearmanimport itertools 10*1fa4b3daSHector Dearmanimport json 11*1fa4b3daSHector Dearmanimport math 12*1fa4b3daSHector Dearmanimport urllib 13*1fa4b3daSHector Dearmanimport urllib2 14*1fa4b3daSHector Dearman 15*1fa4b3daSHector Dearman 16*1fa4b3daSHector Dearman_BASE_URL = 'https://chromium.googlesource.com' 17*1fa4b3daSHector Dearman# Can be up to 10,000. 18*1fa4b3daSHector Dearman_REVISION_COUNT = 10000 19*1fa4b3daSHector Dearman 20*1fa4b3daSHector Dearman_REPOSITORIES = [ 21*1fa4b3daSHector Dearman 'chromium/src', 22*1fa4b3daSHector Dearman 'angle/angle', 23*1fa4b3daSHector Dearman 'skia', 24*1fa4b3daSHector Dearman 'v8/v8', 25*1fa4b3daSHector Dearman] 26*1fa4b3daSHector Dearman 27*1fa4b3daSHector Dearman 28*1fa4b3daSHector Dearmandef Pairwise(iterable): 29*1fa4b3daSHector Dearman """s -> (s0,s1), (s1,s2), (s2, s3), ...""" 30*1fa4b3daSHector Dearman a, b = itertools.tee(iterable) 31*1fa4b3daSHector Dearman next(b, None) 32*1fa4b3daSHector Dearman return itertools.izip(a, b) 33*1fa4b3daSHector Dearman 34*1fa4b3daSHector Dearman 35*1fa4b3daSHector Dearmandef Percentile(data, percentile): 36*1fa4b3daSHector Dearman """Find a percentile of a list of values. 37*1fa4b3daSHector Dearman 38*1fa4b3daSHector Dearman Parameters: 39*1fa4b3daSHector Dearman data: A sorted list of values. 40*1fa4b3daSHector Dearman percentile: The percentile to look up, from 0.0 to 1.0. 41*1fa4b3daSHector Dearman 42*1fa4b3daSHector Dearman Returns: 43*1fa4b3daSHector Dearman The percentile. 44*1fa4b3daSHector Dearman 45*1fa4b3daSHector Dearman Raises: 46*1fa4b3daSHector Dearman ValueError: If data is empty. 47*1fa4b3daSHector Dearman """ 48*1fa4b3daSHector Dearman if not data: 49*1fa4b3daSHector Dearman raise ValueError() 50*1fa4b3daSHector Dearman 51*1fa4b3daSHector Dearman k = (len(data) - 1) * percentile 52*1fa4b3daSHector Dearman f = math.floor(k) 53*1fa4b3daSHector Dearman c = math.ceil(k) 54*1fa4b3daSHector Dearman 55*1fa4b3daSHector Dearman if f == c: 56*1fa4b3daSHector Dearman return data[int(k)] 57*1fa4b3daSHector Dearman return data[int(f)] * (c - k) + data[int(c)] * (k - f) 58*1fa4b3daSHector Dearman 59*1fa4b3daSHector Dearman 60*1fa4b3daSHector Dearmandef CommitTimes(repository, revision_count): 61*1fa4b3daSHector Dearman parameters = urllib.urlencode((('n', revision_count), ('format', 'JSON'))) 62*1fa4b3daSHector Dearman url = '%s/%s/+log?%s' % (_BASE_URL, urllib.quote(repository), parameters) 63*1fa4b3daSHector Dearman data = json.loads(''.join(urllib2.urlopen(url).read().splitlines()[1:])) 64*1fa4b3daSHector Dearman 65*1fa4b3daSHector Dearman commit_times = [] 66*1fa4b3daSHector Dearman for revision in data['log']: 67*1fa4b3daSHector Dearman commit_time_string = revision['committer']['time'] 68*1fa4b3daSHector Dearman commit_time = datetime.datetime.strptime( 69*1fa4b3daSHector Dearman commit_time_string, '%a %b %d %H:%M:%S %Y') 70*1fa4b3daSHector Dearman commit_times.append(commit_time - datetime.timedelta(hours=7)) 71*1fa4b3daSHector Dearman 72*1fa4b3daSHector Dearman return commit_times 73*1fa4b3daSHector Dearman 74*1fa4b3daSHector Dearman 75*1fa4b3daSHector Dearmandef IsWeekday(time): 76*1fa4b3daSHector Dearman return time.weekday() >= 0 and time.weekday() < 5 77*1fa4b3daSHector Dearman 78*1fa4b3daSHector Dearman 79*1fa4b3daSHector Dearmandef main(): 80*1fa4b3daSHector Dearman for repository in _REPOSITORIES: 81*1fa4b3daSHector Dearman commit_times = CommitTimes(repository, _REVISION_COUNT) 82*1fa4b3daSHector Dearman 83*1fa4b3daSHector Dearman commit_durations = [] 84*1fa4b3daSHector Dearman for time1, time2 in Pairwise(commit_times): 85*1fa4b3daSHector Dearman #if not (IsWeekday(time1) and IsWeekday(time2)): 86*1fa4b3daSHector Dearman # continue 87*1fa4b3daSHector Dearman commit_durations.append((time1 - time2).total_seconds() / 60.) 88*1fa4b3daSHector Dearman commit_durations.sort() 89*1fa4b3daSHector Dearman 90*1fa4b3daSHector Dearman print 'REPOSITORY:', repository 91*1fa4b3daSHector Dearman print 'Start Date:', min(commit_times), 'PDT' 92*1fa4b3daSHector Dearman print ' End Date:', max(commit_times), 'PDT' 93*1fa4b3daSHector Dearman print ' Duration:', max(commit_times) - min(commit_times) 94*1fa4b3daSHector Dearman print ' n:', len(commit_times) 95*1fa4b3daSHector Dearman 96*1fa4b3daSHector Dearman for p in (0.25, 0.50, 0.90): 97*1fa4b3daSHector Dearman percentile = Percentile(commit_durations, p) 98*1fa4b3daSHector Dearman print '%3d%% commit duration:' % (p * 100), '%6.1fm' % percentile 99*1fa4b3daSHector Dearman mean = math.fsum(commit_durations) / len(commit_durations) 100*1fa4b3daSHector Dearman print 'Mean commit duration:', '%6.1fm' % mean 101*1fa4b3daSHector Dearman print 102*1fa4b3daSHector Dearman 103*1fa4b3daSHector Dearman 104*1fa4b3daSHector Dearmanif __name__ == '__main__': 105*1fa4b3daSHector Dearman main() 106