incubator-bloodhound-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From g..@apache.org
Subject 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/
Date Mon, 16 Apr 2012 13:00:36 GMT
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



Mime
View raw message