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):
|
def reportInstance(self):
|
||||||
instance = component.getAdapter(self.report, IReportInstance,
|
instance = component.getAdapter(self.report, IReportInstance,
|
||||||
name=self.report.reportType)
|
name=self.report.reportType)
|
||||||
instance.request = self.request
|
instance.view = self
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
#@Lazy
|
#@Lazy
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
provides="loops.expert.report.IReport" trusted="True" />
|
provides="loops.expert.report.IReport" trusted="True" />
|
||||||
<class class="loops.expert.report.Report">
|
<class class="loops.expert.report.Report">
|
||||||
<require permission="zope.View"
|
<require permission="zope.View"
|
||||||
interface="loops.expert.report.IReportSchema" />
|
interface="loops.expert.report.IReport" />
|
||||||
<require permission="zope.ManageContent"
|
<require permission="zope.ManageContent"
|
||||||
set_schema="loops.expert.report.IReportSchema" />
|
set_schema="loops.expert.report.IReport" />
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
<adapter factory="loops.expert.report.DefaultConceptReportInstance"
|
<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 Report as BaseReport
|
||||||
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
||||||
from cybertools.composer.report.interfaces import IReport as IBaseReport
|
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.composer.report.result import ResultSet, Row
|
||||||
from cybertools.util.jeep import Jeep
|
from cybertools.util.jeep import Jeep
|
||||||
from loops.common import AdapterBase
|
from loops.common import AdapterBase
|
||||||
|
@ -40,7 +41,7 @@ from loops.util import _
|
||||||
|
|
||||||
# interfaces
|
# interfaces
|
||||||
|
|
||||||
class IReport(ILoopsAdapter):
|
class IReport(ILoopsAdapter, IReportParams):
|
||||||
""" The report adapter for the persistent object (concept) that stores
|
""" The report adapter for the persistent object (concept) that stores
|
||||||
the report in the concept map.
|
the report in the concept map.
|
||||||
"""
|
"""
|
||||||
|
@ -53,19 +54,11 @@ class IReport(ILoopsAdapter):
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
|
|
||||||
class IReportSchema(IBaseReport, IReport):
|
class IReportInstance(IBaseReport):
|
||||||
""" All report attributes - use for security declarations.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class IReportInstance(Interface):
|
|
||||||
""" The report-type-specific object (an adapter on the report) that
|
""" The report-type-specific object (an adapter on the report) that
|
||||||
does the real report execution stuff.
|
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
|
# report concept adapter and instances
|
||||||
|
|
||||||
|
@ -85,12 +78,18 @@ class ReportInstance(BaseReport):
|
||||||
|
|
||||||
rowFactory = Row
|
rowFactory = Row
|
||||||
|
|
||||||
|
view = None # set upon creation
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
def getResultsRenderer(self, name, macros):
|
def getResultsRenderer(self, name, macros):
|
||||||
return macros[name]
|
return macros[name]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def queryCriteria(self):
|
||||||
|
return self.context.queryCriteria
|
||||||
|
|
||||||
def getResults(self, dynaParams=None):
|
def getResults(self, dynaParams=None):
|
||||||
crit = self.queryCriteria
|
crit = self.queryCriteria
|
||||||
if crit is None:
|
if crit is None:
|
||||||
|
@ -105,8 +104,21 @@ class ReportInstance(BaseReport):
|
||||||
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
|
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
|
||||||
|
|
||||||
def selectObjects(self, parts):
|
def selectObjects(self, parts):
|
||||||
|
# to be implemented by subclass
|
||||||
return []
|
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):
|
class ReportTypeSourceList(object):
|
||||||
|
|
||||||
|
|
|
@ -183,12 +183,47 @@ So we use the PersonWorkItems view, assigning john to the query.
|
||||||
Work Reports
|
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.organize.work.report import WorkReportInstance
|
||||||
>>> from loops.expert.report import IReportInstance
|
>>> from loops.expert.report import IReportInstance
|
||||||
>>> component.provideAdapter(WorkReportInstance,
|
>>> component.provideAdapter(WorkReportInstance,
|
||||||
... provides=IReportInstance,
|
... provides=IReportInstance,
|
||||||
... name='work_report')
|
... 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
|
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.base import LeafQueryCriteria, CompoundQueryCriteria
|
||||||
from cybertools.composer.report.field import Field
|
from cybertools.composer.report.field import Field
|
||||||
from cybertools.composer.report.result import ResultSet, Row as BaseRow
|
from cybertools.composer.report.result import ResultSet, Row as BaseRow
|
||||||
|
from cybertools.organize.interfaces import IWorkItems
|
||||||
from cybertools.util.jeep import Jeep
|
from cybertools.util.jeep import Jeep
|
||||||
|
from loops.common import adapted, baseObject
|
||||||
from loops.expert.report import ReportInstance
|
from loops.expert.report import ReportInstance
|
||||||
|
from loops import util
|
||||||
|
|
||||||
results_template = ViewPageTemplateFile('results.pt')
|
results_template = ViewPageTemplateFile('results.pt')
|
||||||
|
|
||||||
|
|
||||||
task = Field('task', u'Task',
|
tasks = Field('tasks', u'Tasks',
|
||||||
description=u'The task to which work items belong.',
|
description=u'The tasks to which work items belong.',
|
||||||
executionSteps=['query', 'output', 'sort'])
|
executionSteps=['query', 'output', 'sort'])
|
||||||
work = Field('work', u'Work',
|
work = Field('work', u'Work',
|
||||||
description=u'The short description of the work.',
|
description=u'The short description of the work.',
|
||||||
|
@ -65,12 +68,20 @@ class WorkReportInstance(ReportInstance):
|
||||||
label = u'Work Report'
|
label = u'Work Report'
|
||||||
|
|
||||||
rowFactory = WorkRow
|
rowFactory = WorkRow
|
||||||
fields = Jeep((day, dayFrom, dayTo, task, work, workDescription))
|
fields = Jeep((day, dayFrom, dayTo, tasks, work, workDescription))
|
||||||
defaultOutputFields = fields
|
defaultOutputFields = fields
|
||||||
|
|
||||||
@Lazy
|
@property
|
||||||
def queryCriteria(self):
|
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)
|
crit = [LeafQueryCriteria(f.name, f.operator, None, f)
|
||||||
for f in self.getAllQueryFields()]
|
for f in self.getAllQueryFields()]
|
||||||
return CompoundQueryCriteria(crit)
|
return CompoundQueryCriteria(crit)
|
||||||
|
@ -79,8 +90,31 @@ class WorkReportInstance(ReportInstance):
|
||||||
return results_template.macros[name]
|
return results_template.macros[name]
|
||||||
|
|
||||||
def selectObjects(self, parts):
|
def selectObjects(self, parts):
|
||||||
task = parts.get('task')
|
result = []
|
||||||
if not task:
|
tasks = parts.pop('tasks').comparisonValue
|
||||||
return []
|
for t in list(tasks):
|
||||||
return []
|
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>
|
||||||
<tr tal:repeat="row view/results">
|
<tr tal:repeat="row view/results">
|
||||||
<tal:column repeat="col view/displayedColumns">
|
<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>
|
</tal:column>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
Loading…
Add table
Reference in a new issue