#!/usr/bin/env python r""" Exports issues from a list of repositories to individual CSV files. Uses basic authentication (GitHub username + password) to retrieve issues from a repository that username has access to. Supports GitHub API v3. """ import argparse import csv import getpass import requests auth = None states = 'all' def write_issues(response, csv_out): r""" Parses JSON response and writes to CSV. """ print(response) if response.status_code != 200: raise Exception(response.status_code) for issue in response.json(): if 'pull_request' not in issue: labels = ', '.join([l['name'] for l in issue['labels']]) # Below lines to overcome "TypeError: 'NoneType' object has # no attribute '__getitem__'" close_date = issue.get('closed_at') if close_date: close_date = issue.get('closed_at').split('T')[0] assignee_resp = issue.get('assignees', 'Not Assigned') if assignee_resp: owners = ','.join([assignee_login['login'] for assignee_login in assignee_resp]) else: owners = "Not Assigned" milestone_resp = issue.get('milestone', 'Not Assigned') if milestone_resp: milestone_resp = milestone_resp['title'].encode('utf-8') # Change the following line to write out additional fields csv_out.writerow([labels.encode('utf-8'), issue.get('title').encode('utf-8'), issue.get('state').encode('utf-8'), issue.get('created_at').split('T')[0], close_date, issue.get('html_url').encode('utf-8'), issue.get('user').get('login').encode('utf-8'), owners, milestone_resp]) def get_issues_from_github_to_csv(name, response): r""" Requests issues from GitHub API and writes to CSV file. Description of argument(s): name Name of the GitHub repository response GitHub repository response """ print(name) print(states) # Multiple requests are required if response is paged if 'link' in response.headers: pages = {rel[6:-1]: url[url.index('<') + 1:-1] for url, rel in (link.split(';') for link in response.headers['link'].split(','))} while 'last' in pages and 'next' in pages: pages = {rel[6:-1]: url[url.index('<') + 1:-1] for url, rel in (link.split(';') for link in response.headers['link'].split(','))} response = requests.get(pages['next'], auth=auth) write_issues(response, csv_out) if pages['next'] == pages['last']: break parser = argparse.ArgumentParser(description="Write GitHub repository issues " "to CSV file.") parser.add_argument('username', nargs='?', help="GitHub user name, " "formatted as 'username'") parser.add_argument('repositories', nargs='+', help="Repository names, " "formatted as 'basereponame/repo'") parser.add_argument('--all', action='store_true', help="Returns both open " "and closed issues.") args = parser.parse_args() if args.all: state = 'all' username = args.username password = getpass.getpass("Enter your GitHub Password:") auth = (username, password) # To set the csv filename csvfilename = "" for repository in args.repositories: csvfilename_temp = '{}'.format(repository.replace('/', '-')) csvfilename = csvfilename + csvfilename_temp csvfilename = csvfilename + '-issues.csv' with open(csvfilename, 'w') as csvfileout: csv_out = csv.writer(csvfileout) csv_out.writerow(['Labels', 'Title', 'State', 'Open Date', 'Close Date', 'URL', 'Author', 'Assignees', 'Milestone']) for repository in args.repositories: l_url = 'https://api.github.com/repos/{}/issues?state={}' l_url = l_url.format(repository, states) response = requests.get(l_url, auth=auth) write_issues(response, csv_out) get_issues_from_github_to_csv(repository, response) csvfileout.close()