Author: janguenot
Date: Tue Oct 11 20:41:55 2005
New Revision: 28150

Added:
   z3lab/xpdlcore/trunk/tests/test_xpdl.py   (contents, props changed)
   z3lab/xpdlcore/trunk/tests/xpdl.txt   (contents, props changed)
   z3lab/xpdlcore/trunk/xpdl.py   (contents, props changed)
Modified:
   z3lab/xpdlcore/trunk/DEPENDENCIES.txt
Log:
Add the xpdl reader generation zope.wfmc objects directly here avoiding having 
to maintain a local zope.wfmc before the lxml proposal acceptation on the Zope3 
trunk

Modified: z3lab/xpdlcore/trunk/DEPENDENCIES.txt
==============================================================================
--- z3lab/xpdlcore/trunk/DEPENDENCIES.txt       (original)
+++ z3lab/xpdlcore/trunk/DEPENDENCIES.txt       Tue Oct 11 20:41:55 2005
@@ -3,5 +3,6 @@
 - lxml >= 0.7
   http://codespeak.net/lxml/
 
-- Zope3 schema
-- Zope3 interface
+- zope.schema
+- zope.interface
+- zope.wfmc

Added: z3lab/xpdlcore/trunk/tests/test_xpdl.py
==============================================================================
--- (empty file)
+++ z3lab/xpdlcore/trunk/tests/test_xpdl.py     Tue Oct 11 20:41:55 2005
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nuxeo and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests the xpdl facilities generating zope.wfmc objects
+
+$Id$
+"""
+
+import os
+import unittest
+
+import zope.event
+from zope.testing import doctest
+from zope.component import testing
+
+def tearDown(test):
+    testing.tearDown(test)
+    zope.event.subscribers.pop()
+
+def setUp(test):
+    test.globs['this_directory'] = os.path.dirname(__file__)
+    testing.setUp(test)
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(doctest.DocFileSuite(
+        'xpdl.txt', tearDown=tearDown, setUp=setUp,
+        optionflags=doctest.NORMALIZE_WHITESPACE))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: z3lab/xpdlcore/trunk/tests/xpdl.txt
==============================================================================
--- (empty file)
+++ z3lab/xpdlcore/trunk/tests/xpdl.txt Tue Oct 11 20:41:55 2005
@@ -0,0 +1,357 @@
+XPDL Import
+===========
+
+We can import process definitions from files in the XML Process
+Definition Language (XPDL) format. An XPDL file contains multiple
+process definitions arranged in a package. When we load the file, we
+get a package containing some numner of process definitions.
+
+Let's look at an example.  The file `publication.xpdl`
+contains a definition for the publication example developed in the
+"README.txt" file.  We can read it using the xpdl module:
+
+    >>> from zope.wfmc import xpdl
+    >>> import os
+    >>> package = xpdl.read(open(os.path.join(this_directory, 
+    ...                                       'examples/publication.xpdl')))
+
+This package contains a single definition:
+
+    >>> package
+    {u'Publication': ProcessDefinition(u'Publication')}
+
+    >>> pd = package[u'Publication']
+    >>> from zope.wfmc.attributeintegration import AttributeIntegration
+    >>> integration = AttributeIntegration()
+    >>> pd.integration = integration
+
+Now, having read the process definition, we can use it as we did
+before (in "README.txt").  As before, we'll create an event subscriber
+so that we can see what's going on:
+
+    >>> def log_workflow(event):
+    ...     print event
+
+    >>> import zope.event
+    >>> zope.event.subscribers.append(log_workflow)
+
+and we'll register the process definition as a utility:
+
+    >>> import zope.component
+    >>> zope.component.provideUtility(pd, name=pd.id)
+
+and we'll define and register participant and application adapters:
+
+    >>> import zope.interface
+    >>> from zope.wfmc import interfaces
+
+    >>> class Participant(object):
+    ...     zope.component.adapts(interfaces.IActivity)
+    ...     zope.interface.implements(interfaces.IParticipant)
+    ...
+    ...     def __init__(self, activity):
+    ...         self.activity = activity
+
+    >>> class User:
+    ...     def __init__(self):
+    ...         self.work_list = []
+
+    >>> authors = {'bob': User(), 'ted': User(), 'sally': User()}
+
+    >>> reviewer = User()
+    >>> tech1 = User()
+    >>> tech2 = User()
+
+    >>> class Author(Participant):
+    ...     def __init__(self, activity):
+    ...         Participant.__init__(self, activity)
+    ...         author_name = activity.process.workflowRelevantData.author
+    ...         self.user = authors[author_name]
+
+    >>> integration.authorParticipant = Author
+
+    >>> class Reviewer(Participant):
+    ...     user = reviewer
+    >>> integration.reviewerParticipant = Reviewer
+
+    >>> class Tech1(Participant):
+    ...     user = tech1
+    >>> integration.tech1Participant = Tech1
+
+    >>> class Tech2(Participant):
+    ...     user = tech2
+    >>> integration.tech2Participant = Tech2
+
+    >>> integration.SystemParticipant = Participant
+
+    >>> class ApplicationBase(object):
+    ...     zope.component.adapts(interfaces.IParticipant)
+    ...     zope.interface.implements(interfaces.IWorkItem)
+    ...
+    ...     def __init__(self, participant):
+    ...         self.participant = participant
+    ...         self.activity = participant.activity
+    ...         participant.user.work_list.append(self)
+    ...
+    ...     def start(self):
+    ...         pass
+    ...
+    ...     def finish(self):
+    ...         self.participant.activity.workItemFinished(self)
+
+    >>> class Prepare(ApplicationBase):
+    ...
+    ...     def summary(self):
+    ...         process = self.activity.process
+    ...         doc = getattr(process.applicationRelevantData, 'doc', '')
+    ...         if doc:
+    ...             print 'Previous draft:'
+    ...             print doc
+    ...             print 'Changed we need to make:'
+    ...             for change in process.workflowRelevantData.tech_changes:
+    ...                 print change
+    ...         else:
+    ...             print 'Please write the initial draft'
+    ...
+    ...     def finish(self, doc):
+    ...         self.activity.process.applicationRelevantData.doc = doc
+    ...         super(Prepare, self).finish()
+
+    >>> integration.prepareWorkItem = Prepare
+
+    >>> class TechReview(ApplicationBase):
+    ...
+    ...     def getDoc(self):
+    ...         return self.activity.process.applicationRelevantData.doc
+    ...
+    ...     def finish(self, decision, changes):
+    ...         self.activity.workItemFinished(self, decision, changes)
+
+    >>> integration.tech_reviewWorkItem = TechReview
+
+    >>> class Review(TechReview):
+    ...
+    ...     def start(self, publish1, changes1, publish2, changes2):
+    ...         if not (publish1 and publish2):
+    ...             # Reject if either tech reviewer rejects
+    ...             self.activity.workItemFinished(
+    ...                 self, False, changes1 + changes2, ())
+    ...           
+    ...         if changes1 or changes2:
+    ...             # we won't do anyting if there are tech changes
+    ...             self.activity.workItemFinished(
+    ...                 self, True, changes1 + changes2, ())
+    ...
+    ...     def finish(self, ed_changes):
+    ...         self.activity.workItemFinished(self, True, (), ed_changes)
+
+    >>> integration.ed_reviewWorkItem = Review
+
+    >>> class Final(ApplicationBase):
+    ...
+    ...     def summary(self):
+    ...         process = self.activity.process
+    ...         doc = getattr(process.applicationRelevantData, 'doc', '')
+    ...         print 'Previous draft:'
+    ...         print self.activity.process.applicationRelevantData.doc
+    ...         print 'Changed we need to make:'
+    ...         for change in process.workflowRelevantData.ed_changes:
+    ...            print change
+    ...
+    ...     def finish(self, doc):
+    ...         self.activity.process.applicationRelevantData.doc = doc
+    ...         super(Final, self).finish()
+
+    >>> integration.finalWorkItem = Final
+
+    >>> class ReviewFinal(TechReview):
+    ...
+    ...     def finish(self, ed_changes):
+    ...         self.activity.workItemFinished(self, ed_changes)
+
+    >>> integration.rfinalWorkItem = ReviewFinal
+
+
+    >>> class Publish:
+    ...     zope.component.adapts(interfaces.IParticipant)
+    ...     zope.interface.implements(interfaces.IWorkItem)
+    ...
+    ...     def __init__(self, participant):
+    ...         self.participant = participant
+    ...
+    ...     def start(self):
+    ...         print "Published"
+    ...         self.finish()
+    ...
+    ...     def finish(self):
+    ...         self.participant.activity.workItemFinished(self)
+
+
+    >>> integration.publishWorkItem = Publish
+
+    >>> class Reject(Publish):
+    ...     def start(self):
+    ...         print "Rejected"
+    ...         self.finish()
+    
+    >>> integration.rejectWorkItem = Reject
+
+and a process context, so we can pass parameters:
+
+    >>> class PublicationContext:
+    ...     zope.interface.implements(interfaces.IProcessContext)
+    ...
+    ...     def processFinished(self, process, decision):
+    ...         self.decision = decision
+
+Now, let's try out our process.  We'll follow the same steps we did in
+"README.txt", getting the same results:
+
+    >>> context = PublicationContext()
+    >>> proc = pd(context)
+    >>> proc.start('bob')
+    ProcessStarted(Process(u'Publication'))
+    Transition(None, Activity(u'Publication.start'))
+    ActivityStarted(Activity(u'Publication.start'))
+    ActivityFinished(Activity(u'Publication.start'))
+    Transition(Activity(u'Publication.start'), 
+               Activity(u'Publication.prepare'))
+    ActivityStarted(Activity(u'Publication.prepare'))
+
+    >>> item = authors['bob'].work_list.pop()
+    >>> item.finish("I give my pledge, as an American\n"
+    ...             "to save, and faithfully to defend from waste\n"
+    ...             "the natural resources of my Country.")
+    WorkItemFinished(u'prepare')
+    ActivityFinished(Activity(u'Publication.prepare'))
+    Transition(Activity(u'Publication.prepare'), 
+               Activity(u'Publication.tech1'))
+    ActivityStarted(Activity(u'Publication.tech1'))
+    Transition(Activity(u'Publication.prepare'), 
+               Activity(u'Publication.tech2'))
+    ActivityStarted(Activity(u'Publication.tech2'))
+
+    >>> item = tech1.work_list.pop()
+    >>> print item.getDoc()   
+    I give my pledge, as an American
+    to save, and faithfully to defend from waste
+    the natural resources of my Country.
+
+    >>> item.finish(True, ['Change "American" to "human"'])
+    WorkItemFinished(u'tech_review')
+    ActivityFinished(Activity(u'Publication.tech1'))
+    Transition(Activity(u'Publication.tech1'), 
+               Activity(u'Publication.review'))
+
+    >>> item = tech2.work_list.pop()
+    >>> item.finish(True, ['Change "Country" to "planet"'])
+    WorkItemFinished(u'tech_review')
+    ActivityFinished(Activity(u'Publication.tech2'))
+    Transition(Activity(u'Publication.tech2'), 
+               Activity(u'Publication.review'))
+    ActivityStarted(Activity(u'Publication.review'))
+    WorkItemFinished(u'ed_review')
+    ActivityFinished(Activity(u'Publication.review'))
+    Transition(Activity(u'Publication.review'), 
+               Activity(u'Publication.prepare'))
+    ActivityStarted(Activity(u'Publication.prepare'))
+
+    >>> item = authors['bob'].work_list.pop()
+    >>> item.summary()
+    Previous draft:
+    I give my pledge, as an American
+    to save, and faithfully to defend from waste
+    the natural resources of my Country.
+    Changed we need to make:
+    Change "American" to "human"
+    Change "Country" to "planet"
+
+    >>> item.finish("I give my pledge, as an human\n"
+    ...             "to save, and faithfully to defend from waste\n"
+    ...             "the natural resources of my planet.")
+    WorkItemFinished(u'prepare')
+    ActivityFinished(Activity(u'Publication.prepare'))
+    Transition(Activity(u'Publication.prepare'), 
+               Activity(u'Publication.tech1'))
+    ActivityStarted(Activity(u'Publication.tech1'))
+    Transition(Activity(u'Publication.prepare'),
+               Activity(u'Publication.tech2'))
+    ActivityStarted(Activity(u'Publication.tech2'))
+
+    >>> item = tech1.work_list.pop()
+    >>> item.finish(True, [])
+    WorkItemFinished(u'tech_review')
+    ActivityFinished(Activity(u'Publication.tech1'))
+    Transition(Activity(u'Publication.tech1'),
+               Activity(u'Publication.review'))
+
+    >>> item = tech2.work_list.pop()
+    >>> item.finish(True, [])
+    WorkItemFinished(u'tech_review')
+    ActivityFinished(Activity(u'Publication.tech2'))
+    Transition(Activity(u'Publication.tech2'),
+               Activity(u'Publication.review'))
+    ActivityStarted(Activity(u'Publication.review'))
+
+    >>> item = reviewer.work_list.pop()
+    >>> print item.getDoc()
+    I give my pledge, as an human
+    to save, and faithfully to defend from waste
+    the natural resources of my planet.
+
+    >>> item.finish(['change "an" to "a"'])
+    WorkItemFinished(u'ed_review')
+    ActivityFinished(Activity(u'Publication.review'))
+    Transition(Activity(u'Publication.review'),
+               Activity(u'Publication.final'))
+    ActivityStarted(Activity(u'Publication.final'))
+
+    >>> item = authors['bob'].work_list.pop()
+    >>> item.summary()
+    Previous draft:
+    I give my pledge, as an human
+    to save, and faithfully to defend from waste
+    the natural resources of my planet.
+    Changed we need to make:
+    change "an" to "a"
+
+    >>> item.finish("I give my pledge, as a human\n"
+    ...             "to save, and faithfully to defend from waste\n"
+    ...             "the natural resources of my planet.")
+    WorkItemFinished(u'final')
+    ActivityFinished(Activity(u'Publication.final'))
+    Transition(Activity(u'Publication.final'),
+               Activity(u'Publication.rfinal'))
+    ActivityStarted(Activity(u'Publication.rfinal'))
+
+    >>> item = reviewer.work_list.pop()
+    >>> print item.getDoc()
+    I give my pledge, as a human
+    to save, and faithfully to defend from waste
+    the natural resources of my planet.
+
+    >>> item.finish([])
+    WorkItemFinished(u'rfinal')
+    ActivityFinished(Activity(u'Publication.rfinal'))
+    Transition(Activity(u'Publication.rfinal'),
+               Activity(u'Publication.publish'))
+    ActivityStarted(Activity(u'Publication.publish'))
+    Published
+    WorkItemFinished(u'publish')
+    ActivityFinished(Activity(u'Publication.publish'))
+    ProcessFinished(Process(u'Publication'))
+
+    >>> context.decision
+    True
+
+Descriptions
+============
+
+Most process elements can have names and descriptions.
+
+    >>> pd.applications['prepare'].__name__
+    u'Prepare'
+
+    >>> pd.applications['prepare'].description
+    u'Prepare the initial draft'
+

Added: z3lab/xpdlcore/trunk/xpdl.py
==============================================================================
--- (empty file)
+++ z3lab/xpdlcore/trunk/xpdl.py        Tue Oct 11 20:41:55 2005
@@ -0,0 +1,250 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XPDL parser for zope.wfmc
+
+It relies on xpdlcore as core xpdl parser. It parses the xpdl document
+with the use of xpdlcore.parser and gets the xpdlcore definitions
+modelizing the xpdl element tree. Then it creates the wfmc collections
+of zope.wfmc element instances.
+
+$Id$
+"""
+
+# XXX cope with unicode. xpdlcore should be returning unicode
+
+import zope.wfmc.process
+from xpdlcore import parser as xpdlparser
+
+class Package(dict):
+    """Package class definition
+
+    Python's dict style object containing wfmc process definitions and
+    the set of wfmc elements
+    """
+
+    def __init__(self, iparser):
+        """Defines package elements using the xpdlcore parser
+
+        In this order :
+
+            o Applications
+            o Participants
+            o Process definitions
+            o XXX
+        """
+
+        self.applications = {}
+        self.participants = {}
+
+        self.defineApplications(**iparser.applications)
+        self.defineParticipants(**iparser.participants)
+        self.defineProcessDefinitions(**iparser.process_definitions)
+
+    def _getFormalParameterTypes(self):
+        """List of parameter types mapping zope.wfmc class definitions
+        """
+        return {'IN': zope.wfmc.process.InputParameter,
+                'OUT': zope.wfmc.process.OutputParameter,
+                'INOUT': zope.wfmc.process.InputOutputParameter,
+                }
+
+    def _getParameter(self, param):
+        """Get a core definition and return a wfmc parameter object
+        """
+        parameter_types = self._getFormalParameterTypes()
+        mode = param.Mode
+        if not mode:
+            mode = 'IN'
+        return parameter_types[mode](u'%s'%param.Id)
+
+    def defineApplications(self, **applications):
+        """Get xpdlcore application definitoins and create zope.wfmc
+        ones from them.
+        """
+        for id, application in applications.items():
+            app = zope.wfmc.process.Application()
+
+            # Parameters
+            wfmc_parameters = []
+            for param in application.formal_parameters:
+                wfmc_parameters.append(self._getParameter(param))
+            app.defineParameters(*wfmc_parameters)
+
+            # Name
+            name = getattr(application, 'Name', '')
+            if name:
+                app.__name__ = u'%s'%name
+
+            # Id
+            app.id = u'%s'%id
+            self.applications[id] = app
+
+    def defineParticipants(self, **participants):
+        """Get xpdlcore participant definitions and create zope.wfmc
+        ones from them
+        """
+        for id, participant in participants.items():
+            name = getattr(participant, 'Name', '')
+            p = zope.wfmc.process.Participant(u'%s'%name)
+            self.participants[id] = p
+
+    def defineProcessDefinitions(self, **pdefinitions):
+        """Get xpdlcore process definitions and create zope.wfmc
+        ones from them
+        """
+
+        for id, coredef in pdefinitions.items():
+
+            # create a zope.wfmc process definition instance
+            pdef = zope.wfmc.process.ProcessDefinition(u'%s'%id)
+
+            # Name
+            name = getattr(coredef, 'Name', '')
+            if name:
+                pdef.__name__ = u'%s'%name
+
+            # Copy package data
+            pdef.defineApplications(**self.applications)
+            pdef.defineParticipants(**self.participants)
+
+            # Parameters
+            wfmc_parameters = []
+            for idef in coredef.formal_parameters:
+                iid = idef.Id
+                wfmc_parameters.append(self._getParameter(idef))
+            pdef.defineParameters(*wfmc_parameters)
+
+            # Participants
+            for iid, idef in coredef.participants.items():
+                pdef.defineParticipants(
+                    **{str(iid) : zope.wfmc.process.Participant(u'%s'%iid)})
+
+            # Applications
+            # Overrides the package definition related ones
+            for iid, idef in coredef.applications.items():
+
+                # Create a wfmc application instance
+                iapplication = zope.wfmc.process.Application()
+
+                # Parameters
+                wfmc_parameters = []
+                for param in idef.formal_parameters:
+                    wfmc_parameters.append(self._getParameter(param))
+                iapplication.defineParameters(*wfmc_parameters)
+
+                # Name
+                name = getattr(idef, 'Name', '')
+                if name:
+                    iapplication.__name__ = u'%s'%name
+
+                # Id
+                iapplication.id = u'%s'%iid
+
+                # Description
+                iapplication.description = u'%s'%getattr(idef, 'Description', 
'')
+
+                # Define it
+                pdef.defineApplications(**{str(iid) : iapplication})
+
+            # Activities
+            for aid, activity in coredef.activities.items():
+
+                # Create the wfmc activity instance
+                name = getattr(activity, 'Name', '')
+                iactivity = zope.wfmc.process.ActivityDefinition(u'%s'%name)
+                iactivity.id = u'%s'%aid
+
+                # Process
+                iactivity.process = pdef
+
+                # Tool params
+                if (activity.Tool is not None and
+                    activity.Tool.Type == 'APPLICATION'):
+                    iactivity.addApplication(
+                        u'%s'%activity.Tool.Id,
+                        tuple([u'%s'%x
+                               for x in activity.Tool.actual_parameters]))
+
+                # Performer
+                iactivity.performer = u'%s'%activity.Performer
+
+                # Transitions
+                for tres in activity.transition_restrictions:
+
+                    # Join
+                    for type in tres.Join.values():
+                        if type == u'AND':
+                            iactivity.andJoin(True)
+
+                    # Split
+                    for type in tres.Split.values():
+                        if type == u'AND':
+                            iactivity.andSplit(True)
+
+                    # Transition Refs
+                    for tref in tres.transition_refs:
+                        iactivity.addOutgoing(u'%s'%tref.get('Id'))
+
+                # Define this !
+                pdef.defineActivities(**{str(aid) : iactivity})
+
+            # Transitions
+            for idef in coredef.transitions:
+                iid = u'%s'%idef.Id
+                itransition = zope.wfmc.process.TransitionDefinition(
+                    from_=u'%s'%idef.From,
+                    to=u'%s'%idef.To)
+                itransition.id = iid
+                if idef.Condition:
+                    itransition.condition = TextCondition("(%s)" %
+                                                          idef.Condition)
+                # Name
+                name = getattr(idef, 'Name', '')
+                if name:
+                    itransition.__name__ = u'%s'%name
+                pdef.defineTransitions(itransition)
+
+            self[u'%s'%id] = pdef
+
+class TextCondition:
+
+    def __init__(self, source):
+        self.source = source
+
+        # make sure that we can compile the source
+        compile(source, '<string>', 'eval')
+
+    def __getstate__(self):
+        return {'source': self.source}
+
+    def __call__(self, data):
+        # XXX we *depend* on being able to use the data's dict.
+        # This needs to be part of the contract.
+        if not self.source:
+            return True
+        try:
+            compiled = self._v_compiled
+        except AttributeError:
+            self._v_compiled = compile(self.source, '<string>', 'eval')
+            compiled = self._v_compiled
+        return eval(compiled, {'__builtins__': None}, data.__dict__)
+
+def read(file):
+    """Parse file and return an Package instance including workflow
+    process definitions
+
+    BBB with the old SAX parser
+    """
+    return Package(xpdlparser.read(file))
+
-- 
http://lists.nuxeo.com/mailman/listinfo/z3lab-checkins

Reply via email to