Last active
December 30, 2015 03:49
-
-
Save rmoff/7771522 to your computer and use it in GitHub Desktop.
Zendesk to confluence migration script. Hacked together and not pretty, but it does the job!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# | |
# Migrate Zendesk topics/forums to Confluence pages/child pages | |
# | |
# Hacky as hell, I'm not proud! | |
# | |
# @rmoff 2013-12-03 | |
# | |
# Known bugs: | |
# 1. Embedded images with non-safe characters in the filename (eg +) get encoded by Confluence at upload but the | |
# resulting page will still reference the non-encoded filename. | |
import requests, json, os | |
from xmlrpclib import Server,Binary | |
headers = {'Accept':'application/json'} | |
s=Server("https://yourconfluence.url/wiki/rpc/xmlrpc") | |
conf_token = s.confluence2.login("confluence_user","confluence_password") | |
## user vars | |
# | |
# This is a list of Zendesk forum IDs to migrate. | |
# Use zd_categories_and_forums.py to get the forum IDs | |
forums_to_migrate=[21415286] | |
# | |
# This is the key of the target Confluence space to move articles to | |
spacekey='INF' | |
# This page_id is the id of the root page of the space. You can get it by going to the page on Confluence and then | |
# clicking on Tools -> Page Information and picking the ID out of the uRL (https://yourconfluence.url/wiki/pages/viewinfo.action?pageId=xxxxxxxxxx) | |
# Or, run script con_list_spaces.py to see a list of all pages in all spaces and associated IDs | |
space_page_id='7964269' | |
## Forums | |
for forum_id in forums_to_migrate: | |
url='https://zendesk.url/api/v2/forums/%s.json' % (forum_id) | |
forumresponse=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
forum=json.loads(forumresponse.content)['forum'] | |
print 'Processing Zendesk forum %s (id: %s)' % (forum['name'],forum['id']) | |
try: | |
conf_parent_page=s.confluence2.getPage(conf_token,spacekey,forum['name']) | |
print '-> Found existing Confluence page: %s (%s)' % (conf_parent_page['title'],conf_parent_page['url']) | |
except: | |
conf_page_data = {} | |
conf_page_data['space'] = spacekey | |
conf_page_data['title'] = forum['name'] | |
conf_page_data['content'] = '%s<br/><em>Zendesk import</em>' % (forum['description']) | |
conf_page_data['parentId'] = space_page_id | |
conf_parent_page = s.confluence2.storePage(conf_token, conf_page_data) | |
print '-> Created Confluence page: %s (%s)' % (conf_parent_page['title'],conf_parent_page['url']) | |
print 'Debug : s.confluence2.removePage(conf_token,\'%s\')' % (conf_parent_page['id']) | |
## Topics | |
url='https://zendesk.url/api/v2/forums/%s/topics.json' % (forum_id) | |
topicsr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
topics=json.loads(topicsr.content)['topics'] | |
for topic in topics: | |
print '\tTopic %s (id: %s)' % (topic['title'].encode('utf-8'),topic['id']) | |
# Get the name of the source page's author | |
url='https://zendesk.url/api/v2/users/%s.json' % (topic['submitter_id']) | |
userr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
user=json.loads(userr.content)['user'] | |
# Build the new page | |
# NB the API doesn't honour created, modified, creator, modifier | |
conf_page_data = {} | |
conf_page_data['content'] = '<em>Content imported from a Zendesk article written by <strong>%s</strong></em> at %s<hr/>%s' % (user['name'],topic['created_at'],topic['body']) | |
conf_page_data['space'] = spacekey | |
conf_page_data['title'] = topic['title'] | |
conf_page_data['parentId'] = conf_parent_page['id'] | |
try: | |
# Create page | |
conf_page = s.confluence2.storePage(conf_token, conf_page_data) | |
#print '\tConfluence page created, id: %s' % (conf_page['id']) | |
print '\tDebug : s.confluence2.removePage(conf_token,\'%s\')' % (conf_page['id']) | |
# Embedded images - these are a pain in the arse, since they are attachments that need fetching | |
# but are not modelling in the zendesk API as attachments | |
if (conf_page_data['content'].find('<img src="/attachments/token')>0): | |
for img in conf_page_data['content'].split('<img src="/attachments/token')[1:]: | |
raw_filename=img.split('"')[0] | |
filename=raw_filename.split('name=')[1] | |
path='https://zendesk.url/attachments/token%s' % (raw_filename) | |
image_file=requests.get(path, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS'])) | |
conf_attachment={} | |
conf_attachment['fileName']=filename | |
conf_attachment['contentType']=image_file.headers['content-type'] | |
conf_attachment = s.confluence2.addAttachment(conf_token,conf_page['id'],conf_attachment,Binary(image_file.content)) | |
print '\t\tAdded Attachment: %s' % (filename) | |
conf_page['content']=conf_page['content'].replace('/attachments/token%s'%(raw_filename),'/wiki/download/attachments/%s/%s'%(conf_page['id'],filename)) | |
pageupdateoptions={"versionComment":"Fix img src path for %s"%(filename),"minorEdit":True} | |
conf_page=s.confluence2.updatePage(conf_token,conf_page,pageupdateoptions) | |
# Attachments | |
for zd_attachment in topic['attachments']: | |
conf_attachment={} | |
conf_attachment['fileName']=zd_attachment['file_name'] | |
conf_attachment['contentType']=zd_attachment['content_type'] | |
attachment_data=requests.get(zd_attachment['content_url'], auth=(os.environ['ZD_USER'], os.environ['ZD_PASS'])) | |
conf_attachment = s.confluence2.addAttachment(conf_token,conf_page['id'],conf_attachment,Binary(attachment_data.content)) | |
print '\t\tAdded Attachment: %s' % (zd_attachment['file_name']) | |
# Comments | |
if (topic['comments_count']> 0): | |
url='https://zendesk.url/api/v2/topics/%s/comments.json' % (topic['id']) | |
commentsr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
comments=json.loads(commentsr.content)['topic_comments'] | |
comments.reverse() | |
for zd_comment in comments: | |
url='https://zendesk.url/api/v2/users/%s.json' % (zd_comment['user_id']) | |
userr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
user=json.loads(userr.content)['user'] | |
conf_comment={} | |
conf_comment['content']='Comment from <strong>%s</strong> at %s:<br/>\n\n%s' % (user['name'],zd_comment['created_at'],zd_comment['body']) | |
conf_comment['pageId']=conf_page['id'] | |
conf_comment=s.confluence2.addComment(conf_token,conf_comment) | |
for zd_attachment in zd_comment['attachments']: | |
# Comment Attachments | |
conf_comment_attachment={} | |
conf_comment_attachment['fileName']=zd_attachment['file_name'] | |
conf_comment_attachment['contentType']=zd_attachment['content_type'] | |
conf_comment_attachment['comment']=user['name'] | |
attachment_data=requests.get(zd_attachment['content_url'], auth=(os.environ['ZD_USER'], os.environ['ZD_PASS'])) | |
conf_comment_attachment = s.confluence2.addAttachment(conf_token,conf_page['id'],conf_comment_attachment,Binary(attachment_data.content)) | |
print '\t\tAdded Comments Attachment: %s' % (zd_attachment['file_name']) | |
conf_comment['content']+='<br/><em>Attachment : %s</em>' % (conf_comment_attachment['fileName']) | |
s.confluence2.editComment(conf_token,conf_comment) | |
# TODO | |
# Tags - good luck, the confluence API is not helpful when it comes to labels. | |
#for tag in topic['tags']: | |
# print 'tag: %s' % (tag) | |
print '\tMigrated to: %s\n' % (conf_page['url']) | |
except Exception,err: | |
print '** failed to migrate **' | |
print err | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# | |
# export your Zendesk username and password as ZD_USER and ZD_PASS | |
# environment variables or replace them inline below | |
# | |
# ----------- | |
# Lists out all categories and forums | |
# from Zendesk with the respective IDs. | |
# | |
# @rmoff 2013-12-03 | |
import requests, json, os | |
url='https://zendesk.url/api/v2/categories.json' | |
headers = {'Accept':'application/json'} | |
categoryresponse=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
if (categoryresponse.headers['status']=='200 OK'): | |
categories=json.loads(categoryresponse.content) | |
for category in categories['categories']: | |
#print '%s' % (category['name']) | |
#print 'Category %s: %s (%s)' % (category['id'],category['name'],category['description']) | |
url='https://zendesk.url/api/v2/categories/%s/forums.json'% (category['id']) | |
forumresponse=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers) | |
if (forumresponse.headers['status']=='200 OK'): | |
forums=json.loads(forumresponse.content) | |
for forum in forums['forums']: | |
print '%s,%s,%s,%s' % (category['id'],category['name'],forum['id'],forum['name']) | |
# print '\t%s: %s (%s)' % (forum['id'],forum['name'],forum['description']) | |
else: | |
print 'Request failed' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment