def __init__(self, host, changelog_paths): self._changelog_paths = changelog_paths self._filesystem = host.filesystem self._contribution_areas = ContributionAreas(host.filesystem) self._scm = host.scm() self._parsed_revisions = {} self._contributors_statistics = {} self._areas_statistics = dict([(area, {'reviewed': 0, 'unreviewed': 0, 'contributors': {}}) for area in self._contribution_areas.names()]) self._summary = {'reviewed': 0, 'unreviewed': 0} self._longest_filename = max([len(path) - len(self._scm.checkout_root) for path in changelog_paths]) self._filename = '' self._length_of_previous_output = 0
def __init__(self, host, changelog_paths): self._changelog_paths = changelog_paths self._filesystem = host.filesystem self._contribution_areas = ContributionAreas(host.filesystem) self._scm = host.scm() self._parsed_revisions = {} self._contributors_statistics = {} self._areas_statistics = dict([(area, {'reviewed': 0, 'unreviewed': 0, 'contributors': {}}) for area in self._contribution_areas.names()]) self._summary = {'reviewed': 0, 'unreviewed': 0} self._longest_filename = max([len(path) - len(self._scm.checkout_root) for path in changelog_paths]) self._filename = '' self._length_of_previous_output = 0
def test_areas_for_touched_files(self): areas = ContributionAreas(MockFileSystem(), [ _Area('CSS'), _Area('HTML'), _Area('Forms', ['forms', 'input']), _Area('CSS Transforms', [_Intersection('css', 'transforms')]), ]) self._assert_areas_for_touched_files(areas, [], []) self._assert_areas_for_touched_files(areas, ['WebCore/css'], ['CSS']) self._assert_areas_for_touched_files(areas, ['WebCore/html/'], ['HTML']) self._assert_areas_for_touched_files(areas, [ 'WebCore/css/CSSStyleSelector.cpp', 'WebCore/html/HTMLIFrameElement.h' ], ['CSS', 'HTML']) self._assert_areas_for_touched_files(areas, ['WebCore'], []) self._assert_areas_for_touched_files(areas, ['WebCore/html2'], []) self._assert_areas_for_touched_files( areas, ['WebCore/html/HTMLInputElement.cpp'], ['HTML', 'Forms']) self._assert_areas_for_touched_files(areas, ['WebCore/svg/transforms'], []) self._assert_areas_for_touched_files(areas, ['WebCore/css/transforms'], ['CSS', 'CSS Transforms'])
class ChangeLogAnalyzer(object): def __init__(self, host, changelog_paths): self._changelog_paths = changelog_paths self._filesystem = host.filesystem self._contribution_areas = ContributionAreas(host.filesystem) self._scm = host.scm() self._parsed_revisions = {} self._contributors_statistics = {} self._areas_statistics = dict([(area, {'reviewed': 0, 'unreviewed': 0, 'contributors': {}}) for area in self._contribution_areas.names()]) self._summary = {'reviewed': 0, 'unreviewed': 0} self._longest_filename = max([len(path) - len(self._scm.checkout_root) for path in changelog_paths]) self._filename = '' self._length_of_previous_output = 0 def contributors_statistics(self): return self._contributors_statistics def areas_statistics(self): return self._areas_statistics def summary(self): return self._summary def _print_status(self, status): if self._length_of_previous_output: print("\r" + " " * self._length_of_previous_output, end=' ') new_output = ('%' + str(self._longest_filename) + 's: %s') % (self._filename, status) print("\r" + new_output, end=' ') self._length_of_previous_output = len(new_output) def _set_filename(self, filename): if self._filename: print() self._filename = filename def analyze(self): for path in self._changelog_paths: self._set_filename(self._filesystem.relpath(path, self._scm.checkout_root)) with self._filesystem.open_text_file_for_reading(path) as changelog: self._print_status('Parsing entries...') number_of_parsed_entries = self._analyze_entries(ChangeLog.parse_entries_from_file(changelog), path) self._print_status('Done (%d entries)' % number_of_parsed_entries) print() self._summary['contributors'] = len(self._contributors_statistics) self._summary['contributors_with_reviews'] = sum([1 for contributor in self._contributors_statistics.values() if contributor['reviews']['total']]) self._summary['contributors_without_reviews'] = self._summary['contributors'] - self._summary['contributors_with_reviews'] def _collect_statistics_for_contributor_area(self, area, contributor, contribution_type, reviewed): area_contributors = self._areas_statistics[area]['contributors'] if contributor not in area_contributors: area_contributors[contributor] = {'reviews': 0, 'reviewed': 0, 'unreviewed': 0} if contribution_type == 'patches': contribution_type = 'reviewed' if reviewed else 'unreviewed' area_contributors[contributor][contribution_type] += 1 def _collect_statistics_for_contributor(self, contributor, contribution_type, areas, touched_files, reviewed): if contributor not in self._contributors_statistics: self._contributors_statistics[contributor] = { 'reviews': {'total': 0, 'areas': {}, 'files': {}}, 'patches': {'reviewed': 0, 'unreviewed': 0, 'areas': {}, 'files': {}}} statistics = self._contributors_statistics[contributor][contribution_type] if contribution_type == 'reviews': statistics['total'] += 1 elif reviewed: statistics['reviewed'] += 1 else: statistics['unreviewed'] += 1 for area in areas: self._increment_dictionary_value(statistics['areas'], area) self._collect_statistics_for_contributor_area(area, contributor, contribution_type, reviewed) for touchedfile in touched_files: self._increment_dictionary_value(statistics['files'], touchedfile) def _increment_dictionary_value(self, dictionary, key): dictionary[key] = dictionary.get(key, 0) + 1 def _analyze_entries(self, entries, changelog_path): dirname = self._filesystem.dirname(changelog_path) i = 0 for i, entry in enumerate(entries): self._print_status('(%s) entries' % i) assert(entry.authors()) touchedfiles_for_entry = [self._filesystem.relpath(self._filesystem.join(dirname, name), self._scm.checkout_root) for name in entry.touched_files()] areas_for_entry = self._contribution_areas.areas_for_touched_files(touchedfiles_for_entry) authors_for_entry = entry.authors() reviewers_for_entry = entry.reviewers() for reviewer in reviewers_for_entry: self._collect_statistics_for_contributor(reviewer.full_name, 'reviews', areas_for_entry, touchedfiles_for_entry, reviewed=True) for author in authors_for_entry: self._collect_statistics_for_contributor(author['name'], 'patches', areas_for_entry, touchedfiles_for_entry, reviewed=bool(reviewers_for_entry)) for area in areas_for_entry: self._areas_statistics[area]['reviewed' if reviewers_for_entry else 'unreviewed'] += 1 self._summary['reviewed' if reviewers_for_entry else 'unreviewed'] += 1 self._print_status('(%s) entries' % i) return i
class ChangeLogAnalyzer(object): def __init__(self, host, changelog_paths): self._changelog_paths = changelog_paths self._filesystem = host.filesystem self._contribution_areas = ContributionAreas(host.filesystem) self._scm = host.scm() self._parsed_revisions = {} self._contributors_statistics = {} self._areas_statistics = dict([(area, {'reviewed': 0, 'unreviewed': 0, 'contributors': {}}) for area in self._contribution_areas.names()]) self._summary = {'reviewed': 0, 'unreviewed': 0} self._longest_filename = max([len(path) - len(self._scm.checkout_root) for path in changelog_paths]) self._filename = '' self._length_of_previous_output = 0 def contributors_statistics(self): return self._contributors_statistics def areas_statistics(self): return self._areas_statistics def summary(self): return self._summary def _print_status(self, status): if self._length_of_previous_output: print "\r" + " " * self._length_of_previous_output, new_output = ('%' + str(self._longest_filename) + 's: %s') % (self._filename, status) print "\r" + new_output, self._length_of_previous_output = len(new_output) def _set_filename(self, filename): if self._filename: print self._filename = filename def analyze(self): for path in self._changelog_paths: self._set_filename(self._filesystem.relpath(path, self._scm.checkout_root)) with self._filesystem.open_text_file_for_reading(path) as changelog: self._print_status('Parsing entries...') number_of_parsed_entries = self._analyze_entries(ChangeLog.parse_entries_from_file(changelog), path) self._print_status('Done (%d entries)' % number_of_parsed_entries) print self._summary['contributors'] = len(self._contributors_statistics) self._summary['contributors_with_reviews'] = sum([1 for contributor in self._contributors_statistics.values() if contributor['reviews']['total']]) self._summary['contributors_without_reviews'] = self._summary['contributors'] - self._summary['contributors_with_reviews'] def _collect_statistics_for_contributor_area(self, area, contributor, contribution_type, reviewed): area_contributors = self._areas_statistics[area]['contributors'] if contributor not in area_contributors: area_contributors[contributor] = {'reviews': 0, 'reviewed': 0, 'unreviewed': 0} if contribution_type == 'patches': contribution_type = 'reviewed' if reviewed else 'unreviewed' area_contributors[contributor][contribution_type] += 1 def _collect_statistics_for_contributor(self, contributor, contribution_type, areas, touched_files, reviewed): if contributor not in self._contributors_statistics: self._contributors_statistics[contributor] = { 'reviews': {'total': 0, 'areas': {}, 'files': {}}, 'patches': {'reviewed': 0, 'unreviewed': 0, 'areas': {}, 'files': {}}} statistics = self._contributors_statistics[contributor][contribution_type] if contribution_type == 'reviews': statistics['total'] += 1 elif reviewed: statistics['reviewed'] += 1 else: statistics['unreviewed'] += 1 for area in areas: self._increment_dictionary_value(statistics['areas'], area) self._collect_statistics_for_contributor_area(area, contributor, contribution_type, reviewed) for touchedfile in touched_files: self._increment_dictionary_value(statistics['files'], touchedfile) def _increment_dictionary_value(self, dictionary, key): dictionary[key] = dictionary.get(key, 0) + 1 def _analyze_entries(self, entries, changelog_path): dirname = self._filesystem.dirname(changelog_path) for i, entry in enumerate(entries): self._print_status('(%s) entries' % i) assert(entry.authors()) touchedfiles_for_entry = [self._filesystem.relpath(self._filesystem.join(dirname, name), self._scm.checkout_root) for name in entry.touched_files()] areas_for_entry = self._contribution_areas.areas_for_touched_files(touchedfiles_for_entry) authors_for_entry = entry.authors() reviewers_for_entry = entry.reviewers() for reviewer in reviewers_for_entry: self._collect_statistics_for_contributor(reviewer.full_name, 'reviews', areas_for_entry, touchedfiles_for_entry, reviewed=True) for author in authors_for_entry: self._collect_statistics_for_contributor(author['name'], 'patches', areas_for_entry, touchedfiles_for_entry, reviewed=bool(reviewers_for_entry)) for area in areas_for_entry: self._areas_statistics[area]['reviewed' if reviewers_for_entry else 'unreviewed'] += 1 self._summary['reviewed' if reviewers_for_entry else 'unreviewed'] += 1 i += 1 self._print_status('(%s) entries' % i) return i