Skip to content

Instantly share code, notes, and snippets.

@zerocrates
Created August 12, 2011 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zerocrates/1142835 to your computer and use it in GitHub Desktop.
Save zerocrates/1142835 to your computer and use it in GitHub Desktop.
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:
labels.append(x['name'])
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))
try:
return json.load(urllib2.urlopen(request))
except urllib2.HTTPError, e:
print e
print e.read()
print url
sys.exit(1)
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'})
labels.append(label)
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:
return
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:
ticketLabels.append(priorityToLabel[priority])
if type in typeToLabel:
ticketLabels.append(typeToLabel[type])
if component in componentToLabel:
ticketLabels.append(componentToLabel[component])
for x in ticketLabels:
if not x in labels:
importLabel(x)
githubTicket['labels'] = ticketLabels
if milestone:
if not milestone in milestones:
importMilestone(milestone)
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():
initMilestones()
initLabels()
c = conn.cursor()
c.execute('SELECT id, summary, status, description, owner, milestone, type, time, reporter, priority, component FROM ticket')
for ticket in c:
importTicket(ticket)
if __name__ == "__main__":
if len(sys.argv) < 2:
print 'Point me at a trac db!';
sys.exit(1);
conn = sqlite3.connect(sys.argv[1])
domain = 'https://api.github.com'
username = raw_input('GitHub username: ')
password = getpass.getpass('GitHub password: ')
repo = raw_input('Repository name (with user/org): ')
baseUrl = '%s/repos/%s' % (domain, repo)
doImport()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment