From d0ef48e692e4499fc46563f24dcbd86bcb76ff9a Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 9 Jul 2012 11:23:12 +0200 Subject: [PATCH 1/3] provide work report view 'work.html' using its context object for the 'tasks' query criteria, use this instead of 'results.html' --- expert/browser/report.py | 9 +++++++-- organize/work/README.txt | 33 +++++++++++++++------------------ organize/work/configure.zcml | 24 ++++++++++++++++-------- organize/work/report.py | 20 +++++++++++++++++--- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/expert/browser/report.py b/expert/browser/report.py index ccfc26a..2890dc8 100644 --- a/expert/browser/report.py +++ b/expert/browser/report.py @@ -134,7 +134,7 @@ class ResultsView(NodeView): 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 @@ -175,7 +175,7 @@ class ResultsConceptView(ConceptView): return ri def results(self): - return self.reportInstance.getResults(dict(tasks=util.getUidForObject(self.context))) + return self.reportInstance.getResults() @Lazy def displayedColumns(self): @@ -183,3 +183,8 @@ class ResultsConceptView(ConceptView): def getColumnRenderer(self, col): return self.result_macros[col.renderer] + + +class ReportConceptView(ResultsConceptView): + """ View on a concept using a report. + """ diff --git a/organize/work/README.txt b/organize/work/README.txt index 5965094..1e3f88f 100644 --- a/organize/work/README.txt +++ b/organize/work/README.txt @@ -212,25 +212,23 @@ The executable report is a report instance that is an adapter to the ... provides=IReportInstance, ... name='work_report') -The user interface to the report is a report view, the results are presented -in a results view. +The user interface is a ReportConceptView subclass that is directly associated with the task. - >>> 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()) + >>> from loops.organize.work.report import WorkStatementView + >>> reportView = WorkStatementView(task01, TestRequest()) + >>> reportView.nodeView = nodeView - >>> results = resultsView.results()#.getResult() + >>> results = reportView.results() >>> len(list(results)) 1 + >>> for row in results: - ... for col in resultsView.displayedColumns: + ... for col in reportView.displayedColumns: ... print col.getDisplayValue(row), ... print 08/12/28 19:00 20:15 - {'url': '.../home/report/.36', 'title': u'loops Development'} - {'url': '.../home/report/.33', 'title': u'john'} 01:15 00:15 finished + {'url': '.../home/.36', 'title': u'loops Development'} + {'url': '.../home/.33', 'title': u'john'} 01:15 00:15 finished >>> results.totals.data {'effort': 900} @@ -266,18 +264,17 @@ report is available. ... reportType='meeting_minutes') >>> 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 - >>> input = dict(tasks=getUidForObject(ev01)) - >>> resultsView = ResultsView(home, TestRequest(form=input)) - >>> resultsView.virtualTargetObject = meetingMinutes + >>> from loops.organize.work.meeting import MeetingMinutes + >>> reportView = MeetingMinutes(ev01, TestRequest()) + >>> reportView.nodeView = nodeView - >>> results = resultsView.results() + >>> results = reportView.results() >>> len(list(results)) 1 >>> for row in results: - ... for col in resultsView.displayedColumns: + ... for col in reportView.displayedColumns: ... print col.getDisplayValue(row), ... print {'url': 'http://127.0.0.1/loops/views/home/.36', 'title': u'loops Development'} diff --git a/organize/work/configure.zcml b/organize/work/configure.zcml index 4c88e42..562df3f 100644 --- a/organize/work/configure.zcml +++ b/organize/work/configure.zcml @@ -75,10 +75,11 @@ - + @@ -86,10 +87,17 @@ set_schema="loops.expert.report.IReportInstance" /> - + + + diff --git a/organize/work/report.py b/organize/work/report.py index 937bf65..eb41a7a 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -33,13 +33,24 @@ from cybertools.util.date import timeStamp2Date from cybertools.util.format import formatDate from cybertools.util.jeep import Jeep from loops.common import adapted, baseObject +from loops.expert.browser.report import ReportConceptView from loops.expert.field import TargetField, DateField, TextField, UrlField from loops.expert.field import SubReport, SubReportField from loops.expert.report import ReportInstance from loops import util +# reporting views + +class WorkStatementView(ReportConceptView): + + reportName = 'work_statement' + + +# fields + class StateField(Field): + def getDisplayValue(self, row): value = self.getValue(row) return util._(value) @@ -178,9 +189,11 @@ class WorkReportInstance(ReportInstance): rowFactory = WorkRow fields = Jeep((dayFrom, dayTo, tasks, - day, timeStart, timeEnd, task, party, workTitle, #description, + day, timeStart, timeEnd, task, party, workTitle, + #description, duration, effort, state)) + userSettings = (dayFrom, dayTo, party) defaultOutputFields = fields defaultSortCriteria = (day, timeStart,) states = ('done', 'done_x', 'finished') @@ -192,12 +205,13 @@ class WorkReportInstance(ReportInstance): crit = self.context.queryCriteria or [] if not crit and 'tasks' not in form: f = self.fields['tasks'] - tasks = baseObject(self.context).getChildren([self.hasReportPredicate]) + tasks = [self.view.context] tasks = [util.getUidForObject(task) for task in tasks] 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)) + crit.append( + LeafQueryCriteria(f.name, f.operator, form[f.name], f)) return CompoundQueryCriteria(crit) def selectObjects(self, parts): From b924051d49bc15a09e5df1bbca16c2032cdedfd3 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 9 Jul 2012 16:31:23 +0200 Subject: [PATCH 2/3] work in progress: input of report parameters, work statement as example --- browser/node.py | 1 + expert/browser/report.pt | 58 +++++++++++++++++++++++++++++++++++++-- expert/browser/report.py | 19 +++++++++++-- expert/browser/results.pt | 3 +- expert/field.py | 8 +++++- organize/work/report.py | 35 ++++++++++++++++------- 6 files changed, 107 insertions(+), 17 deletions(-) diff --git a/browser/node.py b/browser/node.py index e247dda..5a9a238 100644 --- a/browser/node.py +++ b/browser/node.py @@ -481,6 +481,7 @@ class NodeView(BaseView): def virtualTarget(self): tv = self.viewAnnotations.get('targetView') if tv is not None: + tv.setupController() return tv return self.getViewForTarget(self.virtualTargetObject) diff --git a/expert/browser/report.pt b/expert/browser/report.pt index e1237dd..4dd38db 100644 --- a/expert/browser/report.pt +++ b/expert/browser/report.pt @@ -2,21 +2,73 @@
-
+
-
- + + +
+
+ + + + + + + + + + + + + +
:
+
+
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/expert/browser/report.py b/expert/browser/report.py index 2890dc8..3883238 100644 --- a/expert/browser/report.py +++ b/expert/browser/report.py @@ -170,7 +170,6 @@ class ResultsConceptView(ConceptView): reportType = self.reportType or self.report.reportType ri = component.getAdapter(self.report, IReportInstance, name=reportType) - #ri.view = self.nodeView ri.view = self return ri @@ -185,6 +184,22 @@ class ResultsConceptView(ConceptView): return self.result_macros[col.renderer] -class ReportConceptView(ResultsConceptView): +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 diff --git a/expert/browser/results.pt b/expert/browser/results.pt index b40267c..7d30c4f 100644 --- a/expert/browser/results.pt +++ b/expert/browser/results.pt @@ -35,7 +35,8 @@ i18n:translate="" /> - + diff --git a/expert/field.py b/expert/field.py index 9bdd7d1..502f34a 100644 --- a/expert/field.py +++ b/expert/field.py @@ -25,7 +25,7 @@ from zope import component from zope.i18n.locales import locales from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder -from cybertools.composer.report.field import Field +from cybertools.composer.report.field import Field as BaseField from cybertools.composer.report.result import ResultSet from cybertools.util.date import timeStamp2Date from loops.common import baseObject @@ -33,6 +33,12 @@ from loops.expert.report import ReportInstance from loops import util +class Field(BaseField): + + def getSelectValue(self, row): + return self.getRawValue(row) + + class TextField(Field): format = 'text/restructured' diff --git a/organize/work/report.py b/organize/work/report.py index eb41a7a..86dc356 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -26,15 +26,16 @@ from zope.component import adapter from cybertools.composer.report.base import Report 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.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.jeep import Jeep from loops.common import adapted, baseObject from loops.expert.browser.report import ReportConceptView -from loops.expert.field import TargetField, DateField, TextField, UrlField +from loops.expert.field import Field, TargetField, DateField, \ + TextField, UrlField from loops.expert.field import SubReport, SubReportField from loops.expert.report import ReportInstance from loops import util @@ -58,9 +59,10 @@ class StateField(Field): class TrackDateField(Field): + fieldType = 'date' part = 'date' format = 'short' - renderer = 'right' + cssClass = 'right' def getValue(self, row): value = self.getRawValue(row) @@ -76,6 +78,12 @@ class TrackDateField(Field): view.languageInfo.language) return u'' + def getSelectValue(self, row): + value = self.getRawValue(row) + if not value: + return '' + return timeStamp2ISO(value)[:10] + class TrackTimeField(TrackDateField): @@ -84,7 +92,7 @@ class TrackTimeField(TrackDateField): class DurationField(Field): - renderer = 'right' + cssClass = 'right' def getValue(self, row): value = self.getRawValue(row) @@ -111,11 +119,15 @@ tasks = Field('tasks', u'Tasks', # 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.', + fieldType='date', + operator=u'gt', executionSteps=['query']) -dayTo = Field('dayTo', u'End Day', +dayTo = TrackDateField('dayTo', u'End Day', description=u'The last day until which to select work.', + fieldType='date', + operator=u'le', executionSteps=['query']) day = TrackDateField('day', u'Day', description=u'The day the work was done.', @@ -132,6 +144,7 @@ task = TargetField('taskId', u'Task', executionSteps=['output']) party = TargetField('userName', u'Party', description=u'The party (usually a person) who did the work.', + fieldType='selection', executionSteps=['query', 'sort', 'output']) workTitle = Field('title', u'Title', description=u'The short description of the work.', @@ -178,7 +191,8 @@ class WorkRow(BaseRow): value = self.getDuration(attr) 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): @@ -243,8 +257,9 @@ class WorkReportInstance(ReportInstance): # TODO: take states from parts kw = dict(task=util.getUidForObject(baseObject(task)), state=self.states) - if 'userName' in parts: - kw['userName'] = parts['userName'].comparisonValue + userNameCrit = parts.get('userName') + if userNameCrit and userNameCrit.comparisonValue: + kw['userName'] = userNameCrit.comparisonValue wi = self.workItems return wi.query(**kw) From 1c40b071d812bf0c3ea47549dbeb69842e3ea82d Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 9 Jul 2012 16:45:37 +0200 Subject: [PATCH 3/3] improve styling of date input widget --- expert/browser/report.pt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expert/browser/report.pt b/expert/browser/report.pt index 4dd38db..ef0e32d 100644 --- a/expert/browser/report.pt +++ b/expert/browser/report.pt @@ -58,8 +58,8 @@ -