Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Python Trac ticket to Github issue importer for Issues API v3
#!/usr/bin/env python
#be lazy about unicode
import sys; reload(sys); sys.setdefaultencoding('utf-8');
import sqlite3
import urllib2
import json
import base64
import re
import getpass
from datetime import datetime
# Mappings from Trac usernames to Github usernames (trac: github)
userMappings = {
# Mappings from Trac priorities to Github labels (trac: github)
priorityToLabel = {
# Mappings from Trac ticket types to Github labels (trac: github)
typeToLabel = {
# Mappings from Trac components to Github labels (trac: github)
componentToLabel = {
milestones = {}
labels = []
def initMilestones():
existingMilestones = json.load(urllib2.urlopen(baseUrl + '/milestones'))
for x in existingMilestones:
milestones[x['title']] = x['number']
def initLabels():
existingLabels = json.load(urllib2.urlopen(baseUrl + '/labels'))
for x in existingLabels:
def basic_auth(username, password):
b64_userpass = base64.b64encode('%s:%s' % (username, password))
b64_userpass = b64_userpass.replace('\n', '')
return b64_userpass
def makeRequest(url, data):
request = urllib2.Request(url, json.dumps(data))
request.add_header('Authorization', 'Basic %s' % basic_auth(username, password))
return json.load(urllib2.urlopen(request))
except urllib2.HTTPError, e:
print e
print url
def fixupTracSyntax(tracStr):
# Lone triple braces become triple backticks (code blocks)
tracStr = re.sub(r'^\{{3}$', '\n```', tracStr, flags = re.M)
tracStr = re.sub(r'^\}{3}$', '```', tracStr, flags = re.M)
# Convert Trac-style links to Markdown-style
tracStr = re.sub(r'\[(http.*?)\s+(.*?)\]', r'[\2](\1)', tracStr)
# All other triple braces become single backticks (inline code)
tracStr = tracStr.replace('{{{', '`').replace('}}}', '`')
return tracStr
def formatTime(timestamp):
return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')
def importMilestone(milestone):
print ' * Creating Milestone %s' % milestone
newMilestone = makeRequest(baseUrl + '/milestones', {'title': milestone})
milestones[milestone] = newMilestone['number']
def importLabel(label):
print ' * Creating Label %s' % label
newLabel = makeRequest(baseUrl + '/labels', {'name': label, 'color': 'FFFFFF'})
def importComment(comment, ticketNumber):
author, newvalue, time = comment
print ' - Comment by %s' % author
if newvalue:
newvalue = fixupTracSyntax(newvalue)
body = '*Comment by %s on %s:*\n\n%s' % (author, formatTime(time), fixupTracSyntax(newvalue))
makeRequest('%s/issues/%d/comments' % (baseUrl, ticketNumber), {'body':body})
def importTicket(ticket):
id, summary, status, description, owner, milestone, type, time, reporter, priority, component = ticket
if status == 'closed' or id < 24:
print 'Importing Ticket #%d' % id
githubTicket = {}
githubTicket['title'] = summary
githubTicket['body'] = '*Imported from Trac ticket #%d reported by %s on %s:*\n\n%s' % (id, reporter, formatTime(time), fixupTracSyntax(description))
if owner in userMappings:
githubTicket['assignee'] = userMappings[owner]
ticketLabels = []
if priority in priorityToLabel:
if type in typeToLabel:
if component in componentToLabel:
for x in ticketLabels:
if not x in labels:
githubTicket['labels'] = ticketLabels
if milestone:
if not milestone in milestones:
githubTicket['milestone'] = milestones[milestone]
newTicket = makeRequest(baseUrl + '/issues', githubTicket)
c = conn.cursor()
c.execute('SELECT author, newvalue, time FROM ticket_change WHERE field="comment" AND ticket=%d' % id)
for comment in c: importComment(comment, newTicket['number'])
def doImport():
c = conn.cursor()
c.execute('SELECT id, summary, status, description, owner, milestone, type, time, reporter, priority, component FROM ticket')
for ticket in c:
if __name__ == "__main__":
if len(sys.argv) < 2:
print 'Point me at a trac db!';
conn = sqlite3.connect(sys.argv[1])
domain = ''
username = raw_input('GitHub username: ')
password = getpass.getpass('GitHub password: ')
repo = raw_input('Repository name (with user/org): ')
baseUrl = '%s/repos/%s' % (domain, repo)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment