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 Listing
+
+
+
+ | Task |
+
+
+
+
+ | 2009-01 |
+
+ | 2007-03-30 |
+ 17:30 |
+ 20:00 |
+ 2:30 |
+
+ Task |
+
+ John |
+ Title |
+
+
+
+
+
+
+