|
1 """Specific views for entities implementing IProgress |
|
2 |
|
3 :organization: Logilab |
|
4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
6 """ |
|
7 |
|
8 __docformat__ = "restructuredtext en" |
|
9 |
|
10 from logilab.mtconverter import html_escape |
|
11 |
|
12 from cubicweb.interfaces import IProgress, IMileStone |
|
13 from cubicweb.schema import display_name |
|
14 from cubicweb.common.view import EntityView |
|
15 from cubicweb.common.selectors import interface_selector, accept_selector |
|
16 from cubicweb.web.htmlwidgets import ProgressBarWidget |
|
17 |
|
18 |
|
19 class ProgressTableView(EntityView): |
|
20 """The progress table view is able to display progress information |
|
21 of any object implement IMileStone. |
|
22 |
|
23 The default layout is composoed of 7 columns : parent task, |
|
24 milestone, state, estimated date, cost, progressbar, and todo_by |
|
25 |
|
26 The view accepts an optional ``columns`` paramater that lets you |
|
27 remove or reorder some of those columns. |
|
28 |
|
29 To add new columns, you should extend this class, define a new |
|
30 ``columns`` class attribute and implement corresponding |
|
31 build_COLNAME_cell methods |
|
32 |
|
33 header_for_COLNAME methods allow to customize header's label |
|
34 """ |
|
35 |
|
36 id = 'progress_table_view' |
|
37 title = _('task progression') |
|
38 __selectors__ = (accept_selector, interface_selector) |
|
39 |
|
40 accepts_interfaces = (IMileStone,) |
|
41 |
|
42 # default columns of the table |
|
43 columns = (_('project'), _('milestone'), _('state'), _('eta_date'), |
|
44 _('cost'), _('progress'), _('todo_by')) |
|
45 |
|
46 |
|
47 def call(self, columns=None): |
|
48 """displays all versions in a table""" |
|
49 self.req.add_css('cubicweb.iprogress.css') |
|
50 _ = self.req._ |
|
51 self.columns = columns or self.columns |
|
52 ecls = self.vreg.etype_class(self.rset.description[0][0]) |
|
53 self.w(u'<table class="progress">') |
|
54 self.table_header(ecls) |
|
55 self.w(u'<tbody>') |
|
56 for row in xrange(self.rset.rowcount): |
|
57 self.cell_call(row=row, col=0) |
|
58 self.w(u'</tbody>') |
|
59 self.w(u'</table>') |
|
60 |
|
61 def cell_call(self, row, col): |
|
62 _ = self.req._ |
|
63 entity = self.entity(row, col) |
|
64 infos = {} |
|
65 for col in self.columns: |
|
66 meth = getattr(self, 'build_%s_cell' % col, None) |
|
67 # find the build method or try to find matching attribute |
|
68 if meth: |
|
69 content = meth(entity) |
|
70 else: |
|
71 content = entity.printable_value(col) |
|
72 infos[col] = content |
|
73 if hasattr(entity, 'progress_class'): |
|
74 cssclass = entity.progress_class() |
|
75 else: |
|
76 cssclass = u'' |
|
77 self.w(u"""<tr class="%s" onmouseover="addElementClass(this, 'highlighted');" |
|
78 onmouseout="removeElementClass(this, 'highlighted')">""" % cssclass) |
|
79 line = u''.join(u'<td>%%(%s)s</td>' % col for col in self.columns) |
|
80 self.w(line % infos) |
|
81 self.w(u'</tr>\n') |
|
82 |
|
83 ## header management ###################################################### |
|
84 |
|
85 def header_for_project(self, ecls): |
|
86 """use entity's parent type as label""" |
|
87 return display_name(self.req, ecls.parent_type) |
|
88 |
|
89 def header_for_milestone(self, ecls): |
|
90 """use entity's type as label""" |
|
91 return display_name(self.req, ecls.id) |
|
92 |
|
93 def table_header(self, ecls): |
|
94 """builds the table's header""" |
|
95 self.w(u'<thead><tr>') |
|
96 _ = self.req._ |
|
97 for column in self.columns: |
|
98 meth = getattr(self, 'header_for_%s' % column, None) |
|
99 if meth: |
|
100 colname = meth(ecls) |
|
101 else: |
|
102 colname = _(column) |
|
103 self.w(u'<th>%s</th>' % html_escape(colname)) |
|
104 self.w(u'</tr></thead>\n') |
|
105 |
|
106 |
|
107 ## cell management ######################################################## |
|
108 def build_project_cell(self, entity): |
|
109 """``project`` column cell renderer""" |
|
110 project = entity.get_main_task() |
|
111 if project: |
|
112 return project.view('incontext') |
|
113 return self.req._('no related project') |
|
114 |
|
115 def build_milestone_cell(self, entity): |
|
116 """``milestone`` column cell renderer""" |
|
117 return entity.view('incontext') |
|
118 |
|
119 def build_state_cell(self, entity): |
|
120 """``state`` column cell renderer""" |
|
121 return html_escape(self.req._(entity.state)) |
|
122 |
|
123 def build_eta_date_cell(self, entity): |
|
124 """``eta_date`` column cell renderer""" |
|
125 if entity.finished(): |
|
126 return self.format_date(entity.completion_date()) |
|
127 formated_date = self.format_date(entity.initial_prevision_date()) |
|
128 if entity.in_progress(): |
|
129 eta_date = self.format_date(entity.eta_date()) |
|
130 _ = self.req._ |
|
131 if formated_date: |
|
132 formated_date += u' (%s %s)' % (_('expected:'), eta_date) |
|
133 else: |
|
134 formated_date = u'%s %s' % (_('expected:'), eta_date) |
|
135 return formated_date |
|
136 |
|
137 def build_todo_by_cell(self, entity): |
|
138 """``todo_by`` column cell renderer""" |
|
139 return u', '.join(p.view('outofcontext') for p in entity.contractors()) |
|
140 |
|
141 def build_cost_cell(self, entity): |
|
142 """``cost`` column cell renderer""" |
|
143 _ = self.req._ |
|
144 pinfo = entity.progress_info() |
|
145 totalcost = pinfo.get('estimatedcorrected', pinfo['estimated']) |
|
146 missing = pinfo.get('notestimatedcorrected', pinfo.get('notestimated', 0)) |
|
147 costdescr = [] |
|
148 if missing: |
|
149 # XXX: link to unestimated entities |
|
150 costdescr.append(_('%s not estimated') % missing) |
|
151 estimated = pinfo['estimated'] |
|
152 if estimated and estimated != totalcost: |
|
153 costdescr.append(_('initial estimation %s') % estimated) |
|
154 if costdescr: |
|
155 return u'%s (%s)' % (totalcost, ', '.join(costdescr)) |
|
156 return unicode(totalcost) |
|
157 |
|
158 def build_progress_cell(self, entity): |
|
159 """``progress`` column cell renderer""" |
|
160 progress = u'<div class="progress_data">%s (%.2f%%)</div>' % ( |
|
161 entity.done, entity.progress()) |
|
162 return progress + entity.view('progressbar') |
|
163 |
|
164 |
|
165 class InContextProgressTableView(ProgressTableView): |
|
166 """this views redirects to ``progress_table_view`` but removes |
|
167 the ``project`` column |
|
168 """ |
|
169 id = 'ic_progress_table_view' |
|
170 |
|
171 def call(self): |
|
172 view = self.vreg.select_view('progress_table_view', self.req, self.rset) |
|
173 columns = list(view.columns) |
|
174 try: |
|
175 columns.remove('project') |
|
176 except ValueError: |
|
177 self.info('[ic_progress_table_view] could not remove project from columns') |
|
178 view.dispatch(w=self.w, columns=columns) |
|
179 |
|
180 |
|
181 class ProgressBarView(EntityView): |
|
182 """displays a progress bar""" |
|
183 id = 'progressbar' |
|
184 title = _('progress bar') |
|
185 __selectors__ = (accept_selector, interface_selector) |
|
186 |
|
187 accepts_interfaces = (IProgress,) |
|
188 |
|
189 def cell_call(self, row, col): |
|
190 self.req.add_css('cubicweb.iprogress.css') |
|
191 entity = self.entity(row, col) |
|
192 widget = ProgressBarWidget(entity.done, entity.todo, |
|
193 entity.revised_cost) |
|
194 self.w(widget.render()) |
|
195 |