1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
#!/usr/bin/env python
import sys
import optparse
parser = optparse.OptionParser(usage='Usage: %prog [options] sfexport.xml githubuser/repo')
opts, args = parser.parse_args()
try:
xml_file_name, github_repo = args
github_user = github_repo.split('/')[0]
except (ValueError, IndexError):
parser.print_help()
sys.exit(1)
from BeautifulSoup import BeautifulStoneSoup
print 'Parsing XML export...'
soup = BeautifulStoneSoup(open(xml_file_name, 'r'), convertEntities=BeautifulStoneSoup.ALL_ENTITIES)
trackers = soup.document.find('trackers', recursive=False).findAll('tracker', recursive=False)
if len(trackers) > 1:
print 'Multiple trackers not yet supported, sorry'
sys.exit(1)
tracker = trackers[0]
from urllib import urlencode
from urllib2 import Request, urlopen
from base64 import b64encode
from time import sleep
from getpass import getpass
import re
github_password = getpass('%s\'s GitHub password: ' % github_user)
def rest_call(before, after, data_dict=None):
url = 'https://github.com/api/v2/xml/%s/%s/%s' % (before, github_repo, after)
data = urlencode(data_dict or {})
headers = {
'Authorization': 'Basic %s' % b64encode('%s:%s' % (github_user, github_password)),
}
request = Request(url, data, headers)
response = urlopen(request)
# GitHub limits API calls to 60 per minute
sleep(1)
return response
def labelify(string):
return re.sub(r'[^a-z0-9._-]+', '-', string.lower())
closed_status_ids = []
for status in tracker.statuses('status', recursive=False):
status_id = status.id.string
status_name = status.nameTag.string
if status_name in ['Closed', 'Deleted']:
closed_status_ids.append(status_id)
groups = {}
for group in tracker.groups('group', recursive=False):
groups[group.id.string] = group.group_name.string
categories = {}
for category in tracker.categories('category', recursive=False):
categories[category.id.string] = category.category_name.string
for item in tracker.tracker_items('tracker_item', recursive=False):
title = item.summary.string
body = '\n\n'.join([
'Converted from [SourceForge issue %s](%s), submitted by %s' % (item.id.string, item.url.string, item.submitter.string),
item.details.string,
])
closed = item.status_id.string in closed_status_ids
labels = []
try:
labels.append(labelify(groups[item.group_id.string]))
except KeyError:
pass
try:
labels.append(labelify(categories[item.category_id.string]))
except KeyError:
pass
comments = []
for followup in item.followups('followup', recursive=False):
comments.append('\n\n'.join([
'Submitted by %s' % followup.submitter.string,
followup.details.string,
]))
print 'Creating: %s [%s] (%d comments)%s' % (title, ','.join(labels), len(comments), ' (closed)' if closed else '')
response = rest_call('issues/open', '', {'title': title, 'body': body})
issue = BeautifulStoneSoup(response, convertEntities=BeautifulStoneSoup.ALL_ENTITIES)
number = issue.number.string
for label in labels:
print 'Attaching label: %s' % label
rest_call('issues/label/add', '%s/%s' % (label, number))
for comment in comments:
print 'Creating comment: %s' % comment[:50].replace('\n', ' ')
rest_call('issues/comment', number, {'comment': comment})
if closed:
print 'Closing...'
rest_call('issues/close', number)
|