work in progress: task management with work items
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3091 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
429f1d33c4
commit
92bacf05d4
5 changed files with 151 additions and 33 deletions
81
organize/work/README.txt
Normal file
81
organize/work/README.txt
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
===============================================================
|
||||||
|
loops - Linked Objects for Organization and Processing Services
|
||||||
|
===============================================================
|
||||||
|
|
||||||
|
($Id$)
|
||||||
|
|
||||||
|
Let's do some basic setup
|
||||||
|
|
||||||
|
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||||
|
>>> site = placefulSetUp(True)
|
||||||
|
>>> from zope import component, interface
|
||||||
|
>>> from zope.publisher.browser import TestRequest
|
||||||
|
|
||||||
|
and set up a simple loops site with a concept manager and some concepts
|
||||||
|
(with all the type machinery, what in real life is done via standard
|
||||||
|
ZCML setup):
|
||||||
|
|
||||||
|
>>> from loops.organize.setup import SetupManager
|
||||||
|
>>> component.provideAdapter(SetupManager, name='organize')
|
||||||
|
>>> from loops.organize.work.setup import SetupManager
|
||||||
|
>>> component.provideAdapter(SetupManager, name='organize.work')
|
||||||
|
|
||||||
|
>>> from loops.tests.setup import TestSite
|
||||||
|
>>> t = TestSite(site)
|
||||||
|
>>> concepts, resources, views = t.setup()
|
||||||
|
|
||||||
|
|
||||||
|
Work Items - Plannning and Recording Activities for Tasks
|
||||||
|
=========================================================
|
||||||
|
|
||||||
|
>>> loopsRoot = concepts.getLoopsRoot()
|
||||||
|
>>> records = loopsRoot.getRecordManager()
|
||||||
|
|
||||||
|
More setup
|
||||||
|
----------
|
||||||
|
|
||||||
|
In order to be able to login and store favorites and other personal data
|
||||||
|
we have to prepare our environment. We need some basic adapter registrations,
|
||||||
|
and a pluggable authentication utility with a principal folder.
|
||||||
|
|
||||||
|
>>> from loops.organize.tests import setupObjectsForTesting
|
||||||
|
>>> setupData = setupObjectsForTesting(site, concepts)
|
||||||
|
>>> johnC = setupData.johnC
|
||||||
|
|
||||||
|
We also assign a task as a target to the home node so that we are able
|
||||||
|
to assign work items to this task.
|
||||||
|
|
||||||
|
>>> tTask = concepts['task']
|
||||||
|
>>> home = views['home']
|
||||||
|
>>> from loops.concept import Concept
|
||||||
|
>>> from loops.setup import addAndConfigureObject
|
||||||
|
>>> task01 = addAndConfigureObject(concepts, Concept, 'loops_dev',
|
||||||
|
... title=u'loops Development', conceptType=tTask)
|
||||||
|
>>> home.target = task01
|
||||||
|
|
||||||
|
Forms for adding and editing work items
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
New work items are created using a CreateWorkItemForm.
|
||||||
|
|
||||||
|
>>> from loops.organize.work.browser import CreateWorkItemForm, CreateWorkItem
|
||||||
|
>>> form = CreateWorkItemForm(home, TestRequest())
|
||||||
|
|
||||||
|
When this form is submitted, a form controller is automatically created
|
||||||
|
for the view on the currently shown node. The data from the form is processed
|
||||||
|
by calling the form controller's update method
|
||||||
|
|
||||||
|
>>> input = {'form.action': 'create_workitem', 'workitem.action': 'finish'}
|
||||||
|
>>> request = TestRequest(form=input)
|
||||||
|
>>> from loops.browser.node import NodeView
|
||||||
|
>>> view = NodeView(home, request)
|
||||||
|
>>> cwiController = CreateWorkItem(view, request)
|
||||||
|
|
||||||
|
>>> cwiController.update()
|
||||||
|
False
|
||||||
|
|
||||||
|
|
||||||
|
Fin de partie
|
||||||
|
=============
|
||||||
|
|
||||||
|
>>> placefulTearDown()
|
|
@ -31,11 +31,11 @@ from zope.traversing.browser import absoluteURL
|
||||||
from zope.traversing.api import getName
|
from zope.traversing.api import getName
|
||||||
|
|
||||||
from cybertools.browser.action import actions
|
from cybertools.browser.action import actions
|
||||||
from cybertools.tracking.browser import TrackView
|
|
||||||
from loops.browser.action import DialogAction
|
from loops.browser.action import DialogAction
|
||||||
from loops.browser.form import ObjectForm, EditObject
|
from loops.browser.form import ObjectForm, EditObject
|
||||||
from loops.organize.tracking.browser import BaseTrackView
|
from loops.organize.tracking.browser import BaseTrackView
|
||||||
from loops import util
|
from loops import util
|
||||||
|
from loops.util import _
|
||||||
|
|
||||||
|
|
||||||
work_macros = ViewPageTemplateFile('work_macros.pt')
|
work_macros = ViewPageTemplateFile('work_macros.pt')
|
||||||
|
@ -46,7 +46,7 @@ class WorkItemView(BaseTrackView):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CreateWorkItemForm(ObjectForm):
|
class CreateWorkItemForm(ObjectForm, BaseTrackView):
|
||||||
|
|
||||||
template = work_macros
|
template = work_macros
|
||||||
|
|
||||||
|
@ -63,6 +63,14 @@ class CreateWorkItemForm(ObjectForm):
|
||||||
return time.strftime('%Y-%m-%dT%H:%M')
|
return time.strftime('%Y-%m-%dT%H:%M')
|
||||||
|
|
||||||
|
|
||||||
|
class CreateWorkItem(EditObject, BaseTrackView):
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
url = self.view.virtualTargetUrl + '?version=this'
|
||||||
|
self.request.response.redirect(url)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
|
|
||||||
actions.register('createWorkitem', 'portlet', DialogAction,
|
actions.register('createWorkitem', 'portlet', DialogAction,
|
||||||
|
|
|
@ -28,6 +28,15 @@
|
||||||
class="loops.organize.work.browser.CreateWorkItemForm"
|
class="loops.organize.work.browser.CreateWorkItemForm"
|
||||||
permission="zope.ManageContent" />
|
permission="zope.ManageContent" />
|
||||||
|
|
||||||
|
<zope:adapter
|
||||||
|
name="create_workitem"
|
||||||
|
for="loops.browser.node.NodeView
|
||||||
|
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||||
|
factory="loops.organize.work.browser.CreateWorkItem"
|
||||||
|
permission="zope.View" />
|
||||||
|
|
||||||
|
<!-- setup -->
|
||||||
|
|
||||||
<zope:adapter factory="loops.organize.work.setup.SetupManager"
|
<zope:adapter factory="loops.organize.work.setup.SetupManager"
|
||||||
name="organize.work" />
|
name="organize.work" />
|
||||||
|
|
||||||
|
|
22
organize/work/tests.py
Executable file
22
organize/work/tests.py
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
import unittest, doctest
|
||||||
|
from zope.testing.doctestunit import DocFileSuite
|
||||||
|
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
"Basic tests for the loops.organize.work package."
|
||||||
|
|
||||||
|
def testBasics(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_suite():
|
||||||
|
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||||
|
return unittest.TestSuite((
|
||||||
|
unittest.makeSuite(Test),
|
||||||
|
DocFileSuite('README.txt', optionflags=flags),
|
||||||
|
))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(defaultTest='test_suite')
|
|
@ -7,6 +7,7 @@
|
||||||
id="addWorkitem_form" class="dialog"
|
id="addWorkitem_form" class="dialog"
|
||||||
dojoType="dijit.form.Form">
|
dojoType="dijit.form.Form">
|
||||||
<input type="hidden" name="form.action" value="create_workitem" />
|
<input type="hidden" name="form.action" value="create_workitem" />
|
||||||
|
<input type="hidden" name="workitem.action" value="finish" />
|
||||||
<div class="heading" i18n:translate="">Add Work Item</div>
|
<div class="heading" i18n:translate="">Add Work Item</div>
|
||||||
<div>
|
<div>
|
||||||
<label i18n:translate=""
|
<label i18n:translate=""
|
||||||
|
@ -17,34 +18,33 @@
|
||||||
style="width: 60em"></textarea></div>
|
style="width: 60em"></textarea></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label i18n:translate=""
|
<label i18n:translate=""
|
||||||
for="start-end">Start - End</label>
|
for="start-end">Start - End</label>
|
||||||
<div id="start-end">
|
<div id="start-end">
|
||||||
<input type="text" name="field" style="width: 8em"
|
<input type="text" style="width: 8em"
|
||||||
dojoType="dijit.form.DateTextBox"
|
dojoType="dijit.form.DateTextBox"
|
||||||
name="start_date" required="true"
|
name="start_date" required="true"
|
||||||
tal:attributes="value view/defaultDate" />
|
tal:attributes="value view/defaultDate" />
|
||||||
<input type="text" name="field" style="width: 6em"
|
<input type="text" style="width: 6em"
|
||||||
dojoType="dijit.form.TimeTextBox"
|
dojoType="dijit.form.TimeTextBox"
|
||||||
name="start_time" required="true"
|
name="start_time" required="true"
|
||||||
tal:attributes="value view/defaultTime" /> -
|
tal:attributes="value view/defaultTime" /> -
|
||||||
<input type="text" name="field" style="width: 6em"
|
<input type="text" style="width: 6em"
|
||||||
dojoType="dijit.form.TimeTextBox"
|
dojoType="dijit.form.TimeTextBox"
|
||||||
name="end_time" required="true"
|
name="end_time" required="true"
|
||||||
tal:attributes="value view/defaultTime" />
|
tal:attributes="value view/defaultTime" /></div>
|
||||||
</div>
|
<label i18n:translate=""
|
||||||
<label i18n:translate=""
|
for="duration-effort">Duration / Effort (hh:mm)</label>
|
||||||
for="duration-effort">Duration / Effort (hh:mm)</label>
|
<div id="duration-effort">
|
||||||
<div id="duration-effort">
|
<input type="text" style="width: 5em"
|
||||||
<input type="text" style="width: 5em"
|
dojoType="dijit.form.ValidationTextBox"
|
||||||
dojoType="dijit.form.ValidationTextBox"
|
regexp="[0-9]{1,2}(:[0-5][0-9]){0,1}"
|
||||||
regexp="[0-9]{1,2}(:[0-5][0-9]){0,1}"
|
name="duration" /> /
|
||||||
name="duration" /> /
|
<input type="text" style="width: 5em"
|
||||||
<input type="text" style="width: 5em"
|
dojoType="dijit.form.ValidationTextBox"
|
||||||
dojoType="dijit.form.ValidationTextBox"
|
regexp="[0-9]{1,2}(:[0-5][0-9]){0,1}"
|
||||||
regexp="[0-9]{1,2}(:[0-5][0-9]){0,1}"
|
name="effort" /></div>
|
||||||
name="effort" />
|
</div>
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<label i18n:translate=""
|
<label i18n:translate=""
|
||||||
for="comment">Comment</label>
|
for="comment">Comment</label>
|
||||||
|
@ -53,15 +53,13 @@
|
||||||
dojoType="dijit.form.SimpleTextarea"
|
dojoType="dijit.form.SimpleTextarea"
|
||||||
style="width: 60em"></textarea></div>
|
style="width: 60em"></textarea></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<input value="Save" type="submit"
|
<input value="Save" type="submit"
|
||||||
onClick="return closeDialog(true)"
|
onClick="return closeDialog(true)"
|
||||||
i18n:attributes="value">
|
i18n:attributes="value">
|
||||||
<input type="button" value="Cancel"
|
<input type="button" value="Cancel"
|
||||||
onClick="return closeDialog(false)"
|
onClick="return closeDialog(false)"
|
||||||
i18n:attributes="value">
|
i18n:attributes="value"></div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</metal:block>
|
</metal:block>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue