def get_all_repos(date_bucket_fn, start, by_size=False, lines=False, closed=False): repos = [r for r in Repo.from_yaml() if r.track_pulls] dimensions = [] if closed: dimensions.append(["opened", "merged", "closed"]) else: dimensions.append(["opened", "merged"]) dimensions.append(["internal", "external"]) if by_size: dimensions.append(["small", "large"]) keys = [" ".join(prod) for prod in itertools.product(*dimensions)] bucket_blank = dict.fromkeys(keys, 0) buckets = collections.defaultdict(lambda: dict(bucket_blank)) for repo in repos: get_bucket_data(buckets, repo.name, date_bucket_fn, start=start, by_size=by_size, lines=lines, closed=closed) print("timespan\t" + "\t".join(keys)) for time_period in sorted(buckets.keys()): data = buckets[time_period] print("{}\t{}".format(time_period, "\t".join(str(data[k]) for k in keys)))
def main(argv): parser = argparse.ArgumentParser(description="Count unique contributors over time.") parser.add_argument( "--type", choices=["external", "internal"], default="external", help="What kind of pull requests should be counted [%(default)s]" ) parser.add_argument( "--window", metavar="DAYS", type=int, default=90, help="Count contributors over this large a window [%(default)d]" ) parser.add_argument( "--start", type=date_arg, help="Date to start collecting, format is flexible: " "20141225, Dec/25/2014, 2014-12-25, etc" ) args = parser.parse_args(argv[1:]) if args.start is None: args.start = make_timezone_aware(datetime.datetime(2013, 6, 5)) if args.type == "external": interesting = lambda issue: issue.intext == "external" elif args.type == "internal": interesting = lambda issue: issue.intext == "internal" repos = [ r.name for r in Repo.from_yaml() if r.track_pulls ] for when, num_authors in unique_authors(repos, args.window, interesting): if when < args.start: continue print("{0:%Y-%m-%d}\t{1}".format(when, num_authors))
def main(argv): parser = argparse.ArgumentParser(description="Summarize pull requests by organization.") parser.add_argument("--since", metavar="DAYS", type=int, help="Only consider pull requests closed in the past DAYS days" ) parser.add_argument("--start", type=date_arg, help="Date to start collecting, format is flexible: " "20141225, Dec/25/2014, 2014-12-25, etc" ) parser.add_argument("--end", type=date_arg, help="Date to end collecting, format is flexible: " "25/Dec/2014, 12/25/2014, 2014-12-25, etc" ) args = parser.parse_args(argv[1:]) since = None if args.since: since = date.today() - timedelta(days=args.since) if args.start: if since is not None: raise Exception("Can't use --since and --start") since = args.start repos = [ r for r in Repo.from_yaml() if r.track_pulls ] by_org = collections.defaultdict(list) for repo in repos: for pull in get_pulls(repo.name, state="closed", pull_details="list", org=True, since=since): # We only want external pull requests. if pull['intext'] != "external": continue # We only want merged pull requests. if pull['combinedstate'] != "merged": continue if args.end is not None: # We don't want to count things merged after our end date. merged = dateutil.parser.parse(pull['pull.merged_at']) if merged >= args.end: continue by_org[pull['org']].append(pull) keys = sorted(by_org, key=lambda k: len(by_org[k]), reverse=True) for key in keys: print("{}: {}".format(key, len(by_org[key]))) fmt = "{number:5d} {user.login:>17s} {title}" for i, pull in enumerate(by_org['other']): if i == 0: print("\n'Other' pull requests:") print(pull.format(fmt)) for i, pull in enumerate(by_org['unsigned']): if i == 0: print("\nUnsigned authors:") print(pull.format(fmt))
def get_all_repos(date_bucket_fn): repos = [ r for r in Repo.from_yaml() if r.track_pulls ] def bucket_blank(): return { "opened": { "internal": 0, "external": 0, }, "merged": { "internal": 0, "external": 0, }, } buckets = collections.defaultdict(bucket_blank) for repo in repos: get_bucket_data(buckets, repo.name, date_bucket_fn) print("qrtr\topened internal\tmerged internal\topened external\tmerged external") for q in sorted(buckets.keys()): data = buckets[q] print("{}\t{}\t{}\t{}\t{}".format(q, data["opened"]["internal"], data["merged"]["internal"], data["opened"]["external"], data["merged"]["external"], ))
def main(argv): parser = argparse.ArgumentParser( description="Count unique contributors over time.") parser.add_argument( "--type", choices=["external", "internal"], default="external", help="What kind of pull requests should be counted [%(default)s]") parser.add_argument( "--window", metavar="DAYS", type=int, default=90, help="Count contributors over this large a window [%(default)d]") parser.add_argument("--start", type=date_arg, help="Date to start collecting, format is flexible: " "20141225, Dec/25/2014, 2014-12-25, etc") args = parser.parse_args(argv[1:]) if args.start is None: args.start = make_timezone_aware(datetime.datetime(2013, 6, 5)) if args.type == "external": interesting = lambda issue: issue.intext == "external" elif args.type == "internal": interesting = lambda issue: issue.intext == "internal" repos = [r.name for r in Repo.from_yaml() if r.track_pulls] for when, num_authors in unique_authors(repos, args.window, interesting): if when < args.start: continue print("{0:%Y-%m-%d}\t{1}".format(when, num_authors))
def show_pulls(labels=None, show_comments=False, state="open", since=None, org=False, intext=None, merged=False): """ `labels`: Filters PRs by labels (all are shown if None is specified) `show_comments`: shows the last 5 comments on each PR, if True `state`: Filter PRs by this state (either 'open' or 'closed') `since`: a datetime representing the earliest time from which to pull information. All PRs regardless of time are shown if None is specified. `org`: If True, sorts by PR author affiliation `intext`: specify 'int' (internal) or 'ext' (external) pull request `merged`: If True and state="closed", shows only PRs that were merged. """ num = 0 adds = 0 deletes = 0 repos = [ r for r in Repo.from_yaml() if r.track_pulls ] for repo in repos: issues = get_pulls(repo.name, labels, state, since, org=org or intext, pull_details="all") category = None for issue in issues: issue.repo = repo.nick if intext is not None: if issue.intext != intext: continue if state == 'closed' and merged and issue.combinedstate != 'merged': # If we're filtering on closed PRs, and only want those that are merged, # skip ones that were closed without merge. continue if state == 'closed' and since: # If this PR was closed prior to the last `since` interval of days, continue on # (it may have been *updated* - that is, referenced or commented on - more recently, # but we just want to see what's been merged or closed in the past "since" days) if issue.closed_at < since: continue if org and issue.org != category: # new category! print category header category = issue.org print("-- {category} ----".format(category=category)) print(fformat(ISSUE_FMT, issue)) num += 1 adds += issue.additions deletes += issue.deletions if show_comments: comments = get_comments(issue) last_five_comments = reversed(more_itertools.take(5, comments)) for comment in last_five_comments: print(fformat(COMMENT_FMT, comment)) print() print("{num} pull requests; {adds}+ {deletes}-".format(num=num, adds=adds, deletes=deletes))
def get_all_repos(date_bucket_fn, start, lines=False, internal=False): repos = [r for r in Repo.from_yaml() if r.track_pulls] dimensions = [["merged", "closed", "unresolved", "opened"]] if internal: dimensions.append(["internal"]) else: dimensions.append(["external"]) keys = [" ".join(prod) for prod in itertools.product(*dimensions)] bucket_blank = dict.fromkeys(keys, 0) buckets = collections.defaultdict(lambda: dict(bucket_blank)) for repo in repos: get_bucket_data(buckets, repo.name, date_bucket_fn, start=start, lines=lines, internal=internal) print_repo_output(keys, buckets)
def get_all_repos(date_bucket_fn, start, by_size=False, lines=False, closed=False): repos = [r for r in Repo.from_yaml() if r.track_pulls] dimensions = [] if closed: dimensions.append(["opened", "merged", "closed"]) else: dimensions.append(["opened", "merged"]) dimensions.append(["internal", "external"]) if by_size: dimensions.append(["small", "large"]) keys = [" ".join(prod) for prod in itertools.product(*dimensions)] bucket_blank = dict.fromkeys(keys, 0) buckets = collections.defaultdict(lambda: dict(bucket_blank)) for repo in repos: get_bucket_data(buckets, repo.name, date_bucket_fn, start=start, by_size=by_size, lines=lines, closed=closed) print_repo_output(keys, buckets)
def main(argv): parser = argparse.ArgumentParser(description="Collect info about people commenting on pull requests") parser.add_argument("--since", metavar="DAYS", type=int, default=14, help="Include comments in this many days [%(default)d]" ) parser.add_argument("--debug", action="store_true", help="Break down by organization" ) args = parser.parse_args(argv[1:]) since = None if args.since: since = datetime.now() - timedelta(days=args.since) internal = get_internal() repos = [ r for r in Repo.from_yaml() if r.track_pulls ] for repo in repos: get_comment_data(repo.name, since=since, internal=internal.get) return 0
def get_all_repos(date_bucket_fn, start, lines=False, internal=False): repos = [r for r in Repo.from_yaml() if r.track_pulls] dimensions = [["merged", "closed", "unresolved", "opened"]] if internal: dimensions.append(["internal"]) else: dimensions.append(["external"]) keys = [" ".join(prod) for prod in itertools.product(*dimensions)] bucket_blank = dict.fromkeys(keys, 0) buckets = collections.defaultdict(lambda: dict(bucket_blank)) for repo in repos: get_bucket_data(buckets, repo.name, date_bucket_fn, start=start, lines=lines, internal=internal) print_repo_output(keys, buckets)
def get_all_repos(date_bucket_fn, start, by_size=False, lines=False, closed=False): repos = [r for r in Repo.from_yaml() if r.track_pulls] dimensions = [] if closed: dimensions.append(["opened", "merged", "closed"]) else: dimensions.append(["opened", "merged"]) dimensions.append(["internal", "external"]) if by_size: dimensions.append(["small", "large"]) keys = [" ".join(prod) for prod in itertools.product(*dimensions)] bucket_blank = dict.fromkeys(keys, 0) buckets = collections.defaultdict(lambda: dict(bucket_blank)) for repo in repos: get_bucket_data(buckets, repo.name, date_bucket_fn, start=start, by_size=by_size, lines=lines, closed=closed) print("timespan\t" + "\t".join(keys)) for time_period in sorted(buckets.keys()): data = buckets[time_period] print("{}\t{}".format(time_period, "\t".join(str(data[k]) for k in keys)))
def show_pulls(labels=None, show_comments=False, state="open", since=None, org=False, intext=None, merged=False): """ `labels`: Filters PRs by labels (all are shown if None is specified) `show_comments`: shows the last 5 comments on each PR, if True `state`: Filter PRs by this state (either 'open' or 'closed') `since`: a datetime representing the earliest time from which to pull information. All PRs regardless of time are shown if None is specified. `org`: If True, sorts by PR author affiliation `intext`: specify 'int' (internal) or 'ext' (external) pull request `merged`: If True and state="closed", shows only PRs that were merged. """ num = 0 adds = 0 deletes = 0 repos = [ r for r in Repo.from_yaml() if r.track_pulls ] for repo in repos: issues = get_pulls(repo.name, labels, state, since, org=org or intext, pull_details="all") category = None for issue in issues: issue["repo"] = repo.nick if intext is not None: if issue["intext"] != intext: continue if state == 'closed' and merged and issue['combinedstate'] != 'merged': # If we're filtering on closed PRs, and only want those that are merged, # skip ones that were closed without merge. continue if state == 'closed' and since: # If this PR was closed prior to the last `since` interval of days, continue on # (it may have been *updated* - that is, referenced or commented on - more recently, # but we just want to see what's been merged or closed in the past "since" days) closed_at = dateutil.parser.parse(issue["closed_at"][:-1]) # Remove TZ information if closed_at < since: continue if org and issue.get("org") != category: # new category! print category header category = issue["org"] print("-- {category} ----".format(category=category)) if 0: import pprint pprint.pprint(issue.obj) print(issue.format(ISSUE_FMT)) num += 1 adds += issue['pull']['additions'] deletes += issue['pull']['deletions'] if show_comments: comments_url = URLObject(issue['comments_url']) comments_url = comments_url.set_query_param("sort", "created") comments_url = comments_url.set_query_param("direction", "desc") comments = paginated_get(comments_url) last_five_comments = reversed(more_itertools.take(5, comments)) for comment in last_five_comments: print(comment.format(COMMENT_FMT)) print() print("{num} pull requests; {adds}+ {deletes}-".format(num=num, adds=adds, deletes=deletes))
def main(argv): global DEBUG parser = argparse.ArgumentParser(description="Summarize pull requests.") parser.add_argument( "--since", metavar="DAYS", type=int, default=14, help= "For closed issues, only include issues updated in the past DAYS days [%(default)d]" ) parser.add_argument("--human", action="store_true", help="Human-readable output") parser.add_argument("--org", action="store_true", help="Break down by organization") parser.add_argument("--debug", action="store_true", help="Show debugging info") args = parser.parse_args(argv[1:]) DEBUG = args.debug since = None if args.since: since = date.today() - timedelta(days=args.since) if args.org: categories = sorted(get_all_orgs()) def cat_filter(cat, pr): return pr.org == cat else: categories = ["all"] def cat_filter(cat, pr): return True durations = { "open": { "internal": [], "external": [], }, "closed": { "internal": [], "external": [], } } repos = [r for r in Repo.from_yaml() if r.track_pulls] for repo in repos: get_duration_data(durations, repo.name, since) for linenum, cat in enumerate(categories): ss_friendly = [] for position in ("external", "internal"): for state in ("open", "closed"): seconds = [ p.duration.total_seconds() for p in durations[state][position] if cat_filter(cat, p) ] if seconds: median_seconds = int(statistics.median(seconds)) median_duration = timedelta(seconds=median_seconds) else: median_seconds = -1 median_duration = "no data" population = "all" if state == "closed" and since: population = "since {date}".format(date=since) if args.human: print( "median {position} {state} ({population}): {duration}". format(position=position, state=state, population=population, duration=median_duration)) else: ss_friendly += [len(seconds), median_seconds] if ss_friendly: if linenum == 0: print( "cat\twhen\trepos\teopen\teopenage\teclosed\teclosedage\tiopen\tiopenage\ticlosed\ticlosedage" ) ss_data = "\t".join(str(x) for x in ss_friendly) print("{}\t{:%m/%d/%Y}\t{}\t{}".format(cat, date.today(), len(repos), ss_data))
def main(argv): parser = argparse.ArgumentParser(description="Summarize pull requests by organization.") parser.add_argument( "--since", metavar="DAYS", type=int, help="Only consider pull requests closed in the past DAYS days" ) parser.add_argument( "--start", type=date_arg, help="Date to start collecting, format is flexible: " "20141225, Dec/25/2014, 2014-12-25, etc" ) parser.add_argument( "--end", type=date_arg, help="Date to end collecting, format is flexible: " "25/Dec/2014, 12/25/2014, 2014-12-25, etc" ) parser.add_argument( "--short", action="store_true", help="Only show the short summary" ) args = parser.parse_args(argv[1:]) since = None if args.since: since = make_timezone_aware(datetime.today() - timedelta(days=args.since)) if args.start: if since is not None: raise Exception("Can't use --since and --start") since = args.start repos = [ r for r in Repo.from_yaml() if r.track_pulls ] by_org = collections.defaultdict(list) for repo in repos: for pull in get_pulls(repo.name, state="closed", pull_details="list", org=True, since=since): # We only want external pull requests. if pull.intext != "external": continue # We only want merged pull requests. if pull.combinedstate != "merged": continue # Pull requests can be recently modified even if they were merged # long ago, so only take things merged since our since date. merged = make_timezone_aware(pull.merged_at) if merged < since: continue if args.end is not None: # We don't want to count things merged after our end date. if merged >= args.end: continue pull.repo = repo.nick by_org[pull.org].append(pull) keys = sorted(by_org, key=lambda k: len(by_org[k]), reverse=True) for key in keys: print("{}: {}".format(key, len(by_org[key]))) fmt = "{pull.repo:4s} {pull.number:5d} {pull.user_login:>17s} {pull.title}" if args.short: if 'unsigned' in keys: print("\n-- {} -------".format('unsigned')) for pull in by_org['unsigned']: print(fmt.format(pull=pull)) else: for key in keys: print("\n-- {} -------".format(key)) for pull in by_org[key]: print(fmt.format(pull=pull))
def main(argv): parser = argparse.ArgumentParser(description="Summarize pull requests by organization.") parser.add_argument( "--since", metavar="DAYS", type=int, help="Only consider pull requests closed in the past DAYS days" ) parser.add_argument( "--start", type=date_arg, help="Date to start collecting, format is flexible: " "20141225, Dec/25/2014, 2014-12-25, etc" ) parser.add_argument( "--end", type=date_arg, help="Date to end collecting, format is flexible: " "25/Dec/2014, 12/25/2014, 2014-12-25, etc" ) parser.add_argument( "--short", action="store_true", help="Only show the short summary" ) args = parser.parse_args(argv[1:]) since = None if args.since: since = make_timezone_aware(datetime.today() - timedelta(days=args.since)) if args.start: if since is not None: raise Exception("Can't use --since and --start") since = args.start repos = [ r for r in Repo.from_yaml() if r.track_pulls ] by_org = collections.defaultdict(list) for repo in repos: for pull in get_pulls(repo.name, state="closed", pull_details="list", org=True, since=since): # We only want external pull requests. if pull.intext != "external": continue # We only want merged pull requests. if pull.combinedstate != "merged": continue # Pull requests can be recently modified even if they were merged # long ago, so only take things merged since our since date. merged = make_timezone_aware(pull.merged_at) if merged < since: continue if args.end is not None: # We don't want to count things merged after our end date. if merged >= args.end: continue pull.repo = repo.nick by_org[pull.org].append(pull) keys = sorted(by_org, key=lambda k: len(by_org[k]), reverse=True) for key in keys: print("{}: {}".format(key, len(by_org[key]))) fmt = "{pull.repo:4s} {pull.number:5d} {pull.user_login:>17s} {pull.title}" if args.short: if 'unsigned' in keys: print("\n-- {} -------".format('unsigned')) for pull in by_org['unsigned']: print(fmt.format(pull=pull)) else: for key in keys: print("\n-- {} -------".format(key)) for pull in by_org[key]: print(fmt.format(pull=pull))
#!/usr/bin/env python """List the webhooks in all the repos.""" from __future__ import print_function import pprint from helpers import paginated_get from repos import Repo repos = Repo.from_yaml() repo_names = sorted(repo.name for repo in repos) for repo_name in repo_names: print("\n-- {} ---------------------".format(repo_name)) url = "https://api.github.com/repos/{name}/hooks".format(name=repo_name) for r in paginated_get(url): print("{r[name]}".format(r=r)) for k, v in sorted(r['config'].items()): print(" {k}: {v}".format(k=k, v=v))
def get_wall_data(pretty=False): """Returns a JSON string of aging data for the wall display.""" repos = [ r for r in Repo.from_yaml() if r.track_pulls ] wall_data = WallMaker().show_wall(repos) return json.dumps(wall_data, indent=4 if pretty else None)
def get_wall_data(pretty=False): """Returns a JSON string of aging data for the wall display.""" repos = [r for r in Repo.from_yaml() if r.track_pulls] wall_data = WallMaker().show_wall(repos) return json.dumps(wall_data, indent=4 if pretty else None)
def get_all_external_pulls(): repos = [ r.name for r in Repo.from_yaml() if r.track_pulls ] for repo in repos: for pull in get_external_pulls(repo): yield pull
def main(): repos = [ r.name for r in Repo.from_yaml() if r.track_pulls ] for when, num_authors in unique_authors(repos): print("{0:%Y-%m-%d}\t{1}".format(when, num_authors))
def main(argv): global DEBUG parser = argparse.ArgumentParser(description="Summarize pull requests.") parser.add_argument("--since", metavar="DAYS", type=int, default=14, help="For closed issues, only include issues updated in the past DAYS days [%(default)d]" ) parser.add_argument("--human", action="store_true", help="Human-readable output" ) parser.add_argument("--org", action="store_true", help="Break down by organization" ) parser.add_argument("--debug", action="store_true", help="Show debugging info" ) args = parser.parse_args(argv[1:]) DEBUG = args.debug since = None if args.since: since = date.today() - timedelta(days=args.since) if args.org: categories = sorted(get_all_orgs()) def cat_filter(cat, pr): return pr['org'] == cat else: categories = ["all"] def cat_filter(cat, pr): return True durations = { "open": { "internal": [], "external": [], }, "closed": { "internal": [], "external": [], } } repos = [ r for r in Repo.from_yaml() if r.track_pulls ] for repo in repos: get_duration_data(durations, repo.name, since) for linenum, cat in enumerate(categories): ss_friendly = [] for position in ("external", "internal"): for state in ("open", "closed"): seconds = [p['duration'].total_seconds() for p in durations[state][position] if cat_filter(cat, p)] if seconds: median_seconds = int(statistics.median(seconds)) median_duration = timedelta(seconds=median_seconds) else: median_seconds = -1 median_duration = "no data" population = "all" if state == "closed" and since: population = "since {date}".format(date=since) if args.human: print("median {position} {state} ({population}): {duration}".format( position=position, state=state, population=population, duration=median_duration )) else: ss_friendly += [len(seconds), median_seconds] if ss_friendly: if linenum == 0: print("cat\twhen\trepos\teopen\teopenage\teclosed\teclosedage\tiopen\tiopenage\ticlosed\ticlosedage") ss_data = "\t".join(str(x) for x in ss_friendly) print("{}\t{:%m/%d/%Y}\t{}\t{}".format(cat, date.today(), len(repos), ss_data))
def show_pulls(labels=None, show_comments=False, state="open", since=None, org=False, intext=None, merged=False): """ `labels`: Filters PRs by labels (all are shown if None is specified) `show_comments`: shows the last 5 comments on each PR, if True `state`: Filter PRs by this state (either 'open' or 'closed') `since`: a datetime representing the earliest time from which to pull information. All PRs regardless of time are shown if None is specified. `org`: If True, sorts by PR author affiliation `intext`: specify 'int' (internal) or 'ext' (external) pull request `merged`: If True and state="closed", shows only PRs that were merged. """ num = 0 adds = 0 deletes = 0 repos = [r for r in Repo.from_yaml() if r.track_pulls] for repo in repos: issues = get_pulls(repo.name, labels, state, since, org=org or intext, pull_details="all") category = None for issue in issues: issue.repo = repo.nick if intext is not None: if issue.intext != intext: continue if state == 'closed' and merged and issue.combinedstate != 'merged': # If we're filtering on closed PRs, and only want those that are merged, # skip ones that were closed without merge. continue if state == 'closed' and since: # If this PR was closed prior to the last `since` interval of days, continue on # (it may have been *updated* - that is, referenced or commented on - more recently, # but we just want to see what's been merged or closed in the past "since" days) if issue.closed_at < since: continue if org and issue.org != category: # new category! print category header category = issue.org print("-- {category} ----".format(category=category)) print(fformat(ISSUE_FMT, issue)) num += 1 adds += issue.additions deletes += issue.deletions if show_comments: comments = get_comments(issue) last_five_comments = reversed(more_itertools.take(5, comments)) for comment in last_five_comments: print(fformat(COMMENT_FMT, comment)) print() print("{num} pull requests; {adds}+ {deletes}-".format(num=num, adds=adds, deletes=deletes))