Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save Jammizzle/ad2a94008b56a6f9d17cfdddf5a6dd4d to your computer and use it in GitHub Desktop.
Save Jammizzle/ad2a94008b56a6f9d17cfdddf5a6dd4d to your computer and use it in GitHub Desktop.
Export Issues from Github repo to CSV (API v3 and ZenHub API)
#!/usr/bin/python
import csv
import json
import requests
import configparser
"""
Usage:
Add the following to config.ini with appropriate values:
[DEFAULT]
AUTH_TOKEN =
ZEN_ACCESS =
Exports Issues from a list of repositories to individual CSV files
Uses basic authentication (Github API Token and Zenhub API Token)
to retrieve Issues from a repository that token has access to.
Supports Github API v3 and ZenHubs current working API.
Derived from https://gist.github.com/Kebiled/7b035d7518fdfd50d07e2a285aff3977
@PinnaclePSM Author Jamie Belcher
"""
def write_issues(r, csvout, repo_name, repo_ID):
if not r.status_code == 200:
raise Exception("Request returned status of:"+str(r.status_code))
r_json = r.json()
for issue in r_json:
print(repo_name + ' issue Number: ' + str(issue['number']))
zenhub_issue_url = 'https://api.zenhub.io/p1/repositories/' + str(repo_ID) + '/issues/' + str(issue['number']) + '?access_token=' + ACCESS_TOKEN
zen_r = requests.get(zenhub_issue_url).json()
DateCreated = issue['created_at'][:-10]
if 'pull_request' not in issue:
global ISSUES
ISSUES += 1
assignees, tag, category, priority = '', '', '', ''
for i in issue['assignees'] if issue['assignees'] else []:
assignees += i['login'] + ','
for x in issue['labels'] if issue['labels'] else []:
if "Category" in x['name']:
category = x['name'][11:11 + len(x['name'])]
if "Tag" in x['name']:
tag = x['name'][6:6 + len(x['name'])]
if "Priority" in x['name']:
priority = x['name'][11:11 + len(x['name'])]
estimate = zen_r.get('estimate', dict()).get('value', "")
if category != 'BUG':
Pipeline = zen_r.get('pipeline', dict()).get('name', "")
csvout.writerow([repo_name, issue['number'], issue['title'].encode('utf-8'), category,
tag, assignees[:-1],
priority, Pipeline, DateCreated,
estimate])
def get_issues(repo_data):
repo_name = repo_data[0]
repo_ID = repo_data[1]
issues_for_repo_url = 'https://api.github.com/repos/%s/issues?since=2017-05-01&state=closed' % repo_name
r = requests.get(issues_for_repo_url, auth=AUTH)
write_issues(r, FILEOUTPUT, repo_name, repo_ID)
# more pages? examine the 'link' header returned
if 'link' in r.headers:
pages = dict(
[(rel[6:-1], url[url.index('<') + 1:-1]) for url, rel in
[link.split(';') for link in
r.headers['link'].split(',')]])
while 'last' in pages and 'next' in pages:
pages = dict(
[(rel[6:-1], url[url.index('<') + 1:-1]) for url, rel in
[link.split(';') for link in
r.headers['link'].split(',')]])
r = requests.get(pages['next'], auth=AUTH)
write_issues(r, FILEOUTPUT, repo_name, repo_ID)
if pages['next'] == pages['last']:
break
REPO_LIST = [("*GITHUB REPO*", "*ZENHUB REPOID*")]
config = configparser.ConfigParser()
config.read('config.ini')
AUTH = ('token', config['DEFAULT']['AUTH_TOKEN'])
ACCESS_TOKEN = config['DEFAULT']['ZEN_ACCESS']
ISSUES = 0
FILENAME = 'output.csv'
OPENFILE = open(FILENAME, 'wb')
FILEOUTPUT = csv.writer(OPENFILE)
FILEOUTPUT.writerow(('Repository', 'Issue Number', 'Issue Title', 'Category',
'Tag', 'Assigned To',
'Priority', 'Pipeline',
'Issue Author',
'Created At', 'Estimate Value'
))
for repo_data in REPO_LIST:
get_issues(repo_data)
OPENFILE.close()
[DEFAULT]
AUTH_TOKEN =
ZEN_ACCESS =
@Jammizzle
Copy link
Author

Jammizzle commented May 5, 2017

Future enhancements:

  • Be able to filter out labels into seperate columns (prefix the label to determine it's column)
  • Create a column to store Epics and Filter issues by Epic (ZenhubAPI be with me...)

@sparda2
Copy link

sparda2 commented Jun 7, 2017

Sorry for the dumb question, but how can i use this?

@Jammizzle
Copy link
Author

Jammizzle commented Jun 20, 2017

@sparda2 - You'll need to change:
GITHUB_ACCESS TOKEN to your personal token
ZENHUB_ACCESS_TOKEN to the personal token provided by Zenhub
Filename - Desired filename with extension of .csv
GITHUB_REPO to the repo name (User/RepoName)
ZENHUB_REPO_ID - If you wish to use Zenhub you'll need to set this to the unique ID of the repo

Then from the terminal just run python script.py

@anu87
Copy link

anu87 commented Feb 28, 2018

Hi! this only downloads open issues, how can I download all issues?

@jonmccon
Copy link

jonmccon commented Jul 6, 2018

Has anyone tried pulling the threaded comments too?

@thewebprincess
Copy link

thewebprincess commented Jul 18, 2018

@Jammizzle Any chance you've got time to Add in the ability to export the Epics and Labels, that would make my life complete...

@manu4387
Copy link

Getting this issue any one else faced the same issue

Traceback (most recent call last):
File "C:/Users/Manu/Desktop/export repository1.py", line 95, in
get_issues(repo_data)
File "C:/Users/Manu/Desktop/export repository1.py", line 60, in get_issues
write_issues(r, FILEOUTPUT, repo_name, repo_ID)
File "C:/Users/Manu/Desktop/export repository1.py", line 18, in write_issues
raise Exception(r.status_code)
Exception: 404

@Jammizzle
Copy link
Author

@manu4387 The request perhaps couldn't find your repo, which could be either due to permissions or a potential typo

@manu4387
Copy link

i have 2 factor authentication enabled , i tried to use the Personal authentication token but same issue.
i tried another code for connectivity to get the data which is working fine to test the connectivity with authentication token

@Jammizzle
Copy link
Author

Ahh I haven't tested this with 2FA but I don't know why that'd cause an issue, unless it's Zenhubs permissions? I also haven't used this in quite some time so I'll have a look into setting it up again for myself then I'll get back to you

@manu4387
Copy link

manu4387 commented Oct 25, 2019 via email

@Jammizzle
Copy link
Author

@manu4387 Should be able to just delete all zenhub requests and everything's fine, remember to remove it from the CSV builder as well

@manu4387
Copy link

manu4387 commented Oct 25, 2019 via email

@Jammizzle
Copy link
Author

Can you send me an email at jamie.belcher@phpartnership.com of your REPO_LIST variable?

@manu4387
Copy link

manu4387 commented Oct 25, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment