From e59356f627ee14eec9055c06e733973e684f2e96 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 8 Nov 2011 13:45:44 +0100 Subject: [PATCH 1/5] merge changes from new BlueBream version: use 'hide_children' option on predicates --- browser/concept.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/browser/concept.py b/browser/concept.py index 1b867eb..01084b0 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -18,13 +18,10 @@ """ Definition of the concept view classes. - -$Id$ """ from itertools import groupby from zope import interface, component, schema -from zope.app import zapi from zope.app.catalog.interfaces import ICatalog from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.app.container.contained import ObjectRemovedEvent @@ -43,6 +40,7 @@ from zope.publisher.interfaces.browser import IBrowserRequest from zope.schema.interfaces import IIterableSource from zope.security.proxy import removeSecurityProxy from zope.traversing.api import getName +from zope.traversing.browser import absoluteURL from cybertools.browser.action import actions from cybertools.composer.interfaces import IInstance @@ -182,7 +180,7 @@ class BaseRelationView(BaseView): @Lazy def predicateUrl(self): - return zapi.absoluteURL(self.predicate, self.request) + return absoluteURL(self.predicate, self.request) @Lazy def relevance(self): @@ -313,6 +311,9 @@ class ConceptView(BaseView): break if skip: continue + options = IOptions(adapted(r.predicate), None) + if options is not None and options('hide_children'): + continue if fv.check(r.context): yield r @@ -373,7 +374,7 @@ class ConceptView(BaseView): def parents(self): rels = sorted(self.context.getParentRelations(), - key=(lambda x: x.first.title.lower())) + key=(lambda x: x.first.title and x.first.title.lower())) for r in rels: yield self.childViewFactory(r, self.request) From ef334a50fb543df949a2f07732af81a39fb530f7 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 8 Nov 2011 15:02:34 +0100 Subject: [PATCH 2/5] better control of filtering (ignore filters and hinding of children in management views); use additional predicates for listings in lobo layouts --- browser/concept.pt | 3 ++- browser/concept.py | 22 +++++++++++++--------- browser/concept_related.pt | 3 ++- browser/lobo/standard.py | 11 ++++++++++- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/browser/concept.pt b/browser/concept.pt index 676ce76..d7a34a3 100644 --- a/browser/concept.pt +++ b/browser/concept.pt @@ -11,7 +11,8 @@
Sub-Concepts: + tal:repeat="concept python: + view.getChildren(topLevelOnly=False, useFilter=False)"> subtask diff --git a/browser/concept.py b/browser/concept.py index 01084b0..c4bf3e6 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -276,7 +276,8 @@ class ConceptView(BaseView): if r.order != pos: r.order = pos - def getChildren(self, topLevelOnly=True, sort=True, noDuplicates=True): + def getChildren(self, topLevelOnly=True, sort=True, noDuplicates=True, + useFilter=True): form = self.request.form #if form.get('loops.viewName') == 'index.html' and self.editable: if self.editable: @@ -311,11 +312,13 @@ class ConceptView(BaseView): break if skip: continue - options = IOptions(adapted(r.predicate), None) - if options is not None and options('hide_children'): - continue - if fv.check(r.context): - yield r + if useFilter: + options = IOptions(adapted(r.predicate), None) + if options is not None and options('hide_children'): + continue + if not fv.check(r.context): + continue + yield r def checkCriteria(self, relation, criteria): result = True @@ -347,9 +350,10 @@ class ConceptView(BaseView): return result def isHidden(self, pr): - if (getName(pr.second.conceptType) in - IOptions(adapted(pr.predicate))('hide_parents_for', [])): - #IOptions(pr.predicate)('hide_parents_for', [])): + predOptions = IOptions(adapted(pr.predicate)) + if predOptions('hide_parents'): + return True + if (getName(pr.second.conceptType) in predOptions('hide_parents_for', [])): return True hideRoles = None options = component.queryAdapter(adapted(pr.first), IOptions) diff --git a/browser/concept_related.pt b/browser/concept_related.pt index 1348617..0b680d0 100644 --- a/browser/concept_related.pt +++ b/browser/concept_related.pt @@ -23,7 +23,8 @@
-
Date: Wed, 9 Nov 2011 10:53:05 +0100 Subject: [PATCH 3/5] work in progress: configurable reports - basic interfaces and classes --- browser/common.py | 23 +++++++++++++++++++++++ expert/README.txt | 19 +++++++++++++++++++ expert/browser/configure.zcml | 21 +++++++++++++++++++++ expert/configure.zcml | 28 ++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/browser/common.py b/browser/common.py index 8407f8f..ad541e7 100644 --- a/browser/common.py +++ b/browser/common.py @@ -422,6 +422,8 @@ class BaseView(GenericView, I18NView): def renderDescription(self, text=None): if text is None: text = self.description + if text is None: + return u'' htmlPattern = re.compile(r'<(.+)>.+') if htmlPattern.search(text): return text @@ -827,6 +829,27 @@ class SimpleTerms(object): return (token, self.terms[token]) +class AdapterTerms(SimpleTerms): + """ Allows the selection of a named adapter from a list of tuples + (name, adapter). + + The adapter class may have a 'label' attribute for display. + The translation domain may be overridden by subclasses. + """ + + translate = _ + + def getTerm(self, value): + token, adapter = value + adapter = removeSecurityProxy(adapter) + label = getattr(adapter.__class__, 'label', token) + return SimpleTerm(token, token, self.translate(label)) + + def getValue(self, token): + # just store the name of the adapter + return (token, token) + + class LoopsTerms(object): """ Provide the ITerms interface, e.g. for usage in selection lists. diff --git a/expert/README.txt b/expert/README.txt index 4aece02..613e37b 100644 --- a/expert/README.txt +++ b/expert/README.txt @@ -219,6 +219,25 @@ Query Concepts and Query Views >>> from loops.expert.browser.base import BaseQueryView +Reports +======= + + >>> from loops.expert.report import IReport, Report + >>> component.provideAdapter(Report, provides=IReport) + + >>> report = Report(None) + + >>> from loops.expert.report import DefaultConceptReportInstance + >>> component.provideAdapter(DefaultConceptReportInstance, + ... name='default_concept_report') + + >>> from loops.expert.report import ReportTypeSourceList + >>> source = ReportTypeSourceList(report) + >>> list(source) + [(u'default_concept_report', + )] + + Fin de partie ============= diff --git a/expert/browser/configure.zcml b/expert/browser/configure.zcml index 4d71dfa..a1b4491 100644 --- a/expert/browser/configure.zcml +++ b/expert/browser/configure.zcml @@ -54,4 +54,25 @@ factory="loops.expert.browser.search.ActionExecutor" permission="zope.ManageContent" /> + + + + + + + + diff --git a/expert/configure.zcml b/expert/configure.zcml index 3eff3c3..b0ec33b 100644 --- a/expert/configure.zcml +++ b/expert/configure.zcml @@ -16,6 +16,34 @@ + + + + + + + + + + + + + + + + + + From f072066b3a73ab2bb30f1251ceb7059750bc8430 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Thu, 10 Nov 2011 08:17:12 +0100 Subject: [PATCH 4/5] work in progress: configurable reports - basic interfaces and classes, example report (work_report) --- browser/common.py | 21 --------------------- expert/README.txt | 6 +++--- expert/browser/configure.zcml | 2 +- expert/configure.zcml | 5 +++-- organize/work/README.txt | 10 ++++++++++ organize/work/configure.zcml | 13 +++++++++++++ 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/browser/common.py b/browser/common.py index ad541e7..762fba1 100644 --- a/browser/common.py +++ b/browser/common.py @@ -829,27 +829,6 @@ class SimpleTerms(object): return (token, self.terms[token]) -class AdapterTerms(SimpleTerms): - """ Allows the selection of a named adapter from a list of tuples - (name, adapter). - - The adapter class may have a 'label' attribute for display. - The translation domain may be overridden by subclasses. - """ - - translate = _ - - def getTerm(self, value): - token, adapter = value - adapter = removeSecurityProxy(adapter) - label = getattr(adapter.__class__, 'label', token) - return SimpleTerm(token, token, self.translate(label)) - - def getValue(self, token): - # just store the name of the adapter - return (token, token) - - class LoopsTerms(object): """ Provide the ITerms interface, e.g. for usage in selection lists. diff --git a/expert/README.txt b/expert/README.txt index 613e37b..0404b89 100644 --- a/expert/README.txt +++ b/expert/README.txt @@ -227,15 +227,15 @@ Reports >>> report = Report(None) - >>> from loops.expert.report import DefaultConceptReportInstance + >>> from loops.expert.report import IReportInstance, DefaultConceptReportInstance >>> component.provideAdapter(DefaultConceptReportInstance, + ... provides=IReportInstance, ... name='default_concept_report') >>> from loops.expert.report import ReportTypeSourceList >>> source = ReportTypeSourceList(report) >>> list(source) - [(u'default_concept_report', - )] + [(u'default_concept_report', u'Default Concept Report')] Fin de partie diff --git a/expert/browser/configure.zcml b/expert/browser/configure.zcml index a1b4491..2014a46 100644 --- a/expert/browser/configure.zcml +++ b/expert/browser/configure.zcml @@ -57,7 +57,7 @@ diff --git a/expert/configure.zcml b/expert/configure.zcml index b0ec33b..03543ad 100644 --- a/expert/configure.zcml +++ b/expert/configure.zcml @@ -22,13 +22,14 @@ provides="loops.expert.report.IReport" trusted="True" /> + interface="loops.expert.report.IReportSchema" /> + set_schema="loops.expert.report.IReportSchema" /> ] +Work Reports +============ + + >>> from loops.organize.work.report import WorkReportInstance + >>> from loops.expert.report import IReportInstance + >>> component.provideAdapter(WorkReportInstance, + ... provides=IReportInstance, + ... name='work_report') + + Fin de partie ============= diff --git a/organize/work/configure.zcml b/organize/work/configure.zcml index b48b04c..ccd434f 100644 --- a/organize/work/configure.zcml +++ b/organize/work/configure.zcml @@ -73,6 +73,19 @@ class="loops.organize.work.browser.WorkItemInfo" permission="zope.View" /> + + + + + + + + Date: Fri, 11 Nov 2011 15:18:03 +0100 Subject: [PATCH 5/5] work in progress: configurable reports - basic interfaces and classes, example report (work_report) --- expert/browser/report.pt | 22 ++++++ expert/browser/report.py | 107 ++++++++++++++++++++++++++++ expert/browser/results.pt | 22 ++++++ expert/report.py | 143 ++++++++++++++++++++++++++++++++++++++ organize/work/report.py | 86 +++++++++++++++++++++++ organize/work/results.pt | 61 ++++++++++++++++ 6 files changed, 441 insertions(+) create mode 100644 expert/browser/report.pt create mode 100644 expert/browser/report.py create mode 100644 expert/browser/results.pt create mode 100644 expert/report.py create mode 100644 organize/work/report.py create mode 100644 organize/work/results.pt diff --git a/expert/browser/report.pt b/expert/browser/report.pt new file mode 100644 index 0000000..e1237dd --- /dev/null +++ b/expert/browser/report.pt @@ -0,0 +1,22 @@ + + + +
+
+ +
+ + +
+ +
+
+
+
+ + + diff --git a/expert/browser/report.py b/expert/browser/report.py new file mode 100644 index 0000000..b8f5f9b --- /dev/null +++ b/expert/browser/report.py @@ -0,0 +1,107 @@ +# +# Copyright (c) 2011 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +View classes for reporting. +""" + +from urllib import urlencode +from zope import interface, component +from zope.app.pagetemplate import ViewPageTemplateFile +from zope.cachedescriptors.property import Lazy +from zope.traversing.api import getName, getParent + +from cybertools.browser.form import FormController +from loops.browser.concept import ConceptView +from loops.browser.node import NodeView +from loops.common import adapted, AdapterBase +from loops.expert.report import IReportInstance +from loops.organize.personal.browser.filter import FilterView +from loops.security.common import canWriteObject, checkPermission +from loops import util +from loops.util import _ + + +report_template = ViewPageTemplateFile('report.pt') +results_template = ViewPageTemplateFile('results.pt') + + +class ReportView(ConceptView): + + @Lazy + def report_macros(self): + return self.controller.getTemplateMacros('report', report_template) + + @Lazy + def macro(self): + return self.report_macros['main'] + + @Lazy + def dynamicParams(self): + return self.request.form + + +class ResultsView(NodeView): + + @Lazy + def result_macros(self): + return self.controller.getTemplateMacros('results', results_template) + + @Lazy + def macro(self): + return self.result_macros['main'] + + @Lazy + def item(self): + return self + + @Lazy + def params(self): + params = dict(self.request.form) + if 'report_execute' in params: + del params['report_execute'] + return params + + @Lazy + def report(self): + return adapted(self.virtualTargetObject) + + @Lazy + def reportInstance(self): + instance = component.getAdapter(self.report, IReportInstance, + name=self.report.reportType) + instance.request = self.request + return instance + + #@Lazy + def results(self): + return self.reportInstance.getResults(self.params) + + @Lazy + def resultsRenderer(self): + return self.reportInstance.getResultsRenderer('results', self.result_macros) + + @Lazy + def reportUrl(self): + url = self.virtualTargetUrl + return '?'.join((url, urlencode(self.params))) + + @Lazy + def displayedColumns(self): + return self.reportInstance.getActiveOutputFields() + diff --git a/expert/browser/results.pt b/expert/browser/results.pt new file mode 100644 index 0000000..9c892ad --- /dev/null +++ b/expert/browser/results.pt @@ -0,0 +1,22 @@ + + + +
+
+ +
+ +
+
+ + +
+ Default Results Listing +
+ + + diff --git a/expert/report.py b/expert/report.py new file mode 100644 index 0000000..fdcef10 --- /dev/null +++ b/expert/report.py @@ -0,0 +1,143 @@ +# +# Copyright (c) 2011 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Report type, report concept adapter, and other reporting stuff. +""" + +from zope import schema, component +from zope.component import adapts +from zope.interface import Interface, Attribute, implements +from zope.cachedescriptors.property import Lazy +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.result import ResultSet, Row +from cybertools.util.jeep import Jeep +from loops.common import AdapterBase +from loops.interfaces import ILoopsAdapter +from loops.type import TypeInterfaceSourceList +from loops import util +from loops.util import _ + + +# interfaces + +class IReport(ILoopsAdapter): + """ The report adapter for the persistent object (concept) that stores + the report in the concept map. + """ + + reportType = schema.Choice( + title=_(u'Report Type'), + description=_(u'The type of the report.'), + default=None, + source='loops.expert.reportTypeSource', + required=True) + + +class IReportSchema(IBaseReport, IReport): + """ All report attributes - use for security declarations. + """ + + +class IReportInstance(Interface): + """ 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 + +class Report(AdapterBase): + + implements(IReport) + + _contextAttributes = list(IReport) + +TypeInterfaceSourceList.typeInterfaces += (IReport,) + + +class ReportInstance(BaseReport): + + implements(IReportInstance) + adapts(IReport) + + rowFactory = Row + + def __init__(self, context): + self.context = context + + def getResultsRenderer(self, name, macros): + return macros[name] + + def getResults(self, dynaParams=None): + crit = self.queryCriteria + if crit is None: + return [] + for k, v in dynaParams.items(): + if k in crit.parts.keys(): + crit.parts[k].value = v + parts = Jeep(crit.parts) + result = list(self.selectObjects(parts)) # may modify parts + qc = CompoundQueryCriteria(parts) + return ResultSet(self, result, rowFactory=self.rowFactory, + sortCriteria=self.getSortCriteria(), queryCriteria=qc) + + def selectObjects(self, parts): + return [] + + +class ReportTypeSourceList(object): + + implements(schema.interfaces.IIterableSource) + + def __init__(self, context): + self.context = context + + def __iter__(self): + return iter(self.reportTypes) + + def __contains__(self, value): + return value in [item[0] for item in self] + + @Lazy + def reportTypes(self): + result = [] + for item in component.getAdapters([self.context], IReportInstance): + name, adapter = item + adapter = removeSecurityProxy(adapter) + label = getattr(adapter, 'label', name) + result.append((name, label,)) + return result + + def __len__(self): + return len(self.reportTypes) + + +# default concept report + +class DefaultConceptReportInstance(ReportInstance): + + label = u'Default Concept Report' + diff --git a/organize/work/report.py b/organize/work/report.py new file mode 100644 index 0000000..71ec849 --- /dev/null +++ b/organize/work/report.py @@ -0,0 +1,86 @@ +# +# Copyright (c) 2011 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Work report definitions. +""" + +from zope.app.pagetemplate import ViewPageTemplateFile +from zope.cachedescriptors.property import Lazy +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 +from cybertools.composer.report.result import ResultSet, Row as BaseRow +from cybertools.util.jeep import Jeep +from loops.expert.report import ReportInstance + +results_template = ViewPageTemplateFile('results.pt') + + +task = Field('task', u'Task', + description=u'The task to which work items belong.', + executionSteps=['query', 'output', 'sort']) +work = Field('work', u'Work', + description=u'The short description of the work.', + executionSteps=['output']) +workDescription = Field('workDescription', u'Work Description', + description=u'The long description of the work.', + executionSteps=['output']) +day = Field('day', u'Day', + description=u'The day the work was done.', + executionSteps=['output', 'sort']) +dayFrom = Field('dayFrom', u'Start Day', + description=u'The first day from which to select work.', + executionSteps=['query']) +dayTo = Field('dayTo', u'End Day', + description=u'The last day until which to select work.', + executionSteps=['query']) + + +class WorkRow(BaseRow): + + pass + + +class WorkReportInstance(ReportInstance): + + type = "deliverables" + label = u'Work Report' + + rowFactory = WorkRow + fields = Jeep((day, dayFrom, dayTo, task, work, workDescription)) + defaultOutputFields = fields + + @Lazy + def queryCriteria(self): + # TODO: take from persistent report where appropriate + crit = [LeafQueryCriteria(f.name, f.operator, None, f) + for f in self.getAllQueryFields()] + return CompoundQueryCriteria(crit) + + def getResultsRenderer(self, name, defaultMacros): + return results_template.macros[name] + + def selectObjects(self, parts): + task = parts.get('task') + if not task: + return [] + return [] + diff --git a/organize/work/results.pt b/organize/work/results.pt new file mode 100644 index 0000000..0d1cec0 --- /dev/null +++ b/organize/work/results.pt @@ -0,0 +1,61 @@ + + + +
+

Work Items

+ + + + + + + + +
+
+
+ + +
+ Work Items Listing + + + + + + + + + + + + + + + + + + + +
Task
2009-01
2007-03-3017:3020:002:30 + Task + JohnTitle
+
+ + +