web/views/iprogress.py
author Emile Anclin <emile.anclin@logilab.fr>
Wed, 01 Apr 2009 19:39:11 +0200
changeset 1206 6db7addc2869
parent 1124 5548b608b7d9
child 1263 01152fffd593
child 1346 38b2cf1b0bb8
permissions -rw-r--r--
[doc] rewrite Workflow definition

"""Specific views for entities implementing IProgress

:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""

__docformat__ = "restructuredtext en"

from logilab.mtconverter import html_escape

from cubicweb.interfaces import IProgress, IMileStone
from cubicweb.schema import display_name
from cubicweb.common.view import EntityView
from cubicweb.common.selectors import implement_interface, accept
from cubicweb.web.htmlwidgets import ProgressBarWidget


class ProgressTableView(EntityView):
    """The progress table view is able to display progress information
    of any object implement IMileStone.

    The default layout is composoed of 7 columns : parent task,
    milestone, state, estimated date, cost, progressbar, and todo_by

    The view accepts an optional ``columns`` paramater that lets you
    remove or reorder some of those columns.

    To add new columns, you should extend this class, define a new
    ``columns`` class attribute and implement corresponding
    build_COLNAME_cell methods

    header_for_COLNAME methods allow to customize header's label
    """
    
    id = 'progress_table_view'
    title = _('task progression')
    __selectors__ = (accept, implement_interface)

    accepts_interfaces = (IMileStone,)

    # default columns of the table
    columns = (_('project'), _('milestone'), _('state'), _('eta_date'),
               _('cost'), _('progress'), _('todo_by'))


    def call(self, columns=None):
        """displays all versions in a table"""
        self.req.add_css('cubicweb.iprogress.css')
        _ = self.req._
        self.columns = columns or self.columns
        ecls = self.vreg.etype_class(self.rset.description[0][0])
        self.w(u'<table class="progress">')
        self.table_header(ecls)
        self.w(u'<tbody>')
        for row in xrange(self.rset.rowcount):
            self.cell_call(row=row, col=0)
        self.w(u'</tbody>')
        self.w(u'</table>')

    def cell_call(self, row, col):
        _ = self.req._
        entity = self.entity(row, col)
        infos = {}
        for col in self.columns:
            meth = getattr(self, 'build_%s_cell' % col, None)
            # find the build method or try to find matching attribute
            if meth:
                content = meth(entity)
            else:
                content = entity.printable_value(col)
            infos[col] = content
        if hasattr(entity, 'progress_class'):
            cssclass = entity.progress_class()
        else:
            cssclass = u''
        self.w(u"""<tr class="%s" onmouseover="addElementClass(this, 'highlighted');"
            onmouseout="removeElementClass(this, 'highlighted')">""" % cssclass)
        line = u''.join(u'<td>%%(%s)s</td>' % col for col in self.columns)
        self.w(line % infos)
        self.w(u'</tr>\n')

    ## header management ######################################################

    def header_for_project(self, ecls):
        """use entity's parent type as label"""
        return display_name(self.req, ecls.parent_type)

    def header_for_milestone(self, ecls):
        """use entity's type as label"""
        return display_name(self.req, ecls.id)
    
    def table_header(self, ecls):
        """builds the table's header"""
        self.w(u'<thead><tr>')
        _ = self.req._
        for column in self.columns:
            meth = getattr(self, 'header_for_%s' % column, None)
            if meth:
                colname = meth(ecls)
            else:
                colname = _(column)
            self.w(u'<th>%s</th>' % html_escape(colname))
        self.w(u'</tr></thead>\n')

    
    ## cell management ########################################################
    def build_project_cell(self, entity):
        """``project`` column cell renderer"""
        project = entity.get_main_task()
        if project:
            return project.view('incontext')
        return self.req._('no related project')

    def build_milestone_cell(self, entity):
        """``milestone`` column cell renderer"""
        return entity.view('incontext')

    def build_state_cell(self, entity):
        """``state`` column cell renderer"""
        return html_escape(self.req._(entity.state))
    
    def build_eta_date_cell(self, entity):
        """``eta_date`` column cell renderer"""
        if entity.finished():
            return self.format_date(entity.completion_date())
        formated_date = self.format_date(entity.initial_prevision_date())
        if entity.in_progress():
            eta_date = self.format_date(entity.eta_date())
            _ = self.req._
            if formated_date:
                formated_date += u' (%s %s)' % (_('expected:'), eta_date)
            else:
                formated_date = u'%s %s' % (_('expected:'), eta_date)
        return formated_date
    
    def build_todo_by_cell(self, entity):
        """``todo_by`` column cell renderer"""
        return u', '.join(p.view('outofcontext') for p in entity.contractors())

    def build_cost_cell(self, entity):
        """``cost`` column cell renderer"""
        _ = self.req._
        pinfo = entity.progress_info()
        totalcost = pinfo.get('estimatedcorrected', pinfo['estimated'])
        missing = pinfo.get('notestimatedcorrected', pinfo.get('notestimated', 0))
        costdescr = []
        if missing:
            # XXX: link to unestimated entities
            costdescr.append(_('%s not estimated') % missing)
        estimated = pinfo['estimated']
        if estimated and estimated != totalcost:
            costdescr.append(_('initial estimation %s') % estimated)
        if costdescr:
            return u'%s (%s)' % (totalcost, ', '.join(costdescr))
        return unicode(totalcost)
    
    def build_progress_cell(self, entity):
        """``progress`` column cell renderer"""
        progress =  u'<div class="progress_data">%s (%.2f%%)</div>' % (
            entity.done, entity.progress())
        return progress + entity.view('progressbar')


class InContextProgressTableView(ProgressTableView):
    """this views redirects to ``progress_table_view`` but removes
    the ``project`` column
    """
    id = 'ic_progress_table_view'
    
    def call(self):
        view = self.vreg.select_view('progress_table_view', self.req, self.rset)
        columns = list(view.columns)
        try:
            columns.remove('project')
        except ValueError:
            self.info('[ic_progress_table_view] could not remove project from columns')
        view.dispatch(w=self.w, columns=columns)


class ProgressBarView(EntityView):
    """displays a progress bar"""
    id = 'progressbar'
    title = _('progress bar')
    __selectors__ = (accept, implement_interface)

    accepts_interfaces = (IProgress,)

    def cell_call(self, row, col):
        self.req.add_css('cubicweb.iprogress.css')
        entity = self.entity(row, col)
        widget = ProgressBarWidget(entity.done, entity.todo,
                                   entity.revised_cost)
        self.w(widget.render())