| # -*- 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 |