forked from fnux/rt2zammad
Add actuall migration code
Signed-off-by: Michal Čihař <michal@cihar.com>
This commit is contained in:
parent
43d48c71e2
commit
88bdfc53f8
2 changed files with 161 additions and 40 deletions
|
@ -1,2 +1,3 @@
|
|||
rt
|
||||
zammad_py
|
||||
python-dateutil
|
||||
|
|
200
rt2zammad.py
200
rt2zammad.py
|
@ -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,
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue