ICU-8221 [IcuCodeTools] copy into new branch

X-SVN-Rev: 29372
This commit is contained in:
Unknown User 2011-01-28 00:36:32 +00:00
parent 8a9e92fe51
commit 99d3dcb986
14 changed files with 1144 additions and 0 deletions

12
.gitattributes vendored
View File

@ -620,6 +620,18 @@ tools/trac/IcuCodeTools/0.11/license.html -text
tools/trac/IcuCodeTools/0.11/readme.txt -text
tools/trac/IcuCodeTools/0.11/setup.cfg -text
tools/trac/IcuCodeTools/0.11/setup.py -text
tools/trac/IcuCodeTools/0.12/icucodetools/__init__.py -text
tools/trac/IcuCodeTools/0.12/icucodetools/dcut.py -text
tools/trac/IcuCodeTools/0.12/icucodetools/htdocs/css/icuxtn.css -text
tools/trac/IcuCodeTools/0.12/icucodetools/review.py -text
tools/trac/IcuCodeTools/0.12/icucodetools/templates/nothing.html -text
tools/trac/IcuCodeTools/0.12/icucodetools/templates/review.html -text
tools/trac/IcuCodeTools/0.12/icucodetools/ticketmgr.py -text
tools/trac/IcuCodeTools/0.12/icucodetools/tktlist.py -text
tools/trac/IcuCodeTools/0.12/license.html -text
tools/trac/IcuCodeTools/0.12/readme.txt -text
tools/trac/IcuCodeTools/0.12/setup.cfg -text
tools/trac/IcuCodeTools/0.12/setup.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

3
.gitignore vendored
View File

@ -956,6 +956,9 @@ tools/release/java/src/com/ibm/icu/dev/tools/misc/*.class
tools/trac/IcuCodeTools/0.11/*.egg-info
tools/trac/IcuCodeTools/0.11/build
tools/trac/IcuCodeTools/0.11/icucodetools/*.pyc
tools/trac/IcuCodeTools/0.12/*.egg-info
tools/trac/IcuCodeTools/0.12/build
tools/trac/IcuCodeTools/0.12/icucodetools/*.pyc
tools/unicode/c/genbidi/*.d
tools/unicode/c/genbidi/*.o
tools/unicode/c/genbidi/*.pdb

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved
#
#
#from icutracxtn.web_ui import *

View File

@ -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

View File

@ -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;
}

View File

@ -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('<a href="%s">%s</a>' % (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('<a href="%s">%s</a>' % (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

View File

@ -0,0 +1,25 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<title>Nothing to Review for ticket #${ticket_id}</title>
</head>
<body>
<div id="ctxtnav" class="nav"></div>
<div id="content" class="icucodereview">
<h1>Nothing to review!</h1>
<p>
Nothing to review - no changesets in ticket <a href="${ticket_href}">#${ticket_id}</a>
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,117 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved
-->
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<title>Review for ticket #${ticket_id}</title>
</head>
<body>
<div id="ctxtnav" class="nav"></div>
<div id="content" class="icucodereview">
<h1>Ticket <a href='${href.ticket(ticket_id)}'>#${ticket_id}</a></h1>
<py:if test="revcount">
<h2>${revcount} Changesets</h2>
<ul>
<py:for each="rev in revisions">
<li><a href="${href.changeset(rev.num)}">r${rev.num}</a> &mdash; ${rev.comment}</li>
</py:for>
</ul>
<py:if test="branchcount > 1">
<h2>Files</h2>
<h3>Jump to Sections</h3>
<ul>
<py:for each="branch in branches">
<li><a href="#${branch.name}">${branch.name}</a> &mdash; ${branch.len} files</li>
</py:for>
</ul>
</py:if>
<py:for each="branch in branches">
<hr/>
<a name="${branch.name}">
<h3>
<a href="${href.browser(branch.name)}">${branch.name}</a> &mdash; (${branch.len} files changed)
</h3>
</a>
<style type='text/css'>
</style>
<blockquote>
<table class='icureview'>
<thead>
<tr>
<th>File</th>
<th>Changes</th>
<th>Details</th>
<py:if test="overall_y > 0">
<th>Overall<br/><i>(including other changes)</i></th>
<th>Details</th>
</py:if>
</tr>
</thead>
<tbody>
<py:for each="file in branch.files">
<tr>
<th class='name'>${file.name}</th>
<td class='changes'>
<py:for each="change in file.changes">
${change[0]}<br/>
</py:for>
</td>
<td class='details'>
<py:for each="change in file.changes">
${change[1]}<br/>
</py:for>
</td>
<py:if test="overall_y > 0">
<py:if test="file.overall_y > 0">
<td class='overall'>${file.overall[0]}</td>
<td class='overall'>${file.overall[1]}</td>
</py:if>
</py:if>
</tr>
</py:for>
</tbody>
</table>
</blockquote>
</py:for>
<hr/>
<div style='display:none;'>
<h2>Merge Commands</h2>
Very experimental. Change ??? to the top of the tree you want to merge from (icu or icu4j)<br/>
<textarea>svn merge -c <?cs each:rev = revisions ?><?cs var:rev.num ?>,<?cs /each ?> svn+ssh://source.icu-project.org/repos/icu/???/trunk</textarea>
<hr/>
</div>
<hr/>
<iframe src="http://sites.google.com/site/icucodetools/v1/review" width="100%" height="800px">
<h1><a href="http://sites.google.com/site/icucodetools/v1/review">Help</a></h1>
</iframe>
</py:if>
<hr/>
<i>$Id: $</i>
</div>
</body>
</html>

View File

@ -0,0 +1,170 @@
# -*- 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

View File

@ -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("<a class=\"new ticket\" href=\"%s\" title=\"Ticket x (new)\">#%s</a>"%(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("<a class=\"new ticket\" href=\"%s\" title=\"Ticket x (new)\">#%s</a>"%(req.href.ticket(tkt),tkt))
req.hdf['tickets.%d.html' % ticket] = aa
aa = Markup("<a class=\"new ticket\" href=\"%s\" title=\"Ticket x (new)\">#%s</a>"%(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

View File

@ -0,0 +1,51 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></meta>
<title>ICU License - ICU 1.8.1 and later</title>
</head>
<body BGCOLOR="#ffffff">
<h2>ICU License - ICU 1.8.1 and later</h2>
<p>COPYRIGHT AND PERMISSION NOTICE</p>
<p>
Copyright (c) 1995-2010 International Business Machines Corporation and others
</p>
<p>
All rights reserved.
</p>
<p>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Software, and to permit persons
to whom the Software is furnished to do so, provided that the above
copyright notice(s) and this permission notice appear in all copies
of the Software and that both the above copyright notice(s) and this
permission notice appear in supporting documentation.
</p>
<p>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM,
OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.
</p>
<p>
Except as contained in this notice, the name of a copyright holder shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization of the copyright holder.
</p>
<hr>
<p><small>
All trademarks and registered trademarks mentioned herein are the property of their respective owners.
</small></p>
</body>
</html>

View File

@ -0,0 +1,74 @@
ICU Code Tools plugin
Copyright (C) 2010 IBM Corporation and Others. All Rights Reserved.
See license.html for the license file. This file is part of the
ICU project and is under the same license.
Requirements:
Trac (0.11 or 0.12+?)
Repository
Installing:
1. install the plugin - at least the ticketmanager and review portion.
a. There is no source release at this time: I recommend
that you check out this code with svn
and in this directory, run:
"python setup.py develop"
b. In trac.ini under '[components]' add:
icucodetools.review.reviewmodule = enabled
icucodetools.ticketmgr.ticketmanager = enabled
NOTE: DCUT and TKTLIST parts are not working yet.
Don't bother to enable them.
2. in your trac.ini describe how your changesets describe a ticket.
Our changesets look like this: "ticket:1234: fixed the broken code"
We use this regex:
[icucodetools]
ticket_pattern = ^ticket:(\d+)
3. you may need to run trac-admin <environment> upgrade
4. Grant permission of ICUREVIEW_VIEW to whomever you want to
be able to review tickets.
5. Now, any ticket will have something in the top right corner which says:
"No commits" - no commits against this ticket
"Review 1 commits" - there is only one commit. Clicking this link
will just take you to that single changeset.
"Review n commits" - there are more than one commits against this
ticket.
Troubleshooting:
Q: My commits aren't being found!
A: Check the debug log. It will note commits with unparseable messages
Q: How do I resync the commits?
A: Until we implement trac 0.12 changeset listeners, you can do this:
0. back up your path/to/env/db/trac.db
1. $ sqlite3 path/to/env/db/trac.db
2. sqlite> delete from rev2ticket;
3. sqlite> update system set value='-1' where name='icu_tktmgr_youngest';
4. sqlite> .quit
Now the ticket manager will re-sync the first time you hit a ticket.
FILING BUGS/FEATURE REQUESTS:
- Use ICU's trac repository at http://bugs.icu-project.org/trac
- Use the 'infrastructure' component and clearly identify the 'ICU Code Tools
for Trac' when you file the bug.

View File

@ -0,0 +1,4 @@
# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved
[egg_info]
tag_build = dev
tag_svn_revision = true

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2010 IBM and Others. All Rights Reserved.
# All rights reserved.
#
from setuptools import setup, find_packages
PACKAGE = 'IcuCodeTools'
VERSION = '0.0.1'
setup(
name=PACKAGE, version=VERSION,
description='Miscellaneous ICU Extensions to Trac',
author="Steven R. Loomis", author_email="srl@icu-project.org",
license='BSD', url='http://icu-project.org',
packages=find_packages(exclude=['ez_setup', '*.tests*']),
package_data={
'icucodetools': [
'htdocs/css/*.css',
'templates/*.html',
## 'htdocs/img/*.png',
# 'htdocs/js/*.js',
]
},
entry_points = {
'trac.plugins': [
'icucodetools.ticketmgr = icucodetools.ticketmgr',
'icucodetools.review = icucodetools.review',
'icucodetools.tktlist = icucodetools.tktlist',
'icucodetools.dcut = icucodetools.dcut'
]
}
)