Skip to content

Instantly share code, notes, and snippets.

@534o
Created September 10, 2018 14:42
Show Gist options
  • Save 534o/60ce84c105dbc5126d4d71a0d525e5b1 to your computer and use it in GitHub Desktop.
Save 534o/60ce84c105dbc5126d4d71a0d525e5b1 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2018, Tokyo Opensource Robotics Developers
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Tokyo Opensource Robotics Developers nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""
this script calculate contributions to ros-related packages
"""
import argparse
import os
import re
import math
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import yaml
import colorama
import texttable
from operator import add
# python2/3 compatibility
try:
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse
from urllib.request import Request, urlopen
except ImportError:
from urllib2 import HTTPError, Request, URLError, urlopen
from urlparse import urlparse
# import gzip
# try:
# from cStringIO import StringIO
# except ImportError:
# from io import BytesIO as StringIO
import rosdistro
from github import Github # apt install python-github on 18.04
from github.GithubException import UnknownObjectException
def get_argument_parser():
parser = argparse.ArgumentParser(description="Calculate ROS contributions")
add = parser.add_argument
add('users', help="GitHub ID to measure the contributions", nargs='+')
add('--verbose', '-v', action='store_true', default=False, help="show debug message")
return parser
def get_repository_urls(distro):
urls = {}
# get all repositories for distro
index_url = rosdistro.get_index(rosdistro.get_index_url())
files = rosdistro.get_distribution_files(index_url, distro)
for file in files:
for name, repository in file.repositories.items():
if repository.source_repository is not None:
repo_url = repository.source_repository.url
repo_version = repository.source_repository.version
elif repository.doc_repository is not None:
repo_url = repository.doc_repository.url
repo_version = repository.doc_repository.version
else:
repo_url = repo_version = None
urls[name] = {'url' : repo_url, 'branch' : repo_version}
return urls
def get_contribution_info(user, verbose=None):
global gh
user = gh.get_user(user)
points = {'open_issue': [], 'pull_request': [], 'update_comment': []}
for event in user.get_events():
if event.type in ['CommitCommentEvent', 'CreateEvent', 'DeleteEvent', 'ForkEvent', 'GollumEvent', 'PullRequestReviewEvent', 'PullRequestReviewCommentEvent', 'PublicEvent', 'PushEvent', 'WatchEvent']:
continue
try:
if event.repo.clone_url not in released_repository_urls:
print(colorama.Fore.YELLOW + "{} is not part of ROS release repositories".format(event.repo.clone_url))
# continue ## Uncomment if you do not count unreleased project
# need permission for this
# if user.login in [name.login for name in event.repo.get_collaborators()]:
# print(colorama.Fore.YELLOW + "You are already collaborator of {}".format(event.repo.clone_url))
# # continue ## Uncomment if you do not count unreleased project
if event.type == 'PullRequestEvent':
event_data = event.payload['pull_request']
if verbose:
print('Create pull Request "{}" at {}'.format(event.repo.name, event_data['created_at']))
print(" type : {}".format(event.payload['action']))
if event.payload['action'] != 'opened':
continue
if verbose:
print(u" title : {}".format(event_data['title']))
print(" url : {}".format(event_data['html_url']))
print(" commit : +{} -{}".format(event_data['additions'], event_data['deletions']))
points['pull_request'] += [{'url': event_data['html_url'], 'additions': event_data['additions'], 'deletions': event_data['deletions']}]
elif event.type == 'IssuesEvent':
event_data = event.payload['issue']
if verbose:
print('Open Issue "{}" at {}'.format(event.repo.name, event_data['created_at']))
print(" type : {}".format(event.payload['action']))
if event.payload['action'] != 'opened':
continue
if verbose:
print(u" title : {}".format(event_data['title']))
print(" url : {}".format(event_data['html_url']))
points['open_issue'] += [{'url': event_data['html_url'], 'body': event_data['body']}]
elif event.type == 'IssueCommentEvent':
event_data = event.payload['comment']
issue_data = event.repo.get_issue(event.payload['issue']['number'])
if verbose:
print('Issue comment "{}" at {}'.format(event.repo.name, event_data['created_at']))
print(" type : {}".format(event.payload['action']))
print(u" title : {}".format(issue_data.title))
print(" url : {}".format(event_data['html_url']))
print(" user : {}".format(issue_data.user.login))
# get who opened issue, commit to self issue is not valid
if issue_data.user.login != user.login :
points['update_comment'] += [{'url': event_data['html_url'], 'body': event_data['body']}]
else:
print(colorama.Fore.RED + "Skipped events... {} {}".format(event.type, event.repo.name))
except UnknownObjectException as e:
print(colorama.Fore.RED + "GithubException.UnknownObjectException")
print(e)
pass
except Exception as e:
print(e)
sys.exit(1)
return points
def calc_commit_point (point):
return point['additions'] + point['deletions'] * 2 ## deletion is always good thinkg
def calc_comment_point (point):
return math.floor(len(point['body'])/160) ## assume one line is 80 character and two line is equal to one code line
gh = None
released_repository_urls = []
def main(sysargs):
global gh, released_repository_urls
parser = get_argument_parser()
args = parser.parse_args(sys.argv[1:])
users = args.users
verbose = args.verbose
print(colorama.Fore.GREEN + "get all released ros repository names")
urls = [u['url'] for u in get_repository_urls('indigo').values()]
urls += [u['url'] for u in get_repository_urls('kinetic').values()]
urls += [u['url'] for u in get_repository_urls('melodic').values()]
released_repository_urls = urls
gh = Github()
contribution_info = {}
contribution_point = {}
for user in users:
print(colorama.Fore.GREEN + "get contribution info for {}".format(user))
contribution_info[user] = get_contribution_info(user, verbose=verbose)
for user, points in contribution_info.items():
# show results
if verbose:
table = texttable.Texttable()
table.set_cols_align(["l", "r", "r", "r"])
print("+ Open Issue")
table.add_rows([["url", "body", "length", "points"]] +
[[p['url'], p['body'][:32], len(p['body']), calc_comment_point(p)] for p in points['open_issue']])
print table.draw() + "\n"
table = texttable.Texttable()
table.set_cols_align(["l", "r", "r", "r"])
print("+ Update Comment")
table.add_rows([["url", "body", "length", "points"]] +
[[p['url'], p['body'][:32], len(p['body']), calc_comment_point(p)] for p in points['update_comment']])
print table.draw() + "\n"
table = texttable.Texttable()
table.set_cols_align(["l", "r", "r", "r"])
print("+ Pull Request")
table.add_rows([["url", "additions", "deletion", "points"]] +
[[p['url'], p['additions'], p['deletions'], calc_commit_point(p)] for p in points['pull_request']])
print table.draw() + "\n"
point_list = [calc_comment_point(p) for p in points['open_issue']] + [calc_comment_point(p) for p in points['update_comment']] + [calc_commit_point(p) for p in points['pull_request']]
if len(point_list) > 0 :
contribution_point[user] = reduce(add, point_list)
else:
contribution_point[user] = 0
if verbose:
print(colorama.Fore.GREEN + "{:<16} : {:>10}".format(user, contribution_point[user]))
print(colorama.Fore.GREEN + "================================")
print(colorama.Fore.GREEN + ".. Github Contribution Points ..")
for user, point in sorted(contribution_point.items(), key=lambda x: x[1], reverse=True):
print(colorama.Fore.GREEN + "{:<16} : {:>10}".format(user, point))
return
if __name__ == '__main__':
colorama.init(autoreset=True)
sys.exit(main(sys.argv[1:]) or 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment