def get_bucket_data(buckets, repo_name, date_bucket_fn, start, by_size=False, lines=False, closed=False): print(repo_name) pull_details = "all" if (by_size or lines) else "list" for pull in get_pulls(repo_name, state="all", pull_details=pull_details, org=True): # print("{0.id}: {0.combinedstate} {0.intext}".format(pull)) ignore_ref = "(^release$|^rc/)" if re.search(ignore_ref, pull.base_ref) or pull.title == "Update translations (autogenerated message)": #print("Ignoring pull #{0.number}: {0.title}".format(pull)) continue if by_size: size = " " + size_of_pull(pull) else: size = "" intext = pull.intext if lines: increment = lines_in_pull(pull) else: increment = 1 created = make_timezone_aware(pull.created_at) if created >= start: buckets[date_bucket_fn(created)]["opened " + intext + size] += increment if pull.combinedstate == "merged": merged = make_timezone_aware(pull.merged_at) if merged >= start: buckets[date_bucket_fn(merged)]["merged " + intext + size] += increment elif closed and pull.combinedstate == "closed": closed = make_timezone_aware(pull.closed_at) buckets[date_bucket_fn(closed)]["closed " + intext + size] += increment
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="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 get_bucket_data(buckets, repo_name, date_bucket_fn, start, by_size=False, lines=False, closed=False): print(repo_name) pull_details = "all" if (by_size or lines) else "list" for pull in get_pulls(repo_name, state="all", pull_details=pull_details, org=True): # print("{0.id}: {0.combinedstate} {0.intext}".format(pull)) ignore_ref = "(^release$|^rc/)" if re.search(ignore_ref, pull.base_ref): #print("Ignoring pull #{0.number}: {0.title}".format(pull)) continue if by_size: size = " " + size_of_pull(pull) else: size = "" intext = pull.intext if lines: increment = lines_in_pull(pull) else: increment = 1 created = make_timezone_aware(pull.created_at) if created >= start: buckets[date_bucket_fn(created)]["opened " + intext + size] += increment if pull.combinedstate == "merged": merged = make_timezone_aware(pull.merged_at) if merged >= start: buckets[date_bucket_fn(merged)]["merged " + intext + size] += increment elif closed and pull.combinedstate == "closed": closed = make_timezone_aware(pull.closed_at) buckets[date_bucket_fn(closed)]["closed " + intext + size] += increment
def unique_authors(repos, days_window, interesting): """Produce a sequence of pairs: (date, num-contributors).""" pulls = get_summaries_from_repos(repos, interesting) key = lambda s: make_timezone_aware(s.created) width = datetime.timedelta(days=days_window) step = datetime.timedelta(days=7) for when, window in sliding_window(pulls, key=key, width=width, step=step): num_authors = len(set(p.user for p in window)) yield (when+width, num_authors)
def unique_authors(repos, days_window, interesting): """Produce a sequence of pairs: (date, num-contributors).""" pulls = get_summaries_from_repos(repos, interesting) key = lambda s: make_timezone_aware(s.created) width = datetime.timedelta(days=days_window) step = datetime.timedelta(days=7) for when, window in sliding_window(pulls, key=key, width=width, step=step): num_authors = len(set(p.user for p in window)) yield (when + width, num_authors)
def main(argv): parser = argparse.ArgumentParser(description="Calculate internal & external pull requests, both opened & merged, by quarter.") parser.add_argument( "--monthly", action="store_true", help="Report on months instead of quarters" ) parser.add_argument( "--weekly", action="store_true", help="Report on weeks instead of quarters" ) parser.add_argument( "--by-size", action="store_true", help="Include a breakdown by small/large, which is WILDLY arbitrary, " "and a poor predicter of either effort or impact." ) parser.add_argument( "--lines", action="store_true", help="Count the number of lines changed instead of number of pull requests" ) 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( "--db", action="store_true", help="Use WebhookDB instead of GitHub API" ) parser.add_argument( "--closed", action="store_true", help="Include closed pull requests also" ) args = parser.parse_args(argv[1:]) if args.monthly: date_bucket_fn = date_bucket_month elif args.weekly: date_bucket_fn = date_bucket_week else: date_bucket_fn = date_bucket_quarter if args.start is None: # Simplify the logic by always having a start date, but one so far back # that it is like having no start date. args.start = make_timezone_aware(datetime.datetime(2000, 1, 1)) global get_pulls if args.db: from webhookdb import get_pulls else: from githubapi import get_pulls get_all_repos(date_bucket_fn, by_size=args.by_size, start=args.start, lines=args.lines, closed=args.closed)
def main(argv): parser = argparse.ArgumentParser(description="Summarize pull requests.") parser.add_argument("--monthly", action="store_true", help="Report on months instead of quarters") parser.add_argument("--weekly", action="store_true", help="Report on weeks instead of quarters") parser.add_argument( "--by-size", action="store_true", help="Include a breakdown by small/large, which is WILDLY arbitrary, " "and a poor predicter of either effort or impact.") parser.add_argument( "--lines", action="store_true", help= "Count the number of lines changed instead of number of pull requests") 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("--db", action="store_true", help="Use WebhookDB instead of GitHub API") parser.add_argument("--closed", action="store_true", help="Include closed pull requests also") args = parser.parse_args(argv[1:]) if args.monthly: date_bucket_fn = date_bucket_month elif args.weekly: date_bucket_fn = date_bucket_week else: date_bucket_fn = date_bucket_quarter if args.start is None: # Simplify the logic by always having a start date, but one so far back # that it is like having no start date. args.start = make_timezone_aware(datetime.datetime(2000, 1, 1)) global get_pulls if args.db: from webhookdb import get_pulls else: from githubapi import get_pulls get_all_repos(date_bucket_fn, by_size=args.by_size, start=args.start, lines=args.lines, closed=args.closed)
def main(argv): parser = argparse.ArgumentParser( description= "Calculate external pull requests opened, and their current resolution status, by month." ) parser.add_argument("--quarterly", action="store_true", help="Report on quarters instead of months") parser.add_argument("--weekly", action="store_true", help="Report on weeks instead of months") parser.add_argument("--internal", action="store_true", help="Report on internal, rather than external, prs.") parser.add_argument( "--lines", action="store_true", help= "Count the number of lines changed instead of number of pull requests") 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("--db", action="store_true", help="Use WebhookDB instead of GitHub API") args = parser.parse_args(argv[1:]) if args.quarterly: date_bucket_fn = date_bucket_quarter elif args.weekly: date_bucket_fn = date_bucket_week else: date_bucket_fn = date_bucket_month if args.start is None: # Default list past 24 months two_yr = datetime.datetime.now() - datetime.timedelta(days=365 * 2) # Be sure to start on the 1st day of the month 23 months ago # (we report 24 months of data, inclusive of current month) args.start = make_timezone_aware( datetime.datetime(two_yr.year, two_yr.month + 1, 1)) global get_pulls if args.db: from webhookdb import get_pulls else: from githubapi import get_pulls get_all_repos(date_bucket_fn, start=args.start, lines=args.lines, internal=args.internal)
def main(argv): parser = argparse.ArgumentParser(description="Calculate external pull requests opened, and their current resolution status, by month.") parser.add_argument( "--quarterly", action="store_true", help="Report on quarters instead of months" ) parser.add_argument( "--weekly", action="store_true", help="Report on weeks instead of months" ) parser.add_argument( "--internal", action="store_true", help="Report on internal, rather than external, prs." ) parser.add_argument( "--lines", action="store_true", help="Count the number of lines changed instead of number of pull requests" ) 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( "--db", action="store_true", help="Use WebhookDB instead of GitHub API" ) args = parser.parse_args(argv[1:]) if args.quarterly: date_bucket_fn = date_bucket_quarter elif args.weekly: date_bucket_fn = date_bucket_week else: date_bucket_fn = date_bucket_month if args.start is None: # Default list past 24 months two_yr = datetime.datetime.now() - datetime.timedelta(days=365*2) # Be sure to start on the 1st day of the month 23 months ago # (we report 24 months of data, inclusive of current month) args.start = make_timezone_aware(datetime.datetime(two_yr.year, two_yr.month + 1, 1)) global get_pulls if args.db: from webhookdb import get_pulls else: from githubapi import get_pulls get_all_repos(date_bucket_fn, start=args.start, lines=args.lines, internal=args.internal)
def main(argv): parser = argparse.ArgumentParser( description= "Calculate external pull requests opened, and their current resolution status, by month." ) parser.add_argument("--quarterly", action="store_true", help="Report on quarters instead of months") parser.add_argument("--weekly", action="store_true", help="Report on weeks instead of months") parser.add_argument("--internal", action="store_true", help="Report on internal, rather than external, prs.") parser.add_argument( "--lines", action="store_true", help= "Count the number of lines changed instead of number of pull requests") 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("--db", action="store_true", help="Use WebhookDB instead of GitHub API") args = parser.parse_args(argv[1:]) if args.quarterly: date_bucket_fn = date_bucket_quarter elif args.weekly: date_bucket_fn = date_bucket_week else: date_bucket_fn = date_bucket_month if args.start is None: # Start keeping track Jun 1 2013 (when we became open source) args.start = make_timezone_aware(datetime.datetime(2013, 6, 1)) global get_pulls if args.db: from webhookdb import get_pulls else: from githubapi import get_pulls get_all_repos(date_bucket_fn, start=args.start, lines=args.lines, internal=args.internal)
def main(argv): parser = argparse.ArgumentParser(description="Count external pull requests opened by person") parser.add_argument( "--since", metavar="DAYS", type=int, help="Use a start date DAYS ago" ) 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: " "20141225, Dec/25/2014, 2014-12-25, etc" ) args = parser.parse_args(argv[1:]) if args.start is None: if args.since is None: # Simplify the logic by always having a start date, but one so far back # that it is like having no start date. args.start = make_timezone_aware(datetime.datetime(2000, 1, 1)) else: args.start = make_timezone_aware(datetime.datetime.now() - datetime.timedelta(days=args.since)) if args.end is None: # Simplify the logic by always having an end date, but one so far ahead # that it is like having no end date. args.end = make_timezone_aware(datetime.datetime(2040, 1, 1)) pulls = get_pulls_in_window(args.start, args.end) board = get_contributor_counts(pulls) board = sorted(((v, k) for k,v in board.items()), reverse=True) for i, (count, user_login) in enumerate(board, start=1): print("{:4d}: {:4d} {}".format(i, count, user_login))
def get_bucket_data(buckets, repo_name, date_bucket_fn, start, lines=False, internal=False): print(repo_name) pull_details = "all" if lines else "list" for pull in get_pulls(repo_name, state="all", pull_details=pull_details, org=True): # print("{0.id}: {0.combinedstate} {0.intext}".format(pull)) intext = pull.intext # internal or external # if internal is True, only want to look at "internal" PRs, and if # internal is False, only want to look at "external" PRs. if (internal and intext != 'internal') or (not internal and intext != 'external'): continue ignore_ref = "(^release$|^rc/)" if re.search( ignore_ref, pull.base_ref ) or pull.title == "Update translations (autogenerated message)": # print("Ignoring pull #{0.number}: {0.title}".format(pull)) continue if lines: increment = lines_in_pull(pull) else: increment = 1 created = make_timezone_aware(pull.created_at) bucket_key = date_bucket_fn(created) if created >= start: buckets[bucket_key]["opened " + intext] += increment # Bucket based on its current state if pull.combinedstate == "merged": buckets[bucket_key]["merged " + intext] += increment elif pull.combinedstate == "closed": buckets[bucket_key]["closed " + intext] += increment else: # PR is still open print("Unresolved[{1}]: pull #{0.number}: {0.title}".format( pull, bucket_key)) buckets[bucket_key]["unresolved " + intext] += increment
def main(argv): parser = argparse.ArgumentParser(description="Calculate external pull requests opened, and their current resolution status, by month.") parser.add_argument( "--quarterly", action="store_true", help="Report on quarters instead of months" ) parser.add_argument( "--weekly", action="store_true", help="Report on weeks instead of months" ) parser.add_argument( "--internal", action="store_true", help="Report on internal, rather than external, prs." ) parser.add_argument( "--lines", action="store_true", help="Count the number of lines changed instead of number of pull requests" ) 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( "--db", action="store_true", help="Use WebhookDB instead of GitHub API" ) args = parser.parse_args(argv[1:]) if args.quarterly: date_bucket_fn = date_bucket_quarter elif args.weekly: date_bucket_fn = date_bucket_week else: date_bucket_fn = date_bucket_month if args.start is None: # Start keeping track Jun 1 2013 (when we became open source) args.start = make_timezone_aware(datetime.datetime(2013, 6, 1)) global get_pulls if args.db: from webhookdb import get_pulls else: from githubapi import get_pulls get_all_repos(date_bucket_fn, start=args.start, lines=args.lines, internal=args.internal)
def get_bucket_data(buckets, repo_name, date_bucket_fn, start, lines=False, internal=False): print(repo_name) pull_details = "all" if lines else "list" for pull in get_pulls(repo_name, state="all", pull_details=pull_details, org=True): # print("{0.id}: {0.combinedstate} {0.intext}".format(pull)) intext = pull.intext # internal or external # if internal is True, only want to look at "internal" PRs, and if # internal is False, only want to look at "external" PRs. if (internal and intext != 'internal') or (not internal and intext != 'external'): continue ignore_ref = "(^release$|^rc/)" if re.search(ignore_ref, pull.base_ref) or pull.title == "Update translations (autogenerated message)": # print("Ignoring pull #{0.number}: {0.title}".format(pull)) continue if lines: increment = lines_in_pull(pull) else: increment = 1 created = make_timezone_aware(pull.created_at) bucket_key = date_bucket_fn(created) if created >= start: buckets[bucket_key]["opened " + intext] += increment # Bucket based on its current state if pull.combinedstate == "merged": buckets[bucket_key]["merged " + intext] += increment elif pull.combinedstate == "closed": buckets[bucket_key]["closed " + intext] += increment else: # PR is still open print("Unresolved[{1}]: pull #{0.number}: {0.title}".format(pull, bucket_key)) buckets[bucket_key]["unresolved " + intext] += increment
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 get_pulls_in_window(start, end): for pull in get_all_external_pulls(): if start < make_timezone_aware(pull.created_at) < end: yield pull
def main(argv): parser = argparse.ArgumentParser(description="Summarize pull requests.") parser.add_argument( "--closed", action='store_true', help="Include closed pull requests", ) parser.add_argument( "--merged", action='store_true', help="Include just merged pull requests", ) parser.add_argument( "--open", action='store_true', help="Include open pull requests", ) parser.add_argument( "--external", action='store_true', help="Include external pull requests", ) parser.add_argument( "--internal", action='store_true', help="Include internal pull requests", ) parser.add_argument( "--comments", dest="show_comments", action='store_true', help="Also show 5 most recent comments", ) parser.add_argument( "--debug", help="See what's going on. DEBUG=http or json are fun.", ) parser.add_argument( "--org", action='store_true', help="Include and sort by affiliation", ) parser.add_argument( "--since", metavar="DAYS", type=int, help="Include pull requests active in the last DAYS days.", ) args = parser.parse_args(argv[1:]) if args.debug == "requests": requests.all_requests = [] merged = False if args.merged: if args.open or args.closed: print("--open and --closed options not supported when --merged is specified.") return args.closed = True merged = True if args.open: if args.closed: state = "all" else: state = "open" else: if args.closed: state = "closed" else: state = "open" if args.internal: intext = "internal" elif args.external: intext = "external" else: intext = None since = None if args.since: since = datetime.datetime.now() - datetime.timedelta(days=args.since) since = make_timezone_aware(since) show_pulls( show_comments=args.show_comments, state=state, since=since, org=args.org, intext=intext, merged=merged, ) if args.debug == "requests": print("{} requests:".format(len(requests.all_requests))) for req in requests.all_requests: print(req)
def main(argv): parser = argparse.ArgumentParser(description="Summarize pull requests.") parser.add_argument( "--closed", action='store_true', help="Include closed pull requests", ) parser.add_argument( "--merged", action='store_true', help="Include just merged pull requests", ) parser.add_argument( "--open", action='store_true', help="Include open pull requests", ) parser.add_argument( "--external", action='store_true', help="Include external pull requests", ) parser.add_argument( "--internal", action='store_true', help="Include internal pull requests", ) parser.add_argument( "--comments", dest="show_comments", action='store_true', help="Also show 5 most recent comments", ) parser.add_argument( "--debug", help="See what's going on. DEBUG=http or json are fun.", ) parser.add_argument( "--org", action='store_true', help="Include and sort by affiliation", ) parser.add_argument( "--since", metavar="DAYS", type=int, help="Include pull requests active in the last DAYS days.", ) args = parser.parse_args(argv[1:]) if args.debug == "requests": requests.all_requests = [] merged = False if args.merged: if args.open or args.closed: print( "--open and --closed options not supported when --merged is specified." ) return args.closed = True merged = True if args.open: if args.closed: state = "all" else: state = "open" else: if args.closed: state = "closed" else: state = "open" if args.internal: intext = "internal" elif args.external: intext = "external" else: intext = None since = None if args.since: since = datetime.datetime.now() - datetime.timedelta(days=args.since) since = make_timezone_aware(since) show_pulls( show_comments=args.show_comments, state=state, since=since, org=args.org, intext=intext, merged=merged, ) if args.debug == "requests": print("{} requests:".format(len(requests.all_requests))) for req in requests.all_requests: print(req)