work in progress: reporting, example 'work statement' report
This commit is contained in:
parent
eb73015265
commit
120156cd04
6 changed files with 105 additions and 23 deletions
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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
|
||||
=============
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue