merge branch master
This commit is contained in:
commit
5f95ef439e
22 changed files with 496 additions and 148 deletions
17
README.txt
17
README.txt
|
@ -649,6 +649,23 @@ to the bottom, and to the top.
|
||||||
['m111', 'm114', 'm112', 'm113']
|
['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
|
End-user Forms and Special Views
|
||||||
================================
|
================================
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Common base class for loops browser view classes.
|
Common base class for loops browser view classes.
|
||||||
|
|
||||||
$Id$
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from cgi import parse_qs, parse_qsl
|
from cgi import parse_qs, parse_qsl
|
||||||
|
@ -146,6 +144,9 @@ class BaseView(GenericView, I18NView):
|
||||||
return self.controller.getTemplateMacros('resource', resource_macros)
|
return self.controller.getTemplateMacros('resource', resource_macros)
|
||||||
#return resource_macros.macros
|
#return resource_macros.macros
|
||||||
|
|
||||||
|
def breadcrumbs(self):
|
||||||
|
return []
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def name(self):
|
def name(self):
|
||||||
return getName(self.context)
|
return getName(self.context)
|
||||||
|
|
|
@ -139,7 +139,7 @@ class Layout(Base, ConceptView):
|
||||||
parts = (self.options('parts') or
|
parts = (self.options('parts') or
|
||||||
self.typeOptions('parts') or
|
self.typeOptions('parts') or
|
||||||
['h1', 'g3'])
|
['h1', 'g3'])
|
||||||
ti = adapted(self.context.conceptType).typeInterface
|
#ti = adapted(self.context.conceptType).typeInterface
|
||||||
for p in parts:
|
for p in parts:
|
||||||
viewName = 'lobo_' + p
|
viewName = 'lobo_' + p
|
||||||
view = component.queryMultiAdapter((self.context, self.request),
|
view = component.queryMultiAdapter((self.context, self.request),
|
||||||
|
|
|
@ -455,7 +455,6 @@ div.comment {
|
||||||
}
|
}
|
||||||
|
|
||||||
.blogpost .description {
|
.blogpost .description {
|
||||||
font-weight: bold;
|
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
padding-top: 0.4em;
|
padding-top: 0.4em;
|
||||||
|
|
|
@ -91,6 +91,10 @@ class NodeView(BaseView):
|
||||||
self.recordAccess()
|
self.recordAccess()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def title(self):
|
||||||
|
return self.context.title or getName(self.context)
|
||||||
|
|
||||||
def breadcrumbs(self):
|
def breadcrumbs(self):
|
||||||
if not self.globalOptions('showBreadcrumbs'):
|
if not self.globalOptions('showBreadcrumbs'):
|
||||||
return []
|
return []
|
||||||
|
@ -100,7 +104,13 @@ class NodeView(BaseView):
|
||||||
if menuItem != menu.context:
|
if menuItem != menu.context:
|
||||||
data.append(dict(label=menuItem.title,
|
data.append(dict(label=menuItem.title,
|
||||||
url=absoluteURL(menuItem, self.request)))
|
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=''):
|
def recordAccess(self, viewName=''):
|
||||||
target = self.virtualTargetObject
|
target = self.virtualTargetObject
|
||||||
|
|
|
@ -29,10 +29,11 @@
|
||||||
<metal:breadcrumbs define-slot="breadcrumbs">
|
<metal:breadcrumbs define-slot="breadcrumbs">
|
||||||
<div tal:define="crumbs view/breadcrumbs"
|
<div tal:define="crumbs view/breadcrumbs"
|
||||||
tal:condition="crumbs">
|
tal:condition="crumbs">
|
||||||
|
<span i18n:translate="">You are here:</span>
|
||||||
<span tal:repeat="crumb crumbs">
|
<span tal:repeat="crumb crumbs">
|
||||||
<a tal:attributes="href crumb/url"
|
<a tal:attributes="href crumb/url"
|
||||||
tal:content="crumb/label" />
|
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:breadcrumbs>
|
||||||
<div metal:define-slot="actions"></div>
|
<div metal:define-slot="actions"></div>
|
||||||
<div metal:define-slot="message"></div>
|
<div metal:define-slot="message"></div>
|
||||||
|
@ -63,7 +64,7 @@
|
||||||
tal:attributes="href string:${view/topMenu/url}/impressum">Impressum</a>)
|
tal:attributes="href string:${view/topMenu/url}/impressum">Impressum</a>)
|
||||||
<br />
|
<br />
|
||||||
Powered by
|
Powered by
|
||||||
<b><a href="http://loops.cy55.de">loops</a></b> ·
|
<b><a href="http://www.wissen-statt-suchen.de">loops</a></b> ·
|
||||||
<b><a href="http://wiki.zope.org/zope3">Zope 3</a></b> ·
|
<b><a href="http://wiki.zope.org/zope3">Zope 3</a></b> ·
|
||||||
<b><a href="http://www.python.org">Python</a></b> ·
|
<b><a href="http://www.python.org">Python</a></b> ·
|
||||||
<b><a href="http://www.dojotoolkit.org">Dojo</a></b>.
|
<b><a href="http://www.dojotoolkit.org">Dojo</a></b>.
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
/* general */
|
/* general */
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
a[href]:hover {
|
a[href]:hover {
|
||||||
|
@ -472,23 +472,37 @@ div.comment {
|
||||||
|
|
||||||
/* blog */
|
/* blog */
|
||||||
|
|
||||||
|
.blog h1.headline {
|
||||||
|
clear: both;
|
||||||
|
padding-top: 1em;
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
.blog .description {
|
.blog .description {
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blog .text {
|
||||||
|
font-size: 90%;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog img {
|
||||||
|
padding-top: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
.blogpost .description {
|
.blogpost .description {
|
||||||
font-weight: bold;
|
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
padding-top: 0.4em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blog .info, .blogpost .info {
|
.blog .info, .blogpost .info {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
padding-top: 0.4em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* calendar, work items */
|
/* calendar, work items */
|
||||||
|
|
|
@ -321,10 +321,32 @@ Micro Articles
|
||||||
|
|
||||||
>>> from loops.compound.microart.base import MicroArt
|
>>> from loops.compound.microart.base import MicroArt
|
||||||
>>> from loops.compound.microart.interfaces import IMicroArt
|
>>> from loops.compound.microart.interfaces import IMicroArt
|
||||||
>>> component.provideAdapter(BlogPost, provides=IMicroArt)
|
>>> component.provideAdapter(MicroArt, provides=IMicroArt)
|
||||||
|
|
||||||
>>> tMicroArt = addAndConfigureObject(concepts, Concept, 'microart',
|
>>> 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
|
Fin de partie
|
||||||
|
|
|
@ -43,25 +43,26 @@ class MicroArt(Compound):
|
||||||
|
|
||||||
implements(IMicroArt)
|
implements(IMicroArt)
|
||||||
|
|
||||||
_adapterAttributes = Compound._adapterAttributes + ('text',)
|
_contextAttributes = list(IMicroArt)
|
||||||
_noexportAttributes = ('text',)
|
_adapterAttributes = Compound._adapterAttributes + ('story',)
|
||||||
_textIndexAttributes = ('text',)
|
_noexportAttributes = ('story',)
|
||||||
|
_textIndexAttributes = ('story', 'insight', 'consequences', 'folloUps')
|
||||||
|
|
||||||
defaultTextContentType = 'text/restructured'
|
defaultTextContentType = 'text/html'
|
||||||
textContentType = defaultTextContentType
|
textContentType = defaultTextContentType
|
||||||
|
|
||||||
def getText(self):
|
def getStory(self):
|
||||||
res = self.getParts()
|
res = self.getParts()
|
||||||
if len(res) > 0:
|
if len(res) > 0:
|
||||||
return adapted(res[0]).data
|
return adapted(res[0]).data
|
||||||
return u''
|
return u''
|
||||||
def setText(self, value):
|
def setStory(self, value):
|
||||||
res = self.getParts()
|
res = self.getParts()
|
||||||
if len(res) > 0:
|
if len(res) > 0:
|
||||||
res = adapted(res[0])
|
res = adapted(res[0])
|
||||||
else:
|
else:
|
||||||
tTextDocument = self.conceptManager['textdocument']
|
tTextDocument = self.conceptManager['textdocument']
|
||||||
name = getName(self.context) + '_text'
|
name = getName(self.context) + '_story'
|
||||||
res = addAndConfigureObject(self.resourceManager, Resource, name,
|
res = addAndConfigureObject(self.resourceManager, Resource, name,
|
||||||
title=self.title, contentType=self.defaultTextContentType,
|
title=self.title, contentType=self.defaultTextContentType,
|
||||||
resourceType=tTextDocument)
|
resourceType=tTextDocument)
|
||||||
|
@ -70,4 +71,4 @@ class MicroArt(Compound):
|
||||||
res = adapted(res)
|
res = adapted(res)
|
||||||
res.data = value
|
res.data = value
|
||||||
notify(ObjectModifiedEvent(res.context))
|
notify(ObjectModifiedEvent(res.context))
|
||||||
text = property(getText, setText)
|
story = property(getStory, setStory)
|
||||||
|
|
|
@ -26,7 +26,7 @@ from zope import component
|
||||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||||
from zope.cachedescriptors.property import Lazy
|
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.common import adapted
|
||||||
from loops import util
|
from loops import util
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
@ -37,16 +37,31 @@ view_macros = ViewPageTemplateFile('view_macros.pt')
|
||||||
|
|
||||||
class MicroArtView(ConceptView):
|
class MicroArtView(ConceptView):
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def contentType(self):
|
||||||
|
return 'text/restructured'
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def macros(self):
|
||||||
|
return self.controller.getTemplateMacros('microart.view', view_macros)
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def macro(self):
|
def macro(self):
|
||||||
return view_macros.macros['microart']
|
return self.macros['main']
|
||||||
|
|
||||||
def render(self):
|
@Lazy
|
||||||
return self.renderText(self.data['text'], self.adapted.textContentType)
|
def story(self):
|
||||||
|
return self.renderText(self.adapted.story, self.contentType)
|
||||||
|
|
||||||
def resources(self):
|
@Lazy
|
||||||
stdPred = self.loopsRoot.getConceptManager().getDefaultPredicate()
|
def insight(self):
|
||||||
rels = self.context.getResourceRelations([stdPred])
|
return self.renderText(self.adapted.insight, self.contentType)
|
||||||
for r in rels:
|
|
||||||
yield self.childViewFactory(r, self.request, contextIsSecond=True)
|
@Lazy
|
||||||
|
def consequences(self):
|
||||||
|
return self.renderText(self.adapted.consequences, self.contentType)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def followUps(self):
|
||||||
|
return self.renderText(self.adapted.followUps, self.contentType)
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,11 @@ from loops.compound.interfaces import ICompound
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
|
||||||
|
|
||||||
|
class HtmlField(schema.Text):
|
||||||
|
|
||||||
|
__typeInfo__ = ('html',)
|
||||||
|
|
||||||
|
|
||||||
class IMicroArt(ICompound):
|
class IMicroArt(ICompound):
|
||||||
""" A short article with a few elements, for collecting
|
""" A short article with a few elements, for collecting
|
||||||
relevant information in a knowledge management environment.
|
relevant information in a knowledge management environment.
|
||||||
|
@ -34,6 +39,7 @@ class IMicroArt(ICompound):
|
||||||
|
|
||||||
# title = Ueberschrift, Thema
|
# title = Ueberschrift, Thema
|
||||||
|
|
||||||
|
#story = HtmlField( # Geschichte
|
||||||
story = schema.Text( # Geschichte
|
story = schema.Text( # Geschichte
|
||||||
title=_(u'Story'),
|
title=_(u'Story'),
|
||||||
description=_(u'The story, i.e. the main text of your '
|
description=_(u'The story, i.e. the main text of your '
|
||||||
|
@ -54,7 +60,7 @@ class IMicroArt(ICompound):
|
||||||
|
|
||||||
followUps = schema.Text( #Anschlussfragen
|
followUps = schema.Text( #Anschlussfragen
|
||||||
title=_(u'Follow-up Questions'),
|
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.'),
|
u'similar problems in the future.'),
|
||||||
required=False)
|
required=False)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<!-- ZPT macros for loops.compound.microart views -->
|
<!-- 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"
|
tal:define="data item/data"
|
||||||
class="microart">
|
class="microart">
|
||||||
<metal:block use-macro="view/concept_macros/concepttitle_only" />
|
<metal:block use-macro="view/concept_macros/concepttitle_only" />
|
||||||
|
@ -9,9 +11,17 @@
|
||||||
tal:condition="description">
|
tal:condition="description">
|
||||||
<span tal:content="structure description">Description</span>
|
<span tal:content="structure description">Description</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text"
|
<div class="text">
|
||||||
tal:content="structure item/render">Here comes the text...</div>
|
<div class="span-6 last"
|
||||||
<metal:resources use-macro="view/concept_macros/conceptchildren" />
|
tal:content="structure item/story" />
|
||||||
<metal:resources use-macro="view/concept_macros/conceptresources" />
|
<div class="span-2"
|
||||||
<metal:block use-macro="view/comment_macros/comments" />
|
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>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
|
@ -73,8 +73,7 @@ class ResultsView(NodeView):
|
||||||
@Lazy
|
@Lazy
|
||||||
def params(self):
|
def params(self):
|
||||||
params = dict(self.request.form)
|
params = dict(self.request.form)
|
||||||
if 'report_execute' in params:
|
params.pop('report_execute', None)
|
||||||
del params['report_execute']
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -85,7 +84,7 @@ class ResultsView(NodeView):
|
||||||
def reportInstance(self):
|
def reportInstance(self):
|
||||||
instance = component.getAdapter(self.report, IReportInstance,
|
instance = component.getAdapter(self.report, IReportInstance,
|
||||||
name=self.report.reportType)
|
name=self.report.reportType)
|
||||||
instance.request = self.request
|
instance.view = self
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
#@Lazy
|
#@Lazy
|
||||||
|
@ -105,3 +104,5 @@ class ResultsView(NodeView):
|
||||||
def displayedColumns(self):
|
def displayedColumns(self):
|
||||||
return self.reportInstance.getActiveOutputFields()
|
return self.reportInstance.getActiveOutputFields()
|
||||||
|
|
||||||
|
def getColumnRenderer(self, name):
|
||||||
|
return self.result_macros[name]
|
||||||
|
|
|
@ -15,8 +15,44 @@
|
||||||
|
|
||||||
|
|
||||||
<div metal:define-macro="results">
|
<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>
|
</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>
|
</html>
|
||||||
|
|
|
@ -22,9 +22,9 @@
|
||||||
provides="loops.expert.report.IReport" trusted="True" />
|
provides="loops.expert.report.IReport" trusted="True" />
|
||||||
<class class="loops.expert.report.Report">
|
<class class="loops.expert.report.Report">
|
||||||
<require permission="zope.View"
|
<require permission="zope.View"
|
||||||
interface="loops.expert.report.IReportSchema" />
|
interface="loops.expert.report.IReport" />
|
||||||
<require permission="zope.ManageContent"
|
<require permission="zope.ManageContent"
|
||||||
set_schema="loops.expert.report.IReportSchema" />
|
set_schema="loops.expert.report.IReport" />
|
||||||
</class>
|
</class>
|
||||||
|
|
||||||
<adapter factory="loops.expert.report.DefaultConceptReportInstance"
|
<adapter factory="loops.expert.report.DefaultConceptReportInstance"
|
||||||
|
|
52
expert/field.py
Normal file
52
expert/field.py
Normal 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))
|
||||||
|
|
|
@ -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 Report as BaseReport
|
||||||
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
||||||
from cybertools.composer.report.interfaces import IReport as IBaseReport
|
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.composer.report.result import ResultSet, Row
|
||||||
from cybertools.util.jeep import Jeep
|
from cybertools.util.jeep import Jeep
|
||||||
from loops.common import AdapterBase
|
from loops.common import AdapterBase
|
||||||
|
@ -40,7 +41,7 @@ from loops.util import _
|
||||||
|
|
||||||
# interfaces
|
# interfaces
|
||||||
|
|
||||||
class IReport(ILoopsAdapter):
|
class IReport(ILoopsAdapter, IReportParams):
|
||||||
""" The report adapter for the persistent object (concept) that stores
|
""" The report adapter for the persistent object (concept) that stores
|
||||||
the report in the concept map.
|
the report in the concept map.
|
||||||
"""
|
"""
|
||||||
|
@ -53,19 +54,11 @@ class IReport(ILoopsAdapter):
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
|
|
||||||
class IReportSchema(IBaseReport, IReport):
|
class IReportInstance(IBaseReport):
|
||||||
""" All report attributes - use for security declarations.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class IReportInstance(Interface):
|
|
||||||
""" The report-type-specific object (an adapter on the report) that
|
""" The report-type-specific object (an adapter on the report) that
|
||||||
does the real report execution stuff.
|
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
|
# report concept adapter and instances
|
||||||
|
|
||||||
|
@ -85,19 +78,26 @@ class ReportInstance(BaseReport):
|
||||||
|
|
||||||
rowFactory = Row
|
rowFactory = Row
|
||||||
|
|
||||||
|
view = None # set upon creation
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context):
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
def getResultsRenderer(self, name, macros):
|
def getResultsRenderer(self, name, macros):
|
||||||
return macros[name]
|
return macros[name]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def queryCriteria(self):
|
||||||
|
return self.context.queryCriteria
|
||||||
|
|
||||||
def getResults(self, dynaParams=None):
|
def getResults(self, dynaParams=None):
|
||||||
crit = self.queryCriteria
|
crit = self.queryCriteria
|
||||||
if crit is None:
|
if crit is None:
|
||||||
return []
|
return []
|
||||||
for k, v in dynaParams.items():
|
if dynaParams is not None:
|
||||||
if k in crit.parts.keys():
|
for k, v in dynaParams.items():
|
||||||
crit.parts[k].value = v
|
if k in crit.parts.keys():
|
||||||
|
crit.parts[k].value = v
|
||||||
parts = Jeep(crit.parts)
|
parts = Jeep(crit.parts)
|
||||||
result = list(self.selectObjects(parts)) # may modify parts
|
result = list(self.selectObjects(parts)) # may modify parts
|
||||||
qc = CompoundQueryCriteria(parts)
|
qc = CompoundQueryCriteria(parts)
|
||||||
|
@ -105,8 +105,21 @@ class ReportInstance(BaseReport):
|
||||||
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
|
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
|
||||||
|
|
||||||
def selectObjects(self, parts):
|
def selectObjects(self, parts):
|
||||||
|
# to be implemented by subclass
|
||||||
return []
|
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):
|
class ReportTypeSourceList(object):
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -3,7 +3,7 @@ msgstr ""
|
||||||
|
|
||||||
"Project-Id-Version: $Id$\n"
|
"Project-Id-Version: $Id$\n"
|
||||||
"POT-Creation-Date: 2007-05-22 12:00 CET\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"
|
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
|
||||||
"Language-Team: loops developers <helmutm@cy55.de>\n"
|
"Language-Team: loops developers <helmutm@cy55.de>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
@ -11,6 +11,9 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: kwrite\n"
|
"Generated-By: kwrite\n"
|
||||||
|
|
||||||
|
msgid "You are here:"
|
||||||
|
msgstr "Sie sind hier:"
|
||||||
|
|
||||||
msgid "Concept"
|
msgid "Concept"
|
||||||
msgstr "Begriff"
|
msgstr "Begriff"
|
||||||
|
|
||||||
|
@ -80,6 +83,8 @@ msgstr "Thema bearbeiten..."
|
||||||
msgid "Modify topic."
|
msgid "Modify topic."
|
||||||
msgstr "Thema ändern"
|
msgstr "Thema ändern"
|
||||||
|
|
||||||
|
# blog
|
||||||
|
|
||||||
msgid "Edit Blog Post..."
|
msgid "Edit Blog Post..."
|
||||||
msgstr "Eintrag bearbeiten..."
|
msgstr "Eintrag bearbeiten..."
|
||||||
|
|
||||||
|
@ -101,6 +106,34 @@ msgstr "Tagebucheintrag anlegen"
|
||||||
msgid "Export Blog"
|
msgid "Export Blog"
|
||||||
msgstr "Tagebuch exportieren"
|
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"
|
msgid "Glossary Item"
|
||||||
msgstr "Glossareintrag"
|
msgstr "Glossareintrag"
|
||||||
|
|
||||||
|
|
|
@ -183,12 +183,57 @@ So we use the PersonWorkItems view, assigning john to the query.
|
||||||
Work Reports
|
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.organize.work.report import WorkReportInstance
|
||||||
>>> from loops.expert.report import IReportInstance
|
>>> from loops.expert.report import IReportInstance
|
||||||
>>> component.provideAdapter(WorkReportInstance,
|
>>> component.provideAdapter(WorkReportInstance,
|
||||||
... provides=IReportInstance,
|
... provides=IReportInstance,
|
||||||
... name='work_report')
|
... 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
|
Fin de partie
|
||||||
=============
|
=============
|
||||||
|
|
|
@ -28,59 +28,192 @@ from cybertools.composer.report.base import Report
|
||||||
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
||||||
from cybertools.composer.report.field import Field
|
from cybertools.composer.report.field import Field
|
||||||
from cybertools.composer.report.result import ResultSet, Row as BaseRow
|
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 cybertools.util.jeep import Jeep
|
||||||
|
from loops.common import adapted, baseObject
|
||||||
|
from loops.expert.field import TargetField
|
||||||
from loops.expert.report import ReportInstance
|
from loops.expert.report import ReportInstance
|
||||||
|
from loops import util
|
||||||
results_template = ViewPageTemplateFile('results.pt')
|
|
||||||
|
|
||||||
|
|
||||||
task = Field('task', u'Task',
|
class DateField(Field):
|
||||||
description=u'The task to which work items belong.',
|
|
||||||
executionSteps=['query', 'output', 'sort'])
|
part = 'date'
|
||||||
work = Field('work', u'Work',
|
format = 'short'
|
||||||
description=u'The short description of the work.',
|
renderer = 'right'
|
||||||
executionSteps=['output'])
|
|
||||||
workDescription = Field('workDescription', u'Work Description',
|
def getValue(self, row):
|
||||||
description=u'The long description of the work.',
|
value = self.getRawValue(row)
|
||||||
executionSteps=['output'])
|
if value is None:
|
||||||
day = Field('day', u'Day',
|
return None
|
||||||
description=u'The day the work was done.',
|
return timeStamp2Date(value)
|
||||||
executionSteps=['output', 'sort'])
|
|
||||||
|
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',
|
dayFrom = Field('dayFrom', u'Start Day',
|
||||||
description=u'The first day from which to select work.',
|
description=u'The first day from which to select work.',
|
||||||
executionSteps=['query'])
|
executionSteps=['query'])
|
||||||
dayTo = Field('dayTo', u'End Day',
|
dayTo = Field('dayTo', u'End Day',
|
||||||
description=u'The last day until which to select work.',
|
description=u'The last day until which to select work.',
|
||||||
executionSteps=['query'])
|
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):
|
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):
|
class WorkReportInstance(ReportInstance):
|
||||||
|
|
||||||
type = "deliverables"
|
type = "work_statement"
|
||||||
label = u'Work Report'
|
label = u'Work Statement'
|
||||||
|
|
||||||
rowFactory = WorkRow
|
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):
|
def queryCriteria(self):
|
||||||
# TODO: take from persistent report where appropriate
|
form = self.view.request.form
|
||||||
crit = [LeafQueryCriteria(f.name, f.operator, None, f)
|
crit = self.context.queryCriteria or []
|
||||||
for f in self.getAllQueryFields()]
|
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)
|
return CompoundQueryCriteria(crit)
|
||||||
|
|
||||||
def getResultsRenderer(self, name, defaultMacros):
|
|
||||||
return results_template.macros[name]
|
|
||||||
|
|
||||||
def selectObjects(self, parts):
|
def selectObjects(self, parts):
|
||||||
task = parts.get('task')
|
result = []
|
||||||
if not task:
|
tasks = [util.getObjectForUid(t) for t in parts.pop('tasks').comparisonValue]
|
||||||
return []
|
for t in list(tasks):
|
||||||
return []
|
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'])
|
||||||
|
|
|
@ -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>
|
|
Loading…
Add table
Reference in a new issue