merge branch master

This commit is contained in:
Helmut Merz 2011-12-02 10:16:56 +01:00
commit 5f95ef439e
22 changed files with 496 additions and 148 deletions

View file

@ -649,6 +649,23 @@ to the bottom, and to the top.
['m111', 'm114', 'm112', 'm113']
Breadcrumbs
-----------
>>> view = NodeView(m112, TestRequest())
>>> view.breadcrumbs()
[]
>>> loopsRoot.options = ['showBreadcrumbs']
>>> m114.nodeType = 'page'
>>> m114.target = cc1
>>> view = NodeView(m114, TestRequest())
>>> view.breadcrumbs()
[{'url': 'http://127.0.0.1/loops/views/m1', 'label': u'Menu'},
{'url': 'http://127.0.0.1/loops/views/m1/m11', 'label': u'Zope'},
{'url': 'http://127.0.0.1/loops/views/m1/m11/m114', 'label': u''}]
End-user Forms and Special Views
================================

View file

@ -18,8 +18,6 @@
"""
Common base class for loops browser view classes.
$Id$
"""
from cgi import parse_qs, parse_qsl
@ -146,6 +144,9 @@ class BaseView(GenericView, I18NView):
return self.controller.getTemplateMacros('resource', resource_macros)
#return resource_macros.macros
def breadcrumbs(self):
return []
@Lazy
def name(self):
return getName(self.context)

View file

@ -139,7 +139,7 @@ class Layout(Base, ConceptView):
parts = (self.options('parts') or
self.typeOptions('parts') or
['h1', 'g3'])
ti = adapted(self.context.conceptType).typeInterface
#ti = adapted(self.context.conceptType).typeInterface
for p in parts:
viewName = 'lobo_' + p
view = component.queryMultiAdapter((self.context, self.request),

View file

@ -455,7 +455,6 @@ div.comment {
}
.blogpost .description {
font-weight: bold;
font-size: 90%;
color: #666666;
padding-top: 0.4em;

View file

@ -91,6 +91,10 @@ class NodeView(BaseView):
self.recordAccess()
return result
@Lazy
def title(self):
return self.context.title or getName(self.context)
def breadcrumbs(self):
if not self.globalOptions('showBreadcrumbs'):
return []
@ -100,7 +104,13 @@ class NodeView(BaseView):
if menuItem != menu.context:
data.append(dict(label=menuItem.title,
url=absoluteURL(menuItem, self.request)))
return data
for p in getParents(menuItem):
if p == menu.context:
break
data.insert(1, dict(label=p.title,
url=absoluteURL(p, self.request)))
if self.virtualTarget:
data.extend(self.virtualTarget.breadcrumbs())
def recordAccess(self, viewName=''):
target = self.virtualTargetObject

View file

@ -29,10 +29,11 @@
<metal:breadcrumbs define-slot="breadcrumbs">
<div tal:define="crumbs view/breadcrumbs"
tal:condition="crumbs">
<span i18n:translate="">You are here:</span>
<span tal:repeat="crumb crumbs">
<a tal:attributes="href crumb/url"
tal:content="crumb/label" />
<span tal:condition="not:repeat/crumb/end"> / </span></span>
<span tal:condition="not:repeat/crumb/end"> > </span></span>
</div></metal:breadcrumbs>
<div metal:define-slot="actions"></div>
<div metal:define-slot="message"></div>
@ -63,7 +64,7 @@
tal:attributes="href string:${view/topMenu/url}/impressum">Impressum</a>)
<br />
Powered by
<b><a href="http://loops.cy55.de">loops</a></b> &middot;
<b><a href="http://www.wissen-statt-suchen.de">loops</a></b> &middot;
<b><a href="http://wiki.zope.org/zope3">Zope 3</a></b> &middot;
<b><a href="http://www.python.org">Python</a></b> &middot;
<b><a href="http://www.dojotoolkit.org">Dojo</a></b>.

View file

@ -13,7 +13,7 @@
/* general */
h1, h2, h3, h4, h5, h6 {
margin-bottom: 0.5em;
margin-bottom: 0.4em;
}
a[href]:hover {
@ -472,23 +472,37 @@ div.comment {
/* blog */
.blog h1.headline {
clear: both;
padding-top: 1em;
margin-bottom: 0.2em;
font-size: 150%;
}
.blog .description {
font-size: 90%;
color: #666666;
}
.blog .text {
font-size: 90%;
margin-top: 0.5em;
}
.blog img {
padding-top: 0.2em;
}
.blogpost .description {
font-weight: bold;
font-size: 90%;
color: #666666;
padding-top: 0.4em;
margin-top: 1em;
}
.blog .info, .blogpost .info {
font-style: italic;
font-size: 90%;
color: #666666;
padding-top: 0.4em;
}
/* calendar, work items */

View file

@ -321,10 +321,32 @@ Micro Articles
>>> from loops.compound.microart.base import MicroArt
>>> from loops.compound.microart.interfaces import IMicroArt
>>> component.provideAdapter(BlogPost, provides=IMicroArt)
>>> component.provideAdapter(MicroArt, provides=IMicroArt)
>>> tMicroArt = addAndConfigureObject(concepts, Concept, 'microart',
... title=u'MicroArt', conceptType=tType)
... title=u'MicroArt', conceptType=tType,
... typeInterface=IMicroArt)
>>> ma01 = addAndConfigureObject(concepts, Concept, 'ma01',
... conceptType=tMicroArt,
... title=u'Organizational Knowledge',
... story=u'Systemic KM talks about organizational knowledge.',
... insight=u'Organizational knowledge is not visible.',
... consequences=u'Use examples. Look for strucure and rules. '
... u'Knowledge shows itself in actions.',
... followUps=u'What about collective intelligence? '
... u'How does an organization express itself?')
>>> ma01._insight
u'Organizational knowledge is not visible.'
>>> list(resources)
[..., u'ma01_story']
>>> adMa01 = adapted(ma01)
>>> adMa01.insight
u'Organizational knowledge is not visible.'
>>> adMa01.story
u'Systemic KM talks about organizational knowledge.'
Fin de partie

View file

@ -43,25 +43,26 @@ class MicroArt(Compound):
implements(IMicroArt)
_adapterAttributes = Compound._adapterAttributes + ('text',)
_noexportAttributes = ('text',)
_textIndexAttributes = ('text',)
_contextAttributes = list(IMicroArt)
_adapterAttributes = Compound._adapterAttributes + ('story',)
_noexportAttributes = ('story',)
_textIndexAttributes = ('story', 'insight', 'consequences', 'folloUps')
defaultTextContentType = 'text/restructured'
defaultTextContentType = 'text/html'
textContentType = defaultTextContentType
def getText(self):
def getStory(self):
res = self.getParts()
if len(res) > 0:
return adapted(res[0]).data
return u''
def setText(self, value):
def setStory(self, value):
res = self.getParts()
if len(res) > 0:
res = adapted(res[0])
else:
tTextDocument = self.conceptManager['textdocument']
name = getName(self.context) + '_text'
name = getName(self.context) + '_story'
res = addAndConfigureObject(self.resourceManager, Resource, name,
title=self.title, contentType=self.defaultTextContentType,
resourceType=tTextDocument)
@ -70,4 +71,4 @@ class MicroArt(Compound):
res = adapted(res)
res.data = value
notify(ObjectModifiedEvent(res.context))
text = property(getText, setText)
story = property(getStory, setStory)

View file

@ -26,7 +26,7 @@ from zope import component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from loops.browser.concept import ConceptView, ConceptRelationView
from loops.browser.concept import ConceptView
from loops.common import adapted
from loops import util
from loops.util import _
@ -37,16 +37,31 @@ view_macros = ViewPageTemplateFile('view_macros.pt')
class MicroArtView(ConceptView):
@Lazy
def contentType(self):
return 'text/restructured'
@Lazy
def macros(self):
return self.controller.getTemplateMacros('microart.view', view_macros)
@Lazy
def macro(self):
return view_macros.macros['microart']
return self.macros['main']
def render(self):
return self.renderText(self.data['text'], self.adapted.textContentType)
@Lazy
def story(self):
return self.renderText(self.adapted.story, self.contentType)
def resources(self):
stdPred = self.loopsRoot.getConceptManager().getDefaultPredicate()
rels = self.context.getResourceRelations([stdPred])
for r in rels:
yield self.childViewFactory(r, self.request, contextIsSecond=True)
@Lazy
def insight(self):
return self.renderText(self.adapted.insight, self.contentType)
@Lazy
def consequences(self):
return self.renderText(self.adapted.consequences, self.contentType)
@Lazy
def followUps(self):
return self.renderText(self.adapted.followUps, self.contentType)

View file

@ -27,6 +27,11 @@ from loops.compound.interfaces import ICompound
from loops.util import _
class HtmlField(schema.Text):
__typeInfo__ = ('html',)
class IMicroArt(ICompound):
""" A short article with a few elements, for collecting
relevant information in a knowledge management environment.
@ -34,6 +39,7 @@ class IMicroArt(ICompound):
# title = Ueberschrift, Thema
#story = HtmlField( # Geschichte
story = schema.Text( # Geschichte
title=_(u'Story'),
description=_(u'The story, i.e. the main text of your '
@ -54,7 +60,7 @@ class IMicroArt(ICompound):
followUps = schema.Text( #Anschlussfragen
title=_(u'Follow-up Questions'),
description=_(u'Question for helping to solve or avoid '
description=_(u'Questions for helping to solve or avoid '
u'similar problems in the future.'),
required=False)

View file

@ -1,6 +1,8 @@
<!-- ZPT macros for loops.compound.microart views -->
<html i18n:domain="loops">
<div metal:define-macro="microart"
<div metal:define-macro="main"
tal:define="data item/data"
class="microart">
<metal:block use-macro="view/concept_macros/concepttitle_only" />
@ -9,9 +11,17 @@
tal:condition="description">
<span tal:content="structure description">Description</span>
</div>
<div class="text"
tal:content="structure item/render">Here comes the text...</div>
<metal:resources use-macro="view/concept_macros/conceptchildren" />
<metal:resources use-macro="view/concept_macros/conceptresources" />
<metal:block use-macro="view/comment_macros/comments" />
<div class="text">
<div class="span-6 last"
tal:content="structure item/story" />
<div class="span-2"
tal:content="structure item/insight" />
<div class="span-2"
tal:content="structure item/consequences" />
<div class="span-2 last"
tal:content="structure item/followUps" /></div><br clear="both" />
<metal:block use-macro="view/comment_macros/comments" />
</div>
</html>

View file

@ -73,8 +73,7 @@ class ResultsView(NodeView):
@Lazy
def params(self):
params = dict(self.request.form)
if 'report_execute' in params:
del params['report_execute']
params.pop('report_execute', None)
return params
@Lazy
@ -85,7 +84,7 @@ class ResultsView(NodeView):
def reportInstance(self):
instance = component.getAdapter(self.report, IReportInstance,
name=self.report.reportType)
instance.request = self.request
instance.view = self
return instance
#@Lazy
@ -105,3 +104,5 @@ class ResultsView(NodeView):
def displayedColumns(self):
return self.reportInstance.getActiveOutputFields()
def getColumnRenderer(self, name):
return self.result_macros[name]

View file

@ -15,8 +15,44 @@
<div metal:define-macro="results">
Default Results Listing
<table tal:define="results view/results">
<tr>
<th tal:repeat="col view/displayedColumns"
tal:content="col/title"
i18n:translate="" />
</tr>
<tr tal:repeat="row results">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python:view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
<tr tal:define="row nocall:results/totals"
tal:condition="nocall:row">
<tal:column repeat="col view/displayedColumns">
<metal:column use-macro="python:view.getColumnRenderer(col.renderer)" />
</tal:column>
</tr>
</table>
</div>
<metal:standard define-macro="standard">
<td tal:content="structure python:col.getDisplayValue(row)" />
</metal:standard>
<metal:right define-macro="right">
<td style="text-align: right"
tal:content="python:col.getDisplayValue(row)" />
</metal:right>
<metal:target define-macro="target">
<td tal:define="value python:col.getDisplayValue(row)">
<a tal:omit-tag="not:value/url"
tal:attributes="href value/url"
tal:content="value/title" /></td>
</metal:target>
</html>

View file

@ -22,9 +22,9 @@
provides="loops.expert.report.IReport" trusted="True" />
<class class="loops.expert.report.Report">
<require permission="zope.View"
interface="loops.expert.report.IReportSchema" />
interface="loops.expert.report.IReport" />
<require permission="zope.ManageContent"
set_schema="loops.expert.report.IReportSchema" />
set_schema="loops.expert.report.IReport" />
</class>
<adapter factory="loops.expert.report.DefaultConceptReportInstance"

52
expert/field.py Normal file
View file

@ -0,0 +1,52 @@
#
# 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
#
"""
Field definitions for reports.
"""
from cybertools.composer.report.field import Field
from loops import util
class UrlField(Field):
renderer = 'target'
def getDisplayValue(self, row):
nv = row.parent.context.view.nodeView
return dict(title=self.getValue(row), url=nv.getUrlForTarget(row.context))
class TargetField(Field):
renderer = 'target'
def getValue(self, row):
value = self.getRawValue(row)
if value is None:
return None
return util.getObjectForUid(value)
def getDisplayValue(self, row):
value = self.getValue(row)
if value is None:
return dict(title=u'', url=u'')
view = row.parent.context.view
return dict(title=value.title, url=view.getUrlForTarget(value))

View file

@ -29,6 +29,7 @@ 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.interfaces import IReportParams
from cybertools.composer.report.result import ResultSet, Row
from cybertools.util.jeep import Jeep
from loops.common import AdapterBase
@ -40,7 +41,7 @@ from loops.util import _
# interfaces
class IReport(ILoopsAdapter):
class IReport(ILoopsAdapter, IReportParams):
""" The report adapter for the persistent object (concept) that stores
the report in the concept map.
"""
@ -53,19 +54,11 @@ class IReport(ILoopsAdapter):
required=True)
class IReportSchema(IBaseReport, IReport):
""" All report attributes - use for security declarations.
"""
class IReportInstance(Interface):
class IReportInstance(IBaseReport):
""" 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
@ -85,19 +78,26 @@ class ReportInstance(BaseReport):
rowFactory = Row
view = None # set upon creation
def __init__(self, context):
self.context = context
def getResultsRenderer(self, name, macros):
return macros[name]
@property
def queryCriteria(self):
return self.context.queryCriteria
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
if dynaParams is not None:
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)
@ -105,8 +105,21 @@ class ReportInstance(BaseReport):
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
def selectObjects(self, parts):
# to be implemented by subclass
return []
@Lazy
def conceptManager(self):
return self.view.conceptManager
@Lazy
def recordManager(self):
return self.view.loopsRoot.getRecordManager()
@Lazy
def hasReportPredicate(self):
return self.conceptManager['hasreport']
class ReportTypeSourceList(object):

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: $Id$\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
"PO-Revision-Date: 2011-10-31 12:00 CET\n"
"PO-Revision-Date: 2011-12-02 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: loops developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n"
@ -11,6 +11,9 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: kwrite\n"
msgid "You are here:"
msgstr "Sie sind hier:"
msgid "Concept"
msgstr "Begriff"
@ -80,6 +83,8 @@ msgstr "Thema bearbeiten..."
msgid "Modify topic."
msgstr "Thema ändern"
# blog
msgid "Edit Blog Post..."
msgstr "Eintrag bearbeiten..."
@ -101,6 +106,34 @@ msgstr "Tagebucheintrag anlegen"
msgid "Export Blog"
msgstr "Tagebuch exportieren"
# micro article
msgid "Story"
msgstr "Story"
msgid "The story, i.e. the main text of your micro article. Who did what? What happend?"
msgstr "Die Geschichte, der Haupttext Ihres MikroArtikels. Wer hat was getan? Was geschah?"
msgid "Insight"
msgstr "Einsicht"
msgid "What can we learn from the story? What has gone wrong? What was good?"
msgstr "Was können wir aus der Geschichte lernen? Was ist schiefgegangen? Was war gut?"
msgid "Consequences"
msgstr "Folgerungen"
msgid "What we will do next time in a similar situation?"
msgstr "Was werden wir das nächste Mal in einer ähnlichen Situation tun?"
msgid "Follow-up Questions"
msgstr "Anschlussfragen"
msgid "Questions for helping to solve or avoid similar problems in the future."
msgstr "Fragen, die einem dabei helfen können, das Problem in der Zukunft zu lösen oder zu vermeiden."
# glossary
msgid "Glossary Item"
msgstr "Glossareintrag"

View file

@ -183,12 +183,57 @@ So we use the PersonWorkItems view, assigning john to the query.
Work Reports
============
First we have to make sure that there is a report concept type available.
In addition we need a predicate that connects one or more tasks with a report.
>>> from loops.expert.report import IReport, Report
>>> component.provideAdapter(Report)
>>> tReport = addAndConfigureObject(concepts, Concept, 'report',
... title=u'Report', conceptType=concepts.getTypeConcept(),
... typeInterface=IReport)
>>> hasReport = addAndConfigureObject(concepts, Concept, 'hasreport',
... title=u'has Report', conceptType=concepts.getPredicateType())
Now we can create a report and register it as the report for the task
used above.
>>> workStatement = addAndConfigureObject(concepts, Concept, 'work_statement',
... title=u'Work Statement', conceptType=tReport,
... reportType='work_report')
>>> workStatement.assignChild(task01, hasReport)
The executable report is a report instance that is an adapter to the
(adapted) report instance.
>>> from loops.organize.work.report import WorkReportInstance
>>> from loops.expert.report import IReportInstance
>>> component.provideAdapter(WorkReportInstance,
... provides=IReportInstance,
... name='work_report')
The user interface to the report is a report view, the results are presented
in a results view.
>>> 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())
>>> results = resultsView.results()#.getResult()
>>> len(list(results))
1
>>> for row in results:
... for col in resultsView.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
>>> results.totals.data
{'effort': 900}
Fin de partie
=============

View file

@ -28,59 +28,192 @@ 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.organize.interfaces import IWorkItems
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.field import TargetField
from loops.expert.report import ReportInstance
results_template = ViewPageTemplateFile('results.pt')
from loops import util
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'])
class DateField(Field):
part = 'date'
format = 'short'
renderer = 'right'
def getValue(self, row):
value = self.getRawValue(row)
if value is None:
return None
return timeStamp2Date(value)
def getDisplayValue(self, row):
value = self.getValue(row)
if value:
view = row.parent.context.view
return formatDate(value, self.part, self.format,
view.languageInfo.language)
return u''
class TimeField(DateField):
part = 'time'
class DurationField(Field):
renderer = 'right'
def getValue(self, row):
value = self.getRawValue(row)
if value and 'totals' in self.executionSteps:
data = row.parent.totals.data
data[self.name] = data.get(self.name, 0) + value
if value:
value /= 3600.0
return value
def getDisplayValue(self, row):
value = self.getValue(row)
if not value:
return u''
return u'%02i:%02i' % divmod(value * 60, 60)
tasks = Field('tasks', u'Tasks',
description=u'The tasks from which work items should be selected.',
executionSteps=['query'])
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'])
day = DateField('day', u'Day',
description=u'The day the work was done.',
executionSteps=['sort', 'output'])
timeStart = TimeField('start', u'Start',
description=u'The time the unit of work was started.',
executionSteps=['sort', 'output'])
timeEnd = TimeField('end', u'End',
description=u'The time the unit of work was finished.',
executionSteps=['output'])
task = TargetField('taskId', u'Task',
description=u'The task to which work items belong.',
executionSteps=['output'])
party = TargetField('userName', u'Party',
description=u'The party (usually a person) who did the work.',
executionSteps=['query', 'sort', 'output'])
title = Field('title', u'Title',
description=u'The short description of the work.',
executionSteps=['output'])
description = Field('description', u'Description',
description=u'The long description of the work.',
executionSteps=['x_output'])
duration = DurationField('duration', u'Duration',
description=u'The duration of the work.',
executionSteps=['output'])
effort = DurationField('effort', u'Effort',
description=u'The effort of the work.',
executionSteps=['output', 'totals'])
state = Field('state', u'State',
description=u'The state of the work.',
executionSteps=['query', 'output'])
class WorkRow(BaseRow):
pass
def getRawValue(self, attr):
if attr in self.attributeHandlers:
return self.attributeHandlers[attr](self, attr)
track = self.context
if attr in track.metadata_attributes:
return getattr(track, attr)
return track.data.get(attr, u'')
def getDay(self, attr):
return self.context.timeStamp
def getDuration(self, attr):
value = self.context.data.get('duration')
if value is None:
value = self.getRawValue('end') - self.getRawValue('start')
return value
def getEffort(self, attr):
value = self.context.data.get('effort')
if value is None:
value = self.getDuration(attr)
return value
attributeHandlers = dict(day=getDay, duration=getDuration, effort=getEffort)
class WorkReportInstance(ReportInstance):
type = "deliverables"
label = u'Work Report'
type = "work_statement"
label = u'Work Statement'
rowFactory = WorkRow
fields = Jeep((day, dayFrom, dayTo, task, work, workDescription))
defaultOutputFields = fields
@Lazy
fields = Jeep((dayFrom, dayTo, tasks,
day, timeStart, timeEnd, task, party, title, description,
duration, effort, state))
defaultOutputFields = fields
defaultSortCriteria = (day, timeStart,)
@property
def queryCriteria(self):
# TODO: take from persistent report where appropriate
crit = [LeafQueryCriteria(f.name, f.operator, None, f)
for f in self.getAllQueryFields()]
form = self.view.request.form
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 = [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))
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 []
result = []
tasks = [util.getObjectForUid(t) for t in parts.pop('tasks').comparisonValue]
for t in list(tasks):
tasks.extend(self.getAllSubtasks(t))
for t in tasks:
result.extend(self.selectWorkItems(t, parts))
# remove parts already used for selection from parts list:
parts.pop('userName', None)
return result
def selectWorkItems(self, task, parts):
states = ['done', 'done_x', 'finished']
kw = dict(task=util.getUidForObject(task), state=states)
if 'userName' in parts:
kw['userName'] = parts['userName'].comparisonValue
wi = self.workItems
return wi.query(**kw)
def getAllSubtasks(self, concept):
result = []
for c in concept.getChildren():
if c.conceptType in self.taskTypes:
result.append(c)
result.extend(self.getAllSubtasks(c))
return result
@Lazy
def taskTypes(self):
return (self.conceptManager['task'],
self.conceptManager['event'],
self.conceptManager['project'])
@Lazy
def workItems(self):
return IWorkItems(self.recordManager['work'])

View file

@ -1,61 +0,0 @@
<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>