You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by gj...@apache.org on 2012/04/16 15:00:36 UTC
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/
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
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/
Posted by Gary <ga...@wandisco.com>.
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
>
>