Skip to content

Commit dacd998

Browse files
author
Jeroen Baten
committed
Almost there...
1 parent 07c8415 commit dacd998

File tree

4 files changed

+243
-25
lines changed

4 files changed

+243
-25
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ https://www.theozimmermann.net/2017/10/bugzilla-to-github
3636

3737

3838
example: http://bugs.libreplan.org/attachment.cgi?id=212
39-
hoort bij ticket 635
39+
belongs to ticket 635
40+
4041

41-
moet handmatig geupload naar github.com en attachment toevoegen aan notitie.
4242

bugzilla2github.conf.sample

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
[settings]
22
github_token = 'yoursecretkeygoeshere'
3-
github_url = "https://api.github.com"
4-
github_owner = "kwoot"
5-
github_repo = "bz2gh"
3+
github_url = https://api.github.com
4+
github_owner = kwoot
5+
github_repo = bz2gh
66

7+
bugzilla_url= http://bugs.libreplan.org/
8+
filerepo_url= https://github.com/LibrePlan/bugzilla2githubattachments/blob/master/
79

810
# Database settings
911
engine = django.db.backends.postgresql_psycopg2

bugzilla2github.py

+228-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# - export all attachments: bug_id, filename
88
#
99
# How to use the script:
10+
# 0. Create a virtual environment containing modules mentioned in freeze.txt
1011
# 1. Generate a GitHub access token:
1112
# - on GitHub select "Settings"
1213
# - select "Personal access tokens"
@@ -23,6 +24,7 @@
2324
import psycopg2, psycopg2.extras
2425
import ConfigParser
2526
from pprint import pprint,pformat
27+
from urlparse import urljoin
2628

2729
reload(sys)
2830
sys.setdefaultencoding('utf-8')
@@ -39,6 +41,8 @@
3941
GITHUB_URL = config.get('settings', 'github_url')
4042
GITHUB_OWNER = config.get('settings', 'github_owner')
4143
GITHUB_REPO = config.get('settings', 'github_repo')
44+
BUGZILLA_URL = config.get('settings', 'bugzilla_url')
45+
FILEREPO_URL = config.get('settings', 'filerepo_url')
4246
# read database vars
4347
ENGINE = config.get('settings', 'engine')
4448
HOST = config.get('settings', 'dbhost')
@@ -55,7 +59,11 @@
5559
# force_update = True
5660

5761
def read_bugs(conn):
58-
""" read bug data"""
62+
"""
63+
Read bug data
64+
:param conn: database connection object
65+
:return: object with all bugs found, list of column names
66+
"""
5967
try:
6068
# create a new cursor object
6169
cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
@@ -70,7 +78,12 @@ def read_bugs(conn):
7078
return results,colnames
7179

7280
def read_comments(conn,bug_id):
73-
""" read bug comments data"""
81+
"""
82+
Read bug comments data
83+
:param conn: database connection object
84+
:param bug_id: id of bug to retrieve
85+
:return: object with all comments found, list of column names
86+
"""
7487
try:
7588
# create a new cursor object
7689
cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
@@ -85,8 +98,12 @@ def read_comments(conn,bug_id):
8598
return results,colnames
8699

87100
def read_attachment(conn,attach_id):
88-
""" read attachment data"""
89-
print type(attach_id)
101+
"""
102+
Read attachment data
103+
:param conn: database connection object
104+
:param attach_id: id of attachment
105+
:return: Single object with record, list of column names
106+
"""
90107
try:
91108
# create a new cursor object
92109
cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
@@ -103,7 +120,15 @@ def read_attachment(conn,attach_id):
103120
return result,colnames
104121

105122
def save_attachment(conn,attach_id,bug_id,comment_id,filename):
106-
""" read BLOB data from a table """
123+
"""
124+
Read BLOB data from a table and save to local disc
125+
:param conn: database connection object
126+
:param attach_id: id of attachment to save
127+
:param bug_id: id of bug to use in filename
128+
:param comment_id: if of comment to use in filename
129+
:param filename: Original filename of attachment
130+
:return: None
131+
"""
107132
#conn = None
108133
try:
109134
# create a new cursor object
@@ -112,49 +137,232 @@ def save_attachment(conn,attach_id,bug_id,comment_id,filename):
112137
cur.execute(""" SELECT thedata FROM attach_data WHERE id=%s """,(attach_id,))
113138
blob = cur.fetchone()
114139
open(path_to_dir + str(bug_id) + '_' + str(comment_id) + "_"+ filename, 'wb').write(blob[0])
115-
# close the communication with the PostgresQL database
116-
cur.close()
117140
except (Exception, psycopg2.DatabaseError) as error:
118141
print(error)
142+
return
143+
144+
def get_reporters(conn):
145+
"""
146+
Retrieve all users from Bugzilla database
147+
:param conn: database connection object
148+
:return: object with all results
149+
"""
150+
try:
151+
# create a new cursor object
152+
cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
153+
# execute the SELECT statement
154+
cur.execute(""" SELECT * FROM profiles """)
155+
results = cur.fetchall()
156+
colnames = [desc[0] for desc in cur.description]
157+
except (Exception, psycopg2.DatabaseError) as error:
158+
print(error)
159+
reporters={}
160+
for result in results:
161+
reporters[result["userid"]]=result
162+
return reporters,colnames
163+
164+
165+
def get_reporter(reporters,reporter_id):
166+
"""
167+
routine to find username+email for userid
168+
:param reporters: dictionary with all reporters, indexed on userid
169+
:param reporter_id: id of reporter to find
170+
:return: string username+email
171+
"""
172+
# ['userid', 'login_name', 'cryptpassword', 'realname', 'disabledtext', 'disable_mail', 'mybugslink', 'extern_id']
173+
reporterobj=reporters[reporter_id]
174+
reporterstr=reporterobj["realname"]+" \<<"+reporterobj["login_name"]+">\>"
175+
return reporterstr
176+
177+
178+
def create_body(reporters,bug):
179+
"""
180+
Routine to create issue body text
181+
:param bug: object containing bug record
182+
:return: string with body text
183+
"""
184+
# Note: the issue was created automatically with bugzilla2github.py tool
185+
# Bugzilla Bug ID: 246
186+
#
187+
# Date: 2010-01-16 19:15:00 +0000
188+
# From: Oscar González <[email protected]>
189+
# To: Javier Morán <[email protected]>
190+
# Version: navalplan-1.1 (1.1.x)
191+
192+
#
193+
# Last updated: 2012-09-10 05:56:17 +0000
194+
body=""
195+
196+
# ret["body"].append("# Bugzilla Bug ID: %d" % ret["number"])
197+
# also build link to original issue like: http://bugs.libreplan.org/show_bug.cgi?id=1295
198+
body+="(Original Bugzilla Bug ID: [" + str(bug["bug_id"])+"]("+BUGZILLA_URL+"show_bug.cgi?id="+str(bug["bug_id"])+"))\n\n"
199+
200+
# ret["body"].append("Date: " + ret["created_at"])
201+
body+="Date: " + str(bug["creation_ts"])+"\n"
202+
203+
# ret["body"].append("From: " + ret["user.login"])
204+
reporterstr=get_reporter(reporters,bug["reporter"])
205+
body+="From: " + reporterstr+"\n"
206+
207+
# ret["body"].append("To: " + ", ".join(ret["assignees"]))
208+
if bug["assigned_to"] is not None:
209+
reporterstr = get_reporter(reporters, bug["assigned_to"])
210+
body += "To: " + reporterstr+"\n"
211+
212+
# ret["body"].append("Version: " + ret["version"])
213+
body+="Version: " + str(bug["version"])+"\n"
214+
215+
# [I'm an inline-style link](https://www.google.com)
216+
body+="\n---\n"
217+
body+="(Note: this issue was migrated automatically with [bugzilla2github.py tool](https://github.com/LibrePlan/bugzilla2github) )\n"
218+
219+
220+
# if "cc" in bug:
221+
# ret["body"].append("CC: " + ", ".join(emails_convert(bug.pop("cc"))))
222+
# TODO: chose not to support this. Use Link to original issue to find info
223+
# # Extra information
224+
# ret["body"].append("")
225+
# if "dup_id" in bug:
226+
# ret["body"].append("Duplicates: " + ids_convert(bug.pop("dup_id")))
227+
# TODO: chose not to support this. Use Link to original issue to find info
228+
# if "dependson" in bug:
229+
# ret["body"].append("Depends on: " + ids_convert(bug.pop("dependson")))
230+
# TODO: chose not to support this. Use Link to original issue to find info
231+
# if "blocked" in bug:
232+
# ret["body"].append("Blocker for: " + ids_convert(bug.pop("blocked")))
233+
# TODO: chose not to support this. Use Link to original issue to find info
234+
# if "see_also" in bug:
235+
# ret["body"].append("See also: " + bug.pop("see_also"))
236+
# TODO: chose not to support this. Use Link to original issue to find info
237+
# ret["body"].append("Last updated: " + ret["updated_at"])
238+
# ret["body"].append("")
239+
return body
119240

120241
# Start connection to database
121-
conn = psycopg2.connect(host=HOST,database=DATABASE, user=USER, password=PASSWORD)
242+
try:
243+
conn = psycopg2.connect(host=HOST, database=DATABASE, user=USER, password=PASSWORD)
244+
except:
245+
print "I am unable to connect to the database."
246+
sys.exit(1)
247+
248+
# read all bug reporters
249+
reporters,colnames=get_reporters(conn)
122250
# read all bug reports
123251
bugs,colnames=read_bugs(conn)
124252
#pprint(bugs)
125253
#pprint(colnames)
254+
issue={}
126255
for bug in bugs:
127256
bug_id=bug['bug_id']
128-
#for field in colnames:
129-
# print str(field) + " -> " + str(bug[str(field)])
257+
for field in colnames:
258+
print str(field) + " -> " + str(bug[str(field)])
130259
print "bug_id",bug_id
131-
print "bug_severity",bug['bug_severity']
132-
print "bug_status", bug['bug_status']
133-
print "short_desc", bug['short_desc']
134-
print "priority",bug['priority']
135-
print "version",bug['version']
136-
print "resolution",bug['resolution']
260+
#print "bug_severity",bug['bug_severity']
261+
#print "bug_status", bug['bug_status']
262+
#print "short_desc", bug['short_desc']
263+
#print "priority",bug['priority']
264+
#print "version",bug['version']
265+
#print "resolution",bug['resolution']
266+
267+
body=create_body(reporters,bug)
268+
269+
# Create issues object to send to GitHub
270+
# And an issue with a comment only needs the comment body added:
271+
#
272+
# {
273+
# "issue": {
274+
# "title": "Imported from some other system",
275+
# "body": "..."
276+
# },
277+
# "comments": [
278+
# {
279+
# "body": "talk talk"
280+
# }
281+
# ]
282+
# }
137283

284+
issue={"issue": {"title":bug['short_desc'],
285+
"body":body}}
286+
287+
issuecomments=[]
288+
pprint(issuecomments)
138289
comments,colnames=read_comments(conn,bug_id)
139-
#pprint(colnames)
290+
pprint(colnames)
140291
for comment in comments:
292+
# ['comment_id', 'bug_id', 'who', 'bug_when', 'work_time', 'thetext', 'isprivate', 'already_wrapped', 'type', 'extra_data']
141293
comment_id=comment["comment_id"]
142294
attach_id=comment["extra_data"]
295+
issuecomment=""
296+
# pprint(comment)
297+
print " comment_id", comment["comment_id"]
298+
print " text:", comment["thetext"]
299+
300+
# ret.append("## Bugzilla Comment ID: " + comment.pop("commentid"))
301+
issuecomment += "Bugzilla Comment ID: " + str(comment["comment_id"]) + "\n"
302+
# ret.append("Date: " + comment.pop("bug_when"))
303+
issuecomment += "Date: " + str(comment["bug_when"]) + "\n"
304+
# ret.append("From: " + email_convert(comment.pop("who"),
305+
issuecomment += "From: " + get_reporter(reporters, comment["who"]) + "\n"
306+
# comment.pop("who.name", None)))
307+
# ret.append("")
308+
# ret.append(comment.pop("thetext", ""))
309+
issuecomment += comment["thetext"] + "\n\n"
310+
311+
143312
if attach_id is not None:
144313
print " attach_id",attach_id
145314
# check attachment record for this comment
146315
attachment,colnames=read_attachment(conn,attach_id)
147316
#for activity in activities:
148317
#pprint(colnames)
318+
# 'attach_id', 'bug_id', 'creation_ts', 'modification_time', 'description', 'mimetype', 'ispatch', 'filename',
319+
# 'submitter_id', 'isobsolete', 'isprivate', 'isurl']
149320
#pprint(attachment)
150321
#attachment=attachment.pop
151322
filename=attachment["filename"]
323+
#filesize=attachment[""]
152324
print " We have an attachment:",filename
153325
result=save_attachment(conn,attach_id,bug_id,comment_id,filename)
326+
# ret.append("> Attached file: %s (%s, %s bytes)" % (attach.pop("filename"),
327+
# attach.pop("type"), attach.pop("size")))
328+
# ret.append("> Description: " + attach.pop("desc"))
329+
# TODO: create link to repo file as: https://github.com/LibrePlan/bugzilla2githubattachments/blob/master/LICENSE
330+
issuecomment += "\n---\n\nAttached file: [" + filename + "](" + FILEREPO_URL + str(bug_id) + '_' + str(comment_id) + "_" + filename + ")\n"
331+
issuecomment += "Description: " + attachment["description"] + "\n---\n"
332+
333+
# add stuff to issuecomments list
334+
issuedict = dict()
335+
issuedict["body"] = issuecomment
336+
issuecomments.append(issuedict)
337+
338+
339+
#issue["issue"].append(issuecomments)
340+
#commentsdict={ "comments", issuecomments }
341+
#commentsdict=dict()
342+
#commentsdict.setdefault("comments", issuecomments)
343+
issue["comments"]=issuecomments
344+
# What have we created?
345+
pprint(issue)
346+
347+
# Now let's try to add this issue to a github repo
348+
# https://api.github.com/repos/${GITHUB_USERNAME}/foo/import/issues
349+
urlparts=(str(GITHUB_URL),"repos" ,str(GITHUB_OWNER) , str(GITHUB_REPO) , "import/issues")
350+
url="/".join(urlparts)
351+
# if url[0] == "/":
352+
# u = "%s%s" % (GITHUB_URL, url)
353+
# else:
354+
# u = "%s/repos/%s/%s/%s" % (GITHUB_URL, GITHUB_OWNER, GITHUB_REPO, url)
355+
pprint(url)
356+
d=issue
357+
#sys.exit(4)
358+
#result = requests.post(u, params={"access_token": GITHUB_TOKEN,
359+
headers={"Authorization": "token "+ GITHUB_TOKEN,
360+
"Accept": "application/vnd.github.golden-comet-preview+json" }
361+
result=requests.post(url, headers=headers, data = json.dumps(d))
154362

155-
#pprint(comment)
156-
print " comment_id",comment["comment_id"]
157-
print " text:",comment["thetext"]
363+
pprint(result)
364+
print result.json()
365+
# The end of handling all bugs
158366

159367
# close the communication with the PostgresQL database
160368
if conn is not None:

freeze.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
certifi==2018.1.18
2+
chardet==3.0.4
3+
idna==2.6
4+
pkg-resources==0.0.0
5+
psycopg2==2.7.4
6+
psycopg2-binary==2.7.4
7+
requests==2.18.4
8+
urllib3==1.22

0 commit comments

Comments
 (0)