You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@bloodhound.apache.org by Gary <ga...@wandisco.com> on 2012/04/16 15:15:35 UTC
Re: svn commit: r1326583 - in /incubator/bloodhound/trunk/bloodhound_dashboard:
./ bhdashboard/ bhdashboard/layouts/templates/ bhdashboard/web_ui/ bhdashboard/web_ui/templates/
bhdashboard/web_ui/ticket/ bhdashboard/widgets/ bhdashboard/widgets/templates/
Hi,
This commit is a little on the large side as it includes a refactor of
the dashboard web_ui, but it provides a timeline for the milestone view
and the beginnings of a progress bar.
Cheers,
Gary
On 04/16/2012 02:00 PM, gjm@apache.org wrote:
> Author: gjm
> Date: Mon Apr 16 13:00:35 2012
> New Revision: 1326583
>
> URL: http://svn.apache.org/viewvc?rev=1326583&view=rev
> Log:
> dashboard: Activity feed in milestone view using Bloodhound widget markup; initial version of progress bar widget
>
> Added:
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/_json.py (with props)
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/__init__.py (with props)
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html (with props)
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/__init__.py (with props)
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/roadmap.py (with props)
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html (with props)
> Removed:
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui.py
> Modified:
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/layouts/templates/widget_macros.html
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py
> incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/ticket.py
> incubator/bloodhound/trunk/bloodhound_dashboard/setup.py
>
> Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/_json.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/_json.py?rev=1326583&view=auto
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/_json.py (added)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/_json.py Mon Apr 16 13:00:35 2012
> @@ -0,0 +1,30 @@
> +#!/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
> +
> +Provide a single namespace to access JSON functions.
> +"""
> +
> +try :
> + from json import *
> +except ImportError:
> + from simplejson import *
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/_json.py
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Modified: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/layouts/templates/widget_macros.html
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/layouts/templates/widget_macros.html?rev=1326583&r1=1326582&r2=1326583&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/layouts/templates/widget_macros.html (original)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/layouts/templates/widget_macros.html Mon Apr 16 13:00:35 2012
> @@ -2,8 +2,11 @@
> xmlns="http://www.w3.org/1999/xhtml"
> xmlns:py="http://genshi.edgewall.org/"
> xmlns:xi="http://www.w3.org/2001/XInclude"
> + xmlns:bh="http://issues.apache.org/bloodhound/wiki/Ui/Dashboard"
> py:strip="">
>
> +<!-- Helper functions (py:def) -->
> +
> <div py:def="widget_container(w)" role="application">
> <h1 style="display: inline;">${w.title}</h1>
> <py:if test="w.ctxtnav">
> @@ -27,4 +30,70 @@
> <br/>
> ${w.content}
> </div>
> +
> +<!-- Widget markup (py:match) -->
> +
> +<div py:def="bhnotfound()" class="alert alert-error">
> +<span class="label label-important">Error</span>
> + Dashboard data is missing .
> + Is<code>bhdashboard.web_ui.DashboardModule</code> component disabled?
> +</div>
> +
> +<!--
> + Sample layout tag
> +
> +<bh:layout type="LayoutName">
> +<bh:schema>
> + JSON object describing positioning and ...
> +</bh:schema>
> +<bh:widgets>
> +<bh:w id="simple_widget" type="WidgetName" altlinks="false">
> +<bh:args>
> +<bh:arg name="arg1">value1</bh:arg>
> +<bh:arg name="arg2">value2</bh:arg>
> +</bh:args>
> +</bh:w>
> +<bh:l id="nested_layout" type="LayoutName">
> +<bh:schema>
> + JSON object describing positioning and ...
> +</bh:schema>
> +<bh:widgets>
> + ... Same as before ...
> +</bh:widgets>
> +</bh:l>
> +</bh:widgets>
> +</bh:layout>
> + -->
> +<py:match path="bh:layout">
> +<py:choose test="">
> +<py:when test="bhdb">
> + ${bhdb.embed_layout(context, layout=select('@type'), schema=select('bh:schema'), widgets=select('bh:widgets'))}
> +</py:when>
> +<py:otherwise>
> + ${bhnotfound()}
> +</py:otherwise>
> +</py:choose>
> +</py:match>
> +<!--
> + Sample widget tag
> +
> +<bh:widget id="simple_widget" type="WidgetName" altlinks="false">
> +<bh:args>
> +<bh:arg name="arg1">value1</bh:arg>
> +<bh:arg name="arg2">value2</bh:arg>
> +</bh:args>
> +</bh:widget>
> +
> + -->
> +<py:match path="bh:widget">
> +<py:choose test="">
> +<py:when test="bhdb"
> + py:with="wnm = unicode(select('@type')); args = unicode(select('bh:args/text()')) or {}; altlinks = unicode(select('@altlinks')) != 'false'; ctxtnav = unicode(select('@ctxtnav')) != 'false';">
> + ${widget_container(bhdb.expand_widget(context, dict(args=[wnm, None, {'args': args}], altlinks=altlinks, ctxtnav=ctxtnav)))}
> +</py:when>
> +<py:otherwise>
> + ${bhnotfound()}
> +</py:otherwise>
> +</py:choose>
> +</py:match>
> </html>
>
> Modified: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py?rev=1326583&r1=1326582&r2=1326583&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py (original)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/util.py Mon Apr 16 13:00:35 2012
> @@ -144,6 +144,24 @@ def pretty_wrapper(wrapped, *decorators)
> return update_wrapper(wrapper, wrapped)
>
> #------------------------------------------------------
> +# Trac core
> +#------------------------------------------------------
> +
> +def resolve_ep_class(interface, component, clsnm, **kwargs):
> + r"""Retrieve the class implementing an interface (by name)
> + """
> + ep = ExtensionPoint(interface)
> + for c in ep.extensions(component):
> + if c.__class__.__name__ == clsnm :
> + return c
> + else:
> + if 'default' in kwargs:
> + return kwargs['default']
> + else:
> + raise LookupError('No match found for class %s implementing %s' %
> + (clsnm, interface) )
> +
> +#------------------------------------------------------
> # Context information
> #------------------------------------------------------
>
>
> Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/__init__.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/__init__.py?rev=1326583&view=auto
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/__init__.py (added)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/__init__.py Mon Apr 16 13:00:35 2012
> @@ -0,0 +1,290 @@
> +#!/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
> +
> +Implementing dashboard user interface.
> +"""
> +
> +__metaclass__ = type
> +
> +from itertools import izip
> +import pkg_resources
> +import re
> +
> +from genshi.builder import tag
> +from trac.core import Component, implements
> +from trac.config import Option, IntOption
> +from trac.mimeview.api import Context
> +from trac.util.translation import _
> +from trac.ticket.query import QueryModule
> +from trac.ticket.report import ReportModule
> +from trac.util.compat import groupby
> +from trac.web.api import IRequestHandler, IRequestFilter
> +from trac.web.chrome import add_ctxtnav, add_stylesheet, Chrome, \
> + INavigationContributor, ITemplateProvider
> +
> +from bhdashboard.api import DashboardSystem
> +from bhdashboard import json
> +
> +class DashboardModule(Component):
> + """Web frontend for dashboard infrastructure.
> + """
> + implements(IRequestHandler, IRequestFilter, INavigationContributor, \
> + ITemplateProvider)
> +
> + mainnav_label = Option('dashboard', 'mainnav', 'Dashboard', \
> + """Dashboard label in mainnav""")
> + default_widget_height = IntOption('widgets', 'default_height', 320, \
> + """Default widget height in pixels""")
> +
> + # IRequestFilter methods
> +
> + def pre_process_request(self, req, handler):
> + """Always returns the request handler unchanged.
> + """
> + return handler
> +
> + def post_process_request(self, req, template, data, content_type):
> + """Inject dashboard helpers in data.
> + """
> + if data is not None :
> + data['bhdb'] = DashboardChrome(self.env)
> + return template, data, content_type
> +
> + # IRequestHandler methods
> + def match_request(self, req):
> + """Match dashboard prefix"""
> + return bool(re.match(r'^/dashboard(/.)?', req.path_info))
> +
> + def process_request(self, req):
> + """Initially this will render static widgets. With time it will be
> + more and more dynamic and flexible.
> + """
> + if self.env[QueryModule] is not None:
> + add_ctxtnav(req, _('Custom Query'), req.href.query())
> + if self.env[ReportModule] is not None:
> + add_ctxtnav(req, _('Reports'), req.href.report())
> + template, layout_data = self.expand_layout_data(req,
> + 'bootstrap_grid', self.DASHBOARD_SCHEMA)
> + widgets = self.expand_widget_data(req, layout_data)
> + return template, {
> + 'context' : Context.from_request(req),
> + 'layout' : layout_data,
> + 'widgets' : widgets,
> + 'title' : _(self.mainnav_label),
> + 'default' : {
> + 'height' : self.default_widget_height or None
> + }
> + }, None
> +
> + # INavigationContributor methods
> + def get_active_navigation_item(self, req):
> + """Highlight dashboard mainnav item.
> + """
> + return 'dashboard'
> +
> + def get_navigation_items(self, req):
> + """Add an item in mainnav to access global dashboard
> + """
> + if 'DASHBOARD_VIEW' in req.perm:
> + yield ('mainnav', 'dashboard',
> + tag.a(_(self.mainnav_label), href=req.href.dashboard()))
> +
> + # ITemplateProvider methods
> + def get_htdocs_dirs(self):
> + """List `htdocs` dirs for dashboard and widgets.
> + """
> + resource_filename = pkg_resources.resource_filename
> + return [
> + ('dashboard', resource_filename('bhdashboard', 'htdocs')),
> + #('widgets', resource_filename('bhdashboard.widgets', 'htdocs'))
> + ('layouts', resource_filename('bhdashboard.layouts', 'htdocs'))
> + ]
> +
> + def get_templates_dirs(self):
> + """List `templates` folders for dashboard and widgets.
> + """
> + resource_filename = pkg_resources.resource_filename
> + return [resource_filename('bhdashboard.layouts', 'templates'),
> + resource_filename('bhdashboard.web_ui', 'templates'),
> + resource_filename('bhdashboard.widgets', 'templates')]
> +
> + # Temp vars
> + DASHBOARD_SCHEMA = {
> + 'div' : [
> + {
> + '_class' : 'row',
> + 'div' : [
> + {
> + '_class' : 'span8',
> + 'widgets' : [0]
> + },
> + {
> + '_class' : 'span4',
> + 'widgets' : [1]
> + }
> + ]
> + }
> + ],
> + 'widgets' : [
> + {
> + 'args' : ['Container', None,
> + {'args' : {'layout' : 'bootstrap_btnbar',
> + 'schema' : '''
> + {
> + "toolbar" : [
> + ["Products", null],
> + ["My Tickets", 2],
> + ["All tickets", 1],
> + ["|", null],
> + ["Projects", null],
> + ["Components", 0]
> + ],
> + "active" : 1,
> + "widgets" : [
> + {
> + "args" : [
> + "TicketFieldCloud",
> + null,
> + {"args" : {
> + "field" : "component",
> + "verbose" : true}}]
> + },
> + {
> + "args" : [
> + "TicketQuery", null,
> + {"args" : {
> + "max" : 10,
> + "query" : "''' +
> + 'status!=closed&group=time&col=id&col=summary&col=owner' \
> + '&col=status&col=priority&order=priority&groupdesc=1&desc=1' +
> + '''",
> + "title" : "All Tickets"}
> + }],
> + "altlinks" : false
> + },
> + {
> + "args" : [
> + "TicketQuery", null,
> + {"args" : {
> + "max" : 10,
> + "query" : "''' +
> + 'status!=closed&group=time&col=id&col=summary&col=owner' \
> + '&col=status&col=priority&order=priority&groupdesc=1&desc=1' \
> + '&owner=$USER' +
> + '''",
> + "title" : "My Tickets"}
> + }],
> + "altlinks" : false
> + }
> + ]
> + }
> + ''',
> + 'title' : _("Dashboard")
> + }
> + }]
> + },
> + {
> + 'args' : ['Timeline', None, {'args' : {}}]
> + },
> + ]
> + }
> +
> + # Public API
> + def expand_layout_data(self, req, layout_name, schema):
> + """Determine the template needed to render a specific layout
> + and the data needed to place the widgets at expected
> + location.
> + """
> + layout = DashboardSystem(self.env).resolve_layout(layout_name)
> +
> + ctx = Context.from_request(req)
> + template = layout.expand_layout(layout_name, ctx, {
> + 'schema' : schema
> + })['template']
> + return template, schema
> +
> + def expand_widget_data(self, req, schema):
> + """Expand raw widget data and format it for use in template
> + """
> + # TODO: Implement dynamic dashboard specification
> + widgets_spec = schema.pop('widgets', [])
> + widgets_index = dict([k, list(v)] for k,v in \
> + groupby(widgets_spec, lambda w : w['args'][0]))
> + ctx = Context.from_request(req)
> + try :
> + for wp in DashboardSystem(self.env).widget_providers:
> + for wnm in wp.get_widgets():
> + substitutions = widgets_index.pop(wnm, [])
> + i = -1
> + for i, w in enumerate(substitutions):
> + w['c'] = wp
> + w['args'][1] = ctx
> + self.log.debug('Widget %s (%s substitutions)', wnm, i + 1)
> + if not widgets_index:
> + raise StopIteration("No more widgets")
> + except StopIteration:
> + pass
> + if len(widgets_index)> 0:
> + raise LookupError('Unknown provider for widgets %s',
> + ' , '.join(widgets_index.keys()))
> + chrome = Chrome(self.env)
> + render = chrome.render_template
> + data_strm = (w['c'].render_widget(*w['args']) for w in widgets_spec)
> + return [{'title' : data['title'],
> + 'content' : render(wctx.req, template, data['data'], fragment=True),
> + 'ctxtnav' : w.get('ctxtnav', True) and data.get('ctxtnav') or None,
> + 'altlinks' : w.get('altlinks', True) and data.get('altlinks') or None} \
> + for w, (template, data, wctx) in izip(widgets_spec, data_strm)]
> +
> +class DashboardChrome:
> + """Helper functions providing access to dashboard infrastructure
> + in Genshi templates. Useful to reuse layouts and widgets across
> + website.
> + """
> + def __init__(self, env):
> + self.env = env
> +
> + def embed_layout(self, context, **kwargs):
> + """Render layout and widgets
> +
> + :param context: Rendering context
> + :param layout: Identifier of target layout
> + :param schema: Data describing widget positioning
> + :param widgets: Widgets definition
> + """
> + dbmod = DashboardModule(self.env)
> + raise NotImplementedError("DashboardChrome.embed_layout")
> +
> + def expand_widget(self, context, widget):
> + """Render single widget
> +
> + :param context: Rendering context
> + :param widget: Widget definition
> + """
> + dbmod = DashboardModule(self.env)
> + if isinstance(widget['args'], basestring):
> + widgets['args'] = json.loads(widget['args'])
> + return dbmod.expand_widget_data(
> + context.req,
> + {'widgets' : [widget]}
> + )[0]
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/__init__.py
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html?rev=1326583&view=auto
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html (added)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html Mon Apr 16 13:00:35 2012
> @@ -0,0 +1,28 @@
> +<!DOCTYPE html
> + PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
> + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
> +<html xmlns="http://www.w3.org/1999/xhtml"
> + xmlns:py="http://genshi.edgewall.org/"
> + xmlns:bh="http://issues.apache.org/bloodhound/wiki/Ui/Dashboard"
> + xmlns:xi="http://www.w3.org/2001/XInclude">
> +<xi:include href="layout.html" />
> +<xi:include href="widget_macros.html" />
> +<head>
> +<title>Milestone ${milestone.name}</title>
> +<link py:if="'MILESTONE_MODIFY' in perm(milestone.resource)" rel="alternate" type="application/x-wiki"
> + title="Edit this milestone" href="${href.milestone(milestone.name, action='edit')}" />
> +</head>
> +
> +<body>
> +<div class="row">
> +<div class="span8">
> +<div class="alert">
> +<span class="label label-warning">TODO</span> Include milestone data.
> +</div>
> +</div>
> +<div class="span4">
> +<bh:widget type="Timeline" />
> +</div>
> +</div>
> +</body>
> +</html>
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/templates/bhmilestone.html
> ------------------------------------------------------------------------------
> svn:mime-type = text/html
>
> Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/__init__.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/__init__.py?rev=1326583&view=auto
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/__init__.py (added)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/__init__.py Mon Apr 16 13:00:35 2012
> @@ -0,0 +1,25 @@
> +#!/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
> +
> +Overriden version of Trac ticket interface
> +"""
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/__init__.py
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/roadmap.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/roadmap.py?rev=1326583&view=auto
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/roadmap.py (added)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/roadmap.py Mon Apr 16 13:00:35 2012
> @@ -0,0 +1,61 @@
> +#!/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"""Roadmap view for Apache(TM) Bloodhound
> +
> +Customizing roadmap user interface.
> +"""
> +
> +__metaclass__ = type
> +
> +from itertools import izip
> +import pkg_resources
> +import re
> +
> +from genshi.builder import tag
> +from trac.core import Component, implements
> +from trac.mimeview.api import Context
> +from trac.ticket.roadmap import MilestoneModule, RoadmapModule
> +from trac.util.translation import _
> +from trac.web.api import IRequestFilter
> +from trac.web.chrome import add_ctxtnav, add_stylesheet
> +
> +from bhdashboard.api import DashboardSystem
> +
> +class BloodhoundMilestoneModule(Component):
> + """Override default milestone views.
> + """
> + implements(IRequestFilter)
> +
> + # IRequestFilter methods
> +
> + def pre_process_request(self, req, handler):
> + """Always returns the request handler unchanged.
> + """
> + return handler
> +
> + def post_process_request(self, req, template, data, content_type):
> + """Customize milestone view.
> + """
> + mdl = self.env[MilestoneModule]
> + if mdl is not None and mdl.match_request(req):
> + return 'bhmilestone.html', data, content_type
> + else:
> + return template, data, content_type
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/web_ui/ticket/roadmap.py
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Modified: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py?rev=1326583&r1=1326582&r2=1326583&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py (original)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py Mon Apr 16 13:00:35 2012
> @@ -32,7 +32,7 @@ from genshi.builder import tag
> from trac.core import implements, TracError
> from trac.mimeview.api import Context
> from trac.resource import Resource, ResourceNotFound
> -from trac.ticket.query import QueryModule
> +from trac.ticket.query import Query, QueryModule
> from trac.util.translation import _
> from trac.web.api import RequestDone
>
> @@ -136,3 +136,11 @@ class TicketQueryWidget(WidgetBase):
>
> render_widget = pretty_wrapper(render_widget, check_widget_name)
>
> +#--------------------------------------
> +# Query functions and methods
> +#--------------------------------------
> +
> +def exec_query(env, req, qstr='status!=closed'):
> + """ Perform a ticket query, returning a list of ticket ID's.
> + """
> + return Query.from_string(env, qstr).execute(req)
>
> Added: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html?rev=1326583&view=auto
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html (added)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html Mon Apr 16 13:00:35 2012
> @@ -0,0 +1,8 @@
> +
> +<div class="well"
> + xmlns="http://www.w3.org/1999/xhtml"
> + xmlns:py="http://genshi.edgewall.org/"
> + xmlns:xi="http://www.w3.org/2001/XInclude">
> + $legend
> +<div class="pull-right">${'%d%%' % stats.done_percent}</div>
> +</div>
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_progress.html
> ------------------------------------------------------------------------------
> svn:mime-type = text/html
>
> Modified: incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/ticket.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/ticket.py?rev=1326583&r1=1326582&r2=1326583&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/ticket.py (original)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/ticket.py Mon Apr 16 13:00:35 2012
> @@ -29,13 +29,18 @@ from itertools import imap, islice
> from genshi.builder import tag
> from trac.core import implements, TracError
> from trac.ticket.api import TicketSystem
> +from trac.ticket.roadmap import apply_ticket_permissions, get_ticket_stats, \
> + ITicketGroupStatsProvider, RoadmapModule
> from trac.util.translation import _
> +from trac.web.chrome import add_stylesheet
>
> from bhdashboard.api import DateField, EnumField, InvalidWidgetArgument, \
> ListField
> +from bhdashboard.widgets.query import exec_query
> from bhdashboard.util import WidgetBase, check_widget_name, \
> - dummy_request, merge_links, minmax, \
> - pretty_wrapper, trac_version, trac_tags
> + dummy_request, merge_links, minmax, \
> + pretty_wrapper, resolve_ep_class, \
> + trac_version, trac_tags
>
> class TicketFieldCloudWidget(WidgetBase):
> """Display a tag cloud representing frequency of values assigned to
> @@ -116,3 +121,70 @@ class TicketFieldCloudWidget(WidgetBase)
>
> render_widget = pretty_wrapper(render_widget, check_widget_name)
>
> +class TicketGroupStatsWidget(WidgetBase):
> + """Display progress bar illustrating statistics gathered on a group
> + of tickets.
> + """
> + def get_widget_params(self, name):
> + """Return a dictionary containing arguments specification for
> + the widget with specified name.
> + """
> + return {
> + 'query' : {
> + 'default' : 'status!=closed',
> + 'desc' : """Query string""",
> + },
> + 'stats_provider' : {
> + 'desc' : """Name of the component implementing
> + `ITicketGroupStatsProvider`, which is used to collect statistics
> + on groups of tickets.""",
> + 'default' : 'DefaultTicketGroupStatsProvider'
> + },
> + 'skin' : {
> + 'desc' : """Look and feel of the progress bar""",
> + 'type' : EnumField('info', 'success', 'warning',
> + 'danger',
> + 'info-stripped', 'success-stripped',
> + 'warning-stripped', 'danger-stripped')
> + },
> + 'title' : {
> + 'desc' : """Widget title""",
> + },
> + 'legend' : {
> + 'desc' : """Text on top of the progress bar""",
> + },
> + 'desc' : {
> + 'desc' : """Descriptive (wiki) text""",
> + },
> + }
> + get_widget_params = pretty_wrapper(get_widget_params, check_widget_name)
> +
> + def render_widget(self, name, context, options):
> + """Prepare ticket stats
> + """
> + req = context.req
> + params = ('query', 'stats_provider', 'skin', 'title', 'legend', 'desc')
> + qstr, pnm, skin, title, legend, desc = \
> + self.bind_params(name, options, *params)
> + statsp = resolve_ep_class(ITicketGroupStatsProvider, self, pnm,
> + default=RoadmapModule(self.env).stats_provider)
> + skin = (skin or '').split('-', 2)
> + progress_css = 'progress ' + ' '.join('progress-'+c for c in skin if c)
> +
> + tickets = exec_query(self.env, req, qstr)
> + tickets = apply_ticket_permissions(self.env, req, tickets)
> + stat = get_ticket_stats(self.stats_provider, tickets)
> +
> + add_stylesheet('dashboard/bootstrap.css')
> + return 'widget_progress.html', \
> + {
> + 'title' : title,
> + 'data' : dict(
> + desc=desc,
> + legend=legend,
> + stats=stat,
> + ),
> + }, \
> + context
> +
> + render_widget = pretty_wrapper(render_widget, check_widget_name)
>
> Modified: incubator/bloodhound/trunk/bloodhound_dashboard/setup.py
> URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/bloodhound_dashboard/setup.py?rev=1326583&r1=1326582&r2=1326583&view=diff
> ==============================================================================
> --- incubator/bloodhound/trunk/bloodhound_dashboard/setup.py (original)
> +++ incubator/bloodhound/trunk/bloodhound_dashboard/setup.py Mon Apr 16 13:00:35 2012
> @@ -102,6 +102,14 @@ PKG_INFO = {'bhdashboard' : ('bhdashboar
> # Package data
> ['templates/*', 'htdocs/*'],
> ),
> + 'bhdashboard.web_ui' : ('bhdashboard/web_ui', # Package dir
> + # Package data
> + ['templates/*', 'htdocs/*'],
> + ),
> + 'bhdashboard.web_ui.ticket' : ('bhdashboard/web_ui/ticket', # Package dir
> + # Package data
> + [],
> + ),
> 'bhdashboard.tests' : ('bhdashboard/tests', # Package dir
> # Package data
> ['data/**'],
> @@ -113,6 +121,7 @@ ENTRY_POINTS = r"""
> bhdashboard.api = bhdashboard.api
> bhdashboard.layouts.bootstrap = bhdashboard.layouts.bootstrap
> bhdashboard.web_ui = bhdashboard.web_ui
> + bhdashboard.web_ui.ticket.roadmap = bhdashboard.web_ui.ticket.roadmap
> bhdashboard.widgets.containers = bhdashboard.widgets.containers
> bhdashboard.widgets.query = bhdashboard.widgets.query
> bhdashboard.widgets.report = bhdashboard.widgets.report
>
>