diff --git a/git-stats b/git-stats index c8cf844..75b80af 100755 --- a/git-stats +++ b/git-stats @@ -14,8 +14,8 @@ import sys import time import zlib -IMAGE_TYPE = 'svg' # everything that gnuplot accepts, I guess -GNUPLOT_IMAGE_SPECIFICATIONS = {'svg':'','png':'transparent'} +IMAGE_TYPE = 'svg' # everything that gnuplot accepts, I guess +GNUPLOT_IMAGE_SPECIFICATIONS = {'svg':'','png':'transparent'} GNUPLOT_COMMON = 'set terminal {0} {1}\nset size 1.0,0.5\n'.format(IMAGE_TYPE, GNUPLOT_IMAGE_SPECIFICATIONS[IMAGE_TYPE] ) ON_LINUX = (platform.system() == 'Linux') @@ -41,7 +41,7 @@ def getpipeoutput(cmds, quiet = False): global exectime_external start = time.time() if not quiet and ON_LINUX and os.isatty(1): - print '>> ' + ' | '.join(cmds), + print('>> ' + ' | '.join(cmds), end=' ') sys.stdout.flush() p0 = subprocess.Popen(cmds[0], stdout = subprocess.PIPE, shell = True) p = p0 @@ -52,17 +52,17 @@ def getpipeoutput(cmds, quiet = False): end = time.time() if not quiet: if ON_LINUX and os.isatty(1): - print '\r', - print '[%.5f] >> %s' % (end - start, ' | '.join(cmds)) + print('\r', end=' ') + print('[%.5f] >> %s' % (end - start, ' | '.join(cmds))) exectime_external += (end - start) - return output.rstrip('\n') + return output.rstrip(b'\n').decode('utf-8') def getkeyssortedbyvalues(dict): - return map(lambda el : el[1], sorted(map(lambda el : (el[1], el[0]), dict.items()))) + return [el[1] for el in sorted([(el[1], el[0]) for el in list(dict.items())])] # dict['author'] = { 'commits': 512 } - ...key(dict, 'commits') def getkeyssortedbyvaluekey(d, key): - return map(lambda el : el[1], sorted(map(lambda el : (d[el][key], el), d.keys()))) + return [el[1] for el in sorted([(d[el][key], el) for el in list(d.keys())])] VERSION = 0 def getversion(): @@ -76,20 +76,20 @@ class DataCollector: def __init__(self): self.stamp_created = time.time() self.cache = {} - + ## # This should be the main function to extract data from the repository. def collect(self, dir): self.dir = dir self.projectname = os.path.basename(os.path.abspath(dir)) - + ## # Load cacheable data def loadCache(self, cachefile): if not os.path.exists(cachefile): return - print 'Loading cache...' - f = open(cachefile) + print('Loading cache...') + f = open(cachefile, 'rb') try: self.cache = pickle.loads(zlib.decompress(f.read())) except: @@ -97,7 +97,7 @@ class DataCollector: f.seek(0) self.cache = pickle.load(f) f.close() - + ## # Produce any additional statistics from the extracted data. def refine(self): @@ -107,7 +107,7 @@ class DataCollector: # : get a dictionary of author def getAuthorInfo(self, author): return None - + def getActivityByDayOfWeek(self): return {} @@ -122,36 +122,36 @@ class DataCollector: # Get a list of authors def getAuthors(self): return [] - + def getFirstCommitDate(self): return datetime.datetime.now() - + def getLastCommitDate(self): return datetime.datetime.now() - + def getStampCreated(self): return self.stamp_created - + def getTags(self): return [] - + def getTotalAuthors(self): return -1 - + def getTotalCommits(self): return -1 - + def getTotalFiles(self): return -1 - + def getTotalLOC(self): return -1 - + ## # Save cacheable data def saveCache(self, cachefile): - print 'Saving cache...' - f = open(cachefile, 'w') + print('Saving cache...') + f = open(cachefile, 'bw') #pickle.dump(self.cache, f) data = zlib.compress(pickle.dumps(self.cache)) f.write(data) @@ -219,7 +219,7 @@ class GitDataCollector(DataCollector): self.tags[tag] = { 'stamp': stamp, 'hash' : hash, 'date' : datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d'), 'commits': 0, 'authors': {} } # collect info on tags, starting from latest - tags_sorted_by_date_desc = map(lambda el : el[1], reversed(sorted(map(lambda el : (el[1]['date'], el[0]), self.tags.items())))) + tags_sorted_by_date_desc = [el[1] for el in reversed(sorted([(el[1]['date'], el[0]) for el in list(self.tags.items())]))] prev = None for tag in reversed(tags_sorted_by_date_desc): cmd = 'git shortlog -s "%s"' % tag @@ -358,7 +358,7 @@ class GitDataCollector(DataCollector): try: self.files_by_stamp[int(stamp)] = int(files) except ValueError: - print 'Warning: failed to parse line "%s"' % line + print('Warning: failed to parse line "%s"' % line) # extensions self.extensions = {} # extension -> files, lines @@ -385,7 +385,7 @@ class GitDataCollector(DataCollector): try: self.extensions[ext]['lines'] += self.getLinesInBlob(sha1) except: - print 'Warning: Could not count lines for file "%s"' % line + print('Warning: Could not count lines for file "%s"' % line) # line statistics # outputs: @@ -401,7 +401,7 @@ class GitDataCollector(DataCollector): continue # - if line.find('files changed,') == -1: + if line.find('files changed,') == -1 and line.find('file changed,') == -1: pos = line.find(' ') if pos != -1: try: @@ -412,23 +412,31 @@ class GitDataCollector(DataCollector): self.authors[author]['lines_added'] = self.authors[author].get('lines_added', 0) + inserted self.authors[author]['lines_removed'] = self.authors[author].get('lines_removed', 0) + deleted except ValueError: - print 'Warning: unexpected line "%s"' % line + print('Warning: unexpected line "%s"' % line) else: - print 'Warning: unexpected line "%s"' % line + print('Warning: unexpected line "%s"' % line) else: numbers = re.findall('\d+', line) + numbers = [int(el) for el in numbers] if len(numbers) == 3: - (files, inserted, deleted) = map(lambda el : int(el), numbers) - total_lines += inserted - total_lines -= deleted - self.total_lines_added += inserted - self.total_lines_removed += deleted + (files, inserted, deleted) = numbers + elif 'insertions' not in line: + inserted = 0 + files, deleted = numbers + elif 'deletions' not in line: + deleted = 0 + files, inserted = numbers else: - print 'Warning: failed to handle line "%s"' % line + print('Warning: failed to handle line "%s"' % line) (files, inserted, deleted) = (0, 0, 0) + + total_lines += inserted + total_lines -= deleted + self.total_lines_added += inserted + self.total_lines_removed += deleted #self.changes_by_date[stamp] = { 'files': files, 'ins': inserted, 'del': deleted } self.total_lines = total_lines - + def refine(self): # authors # name -> {place_by_commits, commits_frac, date_first, date_last, timedelta} @@ -437,7 +445,7 @@ class GitDataCollector(DataCollector): for i, name in enumerate(authors_by_commits): self.authors[name]['place_by_commits'] = i + 1 - for name in self.authors.keys(): + for name in list(self.authors.keys()): a = self.authors[name] a['commits_frac'] = (100 * float(a['commits'])) / self.getTotalCommits() date_first = datetime.datetime.fromtimestamp(a['first_commit_stamp']) @@ -446,7 +454,7 @@ class GitDataCollector(DataCollector): a['date_first'] = date_first.strftime('%Y-%m-%d') a['date_last'] = date_last.strftime('%Y-%m-%d') a['timedelta'] = delta - + def getActiveDays(self): return self.active_days @@ -458,12 +466,12 @@ class GitDataCollector(DataCollector): def getAuthorInfo(self, author): return self.authors[author] - + def getAuthors(self, limit = None): res = getkeyssortedbyvaluekey(self.authors, 'commits') res.reverse() return res[:limit] - + def getCommitDeltaDays(self): return (self.last_commit_stamp - self.first_commit_stamp) / 86400 + 1 @@ -471,8 +479,8 @@ class GitDataCollector(DataCollector): return self.domains[domain] def getDomains(self): - return self.domains.keys() - + return list(self.domains.keys()) + def getFilesInCommit(self, rev): try: res = self.cache['files_in_tree'][rev] @@ -486,10 +494,10 @@ class GitDataCollector(DataCollector): def getFirstCommitDate(self): return datetime.datetime.fromtimestamp(self.first_commit_stamp) - + def getLastCommitDate(self): return datetime.datetime.fromtimestamp(self.last_commit_stamp) - + def getLinesInBlob(self, sha1): try: res = self.cache['lines_in_blob'][sha1] @@ -503,22 +511,22 @@ class GitDataCollector(DataCollector): def getTags(self): lines = getpipeoutput(['git show-ref --tags', 'cut -d/ -f3']) return lines.split('\n') - + def getTagDate(self, tag): return self.revToDate('tags/' + tag) - + def getTotalAuthors(self): return self.total_authors - + def getTotalCommits(self): return self.total_commits def getTotalFiles(self): return self.total_files - + def getTotalLOC(self): return self.total_lines - + def revToDate(self, rev): stamp = int(getpipeoutput(['git log --pretty=format:%%at "%s" -n 1' % rev])) return datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d') @@ -527,7 +535,7 @@ class ReportCreator: """Creates the actual report based on given data.""" def __init__(self): pass - + def create(self, data, path): self.data = data self.path = path @@ -555,7 +563,7 @@ class HTMLReportCreator(ReportCreator): shutil.copyfile(src, path + '/' + file) break else: - print 'Warning: "%s" not found, so not copied (searched: %s)' % (file, basedirs) + print('Warning: "%s" not found, so not copied (searched: %s)' % (file, basedirs)) f = open(path + "/index.html", 'w') format = '%Y-%m-%d %H:%M:%S' @@ -752,7 +760,7 @@ class HTMLReportCreator(ReportCreator): f.write('') f.write('') max_commits_on_tz = max(data.commits_by_timezone.values()) - for i in sorted(data.commits_by_timezone.keys(), key = lambda n : int(n)): + for i in sorted(list(data.commits_by_timezone.keys()), key = lambda n : int(n)): commits = data.commits_by_timezone[i] r = 127 + int((float(commits) / max_commits_on_tz) * 128) f.write('' % (i, r, commits)) @@ -859,7 +867,7 @@ class HTMLReportCreator(ReportCreator): #for stamp in sorted(data.files_by_stamp.keys()): # fg.write('%s %d\n' % (datetime.datetime.fromtimestamp(stamp).strftime('%Y-%m-%d'), data.files_by_stamp[stamp])) fg.close() - + f.write('Files by Date'.format(IMAGE_TYPE)) #f.write('

Average file size by date

') @@ -914,7 +922,7 @@ class HTMLReportCreator(ReportCreator): f.write('
TimezoneCommits
%s%d
') f.write('') # sort the tags by date desc - tags_sorted_by_date_desc = map(lambda el : el[1], reversed(sorted(map(lambda el : (el[1]['date'], el[0]), data.tags.items())))) + tags_sorted_by_date_desc = [el[1] for el in reversed(sorted([(el[1]['date'], el[0]) for el in list(data.tags.items())]))] for tag in tags_sorted_by_date_desc: authorinfo = [] authors_by_commits = getkeyssortedbyvalues(data.tags[tag]['authors']) @@ -927,9 +935,9 @@ class HTMLReportCreator(ReportCreator): f.close() self.createGraphs(path) - + def createGraphs(self, path): - print 'Generating graphs...' + print('Generating graphs...') # hour of day f = open(path + '/hour_of_day.plot', 'w') @@ -1065,7 +1073,7 @@ plot 'lines_of_code.dat' using 1:2 w lines for f in files: out = getpipeoutput([gnuplot_cmd + ' "%s"' % f]) if len(out) > 0: - print out + print(out) def printHeader(self, f, title = ''): f.write( @@ -1094,7 +1102,7 @@ plot 'lines_of_code.dat' using 1:2 w lines """) - + class GitStats: def run(self, args_orig): @@ -1110,7 +1118,7 @@ class GitStats: conf[key] = value if len(args) < 2: - print """ + print(""" Usage: gitstats [options] Options: @@ -1118,7 +1126,7 @@ Options: Default config values: %s -""" % conf +""" % conf) sys.exit(0) gitpath = args[0] @@ -1130,34 +1138,33 @@ Default config values: except OSError: pass if not os.path.isdir(outputpath): - print 'FATAL: Output path is not a directory or does not exist' + print('FATAL: Output path is not a directory or does not exist') sys.exit(1) - print 'Git path: %s' % gitpath - print 'Output path: %s' % outputpath + print('Git path: %s' % gitpath) + print('Output path: %s' % outputpath) os.chdir(gitpath) cachefile = os.path.join(outputpath, 'gitstats.cache') - print 'Collecting data...' + print('Collecting data...') data = GitDataCollector() data.loadCache(cachefile) data.collect(gitpath) - print 'Refining data...' + print('Refining data...') data.saveCache(cachefile) data.refine() os.chdir(rundir) - print 'Generating report...' + print('Generating report...') report = HTMLReportCreator() report.create(data, outputpath) time_end = time.time() exectime_internal = time_end - time_start - print 'Execution time %.5f secs, %.5f secs (%.2f %%) in external commands)' % (exectime_internal, exectime_external, (100.0 * exectime_external) / exectime_internal) + print('Execution time %.5f secs, %.5f secs (%.2f %%) in external commands)' % (exectime_internal, exectime_external, (100.0 * exectime_external) / exectime_internal)) g = GitStats() g.run(sys.argv[1:]) -
NameDateCommitsAuthors