1#!/usr/bin/env python3
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, 'w') 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