xref: /openbmc/openbmc-test-automation/tools/github_issues_to_csv (revision 4ebb32815b41550d1d8802b985dcafb1f4e42419)
1e7e9171eSGeorge Keishing#!/usr/bin/env python3
2a464a7ceSSivas SRR
3a464a7ceSSivas SRRr"""
4a464a7ceSSivas SRRExports issues from a list of repositories to individual CSV files.
5a464a7ceSSivas SRRUses basic authentication (GitHub username + password) to retrieve issues
6a464a7ceSSivas SRRfrom a repository that username has access to. Supports GitHub API v3.
7a464a7ceSSivas SRR"""
8a464a7ceSSivas SRRimport argparse
9a464a7ceSSivas SRRimport csv
109fe1b12eSSivas SRRimport getpass
1120f38712SPatrick Williams
12a464a7ceSSivas SRRimport requests
13a464a7ceSSivas SRR
14a464a7ceSSivas SRRauth = None
1520f38712SPatrick Williamsstates = "all"
16a464a7ceSSivas SRR
17a464a7ceSSivas SRR
18a464a7ceSSivas SRRdef write_issues(response, csv_out):
19a464a7ceSSivas SRR    r"""
20a464a7ceSSivas SRR    Parses JSON response and writes to CSV.
21a464a7ceSSivas SRR    """
2265dbeaadSvinaybs6    print(response)
23a464a7ceSSivas SRR    if response.status_code != 200:
24a464a7ceSSivas SRR        raise Exception(response.status_code)
25a464a7ceSSivas SRR    for issue in response.json():
2620f38712SPatrick Williams        if "pull_request" not in issue:
27*4ebb3281SGeorge Keishing            labels = ", ".join([label["name"] for label in issue["labels"]])
28b4eb9ac1SSivas SRR
299fe1b12eSSivas SRR            # Below lines to overcome "TypeError: 'NoneType' object has
309fe1b12eSSivas SRR            # no attribute '__getitem__'"
319fe1b12eSSivas SRR
3220f38712SPatrick Williams            close_date = issue.get("closed_at")
33b4eb9ac1SSivas SRR            if close_date:
3420f38712SPatrick Williams                close_date = issue.get("closed_at").split("T")[0]
35b4eb9ac1SSivas SRR
3620f38712SPatrick Williams            assignee_resp = issue.get("assignees", "Not Assigned")
379fe1b12eSSivas SRR            if assignee_resp:
3820f38712SPatrick Williams                owners = ",".join(
3920f38712SPatrick Williams                    [
4020f38712SPatrick Williams                        assignee_login["login"]
4120f38712SPatrick Williams                        for assignee_login in assignee_resp
4220f38712SPatrick Williams                    ]
4320f38712SPatrick Williams                )
449fe1b12eSSivas SRR            else:
45b4eb9ac1SSivas SRR                owners = "Not Assigned"
46b4eb9ac1SSivas SRR
4720f38712SPatrick Williams            milestone_resp = issue.get("milestone", "Not Assigned")
48b4eb9ac1SSivas SRR            if milestone_resp:
4920f38712SPatrick Williams                milestone_resp = milestone_resp["title"].encode("utf-8")
509fe1b12eSSivas SRR
51a464a7ceSSivas SRR            # Change the following line to write out additional fields
5220f38712SPatrick Williams            csv_out.writerow(
5320f38712SPatrick Williams                [
5420f38712SPatrick Williams                    labels.encode("utf-8"),
5520f38712SPatrick Williams                    issue.get("title").encode("utf-8"),
5620f38712SPatrick Williams                    issue.get("state").encode("utf-8"),
5720f38712SPatrick Williams                    issue.get("created_at").split("T")[0],
58b4eb9ac1SSivas SRR                    close_date,
5920f38712SPatrick Williams                    issue.get("html_url").encode("utf-8"),
6020f38712SPatrick Williams                    issue.get("user").get("login").encode("utf-8"),
6120f38712SPatrick Williams                    owners,
6220f38712SPatrick Williams                    milestone_resp,
6320f38712SPatrick Williams                ]
6420f38712SPatrick Williams            )
65a464a7ceSSivas SRR
66a464a7ceSSivas SRR
670419fb0aSSivas SRRdef get_issues_from_github_to_csv(name, response):
68a464a7ceSSivas SRR    r"""
69a464a7ceSSivas SRR    Requests issues from GitHub API and writes to CSV file.
700419fb0aSSivas SRR    Description of argument(s):
710419fb0aSSivas SRR    name  Name of the GitHub repository
720419fb0aSSivas SRR    response  GitHub repository response
73a464a7ceSSivas SRR    """
7465dbeaadSvinaybs6    print(name)
7565dbeaadSvinaybs6    print(states)
76a464a7ceSSivas SRR
77a464a7ceSSivas SRR    # Multiple requests are required if response is paged
7820f38712SPatrick Williams    if "link" in response.headers:
7920f38712SPatrick Williams        pages = {
8020f38712SPatrick Williams            rel[6:-1]: url[url.index("<") + 1 : -1]
8120f38712SPatrick Williams            for url, rel in (
8220f38712SPatrick Williams                link.split(";") for link in response.headers["link"].split(",")
8320f38712SPatrick Williams            )
8420f38712SPatrick Williams        }
8520f38712SPatrick Williams        while "last" in pages and "next" in pages:
8620f38712SPatrick Williams            pages = {
8720f38712SPatrick Williams                rel[6:-1]: url[url.index("<") + 1 : -1]
8820f38712SPatrick Williams                for url, rel in (
8920f38712SPatrick Williams                    link.split(";")
9020f38712SPatrick Williams                    for link in response.headers["link"].split(",")
9120f38712SPatrick Williams                )
9220f38712SPatrick Williams            }
9320f38712SPatrick Williams            response = requests.get(pages["next"], auth=auth)
94a464a7ceSSivas SRR            write_issues(response, csv_out)
9520f38712SPatrick Williams            if pages["next"] == pages["last"]:
96a464a7ceSSivas SRR                break
97a464a7ceSSivas SRR
98a464a7ceSSivas SRR
9920f38712SPatrick Williamsparser = argparse.ArgumentParser(
10020f38712SPatrick Williams    description="Write GitHub repository issues to CSV file."
10120f38712SPatrick Williams)
102a464a7ceSSivas SRR
10320f38712SPatrick Williamsparser.add_argument(
10420f38712SPatrick Williams    "username", nargs="?", help="GitHub user name, formatted as 'username'"
10520f38712SPatrick Williams)
106a464a7ceSSivas SRR
10720f38712SPatrick Williamsparser.add_argument(
10820f38712SPatrick Williams    "repositories",
10920f38712SPatrick Williams    nargs="+",
11020f38712SPatrick Williams    help="Repository names, formatted as 'basereponame/repo'",
11120f38712SPatrick Williams)
112a464a7ceSSivas SRR
11320f38712SPatrick Williamsparser.add_argument(
11420f38712SPatrick Williams    "--all", action="store_true", help="Returns both open and closed issues."
11520f38712SPatrick Williams)
1160419fb0aSSivas SRR
117a464a7ceSSivas SRRargs = parser.parse_args()
118a464a7ceSSivas SRR
119a464a7ceSSivas SRRif args.all:
12020f38712SPatrick Williams    state = "all"
121a464a7ceSSivas SRR
1220419fb0aSSivas SRRusername = args.username
123a464a7ceSSivas SRR
1249fe1b12eSSivas SRRpassword = getpass.getpass("Enter your GitHub Password:")
125a464a7ceSSivas SRR
126a464a7ceSSivas SRRauth = (username, password)
127a464a7ceSSivas SRR
1280419fb0aSSivas SRR# To set the csv filename
1290419fb0aSSivas SRRcsvfilename = ""
130a464a7ceSSivas SRRfor repository in args.repositories:
13120f38712SPatrick Williams    csvfilename_temp = "{}".format(repository.replace("/", "-"))
1320419fb0aSSivas SRR    csvfilename = csvfilename + csvfilename_temp
13320f38712SPatrick Williamscsvfilename = csvfilename + "-issues.csv"
13420f38712SPatrick Williamswith open(csvfilename, "w") as csvfileout:
1350419fb0aSSivas SRR    csv_out = csv.writer(csvfileout)
13620f38712SPatrick Williams    csv_out.writerow(
13720f38712SPatrick Williams        [
13820f38712SPatrick Williams            "Labels",
13920f38712SPatrick Williams            "Title",
14020f38712SPatrick Williams            "State",
14120f38712SPatrick Williams            "Open Date",
14220f38712SPatrick Williams            "Close Date",
14320f38712SPatrick Williams            "URL",
14420f38712SPatrick Williams            "Author",
14520f38712SPatrick Williams            "Assignees",
14620f38712SPatrick Williams            "Milestone",
14720f38712SPatrick Williams        ]
14820f38712SPatrick Williams    )
1490419fb0aSSivas SRR    for repository in args.repositories:
15020f38712SPatrick Williams        l_url = "https://api.github.com/repos/{}/issues?state={}"
1510419fb0aSSivas SRR        l_url = l_url.format(repository, states)
1520419fb0aSSivas SRR        response = requests.get(l_url, auth=auth)
1530419fb0aSSivas SRR        write_issues(response, csv_out)
1540419fb0aSSivas SRR        get_issues_from_github_to_csv(repository, response)
1550419fb0aSSivas SRRcsvfileout.close()
156