7
7
# - export all attachments: bug_id, filename
8
8
#
9
9
# How to use the script:
10
+ # 0. Create a virtual environment containing modules mentioned in freeze.txt
10
11
# 1. Generate a GitHub access token:
11
12
# - on GitHub select "Settings"
12
13
# - select "Personal access tokens"
23
24
import psycopg2 , psycopg2 .extras
24
25
import ConfigParser
25
26
from pprint import pprint ,pformat
27
+ from urlparse import urljoin
26
28
27
29
reload (sys )
28
30
sys .setdefaultencoding ('utf-8' )
39
41
GITHUB_URL = config .get ('settings' , 'github_url' )
40
42
GITHUB_OWNER = config .get ('settings' , 'github_owner' )
41
43
GITHUB_REPO = config .get ('settings' , 'github_repo' )
44
+ BUGZILLA_URL = config .get ('settings' , 'bugzilla_url' )
45
+ FILEREPO_URL = config .get ('settings' , 'filerepo_url' )
42
46
# read database vars
43
47
ENGINE = config .get ('settings' , 'engine' )
44
48
HOST = config .get ('settings' , 'dbhost' )
55
59
# force_update = True
56
60
57
61
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
+ """
59
67
try :
60
68
# create a new cursor object
61
69
cur = conn .cursor (cursor_factory = psycopg2 .extras .DictCursor )
@@ -70,7 +78,12 @@ def read_bugs(conn):
70
78
return results ,colnames
71
79
72
80
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
+ """
74
87
try :
75
88
# create a new cursor object
76
89
cur = conn .cursor (cursor_factory = psycopg2 .extras .DictCursor )
@@ -85,8 +98,12 @@ def read_comments(conn,bug_id):
85
98
return results ,colnames
86
99
87
100
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
+ """
90
107
try :
91
108
# create a new cursor object
92
109
cur = conn .cursor (cursor_factory = psycopg2 .extras .DictCursor )
@@ -103,7 +120,15 @@ def read_attachment(conn,attach_id):
103
120
return result ,colnames
104
121
105
122
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
+ """
107
132
#conn = None
108
133
try :
109
134
# create a new cursor object
@@ -112,49 +137,232 @@ def save_attachment(conn,attach_id,bug_id,comment_id,filename):
112
137
cur .execute (""" SELECT thedata FROM attach_data WHERE id=%s """ ,(attach_id ,))
113
138
blob = cur .fetchone ()
114
139
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 ()
117
140
except (Exception , psycopg2 .DatabaseError ) as error :
118
141
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
119
240
120
241
# 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 )
122
250
# read all bug reports
123
251
bugs ,colnames = read_bugs (conn )
124
252
#pprint(bugs)
125
253
#pprint(colnames)
254
+ issue = {}
126
255
for bug in bugs :
127
256
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 )])
130
259
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
+ # }
137
283
284
+ issue = {"issue" : {"title" :bug ['short_desc' ],
285
+ "body" :body }}
286
+
287
+ issuecomments = []
288
+ pprint (issuecomments )
138
289
comments ,colnames = read_comments (conn ,bug_id )
139
- # pprint(colnames)
290
+ pprint (colnames )
140
291
for comment in comments :
292
+ # ['comment_id', 'bug_id', 'who', 'bug_when', 'work_time', 'thetext', 'isprivate', 'already_wrapped', 'type', 'extra_data']
141
293
comment_id = comment ["comment_id" ]
142
294
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
+
143
312
if attach_id is not None :
144
313
print " attach_id" ,attach_id
145
314
# check attachment record for this comment
146
315
attachment ,colnames = read_attachment (conn ,attach_id )
147
316
#for activity in activities:
148
317
#pprint(colnames)
318
+ # 'attach_id', 'bug_id', 'creation_ts', 'modification_time', 'description', 'mimetype', 'ispatch', 'filename',
319
+ # 'submitter_id', 'isobsolete', 'isprivate', 'isurl']
149
320
#pprint(attachment)
150
321
#attachment=attachment.pop
151
322
filename = attachment ["filename" ]
323
+ #filesize=attachment[""]
152
324
print " We have an attachment:" ,filename
153
325
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 \n Attached 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 ))
154
362
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
158
366
159
367
# close the communication with the PostgresQL database
160
368
if conn is not None :
0 commit comments