Commit 2228db0b authored by Rémi Cardona's avatar Rémi Cardona
Browse files

Drop iprogress code (closes #2777628)

parent 8ca1a0da5a29
......@@ -6,3 +6,11 @@ API changes
* The SIOC views and adapters have been removed from CubicWeb and moved to the
`sioc` cube.
Deprecated Code Drops
----------------------
* The progress views and adapters have been removed from CubicWeb. These
classes were deprecated since 3.14.0. They are still available in the
`iprogress` cube.
......@@ -136,7 +136,6 @@ INDEX_IN_ORDER = [
'cubicweb.preferences',
'cubicweb.edition',
'cubicweb.reledit',
'cubicweb.iprogress',
'cubicweb.rhythm',
'cubicweb.gmap',
'cubicweb.timeline-ext',
......
......@@ -404,117 +404,3 @@ class adapter_deprecated(view.auto_unwrap_bw_compat):
"%(cls)s is deprecated") % {'cls': cls.__name__}
warn(msg, DeprecationWarning, stacklevel=2)
return type.__call__(cls, *args, **kwargs)
class IProgressAdapter(view.EntityAdapter):
"""something that has a cost, a state and a progression.
You should at least override progress_info an in_progress methods on
concrete implementations.
"""
__metaclass__ = adapter_deprecated
__deprecation_warning__ = '[3.14] IProgressAdapter has been moved to iprogress cube'
__needs_bw_compat__ = True
__regid__ = 'IProgress'
__select__ = implements(IProgress, warn=False) # XXX for bw compat, should be abstract
@property
@view.implements_adapter_compat('IProgress')
def cost(self):
"""the total cost"""
return self.progress_info()['estimated']
@property
@view.implements_adapter_compat('IProgress')
def revised_cost(self):
return self.progress_info().get('estimatedcorrected', self.cost)
@property
@view.implements_adapter_compat('IProgress')
def done(self):
"""what is already done"""
return self.progress_info()['done']
@property
@view.implements_adapter_compat('IProgress')
def todo(self):
"""what remains to be done"""
return self.progress_info()['todo']
@view.implements_adapter_compat('IProgress')
def progress_info(self):
"""returns a dictionary describing progress/estimated cost of the
version.
- mandatory keys are (''estimated', 'done', 'todo')
- optional keys are ('notestimated', 'notestimatedcorrected',
'estimatedcorrected')
'noestimated' and 'notestimatedcorrected' should default to 0
'estimatedcorrected' should default to 'estimated'
"""
raise NotImplementedError
@view.implements_adapter_compat('IProgress')
def finished(self):
"""returns True if status is finished"""
return not self.in_progress()
@view.implements_adapter_compat('IProgress')
def in_progress(self):
"""returns True if status is not finished"""
raise NotImplementedError
@view.implements_adapter_compat('IProgress')
def progress(self):
"""returns the % progress of the task item"""
try:
return 100. * self.done / self.revised_cost
except ZeroDivisionError:
# total cost is 0 : if everything was estimated, task is completed
if self.progress_info().get('notestimated'):
return 0.
return 100
@view.implements_adapter_compat('IProgress')
def progress_class(self):
return ''
class IMileStoneAdapter(IProgressAdapter):
__metaclass__ = adapter_deprecated
__deprecation_warning__ = '[3.14] IMileStoneAdapter has been moved to iprogress cube'
__needs_bw_compat__ = True
__regid__ = 'IMileStone'
__select__ = implements(IMileStone, warn=False) # XXX for bw compat, should be abstract
parent_type = None # specify main task's type
@view.implements_adapter_compat('IMileStone')
def get_main_task(self):
"""returns the main ITask entity"""
raise NotImplementedError
@view.implements_adapter_compat('IMileStone')
def initial_prevision_date(self):
"""returns the initial expected end of the milestone"""
raise NotImplementedError
@view.implements_adapter_compat('IMileStone')
def eta_date(self):
"""returns expected date of completion based on what remains
to be done
"""
raise NotImplementedError
@view.implements_adapter_compat('IMileStone')
def completion_date(self):
"""returns date on which the subtask has been completed"""
raise NotImplementedError
@view.implements_adapter_compat('IMileStone')
def contractors(self):
"""returns the list of persons supposed to work on this task"""
raise NotImplementedError
......@@ -54,23 +54,23 @@ class VRegistryTC(TestCase):
def test_load_subinterface_based_appobjects(self):
self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
# check progressbar was kicked
self.assertFalse(self.vreg['views'].get('progressbar'))
self.vreg.register_objects([join(BASE, 'web', 'views', 'idownloadable.py')])
# check downloadlink was kicked
self.assertFalse(self.vreg['views'].get('downloadlink'))
# we've to emulate register_objects to add custom MyCard objects
path = [join(BASE, 'entities', '__init__.py'),
join(BASE, 'entities', 'adapters.py'),
join(BASE, 'web', 'views', 'iprogress.py')]
join(BASE, 'web', 'views', 'idownloadable.py')]
filemods = self.vreg.init_registration(path, None)
for filepath, modname in filemods:
self.vreg.load_file(filepath, modname)
class CardIProgressAdapter(EntityAdapter):
__regid__ = 'IProgress'
class CardIDownloadableAdapter(EntityAdapter):
__regid__ = 'IDownloadable'
self.vreg._loadedmods[__name__] = {}
self.vreg.register(CardIProgressAdapter)
self.vreg.register(CardIDownloadableAdapter)
self.vreg.initialization_completed()
# check progressbar isn't kicked
self.assertEqual(len(self.vreg['views']['progressbar']), 1)
self.assertEqual(len(self.vreg['views']['downloadlink']), 1)
def test_properties(self):
self.vreg.reset()
......
/*
* :organization: Logilab
* :copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
* :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
*/
/******************************************************************************/
/* progressbar */
/******************************************************************************/
.done { background:red }
.inprogress { background:green }
.overpassed { background: yellow}
canvas.progressbar {
border:1px solid black;
}
.progressbarback {
border: 1px solid #000000;
background: transparent;
height: 10px;
width: 100px;
}
/******************************************************************************/
/* progress table */
/******************************************************************************/
table.progress {
/* The default table view */
margin: 10px 0px 1em;
width: 100%;
font-size: 0.9167em;
}
table.progress th {
white-space: nowrap;
font-weight: bold;
background: %(listingHeaderBgColor)s;
padding: 2px 4px;
font-size:8pt;
}
table.progress th,
table.progress td {
border: 1px solid %(listingBorderColor)s;
}
table.progress td {
text-align: right;
padding: 2px 3px;
}
table.progress th.tdleft,
table.progress td.tdleft {
text-align: left;
padding: 2px 3px 2px 5px;
}
table.progress tr.highlighted {
background-color: %(listingHighlightedBgColor)s;
}
table.progress tr.highlighted .progressbarback {
border: 1px solid %(listingHighlightedBgColor)s;
}
table.progress .progressbarback {
border: 1px solid #777;
}
.progress_data {
padding-right: 3px;
}
\ No newline at end of file
function ProgressBar() {
this.budget = 100;
this.todo = 100;
this.done = 100;
this.color_done = "green";
this.color_budget = "blue";
this.color_todo = "#cccccc"; // grey
this.height = 16;
this.middle = this.height / 2;
this.radius = 4;
}
ProgressBar.prototype.draw_one_rect = function(ctx, pos, color, fill) {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = color;
if (fill) {
ctx.fillStyle = color;
ctx.fillRect(0, 0, pos, this.middle * 2);
} else {
ctx.lineWidth = 2;
ctx.strokeStyle = "black";
ctx.moveTo(pos, 0);
ctx.lineTo(pos, this.middle * 2);
ctx.stroke();
}
};
ProgressBar.prototype.draw_one_circ = function(ctx, pos, color) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = color;
ctx.moveTo(0, this.middle);
ctx.lineTo(pos, this.middle);
ctx.arc(pos, this.middle, this.radius, 0, Math.PI * 2, true);
ctx.stroke();
};
ProgressBar.prototype.draw_circ = function(ctx) {
this.draw_one_circ(ctx, this.budget, this.color_budget);
this.draw_one_circ(ctx, this.todo, this.color_todo);
this.draw_one_circ(ctx, this.done, this.color_done);
};
ProgressBar.prototype.draw_rect = function(ctx) {
this.draw_one_rect(ctx, this.todo, this.color_todo, true);
this.draw_one_rect(ctx, this.done, this.color_done, true);
this.draw_one_rect(ctx, this.budget, this.color_budget, false);
};
function draw_progressbar(cid, done, todo, budget, color) {
var canvas = document.getElementById(cid);
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var bar = new ProgressBar();
bar.budget = budget;
bar.todo = todo;
bar.done = done;
bar.color_done = color;
bar.draw_rect(ctx);
}
}
# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""Specific views for entities implementing IProgress/IMileStone"""
__docformat__ = "restructuredtext en"
_ = unicode
from math import floor
from logilab.common.deprecation import class_deprecated
from logilab.mtconverter import xml_escape
from cubicweb.utils import make_uid
from cubicweb.predicates import adaptable
from cubicweb.schema import display_name
from cubicweb.view import EntityView
from cubicweb.web.views.tableview import EntityAttributesTableView
class ProgressTableView(EntityAttributesTableView):
"""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
"""
__metaclass__ = class_deprecated
__deprecation_warning__ = '[3.14] %(cls)s is deprecated'
__regid__ = 'progress_table_view'
__select__ = adaptable('IMileStone')
title = _('task progression')
table_css = "progress"
css_files = ('cubicweb.iprogress.css',)
# default columns of the table
columns = (_('project'), _('milestone'), _('state'), _('eta_date'),
_('cost'), _('progress'), _('todo_by'))
def cell_call(self, row, col):
_ = self._cw._
entity = self.cw_rset.get_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
cssclass = entity.cw_adapt_to('IMileStone').progress_class()
self.w(u"""<tr class="%s" onmouseover="$(this).addClass('highlighted');"
onmouseout="$(this).removeClass('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, sample):
"""use entity's parent type as label"""
return display_name(self._cw, sample.cw_adapt_to('IMileStone').parent_type)
def header_for_milestone(self, sample):
"""use entity's type as label"""
return display_name(self._cw, sample.__regid__)
## cell management ########################################################
def build_project_cell(self, entity):
"""``project`` column cell renderer"""
project = entity.cw_adapt_to('IMileStone').get_main_task()
if project:
return project.view('incontext')
return self._cw._('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 xml_escape(entity.cw_adapt_to('IWorkflowable').printable_state)
def build_eta_date_cell(self, entity):
"""``eta_date`` column cell renderer"""
imilestone = entity.cw_adapt_to('IMileStone')
if imilestone.finished():
return self._cw.format_date(imilestone.completion_date())
formated_date = self._cw.format_date(imilestone.initial_prevision_date())
if imilestone.in_progress():
eta_date = self._cw.format_date(imilestone.eta_date())
_ = self._cw._
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"""
imilestone = entity.cw_adapt_to('IMileStone')
return u', '.join(p.view('outofcontext') for p in imilestone.contractors())
def build_cost_cell(self, entity):
"""``cost`` column cell renderer"""
_ = self._cw._
imilestone = entity.cw_adapt_to('IMileStone')
pinfo = imilestone.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"""
return entity.view('progressbar')
class InContextProgressTableView(ProgressTableView):
"""this views redirects to ``progress_table_view`` but removes
the ``project`` column
"""
__metaclass__ = class_deprecated
__deprecation_warning__ = '[3.14] %(cls)s is deprecated'
__regid__ = 'ic_progress_table_view'
def call(self, columns=None):
view = self._cw.vreg['views'].select('progress_table_view', self._cw,
rset=self.cw_rset)
columns = list(columns or view.columns)
try:
columns.remove('project')
except ValueError:
self.info('[ic_progress_table_view] could not remove project from columns')
view.render(w=self.w, columns=columns)
class ProgressBarView(EntityView):
"""displays a progress bar"""
__metaclass__ = class_deprecated
__deprecation_warning__ = '[3.14] %(cls)s is deprecated'
__regid__ = 'progressbar'
__select__ = adaptable('IProgress')
title = _('progress bar')
precision = 0.1
red_threshold = 1.1
orange_threshold = 1.05
yellow_threshold = 1
@classmethod
def overrun(cls, iprogress):
done = iprogress.done or 0
todo = iprogress.todo or 0
budget = iprogress.revised_cost or 0
if done + todo > budget:
overrun = done + todo - budget
else:
overrun = 0
if overrun < cls.precision:
overrun = 0
return overrun
@classmethod
def overrun_percentage(cls, iprogress):
budget = iprogress.revised_cost or 0
if budget == 0:
return 0
return cls.overrun(iprogress) * 100. / budget
def cell_call(self, row, col):
self._cw.add_css('cubicweb.iprogress.css')
self._cw.add_js('cubicweb.iprogress.js')
entity = self.cw_rset.get_entity(row, col)
iprogress = entity.cw_adapt_to('IProgress')
done = iprogress.done or 0
todo = iprogress.todo or 0
budget = iprogress.revised_cost or 0
if budget == 0:
pourcent = 100
else:
pourcent = done*100./budget
if pourcent > 100.1:
color = 'red'
elif todo+done > self.red_threshold*budget:
color = 'red'
elif todo+done > self.orange_threshold*budget:
color = 'orange'
elif todo+done > self.yellow_threshold*budget:
color = 'yellow'
else:
color = 'green'
if pourcent < 0:
pourcent = 0
if floor(done) == done or done>100:
done_str = '%i' % done
else:
done_str = '%.1f' % done
if floor(budget) == budget or budget>100:
budget_str = '%i' % budget
else:
budget_str = '%.1f' % budget
title = u'%s/%s = %i%%' % (done_str, budget_str, pourcent)
short_title = title
overrunpercent = self.overrun_percentage(iprogress)
if overrunpercent:
overrun = self.overrun(iprogress)
title += u' overrun +%sj (+%i%%)' % (overrun, overrunpercent)
if floor(overrun) == overrun or overrun > 100:
short_title += u' +%i' % overrun
else:
short_title += u' +%.1f' % overrun
# write bars
maxi = max(done+todo, budget)
if maxi == 0:
maxi = 1
cid = make_uid('progress_bar')
self._cw.html_headers.add_onload(
'draw_progressbar("canvas%s", %i, %i, %i, "%s");' %
(cid, int(100.*done/maxi), int(100.*(done+todo)/maxi),
int(100.*budget/maxi), color))
self.w(u'%s<br/>'
u'<canvas class="progressbar" id="canvas%s" width="100" height="10"></canvas>'
% (xml_escape(short_title), cid))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment