entities/wfobjs.py
changeset 3629 559cad62c786
parent 3589 a5432f99f2d9
parent 3628 440931181322
child 3674 387d51af966d
equal deleted inserted replaced
3590:1b0dbcf4b214 3629:559cad62c786
   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')
   295     def subwf_state(self):
   305     def subwf_state(self):
   296         return self.subworkflow_state[0]
   306         return self.subworkflow_state[0]
   297 
   307 
   298     @property
   308     @property
   299     def destination(self):
   309     def destination(self):
   300         return self.destination_state[0]
   310         return self.destination_state and self.destination_state[0] or None
   301 
   311 
   302 
   312 
   303 class State(AnyEntity):
   313 class State(AnyEntity):
   304     """customized class for State entities"""
   314     """customized class for State entities"""
   305     __regid__ = 'State'
   315     __regid__ = 'State'
   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