work in progress: reporting; example 'work statement' report is operational

This commit is contained in:
Helmut Merz 2011-11-27 19:44:58 +01:00
parent 55fbb7eefc
commit 0a1d2ec262
5 changed files with 150 additions and 102 deletions

View file

@ -15,7 +15,25 @@
<div metal:define-macro="results">
Default Results Listing
<h2 i18n:translate="">Work Items</h2>
<table tal:define="results view/results">
<tr>
<th tal:repeat="col view/displayedColumns"
tal:content="col/title"
i18n:translate="" />
</tr>
<tr tal:repeat="row results">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python:view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
<tr tal:define="row nocall:results/totals"
tal:condition="nocall:row">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python:view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
</table>
</div>
@ -24,11 +42,18 @@
</metal:standard>
<metal:standard define-macro="target">
<metal:right define-macro="right">
<td style="text-align: right"
tal:content="python:col.getDisplayValue(row)" />
</metal:right>
<metal:target define-macro="target">
<td tal:define="value python:col.getDisplayValue(row)">
<a tal:attributes="href value/url"
<a tal:omit-tag="not:value/url"
tal:attributes="href value/url"
tal:content="value/title" /></td>
</metal:standard>
</metal:target>
</html>

43
expert/field.py Normal file
View file

@ -0,0 +1,43 @@
#
# Copyright (c) 2011 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Field definitions for reports.
"""
from cybertools.composer.report.field import Field
from loops import util
class TargetField(Field):
renderer = 'target'
def getValue(self, row):
value = self.getRawValue(row)
if value is None:
return None
return util.getObjectForUid(value)
def getDisplayValue(self, row):
value = self.getValue(row)
if value is None:
return dict(title=u'', url=u'')
view = row.parent.context.view
return dict(title=value.title, url=view.getUrlForTarget(value))

View file

@ -220,16 +220,20 @@ in a results view.
>>> from loops.expert.browser.report import ReportView, ResultsView
>>> resultsView = ResultsView(reportNode, TestRequest())
>>> results = resultsView.results().getResult()
>>> len(results)
>>> results = resultsView.results()#.getResult()
>>> len(list(results))
1
>>> for row in results:
... for col in resultsView.displayedColumns:
... print col.getDisplayValue(row),
... print
08/12/28 1230487200 1230491700
08/12/28 19:00 20:15
{'url': '.../home/report/.36', 'title': u'loops Development'}
{'url': '.../home/report/.33', 'title': u'john'} 4500 900 finished
{'url': '.../home/report/.33', 'title': u'john'} 01:15 00:15 finished
>>> results.totals.data
{'effort': 900}
Fin de partie
=============

View file

@ -33,41 +33,57 @@ from cybertools.util.date import timeStamp2Date
from cybertools.util.format import formatDate
from cybertools.util.jeep import Jeep
from loops.common import adapted, baseObject
from loops.expert.field import TargetField
from loops.expert.report import ReportInstance
from loops import util
results_template = ViewPageTemplateFile('results.pt')
class DateField(Field):
class TargetField(Field):
renderer = 'target'
part = 'date'
format = 'short'
renderer = 'right'
def getValue(self, row):
value = self.getRawValue(row)
return util.getObjectForUid(value)
def getDisplayValue(self, row):
value = self.getValue(row)
if value is None:
return dict(title=self.getRawValue(row), url=u'')
view = row.parent.context.view
return dict(title=value.title, url=view.getUrlForTarget(value))
class DayField(Field):
def getValue(self, row):
return timeStamp2Date(self.getRawValue(row))
return None
return timeStamp2Date(value)
def getDisplayValue(self, row):
value = self.getValue(row)
if value:
view = row.parent.context.view
return formatDate(value, 'date', 'short', view.languageInfo.language)
return formatDate(value, self.part, self.format,
view.languageInfo.language)
return u''
class TimeField(DateField):
part = 'time'
class DurationField(Field):
renderer = 'right'
def getValue(self, row):
value = self.getRawValue(row)
if value and 'totals' in self.executionSteps:
data = row.parent.totals.data
data[self.name] = data.get(self.name, 0) + value
if value:
value /= 3600.0
return value
def getDisplayValue(self, row):
value = self.getValue(row)
if not value:
return u''
return u'%02i:%02i' % divmod(value * 60, 60)
tasks = Field('tasks', u'Tasks',
description=u'The tasks from which work items should be selected.',
executionSteps=['query'])
@ -77,13 +93,13 @@ dayFrom = Field('dayFrom', u'Start Day',
dayTo = Field('dayTo', u'End Day',
description=u'The last day until which to select work.',
executionSteps=['query'])
day = DayField('day', u'Day',
day = DateField('day', u'Day',
description=u'The day the work was done.',
executionSteps=['sort', 'output'])
timeStart = Field('start', u'Start Time',
timeStart = TimeField('start', u'Start',
description=u'The time the unit of work was started.',
executionSteps=['sort', 'output'])
timeEnd = Field('end', u'End Time',
timeEnd = TimeField('end', u'End',
description=u'The time the unit of work was finished.',
executionSteps=['output'])
task = TargetField('taskId', u'Task',
@ -91,19 +107,19 @@ task = TargetField('taskId', u'Task',
executionSteps=['output'])
party = TargetField('userName', u'Party',
description=u'The party (usually a person) who did the work.',
executionSteps=['sort', 'output'])
executionSteps=['query', 'sort', 'output'])
title = Field('title', u'Title',
description=u'The short description of the work.',
executionSteps=['output'])
description = Field('description', u'Description',
description=u'The long description of the work.',
executionSteps=['x_output'])
duration = Field('duration', u'Duration',
duration = DurationField('duration', u'Duration',
description=u'The duration of the work.',
executionSteps=['output'])
effort = Field('effort', u'Effort',
effort = DurationField('effort', u'Effort',
description=u'The effort of the work.',
executionSteps=['output', 'sum'])
executionSteps=['output', 'totals'])
state = Field('state', u'State',
description=u'The state of the work.',
executionSteps=['query', 'output'])
@ -122,7 +138,19 @@ class WorkRow(BaseRow):
def getDay(self, attr):
return self.context.timeStamp
attributeHandlers = dict(day=getDay)
def getDuration(self, attr):
value = self.context.data.get('duration')
if value is None:
value = self.getRawValue('end') - self.getRawValue('start')
return value
def getEffort(self, attr):
value = self.context.data.get('effort')
if value is None:
value = self.getDuration(attr)
return value
attributeHandlers = dict(day=getDay, duration=getDuration, effort=getEffort)
class WorkReportInstance(ReportInstance):
@ -131,37 +159,46 @@ class WorkReportInstance(ReportInstance):
label = u'Work Report'
rowFactory = WorkRow
fields = Jeep((dayFrom, dayTo, tasks,
day, timeStart, timeEnd, task, party, title, description,
duration, effort, state))
defaultOutputFields = fields
defaultSortCriteria = (day, timeStart,)
@property
def queryCriteria(self):
form = self.view.request.form
crit = self.context.queryCriteria
if crit is None:
f = self.fields['tasks']
tasks = baseObject(self.context).getChildren([self.hasReportPredicate])
tasks = [util.getUidForObject(task) for task in tasks]
crit = [LeafQueryCriteria(f.name, f.operator, tasks, f)]
for f in self.getAllQueryFields():
if f.name in form:
crit.append(LeafQueryCriteria(f.name, f.operator, form[f.name], f))
return CompoundQueryCriteria(crit)
def getResultsRenderer(self, name, defaultMacros):
return results_template.macros[name]
def selectObjects(self, parts):
result = []
tasks = parts.pop('tasks').comparisonValue
tasks = [util.getObjectForUid(t) for t in parts.pop('tasks').comparisonValue]
for t in list(tasks):
tasks.extend(self.getAllSubtasks(t))
for t in tasks:
result.extend(self.selectWorkItems(t, parts))
# TODO: remove parts already used for selection from parts list
# remove parts already used for selection from parts list:
parts.pop('userName', None)
return result
def selectWorkItems(self, task, parts):
wi = self.workItems
states = ['done', 'done_x', 'finished']
return wi.query(task=util.getUidForObject(task), state=states)
kw = dict(task=util.getUidForObject(task), state=states)
if 'userName' in parts:
kw['userName'] = parts['userName'].comparisonValue
wi = self.workItems
return wi.query(**kw)
def getAllSubtasks(self, concept):
result = []

View file

@ -1,61 +0,0 @@
<html i18n:domain="loops">
<div metal:define-macro="results">
<h2 i18n:translate="">Work Items</h2>
<table>
<tr>
<th tal:repeat="col view/displayedColumns"
tal:content="col/title"
i18n:translate="" />
</tr>
<tr tal:repeat="row view/results">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python:view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
</table>
</div>
<div metal:define-macro="xx_results">
Work Items Listing
<table class="listing">
<tr>
<tal:colheader repeat="column work/allColumns">
<th tal:condition="python: column in work.columns"
tal:content="column"
i18n:translate="">Task</th>
</tal:colheader>
</tr>
<tal:workitem tal:repeat="row work/listWorkItems">
<tr tal:condition="row/monthChanged">
<td class="headline"
tal:attributes="colspan python: len(work.columns)"
tal:content="row/month">2009-01</td></tr>
<tr tal:attributes="class python:
(repeat['row'].odd() and 'even' or 'odd')">
<td class="nowrap center"
tal:define="today python: row.isToday and ' today' or ''"
tal:attributes="title row/weekDay;
class string:nowrap center$today"
i18n:attributes="title"
tal:content="row/day">2007-03-30</td>
<td class="nowrap center" tal:content="row/start">17:30</td>
<td class="nowrap center" tal:content="row/end">20:00</td>
<td class="nowrap center" tal:content="row/duration">2:30</td>
<td tal:condition="python: 'Task' in work.columns">
<a tal:attributes="href row/objectData/url"
tal:content="row/objectData/title">Task</a></td>
<td tal:condition="python: 'User' in work.columns">
<a tal:attributes="href row/user/url"
tal:content="row/user/title">John</a></td>
<td tal:content="row/track/title"
tal:attributes="title row/descriptionPlain">Title</td>
</tr>
</tal:workitem>
</table>
</div>
</html>