xref: /openbmc/openbmc-test-automation/tools/github_issues_to_csv (revision 249fbcc0be4641e2d43987badc764e1f73a6ebeb)
1#!/usr/bin/env python3
2
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"""
8
9import argparse
10import csv
11import getpass
12
13import requests
14
15auth = None
16states = "all"
17
18
19def write_issues(response, csv_out):
20    r"""
21    Parses JSON response and writes to CSV.
22    """
23    print(response)
24    if response.status_code != 200:
25        raise Exception(response.status_code)
26    for issue in response.json():
27        if "pull_request" not in issue:
28            labels = ", ".join([label["name"] for label in issue["labels"]])
29
30            # Below lines to overcome "TypeError: 'NoneType' object has
31            # no attribute '__getitem__'"
32
33            close_date = issue.get("closed_at")
34            if close_date:
35                close_date = issue.get("closed_at").split("T")[0]
36
37            assignee_resp = issue.get("assignees", "Not Assigned")
38            if assignee_resp:
39                owners = ",".join(
40                    [
41                        assignee_login["login"]
42                        for assignee_login in assignee_resp
43                    ]
44                )
45            else:
46                owners = "Not Assigned"
47
48            milestone_resp = issue.get("milestone", "Not Assigned")
49            if milestone_resp:
50                milestone_resp = milestone_resp["title"].encode("utf-8")
51
52            # Change the following line to write out additional fields
53            csv_out.writerow(
54                [
55                    labels.encode("utf-8"),
56                    issue.get("title").encode("utf-8"),
57                    issue.get("state").encode("utf-8"),
58                    issue.get("created_at").split("T")[0],
59                    close_date,
60                    issue.get("html_url").encode("utf-8"),
61                    issue.get("user").get("login").encode("utf-8"),
62                    owners,
63                    milestone_resp,
64                ]
65            )
66
67
68def get_issues_from_github_to_csv(name, response):
69    r"""
70    Requests issues from GitHub API and writes to CSV file.
71    Description of argument(s):
72    name  Name of the GitHub repository
73    response  GitHub repository response
74    """
75    print(name)
76    print(states)
77
78    # Multiple requests are required if response is paged
79    if "link" in response.headers:
80        pages = {
81            rel[6:-1]: url[url.index("<") + 1 : -1]
82            for url, rel in (
83                link.split(";") for link in response.headers["link"].split(",")
84            )
85        }
86        while "last" in pages and "next" in pages:
87            pages = {
88                rel[6:-1]: url[url.index("<") + 1 : -1]
89                for url, rel in (
90                    link.split(";")
91                    for link in response.headers["link"].split(",")
92                )
93            }
94            response = requests.get(pages["next"], auth=auth)
95            write_issues(response, csv_out)
96            if pages["next"] == pages["last"]:
97                break
98
99
100parser = argparse.ArgumentParser(
101    description="Write GitHub repository issues to CSV file."
102)
103
104parser.add_argument(
105    "username", nargs="?", help="GitHub user name, formatted as 'username'"
106)
107
108parser.add_argument(
109    "repositories",
110    nargs="+",
111    help="Repository names, formatted as 'basereponame/repo'",
112)
113
114parser.add_argument(
115    "--all", action="store_true", help="Returns both open and closed issues."
116)
117
118args = parser.parse_args()
119
120if args.all:
121    state = "all"
122
123username = args.username
124
125password = getpass.getpass("Enter your GitHub Password:")
126
127auth = (username, password)
128
129# To set the csv filename
130csvfilename = ""
131for repository in args.repositories:
132    csvfilename_temp = "{}".format(repository.replace("/", "-"))
133    csvfilename = csvfilename + csvfilename_temp
134csvfilename = csvfilename + "-issues.csv"
135with open(csvfilename, "w") as csvfileout:
136    csv_out = csv.writer(csvfileout)
137    csv_out.writerow(
138        [
139            "Labels",
140            "Title",
141            "State",
142            "Open Date",
143            "Close Date",
144            "URL",
145            "Author",
146            "Assignees",
147            "Milestone",
148        ]
149    )
150    for repository in args.repositories:
151        l_url = "https://api.github.com/repos/{}/issues?state={}"
152        l_url = l_url.format(repository, states)
153        response = requests.get(l_url, auth=auth)
154        write_issues(response, csv_out)
155        get_issues_from_github_to_csv(repository, response)
156csvfileout.close()
157