Author: matevz Date: Thu Mar 21 13:25:19 2013 New Revision: 1459300 URL: http://svn.apache.org/r1459300 Log: #325 - Multiproduct UI: Dashboard (versions and components added to product well)
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/product.py incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/templates/widget_product.html incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/product.py URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/product.py?rev=1459300&r1=1459299&r2=1459300&view=diff ============================================================================== --- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/product.py (original) +++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/product.py Thu Mar 21 13:25:19 2013 @@ -24,8 +24,10 @@ r"""Project dashboard for Apache(TM) Blo Widgets displaying product information (multiproduct). """ +import itertools + from trac.util.translation import _ -from trac.ticket.model import Milestone +from trac.ticket.model import Milestone, Component, Version from trac.ticket.query import Query from bhdashboard.util import WidgetBase, check_widget_name, pretty_wrapper @@ -52,49 +54,57 @@ class ProductWidget(WidgetBase): get_widget_params = pretty_wrapper(get_widget_params, check_widget_name) - def _get_product_milestones(self, req, product, max_): - def new_milestone(env, name, url, ticket_count=None): - m = Milestone(env) - m.name = name - m.url = url - m.ticket_count = ticket_count - return m - + def _get_product_info(self, product, resource, max_): penv = ProductEnvironment(self.env, product.prefix) href = ProductizedHref(self.env, penv.href.base) - milestones = [] + results = [] - mquery = Milestone.select(penv) - for m in mquery[:max_]: - m.url = href.milestone(m.name) - m.ticket_count = penv.db_query( - 'SELECT count(*) FROM ticket WHERE milestone="%s" ' - 'AND status <> "closed"' % m.name)[0][0] - milestones.append(m) + # 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 %s="%s" ' + 'AND status <> "closed"' % (resource['name'], q.name))[0][0] + results.append(q) - # add a '(No milestone)' entry if there are tickets - # without an assigned milestone in the product + # 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 milestone="" ' - 'AND status <> "closed"')[0][0] + 'SELECT COUNT(*) FROM ticket WHERE %s="" ' + 'AND status <> "closed"' % (resource['name'],))[0][0] if ticket_count != 0: - milestones.append(new_milestone(penv, _('(No milestone)'), - Query.from_string(penv, - 'status=!closed&col=id&col=summary&col=owner' - '&col=status&col=priority&order=priority&milestone=' - ).get_href(href), - ticket_count)) - - milestones.sort(key=lambda x: x.ticket_count, reverse=True) - - # add a link to the milestone list if there are - # more than max milestones defined - if len(mquery) > max_: - milestones.append(new_milestone(penv, _('... more'), - href.milestone(), None)) - - return milestones + 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 @@ -108,7 +118,13 @@ class ProductWidget(WidgetBase): if not isinstance(req.perm.env, ProductEnvironment): for p in Product.select(self.env): if 'PRODUCT_VIEW' in req.product_perm(p.prefix): - p.milestones = self._get_product_milestones(req, p, max_) + 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_)) data.setdefault('product_list', []).append(p) title = _('Products') Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/templates/widget_product.html URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/templates/widget_product.html?rev=1459300&r1=1459299&r2=1459300&view=diff ============================================================================== --- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/templates/widget_product.html (original) +++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/widgets/templates/widget_product.html Thu Mar 21 13:25:19 2013 @@ -24,10 +24,11 @@ <div py:if="product_list" class="row" id="products"> <div py:for="p in product_list" class="span3 well product-well"> <h4>☆ <a href="${req.href.products(p.prefix)}">$p.name ($p.prefix)</a></h4> - <h5 class="milestone-title">Milestones</h5> + + <h5>Milestones</h5> <py:choose test=""> <py:when test="p.milestones"> - <ul py:if="p.milestones" class="milestones"> + <ul class="subset"> <li py:for="m in p.milestones"> <a href="$m.url">$m.name<py:if test="m.ticket_count is not None"> ($m.ticket_count)</py:if></a> </li> @@ -37,6 +38,34 @@ (No milestones for this product) </py:otherwise> </py:choose> + + <h5>Components</h5> + <py:choose test=""> + <py:when test="p.components"> + <ul class="subset"> + <li py:for="c in p.components"> + <a href="$c.url">$c.name<py:if test="c.ticket_count is not None"> ($c.ticket_count)</py:if></a> + </li> + </ul> + </py:when> + <py:otherwise> + (No components for this product) + </py:otherwise> + </py:choose> + + <h5>Versions</h5> + <py:choose test=""> + <py:when test="p.versions"> + <ul class="subset"> + <li py:for="v in p.versions"> + <a href="$v.url">$v.name<py:if test="v.ticket_count is not None"> ($v.ticket_count)</py:if></a> + </li> + </ul> + </py:when> + <py:otherwise> + (No versions for this product) + </py:otherwise> + </py:choose> </div> </div> </div> Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css?rev=1459300&r1=1459299&r2=1459300&view=diff ============================================================================== --- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css (original) +++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css Thu Mar 21 13:25:19 2013 @@ -1123,9 +1123,9 @@ table.wiki th { padding: 10px; } -#products .milestones { +#products .subset { list-style-type: none; - margin: 0; + margin: 0 0 10px 0; padding: 0; }