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"> <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> </div>
@ -24,11 +42,18 @@
</metal:standard> </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)"> <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> tal:content="value/title" /></td>
</metal:standard> </metal:target>
</html> </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 >>> from loops.expert.browser.report import ReportView, ResultsView
>>> resultsView = ResultsView(reportNode, TestRequest()) >>> resultsView = ResultsView(reportNode, TestRequest())
>>> results = resultsView.results().getResult() >>> results = resultsView.results()#.getResult()
>>> len(results) >>> len(list(results))
1 1
>>> for row in results: >>> for row in results:
... for col in resultsView.displayedColumns: ... for col in resultsView.displayedColumns:
... print col.getDisplayValue(row), ... print col.getDisplayValue(row),
... print ... print
08/12/28 1230487200 1230491700 08/12/28 19:00 20:15
{'url': '.../home/report/.36', 'title': u'loops Development'} {'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 Fin de partie
============= =============

View file

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