Created
August 12, 2011 19:49
-
-
Save zerocrates/1142835 to your computer and use it in GitHub Desktop.
Python Trac ticket to Github issue importer for Issues API v3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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