def get_duration_data(durations, owner_repo="edx/edx-platform", since=None): """ Update `durations`, a dict of dict of lists of pull requests. `durations` has four lists of data, where each list contains pull requests: internal open pull requests (all) external open pull requests (all) internal closed pull requests (since the `since` value) external closed pull requests (since the `since` value) These lists are organized into a dictionary that categorizes the lists by position and state. """ open_issues_generator = itertools.izip( get_pulls(owner_repo, state="open", org=True), itertools.repeat("open")) closed_issues_generator = itertools.izip( get_pulls(owner_repo, state="closed", since=since, org=True), itertools.repeat("closed")) for issue, state in itertools.chain(open_issues_generator, closed_issues_generator): created_at = issue.created_at if state == "open": closed_at = datetime.utcnow() else: closed_at = issue.closed_at issue.duration = closed_at - created_at if DEBUG: print("{pr.id}: {pr.intext} {state}".format(pr=issue, state=state), file=sys.stderr) durations[state][issue.intext].append(issue)
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 get_duration_data(durations, owner_repo="edx/edx-platform", since=None): """ Update `durations`, a dict of dict of lists of pull requests. `durations` has four lists of data, where each list contains pull requests: internal open pull requests (all) external open pull requests (all) internal closed pull requests (since the `since` value) external closed pull requests (since the `since` value) These lists are organized into a dictionary that categorizes the lists by position and state. """ open_issues_generator = itertools.izip( get_pulls(owner_repo, state="open", org=True), itertools.repeat("open") ) closed_issues_generator = itertools.izip( get_pulls(owner_repo, state="closed", since=since, org=True), itertools.repeat("closed") ) for issue, state in itertools.chain(open_issues_generator, closed_issues_generator): created_at = issue.created_at if state == "open": closed_at = datetime.utcnow() else: closed_at = issue.closed_at issue.duration = closed_at - created_at if DEBUG: print("{pr.id}: {pr.intext} {state}".format( pr=issue, state=state ), file=sys.stderr) durations[state][issue.intext].append(issue)
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 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 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_external_pulls(repo): """Produce a stream of external pull requests.""" for issue in get_pulls(repo, state="all", org=True): if issue.intext == 'external': yield issue
def get_filtered_pulls(repo, interesting): """Produce a stream of external pull requests.""" for issue in get_pulls(repo, state="all", org=True): if interesting(issue): yield issue