blob: 62e31a53f85b2a5956a479fc5f69b63e1c38deff [file] [log] [blame]
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved
# Author: <srl@icu-project.org>
#
#
# Ticket management.
# This component manages the revision to ticket map.
from trac.core import Component, implements, TracError
from trac.env import IEnvironmentSetupParticipant
from trac.db import Table, Column, Index, DatabaseManager
from trac.config import Option
from trac.util.text import exception_to_unicode
import re
tktmgr_schema = [
Table('rev2ticket', key='rev')[ # map rev->ticket
Column('rev', type='int'), # changeset id
Column('ticket', type='int'), # ticket #
Index(['ticket'])], # index by ticket
]
class TicketManager(Component):
implements(IEnvironmentSetupParticipant)
# implements(IEnvironmentSetupParticipant, IRepositoryObserver)
ticket_pattern = Option('icucodetools', 'ticket_pattern', '^ticket:(\d+)',
"""A regex matching the commit messages. Group 1 must return a number.""")
def icu_tktmgr(self):
return 1;
known_youngest = -1
def environment_created(self):
db = self.env.get_db_cnx()
connector, _ = DatabaseManager(self.env)._get_connector()
cursor = db.cursor()
for table in tktmgr_schema:
for stmt in connector.to_sql(table):
cursor.execute(stmt)
cursor.execute("INSERT INTO system (name,value) "
"VALUES ('icu_tktmgr',%s)", (self.icu_tktmgr(),))
db.commit()
self.log.info('Database update: icu_tktmgr tables version %d ',
self.icu_tktmgr())
print 'icucodetools.ticketmgr: Note, first review will take a while.\n'
def youngest_rev(self,db):
if (self.known_youngest < 0):
#print('Did not know youngest value.')
cursor = db.cursor()
cursor.execute("SELECT value FROM system WHERE name='icu_tktmgr_youngest'")
row = cursor.fetchone()
if not row:
cursor.execute("INSERT INTO system (name,value) "
"VALUES ('icu_tktmgr_youngest','-1')")
db.commit()
self.known_youngest = -2
return -1
else:
known_youngest = int(row[0])
self.known_youngest = known_youngest
return self.known_youngest
def check_sync(self, log, db, repos):
ourYoungest = self.youngest_rev(db)
theirYoungest = repos.get_youngest_rev()
#log.info("TKT: check_sync %d/%d" % (ourYoungest,theirYoungest))
if(ourYoungest <= theirYoungest):
self.resync(log, db, repos, ourYoungest, theirYoungest)
def environment_needs_upgrade(self, db):
cursor = db.cursor()
cursor.execute("SELECT value FROM system WHERE name='icu_tktmgr'")
row = cursor.fetchone()
if not row or int(row[0]) < self.icu_tktmgr():
return True
def upgrade_environment(self, db):
cursor = db.cursor()
cursor.execute("SELECT value FROM system WHERE name='icu_tktmgr'")
row = cursor.fetchone()
if not row:
self.environment_created()
else:
self.log.info('Do not know how to upgrade icutraxctn_ticketmgr tables to %d',
self.icu_tktmgr())
cursor.close()
def resync(self, log, db, repos, ourYoungest, theirYoungest):
self.log.info('resync: ourYoungest=%d theirYoungest=%d' % (ourYoungest, theirYoungest))
if (ourYoungest < 0):
# start at rev 1
ourYoungest = 1
#self.ticket_pattern = self.env.config.get('icucodetools', 'ticket_pattern', '^cldrbug (\d+):')
# log.info("Pat: %s" % (self.ticket_pattern))
try:
self.ticket_match = re.compile(self.ticket_pattern)
except Exception, e:
found = self.env.config.get('icucodetools', 'ticket_pattern', 'NoneFound')
raise TracError('Could not compile icucodetools.ticket_pattern=/%s/ but /%s/: %s' % (self.ticket_pattern, found, exception_to_unicode(e, traceback=True)))
# self.ticket_match = re.compile(self.ticket_pattern.get())
# self.ticket_match = re.compile('.*')
for i in range(ourYoungest, theirYoungest+1):
#log.warning('syncing: %d [%d/%d+1]', i, theirYoungest)
cset = repos.get_changeset(i)
self.revision_changed(log, cset, i, db.cursor())
db.commit()
cursor = db.cursor();
cursor.execute("update system set value='%s' where name='icu_tktmgr_youngest'" % (theirYoungest))
db.commit()
#log.warn("self.known_youngest was %d [%d/%d]" % (self.known_youngest,ourYoungest,theirYoungest))
# update known youngest.
self.known_youngest = theirYoungest
#log.warn("self.known_youngest now %d [%d/%d]" % (self.known_youngest,ourYoungest,theirYoungest))
return
# IRepositoryObserver methods
def revision_changed(self, log, cset, next_youngest, cursor):
# sync the 'rev2ticket' table
message = cset.message or '--'
# can we load a ticket from it? "ticket:1234: Message"
res = self.ticket_match.match(message)
if res:
tickname = res.group(1)
try:
int(res.group(1)) # should be int
except Exception, e:
log.warning('Revision [%s] had unparseable ticket number [%s]: [%s]' %
(next_youngest, tickname, e))
return
try:
#log.warning('r%s=#%s' % (str(next_youngest), tickname))
cursor.execute("INSERT OR IGNORE INTO rev2ticket "
" (rev,ticket) "
"VALUES (%s,%s) ",
(str(next_youngest), tickname))
except Exception, e: # *another* 1.1. resync attempt won
log.warning('rev2ticket %s could not cache: %s' %
(next_youngest, e))
else:
log.warning('Revision %s had unmatched message %s' %
(next_youngest, cset.message))
def repository_resync(self, cursor):
cursor.execute("DELETE FROM rev2ticket");
def tkt2revs(self, log, db, repos, req, ticket):
"""Given a ticket, return a list of revs.
"""
self.check_sync(log, db, repos)
cursor = db.cursor()
cursor.execute("select rt.rev from rev2ticket as rt where rt.ticket = %d order by rt.rev" % int(ticket))
revs = []
for rev, in cursor:
rev = int(rev)
revs.append(rev)
cursor.close()
return revs