blob: ce6e1e23acb2076358605a20a84d3b48b2d37464 [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Sivas SRRa464a7c2017-02-19 23:35:59 -06002
3r"""
4Exports issues from a list of repositories to individual CSV files.
5Uses basic authentication (GitHub username + password) to retrieve issues
6from a repository that username has access to. Supports GitHub API v3.
7"""
8import argparse
9import csv
Sivas SRR9fe1b122017-03-05 22:29:28 -060010import getpass
Patrick Williams20f38712022-12-08 06:18:26 -060011
Sivas SRRa464a7c2017-02-19 23:35:59 -060012import requests
13
14auth = None
Patrick Williams20f38712022-12-08 06:18:26 -060015states = "all"
Sivas SRRa464a7c2017-02-19 23:35:59 -060016
17
18def write_issues(response, csv_out):
19 r"""
20 Parses JSON response and writes to CSV.
21 """
vinaybs665dbeaa2019-09-19 01:46:49 -050022 print(response)
Sivas SRRa464a7c2017-02-19 23:35:59 -060023 if response.status_code != 200:
24 raise Exception(response.status_code)
25 for issue in response.json():
Patrick Williams20f38712022-12-08 06:18:26 -060026 if "pull_request" not in issue:
27 labels = ", ".join([lable["name"] for lable in issue["labels"]])
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050028
Sivas SRR9fe1b122017-03-05 22:29:28 -060029 # Below lines to overcome "TypeError: 'NoneType' object has
30 # no attribute '__getitem__'"
31
Patrick Williams20f38712022-12-08 06:18:26 -060032 close_date = issue.get("closed_at")
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050033 if close_date:
Patrick Williams20f38712022-12-08 06:18:26 -060034 close_date = issue.get("closed_at").split("T")[0]
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050035
Patrick Williams20f38712022-12-08 06:18:26 -060036 assignee_resp = issue.get("assignees", "Not Assigned")
Sivas SRR9fe1b122017-03-05 22:29:28 -060037 if assignee_resp:
Patrick Williams20f38712022-12-08 06:18:26 -060038 owners = ",".join(
39 [
40 assignee_login["login"]
41 for assignee_login in assignee_resp
42 ]
43 )
Sivas SRR9fe1b122017-03-05 22:29:28 -060044 else:
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050045 owners = "Not Assigned"
46
Patrick Williams20f38712022-12-08 06:18:26 -060047 milestone_resp = issue.get("milestone", "Not Assigned")
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050048 if milestone_resp:
Patrick Williams20f38712022-12-08 06:18:26 -060049 milestone_resp = milestone_resp["title"].encode("utf-8")
Sivas SRR9fe1b122017-03-05 22:29:28 -060050
Sivas SRRa464a7c2017-02-19 23:35:59 -060051 # Change the following line to write out additional fields
Patrick Williams20f38712022-12-08 06:18:26 -060052 csv_out.writerow(
53 [
54 labels.encode("utf-8"),
55 issue.get("title").encode("utf-8"),
56 issue.get("state").encode("utf-8"),
57 issue.get("created_at").split("T")[0],
58 close_date,
59 issue.get("html_url").encode("utf-8"),
60 issue.get("user").get("login").encode("utf-8"),
61 owners,
62 milestone_resp,
63 ]
64 )
Sivas SRRa464a7c2017-02-19 23:35:59 -060065
66
Sivas SRR0419fb02017-05-28 10:09:18 -050067def get_issues_from_github_to_csv(name, response):
Sivas SRRa464a7c2017-02-19 23:35:59 -060068 r"""
69 Requests issues from GitHub API and writes to CSV file.
Sivas SRR0419fb02017-05-28 10:09:18 -050070 Description of argument(s):
71 name Name of the GitHub repository
72 response GitHub repository response
Sivas SRRa464a7c2017-02-19 23:35:59 -060073 """
vinaybs665dbeaa2019-09-19 01:46:49 -050074 print(name)
75 print(states)
Sivas SRRa464a7c2017-02-19 23:35:59 -060076
Sivas SRR0419fb02017-05-28 10:09:18 -050077 # Multiple requests are required if response is paged
Patrick Williams20f38712022-12-08 06:18:26 -060078 if "link" in response.headers:
79 pages = {
80 rel[6:-1]: url[url.index("<") + 1 : -1]
81 for url, rel in (
82 link.split(";") for link in response.headers["link"].split(",")
83 )
84 }
85 while "last" in pages and "next" in pages:
86 pages = {
87 rel[6:-1]: url[url.index("<") + 1 : -1]
88 for url, rel in (
89 link.split(";")
90 for link in response.headers["link"].split(",")
91 )
92 }
93 response = requests.get(pages["next"], auth=auth)
Sivas SRR0419fb02017-05-28 10:09:18 -050094 write_issues(response, csv_out)
Patrick Williams20f38712022-12-08 06:18:26 -060095 if pages["next"] == pages["last"]:
Sivas SRR0419fb02017-05-28 10:09:18 -050096 break
Sivas SRRa464a7c2017-02-19 23:35:59 -060097
Sivas SRRa464a7c2017-02-19 23:35:59 -060098
Patrick Williams20f38712022-12-08 06:18:26 -060099parser = argparse.ArgumentParser(
100 description="Write GitHub repository issues to CSV file."
101)
Sivas SRRa464a7c2017-02-19 23:35:59 -0600102
Patrick Williams20f38712022-12-08 06:18:26 -0600103parser.add_argument(
104 "username", nargs="?", help="GitHub user name, formatted as 'username'"
105)
Sivas SRRa464a7c2017-02-19 23:35:59 -0600106
Patrick Williams20f38712022-12-08 06:18:26 -0600107parser.add_argument(
108 "repositories",
109 nargs="+",
110 help="Repository names, formatted as 'basereponame/repo'",
111)
Sivas SRRa464a7c2017-02-19 23:35:59 -0600112
Patrick Williams20f38712022-12-08 06:18:26 -0600113parser.add_argument(
114 "--all", action="store_true", help="Returns both open and closed issues."
115)
Sivas SRR0419fb02017-05-28 10:09:18 -0500116
Sivas SRRa464a7c2017-02-19 23:35:59 -0600117args = parser.parse_args()
118
119if args.all:
Patrick Williams20f38712022-12-08 06:18:26 -0600120 state = "all"
Sivas SRRa464a7c2017-02-19 23:35:59 -0600121
Sivas SRR0419fb02017-05-28 10:09:18 -0500122username = args.username
Sivas SRRa464a7c2017-02-19 23:35:59 -0600123
Sivas SRR9fe1b122017-03-05 22:29:28 -0600124password = getpass.getpass("Enter your GitHub Password:")
Sivas SRRa464a7c2017-02-19 23:35:59 -0600125
126auth = (username, password)
127
Sivas SRR0419fb02017-05-28 10:09:18 -0500128# To set the csv filename
129csvfilename = ""
Sivas SRRa464a7c2017-02-19 23:35:59 -0600130for repository in args.repositories:
Patrick Williams20f38712022-12-08 06:18:26 -0600131 csvfilename_temp = "{}".format(repository.replace("/", "-"))
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500132 csvfilename = csvfilename + csvfilename_temp
Patrick Williams20f38712022-12-08 06:18:26 -0600133csvfilename = csvfilename + "-issues.csv"
134with open(csvfilename, "w") as csvfileout:
Sivas SRR0419fb02017-05-28 10:09:18 -0500135 csv_out = csv.writer(csvfileout)
Patrick Williams20f38712022-12-08 06:18:26 -0600136 csv_out.writerow(
137 [
138 "Labels",
139 "Title",
140 "State",
141 "Open Date",
142 "Close Date",
143 "URL",
144 "Author",
145 "Assignees",
146 "Milestone",
147 ]
148 )
Sivas SRR0419fb02017-05-28 10:09:18 -0500149 for repository in args.repositories:
Patrick Williams20f38712022-12-08 06:18:26 -0600150 l_url = "https://api.github.com/repos/{}/issues?state={}"
Sivas SRR0419fb02017-05-28 10:09:18 -0500151 l_url = l_url.format(repository, states)
152 response = requests.get(l_url, auth=auth)
153 write_issues(response, csv_out)
154 get_issues_from_github_to_csv(repository, response)
155csvfileout.close()