summaryrefslogtreecommitdiff
path: root/issues.py
blob: 4b275f5076e525a25b3fff838d0f1609a2f8913a (plain)
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
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env python

import sys
import optparse

parser = optparse.OptionParser(usage='Usage: %prog [options] sfexport.xml githubuser/repo')
parser.add_option('-s', '--start', dest='start_id', action='store', help='id of first issue to import; useful for aborted runs')
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)
    if data_dict is None:
        data = None
    else:
        data = urlencode([(unicode(key).encode('utf-8'), unicode(value).encode('utf-8')) for key, value in data_dict.iteritems()])
    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

started = opts.start_id is None
for item in tracker.tracker_items('tracker_item', recursive=False):
    if not started:
        if item.id.string == opts.start_id:
            started = True
        else:
            continue
    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)