Add actuall migration code

Signed-off-by: Michal Čihař <michal@cihar.com>
This commit is contained in:
Michal Čihař 2019-05-27 11:56:19 +02:00
parent 43d48c71e2
commit 88bdfc53f8
2 changed files with 161 additions and 40 deletions

View file

@ -1,2 +1,3 @@
rt
zammad_py
python-dateutil

View file

@ -3,15 +3,27 @@
Quick and dirty attempt to migrate issues from Request Tracker to Zammad.
"""
import pickle
import base64
import json
import os
import pickle
import sys
from requests.exceptions import HTTPError
from rt import Rt
from zammad_py import ZammadAPI
from zammad_py.api import TagList
from rt import Rt, ALL_QUEUES
from zammad_py.api import Resource, TagList, TicketArticle
class Tag(Resource):
path_attribute = "tags"
def add(self, obj, id, item):
response = self._connection.session.get(
self.url + "/add?object=%s&o_id=%d&item=%s" % (obj, id, item)
)
return self._raise_or_return_json(response)
TEMPLATE = """{
"zammad_host": "",
@ -24,39 +36,55 @@ TEMPLATE = """{
}
"""
if not os.path.exists('rt2zammad.json'):
print('Missing rt2zammad.json!')
print('Create one based on following template:')
COMMENT_TEMPLATE = """
Ticket imported from Request Tracker
Created: {Created}
Resolved: {Resolved}
"""
if not os.path.exists("rt2zammad.json"):
print("Missing rt2zammad.json!")
print("Create one based on following template:")
print(TEMPLATE)
sys.exit(1)
with open('rt2zammad.json') as handle:
with open("rt2zammad.json") as handle:
config = json.load(handle)
target = ZammadAPI(
host=config['zammad_host'],
username=config['zammad_user'],
password=config['zammad_password'],
is_secure=config['zammad_secure'],
)
def get_zammad(**kwargs):
return ZammadAPI(
host=config["zammad_host"],
username=config["zammad_user"],
password=config["zammad_password"],
is_secure=config["zammad_secure"],
**kwargs
)
target = get_zammad()
target.user.me()
source = Rt(config['rt_url'], config['rt_user'], config['rt_pass'])
source = Rt(config["rt_url"], config["rt_user"], config["rt_pass"])
if not source.login():
print('Failed to login to RT!')
print("Failed to login to RT!")
sys.exit(2)
if os.path.exists('rt2zammad.cache'):
if os.path.exists("rt2zammad.cache"):
# Load RT from cache
with open('rt2zammad.cache', 'rb') as handle:
with open("rt2zammad.cache", "rb") as handle:
data = pickle.load(handle)
users = data['users']
queues = data['queues']
tickets = data['tickets']
users = data["users"]
queues = data["queues"]
tickets = data["tickets"]
attachments = data["attachments"]
else:
# Load RT from remote
users = {}
attachments = {}
tickets = []
queues = set()
@ -64,33 +92,125 @@ else:
if username not in users:
users[username] = source.get_user(username)
for i in range(1, 1000):
print('Loading ticket {}'.format(i))
print("Loading ticket {}".format(i))
ticket = source.get_ticket(i)
if ticket is None:
break
queues.add(ticket['Queue'])
ensure_user(ticket['Creator'])
ensure_user(ticket['Owner'])
queues.add(ticket["Queue"])
ensure_user(ticket["Creator"])
ensure_user(ticket["Owner"])
history = source.get_history(i)
attachments = []
for a in source.get_attachments_ids(i):
attachment = source.get_attachment(i, a)
attachments.append(attachments)
ensure_user(attachment['Creator'])
tickets.append({
'ticket': ticket,
'history': history,
'attachments': attachments,
})
with open('rt2zammad.cache', 'wb') as handle:
data = pickle.dump({'users': users, 'queues': queues, 'tickets': tickets}, handle)
for item in history:
for a, title in item["Attachments"]:
attachments[a] = source.get_attachment(i, a)
ensure_user(item["Creator"])
tickets.append({"ticket": ticket, "history": history})
with open("rt2zammad.cache", "wb") as handle:
data = pickle.dump(
{
"users": users,
"queues": queues,
"tickets": tickets,
"attachments": attachments,
},
handle,
)
# Create tags
tag_list = TagList(target)
tags = {tag['name'] for tag in tag_list.all()}
ticket_article = TicketArticle(target)
tag_obj = Tag(target)
tags = {tag["name"] for tag in tag_list.all()}
for queue in queues:
queue = queue.lower().split()[0]
if queue not in tags:
tag_list.create({'name': queue})
tag_list.create({"name": queue})
STATUSMAP = {"new": 1, "open": 2, "resolved": 4, "rejected": 4, "deleted": 4}
USERMAP = {}
for user in target.user.all():
USERMAP[user["email"].lower()] = user["login"]
def get_user(userdata):
email = userdata["EmailAddress"]
# Search existing users
if email not in USERMAP:
for user in target.user.search({"query": email}):
USERMAP[user["email"].lower()] = user["login"]
# Create new one
if email not in USERMAP:
kwargs = {"email": email}
if "RealName" in userdata:
realname = userdata["RealName"]
if ", " in realname:
last, first = realname.split(", ", 1)
elif " " in realname:
first, last = realname.split(None, 1)
else:
last = realname
first = ""
kwargs["lastname"] = last
kwargs["firstname"] = first
user = target.user.create(kwargs)
USERMAP[user["email"].lower()] = user["login"]
return USERMAP[email.lower()]
# Create tickets
for ticket in tickets:
label = "RT-{}".format(ticket["ticket"]["id"].split("/")[1])
print("Importing {}".format(label))
new = get_zammad(
on_behalf_of=get_user(users[ticket["ticket"]["Creator"]])
).ticket.create(
{
"title": "{} [{}]".format(ticket["ticket"]["Subject"], label),
"group": "Users",
"state_id": STATUSMAP[ticket["ticket"]["Status"]],
"customer_id": "guess:{}".format(
users[ticket["ticket"]["Creator"]]["EmailAddress"]
),
"note": "RT-import:{}".format(ticket["ticket"]["id"]),
"article": {
"subject": ticket["ticket"]["Subject"],
"body": ticket["history"][0]["Content"],
},
}
)
tag_obj.add("Ticket", new["id"], ticket["ticket"]["Queue"].lower().split()[0])
ticket_article.create(
{
"ticket_id": new["id"],
"body": COMMENT_TEMPLATE.format(**ticket["ticket"]),
"internal": True,
}
)
for item in ticket["history"]:
if item["Type"] not in ("Correspond", "Comment"):
continue
files = []
for a, title in item["Attachments"]:
data = attachments[a]
if data["Filename"] in ("", "signature.asc"):
continue
files.append(
{
"filename": data["Filename"],
"data": base64.b64encode(data["Content"]).decode("utf-8"),
"mime-type": data["ContentType"],
}
)
TicketArticle(get_zammad(on_behalf_of=get_user(users[item["Creator"]]))).create(
{
"ticket_id": new["id"],
"body": item["Content"],
"internal": item["Type"] == "Comment",
"attachments": files,
}
)