blob: c287f7c5999c917c26478880869f6aa77739e935 [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
Sivas SRRa464a7c2017-02-19 23:35:59 -060011import requests
12
13auth = None
14states = 'all'
15
16
17def write_issues(response, csv_out):
18 r"""
19 Parses JSON response and writes to CSV.
20 """
vinaybs665dbeaa2019-09-19 01:46:49 -050021 print(response)
Sivas SRRa464a7c2017-02-19 23:35:59 -060022 if response.status_code != 200:
23 raise Exception(response.status_code)
24 for issue in response.json():
25 if 'pull_request' not in issue:
Patrick Williamsa57fef42022-12-03 07:00:14 -060026 labels = ', '.join([lable['name'] for lable in issue['labels']])
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050027
Sivas SRR9fe1b122017-03-05 22:29:28 -060028 # Below lines to overcome "TypeError: 'NoneType' object has
29 # no attribute '__getitem__'"
30
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050031 close_date = issue.get('closed_at')
32 if close_date:
33 close_date = issue.get('closed_at').split('T')[0]
34
35 assignee_resp = issue.get('assignees', 'Not Assigned')
Sivas SRR9fe1b122017-03-05 22:29:28 -060036 if assignee_resp:
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050037 owners = ','.join([assignee_login['login'] for
38 assignee_login in assignee_resp])
Sivas SRR9fe1b122017-03-05 22:29:28 -060039 else:
Sivas SRRb4eb9ac2017-03-16 01:13:03 -050040 owners = "Not Assigned"
41
42 milestone_resp = issue.get('milestone', 'Not Assigned')
43 if milestone_resp:
44 milestone_resp = milestone_resp['title'].encode('utf-8')
Sivas SRR9fe1b122017-03-05 22:29:28 -060045
Sivas SRRa464a7c2017-02-19 23:35:59 -060046 # Change the following line to write out additional fields
47 csv_out.writerow([labels.encode('utf-8'),
Gunnar Mills096cd562018-03-26 10:19:12 -050048 issue.get('title').encode('utf-8'),
49 issue.get('state').encode('utf-8'),
50 issue.get('created_at').split('T')[0],
51 close_date,
52 issue.get('html_url').encode('utf-8'),
53 issue.get('user').get('login').encode('utf-8'),
54 owners, milestone_resp])
Sivas SRRa464a7c2017-02-19 23:35:59 -060055
56
Sivas SRR0419fb02017-05-28 10:09:18 -050057def get_issues_from_github_to_csv(name, response):
Sivas SRRa464a7c2017-02-19 23:35:59 -060058 r"""
59 Requests issues from GitHub API and writes to CSV file.
Sivas SRR0419fb02017-05-28 10:09:18 -050060 Description of argument(s):
61 name Name of the GitHub repository
62 response GitHub repository response
Sivas SRRa464a7c2017-02-19 23:35:59 -060063 """
vinaybs665dbeaa2019-09-19 01:46:49 -050064 print(name)
65 print(states)
Sivas SRRa464a7c2017-02-19 23:35:59 -060066
Sivas SRR0419fb02017-05-28 10:09:18 -050067 # Multiple requests are required if response is paged
68 if 'link' in response.headers:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050069 pages = {rel[6:-1]: url[url.index('<') + 1:-1] for url, rel in
Sivas SRR0419fb02017-05-28 10:09:18 -050070 (link.split(';') for link in
Gunnar Mills096cd562018-03-26 10:19:12 -050071 response.headers['link'].split(','))}
Sivas SRR0419fb02017-05-28 10:09:18 -050072 while 'last' in pages and 'next' in pages:
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -050073 pages = {rel[6:-1]: url[url.index('<') + 1:-1] for url, rel in
Sivas SRRa464a7c2017-02-19 23:35:59 -060074 (link.split(';') for link in
Gunnar Mills096cd562018-03-26 10:19:12 -050075 response.headers['link'].split(','))}
Sivas SRR0419fb02017-05-28 10:09:18 -050076 response = requests.get(pages['next'], auth=auth)
77 write_issues(response, csv_out)
78 if pages['next'] == pages['last']:
79 break
Sivas SRRa464a7c2017-02-19 23:35:59 -060080
Sivas SRRa464a7c2017-02-19 23:35:59 -060081
82parser = argparse.ArgumentParser(description="Write GitHub repository issues "
83 "to CSV file.")
84
Sivas SRR0419fb02017-05-28 10:09:18 -050085parser.add_argument('username', nargs='?', help="GitHub user name, "
Sivas SRRa464a7c2017-02-19 23:35:59 -060086 "formatted as 'username'")
87
Sivas SRRa464a7c2017-02-19 23:35:59 -060088parser.add_argument('repositories', nargs='+', help="Repository names, "
89 "formatted as 'basereponame/repo'")
90
91parser.add_argument('--all', action='store_true', help="Returns both open "
92 "and closed issues.")
Sivas SRR0419fb02017-05-28 10:09:18 -050093
Sivas SRRa464a7c2017-02-19 23:35:59 -060094args = parser.parse_args()
95
96if args.all:
97 state = 'all'
98
Sivas SRR0419fb02017-05-28 10:09:18 -050099username = args.username
Sivas SRRa464a7c2017-02-19 23:35:59 -0600100
Sivas SRR9fe1b122017-03-05 22:29:28 -0600101password = getpass.getpass("Enter your GitHub Password:")
Sivas SRRa464a7c2017-02-19 23:35:59 -0600102
103auth = (username, password)
104
Sivas SRR0419fb02017-05-28 10:09:18 -0500105# To set the csv filename
106csvfilename = ""
Sivas SRRa464a7c2017-02-19 23:35:59 -0600107for repository in args.repositories:
Sivas SRR0419fb02017-05-28 10:09:18 -0500108 csvfilename_temp = '{}'.format(repository.replace('/', '-'))
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500109 csvfilename = csvfilename + csvfilename_temp
110csvfilename = csvfilename + '-issues.csv'
vinaybs665dbeaa2019-09-19 01:46:49 -0500111with open(csvfilename, 'w') as csvfileout:
Sivas SRR0419fb02017-05-28 10:09:18 -0500112 csv_out = csv.writer(csvfileout)
113 csv_out.writerow(['Labels', 'Title', 'State', 'Open Date',
114 'Close Date', 'URL', 'Author', 'Assignees',
115 'Milestone'])
116 for repository in args.repositories:
117 l_url = 'https://api.github.com/repos/{}/issues?state={}'
118 l_url = l_url.format(repository, states)
119 response = requests.get(l_url, auth=auth)
120 write_issues(response, csv_out)
121 get_issues_from_github_to_csv(repository, response)
122csvfileout.close()