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.req.execute('SET T destination_state S ' |
134 tostate = tostate.eid |
135 'WHERE S eid %(s)s, T eid %(t)s', |
135 self.req.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'): |
255 return self.subwf.initial |
256 return self.subwf.initial |
256 |
257 |
257 def add_exit_point(self, fromstate, tostate): |
258 def add_exit_point(self, fromstate, tostate): |
258 if hasattr(fromstate, 'eid'): |
259 if hasattr(fromstate, 'eid'): |
259 fromstate = fromstate.eid |
260 fromstate = fromstate.eid |
260 if hasattr(tostate, 'eid'): |
261 if tostate is None: |
261 tostate = tostate.eid |
262 self.req.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, ' |
262 self.req.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, ' |
263 'X subworkflow_state FS WHERE T eid %(t)s, FS eid %(fs)s', |
263 'X subworkflow_state FS, X destination_state TS ' |
264 {'t': self.eid, 'fs': fromstate}, ('t', 'fs')) |
264 'WHERE T eid %(t)s, FS eid %(fs)s, TS eid %(ts)s', |
265 else: |
265 {'t': self.eid, 'fs': fromstate, 'ts': tostate}, |
266 if hasattr(tostate, 'eid'): |
266 ('t', 'fs', 'ts')) |
267 tostate = tostate.eid |
267 |
268 self.req.execute('INSERT SubWorkflowExitPoint X: T subworkflow_exit X, ' |
268 def get_exit_point(self, state): |
269 'X subworkflow_state FS, X destination_state TS ' |
|
270 'WHERE T eid %(t)s, FS eid %(fs)s, TS eid %(ts)s', |
|
271 {'t': self.eid, 'fs': fromstate, 'ts': tostate}, |
|
272 ('t', 'fs', 'ts')) |
|
273 |
|
274 def get_exit_point(self, entity, stateeid): |
269 """if state is an exit point, return its associated destination state""" |
275 """if state is an exit point, return its associated destination state""" |
270 if hasattr(state, 'eid'): |
276 if hasattr(stateeid, 'eid'): |
271 state = state.eid |
277 stateeid = stateeid.eid |
272 stateeid = self.exit_points().get(state) |
278 try: |
273 if stateeid is not None: |
279 tostateeid = self.exit_points()[stateeid] |
274 return self.req.entity_from_eid(stateeid) |
280 except KeyError: |
275 return None |
281 return None |
|
282 if tostateeid is None: |
|
283 # go back to state from which we've entered the subworkflow |
|
284 return entity.subworkflow_input_trinfo().previous_state |
|
285 return self.req.entity_from_eid(tostateeid) |
276 |
286 |
277 @cached |
287 @cached |
278 def exit_points(self): |
288 def exit_points(self): |
279 result = {} |
289 result = {} |
280 for ep in self.subworkflow_exit: |
290 for ep in self.subworkflow_exit: |
281 result[ep.subwf_state.eid] = ep.destination.eid |
291 result[ep.subwf_state.eid] = ep.destination and ep.destination.eid |
282 return result |
292 return result |
283 |
293 |
284 def clear_all_caches(self): |
294 def clear_all_caches(self): |
285 super(WorkflowableMixIn, self).clear_all_caches() |
295 super(WorkflowableMixIn, self).clear_all_caches() |
286 clear_cache(self, 'exit_points') |
296 clear_cache(self, 'exit_points') |
455 """change the entity's state by firing transition of the given name in |
465 """change the entity's state by firing transition of the given name in |
456 entity's workflow |
466 entity's workflow |
457 """ |
467 """ |
458 assert self.current_workflow |
468 assert self.current_workflow |
459 if isinstance(tr, basestring): |
469 if isinstance(tr, basestring): |
460 tr = self.current_workflow.transition_by_name(tr) |
470 _tr = self.current_workflow.transition_by_name(tr) |
461 assert tr is not None, 'not a %s transition: %s' % (self.id, tr) |
471 assert _tr is not None, 'not a %s transition: %s' % (self.id, tr) |
|
472 tr = _tr |
462 return self._add_trinfo(comment, commentformat, tr.eid) |
473 return self._add_trinfo(comment, commentformat, tr.eid) |
463 |
474 |
464 def change_state(self, statename, comment=None, commentformat=None, tr=None): |
475 def change_state(self, statename, comment=None, commentformat=None, tr=None): |
465 """change the entity's state to the given state (name or entity) in |
476 """change the entity's state to the given state (name or entity) in |
466 entity's workflow. This method should only by used by manager to fix an |
477 entity's workflow. This method should only by used by manager to fix an |
481 statename)) |
492 statename)) |
482 stateeid = state.eid |
493 stateeid = state.eid |
483 # XXX try to find matching transition? |
494 # XXX try to find matching transition? |
484 return self._add_trinfo(comment, commentformat, tr and tr.eid, stateeid) |
495 return self._add_trinfo(comment, commentformat, tr and tr.eid, stateeid) |
485 |
496 |
486 def subworkflow_input_transition(self): |
497 def subworkflow_input_trinfo(self): |
487 """return the transition which has went through the current sub-workflow |
498 """return the TrInfo which has be recorded when this entity went into |
|
499 the current sub-workflow |
488 """ |
500 """ |
489 if self.main_workflow.eid == self.current_workflow.eid: |
501 if self.main_workflow.eid == self.current_workflow.eid: |
490 return # doesn't make sense |
502 return # doesn't make sense |
491 subwfentries = [] |
503 subwfentries = [] |
492 for trinfo in self.workflow_history: |
504 for trinfo in self.workflow_history: |
501 else: |
513 else: |
502 # enter |
514 # enter |
503 subwfentries.append(trinfo) |
515 subwfentries.append(trinfo) |
504 if not subwfentries: |
516 if not subwfentries: |
505 return None |
517 return None |
506 return subwfentries[-1].transition |
518 return subwfentries[-1] |
|
519 |
|
520 def subworkflow_input_transition(self): |
|
521 """return the transition which has went through the current sub-workflow |
|
522 """ |
|
523 return getattr(self.subworkflow_input_trinfo(), 'transition', None) |
507 |
524 |
508 def clear_all_caches(self): |
525 def clear_all_caches(self): |
509 super(WorkflowableMixIn, self).clear_all_caches() |
526 super(WorkflowableMixIn, self).clear_all_caches() |
510 clear_cache(self, 'cwetype_workflow') |
527 clear_cache(self, 'cwetype_workflow') |
511 |
528 |