""" tt2p4.py
Convert TestTrack Defects to Perforce jobs.
This script takes defects which have a "changelists" custom field and creates P4 jobs, associating them with the changelists from TT.
You must have the TestTrack ODBC driver installed for this to work.
"""
import os, string, time, sys
import dbi, odbc
import P4Client
import P4Handler
# TestTrack database must have a custom field called "Changelists"
# Each TestTrack user must have a Notes entry with the first line reading: PerforceID: perforceusername
# Put name of DSN here
odbcDsn = "lumitest"
# Put name of client used for fixes.
fixClient = "lumigent-tt2p4.py"
defaultPerforceUser = "stephen.ng" # in case we fail getting the user name
p4Handler = None
tt2P4Users = None
users = {}
def addJobs(cur, defectsChanged):
row = cur.fetchone()
while row:
if defectsChanged.has_key(row[0]):
jobname = "DEF%05d" % row[0]
ttUserId = defectsChanged[row[0]]
# The defectEditorUser is the person who inserted the "changelists" field.
defectEditorUser = tt2P4Users[ttUserId]
try:
p4Handler.Run("job", "-d", jobname)
except P4Handler.Error, err:
# It's okay to delete non-existent job.
if string.find(err.value, "doesn't exist") == -1:
raise(err)
# Add this defect if it has changelists associated
if row[3] != None:
jobSpec = "Job: " + jobname + "\n"
jobSpec += "Status: open\n"
jobSpec += "User: " + defectEditorUser + "\n"
jobSpec += "FoundBy: " + string.strip(row[5]) + ", " + string.strip(row[4]) + "\n"
jobSpec += "DefectNumber: %d\n" % row[0]
jobSpec += "LongDescription:\n\t"
jobSpec += string.replace(row[6], "\n", "\n\t")
jobSpec += "\nDescription:\n"
jobSpec += "\t" + string.strip(row[1])
p4Handler.inputData = jobSpec
p4Handler.p4.User = defectEditorUser
p4Handler.Run("job", "-i")
changelists = string.splitfields(string.strip(row[3]), ",")
jobStatus = "closed"
for changelist in changelists:
# Fixes are owned by the user who created the changelist.
# Get the changelist
try:
changeinfo = p4Handler.Run("change", "-o", `int(changelist)`)
changeinfo = string.split(changeinfo[0]["data"], "\n")
except P4Handler.Error, err:
if string.find(err.value, "unknown") != -1:
# changelist doesn't exist, try next one.
print "warning: " + err.value
continue
else:
raise(err)
# Parse changelist lines (too bad stat not supported).
for line in changeinfo:
if line[:5] == "User:":
changeUser = string.split(line)[1]
if line[:7] == "Status:":
if string.split(line)[1] == "pending":
jobStatus = "open"
break
# Validate the user name
if users.has_key(changeUser):
p4Handler.p4.User = changeUser
else:
p4Handler.p4.User = defectEditorUser
# At last! Create the fix.
try:
p4Handler.Run("fix", "-s", jobStatus, "-c", `int(changelist)`, jobname)
except P4Client.error, err:
print "fix error" + `err`
row = cur.fetchone()
db = None
lastTTChange = 0
def convertDefects():
global lastTTChange
# Which defects have been changed?
defectsChanged = {}
cur = db.cursor()
cur.execute("select IDDEFREC, NOTES, IDRECORD, IDUSER from DEFLOG where IDRECORD > %d" % lastTTChange)
row = cur.fetchone()
while row:
defectNumber, changeNotes, ttChange, idUser = row
if string.find(changeNotes, "Changelists") != -1:
defectsChanged[defectNumber] = idUser
if ttChange > lastTTChange:
lastTTChange = ttChange
row = cur.fetchone()
cur.close()
if len(defectsChanged) > 0:
cur = db.cursor()
cur.execute("select DEFECTNUM, SUMMARY, DATEMODIFY, CUSTVALUE, FIRSTNAME, LASTNAME, REPORTBY.DESCRPTN from CUSTMVAL, CUSTMDEF, DEFECTS, USERS, REPORTBY where CUSTMVAL.IDDEFREC=DEFECTS.IDRECORD and DEFECTS.IDENTERBY=USERS.IDRECORD and REPORTBY.IDDEFREC=DEFECTS.IDRECORD and CUSTMVAL.IDCUSTREC=CUSTMDEF.IDRECORD and CUSTMDEF.FIELDNAME='Changelists' ORDER BY DEFECTNUM")
row = cur.fetchone()
addJobs(cur, defectsChanged)
cur.close()
return `defectsChanged`
return None
def init():
import sys
global db, p4Handler, users, tt2P4Users
p4Handler = P4Handler.P4Handler()
p4Handler.p4.Client = fixClient
for u in p4Handler.Run("users"):
users[u["User"]] = u["FullName"]
db = odbc.odbc(odbcDsn)
# Get mapping of user ids to Perforce names
cur = db.cursor()
cur.execute("select IDRECORD, NOTES FROM USERS")
tt2P4Users = {}
row = cur.fetchone()
while row:
try:
tt2P4Users[int(row[0])] = string.split(row[1])[1]
except:
tt2P4Users[int(row[0])] = defaultPerforceUser
# raise RuntimeError("Every user must have a PerforceID: username entry as the first line of Notes")
row = cur.fetchone()
cur.close()
def teardown():
p4Handler.Final()
def connect():
global db
try:
time.sleep(300)
db = odbc.odbc(odbcDsn)
return db
except:
print "Unexpected error:", sys.exc_info()[0]
# print "DBI error: " + `err`
return None
def tryConnect(attempts):
for i in range(attempts):
if connect() != None:
return
time.sleep(300)
raise RuntimeError("can't connect")
if __name__=='__main__':
init()
while 1:
try:
convertDefects()
time.sleep(1)
print ".",
except:
print "Unexpected error:", sys.exc_info()[0]
tryConnect(10)