| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | """ |
|---|
| 3 | Progress meter macro plugin for Trac. |
|---|
| 4 | """ |
|---|
| 5 | |
|---|
| 6 | from genshi.builder import tag |
|---|
| 7 | |
|---|
| 8 | from trac.core import * |
|---|
| 9 | from trac.wiki.api import IWikiMacroProvider, parse_args |
|---|
| 10 | from trac.web.chrome import add_stylesheet, ITemplateProvider |
|---|
| 11 | from trac.wiki.macros import WikiMacroBase |
|---|
| 12 | from trac.ticket.query import Query |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | __all__ = ['ProgressMeterMacro'] |
|---|
| 16 | |
|---|
| 17 | class ProgressMeterMacro(WikiMacroBase): |
|---|
| 18 | """ |
|---|
| 19 | ProgressMeter (wiki macro) plugin |
|---|
| 20 | Usage and installation instructions can be found at: |
|---|
| 21 | http://trac-hacks.org/wiki/ProgressMeterMacro |
|---|
| 22 | """ |
|---|
| 23 | implements(IWikiMacroProvider, ITemplateProvider) |
|---|
| 24 | |
|---|
| 25 | # IWikiMacroProvider methods |
|---|
| 26 | def expand_macro(self, formatter, name, content): |
|---|
| 27 | # Stripping content -- allows using spaces within arguments -- |
|---|
| 28 | # and checking whether there is not argument 'status' |
|---|
| 29 | content = ','.join([x.strip() for x in content.split(',') if not x.strip().startswith('status')]) |
|---|
| 30 | |
|---|
| 31 | # Parsing arguments (copied from ticket/query.py from standard trac distribution) |
|---|
| 32 | # suggested by dhellman |
|---|
| 33 | req = formatter.req |
|---|
| 34 | query_string = '' |
|---|
| 35 | argv, kwargs = parse_args(content, strict=False) |
|---|
| 36 | if len(argv) > 0 and not 'ticket_value' in kwargs: # 0.10 compatibility hack |
|---|
| 37 | kwargs['ticket_value'] = argv[0] |
|---|
| 38 | |
|---|
| 39 | ticket_value = kwargs.pop('ticket_value', 'list').strip().lower() |
|---|
| 40 | query_string = '&'.join(['%s=%s' % item for item in kwargs.iteritems()]) |
|---|
| 41 | cnt = {} |
|---|
| 42 | qs_add = {'total': '', 'closed': '&status=closed', 'active': '&status=!closed'} |
|---|
| 43 | for key in ('closed', 'total'): |
|---|
| 44 | query = Query.from_string(self.env, query_string + qs_add[key]) |
|---|
| 45 | tickets = query.execute(req) |
|---|
| 46 | cnt[key] = (tickets and len(tickets) or 0) |
|---|
| 47 | |
|---|
| 48 | # calculate the number of active tickets |
|---|
| 49 | cnt['active'] = cnt['total'] - cnt['closed'] |
|---|
| 50 | |
|---|
| 51 | # Getting percent of active/closed tickets |
|---|
| 52 | percents = {'closed': float(cnt['closed']) / float(cnt['total'])} |
|---|
| 53 | percents['active'] = 1 - percents['closed'] |
|---|
| 54 | |
|---|
| 55 | add_stylesheet(formatter.req, 'progressmeter/css/progressmeter.css') |
|---|
| 56 | |
|---|
| 57 | main_div = tag.div(class_='milestone') |
|---|
| 58 | |
|---|
| 59 | # Add title above progress bar |
|---|
| 60 | argv and main_div.children.append(tag.h2(argv)) |
|---|
| 61 | |
|---|
| 62 | # Add progress bar |
|---|
| 63 | table = tag.table(class_='progress')(tag.tr()) |
|---|
| 64 | |
|---|
| 65 | for key in reversed(percents.keys()): |
|---|
| 66 | # reversing because we want the closed tickets to be processed firstly |
|---|
| 67 | percents[key] = unicode(int(percents[key] * 100)) + u'%' |
|---|
| 68 | table.children[0](tag.td(style='width: '+percents[key], class_=key) |
|---|
| 69 | (tag.a(title="%i of %i tickets %s" % (cnt[key], cnt['total'], key.title()), |
|---|
| 70 | href="%s?%s" % (formatter.href.query(),query_string + qs_add[key])))) |
|---|
| 71 | main_div.children.append(table) |
|---|
| 72 | |
|---|
| 73 | # Add percentage displaied to the right of the progress bar |
|---|
| 74 | percent_para = tag.p(class_='percent')(percents['closed']) |
|---|
| 75 | main_div.children.append(percent_para) |
|---|
| 76 | |
|---|
| 77 | # Add ticket count below progress bar |
|---|
| 78 | ticket_count = tag.dl() |
|---|
| 79 | |
|---|
| 80 | for key in qs_add.keys(): |
|---|
| 81 | ticket_count.children.append(tag.dt()("%s tickets:" % key.title())) |
|---|
| 82 | ticket_count.children.append(tag.dd()(tag.a(str(cnt[key]), |
|---|
| 83 | href="%s?%s" % (formatter.href.query(), query_string + qs_add[key])))) |
|---|
| 84 | main_div.children.append(ticket_count) |
|---|
| 85 | |
|---|
| 86 | return main_div |
|---|
| 87 | |
|---|
| 88 | |
|---|
| 89 | # ITemplateProvider methods |
|---|
| 90 | def get_htdocs_dirs(self): |
|---|
| 91 | """ Makes the 'htdocs' folder available for Trac. """ |
|---|
| 92 | from pkg_resources import resource_filename |
|---|
| 93 | return [('progressmeter', resource_filename('progressmeter', 'htdocs'))] |
|---|
| 94 | |
|---|
| 95 | def get_templates_dirs(self): |
|---|
| 96 | return [] # must return an iterable |
|---|
| 97 | |
|---|