diff --git a/.gitattributes b/.gitattributes index 9931d13ee2a..2994bea1138 100644 --- a/.gitattributes +++ b/.gitattributes @@ -440,6 +440,14 @@ tools/release/java/.classpath -text tools/release/java/.project -text tools/release/java/Makefile -text tools/release/java/icu4c.css -text +tools/trac/IcuCodeTools/icucodetools/__init__.py -text +tools/trac/IcuCodeTools/icucodetools/dcut.py -text +tools/trac/IcuCodeTools/icucodetools/htdocs/css/icuxtn.css -text +tools/trac/IcuCodeTools/icucodetools/review.py -text +tools/trac/IcuCodeTools/icucodetools/templates/nothing.html -text +tools/trac/IcuCodeTools/icucodetools/templates/review.html -text +tools/trac/IcuCodeTools/icucodetools/ticketmgr.py -text +tools/trac/IcuCodeTools/icucodetools/tktlist.py -text tools/unicodetools/com/ibm/rbm/docs/images/TitleLogo_transparent.gif -text tools/unicodetools/com/ibm/rbm/docs/images/arrow_bullet.gif -text tools/unicodetools/com/ibm/rbm/docs/images/diamond_bullet.gif -text diff --git a/.gitignore b/.gitignore index bf150b05ebf..68663d6f54c 100644 --- a/.gitignore +++ b/.gitignore @@ -785,6 +785,9 @@ tools/release/java/APIChangeReport.html tools/release/java/Makefile.local tools/release/java/classes tools/release/java/lib +tools/trac/IcuCodeTools/*.egg-info +tools/trac/IcuCodeTools/build +tools/trac/IcuCodeTools/icucodetools/*.pyc tools/unicode/c/genbidi/*.d tools/unicode/c/genbidi/*.o tools/unicode/c/genbidi/*.pdb diff --git a/tools/trac/IcuCodeTools/icucodetools/__init__.py b/tools/trac/IcuCodeTools/icucodetools/__init__.py new file mode 100755 index 00000000000..82d67562522 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved +# +# +#from icutracxtn.web_ui import * diff --git a/tools/trac/IcuCodeTools/icucodetools/dcut.py b/tools/trac/IcuCodeTools/icucodetools/dcut.py new file mode 100755 index 00000000000..0b7486f3ce2 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/dcut.py @@ -0,0 +1,151 @@ +# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved + +import re + +from trac.core import * +from trac.util import Markup +from trac.web import IRequestHandler +from trac.web.chrome import add_stylesheet, INavigationContributor, \ + ITemplateProvider +from trac.web.href import Href +#from trac.versioncontrol.web_ui.changeset import IChangesetRangeLink +from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider, \ + Formatter + + +class DcutModule(Component): + + implements(IRequestHandler) +# implements(IRequestHandler,IChangesetRangeLink) + + def revision_range_link(self, req, base, start, end): + return ('DCUT Helper', req.href('dcut',old_path=base,new_path=base,old=start,new=end)) + + _request_re = re.compile(r"/dcut(?:/([^/]+))?(/.*)?$") + + def match_request(self, req): + match = re.match(self._request_re, req.path_info) + if match: + return True + + def process_request(self, req): + + idx = 0 + # srl +# show_files = self.timeline_show_files + db = self.env.get_db_cnx() + ticketlist = {} # dict of ticket->??? + revlist = {} # dict of revision-> + # well. check it again,. + + srldebug=False + + repos = self.env.get_repository(req.authname) + + new_path = req.args.get('new_path') + new_rev = req.args.get('new') + old_path = req.args.get('old_path') + old_rev = req.args.get('old') + + new_path = repos.normalize_path(new_path) + new_rev = repos.normalize_rev(new_rev) + old_path = repos.normalize_path(old_path) + old_rev = repos.normalize_rev(old_rev) + + old_rev = int(old_rev) + new_rev = int(new_rev) + + req.hdf['changeset.diff_href'] = req.href('changeset',old_path=old_path,new=new_rev,new_path=new_path,old=old_rev) + + req.hdf['is_dcut']=1 + + req.hdf['changeset.old_rev'] = old_rev + req.hdf['changeset.new_rev'] = new_rev + req.hdf['changeset.old_path'] = old_path + req.hdf['changeset.new_path'] = new_path + + if True: + req.hdf['target_path'] = '.'; + # okay. manually tromp through 'em + nrev = old_rev+1 + while nrev <= new_rev: + chgset = repos.get_changeset(nrev) + message = chgset.message or '--' + # can we load a ticket from it? + splits=message.split(':') + if message.startswith('ticket:') and len(splits)>2: + tickname=splits[1] + try: + ticknum=int(tickname) + except Exception,e: + nrev = nrev+1 + continue + # yes, we have a ticket # + files=[] + for chg in chgset.get_changes(): + if not chg[0].startswith(old_path): + continue + files.append(chg) + if len(files)==0: + nrev = nrev+1 + continue # no relevant files + titem=(ticknum,files,chgset) + if ticknum in ticketlist: + ticketlist[ticknum].append( titem ) + else: + ticketlist[ticknum]=[ titem ] + revlist[nrev]=titem + else: + print "malformed ticket? %s at %d" % (message,nrev) # don't know the syntax for die.. + nrev = nrev+1 + if len(ticketlist): + tickets=ticketlist.keys() + tickets.sort() + for ticket in tickets: + aticket=ticketlist[ticket] # (ticket,files,chg) + cmt = 'ticket:%d - %d revs: ' % (ticket,len(aticket)) + for rev in aticket: + revn = rev[2].rev + filecount = len(rev[1]) + cmt = cmt + 'r%d - %d files (' % (revn,filecount) + for file in rev[1]: + cmt = cmt + file[0] + ' ' + cmt = cmt + ') ' + req.hdf['tickets.%d.comment' % ticket] = wiki_to_oneliner(cmt, self.env, db, shorten=False) + req.hdf['tickets.%d.number' % ticket] = ticket + if len(revlist): + revs=revlist.keys() + revs.sort() + for rev in revs: + arev=revlist[rev] # (ticket,files,chg) + cmt = 'r%d ticket:%d ' % (arev[2].rev,arev[0]) + filecount = len(arev[1]) + cmt = cmt + ' - %d files (' % filecount + shortfiles='' + j = 0 + for file in arev[1]: + req.hdf['revs.%d.files.%d.path' % (rev,j)] = file[0] + req.hdf['revs.%d.files.%d.kind' % (rev,j)] = file[1] + req.hdf['revs.%d.files.%d.change' % (rev,j)] = file[2] + shortpath=file[0][len(old_path)+1:] + req.hdf['revs.%d.files.%d.shortpath' % (rev,j)] = shortpath + cmt = cmt + file[0] + ' ' + shortfiles = shortfiles + ' ' + shortpath + j=j+1 + cmt = cmt + ') ' + req.hdf['revs.%d.comment' % rev] = wiki_to_oneliner(cmt, self.env, db, shorten=False) + req.hdf['revs.%d.number' % rev] = rev + req.hdf['revs.%d.shortfiles' % rev] = shortfiles + req.hdf['revs.%d.backnumber' % rev] = (rev-1) + req.hdf['revs.%d.ticket' % rev] = arev[0] + + +# if isinstance(template, basestring): +# req.hdf['admin.page_template'] = template +# else: +# req.hdf['admin.page_content'] = Markup(template.render()) + + content_type = "text/html" + add_stylesheet(req, 'css/icuxtn.css') + return 'dcut.cs', content_type + diff --git a/tools/trac/IcuCodeTools/icucodetools/htdocs/css/icuxtn.css b/tools/trac/IcuCodeTools/icucodetools/htdocs/css/icuxtn.css new file mode 100644 index 00000000000..afe09037ddc --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/htdocs/css/icuxtn.css @@ -0,0 +1,30 @@ +/* Copyright (C) 2010 International Business Machines Corporation and Others. All Rights Reserved. */ + +/* @override http://unicode.org/cldr/trac/chrome/icucodetools/css/icuxtn.css */ + + +table.icureview { + border-collapse: collapse; + border: 1px solid gray; +} + +table.icureview th, table.icureview td { + text-align: left; + border: 1px solid gray; + padding: 2px; +} + +table.icureview thead th { + font-weight: bold; +} + +table.icureview td.changes { + text-align: right; +} + +table.icureview td.overall { + background-color: silver; + font-style: italic; + text-align: right; +} + diff --git a/tools/trac/IcuCodeTools/icucodetools/review.py b/tools/trac/IcuCodeTools/icucodetools/review.py new file mode 100755 index 00000000000..9cbf6499730 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/review.py @@ -0,0 +1,296 @@ +# Copyright (C) 2007-2010 International Business Machines Corporation and Others. All Rights Reserved. + +# Review module. +# TODO: refactor ticket manipulation items into ticketmgr. + +import re + +from trac.core import Component, implements +from trac.core import ComponentManager +from trac.core import TracError +from trac.util import Markup +from trac.web import IRequestHandler +from trac.web.chrome import add_stylesheet, ITemplateProvider, add_ctxtnav +from trac.versioncontrol import Changeset +from trac.web.api import IRequestFilter +from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider + +from genshi.builder import tag +#from trac.env import IEnvironmentSetupParticipant +from trac.perm import IPermissionRequestor +from trac.config import ListOption +from icucodetools.ticketmgr import TicketManager +from pkg_resources import resource_filename #@UnresolvedImport + +class ReviewModule(Component): + + implements(ITemplateProvider, IRequestFilter, IRequestHandler, IPermissionRequestor) + + # path to match for review + path_match = re.compile(r'/icureview/([0-9]+)') + + voteable_paths = ListOption('icucodetools', 'paths', '/ticket*', + doc='List of URL paths to show reviews on. Globs are supported.') + + # IPermissionRequestor methods + def get_permission_actions(self): + return ['ICUREVIEW_VIEW'] + + # ITemplateProvider methods + def get_templates_dirs(self): + return [resource_filename(__name__, 'templates')] + + def get_htdocs_dirs(self): + return [('icucodetools', resource_filename(__name__, 'htdocs'))] + + # IRequestFilter methods + def pre_process_request(self, req, handler): + if 'ICUREVIEW_VIEW' not in req.perm: + return handler + + if self.match_ticketpage(req): + self.render_reviewlink(req) + + return handler + + def post_process_request(self, req, template, data, content_type): + return (template, data, content_type) + + def render_reviewlink(self, req): + #add_stylesheet(req, 'icucodetools/css/icuxtn.css') + + els = [] + + ticket_mgr = TicketManager(self.compmgr) + + db = self.env.get_db_cnx() + repos = self.env.get_repository() + if not repos: + raise TracError("Could not get repository for %s" % (req.authname)) + + revs = ticket_mgr.tkt2revs(self.log, db, repos, req, req.args['ticket']) + + if not revs: + str = 'No commits.' + li = tag.li(str) + els.append(li) + else: + str = ' %d commits.' % len(revs) + href = req.href.review(req.args['ticket']) + a = tag.a('Review', href=href) + li = tag.li(a + str) + els.append(li) + + ul = tag.ul(els, class_='review') + className = '' + title = "Reviews" + add_ctxtnav(req, tag.span(tag.object(ul), id='icureview', title=title, class_=className)) + + + def match_request(self, req): + match = re.match('/review(?:/([^/]+))?(?:/([^/]+))?(?:/(.*)$)?', req.path_info) + if match: + req.args['ticket'] = match.group(1) + return True + + def match_ticketpage(self, req): + match = re.match('/ticket(?:/([^/]+))?(?:/([^/]+))?(?:/(.*)$)?', req.path_info) + if match: + req.args['ticket'] = match.group(1) + return True + + def changeToRange(self, c_new, change): + # q: (u'trunk/Locale.java', 'file', 'add', None, u'-1') from r3 + # q: (u'trunk/util.c', 'file', 'edit', u'trunk/util.c', u'2') from r4 +# c_path = change[0] +# c_itemtype = change[1] + c_type = change[2] + c_oldpath = change[3] + c_old = int(change[4] or -1) + if(c_type in (Changeset.COPY,Changeset.MOVE)): + return (-1, c_new, c_type, c_old, c_oldpath) # ignore OLD rev for these + elif(c_type in (Changeset.DELETE)): + return (c_old, -1, c_type) + else: + return (c_old, c_new, c_type) + + def describeChange(self, file, change, req, db): + what = change[2] or 'change' + where = 'r%d:%d' % (change[0],change[1]) + if(change[0] == -1): + if(change[1] == -1): + url = None + what = "noop" + where = None + else: + #if change[2] == 'add+commits': + url = req.href.browser(file, rev=change[1]) # 'add' + where = 'r%d' % change[1] + what = change[2] + elif(change[1] == -1): + url = None # deleted + what = "deleted" + where = None + else: + url = req.href.changeset(old_path=file, old=change[0], new_path=file, new=change[1]) + if url: + what = Markup('%s' % (url,what)) + if where: + return (what, tag.a(where, href=req.href.search(q=where))) + #return (what, where) + else: + return (what, '') + + + def process_request(self, req): + #ok, what are we about. + #db = self.env.get_db_cnx() + #ticketlist = {} # dict of ticket->??? + #revlist = {} # dict of revision-> + repos = self.env.get_repository() + + new_path = req.args.get('new_path') + new_rev = req.args.get('new') + old_path = req.args.get('old_path') + old_rev = req.args.get('old') + + new_path = repos.normalize_path(new_path) + new_rev = repos.normalize_rev(new_rev) + old_path = repos.normalize_path(old_path) + old_rev = repos.normalize_rev(old_rev) + + +# if not req.perm.has_permission('TICKET_MODIFY'): +# return req.redirect(req.href.browser()) + + old_rev = int(old_rev) + new_rev = int(new_rev) + + ticket = req.args.get('ticket') + try: + ticket = int(ticket) + except Exception: + ticket = 0 +# req.hdf['review.ticket'] = ticket +# req.hdf['review.tickethtml'] = tag.a(ticket, req.href.ticket(ticket)) + + data = {} + + data['overall_y'] = 0 + data['ticket_id'] = req.args['ticket'] + data['ticket_href'] = req.href.ticket(req.args['ticket']) + + ticket_mgr = TicketManager(self.compmgr) + + db = self.env.get_db_cnx() + repos = self.env.get_repository() + + revs = ticket_mgr.tkt2revs(self.log, db, repos, req, req.args['ticket']) + + if (not revs or len(revs)==0): + # nothing to review. shouldn't happen + return ('nothing.html', data, 'text/html') + elif(len(revs)==1): + # only one change - just do a changeset view + return req.redirect(req.href.changeset(revs[0])) + + revcount = 0 + branches = {} + files = {} + # may be 0 revs. + revisions = [] + + for rev in revs: + chgset = repos.get_changeset(rev) + # q: (u'trunk/Locale.java', 'file', 'add', None, u'-1') from r3 + # q: (u'trunk/util.c', 'file', 'edit', u'trunk/util.c', u'2') from r4 + message = chgset.message or '--' + revcount = revcount + 1 + revision = {} + revision['rev'] = tag.a(rev, req.href.changeset(rev)) + revision['num'] = rev + revision['comment'] = message #wiki_to_oneliner( message, self.env, db, shorten=False ) + for chg in chgset.get_changes(): + path = chg[0] + if path in files: + item = files[path] + else: + item = [] + files[path] = item; + item.append(self.changeToRange(rev,chg)) + revisions.append(revision) + data['revisions'] = revisions + + if(revcount > 0): + data['revcount'] = revcount + + # print "files: %d" % len(files) + # go throuhg each file and calculate its minimum range + filelist = files.keys() + filelist.sort() +# print 'bar to %d len of %s' % (len(filelist),str(filelist)) + for file in filelist: + changes = files[file] + i = 0 +# print " looping from %d to %d over %d " % (i,len(changes)-1,len(changes)) + while len(changes)>1 and i<(len(changes)-1): + if changes[i][1] == changes[i+1][0]: + if changes[i][0] == -1: + changes[i+1] = (changes[i][0],changes[i+1][1],'add+commits') # retain 'first' rev + else: + changes[i+1] = (changes[i][0],changes[i+1][1],'multiple commits') # retain 'first' rev + + changes = changes[:i] + changes[i+1:] # and shift down +# print "merged: %s" % str(changes) + files[file] = changes + else: + i = i + 1 + + # now, write 'em out + sera = 0 + #files_data = [] + for file in filelist: + sera = sera+1 + file_data = {} + file_data['name'] = Markup('%s' % (req.href.browser(file),file)) + branch_name = '/'.join(file.split('/')[0:2]) + #print "branch is: (%s)" % (branch_name) + branches_data = branches.get(branch_name, {}) + files_data = branches_data.get('files',[]) + + changes = files[file] + cha = 0 + changes_data = [] + for change in changes: + cha = cha + 1 +# print "%s output %s " % (file, str(change)) + changes_data.append(self.describeChange(file, change, req, db)) + file_data['changes'] = changes_data + if(len(changes)>1): + whathtml = self.describeChange(file, (changes[0][0], changes[len(changes)-1][1], 'overall'), req, db) + file_data['overall'] = whathtml + file_data['overall_y'] = 1 + data['overall_y'] = 1 + else: + file_data['overall_y'] = 0 + files_data.append(file_data) + # sets + branches_data['files'] = files_data + branches_data['len'] = len(files_data) + branches_data['name'] = branch_name + branches[branch_name] = branches_data + #data['files'] = files_data + #data['branches'] = branches + + # .. convert dict to array. + branch_list = [] + for branch in branches: + branch_list.append(branches[branch]) + data['branches'] = branch_list + data['lastbranch'] = branch + data['branchcount'] = len(branches) + + content_type = "text/html" + add_stylesheet(req, 'icucodetools/css/icuxtn.css') + return 'review.html', data, content_type + diff --git a/tools/trac/IcuCodeTools/icucodetools/templates/nothing.html b/tools/trac/IcuCodeTools/icucodetools/templates/nothing.html new file mode 100644 index 00000000000..6fa9ee43a90 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/templates/nothing.html @@ -0,0 +1,25 @@ + + + + + + Nothing to Review for ticket #${ticket_id} + + + + + +
+

Nothing to review!

+

+ Nothing to review - no changesets in ticket #${ticket_id} +

+
+ + diff --git a/tools/trac/IcuCodeTools/icucodetools/templates/review.html b/tools/trac/IcuCodeTools/icucodetools/templates/review.html new file mode 100644 index 00000000000..d86205e57b5 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/templates/review.html @@ -0,0 +1,117 @@ + + + + + + Review for ticket #${ticket_id} + + + + + +
+ +

Ticket #${ticket_id}

+ + + +

${revcount} Changesets

+ + + +

Files

+

Jump to Sections

+ +
+ + +
+ +

+ ${branch.name} — (${branch.len} files changed) +

+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileChangesDetailsOverall
(including other changes)
Details
${file.name} + + ${change[0]}
+
+
+ + ${change[1]}
+
+
${file.overall[0]}${file.overall[1]}
+ +
+ +
+ +
+ +
+

Merge Commands

+Very experimental. Change ??? to the top of the tree you want to merge from (icu or icu4j)
+ + +
+
+
+ + +
+
+$Id: $ +
+ + diff --git a/tools/trac/IcuCodeTools/icucodetools/ticketmgr.py b/tools/trac/IcuCodeTools/icucodetools/ticketmgr.py new file mode 100755 index 00000000000..6a7361fe4b5 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/ticketmgr.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved +# Author: +# + +# +# 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()) + + 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 + cursor = db.cursor(); + + #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.info('syncing: %d', i) + cset = repos.get_changeset(i) + self.revision_changed(log, cset, i, cursor) + cursor = db.cursor(); + cursor.execute("update system set value='%s' where name='icu_tktmgr_youngest'" % (theirYoungest)) + db.commit() + 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: + cursor.execute("INSERT INTO rev2ticket " + " (rev,ticket) " + "VALUES (%s,%s)", + (str(next_youngest), tickname)) + except Exception, e: # *another* 1.1. resync attempt won + log.warning('rev2ticket %s already cached: %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) + return revs diff --git a/tools/trac/IcuCodeTools/icucodetools/tktlist.py b/tools/trac/IcuCodeTools/icucodetools/tktlist.py new file mode 100755 index 00000000000..a7de4963e61 --- /dev/null +++ b/tools/trac/IcuCodeTools/icucodetools/tktlist.py @@ -0,0 +1,171 @@ +# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved + +import re + +import sys + +from trac.core import * +from trac.util import Markup +from trac.web import IRequestHandler +from trac.web.chrome import add_stylesheet, INavigationContributor, \ + ITemplateProvider +from trac.web.href import Href +#from trac.versioncontrol.web_ui.changeset import IChangesetRangeLink +from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider, \ + Formatter +from trac.ticket import Ticket + + + +class TicketlistModule(Component): + + implements(IRequestHandler) +# implements(IRequestHandler,IChangesetRangeLink) + + def revision_range_link(self, req, base, start, end): + return ('Ticket List', req.href('tktlist',old_path=base,new_path=base,old=start,new=end)) + + _request_re = re.compile(r"/tktlist(?:/([^/]+))?(/.*)?$") + + def match_request(self, req): + match = re.match(self._request_re, req.path_info) + if match: + return True + + def process_request(self, req): + #ok, what are we about. + db = self.env.get_db_cnx() + ticketlist = {} # dict of ticket->??? + revlist = {} # dict of revision-> + repos = self.env.get_repository(req.authname) + + if not req.perm.has_permission('TICKET_MODIFY'): + return req.redirect(req.href.browser()) + + # shortcut - if "revs" is set, just use that + revs = req.args.get('revs') + if revs and len(revs)>0: + content_type = "text/html" + add_stylesheet(req, 'css/icuxtn.css') + req.hdf['tix.revs'] = revs + items = revs.split() + outstr = '1=0 ' + for item in items: + rev = int(item) # may fail + outstr = 'rt.rev=%d'%(rev) + req.hdf['is_dcut']=1 + req.hdf['tix.sql'] = outstr + # test - get relevant revs + # print "otime=%s, ntime=%s"%(type(otime),type(ntime)) + #cursor.execute("select distinct t.id,t.summary from ticket as t,revision as r, rev2ticket as rt " + # " where t.id = rt.ticket and (%s) order by t.id"%(outstr)) + allsql = "select distinct ticket from rev2ticket as rt where %s order by rt.ticket"%(outstr) + allsql = "select ticket from rev2ticket where rev=%s order by ticket" + cursor = db.cursor() + # cursor.execute("select ticket from rev2ticket where rev=%s order by ticket",("22913",)) + cursor.execute("select rt.rev from rev2ticket as rt where rt.ticket = %d order by rt.rev" % int(6010)) + ticket = 0 + req.hdf['tix.sql'] = allsql + req.hdf['tix.sql']="zero" + for tkt in cursor: + req.hdf['tix.sql']=tkt + summ = ""; + #sys.stderr.write(" tkt %s summ %s from (d-d)" % (tkt,summ)) + ticket = ticket + 1 + try: + req.hdf['tickets.%d.comment' % ticket] = summ + #req.hdf['tickets.%d.commenthtml' % ticket] = wiki_to_oneliner( summ, self.env, db, shorten=True ) + except Exception,e: + req.hdf['tix.sql']=e + #req.hdf['tickets.%d.commenthtml' % ticket] = '' + req.hdf['tickets.%d.comment' % ticket] = '' + req.hdf['tickets.%d.number' % ticket] = tkt + #aa = Markup("#%s"%(req.href.ticket(tkt),tkt)) + #req.hdf['tickets.%d.html' % ticket] = aa + # set RDF here + return 'tktrevs.cs', content_type + + new_path = req.args.get('new_path') + new_rev = req.args.get('new') + old_path = req.args.get('old_path') + old_rev = req.args.get('old') + + new_path = repos.normalize_path(new_path) + new_rev = repos.normalize_rev(new_rev) + old_path = repos.normalize_path(old_path) + old_rev = repos.normalize_rev(old_rev) + + + old_rev = int(old_rev) + new_rev = int(new_rev) + + req.hdf['changeset.diff_href'] = req.href('changeset',old_path=old_path,new=new_rev,new_path=new_path,old=old_rev) + + req.hdf['is_dcut']=1 + + req.hdf['changeset.old_rev'] = old_rev + req.hdf['changeset.new_rev'] = new_rev + req.hdf['changeset.old_path'] = old_path + req.hdf['changeset.new_path'] = new_path + content_type = "text/html" + add_stylesheet(req, 'css/icuxtn.css') + + + + # first, get relevant changes. + req.hdf['target_path'] = '.'; + # okay. manually tromp through 'em + oset = repos.get_changeset(old_rev); + nset = repos.get_changeset(new_rev); + otime = int(oset.date) + ntime = int(nset.date) + + norm_tr="style='border: 1px dashed green; background-color: #CFC;'" + closed_tr="style='color: #666;'" + norev_tr="style='background-color:#FDD; border: 1px solid #F99; font-weight: bold;'" + + req.hdf['sample.norm.tr'] = norm_tr + req.hdf['sample.closed.tr'] = closed_tr + req.hdf['sample.norev.tr'] = norev_tr + +# print " searching in (%s-%s)" % (otime, ntime) + + # test - get relevant revs + cursor = db.cursor() +# print "otime=%s, ntime=%s"%(type(otime),type(ntime)) + cursor.execute("select distinct t.id,t.summary,t.owner, t.milestone, t.status " + " , c.value " + " from ticket as t,revision as r, rev2ticket as rt " + " left join ticket_custom as c " + " on ( c.name = 'revw' AND c.ticket = t.id ) " +# " , ticket_custom as c " + " where t.id = rt.ticket and rt.rev = r.rev and r.time > %s and r.time <= %s " + "and exists ( select nc.rev from node_change as nc where nc.rev=r.rev and nc.path like %s ) " + "order by t.id", (str(otime), str(ntime), (old_path + "%"))) + ticket = 0 + for tkt,summ,ownr,milestone,status, revw in cursor: +# print " tkt %s summ %s from (%d-%d)" % (tkt,summ, otime, ntime) + ticket = ticket + 1 + try: + req.hdf['tickets.%d.comment' % ticket] = summ + req.hdf['tickets.%d.commenthtml' % ticket] = wiki_to_oneliner( summ, self.env, db, shorten=True ) + except Exception,e: + req.hdf['tickets.%d.commenthtml' % ticket] = '' + req.hdf['tickets.%d.comment' % ticket] = '' + req.hdf['tickets.%d.number' % ticket] = tkt + aa = Markup("#%s"%(req.href.ticket(tkt),tkt)) + req.hdf['tickets.%d.html' % ticket] = aa + aa = Markup("#%s"%(req.href.ticket(tkt),tkt)) + req.hdf['tickets.%d.owner' % ticket] = ownr + req.hdf['tickets.%d.milestone' % ticket] = wiki_to_oneliner( "milestone:%s"%milestone , self.env, db, shorten=False ) + req.hdf['tickets.%d.reviewer' % ticket] = revw + req.hdf['tickets.%d.statushtml' % ticket] = wiki_to_oneliner( "#%s"%(tkt), self.env, db, shorten=False ) + req.hdf['tickets.%d.html' % ticket] = aa + req.hdf['tickets.%d.tr' % ticket] = norm_tr + if status and status.startswith('closed'): + req.hdf['tickets.%d.tr' % ticket] = closed_tr + if ( not revw ) or len(revw)<1: + req.hdf['tickets.%d.tr' % ticket] = norev_tr + + return 'tktlist.cs', content_type +