Hi Florian,
as promised, here's a copy of the converter script in its current state and a
short description of the whole conversion process.
Have fun with it! ;)
Dirk
On 25.09.2015 10:55, Florian Miedniak wrote:
Dirk, a copy of the script would be nice, so I could have a very first try, if
a migration would be possible in general.
-Florian
2015-09-25 10:47 GMT+02:00 Dirk Baechle >:
Florian,
thanks for the additional info about Jira license fees.
My converter script pulls the issues from Tigris to a folder in XML format.
From there, they get pushed into Roundup via its RPC
interface. So you'd have to care only about the second part only.
If you're interested, I can send you a copy of the script.
Best regards,
Dirk
""" Import tracker data from Tigris.org
This script needs the following steps to work:
1. Extract the issues as XML files via the project's xml.cgi:
import_tigris.py files
this will place all the downloaded XML files in the files dir.
An example:
import_tigris.py files scons import
2. Import the data via xmlrpc:
import_tigris.py push
Example:
import_tigris.py push http://admin:admin@localhost:8917/demo/xmlrpc import
And you're done!
"""
import sys
import os
import glob
import lxml
import lxml.etree
from urllib2 import urlopen
import base64
import xmlrpclib
import csv
# -
# natsort: Natural string sorting.
# -
# By Seo Sanghyeon. Some changes by Connelly Barnes.
def try_int(s):
"Convert to integer if possible."
try: return int(s)
except: return s
def natsort_key(s):
"Used internally to get a tuple by which s is sorted."
import re
return map(try_int, re.findall(r'(\d+|\D+)', s))
def natcmp(a, b):
"Natural string comparison, case sensitive."
return cmp(natsort_key(a), natsort_key(b))
def natcasecmp(a, b):
"Natural string comparison, ignores case."
return natcmp(a.lower(), b.lower())
def natsort(seq, cmp=natcmp):
"In-place natural string sort."
seq.sort(cmp)
def natsorted(seq, cmp=natcmp):
"Returns a copy of seq, sorted by natural string sort."
import copy
temp = copy.copy(seq)
natsort(temp, cmp)
return temp
# -
# Download issues from Tigris
# -
def issue_exists(id, url):
""" Return whether the issue page with the given
index (1-based!) exists, or not.
@param id Index (1-based) of the issue to test
@param url Base URL to the project's xml.cgi (no params attached!)
@return `True` if the issue exists, `False` if not
"""
query_url = url + '?include_attachments=false=%d' % id
try:
issues_xml = lxml.etree.XML(urlopen(query_url).read())
for issue in issues_xml.xpath('issue'):
error = issue.attrib.get('status_code', None)
if error and error == "404":
return False
else:
return True
except:
pass
return False
def binprobe(left, right, index_exists):
""" Searches the last existing entry in a
"sequence of indices" from left to right (including).
Assumes that "left" starts on an existing entry,
and left <= right, and left >= 0, and right >= 0.
The index "right" may either be the last existing entry,
or points to an entry that doesn't exist.
@param left Start index
@param right End index
@param index_exists Function that checks whether a 1-based index
is in or out of the sequence (exists or not).
@return 1-based index of the last existing entry, in
the given interval
"""
while ((right - left) > 1):
middle = left + (right - left) // 2
if not index_exists(middle):
right = middle - 1
else:
left = middle
# Special handling for when only the two
# last IDs are left...or a single one (left=right).
if index_exists(right):
return right
return left
def get_number_of_issues(url, start_id=1, BSEARCH_STEP_SIZE=1024):
""" Return the 1-based index of the highest available (=existing)
issue for the given base URL, when starting to
probe at start_id.
@param url Base URL to the project's xml.cgi (no params attached!)
@param start_id Index (1-based) from where to probe upwards
@return 1-based index of the last existing issue
"""
# Start at the given index
id = start_id
# Loop in large steps, until id doesn't exist
steps = 0
while issue_exists(id, url):
id += BSEARCH_STEP_SIZE
steps += 1
if steps:
# Start the binary search
left = id -