work in progress: reporting, example 'work statement' report

This commit is contained in:
Helmut Merz 2011-11-26 14:54:24 +01:00
parent eb73015265
commit 120156cd04
6 changed files with 105 additions and 23 deletions

View file

@ -85,7 +85,7 @@ class ResultsView(NodeView):
def reportInstance(self):
instance = component.getAdapter(self.report, IReportInstance,
name=self.report.reportType)
instance.request = self.request
instance.view = self
return instance
#@Lazy

View file

@ -22,9 +22,9 @@
provides="loops.expert.report.IReport" trusted="True" />
<class class="loops.expert.report.Report">
<require permission="zope.View"
interface="loops.expert.report.IReportSchema" />
interface="loops.expert.report.IReport" />
<require permission="zope.ManageContent"
set_schema="loops.expert.report.IReportSchema" />
set_schema="loops.expert.report.IReport" />
</class>
<adapter factory="loops.expert.report.DefaultConceptReportInstance"

View file

@ -29,6 +29,7 @@ from zope.security.proxy import removeSecurityProxy
from cybertools.composer.report.base import Report as BaseReport
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
from cybertools.composer.report.interfaces import IReport as IBaseReport
from cybertools.composer.report.interfaces import IReportParams
from cybertools.composer.report.result import ResultSet, Row
from cybertools.util.jeep import Jeep
from loops.common import AdapterBase
@ -40,7 +41,7 @@ from loops.util import _
# interfaces
class IReport(ILoopsAdapter):
class IReport(ILoopsAdapter, IReportParams):
""" The report adapter for the persistent object (concept) that stores
the report in the concept map.
"""
@ -53,19 +54,11 @@ class IReport(ILoopsAdapter):
required=True)
class IReportSchema(IBaseReport, IReport):
""" All report attributes - use for security declarations.
"""
class IReportInstance(Interface):
class IReportInstance(IBaseReport):
""" The report-type-specific object (an adapter on the report) that
does the real report execution stuff.
"""
label = Attribute('The user-friendly label of the report type specified '
'by this instance class.')
# report concept adapter and instances
@ -85,12 +78,18 @@ class ReportInstance(BaseReport):
rowFactory = Row
view = None # set upon creation
def __init__(self, context):
self.context = context
def getResultsRenderer(self, name, macros):
return macros[name]
@property
def queryCriteria(self):
return self.context.queryCriteria
def getResults(self, dynaParams=None):
crit = self.queryCriteria
if crit is None:
@ -105,8 +104,21 @@ class ReportInstance(BaseReport):
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
def selectObjects(self, parts):
# to be implemented by subclass
return []
@Lazy
def conceptManager(self):
return self.view.conceptManager
@Lazy
def recordManager(self):
return self.view.loopsRoot.getRecordManager()
@Lazy
def hasReportPredicate(self):
return self.conceptManager['hasreport']
class ReportTypeSourceList(object):

View file

@ -183,12 +183,47 @@ So we use the PersonWorkItems view, assigning john to the query.
Work Reports
============
First we have to make sure that there is a report concept type available.
In addition we need a predicate that connects one or more tasks with a report.
>>> from loops.expert.report import IReport, Report
>>> component.provideAdapter(Report)
>>> tReport = addAndConfigureObject(concepts, Concept, 'report',
... title=u'Report', conceptType=concepts.getTypeConcept(),
... typeInterface=IReport)
>>> hasReport = addAndConfigureObject(concepts, Concept, 'hasreport',
... title=u'has Report', conceptType=concepts.getPredicateType())
Now we can create a report and register it as the report for the task
used above.
>>> workStatement = addAndConfigureObject(concepts, Concept, 'work_statement',
... title=u'Work Statement', conceptType=tReport,
... reportType='work_report')
>>> workStatement.assignChild(task01, hasReport)
The executable report is a report instance that is an adapter to the
(adapted) report instance.
>>> from loops.organize.work.report import WorkReportInstance
>>> from loops.expert.report import IReportInstance
>>> component.provideAdapter(WorkReportInstance,
... provides=IReportInstance,
... name='work_report')
The user interface to the report is a report view, the results are presented
in a results view.
>>> from loops.view import Node
>>> reportNode = addAndConfigureObject(home, Node, 'report',
... title=u'Report', target=workStatement)
>>> from loops.expert.browser.report import ReportView, ResultsView
>>> resultsView = ResultsView(reportNode, TestRequest())
>>> results = resultsView.results()
>>> len(results.data)
2
Fin de partie
=============

View file

@ -28,14 +28,17 @@ from cybertools.composer.report.base import Report
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
from cybertools.composer.report.field import Field
from cybertools.composer.report.result import ResultSet, Row as BaseRow
from cybertools.organize.interfaces import IWorkItems
from cybertools.util.jeep import Jeep
from loops.common import adapted, baseObject
from loops.expert.report import ReportInstance
from loops import util
results_template = ViewPageTemplateFile('results.pt')
task = Field('task', u'Task',
description=u'The task to which work items belong.',
tasks = Field('tasks', u'Tasks',
description=u'The tasks to which work items belong.',
executionSteps=['query', 'output', 'sort'])
work = Field('work', u'Work',
description=u'The short description of the work.',
@ -65,12 +68,20 @@ class WorkReportInstance(ReportInstance):
label = u'Work Report'
rowFactory = WorkRow
fields = Jeep((day, dayFrom, dayTo, task, work, workDescription))
fields = Jeep((day, dayFrom, dayTo, tasks, work, workDescription))
defaultOutputFields = fields
@Lazy
@property
def queryCriteria(self):
# TODO: take from persistent report where appropriate
crit = self.context.queryCriteria
if crit is None:
f = self.fields['tasks']
tasks = baseObject(self.context).getChildren([self.hasReportPredicate])
crit = [LeafQueryCriteria(f.name, f.operator, tasks, f)]
return CompoundQueryCriteria(crit)
@property
def xx_queryCriteria(self):
crit = [LeafQueryCriteria(f.name, f.operator, None, f)
for f in self.getAllQueryFields()]
return CompoundQueryCriteria(crit)
@ -79,8 +90,31 @@ class WorkReportInstance(ReportInstance):
return results_template.macros[name]
def selectObjects(self, parts):
task = parts.get('task')
if not task:
return []
return []
result = []
tasks = 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
return result
def selectWorkItems(self, task, parts):
wi = self.workItems
return wi.query(task=util.getUidForObject(task))
def getAllSubtasks(self, concept):
result = []
for c in concept.getChildren():
if c.conceptType == self.taskType:
result.append(c)
result.extend(self.getAllSubtasks(c))
return result
@Lazy
def taskType(self):
return self.conceptManager['task']
@Lazy
def workItems(self):
return IWorkItems(self.recordManager['work'])

View file

@ -11,7 +11,8 @@
</tr>
<tr tal:repeat="row view/results">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python: view.getColumnRenderer(col.renderer)" />
<p tal:content="col/renderer" />
<xmetal:column use-macro="python:view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
</table>