7
7
Python 2.4 from http://www.python.org/
8
8
MySQL >= 3.23 from http://www.mysql.org/
9
9
10
+ Example use:
11
+ python mantis2trac.py --db mantis --tracenv /usr/local/trac-projects/myproj/ \
12
+ --host localhost --user root --clean --append --products foo,bar
13
+
14
+ Version 1.6
15
+ Author: Steffen Mecke ([email protected] )
16
+ Date: January 8, 2015
17
+
10
18
Version 1.5
11
19
Author: Matthew Parmelee ([email protected] )
12
20
Date: July 16, 2013
30
38
Mark Rowe <[email protected] > - original TracDatabase class
31
39
Bill Soudan <[email protected] > - Many enhancements
32
40
33
- Example use :
34
- python mantis2trac.py --db mantis --tracenv /usr/local/trac-projects/myproj/ \
35
- --host localhost --user root --clean --products foo,bar
41
+ Changes in version 1.6 :
42
+ - allow to append to an existing project (with correct id mapping)
43
+ - fixed to work with mysql by removing INSERT OR REPLACE syntax
36
44
37
45
Changes in version 1.5:
38
46
- repaired queries to be consistent with Mantis updates
125
133
MANTIS_PASSWORD = ''
126
134
127
135
# Path to the Trac environment.
128
- TRAC_ENV = ''
136
+ TRAC_ENV = '/var/www/trac/projectname/ '
129
137
130
138
# If true, all existing Trac tickets will be removed
131
139
# prior to import.
132
- TRAC_CLEAN = True
140
+ TRAC_CLEAN = False
141
+ # If TRAC_CLEAN is true and this is true, tickets will be appended
142
+ TRAC_APPEND = True
133
143
134
144
# Enclose imported ticket description and comments in a {{{ }}}
135
145
# preformat block? This formats the text in a fixed-point font.
@@ -319,7 +329,8 @@ def __getitem__(self, item):
319
329
statusXlator = FieldTranslator (STATUS_TRANSLATE )
320
330
321
331
class TracDatabase (object ):
322
- def __init__ (self , path ):
332
+ def __init__ (self , path , append ):
333
+ self .append = _append
323
334
self .env = Environment (path )
324
335
self ._db = self .env .get_db_cnx ()
325
336
self ._db .autocommit = False
@@ -335,8 +346,9 @@ def hasTickets(self):
335
346
return int (c .fetchall ()[0 ][0 ]) > 0
336
347
337
348
def assertNoTickets (self ):
338
- if self .hasTickets ():
339
- raise Exception ("Will not modify database with existing tickets!" )
349
+ if not self ._append or self .hasTickets ():
350
+ raise Exception ("Will not modify database with existing tickets!" )
351
+ return
340
352
341
353
def setSeverityList (self , s ):
342
354
"""Remove all severities, set them to `s`"""
@@ -413,15 +425,15 @@ def addTicket(self, id, time, changetime, component,
413
425
if PREFORMAT_COMMENTS :
414
426
desc = '{{{\n %s\n }}}' % desc
415
427
print "inserting ticket %s -- \" %s\" " % (id , summary [0 :40 ].replace ("\n " , " " ))
416
- c .execute ("""INSERT OR REPLACE INTO ticket (id, type, time, changetime, component,
428
+ c .execute ("""INSERT INTO ticket (type, time, changetime, component,
417
429
severity, priority, owner, reporter, cc,
418
430
version, milestone, status, resolution,
419
431
summary, description, keywords)
420
- VALUES (%s, %s, %s, %s, %s,
432
+ VALUES (%s, %s, %s, %s,
421
433
%s, %s, %s, %s, %s,
422
434
%s, %s, %s, %s,
423
435
%s, %s, %s)""" ,
424
- (id , type , self .convertTime (time ), self .convertTime (changetime ), component .encode ('utf-8' ),
436
+ (type , self .convertTime (time ), self .convertTime (changetime ), component .encode ('utf-8' ),
425
437
severity .encode ('utf-8' ), priority .encode ('utf-8' ), owner , reporter , cc ,
426
438
version , milestone .encode ('utf-8' ), status .lower (), resolution ,
427
439
summary .decode ('utf-8' ), desc , keywords .encode ('utf-8' )))
@@ -450,7 +462,7 @@ def addTicketComment(self, ticket, time, author, value):
450
462
comment = '{{{\n %s\n }}}' % comment
451
463
452
464
c = self .db ().cursor ()
453
- c .execute ("""INSERT OR IGNORE INTO ticket_change (ticket, time, author, field, oldvalue, newvalue)
465
+ c .execute ("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue)
454
466
VALUES (%s, %s, %s, %s, %s, %s)""" ,
455
467
(ticket , self .convertTime (time ), author , 'comment' , '' , comment ))
456
468
self .db ().commit ()
@@ -471,7 +483,7 @@ def addTicketChange(self, ticket, time, author, field, oldvalue, newvalue):
471
483
fixtime = c .fetchall ()
472
484
if fixtime :
473
485
time = time + 1
474
- c .execute ("""INSERT OR IGNORE INTO ticket_change (ticket, time, author, field, oldvalue, newvalue)
486
+ c .execute ("""INSERT INTO ticket_change (ticket, time, author, field, oldvalue, newvalue)
475
487
VALUES (%s, %s, %s, %s, %s, %s)""" ,
476
488
(ticket , self .convertTime (time ), author , field , oldvalue .encode ('utf-8' ), newvalue .encode ('utf-8' )))
477
489
self .db ().commit ()
@@ -513,7 +525,7 @@ def getLoginName(self, cursor, userid):
513
525
if not r :
514
526
sessionSql = """INSERT INTO session
515
527
(sid, authenticated, last_visit)
516
- VALUES (%s, %s, %s )""" , (result [0 ]['username' ].encode ('utf-8' ), '1' , self .convertTime (result [0 ]['last_visit' ]))
528
+ VALUES (%s, %s, %d )""" , (result [0 ]['username' ].encode ('utf-8' ), '1' , self .convertTime (result [0 ]['last_visit' ]))
517
529
# pre-populate the session table and the realname/email table with user data
518
530
try :
519
531
c .execute (sessionSql )
@@ -588,7 +600,7 @@ def productFilter(fieldName, products):
588
600
result += "%s = '%s'" % (fieldName , product )
589
601
return result
590
602
591
- def convert (_db , _host , _user , _password , _env , _force ):
603
+ def convert (_db , _host , _user , _password , _env , _force , _append ):
592
604
activityFields = FieldTranslator ()
593
605
594
606
# account for older versions of mantis
@@ -606,7 +618,7 @@ def convert(_db, _host, _user, _password, _env, _force):
606
618
607
619
# init Trac environment
608
620
print "Trac database('%s'): connecting..." % (_env )
609
- trac = TracDatabase (_env )
621
+ trac = TracDatabase (_env , _append )
610
622
611
623
# force mode...
612
624
if _force == 1 :
@@ -748,8 +760,9 @@ def convert(_db, _host, _user, _password, _env, _force):
748
760
del longdescs [0 ]
749
761
750
762
# Add the ticket to the Trac database
751
- trac .addTicket (** ticket )
752
-
763
+ new_id = trac .addTicket (** ticket )
764
+ print "ticket %s has id %s" % (bugid , new_id )
765
+
753
766
#
754
767
# Add ticket comments
755
768
#
@@ -764,7 +777,7 @@ def convert(_db, _host, _user, _password, _env, _force):
764
777
project , branch , commit = activity [0 ]['new_value' ].split ()
765
778
wikivalue = '%s [/browser/?rev=%s %s] [%s]' % (project , commit , branch , commit )
766
779
note ['note' ] = note ['note' ] + "\n \n " + wikivalue
767
- trac .addTicketComment (bugid , note ['date_submitted' ], trac .getLoginName (mysql_cur , note ['reporter_id' ]), note ['note' ])
780
+ trac .addTicketComment (new_id , note ['date_submitted' ], trac .getLoginName (mysql_cur , note ['reporter_id' ]), note ['note' ])
768
781
769
782
#
770
783
# Convert ticket changes
@@ -800,7 +813,7 @@ def convert(_db, _host, _user, _password, _env, _force):
800
813
# - 'version' -> 'milestone'
801
814
802
815
ticketChange = {}
803
- ticketChange ['ticket' ] = bugid
816
+ ticketChange ['ticket' ] = new_id
804
817
ticketChange ['oldvalue' ] = activity ['old_value' ]
805
818
ticketChange ['newvalue' ] = activity ['new_value' ]
806
819
ticketChange ['time' ] = activity ['date_modified' ]
@@ -929,24 +942,24 @@ def convert(_db, _host, _user, _password, _env, _force):
929
942
930
943
try :
931
944
try :
932
- if (os .path .isdir (trac .get_attachments_dir (bugid )) == False ):
945
+ if (os .path .isdir (trac .get_attachments_dir (new_id )) == False ):
933
946
try :
934
- os .mkdir (trac .get_attachments_dir (bugid ))
947
+ os .mkdir (trac .get_attachments_dir (new_id ))
935
948
except :
936
- errorStr = " * ERROR: couldnt create attachment directory in filesystem at %s" % trac .get_attachments_dir (bugid )
949
+ errorStr = " * ERROR: couldnt create attachment directory in filesystem at %s" % trac .get_attachments_dir (new_id )
937
950
errors .append (errorStr )
938
951
print errorStr
939
952
# trac stores the files with the special characters like spaces in the filename encoded to the url
940
953
# equivalents, so we have to urllib.quote() the filename we're saving.
941
- attachmentFile = open (trac .get_attachments_dir (bugid ) + quote (attachment ['filename' ]),'wb' )
954
+ attachmentFile = open (trac .get_attachments_dir (new_id ) + quote (attachment ['filename' ]),'wb' )
942
955
attachmentFile .write (attachment ['content' ])
943
956
attachmentFile .close ()
944
957
except :
945
- errorStr = " * ERROR: couldnt dump attachment data into filesystem at %s" % trac .get_attachments_dir (bugid ) + attachment ['filename' ]
958
+ errorStr = " * ERROR: couldnt dump attachment data into filesystem at %s" % trac .get_attachments_dir (new_id ) + attachment ['filename' ]
946
959
errors .append (errorStr )
947
960
print errorStr
948
961
else :
949
- attach_sql = """INSERT INTO attachment (type,id,filename,size,time,description,author,ipnr) VALUES ('ticket',%s,'%s',%i,%i,'%s','%s','127.0.0.1')""" % (bugid ,attachment ['filename' ].encode ('utf-8' ),attachment ['filesize' ],trac .convertTime (attachment ['date_added' ]),attachment ['description' ].encode ('utf-8' ),author )
962
+ attach_sql = """INSERT INTO attachment (type,id,filename,size,time,description,author,ipnr) VALUES ('ticket',%s,'%s',%i,%i,'%s','%s','127.0.0.1')""" % (new_id ,attachment ['filename' ].encode ('utf-8' ),attachment ['filesize' ],trac .convertTime (attachment ['date_added' ]),attachment ['description' ].encode ('utf-8' ),author )
950
963
try :
951
964
c = trac .db ().cursor ()
952
965
c .execute (attach_sql )
@@ -960,12 +973,12 @@ def convert(_db, _host, _user, _password, _env, _force):
960
973
961
974
totalAttachments += 1
962
975
hash = hashlib .sha1 ()
963
- hash .update (str (bugid ).encode ('utf-8' ))
976
+ hash .update (str (new_id ).encode ('utf-8' ))
964
977
path_hash = hash .hexdigest ()
965
978
hash = hashlib .sha1 ()
966
979
hash .update (attachment ['filename' ].encode ('utf-8' ))
967
980
file_hash = hash .hexdigest ()
968
- old_path = 'importfiles/%s/%s' % (bugid , quote (attachment ['filename' ]))
981
+ old_path = 'importfiles/%s/%s' % (new_id , quote (attachment ['filename' ]))
969
982
new_path = TRAC_ENV + '/files/attachments/ticket/'
970
983
new_path += '%s/%s/' % (path_hash [0 :3 ], path_hash )
971
984
try :
@@ -1009,6 +1022,7 @@ def usage():
1009
1022
print " -u | --user <MySQL username> - Effective Mantis database user"
1010
1023
print " -p | --passwd <MySQL password> - Mantis database user password"
1011
1024
print " -c | --clean - Remove current Trac tickets before importing"
1025
+ print " -a | --append - Append bugs to existing project"
1012
1026
print " --products <product1,product2> - List of products to import from mantis"
1013
1027
print " --help | help - This help info"
1014
1028
print
@@ -1045,6 +1059,8 @@ def main():
1045
1059
iter = iter + 1
1046
1060
elif sys .argv [iter ] in ['-c' , '--clean' ]:
1047
1061
TRAC_CLEAN = 1
1062
+ elif sys .argv [iter ] in ['-a' , '--append' ]:
1063
+ TRAC_APPEND = 1
1048
1064
elif sys .argv [iter ] in ['--products' ] and iter + 1 < len (sys .argv ):
1049
1065
PRODUCTS = sys .argv [iter + 1 ].split (',' )
1050
1066
iter = iter + 1
@@ -1055,7 +1071,7 @@ def main():
1055
1071
else :
1056
1072
usage ()
1057
1073
1058
- convert (MANTIS_DB , MANTIS_HOST , MANTIS_USER , MANTIS_PASSWORD , TRAC_ENV , TRAC_CLEAN )
1074
+ convert (MANTIS_DB , MANTIS_HOST , MANTIS_USER , MANTIS_PASSWORD , TRAC_ENV , TRAC_CLEAN , TRAC_APPEND )
1059
1075
1060
1076
if __name__ == '__main__' :
1061
1077
main ()
0 commit comments