1#!/usr/bin/env python 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""" 8import argparse 9import csv 10import getpass 11import 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 """ 21 print response 22 if response.status_code != 200: 23 raise Exception(response.status_code) 24 for issue in response.json(): 25 if 'pull_request' not in issue: 26 labels = ', '.join([l['name'] for l in issue['labels']]) 27 28 # Below lines to overcome "TypeError: 'NoneType' object has 29 # no attribute '__getitem__'" 30 31 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') 36 if assignee_resp: 37 owners = ','.join([assignee_login['login'] for 38 assignee_login in assignee_resp]) 39 else: 40 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') 45 46 # Change the following line to write out additional fields 47 csv_out.writerow([labels.encode('utf-8'), 48 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]) 55 56 57def get_issues_from_github_to_csv(name): 58 r""" 59 Requests issues from GitHub API and writes to CSV file. 60 """ 61 print name 62 print states 63 l_url = 'https://api.github.com/repos/{}/issues?state={}'.format(name, 64 states) 65 print l_url 66 # 'https://api.github.com/repos/{}/issues?state={}'.format(name, state) 67 response = requests.get(l_url, auth=auth) 68 69 csvfilename = '{}-issues.csv'.format(name.replace('/', '-')) 70 with open(csvfilename, 'w') as csvfile: 71 csv_out = csv.writer(csvfile) 72 csv_out.writerow(['Labels', 'Title', 'State', 'Open Date', 73 'Close Date', 'URL', 'Author', 'Assignees', 74 'Milestone']) 75 write_issues(response, csv_out) 76 77 # Multiple requests are required if response is paged 78 if 'link' in response.headers: 79 pages = {rel[6:-1]: url[url.index('<')+1:-1] for url, rel in 80 (link.split(';') for link in 81 response.headers['link'].split(','))} 82 while 'last' in pages and 'next' in pages: 83 pages = {rel[6:-1]: url[url.index('<')+1:-1] for url, rel in 84 (link.split(';') for link in 85 response.headers['link'].split(','))} 86 response = requests.get(pages['next'], auth=auth) 87 write_issues(response, csv_out) 88 if pages['next'] == pages['last']: 89 break 90 91 csvfile.close() 92 93parser = argparse.ArgumentParser(description="Write GitHub repository issues " 94 "to CSV file.") 95 96parser.add_argument('username', nargs='+', help="GitHub user name, " 97 "formatted as 'username'") 98 99parser.add_argument('repositories', nargs='+', help="Repository names, " 100 "formatted as 'basereponame/repo'") 101 102parser.add_argument('--all', action='store_true', help="Returns both open " 103 "and closed issues.") 104args = parser.parse_args() 105 106if args.all: 107 state = 'all' 108 109for argusername in args.username: 110 username = argusername 111 112password = getpass.getpass("Enter your GitHub Password:") 113 114auth = (username, password) 115 116for repository in args.repositories: 117 get_issues_from_github_to_csv(repository) 118