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, response): 58 r""" 59 Requests issues from GitHub API and writes to CSV file. 60 Description of argument(s): 61 name Name of the GitHub repository 62 response GitHub repository response 63 """ 64 print name 65 print states 66 67 # Multiple requests are required if response is paged 68 if 'link' in response.headers: 69 pages = {rel[6:-1]: url[url.index('<') + 1:-1] for url, rel in 70 (link.split(';') for link in 71 response.headers['link'].split(','))} 72 while 'last' in pages and 'next' in pages: 73 pages = {rel[6:-1]: url[url.index('<') + 1:-1] for url, rel in 74 (link.split(';') for link in 75 response.headers['link'].split(','))} 76 response = requests.get(pages['next'], auth=auth) 77 write_issues(response, csv_out) 78 if pages['next'] == pages['last']: 79 break 80 81 82parser = argparse.ArgumentParser(description="Write GitHub repository issues " 83 "to CSV file.") 84 85parser.add_argument('username', nargs='?', help="GitHub user name, " 86 "formatted as 'username'") 87 88parser.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.") 93 94args = parser.parse_args() 95 96if args.all: 97 state = 'all' 98 99username = args.username 100 101password = getpass.getpass("Enter your GitHub Password:") 102 103auth = (username, password) 104 105# To set the csv filename 106csvfilename = "" 107for repository in args.repositories: 108 csvfilename_temp = '{}'.format(repository.replace('/', '-')) 109 csvfilename = csvfilename + csvfilename_temp 110csvfilename = csvfilename + '-issues.csv' 111with open(csvfilename, 'wb') as csvfileout: 112 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() 123