blob: 1cedf15b9875e069c7df14412d8f88b3c4976a35 [file] [log] [blame]
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
r"""Project dashboard for Apache(TM) Bloodhound
Widgets displaying product information (multiproduct).
"""
import itertools
from genshi.builder import tag
from trac.resource import Neighborhood
from trac.util.translation import _
from trac.ticket.model import Milestone, Component, Version
from trac.ticket.query import Query
from bhdashboard.util import WidgetBase, check_widget_name, pretty_wrapper
from multiproduct.env import Product, ProductEnvironment
from multiproduct.hooks import ProductizedHref
__metaclass__ = type
class ProductWidget(WidgetBase):
"""Display products available to the user.
"""
def get_widget_params(self, name):
"""Return a dictionary containing arguments specification for
the widget with specified name.
"""
return {'max' : {'desc' : """Limit the number of products displayed""",
'type' : int},
'cols' : {'desc' : """Number of columns""",
'type' : int}
}
get_widget_params = pretty_wrapper(get_widget_params, check_widget_name)
def _get_product_info(self, product, resource, max_):
penv = ProductEnvironment(self.env, product.prefix)
href = ProductizedHref(self.env, penv.href.base)
results = []
# some queries return a list/tuple, some a generator,
# hence count() to get the result length
def count(iter_):
try:
return len(iter_)
except TypeError:
return sum(1 for _ in iter_)
query = resource['type'].select(penv)
for q in itertools.islice(query, max_):
q.url = href(resource['name'], q.name) if resource.get('hrefurl') \
else Query.from_string(penv, 'order=priority&%s=%s' %
(resource['name'], q.name)).get_href(href)
q.ticket_count = penv.db_query(
"""SELECT COUNT(*) FROM ticket WHERE ticket.%s='%s'
AND ticket.status <> 'closed'""" % (resource['name'], q.name))[0][0]
results.append(q)
# add a '(No <milestone/component/version>)' entry if there are
# tickets without an assigned resource in the product
ticket_count = penv.db_query(
"""SELECT COUNT(*) FROM ticket WHERE %s=''
AND status <> 'closed'""" % (resource['name'],))[0][0]
if ticket_count != 0:
q = resource['type'](penv)
q.name = '(No %s)' % (resource['name'],)
q.url = Query.from_string(penv,
'status=!closed&col=id&col=summary&col=owner'
'&col=status&col=priority&order=priority&%s=' %
(resource['name'],)).get_href(href)
q.ticket_count = ticket_count
results.append(q)
results.sort(key=lambda x: x.ticket_count, reverse=True)
# add a link to the resource list if there are
# more than max resources defined
if count(query) > max_:
q = resource['type'](penv)
q.name = _('... more')
q.ticket_count = None
q.url = href(resource['name']) if resource.get('hrefurl') \
else href.product(product.prefix)
results.append(q)
return results
def render_widget(self, name, context, options):
"""Gather product list and render data in compact view
"""
data = {}
req = context.req
title = ''
params = ('max', 'cols')
max_, cols = self.bind_params(name, options, *params)
if not isinstance(req.perm.env, ProductEnvironment):
for p in Product.select(self.env):
if 'PRODUCT_VIEW' in req.perm(Neighborhood('product', p.prefix)):
for resource in (
{ 'type': Milestone, 'name': 'milestone', 'hrefurl': True },
{ 'type': Component, 'name': 'component' },
{ 'type': Version, 'name': 'version' },
):
setattr(p, resource['name'] + 's',
self._get_product_info(p, resource, max_))
p.owner_link = Query.from_string(self.env, 'status!=closed&'
'col=id&col=summary&col=owner&col=status&col=priority&'
'order=priority&group=product&owner=%s'
% (p._data['owner'] or '', )).get_href(req.href)
data.setdefault('product_list', []).append(p)
title = _('Products')
data['colseq'] = itertools.cycle(xrange(cols - 1, -1, -1)) if cols \
else itertools.repeat(1)
return 'widget_product.html', \
{
'title': title,
'data': data,
'ctxtnav' : [
tag.a(_('More'),
href = context.req.href('products'))],
}, \
context
render_widget = pretty_wrapper(render_widget, check_widget_name)