merge branch master - important: 'limits' parameter for reports
This commit is contained in:
commit
2fdf3a99b9
18 changed files with 422 additions and 51 deletions
|
@ -75,9 +75,32 @@ class ResultsView(NodeView):
|
||||||
@Lazy
|
@Lazy
|
||||||
def params(self):
|
def params(self):
|
||||||
params = dict(self.request.form)
|
params = dict(self.request.form)
|
||||||
params.pop('report_execute', None)
|
params = self.parseParams(params)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
def parseParams(self, params):
|
||||||
|
params.pop('report_execute', None)
|
||||||
|
if 'limits' in params:
|
||||||
|
params['limits'] = self.parseLimitsParam(params['limits'])
|
||||||
|
return params
|
||||||
|
|
||||||
|
def parseLimitsParam(self, value):
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
if isinstance(value, basestring):
|
||||||
|
limits = value.split(',')
|
||||||
|
else:
|
||||||
|
limits = value
|
||||||
|
if len(limits) < 2:
|
||||||
|
limits.append(None)
|
||||||
|
result = []
|
||||||
|
for p in limits[:2]:
|
||||||
|
if not p:
|
||||||
|
result.append(None)
|
||||||
|
else:
|
||||||
|
result.append(int(p))
|
||||||
|
return result
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def report(self):
|
def report(self):
|
||||||
return adapted(self.virtualTargetObject)
|
return adapted(self.virtualTargetObject)
|
||||||
|
@ -147,7 +170,8 @@ class ResultsConceptView(ConceptView):
|
||||||
reportType = self.reportType or self.report.reportType
|
reportType = self.reportType or self.report.reportType
|
||||||
ri = component.getAdapter(self.report, IReportInstance,
|
ri = component.getAdapter(self.report, IReportInstance,
|
||||||
name=reportType)
|
name=reportType)
|
||||||
ri.view = self.nodeView
|
#ri.view = self.nodeView
|
||||||
|
ri.view = self
|
||||||
return ri
|
return ri
|
||||||
|
|
||||||
def results(self):
|
def results(self):
|
||||||
|
|
|
@ -95,15 +95,21 @@ class ReportInstance(BaseReport):
|
||||||
crit = self.queryCriteria
|
crit = self.queryCriteria
|
||||||
if crit is None:
|
if crit is None:
|
||||||
return []
|
return []
|
||||||
|
limits = self.limits
|
||||||
if dynaParams is not None:
|
if dynaParams is not None:
|
||||||
for k, v in dynaParams.items():
|
for k, v in dynaParams.items():
|
||||||
|
if k == 'limits':
|
||||||
|
limits = v
|
||||||
|
break
|
||||||
if k in crit.parts.keys():
|
if k in crit.parts.keys():
|
||||||
crit.parts[k].comparisonValue = v
|
crit.parts[k].comparisonValue = 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)
|
||||||
return ResultSet(self, result, rowFactory=self.rowFactory,
|
return ResultSet(self, result, rowFactory=self.rowFactory,
|
||||||
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
|
sortCriteria=self.getSortCriteria(), queryCriteria=qc,
|
||||||
|
limits=limits)
|
||||||
|
|
||||||
def selectObjects(self, parts):
|
def selectObjects(self, parts):
|
||||||
# to be implemented by subclass
|
# to be implemented by subclass
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -32,6 +32,7 @@ from loops.browser.common import BaseView
|
||||||
from loops.browser.concept import ConceptView
|
from loops.browser.concept import ConceptView
|
||||||
from loops.expert.browser.report import ResultsConceptView
|
from loops.expert.browser.report import ResultsConceptView
|
||||||
from loops.knowledge.interfaces import IPerson, ITask
|
from loops.knowledge.interfaces import IPerson, ITask
|
||||||
|
from loops.knowledge.qualification import QualificationRecord
|
||||||
from loops.organize.work.browser import CreateWorkItemForm, CreateWorkItem
|
from loops.organize.work.browser import CreateWorkItemForm, CreateWorkItem
|
||||||
from loops.organize.party import getPersonForUser
|
from loops.organize.party import getPersonForUser
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
@ -120,6 +121,8 @@ class PersonQualificationView(ResultsConceptView):
|
||||||
class CreateQualificationRecordForm(CreateWorkItemForm):
|
class CreateQualificationRecordForm(CreateWorkItemForm):
|
||||||
|
|
||||||
macros = knowledge_macros
|
macros = knowledge_macros
|
||||||
|
recordManagerName = 'qualification'
|
||||||
|
trackFactory = QualificationRecord
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def macro(self):
|
def macro(self):
|
||||||
|
|
|
@ -100,6 +100,19 @@
|
||||||
factory="loops.knowledge.browser.Candidates"
|
factory="loops.knowledge.browser.Candidates"
|
||||||
permission="zope.View" />
|
permission="zope.View" />
|
||||||
|
|
||||||
|
<browser:page
|
||||||
|
name="create_qualification.html"
|
||||||
|
for="loops.interfaces.INode"
|
||||||
|
class="loops.knowledge.browser.CreateQualificationRecordForm"
|
||||||
|
permission="zope.View" />
|
||||||
|
|
||||||
|
<zope:adapter
|
||||||
|
name="create_qualification"
|
||||||
|
for="loops.browser.node.NodeView
|
||||||
|
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||||
|
factory="loops.knowledge.browser.CreateQualificationRecord"
|
||||||
|
permission="zope.View" />
|
||||||
|
|
||||||
<!-- other adapters -->
|
<!-- other adapters -->
|
||||||
|
|
||||||
<zope:adapter factory="loops.knowledge.schema.PersonSchemaFactory" />
|
<zope:adapter factory="loops.knowledge.schema.PersonSchemaFactory" />
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
type(u'competence', u'Competence', viewName=u'',
|
type(u'competence', u'Kompetenz', viewName=u'',
|
||||||
typeInterface=u'', options=u'action.portlet:edit_concept')
|
typeInterface=u'', options=u'action.portlet:create_subtype,edit_concept')
|
||||||
type(u'person', u'Person', viewName=u'',
|
type(u'person', u'Person', viewName=u'',
|
||||||
typeInterface=u'loops.knowledge.interfaces.IPerson',
|
typeInterface=u'loops.knowledge.interfaces.IPerson',
|
||||||
options=u'action.portlet:editPerson')
|
options=u'action.portlet:createQualification,editPerson')
|
||||||
type(u'task', u'Aufgabe', viewName=u'',
|
type(u'task', u'Aufgabe', viewName=u'',
|
||||||
typeInterface=u'loops.knowledge.interfaces.ITask',
|
typeInterface=u'loops.knowledge.interfaces.ITask',
|
||||||
options=u'action.portlet:createTopic,editTopic')
|
options=u'action.portlet:createTask,editTask')
|
||||||
type(u'topic', u'Thema', viewName=u'',
|
type(u'topic', u'Thema', viewName=u'',
|
||||||
typeInterface=u'loops.knowledge.interfaces.ITopic',
|
typeInterface=u'loops.knowledge.interfaces.ITopic',
|
||||||
options=u'action.portlet:createTopic,editTopic')
|
options=u'action.portlet:createTask,createTopic,editTopic')
|
||||||
type(u'training', u'Schulung', viewName=u'',
|
type(u'training', u'Schulung', viewName=u'',
|
||||||
typeInterface=u'loops.organize.interfaces.ITask',
|
typeInterface=u'loops.organize.interfaces.ITask',
|
||||||
options=u'action.portlet:create_subtype,edit_concept')
|
options=u'action.portlet:edit_concept')
|
||||||
|
|
||||||
concept(u'general', u'Allgemein', u'domain')
|
concept(u'general', u'Allgemein', u'domain')
|
||||||
concept(u'system', u'System', u'domain')
|
concept(u'system', u'System', u'domain')
|
||||||
|
@ -26,19 +26,19 @@ concept(u'issubtype', u'is Subtype', u'predicate', options=u'hide_children',
|
||||||
predicateInterface='loops.interfaces.IIsSubtype')
|
predicateInterface='loops.interfaces.IIsSubtype')
|
||||||
|
|
||||||
# structure
|
# structure
|
||||||
child(u'competence', u'general', u'standard')
|
child(u'general', u'competence', u'standard')
|
||||||
child(u'depends', u'general', u'standard')
|
child(u'general', u'depends', u'standard')
|
||||||
child(u'knows', u'general', u'standard')
|
child(u'general', u'knows', u'standard')
|
||||||
child(u'person', u'general', u'standard')
|
child(u'general', u'person', u'standard')
|
||||||
child(u'provides', u'general', u'standard')
|
child(u'general', u'provides', u'standard')
|
||||||
child(u'requires', u'general', u'standard')
|
child(u'general', u'requires', u'standard')
|
||||||
child(u'task', u'general', u'standard')
|
child(u'general', u'task', u'standard')
|
||||||
child(u'topic', u'general', u'standard')
|
child(u'general', u'topic', u'standard')
|
||||||
child(u'training', u'general', u'standard')
|
child(u'general', u'training', u'standard')
|
||||||
|
|
||||||
child(u'issubtype', u'system', u'standard')
|
child(u'system', u'issubtype', u'standard')
|
||||||
|
|
||||||
child(u'training', u'competence', u'issubtype', usePredicate=u'provides')
|
child(u'competence', u'training', u'issubtype', usePredicate=u'provides')
|
||||||
|
|
||||||
# records
|
# records
|
||||||
records(u'qualification', u'loops.knowledge.qualification.QualificationRecord')
|
records(u'qualification', u'loops.knowledge.qualification.QualificationRecord')
|
||||||
|
|
44
knowledge/data/loops_knowledge_update_de.dmp
Normal file
44
knowledge/data/loops_knowledge_update_de.dmp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
type(u'competence', u'Kompetenz', viewName=u'',
|
||||||
|
typeInterface=u'', options=u'action.portlet:create_subtype,edit_concept')
|
||||||
|
# type(u'person', u'Person', viewName=u'',
|
||||||
|
# typeInterface=u'loops.knowledge.interfaces.IPerson',
|
||||||
|
# options=u'action.portlet:editPerson')
|
||||||
|
# type(u'task', u'Aufgabe', viewName=u'',
|
||||||
|
# typeInterface=u'loops.knowledge.interfaces.ITask',
|
||||||
|
# options=u'action.portlet:createTask,editTask')
|
||||||
|
# type(u'topic', u'Thema', viewName=u'',
|
||||||
|
# typeInterface=u'loops.knowledge.interfaces.ITopic',
|
||||||
|
# options=u'action.portlet:createTask,createTopic,editTopic')
|
||||||
|
type(u'training', u'Schulung', viewName=u'',
|
||||||
|
typeInterface=u'loops.organize.interfaces.ITask',
|
||||||
|
options=u'action.portlet:edit_concept')
|
||||||
|
|
||||||
|
concept(u'general', u'Allgemein', u'domain')
|
||||||
|
concept(u'system', u'System', u'domain')
|
||||||
|
|
||||||
|
# predicates
|
||||||
|
concept(u'depends', u'depends', u'predicate')
|
||||||
|
concept(u'knows', u'knows', u'predicate')
|
||||||
|
concept(u'provides', u'provides', u'predicate')
|
||||||
|
concept(u'requires', u'requires', u'predicate')
|
||||||
|
|
||||||
|
concept(u'issubtype', u'is Subtype', u'predicate', options=u'hide_children',
|
||||||
|
predicateInterface='loops.interfaces.IIsSubtype')
|
||||||
|
|
||||||
|
# structure
|
||||||
|
child(u'general', u'competence', u'standard')
|
||||||
|
child(u'general', u'depends', u'standard')
|
||||||
|
child(u'general', u'knows', u'standard')
|
||||||
|
#child(u'general', u'person', u'standard')
|
||||||
|
child(u'general', u'provides', u'standard')
|
||||||
|
child(u'general', u'requires', u'standard')
|
||||||
|
#child(u'general', u'task', u'standard')
|
||||||
|
#child(u'general', u'topic', u'standard')
|
||||||
|
child(u'general', u'training', u'standard')
|
||||||
|
|
||||||
|
child(u'system', u'issubtype', u'standard')
|
||||||
|
|
||||||
|
child(u'competence', u'training', u'issubtype', usePredicate=u'provides')
|
||||||
|
|
||||||
|
# records
|
||||||
|
records(u'qualification', u'loops.knowledge.qualification.QualificationRecord')
|
|
@ -62,6 +62,48 @@
|
||||||
<metal:block define-macro="create_qualification">
|
<metal:block define-macro="create_qualification">
|
||||||
<form method="post" id="addQualification_form" class="dialog"
|
<form method="post" id="addQualification_form" class="dialog"
|
||||||
dojoType="dijit.form.Form">
|
dojoType="dijit.form.Form">
|
||||||
|
<input type="hidden" name="form.action" value="create_qualification" />
|
||||||
|
<input type="hidden" name="id"
|
||||||
|
tal:attributes="value request/form/id|nothing" />
|
||||||
|
<div class="heading" i18n:translate="">Add Qualification Record</div>
|
||||||
|
<div>
|
||||||
|
<label i18n:translate="" for="title">Title</label>
|
||||||
|
<div>
|
||||||
|
<input name="title" id="title" style="width: 60em"
|
||||||
|
dojoType="dijit.form.ValidationTextBox" required
|
||||||
|
tal:attributes="value view/title" /></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label i18n:translate="" for="action">Action</label>
|
||||||
|
<select name="workitem.action" id="action" style="display:none">
|
||||||
|
<option tal:repeat="action view/actions"
|
||||||
|
tal:attributes="value action/name"
|
||||||
|
tal:content="action/title"
|
||||||
|
i18n:translate="" />
|
||||||
|
</select>
|
||||||
|
<span id="target_competence">
|
||||||
|
<label i18n:translate="assign_to_competence" for="input_competence"
|
||||||
|
style="display: inline">Assign to Competence</label>
|
||||||
|
<span dojoType="dojox.data.QueryReadStore"
|
||||||
|
jsId="competence_search_store"
|
||||||
|
tal:define="types python:
|
||||||
|
view.getTypesParamsForFilteringSelect(['competence'])"
|
||||||
|
tal:attributes="url string:listConceptsForComboBox.js$types;" >
|
||||||
|
</span>
|
||||||
|
<input dojoType="dijit.form.FilteringSelect"
|
||||||
|
autoComplete="False" labelAttr="label" searchDelay="400"
|
||||||
|
name="competence" id="input_competence"
|
||||||
|
store="competence_search_store" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label i18n:translate="" for="comment">Comment</label>
|
||||||
|
<div>
|
||||||
|
<textarea name="comment" cols="80" rows="4" id="comment"
|
||||||
|
dojoType="dijit.form.SimpleTextarea"
|
||||||
|
style="width: 60em"
|
||||||
|
tal:content="view/comment"></textarea></div>
|
||||||
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<input value="Save" type="submit"
|
<input value="Save" type="submit"
|
||||||
i18n:attributes="value">
|
i18n:attributes="value">
|
||||||
|
|
|
@ -38,8 +38,12 @@ from loops.organize.work.base import WorkItem, WorkItems
|
||||||
@implementer(IStatesDefinition)
|
@implementer(IStatesDefinition)
|
||||||
def qualificationStates():
|
def qualificationStates():
|
||||||
return StatesDefinition('qualification',
|
return StatesDefinition('qualification',
|
||||||
|
State('new', 'new', ('assign',),
|
||||||
|
color='grey'),
|
||||||
State('open', 'open',
|
State('open', 'open',
|
||||||
('register', 'pass', 'fail', 'cancel', 'modify'),
|
('register',
|
||||||
|
#'pass', 'fail',
|
||||||
|
'cancel', 'modify'),
|
||||||
color='red'),
|
color='red'),
|
||||||
State('registered', 'registered',
|
State('registered', 'registered',
|
||||||
('register', 'pass', 'fail', 'unregister', 'cancel', 'modify'),
|
('register', 'pass', 'fail', 'unregister', 'cancel', 'modify'),
|
||||||
|
@ -61,14 +65,17 @@ def qualificationStates():
|
||||||
State('open_x', 'open', ('modify',), color='red'),
|
State('open_x', 'open', ('modify',), color='red'),
|
||||||
State('registered_x', 'registered', ('modify',), color='yellow'),
|
State('registered_x', 'registered', ('modify',), color='yellow'),
|
||||||
# transitions:
|
# transitions:
|
||||||
|
Transition('assign', 'assign', 'open'),
|
||||||
Transition('register', 'register', 'registered'),
|
Transition('register', 'register', 'registered'),
|
||||||
Transition('pass', 'pass', 'passed'),
|
Transition('pass', 'pass', 'passed'),
|
||||||
Transition('fail', 'fail', 'failed'),
|
Transition('fail', 'fail', 'failed'),
|
||||||
Transition('unregister', 'unregister', 'open'),
|
Transition('unregister', 'unregister', 'open'),
|
||||||
Transition('cancel', 'cancel', 'cancelled'),
|
Transition('cancel', 'cancel', 'cancelled'),
|
||||||
|
Transition('modify', 'modify', 'open'),
|
||||||
Transition('close', 'close', 'closed'),
|
Transition('close', 'close', 'closed'),
|
||||||
Transition('open', 'open', 'open'),
|
Transition('open', 'open', 'open'),
|
||||||
initialState='open')
|
#initialState='open')
|
||||||
|
initialState='new') # TODO: handle assignment to competence
|
||||||
|
|
||||||
|
|
||||||
class QualificationRecord(WorkItem):
|
class QualificationRecord(WorkItem):
|
||||||
|
|
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: 2012-05-30 12:00 CET\n"
|
"PO-Revision-Date: 2012-06-10 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"
|
||||||
|
@ -242,6 +242,9 @@ msgstr "Folgetermin anlegen..."
|
||||||
msgid "Create an event that is linked to this one."
|
msgid "Create an event that is linked to this one."
|
||||||
msgstr "Einen neuen Termin anlegen, der mit diesem verknüpft ist."
|
msgstr "Einen neuen Termin anlegen, der mit diesem verknüpft ist."
|
||||||
|
|
||||||
|
msgid "Create Follow-up Event for: $event"
|
||||||
|
msgstr "Folgetermin anlegen für: $event"
|
||||||
|
|
||||||
msgid "Edit Event..."
|
msgid "Edit Event..."
|
||||||
msgstr "Termin bearbeiten..."
|
msgstr "Termin bearbeiten..."
|
||||||
|
|
||||||
|
@ -290,6 +293,9 @@ msgstr "Besprechungsprotokoll anzeigen..."
|
||||||
msgid "Show meeting minutes for this object."
|
msgid "Show meeting minutes for this object."
|
||||||
msgstr "Besprechungsprotokoll für dieses Objekt anzeigen."
|
msgstr "Besprechungsprotokoll für dieses Objekt anzeigen."
|
||||||
|
|
||||||
|
msgid "Download Meeting Minutes"
|
||||||
|
msgstr "Besprechungsprotokoll generieren"
|
||||||
|
|
||||||
msgid "Task/Action"
|
msgid "Task/Action"
|
||||||
msgstr "Aufgabe"
|
msgstr "Aufgabe"
|
||||||
|
|
||||||
|
@ -395,6 +401,9 @@ msgstr "Thema"
|
||||||
msgid "Task"
|
msgid "Task"
|
||||||
msgstr "Aufgabe"
|
msgstr "Aufgabe"
|
||||||
|
|
||||||
|
msgid "Tasks"
|
||||||
|
msgstr "Aufgaben"
|
||||||
|
|
||||||
msgid "Domain"
|
msgid "Domain"
|
||||||
msgstr "Bereich"
|
msgstr "Bereich"
|
||||||
|
|
||||||
|
|
|
@ -260,7 +260,7 @@ Automatic security settings on persons
|
||||||
|
|
||||||
>>> from zope.traversing.api import getName
|
>>> from zope.traversing.api import getName
|
||||||
>>> list(sorted(getName(c) for c in concepts['person'].getChildren()))
|
>>> list(sorted(getName(c) for c in concepts['person'].getChildren()))
|
||||||
[u'general', u'jim', u'john', u'martha', u'person.newuser']
|
[u'jim', u'john', u'martha', u'person.newuser']
|
||||||
|
|
||||||
Person objects that have a user assigned to them receive this user
|
Person objects that have a user assigned to them receive this user
|
||||||
(principal) as their owner.
|
(principal) as their owner.
|
||||||
|
@ -390,6 +390,11 @@ Events listing
|
||||||
>>> list(listing.events())
|
>>> list(listing.events())
|
||||||
[<loops.browser.concept.ConceptRelationView ...>]
|
[<loops.browser.concept.ConceptRelationView ...>]
|
||||||
|
|
||||||
|
Creation of follow-up event
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
>>> from loops.organize.browser.event import CreateFollowUpEvent
|
||||||
|
|
||||||
|
|
||||||
Send Email to Members
|
Send Email to Members
|
||||||
=====================
|
=====================
|
||||||
|
|
|
@ -23,9 +23,11 @@ Definition of view classes and other browser related stuff for tasks.
|
||||||
import calendar
|
import calendar
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from urllib import urlencode
|
from urllib import urlencode
|
||||||
from zope import interface, component
|
from zope.app.container.interfaces import INameChooser
|
||||||
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 zope import interface, component
|
||||||
|
from zope.traversing.api import getName
|
||||||
|
|
||||||
from cybertools.browser.action import actions
|
from cybertools.browser.action import actions
|
||||||
from cybertools.meta.interfaces import IOptions
|
from cybertools.meta.interfaces import IOptions
|
||||||
|
@ -34,8 +36,12 @@ from loops.browser.concept import ConceptView
|
||||||
from loops.browser.form import CreateConceptPage, CreateConcept
|
from loops.browser.form import CreateConceptPage, CreateConcept
|
||||||
from loops.browser.form import EditConceptPage, EditConcept
|
from loops.browser.form import EditConceptPage, EditConcept
|
||||||
from loops.browser.node import NodeView
|
from loops.browser.node import NodeView
|
||||||
from loops.common import adapted
|
from loops.common import adapted, baseObject
|
||||||
|
from loops.concept import Concept
|
||||||
|
from loops.organize.work.meeting import MeetingMinutes
|
||||||
|
from loops.setup import addAndConfigureObject
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
from loops import util
|
||||||
|
|
||||||
|
|
||||||
organize_macros = ViewPageTemplateFile('view_macros.pt')
|
organize_macros = ViewPageTemplateFile('view_macros.pt')
|
||||||
|
@ -247,12 +253,39 @@ class CalendarInfo(NodeView):
|
||||||
|
|
||||||
# special forms
|
# special forms
|
||||||
|
|
||||||
class CreateFollowUpEventForm(CreateConceptPage):
|
class CreateFollowUpEventForm(CreateConceptPage, MeetingMinutes):
|
||||||
|
|
||||||
fixedType = True
|
fixedType = True
|
||||||
typeToken = '.loops/concepts/event'
|
typeToken = '.loops/concepts/event'
|
||||||
form_action = 'create_followup_event'
|
form_action = 'create_followup_event'
|
||||||
showAssignments = True
|
showAssignments = False
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def macro(self):
|
||||||
|
return organize_macros.macros['create_followup_event']
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def baseEvent(self):
|
||||||
|
return adapted(self.virtualTargetObject)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def title(self):
|
||||||
|
event = self.baseEvent
|
||||||
|
evView = ConceptView(event, self.request)
|
||||||
|
eventTitle = u'%s, %s' % (event.title, evView.data['start'])
|
||||||
|
return _(u'Create Follow-up Event for: $event',
|
||||||
|
mapping=dict(event=eventTitle))
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def data(self):
|
||||||
|
data = self.getData()
|
||||||
|
data['title'] = self.baseEvent.title
|
||||||
|
data['description'] = self.baseEvent.description
|
||||||
|
return data
|
||||||
|
|
||||||
|
def results(self):
|
||||||
|
return self.reportInstance.getResults(
|
||||||
|
dict(tasks=util.getUidForObject(self.virtualTargetObject)))
|
||||||
|
|
||||||
|
|
||||||
class EditFollowUpEventForm(EditConceptPage, CreateFollowUpEventForm):
|
class EditFollowUpEventForm(EditConceptPage, CreateFollowUpEventForm):
|
||||||
|
@ -271,6 +304,55 @@ class CreateFollowUpEvent(CreateConcept, BaseFollowUpController):
|
||||||
|
|
||||||
defaultTypeToken = '.loops/concepts/event'
|
defaultTypeToken = '.loops/concepts/event'
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def followsPredicate(self):
|
||||||
|
return self.view.conceptManager['follows']
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def baseEvent(self):
|
||||||
|
return adapted(self.view.virtualTargetObject)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
result = super(CreateFollowUpEvent, self).update()
|
||||||
|
form = self.request.form
|
||||||
|
toBeAssigned = form.get('cb_select_tasks') or []
|
||||||
|
print '***', toBeAssigned
|
||||||
|
for uid in toBeAssigned:
|
||||||
|
task = util.getObjectForUid(uid)
|
||||||
|
self.createFollowUpTask(adapted(task))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def createFollowUpTask(self, source):
|
||||||
|
cm = self.view.conceptManager
|
||||||
|
stask = baseObject(source)
|
||||||
|
bevt = baseObject(self.baseEvent)
|
||||||
|
taskType = stask.conceptType
|
||||||
|
taskName = getName(stask)
|
||||||
|
name = INameChooser(cm).chooseName(taskName, stask)
|
||||||
|
newTask = addAndConfigureObject(cm, Concept, name,
|
||||||
|
conceptType=taskType,
|
||||||
|
title=source.title,
|
||||||
|
description=source.description,
|
||||||
|
start=source.start,
|
||||||
|
end=source.end)
|
||||||
|
stask.assignChild(newTask, self.followsPredicate)
|
||||||
|
for rel in stask.getParentRelations():
|
||||||
|
if rel.predicate != self.view.typePredicate:
|
||||||
|
if rel.first == bevt:
|
||||||
|
parent = self.object
|
||||||
|
else:
|
||||||
|
parent = rel.first
|
||||||
|
newTask.assignParent(parent, rel.predicate,
|
||||||
|
order=rel.order, relevance=rel.relevance)
|
||||||
|
return newTask
|
||||||
|
|
||||||
|
def assignConcepts(self, obj):
|
||||||
|
bevt = baseObject(self.baseEvent)
|
||||||
|
bevt.assignChild(obj, self.followsPredicate)
|
||||||
|
for rel in bevt.getParentRelations():
|
||||||
|
if rel.predicate != self.view.typePredicate:
|
||||||
|
obj.assignParent(rel.first, rel.predicate)
|
||||||
|
|
||||||
|
|
||||||
class EditFollowUpEvent(EditConcept, BaseFollowUpController):
|
class EditFollowUpEvent(EditConcept, BaseFollowUpController):
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<!-- $Id$ -->
|
<html i18n:domain="loops">
|
||||||
|
|
||||||
|
|
||||||
<metal:task define-macro="task">
|
<metal:task define-macro="task">
|
||||||
<metal:data use-macro="view/concept_macros/conceptdata">
|
<metal:data use-macro="view/concept_macros/conceptdata">
|
||||||
|
@ -56,6 +55,26 @@
|
||||||
</metal:block>
|
</metal:block>
|
||||||
|
|
||||||
|
|
||||||
|
<form metal:define-macro="create_followup_event" method="post"
|
||||||
|
id="dialog_form" class="dialog"
|
||||||
|
dojoType="dijit.form.Form"
|
||||||
|
tal:define="item nocall:item|nocall:view">
|
||||||
|
<h2 tal:content="view/title"
|
||||||
|
i18n:translate="" />
|
||||||
|
<input type="hidden" name="form.action"
|
||||||
|
tal:attributes="value view/form_action" />
|
||||||
|
<metal:data use-macro="view/fieldRenderers/fields" />
|
||||||
|
<h3 i18n:translate="">Tasks</h3>
|
||||||
|
<tal:tasks define="report item/reportInstance;
|
||||||
|
reportView nocall:item;
|
||||||
|
results reportView/results;
|
||||||
|
cb_name string:cb_select_tasks;">
|
||||||
|
<metal:results use-macro="view/resultsRenderer" />
|
||||||
|
</tal:tasks>
|
||||||
|
<metal:buttons use-macro="view/form_macros/buttons" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
<form metal:define-macro="send_email"
|
<form metal:define-macro="send_email"
|
||||||
method="post" id="sendEmail_form" name="sendEmail" class="dialog"
|
method="post" id="sendEmail_form" name="sendEmail" class="dialog"
|
||||||
dojoType="dijit.form.Form">
|
dojoType="dijit.form.Form">
|
||||||
|
@ -113,3 +132,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
|
@ -307,6 +307,8 @@ class UserWorkItems(PersonWorkItems):
|
||||||
class CreateWorkItemForm(ObjectForm, BaseTrackView):
|
class CreateWorkItemForm(ObjectForm, BaseTrackView):
|
||||||
|
|
||||||
template = work_macros
|
template = work_macros
|
||||||
|
recordManagerName = 'work'
|
||||||
|
trackFactory = WorkItem
|
||||||
|
|
||||||
def checkPermissions(self):
|
def checkPermissions(self):
|
||||||
return canAccessObject(self.task or self.target)
|
return canAccessObject(self.task or self.target)
|
||||||
|
@ -325,9 +327,10 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
|
||||||
def track(self):
|
def track(self):
|
||||||
id = self.request.form.get('id')
|
id = self.request.form.get('id')
|
||||||
if id is not None:
|
if id is not None:
|
||||||
workItems = self.loopsRoot.getRecordManager()['work']
|
workItems = self.loopsRoot.getRecordManager()[
|
||||||
|
self.recordManagerName]
|
||||||
return workItems.get(id)
|
return workItems.get(id)
|
||||||
return WorkItem(None, 0, None, {})
|
return self.trackFactory(None, 0, None, {})
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def title(self):
|
def title(self):
|
||||||
|
|
|
@ -103,6 +103,12 @@
|
||||||
class="loops.organize.work.meeting.MeetingMinutes"
|
class="loops.organize.work.meeting.MeetingMinutes"
|
||||||
permission="zope.View" />
|
permission="zope.View" />
|
||||||
|
|
||||||
|
<browser:page
|
||||||
|
name="meeting_minutes.doc"
|
||||||
|
for="loops.interfaces.IConceptSchema"
|
||||||
|
class="loops.organize.work.meeting.MeetingMinutesDocument"
|
||||||
|
permission="zope.View" />
|
||||||
|
|
||||||
<!-- setup -->
|
<!-- setup -->
|
||||||
|
|
||||||
<zope:adapter factory="loops.organize.work.setup.SetupManager"
|
<zope:adapter factory="loops.organize.work.setup.SetupManager"
|
||||||
|
|
|
@ -3,19 +3,58 @@
|
||||||
|
|
||||||
<div metal:define-macro="content"
|
<div metal:define-macro="content"
|
||||||
tal:define="report item/reportInstance;
|
tal:define="report item/reportInstance;
|
||||||
reportView nocall:item">
|
reportView nocall:item;
|
||||||
|
results reportView/results">
|
||||||
<div tal:attributes="class string:content-$level;">
|
<div tal:attributes="class string:content-$level;">
|
||||||
<metal:block use-macro="view/concept_macros/concepttitle" />
|
<metal:block use-macro="view/concept_macros/concepttitle" />
|
||||||
<metal:block use-macro="view/concept_macros/conceptfields" />
|
<metal:block use-macro="view/concept_macros/conceptfields" />
|
||||||
</div><br />
|
</div><br />
|
||||||
|
<span class="button"
|
||||||
|
tal:condition="item/reportDownload">
|
||||||
|
<a target="_blank"
|
||||||
|
tal:attributes="href string:${item/targetUrl}/${item/reportDownload}"
|
||||||
|
i18n:translate="">Download Meeting Minutes</a></span>
|
||||||
|
<br />
|
||||||
<div metal:use-macro="reportView/resultsRenderer" />
|
<div metal:use-macro="reportView/resultsRenderer" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div metal:define-macro="document"
|
||||||
|
tal:define="item nocall:view;
|
||||||
|
report item/reportInstance;
|
||||||
|
reportView nocall:item;
|
||||||
|
results reportView/results;
|
||||||
|
fields results/context/fields">
|
||||||
|
<metal:header use-macro="item/macros/header" />
|
||||||
|
<metal:results use-macro="reportView/resultsRenderer" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div metal:define-macro="header"
|
||||||
|
tal:define="row results/first">
|
||||||
|
<h1 i18n:translate="">Meeting Minutes</h1>
|
||||||
|
<h2 tal:define="col fields/eventTitle">
|
||||||
|
<metal:col use-macro="python:item.getColumnRenderer(col)" /></h2>
|
||||||
|
<div>
|
||||||
|
<span tal:define="col fields/eventStart">
|
||||||
|
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span> -
|
||||||
|
<span tal:define="col fields/eventEnd">
|
||||||
|
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span>
|
||||||
|
</div>
|
||||||
|
<div tal:define="col fields/eventDescription">
|
||||||
|
<metal:col use-macro="python:item.getColumnRenderer(col)" /></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div metal:define-macro="results">
|
<div metal:define-macro="results">
|
||||||
<table class="report"
|
<table class="report"
|
||||||
tal:define="results reportView/results">
|
tal:define="showCheckboxes cb_name|nothing">
|
||||||
<tr>
|
<tr>
|
||||||
|
<th tal:condition="showCheckboxes"
|
||||||
|
style="border: 1px solid grey">
|
||||||
|
<input type="checkbox" checked
|
||||||
|
tal:attributes="onclick
|
||||||
|
string:toggleCheckBoxes(this, '$cb_name:list')" /></th>
|
||||||
<th i18n:translate=""
|
<th i18n:translate=""
|
||||||
style="border: 1px solid grey">Task/Action</th>
|
style="border: 1px solid grey">Task/Action</th>
|
||||||
<th style="border: 1px solid grey"
|
<th style="border: 1px solid grey"
|
||||||
|
@ -27,6 +66,12 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tal:task repeat="row results">
|
<tal:task repeat="row results">
|
||||||
<tr tal:repeat="colname python: ('title', 'description',)">
|
<tr tal:repeat="colname python: ('title', 'description',)">
|
||||||
|
<td tal:condition="showCheckboxes"
|
||||||
|
style="border: 1px solid grey">
|
||||||
|
<input type="checkbox" checked
|
||||||
|
tal:condition="python:colname == 'title'"
|
||||||
|
tal:attributes="name string:$cb_name:list;
|
||||||
|
value row/context/uid" /></td>
|
||||||
<td style="border: 1px solid grey"
|
<td style="border: 1px solid grey"
|
||||||
tal:define="col report/fields/?colname"
|
tal:define="col report/fields/?colname"
|
||||||
tal:attributes="class col/cssClass">
|
tal:attributes="class col/cssClass">
|
||||||
|
@ -49,6 +94,8 @@
|
||||||
<div metal:define-macro="subreport"
|
<div metal:define-macro="subreport"
|
||||||
tal:define="results python:col.getValue(row)">
|
tal:define="results python:col.getValue(row)">
|
||||||
<tr class="listing" tal:repeat="row results">
|
<tr class="listing" tal:repeat="row results">
|
||||||
|
<td tal:condition="showCheckboxes"
|
||||||
|
style="border: 1px solid grey" />
|
||||||
<td tal:repeat="col results/displayedColumns"
|
<td tal:repeat="col results/displayedColumns"
|
||||||
tal:attributes="class col/cssClass"
|
tal:attributes="class col/cssClass"
|
||||||
style="border: 1px solid grey">
|
style="border: 1px solid grey">
|
||||||
|
|
|
@ -24,6 +24,7 @@ from zope.app.pagetemplate import ViewPageTemplateFile
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
|
||||||
from cybertools.browser.action import actions
|
from cybertools.browser.action import actions
|
||||||
|
from cybertools.docgen.base import WordDocument
|
||||||
from loops.browser.action import TargetAction
|
from loops.browser.action import TargetAction
|
||||||
from loops.expert.browser.report import ResultsConceptView
|
from loops.expert.browser.report import ResultsConceptView
|
||||||
from loops.util import _
|
from loops.util import _
|
||||||
|
@ -41,6 +42,7 @@ actions.register('meeting_minutes', 'portlet', TargetAction,
|
||||||
class MeetingMinutes(ResultsConceptView):
|
class MeetingMinutes(ResultsConceptView):
|
||||||
|
|
||||||
reportName = 'meeting_minutes'
|
reportName = 'meeting_minutes'
|
||||||
|
reportDownload = 'meeting_minutes.doc'
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
def meeting_macros(self):
|
def meeting_macros(self):
|
||||||
|
@ -59,3 +61,25 @@ class MeetingMinutes(ResultsConceptView):
|
||||||
if renderer == 'subreport':
|
if renderer == 'subreport':
|
||||||
return self.meeting_macros[renderer]
|
return self.meeting_macros[renderer]
|
||||||
return self.result_macros[renderer]
|
return self.result_macros[renderer]
|
||||||
|
|
||||||
|
|
||||||
|
class MeetingMinutesDocument(WordDocument, MeetingMinutes):
|
||||||
|
|
||||||
|
isToplevel = True
|
||||||
|
|
||||||
|
def __init__(self, context, request):
|
||||||
|
MeetingMinutes.__init__(self, context, request)
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def macros(self):
|
||||||
|
return meeting_template.macros
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def reportRenderer(self):
|
||||||
|
return self.macros['document']
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def content(self):
|
||||||
|
return self.reportRenderer
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,14 +26,14 @@ from zope.component import adapter
|
||||||
|
|
||||||
from cybertools.composer.report.base import Report
|
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, CalculatedField
|
||||||
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.organize.interfaces import IWorkItems
|
||||||
from cybertools.util.date import timeStamp2Date
|
from cybertools.util.date import timeStamp2Date
|
||||||
from cybertools.util.format import formatDate
|
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.common import adapted, baseObject
|
||||||
from loops.expert.field import TargetField, TextField, UrlField
|
from loops.expert.field import TargetField, DateField, TextField, UrlField
|
||||||
from loops.expert.field import SubReport, SubReportField
|
from loops.expert.field import SubReport, SubReportField
|
||||||
from loops.expert.report import ReportInstance
|
from loops.expert.report import ReportInstance
|
||||||
from loops import util
|
from loops import util
|
||||||
|
@ -44,7 +44,8 @@ class StateField(Field):
|
||||||
value = self.getValue(row)
|
value = self.getValue(row)
|
||||||
return util._(value)
|
return util._(value)
|
||||||
|
|
||||||
class DateField(Field):
|
|
||||||
|
class TrackDateField(Field):
|
||||||
|
|
||||||
part = 'date'
|
part = 'date'
|
||||||
format = 'short'
|
format = 'short'
|
||||||
|
@ -65,7 +66,7 @@ class DateField(Field):
|
||||||
return u''
|
return u''
|
||||||
|
|
||||||
|
|
||||||
class TimeField(DateField):
|
class TrackTimeField(TrackDateField):
|
||||||
|
|
||||||
part = 'time'
|
part = 'time'
|
||||||
|
|
||||||
|
@ -105,14 +106,14 @@ dayFrom = Field('dayFrom', u'Start Day',
|
||||||
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',
|
day = TrackDateField('day', u'Day',
|
||||||
description=u'The day the work was done.',
|
description=u'The day the work was done.',
|
||||||
cssClass='center',
|
cssClass='center',
|
||||||
executionSteps=['sort', 'output'])
|
executionSteps=['sort', 'output'])
|
||||||
timeStart = TimeField('start', u'Start',
|
timeStart = TrackTimeField('start', u'Start',
|
||||||
description=u'The time the unit of work was started.',
|
description=u'The time the unit of work was started.',
|
||||||
executionSteps=['sort', 'output'])
|
executionSteps=['sort', 'output'])
|
||||||
timeEnd = TimeField('end', u'End',
|
timeEnd = TrackTimeField('end', u'End',
|
||||||
description=u'The time the unit of work was finished.',
|
description=u'The time the unit of work was finished.',
|
||||||
executionSteps=['output'])
|
executionSteps=['output'])
|
||||||
task = TargetField('taskId', u'Task',
|
task = TargetField('taskId', u'Task',
|
||||||
|
@ -272,7 +273,21 @@ class MeetingMinutesWork(WorkReportInstance, SubReport):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
taskTitle = UrlField('title', u'Title',
|
eventTitle = CalculatedField('eventTitle', u'Event Title',
|
||||||
|
description=u'',
|
||||||
|
executionSteps=(['header']))
|
||||||
|
eventDescription = CalculatedField('eventDescription', u'Event Description',
|
||||||
|
description=u'',
|
||||||
|
executionSteps=(['header']))
|
||||||
|
eventStart = DateField('eventStart', u'Event Start',
|
||||||
|
description=u'',
|
||||||
|
format=('dateTime', 'short'),
|
||||||
|
executionSteps=(['header']))
|
||||||
|
eventEnd = DateField('eventEnd', u'Event End',
|
||||||
|
description=u'',
|
||||||
|
format=('dateTime', 'short'),
|
||||||
|
executionSteps=(['header']))
|
||||||
|
taskTitle = UrlField('title', u'Task Title',
|
||||||
description=u'The short description of the task.',
|
description=u'The short description of the task.',
|
||||||
cssClass='header-1',
|
cssClass='header-1',
|
||||||
executionSteps=['output'])
|
executionSteps=['output'])
|
||||||
|
@ -288,27 +303,46 @@ workItems = SubReportField('workItems', u'Work Items',
|
||||||
|
|
||||||
class TaskRow(BaseRow):
|
class TaskRow(BaseRow):
|
||||||
|
|
||||||
pass
|
@Lazy
|
||||||
|
def event(self):
|
||||||
|
return self.parent.context.view.adapted
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def eventTitle(self):
|
||||||
|
return self.event.title
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def eventDescription(self):
|
||||||
|
return self.event.description
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def eventStart(self):
|
||||||
|
return self.event.start
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
def eventEnd(self):
|
||||||
|
return self.event.end
|
||||||
|
|
||||||
|
useRowProperty = BaseRow.useRowProperty
|
||||||
|
attributeHandlers = dict(
|
||||||
|
eventStart=useRowProperty,
|
||||||
|
eventEnd=useRowProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MeetingMinutes(WorkReportInstance):
|
class MeetingMinutes(WorkReportInstance):
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# header (event) fields: title, description, from/to,
|
|
||||||
# location, participants (or put in description?)
|
|
||||||
# result set field for work items
|
|
||||||
# work item fields: title, description, party, deadline, state
|
|
||||||
|
|
||||||
type = "meeting_minutes"
|
type = "meeting_minutes"
|
||||||
label = u'Meeting Minutes'
|
label = u'Meeting Minutes'
|
||||||
|
|
||||||
rowFactory = TaskRow
|
rowFactory = TaskRow
|
||||||
|
|
||||||
fields = Jeep((tasks, taskTitle, taskDescription, workItems))
|
fields = Jeep((eventTitle, eventStart, eventEnd, eventDescription,
|
||||||
|
tasks, taskTitle, taskDescription, workItems))
|
||||||
defaultOutputFields = fields
|
defaultOutputFields = fields
|
||||||
states = ('planned', 'accepted', 'done', 'done_x', 'finished')
|
states = ('planned', 'accepted', 'done', 'done_x', 'finished')
|
||||||
|
|
||||||
def selectObjects(self, parts):
|
def selectObjects(self, parts):
|
||||||
return self.getTasks(parts)[1:]
|
return [adapted(t) for t in self.getTasks(parts)[1:]]
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue