diff options
Diffstat (limited to 'issues.py')
-rwxr-xr-x | issues.py | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/issues.py b/issues.py new file mode 100755 index 0000000..70b88c1 --- /dev/null +++ b/issues.py @@ -0,0 +1,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) + |