Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Sven Franck
erp5
Commits
603c8600
Commit
603c8600
authored
Dec 10, 2014
by
wenjie.zheng
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ERP5Workflow Method Generation for live test.
parent
425949fe
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
158 additions
and
63 deletions
+158
-63
product/ERP5Type/Base.py
product/ERP5Type/Base.py
+133
-58
product/ERP5Type/ERP5Type.py
product/ERP5Type/ERP5Type.py
+5
-4
product/ERP5Type/dynamic/lazy_class.py
product/ERP5Type/dynamic/lazy_class.py
+20
-1
No files found.
product/ERP5Type/Base.py
View file @
603c8600
...
...
@@ -165,100 +165,135 @@ class WorkflowMethod(Method):
# already better than what we had. I (JPS) would prefer to use
# critical sections in this part of the code and a
# thread variable which tells in which semantic context the code
# should
n
e executed. - XXX
# should
b
e executed. - XXX
return
self
.
_m
(
instance
,
*
args
,
**
kw
)
#=============== Workflow5 Project, Wenjie, Dec 2014 =====================
# Get Workflow5 which is an ERP5 default module.
# Available workflow5 bt should be initially installed.
# Only check Base Type obejct:
wf5_module
=
instance
.
getPortalObject
().
getDefaultModule
(
portal_type
=
"Workflow"
)
valid_list5
=
[]
valid_transition_item_list5
=
[]
#if instance.portal_type == "Object Type":
#raise NotImplementedError (instance.getTypeInfo())# Base Type at Object Type
if
hasattr
(
instance
.
getTypeInfo
(),
"workflow_list"
):
WorkflowList
=
instance
.
getTypeInfo
().
getTypeWorkflowList
()
#raise NotImplementedError (WorkflowList)# new_workflow
for
wf_id
in
WorkflowList
:
wf5
=
wf5_module
.
_getOb
(
wf_id
)
for
transition
in
wf5
.
objectValues
(
portal_type
=
'Transition'
):
valid_list5
.
append
(
transition
.
getId
())
#raise NotImplementedError (valid_list5) # ['transition1', 'transition2']
if
valid_list5
:
valid_transition_item_list5
.
append
((
wf_id
,
valid_list5
))
# execute method
for
wf_id
,
transition_list
in
valid_transition_item_list5
:
# ??? see original <245, 256>
for
tr
in
transition_list
:
method5
=
wf5_module
.
_getOb
(
wf_id
).
_getOb
(
tr
)
method5
.
execute
(
instance
)
# also execute before & after script
#return method5._changeState(instance) # only change state
#raise NotImplementedError (instance.getId()) # new_object
#raise NotImplementedError (method5) # Transition at transition1
#raise NotImplementedError (instance.getCategoryStateTitle()) # None
#=================================== wf5 =================================
# New implementation does not use any longer wrapWorkflowMethod
# but directly calls the workflow methods
try
:
wf
=
getattr
(
instance
.
getPortalObject
(),
'portal_workflow'
)
wf
=
getattr
(
instance
.
getPortalObject
(),
'portal_workflow'
)
# portal_workflow is a list!
except
AttributeError
:
# XXX instance is unwrapped(no acquisition)
# XXX I must think that what is a correct behavior.(Yusei)
return
self
.
_m
(
instance
,
*
args
,
**
kw
)
try
:
# Build a list of transitions which may need to be invoked
instance_path
=
instance
.
getPhysicalPath
()
portal_type
=
instance
.
portal_type
transactional_variable
=
getTransactionalVariable
()
invoke_once_dict
=
self
.
_invoke_once
.
get
(
portal_type
,
{})
valid_invoke_once_item_list
=
[]
instance_path
=
instance
.
getPhysicalPath
()
portal_type
=
instance
.
portal_type
transactional_variable
=
getTransactionalVariable
()
invoke_once_dict
=
self
.
_invoke_once
.
get
(
portal_type
,
{})
valid_invoke_once_item_list
=
[]
# Only keep those transitions which were never invoked
once_transition_dict
=
{}
for
wf_id
,
transition_list
in
invoke_once_dict
.
iteritems
():
valid_transition_list
=
[]
for
transition_id
in
transition_list
:
once_transition_key
=
(
'Products.ERP5Type.Base.WorkflowMethod.__call__'
,
wf_id
,
transition_id
,
instance_path
)
once_transition_dict
[(
wf_id
,
transition_id
)]
=
once_transition_key
if
once_transition_key
not
in
transactional_variable
:
valid_transition_list
.
append
(
transition_id
)
if
valid_transition_list
:
valid_invoke_once_item_list
.
append
((
wf_id
,
valid_transition_list
))
candidate_transition_item_list
=
valid_invoke_once_item_list
+
\
once_transition_dict
=
{}
for
wf_id
,
transition_list
in
invoke_once_dict
.
iteritems
():
valid_transition_list
=
[]
for
transition_id
in
transition_list
:
once_transition_key
=
(
'Products.ERP5Type.Base.WorkflowMethod.__call__'
,
wf_id
,
transition_id
,
instance_path
)
once_transition_dict
[(
wf_id
,
transition_id
)]
=
once_transition_key
if
once_transition_key
not
in
transactional_variable
:
valid_transition_list
.
append
(
transition_id
)
if
valid_transition_list
:
valid_invoke_once_item_list
.
append
((
wf_id
,
valid_transition_list
))
candidate_transition_item_list
=
valid_invoke_once_item_list
+
\
self
.
_invoke_always
.
get
(
portal_type
,
{}).
items
()
#LOG('candidate_transition_item_list %s' % self.__name__, 0, str(candidate_transition_item_list))
# Try to return immediately if there are no transition to invoke
if
not
candidate_transition_item_list
:
return
apply
(
self
.
__dict__
[
'_m'
],
(
instance
,)
+
args
,
kw
)
if
not
candidate_transition_item_list
:
return
apply
(
self
.
__dict__
[
'_m'
],
(
instance
,)
+
args
,
kw
)
# Prepare a list of transitions which should be invoked.
# This list is based on the results of isWorkflowMethodSupported.
# An interaction is ignored if the guard prevents execution.
# Otherwise, an exception is raised if the workflow transition does not
# exist from the current state, or if the guard rejects it.
valid_transition_item_list
=
[]
for
wf_id
,
transition_list
in
candidate_transition_item_list
:
candidate_workflow
=
wf
[
wf_id
]
valid_list
=
[]
for
transition_id
in
transition_list
:
if
candidate_workflow
.
isWorkflowMethodSupported
(
instance
,
transition_id
):
valid_list
.
append
(
transition_id
)
once_transition_key
=
once_transition_dict
.
get
((
wf_id
,
transition_id
))
if
once_transition_key
:
# a run-once transition, prevent it from running again in
# the same transaction
transactional_variable
[
once_transition_key
]
=
1
elif
candidate_workflow
.
__class__
.
__name__
==
'DCWorkflowDefinition'
:
raise
UnsupportedWorkflowMethod
(
instance
,
wf_id
,
transition_id
)
valid_transition_item_list
=
[]
for
wf_id
,
transition_list
in
candidate_transition_item_list
:
candidate_workflow
=
wf
[
wf_id
]
valid_list
=
[]
for
transition_id
in
transition_list
:
if
candidate_workflow
.
isWorkflowMethodSupported
(
instance
,
transition_id
):
valid_list
.
append
(
transition_id
)
once_transition_key
=
once_transition_dict
.
get
((
wf_id
,
transition_id
))
if
once_transition_key
:
# a run-once transition, prevent it from running again in
# the same transaction
transactional_variable
[
once_transition_key
]
=
1
elif
candidate_workflow
.
__class__
.
__name__
==
'DCWorkflowDefinition'
:
raise
UnsupportedWorkflowMethod
(
instance
,
wf_id
,
transition_id
)
# XXX Keep the log for projects that needs to comment out
# the previous line.
LOG
(
"WorkflowMethod.__call__"
,
ERROR
,
LOG
(
"WorkflowMethod.__call__"
,
ERROR
,
"Transition %s/%s on %r is ignored. Current state is %r."
%
(
wf_id
,
transition_id
,
instance
,
candidate_workflow
.
_getWorkflowStateOf
(
instance
,
id_only
=
1
)))
if
valid_list
:
valid_transition_item_list
.
append
((
wf_id
,
valid_list
))
if
valid_list
:
valid_transition_item_list
.
append
((
wf_id
,
valid_list
))
#LOG('valid_transition_item_list %s' % self.__name__, 0, str(valid_transition_item_list))
# Call whatever must be called before changing states
for
wf_id
,
transition_list
in
valid_transition_item_list
:
wf
[
wf_id
].
notifyBefore
(
instance
,
transition_list
,
args
=
args
,
kw
=
kw
)
# Call whatever must be called before changing states
for
wf_id
,
transition_list
in
valid_transition_item_list
:
wf
[
wf_id
].
notifyBefore
(
instance
,
transition_list
,
args
=
args
,
kw
=
kw
)
# Compute expected result
result
=
apply
(
self
.
__dict__
[
'_m'
],
(
instance
,)
+
args
,
kw
)
# Compute expected result
result
=
apply
(
self
.
__dict__
[
'_m'
],
(
instance
,)
+
args
,
kw
)
# Change the state of statefull workflows
for
wf_id
,
transition_list
in
valid_transition_item_list
:
try
:
wf
[
wf_id
].
notifyWorkflowMethod
(
instance
,
transition_list
,
args
=
args
,
kw
=
kw
)
except
ObjectDeleted
:
# Re-raise with a different result.
raise
ObjectDeleted
(
result
)
except
ObjectMoved
,
ex
:
# Re-raise with a different result.
raise
ObjectMoved
(
ex
.
getNewObject
(),
result
)
# Change the state of statefull workflows
for
wf_id
,
transition_list
in
valid_transition_item_list
:
try
:
wf
[
wf_id
].
notifyWorkflowMethod
(
instance
,
transition_list
,
args
=
args
,
kw
=
kw
)
except
ObjectDeleted
:
# Re-raise with a different result.
raise
ObjectDeleted
(
result
)
except
ObjectMoved
,
ex
:
# Re-raise with a different result.
raise
ObjectMoved
(
ex
.
getNewObject
(),
result
)
# Call whatever must be called after changing states
for
wf_id
,
transition_list
in
valid_transition_item_list
:
wf
[
wf_id
].
notifySuccess
(
instance
,
transition_list
,
result
,
args
=
args
,
kw
=
kw
)
# Return result finally
return
result
for
wf_id
,
transition_list
in
valid_transition_item_list
:
# /product/ERP5/InteractionWorkflow.py, update value, provide info
wf
[
wf_id
].
notifySuccess
(
instance
,
transition_list
,
result
,
args
=
args
,
kw
=
kw
)
# Return result finally
return
result
except
:
pass
# Interactions should not be disabled during normal operation. Only in very
# rare and specific cases like data migration. That's why it is implemented
# with temporary monkey-patching, instead of slowing down __call__ with yet
...
...
@@ -492,6 +527,43 @@ def getClassPropertyList(klass):
if
p
not
in
ps_list
])
return
ps_list
# =================== Workflow5 Project, Wenjie, Dec 2014 ======================
### this function will be used in /product/ERP5Type/dynamic/lazy_class.py
### in generatePortalTypeAccessors()
def
intializePortalTypeERP5WorkflowMethod
(
ptype_klass
,
portal_workflow5
):
### portal_workflow5 is the entire ERP5Workflow module, need to access the
### workflow_list from instance's portal type. So only the related erp5 workflow will be used.
wf5_module
=
aq_inner
(
portal_workflow5
)
portal_type
=
portal_workflow5
.
getPortalObject
().
getDefaultModule
(
portal_type
=
"portal_types"
)
pt
=
portal_type
.
_getOb
(
ptype_klass
.
__name__
)
#raise NotImplementedError (portal_type)
#raise NotImplementedError (wf5_module)#<Workflow Module at workflow_module>
for
workflow5
in
pt
.
workflow_list
:
for
tr
in
wf5_module
.
_getOb
(
workflow5
).
objectValues
(
portal_type
=
"Transition"
):
tr_id
=
tr
.
id
method_id
=
tr_id
wf_id
=
workflow5
ptype_klass
.
security
.
declareProtected
(
Permissions
.
AccessContentsInformation
,
method_id
)
ptype_klass
.
registerWorkflowMethod
(
method_id
,
wf_id
,
tr_id
)
method
=
getattr
(
ptype_klass
,
method_id
)
# Wrap method
if
not
callable
(
method
):
LOG
(
'initializePortalTypeERP5WorkflowMethods'
,
100
,
'WARNING! Can not initialize %s on %s'
%
\
(
method_id
,
portal_type
))
continue
if
not
isinstance
(
method
,
WorkflowMethod
):
method
=
WorkflowMethod
(
method
)
setattr
(
ptype_klass
,
method_id
,
method
)
method
.
registerTransitionAlways
(
portal_type
,
wf_id
,
tr_id
)
# =================== WF5 ======================================================
def
initializePortalTypeDynamicWorkflowMethods
(
ptype_klass
,
portal_workflow
):
"""We should now make sure workflow methods are defined
and also make sure simulation state is defined."""
...
...
@@ -505,6 +577,8 @@ def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
dc_workflow_dict
=
dict
()
interaction_workflow_dict
=
dict
()
# DCworkflow
for
wf
in
portal_workflow
.
getWorkflowsFor
(
portal_type
):
wf_id
=
wf
.
id
wf_type
=
wf
.
__class__
.
__name__
...
...
@@ -545,6 +619,7 @@ def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
storage
[
wf_id
]
=
(
transition_id_set
,
trigger_dict
)
# Generate Workflow method:
for
wf_id
,
v
in
dc_workflow_dict
.
iteritems
():
transition_id_set
,
trigger_dict
=
v
for
tr_id
,
tdef
in
trigger_dict
.
iteritems
():
...
...
product/ERP5Type/ERP5Type.py
View file @
603c8600
...
...
@@ -46,6 +46,7 @@ from TranslationProviderBase import TranslationProviderBase
from
sys
import
exc_info
from
zLOG
import
LOG
,
ERROR
from
Products.CMFCore.exceptions
import
zExceptions_Unauthorized
from
types
import
NoneType
def
getCurrentUserIdOrAnonymousToken
():
"""Return connected user_id or simple token for
...
...
@@ -421,10 +422,9 @@ class ERP5TypeInformation(XMLObject,
for
workflow5
in
self
.
getTypeWorkflowList
():
workflow_module
=
portal
.
getDefaultModule
(
portal_type
=
"Workflow"
)
if
workflow_module
is
not
None
:
workflow5
=
workflow_module
.
_getOb
(
workflow5
)
workflow5
.
initializeDocument
(
ob
)
workflow5
=
workflow_module
.
_getOb
(
workflow5
)
workflow5
.
initializeDocument
(
ob
)
if
not
temp_object
:
init_script
=
self
.
getTypeInitScriptId
()
...
...
@@ -531,6 +531,7 @@ class ERP5TypeInformation(XMLObject,
"""
Return all the properties of the Portal Type
"""
### cls's class is PortalTypeMetaClass defined in ERP5Type/dynamic/lazy_class.py
cls
=
self
.
getPortalObject
().
portal_types
.
getPortalTypeClass
(
self
.
getId
())
return_set
=
set
()
for
property_dict
in
cls
.
getAccessorHolderPropertyList
(
content
=
True
):
...
...
product/ERP5Type/dynamic/lazy_class.py
View file @
603c8600
...
...
@@ -7,7 +7,7 @@ from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter
from
Products.ERP5Type.Globals
import
InitializeClass
from
Products.ERP5Type.Base
import
Base
as
ERP5Base
from
.
import
aq_method_lock
from
Products.ERP5Type.Base
import
PropertyHolder
,
initializePortalTypeDynamicWorkflowMethods
from
Products.ERP5Type.Base
import
PropertyHolder
,
initializePortalTypeDynamicWorkflowMethods
,
intializePortalTypeERP5WorkflowMethod
from
Products.ERP5Type.Utils
import
UpperCase
from
Products.ERP5Type.Core.CategoryProperty
import
CategoryProperty
from
ExtensionClass
import
ExtensionClass
,
pmc_init_of
...
...
@@ -266,6 +266,25 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
else
:
initializePortalTypeDynamicWorkflowMethods
(
cls
,
portal_workflow
)
# ================== Workflow5 Project, Wenjie, Dec 2014 =======================
#raise NotImplementedError (cls.__name__) # Category Property
### the ERP5Workflow list is defined in ERP5Type, only try to get erp5workflow
### when it's an erp5workflow related type.
if
cls
.
__name__
==
"Object Type"
:
portal_workflow5
=
site
.
getDefaultModule
(
portal_type
=
"Workflow"
)
#raise NotImplementedError (portal_workflow5) #<Workflow Module at workflow_module>
#raise NotImplementedError (cls.__module__) #<class 'erp5.portal_type.Category Property'>
if
portal_workflow5
is
None
:
LOG
(
"ERP5Type.Dynamic"
,
WARNING
,
"no workflow5 methods for %s"
%
cls
.
__name__
)
else
:
intializePortalTypeERP5WorkflowMethod
(
cls
,
portal_workflow5
)
# ================== WF5 =======================================================
# portal type group methods, isNodeType, isResourceType...
from
Products.ERP5Type.ERP5Type
import
ERP5TypeInformation
# XXX possible optimization:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment