merge branch master

This commit is contained in:
Helmut Merz 2011-11-11 15:27:12 +01:00
commit 153f0ee0e7
14 changed files with 558 additions and 11 deletions

View file

@ -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,10 +312,12 @@ class ConceptView(BaseView):
break
if skip:
continue
if useFilter:
options = IOptions(adapted(r.predicate), None)
if options is not None and options('hide_children'):
continue
if fv.check(r.context):
if not fv.check(r.context):
continue
yield r
def checkCriteria(self, relation, criteria):
@ -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)

View file

@ -23,7 +23,8 @@
</div>
<div metal:use-macro="views/relation_macros/clients" />
<div tal:define="items view/children;
<div tal:define="items python:
view.getChildren(topLevelOnly=False, useFilter=False);
action string:remove;
qualifier string:children;
summary string:Currently assigned objects;

View file

@ -159,11 +159,20 @@ class BasePart(Base):
gridPattern = []
showImage = True
@Lazy
def childPredicates(self):
preds = [self.defaultPredicate]
for name in ['ispartof', 'hasoverview']:
pred = self.conceptManager.get(name)
if pred is not None:
preds.append(pred)
return preds
def getChildren(self):
subtypeNames = (self.params.get('subtypes') or [''])[0].split(',')
subtypes = [self.conceptManager[st] for st in subtypeNames if st]
result = []
childRels = self.context.getChildRelations([self.defaultPredicate])
childRels = self.context.getChildRelations(self.childPredicates)
if subtypes:
childRels = [r for r in childRels
if r.second.conceptType in subtypes]

View file

@ -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 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')]
Fin de partie
=============

View file

@ -54,4 +54,25 @@
factory="loops.expert.browser.search.ActionExecutor"
permission="zope.ManageContent" />
<!-- reporting -->
<zope:adapter
factory="loops.browser.common.SimpleTerms"
for="loops.expert.report.ReportTypeSourceList
zope.publisher.interfaces.browser.IBrowserRequest" />
<zope:adapter
name="report.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="loops.expert.browser.report.ReportView"
permission="zope.View" />
<browser:page
name="results.html"
for="loops.interfaces.INode"
class="loops.expert.browser.report.ResultsView"
permission="zope.View" />
</configure>

22
expert/browser/report.pt Normal file
View file

@ -0,0 +1,22 @@
<html i18n:domain="loops">
<div metal:define-macro="main">
<div tal:attributes="class string:content-$level;">
<metal:block use-macro="view/concept_macros/concepttitle" />
<form method="post" name="report_data" action="results.html">
<tal:hidden define="params item/dynamicParams">
<input type="hidden"
tal:repeat="name params"
tal:attributes="name name;
value params/?name" /></tal:hidden>
<div metal:define-macro="buttons">
<input type="submit" name="report_execute" value="Execute Report"
i18n:attributes="value" />
</div>
</form>
</div>
</div>
</html>

107
expert/browser/report.py Normal file
View file

@ -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()

22
expert/browser/results.pt Normal file
View file

@ -0,0 +1,22 @@
<html i18n:domain="loops">
<div metal:define-macro="main"
tal:define="item nocall:item/virtualTarget;
report view/reportInstance">
<div tal:attributes="class string:content-$level;">
<metal:block use-macro="view/concept_macros/concepttitle" />
</div>
<div>
<a tal:attributes="href view/reportUrl">Back to the report definition</a>
</div>
<div metal:use-macro="view/resultsRenderer" />
</div>
<div metal:define-macro="results">
Default Results Listing
</div>
</html>

View file

@ -16,6 +16,35 @@
<adapter factory="loops.expert.setup.SetupManager"
name="expert" />
<!-- reporting -->
<adapter factory="loops.expert.report.Report"
provides="loops.expert.report.IReport" trusted="True" />
<class class="loops.expert.report.Report">
<require permission="zope.View"
interface="loops.expert.report.IReportSchema" />
<require permission="zope.ManageContent"
set_schema="loops.expert.report.IReportSchema" />
</class>
<adapter factory="loops.expert.report.DefaultConceptReportInstance"
name="default_concept_report"
provides="loops.expert.report.IReportInstance"
trusted="True" />
<class class="loops.expert.report.DefaultConceptReportInstance">
<require permission="zope.View"
interface="loops.expert.report.IReportInstance" />
<require permission="zope.ManageContent"
set_schema="loops.expert.report.IReportInstance" />
</class>
<utility
provides="zope.schema.interfaces.IVocabularyFactory"
component="loops.expert.report.ReportTypeSourceList"
name="loops.expert.reportTypeSource" />
<!-- includes -->
<include package=".browser" />
</configure>

143
expert/report.py Normal file
View file

@ -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'

View file

@ -180,6 +180,16 @@ So we use the PersonWorkItems view, assigning john to the query.
'creator': '33'}>]
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
=============

View file

@ -73,6 +73,19 @@
class="loops.organize.work.browser.WorkItemInfo"
permission="zope.View" />
<!-- reporting -->
<zope:adapter factory="loops.organize.work.report.WorkReportInstance"
name="work_report"
provides="loops.expert.report.IReportInstance"
trusted="True" />
<zope:class class="loops.organize.work.report.WorkReportInstance">
<require permission="zope.View"
interface="loops.expert.report.IReportInstance" />
<require permission="zope.ManageContent"
set_schema="loops.expert.report.IReportInstance" />
</zope:class>
<!-- setup -->
<zope:adapter factory="loops.organize.work.setup.SetupManager"

86
organize/work/report.py Normal file
View file

@ -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 []

61
organize/work/results.pt Normal file
View file

@ -0,0 +1,61 @@
<html i18n:domain="loops">
<div metal:define-macro="results">
<h2 i18n:translate="">Work Items</h2>
<table>
<tr>
<th tal:repeat="col view/displayedColumns"
tal:content="col/title"
i18n:translate="" />
</tr>
<tr tal:repeat="row view/results">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python: view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
</table>
</div>
<div metal:define-macro="xx_results">
Work Items Listing
<table class="listing">
<tr>
<tal:colheader repeat="column work/allColumns">
<th tal:condition="python: column in work.columns"
tal:content="column"
i18n:translate="">Task</th>
</tal:colheader>
</tr>
<tal:workitem tal:repeat="row work/listWorkItems">
<tr tal:condition="row/monthChanged">
<td class="headline"
tal:attributes="colspan python: len(work.columns)"
tal:content="row/month">2009-01</td></tr>
<tr tal:attributes="class python:
(repeat['row'].odd() and 'even' or 'odd')">
<td class="nowrap center"
tal:define="today python: row.isToday and ' today' or ''"
tal:attributes="title row/weekDay;
class string:nowrap center$today"
i18n:attributes="title"
tal:content="row/day">2007-03-30</td>
<td class="nowrap center" tal:content="row/start">17:30</td>
<td class="nowrap center" tal:content="row/end">20:00</td>
<td class="nowrap center" tal:content="row/duration">2:30</td>
<td tal:condition="python: 'Task' in work.columns">
<a tal:attributes="href row/objectData/url"
tal:content="row/objectData/title">Task</a></td>
<td tal:condition="python: 'User' in work.columns">
<a tal:attributes="href row/user/url"
tal:content="row/user/title">John</a></td>
<td tal:content="row/track/title"
tal:attributes="title row/descriptionPlain">Title</td>
</tr>
</tal:workitem>
</table>
</div>
</html>