122 'WHERE S eid %(s)s, T eid %(t)s', |
122 'WHERE S eid %(s)s, T eid %(t)s', |
123 {'s': state, 't': tr.eid}, ('s', 't')) |
123 {'s': state, 't': tr.eid}, ('s', 't')) |
124 tr.set_transition_permissions(requiredgroups, conditions, reset=False) |
124 tr.set_transition_permissions(requiredgroups, conditions, reset=False) |
125 return tr |
125 return tr |
126 |
126 |
127 def add_transition(self, name, fromstates, tostate, |
127 def add_transition(self, name, fromstates, tostate=None, |
128 requiredgroups=(), conditions=(), **kwargs): |
128 requiredgroups=(), conditions=(), **kwargs): |
129 """add a transition to this workflow from some state(s) to another""" |
129 """add a transition to this workflow from some state(s) to another""" |
130 tr = self._add_transition('Transition', name, fromstates, |
130 tr = self._add_transition('Transition', name, fromstates, |
131 requiredgroups, conditions, **kwargs) |
131 requiredgroups, conditions, **kwargs) |
132 if hasattr(tostate, 'eid'): |
132 if tostate is not None: |
133 tostate = tostate.eid |
133 if hasattr(tostate, 'eid'): |
134 self._cw.execute('SET T destination_state S ' |
134 tostate = tostate.eid |
135 'WHERE S eid %(s)s, T eid %(t)s', |
135 self._cw.execute('SET T destination_state S ' |
136 {'t': tr.eid, 's': tostate}, ('s', 't')) |
136 'WHERE S eid %(s)s, T eid %(t)s', |
|
137 {'t': tr.eid, 's': tostate}, ('s', 't')) |
137 return tr |
138 return tr |
138 |
139 |
139 def add_wftransition(self, name, subworkflow, fromstates, exitpoints, |
140 def add_wftransition(self, name, subworkflow, fromstates, exitpoints=(), |
140 requiredgroups=(), conditions=(), **kwargs): |
141 requiredgroups=(), conditions=(), **kwargs): |
141 """add a workflow transition to this workflow""" |
142 """add a workflow transition to this workflow""" |
142 tr = self._add_transition('WorkflowTransition', name, fromstates, |
143 tr = self._add_transition('WorkflowTransition', name, fromstates, |
143 requiredgroups, conditions, **kwargs) |
144 requiredgroups, conditions, **kwargs) |
144 if hasattr(subworkflow, 'eid'): |
145 if hasattr(subworkflow, 'eid'): |
145 subworkflow = subworkflow.eid |
146 subworkflow = subworkflow.eid |
146 self._cw.execute('SET T subworkflow WF WHERE WF eid %(wf)s,T eid %(t)s', |
147 assert _cw.req.execute('SET T subworkflow WF WHERE WF eid %(wf)s,T eid %(t)s', |
147 {'t': tr.eid, 'wf': subworkflow}, ('wf', 't')) |
148 {'t': tr.eid, 'wf': subworkflow}, ('wf', 't')) |
148 for fromstate, tostate in exitpoints: |
149 for fromstate, tostate in exitpoints: |
149 tr.add_exit_point(fromstate, tostate) |
150 tr.add_exit_point(fromstate, tostate) |
150 return tr |
151 return tr |
151 |
152 |
152 |
153 |
256 return self.subwf.initial |
257 return self.subwf.initial |
257 |
258 |
258 def add_exit_point(self, fromstate, tostate): |
259 def add_exit_point(self, fromstate, tostate): |
259 if hasattr(fromstate, 'eid'): |
260 if hasattr(fromstate, 'eid'): |
260 fromstate = fromstate.eid |
261 fromstate = fromstate.eid |
261 if hasattr(tostate, 'eid'): |
262 if tostate is None: |
262 tostate = tostate.eid |
263 self._cw.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, ' |
263 self._cw.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, ' |
264 'X subworkflow_state FS WHERE T eid %(t)s, FS eid %(fs)s', |
264 'X subworkflow_state FS, X destination_state TS ' |
265 {'t': self.eid, 'fs': fromstate}, ('t', 'fs')) |
265 'WHERE T eid %(t)s, FS eid %(fs)s, TS eid %(ts)s', |
266 else: |
266 {'t': self.eid, 'fs': fromstate, 'ts': tostate}, |
267 if hasattr(tostate, 'eid'): |
267 ('t', 'fs', 'ts')) |
268 tostate = tostate.eid |
268 |
269 self._cw.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, ' |
269 def get_exit_point(self, state): |
270 'X subworkflow_state FS, X destination_state TS ' |
|
271 'WHERE T eid %(t)s, FS eid %(fs)s, TS eid %(ts)s', |
|
272 {'t': self.eid, 'fs': fromstate, 'ts': tostate}, |
|
273 ('t', 'fs', 'ts')) |
|
274 |
|
275 def get_exit_point(self, entity, stateeid): |
270 """if state is an exit point, return its associated destination state""" |
276 """if state is an exit point, return its associated destination state""" |
271 if hasattr(state, 'eid'): |
277 if hasattr(stateeid, 'eid'): |
272 state = state.eid |
278 stateeid = stateeid.eid |
273 stateeid = self.exit_points().get(state) |
279 try: |
274 if stateeid is not None: |
280 tostateeid = self.exit_points()[stateeid] |
275 return self._cw.entity_from_eid(stateeid) |
281 except KeyError: |
276 return None |
282 return None |
|
283 if tostateeid is None: |
|
284 # go back to state from which we've entered the subworkflow |
|
285 return entity.subworkflow_input_trinfo().previous_state |
|
286 return self._cw.entity_from_eid(tostateeid) |
277 |
287 |
278 @cached |
288 @cached |
279 def exit_points(self): |
289 def exit_points(self): |
280 result = {} |
290 result = {} |
281 for ep in self.subworkflow_exit: |
291 for ep in self.subworkflow_exit: |
282 result[ep.subwf_state.eid] = ep.destination.eid |
292 result[ep.subwf_state.eid] = ep.destination and ep.destination.eid |
283 return result |
293 return result |
284 |
294 |
285 def clear_all_caches(self): |
295 def clear_all_caches(self): |
286 super(WorkflowableMixIn, self).clear_all_caches() |
296 super(WorkflowableMixIn, self).clear_all_caches() |
287 clear_cache(self, 'exit_points') |
297 clear_cache(self, 'exit_points') |
456 """change the entity's state by firing transition of the given name in |
466 """change the entity's state by firing transition of the given name in |
457 entity's workflow |
467 entity's workflow |
458 """ |
468 """ |
459 assert self.current_workflow |
469 assert self.current_workflow |
460 if isinstance(tr, basestring): |
470 if isinstance(tr, basestring): |
461 tr = self.current_workflow.transition_by_name(tr) |
471 _tr = self.current_workflow.transition_by_name(tr) |
462 tr = self.current_workflow.transition_by_name(trname) |
472 assert _tr is not None, 'not a %s transition: %s' % ( |
463 if tr is None: |
473 self.__regid__, tr) |
464 raise WorkflowException('not a %s transition: %s' % (self.__regid__, |
474 tr = _tr |
465 trname)) |
|
466 return self._add_trinfo(comment, commentformat, tr.eid) |
475 return self._add_trinfo(comment, commentformat, tr.eid) |
467 |
476 |
468 def change_state(self, statename, comment=None, commentformat=None, tr=None): |
477 def change_state(self, statename, comment=None, commentformat=None, tr=None): |
469 """change the entity's state to the given state (name or entity) in |
478 """change the entity's state to the given state (name or entity) in |
470 entity's workflow. This method should only by used by manager to fix an |
479 entity's workflow. This method should only by used by manager to fix an |
485 statename)) |
494 statename)) |
486 stateeid = state.eid |
495 stateeid = state.eid |
487 # XXX try to find matching transition? |
496 # XXX try to find matching transition? |
488 return self._add_trinfo(comment, commentformat, tr and tr.eid, stateeid) |
497 return self._add_trinfo(comment, commentformat, tr and tr.eid, stateeid) |
489 |
498 |
490 def subworkflow_input_transition(self): |
499 def subworkflow_input_trinfo(self): |
491 """return the transition which has went through the current sub-workflow |
500 """return the TrInfo which has be recorded when this entity went into |
|
501 the current sub-workflow |
492 """ |
502 """ |
493 if self.main_workflow.eid == self.current_workflow.eid: |
503 if self.main_workflow.eid == self.current_workflow.eid: |
494 return # doesn't make sense |
504 return # doesn't make sense |
495 subwfentries = [] |
505 subwfentries = [] |
496 for trinfo in reversed(self.workflow_history): |
506 for trinfo in self.workflow_history: |
497 if (trinfo.transition and |
507 if (trinfo.transition and |
498 trinfo.previous_state.workflow.eid != trinfo.new_state.workflow.eid): |
508 trinfo.previous_state.workflow.eid != trinfo.new_state.workflow.eid): |
499 # entering or leaving a subworkflow |
509 # entering or leaving a subworkflow |
500 if (subwfentries and |
510 if (subwfentries and |
501 subwfentries[-1].new_state.workflow.eid == trinfo.previous_state.workflow.eid): |
511 subwfentries[-1].new_state.workflow.eid == trinfo.previous_state.workflow.eid and |
|
512 subwfentries[-1].previous_state.workflow.eid == trinfo.new_state.workflow.eid): |
502 # leave |
513 # leave |
503 del subwfentries[-1] |
514 del subwfentries[-1] |
504 else: |
515 else: |
505 # enter |
516 # enter |
506 subwfentries.append(trinfo) |
517 subwfentries.append(trinfo) |
507 if not subwfentries: |
518 if not subwfentries: |
508 return None |
519 return None |
509 return subwfentries[-1].transition |
520 return subwfentries[-1] |
|
521 |
|
522 def subworkflow_input_transition(self): |
|
523 """return the transition which has went through the current sub-workflow |
|
524 """ |
|
525 return getattr(self.subworkflow_input_trinfo(), 'transition', None) |
510 |
526 |
511 def clear_all_caches(self): |
527 def clear_all_caches(self): |
512 super(WorkflowableMixIn, self).clear_all_caches() |
528 super(WorkflowableMixIn, self).clear_all_caches() |
513 clear_cache(self, 'cwetype_workflow') |
529 clear_cache(self, 'cwetype_workflow') |
514 |
530 |