merge branch master

This commit is contained in:
Helmut Merz 2012-07-10 09:08:25 +02:00
commit 0aa539e8f8
8 changed files with 162 additions and 47 deletions

View file

@ -483,6 +483,7 @@ class NodeView(BaseView):
def virtualTarget(self): def virtualTarget(self):
tv = self.viewAnnotations.get('targetView') tv = self.viewAnnotations.get('targetView')
if tv is not None: if tv is not None:
tv.setupController()
return tv return tv
setup = self.setupTarget setup = self.setupTarget
return self.getViewForTarget(self.virtualTargetObject, setup=setup) return self.getViewForTarget(self.virtualTargetObject, setup=setup)

View file

@ -2,21 +2,73 @@
<div metal:define-macro="main"> <div metal:define-macro="main">
<div tal:attributes="class string:content-$level;"> <div tal:define="report item/reportInstance;
reportView nocall:item"
tal:attributes="class string:content-$level;">
<metal:block use-macro="view/concept_macros/concepttitle" /> <metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post" name="report_data" action="results.html"> <form method="post" name="report_data">
<tal:hidden define="params item/dynamicParams"> <tal:hidden define="params item/dynamicParams"
tal:condition="nothing">
<input type="hidden" <input type="hidden"
tal:repeat="name params" tal:repeat="name params"
tal:attributes="name name; tal:attributes="name name;
value params/?name" /></tal:hidden> value params/?name" /></tal:hidden>
<div metal:use-macro="item/report_macros/params" />
<div metal:define-macro="buttons"> <div metal:define-macro="buttons">
<input type="submit" name="report_execute" value="Execute Report" <input type="submit" name="report_execute" value="Execute Report"
i18n:attributes="value" /> i18n:attributes="value" />
</div> </div>
</form> </form>
<div metal:use-macro="item/resultsRenderer" />
</div> </div>
</div> </div>
<metal:block define-macro="params">
<metal:block use-macro="item/report_macros/query" />
</metal:block>
<metal:block define-macro="query"
tal:define="criteria
item/reportInstance/queryCriteria/parts|nothing">
<table>
<tr tal:repeat="field item/queryFields">
<tal:field define="fieldType field/fieldType;
crit python:criteria and criteria.get(field.name)">
<td><b><span tal:content="field/title" />: </b></td>
<td><metal:field use-macro="item/report_macros/?fieldType" /></td>
</tal:field>
</tr>
</table>
<br />
</metal:block>
<metal:field define-macro="textline">
<input tal:attributes="name string:${field/name};
value crit/comparisonValue|nothing" />
</metal:field>
<metal:field define-macro="number">
<input tal:attributes="name string:query.field.${field/name};
value crit/comparisonValue|nothing" />
</metal:field>
<metal:field define-macro="date">
<input dojoType="dijit.form.DateTextBox" style="width: 8em"
constraints="{datePattern: 'd.M.y',
min: '1980-01-01', max: '2020-12-31'}"
tal:attributes="name string:${field/name};
value crit/comparisonValue|nothing" />
</metal:field>
<metal:field define-macro="selection">
<metal:use use-macro="item/report_macros/textline" />
</metal:field>
</html> </html>

View file

@ -134,7 +134,7 @@ class ResultsView(NodeView):
class ResultsConceptView(ConceptView): class ResultsConceptView(ConceptView):
""" View on a concept using a report. """ View on a concept using the results of a report.
""" """
reportName = None # define in subclass if applicable reportName = None # define in subclass if applicable
@ -170,12 +170,11 @@ class ResultsConceptView(ConceptView):
reportType = self.reportType or self.report.reportType reportType = self.reportType or self.report.reportType
ri = component.getAdapter(self.report, IReportInstance, ri = component.getAdapter(self.report, IReportInstance,
name=reportType) name=reportType)
#ri.view = self.nodeView
ri.view = self ri.view = self
return ri return ri
def results(self): def results(self):
return self.reportInstance.getResults(dict(tasks=util.getUidForObject(self.context))) return self.reportInstance.getResults()
@Lazy @Lazy
def displayedColumns(self): def displayedColumns(self):
@ -183,3 +182,24 @@ class ResultsConceptView(ConceptView):
def getColumnRenderer(self, col): def getColumnRenderer(self, col):
return self.result_macros[col.renderer] return self.result_macros[col.renderer]
class ReportConceptView(ResultsConceptView, ReportView):
""" View on a concept using a report.
"""
def setupController(self):
super(ReportConceptView, self).setupController()
self.registerDojoDateWidget()
@Lazy
def macro(self):
return self.report_macros['main']
@Lazy
def queryFields(self):
ri = self.reportInstance
qf = ri.getAllQueryFields()
if ri.userSettings:
return [f for f in qf if f in ri.userSettings]
return qf

View file

@ -35,7 +35,8 @@
i18n:translate="" /> i18n:translate="" />
</tr> </tr>
<tr tal:repeat="row results"> <tr tal:repeat="row results">
<td tal:repeat="col results/displayedColumns"> <td tal:repeat="col results/displayedColumns"
tal:attributes="class col/cssClass">
<metal:column use-macro="python: <metal:column use-macro="python:
reportView.getColumnRenderer(col)" /> reportView.getColumnRenderer(col)" />
</td> </td>

View file

@ -25,7 +25,8 @@ from zope import component
from zope.i18n.locales import locales from zope.i18n.locales import locales
from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder
from cybertools.composer.report.field import Field, TableCellStyle from cybertools.composer.report.field import Field as BaseField
from cybertools.composer.report.field import TableCellStyle
from cybertools.composer.report.result import ResultSet from cybertools.composer.report.result import ResultSet
from cybertools.util.date import timeStamp2Date from cybertools.util.date import timeStamp2Date
from loops.common import baseObject from loops.common import baseObject
@ -33,6 +34,12 @@ from loops.expert.report import ReportInstance
from loops import util from loops import util
class Field(BaseField):
def getSelectValue(self, row):
return self.getRawValue(row)
class TextField(Field): class TextField(Field):
format = 'text/restructured' format = 'text/restructured'

View file

@ -212,25 +212,23 @@ The executable report is a report instance that is an adapter to the
... provides=IReportInstance, ... provides=IReportInstance,
... name='work_report') ... name='work_report')
The user interface to the report is a report view, the results are presented The user interface is a ReportConceptView subclass that is directly associated with the task.
in a results view.
>>> from loops.view import Node >>> from loops.organize.work.report import WorkStatementView
>>> reportNode = addAndConfigureObject(home, Node, 'report', >>> reportView = WorkStatementView(task01, TestRequest())
... title=u'Report', target=workStatement) >>> reportView.nodeView = nodeView
>>> from loops.expert.browser.report import ReportView, ResultsView
>>> resultsView = ResultsView(reportNode, TestRequest())
>>> results = resultsView.results()#.getResult() >>> results = reportView.results()
>>> len(list(results)) >>> len(list(results))
1 1
>>> for row in results: >>> for row in results:
... for col in resultsView.displayedColumns: ... for col in reportView.displayedColumns:
... print col.getDisplayValue(row), ... print col.getDisplayValue(row),
... print ... print
08/12/28 19:00 20:15 08/12/28 19:00 20:15
{'url': '.../home/report/.36', 'title': u'loops Development'} {'url': '.../home/.36', 'title': u'loops Development'}
{'url': '.../home/report/.33', 'title': u'john'} 01:15 00:15 finished {'url': '.../home/.33', 'title': u'john'} 01:15 00:15 finished
>>> results.totals.data >>> results.totals.data
{'effort': 900} {'effort': 900}
@ -266,18 +264,17 @@ report is available.
... reportType='meeting_minutes') ... reportType='meeting_minutes')
>>> meetingMinutes.assignChild(tEvent, hasReport) >>> meetingMinutes.assignChild(tEvent, hasReport)
We can now access the report using a results view. We can now access the report using a corresponding report-based view.
>>> from loops.util import getUidForObject >>> from loops.organize.work.meeting import MeetingMinutes
>>> input = dict(tasks=getUidForObject(ev01)) >>> reportView = MeetingMinutes(ev01, TestRequest())
>>> resultsView = ResultsView(home, TestRequest(form=input)) >>> reportView.nodeView = nodeView
>>> resultsView.virtualTargetObject = meetingMinutes
>>> results = resultsView.results() >>> results = reportView.results()
>>> len(list(results)) >>> len(list(results))
1 1
>>> for row in results: >>> for row in results:
... for col in resultsView.displayedColumns: ... for col in reportView.displayedColumns:
... print col.getDisplayValue(row), ... print col.getDisplayValue(row),
... print ... print
{'url': 'http://127.0.0.1/loops/views/home/.36', 'title': u'loops Development'} {'url': 'http://127.0.0.1/loops/views/home/.36', 'title': u'loops Development'}

View file

@ -75,8 +75,9 @@
<!-- reporting --> <!-- reporting -->
<zope:adapter factory="loops.organize.work.report.WorkReportInstance" <zope:adapter
name="work_report" name="work_report"
factory="loops.organize.work.report.WorkReportInstance"
provides="loops.expert.report.IReportInstance" provides="loops.expert.report.IReportInstance"
trusted="True" /> trusted="True" />
<zope:class class="loops.organize.work.report.WorkReportInstance"> <zope:class class="loops.organize.work.report.WorkReportInstance">
@ -86,8 +87,15 @@
set_schema="loops.expert.report.IReportInstance" /> set_schema="loops.expert.report.IReportInstance" />
</zope:class> </zope:class>
<zope:adapter factory="loops.organize.work.report.MeetingMinutes" <browser:page
name="work.html"
for="loops.organize.interfaces.ITask"
class="loops.organize.work.report.WorkStatementView"
permission="zope.View" />
<zope:adapter
name="meeting_minutes" name="meeting_minutes"
factory="loops.organize.work.report.MeetingMinutes"
provides="loops.expert.report.IReportInstance" provides="loops.expert.report.IReportInstance"
trusted="True" /> trusted="True" />
<zope:class class="loops.organize.work.report.MeetingMinutes"> <zope:class class="loops.organize.work.report.MeetingMinutes">

View file

@ -26,20 +26,32 @@ from zope.component import adapter
from cybertools.composer.report.base import Report 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, CalculatedField from cybertools.composer.report.field import CalculatedField
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.organize.interfaces import IWorkItems
from cybertools.util.date import timeStamp2Date from cybertools.util.date import timeStamp2Date, timeStamp2ISO
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, DateField, TextField, UrlField from loops.expert.browser.report import ReportConceptView
from loops.expert.field import Field, TargetField, DateField, \
TextField, UrlField
from loops.expert.field import SubReport, SubReportField from loops.expert.field import SubReport, SubReportField
from loops.expert.report import ReportInstance from loops.expert.report import ReportInstance
from loops import util from loops import util
# reporting views
class WorkStatementView(ReportConceptView):
reportName = 'work_statement'
# fields
class StateField(Field): class StateField(Field):
def getDisplayValue(self, row): def getDisplayValue(self, row):
value = self.getValue(row) value = self.getValue(row)
return util._(value) return util._(value)
@ -47,9 +59,10 @@ class StateField(Field):
class TrackDateField(Field): class TrackDateField(Field):
fieldType = 'date'
part = 'date' part = 'date'
format = 'short' format = 'short'
renderer = 'right' cssClass = 'right'
def getValue(self, row): def getValue(self, row):
value = self.getRawValue(row) value = self.getRawValue(row)
@ -65,6 +78,12 @@ class TrackDateField(Field):
view.languageInfo.language) view.languageInfo.language)
return u'' return u''
def getSelectValue(self, row):
value = self.getRawValue(row)
if not value:
return ''
return timeStamp2ISO(value)[:10]
class TrackTimeField(TrackDateField): class TrackTimeField(TrackDateField):
@ -73,7 +92,7 @@ class TrackTimeField(TrackDateField):
class DurationField(Field): class DurationField(Field):
renderer = 'right' cssClass = 'right'
def getValue(self, row): def getValue(self, row):
value = self.getRawValue(row) value = self.getRawValue(row)
@ -100,11 +119,15 @@ tasks = Field('tasks', u'Tasks',
# work report fields # work report fields
dayFrom = Field('dayFrom', u'Start Day', dayFrom = TrackDateField('dayFrom', u'Start Day',
description=u'The first day from which to select work.', description=u'The first day from which to select work.',
fieldType='date',
operator=u'gt',
executionSteps=['query']) executionSteps=['query'])
dayTo = Field('dayTo', u'End Day', dayTo = TrackDateField('dayTo', u'End Day',
description=u'The last day until which to select work.', description=u'The last day until which to select work.',
fieldType='date',
operator=u'le',
executionSteps=['query']) executionSteps=['query'])
day = TrackDateField('day', u'Day', day = TrackDateField('day', u'Day',
description=u'The day the work was done.', description=u'The day the work was done.',
@ -121,6 +144,7 @@ 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.',
fieldType='selection',
executionSteps=['query', 'sort', 'output']) executionSteps=['query', 'sort', 'output'])
workTitle = Field('title', u'Title', workTitle = Field('title', u'Title',
description=u'The short description of the work.', description=u'The short description of the work.',
@ -167,7 +191,8 @@ class WorkRow(BaseRow):
value = self.getDuration(attr) value = self.getDuration(attr)
return value return value
attributeHandlers = dict(day=getDay, duration=getDuration, effort=getEffort) attributeHandlers = dict(day=getDay, dayFrom=getDay, dayTo=getDay,
duration=getDuration, effort=getEffort)
class WorkReportInstance(ReportInstance): class WorkReportInstance(ReportInstance):
@ -178,9 +203,11 @@ class WorkReportInstance(ReportInstance):
rowFactory = WorkRow rowFactory = WorkRow
fields = Jeep((dayFrom, dayTo, tasks, fields = Jeep((dayFrom, dayTo, tasks,
day, timeStart, timeEnd, task, party, workTitle, #description, day, timeStart, timeEnd, task, party, workTitle,
#description,
duration, effort, state)) duration, effort, state))
userSettings = (dayFrom, dayTo, party)
defaultOutputFields = fields defaultOutputFields = fields
defaultSortCriteria = (day, timeStart,) defaultSortCriteria = (day, timeStart,)
states = ('done', 'done_x', 'finished') states = ('done', 'done_x', 'finished')
@ -192,12 +219,13 @@ class WorkReportInstance(ReportInstance):
crit = self.context.queryCriteria or [] crit = self.context.queryCriteria or []
if not crit and 'tasks' not in form: if not crit and 'tasks' not in form:
f = self.fields['tasks'] f = self.fields['tasks']
tasks = baseObject(self.context).getChildren([self.hasReportPredicate]) tasks = [self.view.context]
tasks = [util.getUidForObject(task) for task in tasks] 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(): for f in self.getAllQueryFields():
if f.name in form: if f.name in form:
crit.append(LeafQueryCriteria(f.name, f.operator, form[f.name], f)) crit.append(
LeafQueryCriteria(f.name, f.operator, form[f.name], f))
return CompoundQueryCriteria(crit) return CompoundQueryCriteria(crit)
def selectObjects(self, parts): def selectObjects(self, parts):
@ -229,8 +257,9 @@ class WorkReportInstance(ReportInstance):
# TODO: take states from parts # TODO: take states from parts
kw = dict(task=util.getUidForObject(baseObject(task)), kw = dict(task=util.getUidForObject(baseObject(task)),
state=self.states) state=self.states)
if 'userName' in parts: userNameCrit = parts.get('userName')
kw['userName'] = parts['userName'].comparisonValue if userNameCrit and userNameCrit.comparisonValue:
kw['userName'] = userNameCrit.comparisonValue
wi = self.workItems wi = self.workItems
return wi.query(**kw) return wi.query(**kw)