blob: ce6e1e23acb2076358605a20a84d3b48b2d37464 [file] [log] [blame]
#!/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()