def is_valid(self):
        self.errors = {
                'workflow':[], 
                'states': {},
                'transitions':{},
             }
        valid = True

        # The graph must have only one start node
        if self.states.filter(is_start_state=True).count() != 1:
            self.errors['workflow'].append(__('There must be only one start'\
                ' state'))
            valid = False

        # The graph must have at least one end state
        if self.states.filter(is_end_state=True).count() < 1:
            self.errors['workflow'].append(__('There must be at least one end'\
                ' state'))
            valid = False

        # Check for orphan nodes / cul-de-sac nodes
        all_states = self.states.all()
        for state in all_states:
            if state.transitions_into.all().count() == 0 and state.is_start_state == False:
                if not state.id in self.errors['states']:
                    self.errors['states'][state.id] = list()
                self.errors['states'][state.id].append(__('This state is'\
                        ' orphaned. There is no way to get to it given the'\
                        ' current workflow topology.'))
                valid = False

            if state.transitions_from.all().count() == 0 and state.is_end_state == False:
                if not state.id in self.errors['states']:
                    self.errors['states'][state.id] = list()
                self.errors['states'][state.id].append(__('This state is a'\
                        ' dead end. It is not marked as an end state and there'\
                        ' is no way to exit from it.'))
                valid = False

        # Check the role collections are compatible between states and
        # transitions (i.e. there cannot be any transitions that are only
        # available to participants with roles that are not also roles
        # associated with the parent state).
        for state in all_states:
            # *at least* one role from the state must also be associated
            # with each transition where the state is the from_state 
            state_roles = state.roles.all()
            for transition in state.transitions_from.all():
                if not transition.roles.filter(pk__in=[r.id for r in state_roles]):
                    if not transition.id in self.errors['transitions']:
                        self.errors['transitions'][transition.id] = list()
                    self.errors['transitions'][transition.id].append(__('This'\
                            ' transition is not navigable because none of the'\
                            ' roles associated with the parent state have'\
                            ' permission to use it.'))
                    valid = False
        return valid
run a juttle program through the juttle streaming API and return the
dataset/ETHPy150Open ntoll/workflow/workflow/models.py/Workflow.is_valid--crossover with--dataset/ETHPy150Open jut-io/jut-python-tools/jut/api/data_engine.py/run