#!/usr/bin/env python3 r""" Exports issues from a list of repositories to individual CSV files. Uses basic authentication (GitHub username + password) to retrieve issues from a repository that username has access to. Supports GitHub API v3. """ import argparse import csv import getpass import requests auth = None states = "all" def write_issues(response, csv_out): r""" Parses JSON response and writes to CSV. """ print(response) if response.status_code != 200: raise Exception(response.status_code) for issue in response.json(): if "pull_request" not in issue: labels = ", ".join([lable["name"] for lable in issue["labels"]]) # Below lines to overcome "TypeError: 'NoneType' object has # no attribute '__getitem__'" close_date = issue.get("closed_at") if close_date: close_date = issue.get("closed_at").split("T")[0] assignee_resp = issue.get("assignees", "Not Assigned") if assignee_resp: owners = ",".join( [ assignee_login["login"] for assignee_login in assignee_resp ] ) else: owners = "Not Assigned" milestone_resp = issue.get("milestone", "Not Assigned") if milestone_resp: milestone_resp = milestone_resp["title"].encode("utf-8") # Change the following line to write out additional fields csv_out.writerow( [ labels.encode("utf-8"), issue.get("title").encode("utf-8"), issue.get("state").encode("utf-8"), issue.get("created_at").split("T")[0], close_date, issue.get("html_url").encode("utf-8"), issue.get("user").get("login").encode("utf-8"), owners, milestone_resp, ] ) def get_issues_from_github_to_csv(name, response): r""" Requests issues from GitHub API and writes to CSV file. Description of argument(s): name Name of the GitHub repository response GitHub repository response """ print(name) print(states) # Multiple requests are required if response is paged if "link" in response.headers: pages = { rel[6:-1]: url[url.index("<") + 1 : -1] for url, rel in ( link.split(";") for link in response.headers["link"].split(",") ) } while "last" in pages and "next" in pages: pages = { rel[6:-1]: url[url.index("<") + 1 : -1] for url, rel in ( link.split(";") for link in response.headers["link"].split(",") ) } response = requests.get(pages["next"], auth=auth) write_issues(response, csv_out) if pages["next"] == pages["last"]: break parser = argparse.ArgumentParser( description="Write GitHub repository issues to CSV file." ) parser.add_argument( "username", nargs="?", help="GitHub user name, formatted as 'username'" ) parser.add_argument( "repositories", nargs="+", help="Repository names, formatted as 'basereponame/repo'", ) parser.add_argument( "--all", action="store_true", help="Returns both open and closed issues." ) args = parser.parse_args() if args.all: state = "all" username = args.username password = getpass.getpass("Enter your GitHub Password:") auth = (username, password) # To set the csv filename csvfilename = "" for repository in args.repositories: csvfilename_temp = "{}".format(repository.replace("/", "-")) csvfilename = csvfilename + csvfilename_temp csvfilename = csvfilename + "-issues.csv" with open(csvfilename, "w") as csvfileout: csv_out = csv.writer(csvfileout) csv_out.writerow( [ "Labels", "Title", "State", "Open Date", "Close Date", "URL", "Author", "Assignees", "Milestone", ] ) for repository in args.repositories: l_url = "https://api.github.com/repos/{}/issues?state={}" l_url = l_url.format(repository, states) response = requests.get(l_url, auth=auth) write_issues(response, csv_out) get_issues_from_github_to_csv(repository, response) csvfileout.close()