# HG changeset patch # User Sylvain Thénault # Date 1254319612 -7200 # Node ID 77a69de16709a780131bc5ea4ccaccdfee57c902 # Parent efeb16ff93f3f9dbf11cdbcca09ef46f8a0f2435 support for automatic transition diff -r efeb16ff93f3 -r 77a69de16709 entities/test/unittest_wfobjs.py --- a/entities/test/unittest_wfobjs.py Wed Sep 30 16:06:04 2009 +0200 +++ b/entities/test/unittest_wfobjs.py Wed Sep 30 16:06:52 2009 +0200 @@ -343,6 +343,47 @@ ('asleep', 'activated', None, 'workflow changed to "default user workflow"'),]) + +class AutoTransitionTC(EnvBasedTC): + + def setup_database(self): + self.wf = add_wf(self, 'CWUser') + asleep = self.wf.add_state('asleep', initial=True) + dead = self.wf.add_state('dead') + self.wf.add_transition('rest', asleep, asleep) + self.wf.add_transition('sick', asleep, dead, type=u'auto', + conditions=({'expr': u'U surname "toto"', + 'mainvars': u'U'},)) + + def test_auto_transition_fired(self): + user = self.create_user('member') + self.execute('SET X custom_workflow WF WHERE X eid %(x)s, WF eid %(wf)s', + {'wf': self.wf.eid, 'x': user.eid}) + self.commit() + user.clear_all_caches() + self.assertEquals(user.state, 'asleep') + self.assertEquals([t.name for t in user.possible_transitions()], + ['rest']) + user.fire_transition('rest') + self.commit() + user.clear_all_caches() + self.assertEquals(user.state, 'asleep') + self.assertEquals([t.name for t in user.possible_transitions()], + ['rest']) + self.assertEquals(parse_hist(user.workflow_history), + [('asleep', 'asleep', 'rest', None)]) + self.request().user.set_attributes(surname=u'toto') # fulfill condition + self.commit() + user.fire_transition('rest') + self.commit() + user.clear_all_caches() + self.assertEquals(user.state, 'dead') + self.assertEquals(parse_hist(user.workflow_history), + [('asleep', 'asleep', 'rest', None), + ('asleep', 'asleep', 'rest', None), + ('asleep', 'dead', 'sick', None),]) + + from cubicweb.devtools.apptest import RepositoryBasedTC class WorkflowHooksTC(RepositoryBasedTC): diff -r efeb16ff93f3 -r 77a69de16709 entities/wfobjs.py --- a/entities/wfobjs.py Wed Sep 30 16:06:04 2009 +0200 +++ b/entities/wfobjs.py Wed Sep 30 16:06:52 2009 +0200 @@ -223,11 +223,14 @@ conditions = (conditions,) for expr in conditions: if isinstance(expr, str): - expr = unicode(expr) + kwargs = {'expr': unicode(expr)} + elif isinstance(expr, dict): + kwargs = expr + kwargs['x'] = self.eid + kwargs.setdefault('mainvars', u'X') self.req.execute('INSERT RQLExpression X: X exprtype "ERQLExpression", ' - 'X expression %(expr)s, T condition X ' - 'WHERE T eid %(x)s', - {'x': self.eid, 'expr': expr}, 'x') + 'X expression %(expr)s, X mainvars %(mainvars)s, ' + 'T condition X WHERE T eid %(x)s', kwargs, 'x') # XXX clear caches? @@ -415,16 +418,17 @@ self.warning("can't find any workflow for %s", self.id) return None - def possible_transitions(self): + def possible_transitions(self, type='normal'): """generates transition that MAY be fired for the given entity, expected to be in this state """ if self.current_state is None or self.current_workflow is None: return rset = self.req.execute( - 'Any T,N WHERE S allowed_transition T, S eid %(x)s, ' - 'T name N, T transition_of WF, WF eid %(wfeid)s', - {'x': self.current_state.eid, + 'Any T,TT, TN WHERE S allowed_transition T, S eid %(x)s, ' + 'T type TT, T type %(type)s, ' + 'T name TN, T transition_of WF, WF eid %(wfeid)s', + {'x': self.current_state.eid, 'type': type, 'wfeid': self.current_workflow.eid}, 'x') for tr in rset.entities(): if tr.may_be_fired(self.eid): @@ -446,13 +450,14 @@ kwargs['S'] = tseid return self.req.create_entity('TrInfo', *args, **kwargs) - def fire_transition(self, trname, comment=None, commentformat=None): + def fire_transition(self, tr, comment=None, commentformat=None): """change the entity's state by firing transition of the given name in entity's workflow """ assert self.current_workflow - tr = self.current_workflow.transition_by_name(trname) - assert tr is not None, 'not a %s transition: %s' % (self.id, trname) + if isinstance(tr, basestring): + tr = self.current_workflow.transition_by_name(tr) + assert tr is not None, 'not a %s transition: %s' % (self.id, tr) return self._add_trinfo(comment, commentformat, tr.eid) def change_state(self, statename, comment=None, commentformat=None, tr=None): diff -r efeb16ff93f3 -r 77a69de16709 misc/migration/3.5.3_Any.py --- a/misc/migration/3.5.3_Any.py Wed Sep 30 16:06:04 2009 +0200 +++ b/misc/migration/3.5.3_Any.py Wed Sep 30 16:06:52 2009 +0200 @@ -1,2 +1,4 @@ sync_schema_props_perms('state_of') sync_schema_props_perms('transition_of') + +add_attribute('BaseTransition', 'type') diff -r efeb16ff93f3 -r 77a69de16709 schemas/workflow.py --- a/schemas/workflow.py Wed Sep 30 16:06:04 2009 +0200 +++ b/schemas/workflow.py Wed Sep 30 16:06:52 2009 +0200 @@ -68,6 +68,7 @@ name = String(required=True, indexed=True, internationalizable=True, maxsize=256) + type = String(vocabulary=(_('normal'), _('auto')), default='normal') description = RichString(fulltextindexed=True, description=_('semantic description of this transition')) condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject', diff -r efeb16ff93f3 -r 77a69de16709 server/hooks.py --- a/server/hooks.py Wed Sep 30 16:06:04 2009 +0200 +++ b/server/hooks.py Wed Sep 30 16:06:52 2009 +0200 @@ -434,6 +434,18 @@ session.add_relation(x, 'in_state', newstate) +class FireAutotransitionOp(PreCommitOperation): + """try to fire auto transition after state changes""" + + def precommit_event(self): + session = self.session + entity = self.entity + autotrs = list(entity.possible_transitions('auto')) + if autotrs: + assert len(autotrs) == 1 + entity.fire_transition(autotrs[0]) + + def before_add_trinfo(session, entity): """check the transition is allowed, add missing information. Expect that: * wf_info_for inlined relation is set @@ -513,6 +525,8 @@ nocheck = session.transaction_data.setdefault('skip-security', set()) nocheck.add((entity.eid, 'from_state', fromstate.eid)) nocheck.add((entity.eid, 'to_state', deststateeid)) + FireAutotransitionOp(session, entity=forentity) + def after_add_trinfo(session, entity): """change related entity state"""