1a464a7ceSSivas SRR#!/usr/bin/env python 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 11a464a7ceSSivas SRRimport requests 12a464a7ceSSivas SRR 13a464a7ceSSivas SRRauth = None 14a464a7ceSSivas SRRstates = 'all' 15a464a7ceSSivas SRR 16a464a7ceSSivas SRR 17a464a7ceSSivas SRRdef write_issues(response, csv_out): 18a464a7ceSSivas SRR r""" 19a464a7ceSSivas SRR Parses JSON response and writes to CSV. 20a464a7ceSSivas SRR """ 21a464a7ceSSivas SRR print response 22a464a7ceSSivas SRR if response.status_code != 200: 23a464a7ceSSivas SRR raise Exception(response.status_code) 24a464a7ceSSivas SRR for issue in response.json(): 25a464a7ceSSivas SRR if 'pull_request' not in issue: 26a464a7ceSSivas SRR labels = ', '.join([l['name'] for l in issue['labels']]) 27b4eb9ac1SSivas SRR 289fe1b12eSSivas SRR # Below lines to overcome "TypeError: 'NoneType' object has 299fe1b12eSSivas SRR # no attribute '__getitem__'" 309fe1b12eSSivas SRR 31b4eb9ac1SSivas SRR close_date = issue.get('closed_at') 32b4eb9ac1SSivas SRR if close_date: 33b4eb9ac1SSivas SRR close_date = issue.get('closed_at').split('T')[0] 34b4eb9ac1SSivas SRR 35b4eb9ac1SSivas SRR assignee_resp = issue.get('assignees', 'Not Assigned') 369fe1b12eSSivas SRR if assignee_resp: 37b4eb9ac1SSivas SRR owners = ','.join([assignee_login['login'] for 38b4eb9ac1SSivas SRR assignee_login in assignee_resp]) 399fe1b12eSSivas SRR else: 40b4eb9ac1SSivas SRR owners = "Not Assigned" 41b4eb9ac1SSivas SRR 42b4eb9ac1SSivas SRR milestone_resp = issue.get('milestone', 'Not Assigned') 43b4eb9ac1SSivas SRR if milestone_resp: 44b4eb9ac1SSivas SRR milestone_resp = milestone_resp['title'].encode('utf-8') 459fe1b12eSSivas SRR 46a464a7ceSSivas SRR # Change the following line to write out additional fields 47a464a7ceSSivas SRR csv_out.writerow([labels.encode('utf-8'), 489fe1b12eSSivas SRR issue.get('title').encode('utf-8'), 499fe1b12eSSivas SRR issue.get('state').encode('utf-8'), 50b4eb9ac1SSivas SRR issue.get('created_at').split('T')[0], 51b4eb9ac1SSivas SRR close_date, 529fe1b12eSSivas SRR issue.get('html_url').encode('utf-8'), 539fe1b12eSSivas SRR issue.get('user').get('login').encode('utf-8'), 54b4eb9ac1SSivas SRR owners, milestone_resp]) 55a464a7ceSSivas SRR 56a464a7ceSSivas SRR 57*0419fb0aSSivas SRRdef get_issues_from_github_to_csv(name, response): 58a464a7ceSSivas SRR r""" 59a464a7ceSSivas SRR Requests issues from GitHub API and writes to CSV file. 60*0419fb0aSSivas SRR Description of argument(s): 61*0419fb0aSSivas SRR name Name of the GitHub repository 62*0419fb0aSSivas SRR response GitHub repository response 63a464a7ceSSivas SRR """ 64a464a7ceSSivas SRR print name 65a464a7ceSSivas SRR print states 66a464a7ceSSivas SRR 67a464a7ceSSivas SRR # Multiple requests are required if response is paged 68a464a7ceSSivas SRR if 'link' in response.headers: 69a464a7ceSSivas SRR pages = {rel[6:-1]: url[url.index('<')+1:-1] for url, rel in 70a464a7ceSSivas SRR (link.split(';') for link in 71a464a7ceSSivas SRR response.headers['link'].split(','))} 72a464a7ceSSivas SRR while 'last' in pages and 'next' in pages: 73a464a7ceSSivas SRR pages = {rel[6:-1]: url[url.index('<')+1:-1] for url, rel in 74a464a7ceSSivas SRR (link.split(';') for link in 75a464a7ceSSivas SRR response.headers['link'].split(','))} 76a464a7ceSSivas SRR response = requests.get(pages['next'], auth=auth) 77a464a7ceSSivas SRR write_issues(response, csv_out) 78a464a7ceSSivas SRR if pages['next'] == pages['last']: 79a464a7ceSSivas SRR break 80a464a7ceSSivas SRR 81a464a7ceSSivas SRR 82a464a7ceSSivas SRRparser = argparse.ArgumentParser(description="Write GitHub repository issues " 83a464a7ceSSivas SRR "to CSV file.") 84a464a7ceSSivas SRR 85*0419fb0aSSivas SRRparser.add_argument('username', nargs='?', help="GitHub user name, " 86a464a7ceSSivas SRR "formatted as 'username'") 87a464a7ceSSivas SRR 88a464a7ceSSivas SRRparser.add_argument('repositories', nargs='+', help="Repository names, " 89a464a7ceSSivas SRR "formatted as 'basereponame/repo'") 90a464a7ceSSivas SRR 91a464a7ceSSivas SRRparser.add_argument('--all', action='store_true', help="Returns both open " 92a464a7ceSSivas SRR "and closed issues.") 93*0419fb0aSSivas SRR 94a464a7ceSSivas SRRargs = parser.parse_args() 95a464a7ceSSivas SRR 96a464a7ceSSivas SRRif args.all: 97a464a7ceSSivas SRR state = 'all' 98a464a7ceSSivas SRR 99*0419fb0aSSivas SRRusername = args.username 100a464a7ceSSivas SRR 1019fe1b12eSSivas SRRpassword = getpass.getpass("Enter your GitHub Password:") 102a464a7ceSSivas SRR 103a464a7ceSSivas SRRauth = (username, password) 104a464a7ceSSivas SRR 105*0419fb0aSSivas SRR# To set the csv filename 106*0419fb0aSSivas SRRcsvfilename = "" 107a464a7ceSSivas SRRfor repository in args.repositories: 108*0419fb0aSSivas SRR csvfilename_temp = '{}'.format(repository.replace('/', '-')) 109*0419fb0aSSivas SRR csvfilename = csvfilename+csvfilename_temp 110*0419fb0aSSivas SRRcsvfilename = csvfilename+'-issues.csv' 111*0419fb0aSSivas SRRwith open(csvfilename, 'wb') as csvfileout: 112*0419fb0aSSivas SRR csv_out = csv.writer(csvfileout) 113*0419fb0aSSivas SRR csv_out.writerow(['Labels', 'Title', 'State', 'Open Date', 114*0419fb0aSSivas SRR 'Close Date', 'URL', 'Author', 'Assignees', 115*0419fb0aSSivas SRR 'Milestone']) 116*0419fb0aSSivas SRR for repository in args.repositories: 117*0419fb0aSSivas SRR l_url = 'https://api.github.com/repos/{}/issues?state={}' 118*0419fb0aSSivas SRR l_url = l_url.format(repository, states) 119*0419fb0aSSivas SRR response = requests.get(l_url, auth=auth) 120*0419fb0aSSivas SRR write_issues(response, csv_out) 121*0419fb0aSSivas SRR get_issues_from_github_to_csv(repository, response) 122*0419fb0aSSivas SRRcsvfileout.close() 123