GithubHelp home page GithubHelp logo

Comments (14)

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024 1

I found some code on the internet and modified it a bit to make it work in simple cases:

# import logging
# logger = logging.getLogger(__name__)
# logger.addHandler(logging.StreamHandler())
# logger.setLevel(logging.INFO)




# Source: http://sunshout.tistory.com/1773

from io import BytesIO

from SpiffWorkflow.bpmn.workflow import BpmnWorkflow
from SpiffWorkflow.bpmn.serializer.BpmnSerializer import BpmnSerializer
from SpiffWorkflow.bpmn.serializer.CompactWorkflowSerializer import CompactWorkflowSerializer
from SpiffWorkflow import Task
from SpiffWorkflow.specs import WorkflowSpec
from SpiffWorkflow.bpmn.serializer.Packager import Packager
from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnParser

from SpiffWorkflow.bpmn.specs.BpmnSpecMixin import BpmnSpecMixin
from SpiffWorkflow.specs.Simple import Simple
from SpiffWorkflow.bpmn.parser.TaskParser import TaskParser

from SpiffWorkflow.bpmn.parser.util import *

from SpiffWorkflow.bpmn.specs.UserTask import UserTask

class ServiceTask(Simple, BpmnSpecMixin):
#class ServiceTask(UserTask):
    """
    Task Spec for a bpmn:serviceTask node.
    """
    def is_engine_task(self):
        return False

    def entering_complete_state(self, task):
        print("Do command : %s" % task.get_description())

class ServiceTaskParser(TaskParser):
    pass

class ScriptTask(Simple, BpmnSpecMixin):

    """
    Task Spec for a bpmn:scriptTask node.
    """

    def __init__(self, wf_spec, name, script, **kwargs):
        """
        Constructor.

        :param script: the script that must be executed by the script engine.
        """
        super(ScriptTask, self).__init__(wf_spec, name, **kwargs)
        self.script = script

    def _on_complete_hook(self, task):
        if task.workflow._is_busy_with_restore():
            return
        assert not task.workflow.read_only
        task.workflow.script_engine.execute(task, self.script)
        super(ScriptTask, self)._on_complete_hook(task)

class ScriptTaskParser(TaskParser):

    """
    Parses a script task
    """

    def create_task(self):
        script = self.get_script()
        return self.spec_class(self.spec, self.get_task_spec_name(), script, description=self.node.get('name', None))

    # def get_script(self):
    #     """
    #     Gets the script content from the node. A subclass can override this method, if the script needs
    #     to be pre-parsed. The result of this call will be passed to the Script Engine for execution.
    #     """
    #     return one(self.xpath('.//bpmn:script')).text

    def get_script(self):
        """
        Gets the script content from the node. A subclass can override this method, if the script needs
        to be pre-parsed. The result of this call will be passed to the Script Engine for execution.
        """

        return """print('HAHAHA')"""

class CloudBpmnParser(BpmnParser):
    OVERRIDE_PARSER_CLASSES = {
        full_tag('serviceTask') :   (ServiceTaskParser, ServiceTask),
        full_tag('scriptTask') :    (ScriptTaskParser, ScriptTask),
    }


class InMemoryPackager(Packager):
    """
    Creates spiff's wf packages on the fly.
    """
    PARSER_CLASS = CloudBpmnParser

    @classmethod
    def package_in_memory(cls, workflow_name, workflow_files, editor='signavio'):
        """
        Generates wf packages from workflow diagrams.
        Args:
            workflow_name: Name of wf
            workflow_files:  Diagram  file.
        Returns:
            Workflow package (file like) object
        """
        s = BytesIO()
        p = cls(s, workflow_name, meta_data=[], editor=editor)
        p.add_bpmn_files_by_glob(workflow_files)
        p.create_package()
        return s.getvalue()

class Node(object):
    """
    Keep the Task information
    """
    def __init__(self, task):
        self.input = {}
        self.output = {}
        self.task = None
        self.task_type = None
        self.task_name = None
        self.description = None
        self.activity = None
        self.init_task(task)

    def init_task(self, task):
        self.task = task
        self.task_type = task.task_spec.__class__.__name__
        self.task_name = task.get_name()
        self.description = task.get_description()
        self.activity = getattr(task.task_spec, 'service_class', '')

    def show(self):
        print("task type:%s" % self.task_type)
        print("task name:%s" % self.task_name)
        print("description:%s" % self.description)
        print("activity :%s" % self.activity)
        print("state name:%s" % self.task.get_state_name())
        print("\n")


class BpmnEngine:
    def __init__(self, path, name):
        self.spec = self.load_spec(path, name)
        self.workflow = BpmnWorkflow(self.spec)
        self.run_engine()

    # def create_workflow(self):
    #     self.workflow_spec = self.load_workflow_spec()

    def load_spec(self, content_path, workflow_name):
        return self.load_workflow_spec(content_path, workflow_name)

    def load_workflow_spec(self, content_path, workflow_name):
        package = InMemoryPackager.package_in_memory(workflow_name, content_path)
        return BpmnSerializer().deserialize_workflow_spec(package)

    # def start_engine(self, **kwargs):
    #     self.setUp()

    def run_engine(self): # Old? Simple test?
        for task in self.workflow.get_tasks():
            task_name = task.get_name()
            print(task_name)

    def run_engine(self):
        while 1:
            self.workflow.do_engine_steps()
            tasks = self.workflow.get_tasks(Task.READY)
            if len(tasks) == 0:
                break
            for task in tasks:
                current_node = Node(task)
                current_node.show()
                self.workflow.complete_task_from_id(task.id)


#BpmnEngine('tests/PizzaSimple.bpmn', '_6-2')
#BpmnEngine('tests/PizzaSimple.bpmn', 'Hungry for pizza') # Does not work because the process should have a name (we are not triggering a start event)

#BpmnEngine('tests/PizzaSimpleWithScriptTask.bpmn', '_6-2')

BpmnEngine('tests/PizzaSimpleWithScriptTaskAndCondition.bpmn', '_6-2') # TODO because does not work

I will extend my code and make it better the next days/weeks/months.

tests.zip

from spiffworkflow.

knipknap avatar knipknap commented on July 21, 2024

Oh, that's cool, Thanks! That will definitely help in writing the docs.

from spiffworkflow.

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024

I have a much cleaner code now. If you want to have an example of how to use BPMN and how to run it properly, please contact me.

from spiffworkflow.

knipknap avatar knipknap commented on July 21, 2024

Hi Denny, Please, that would be great!

from spiffworkflow.

thehackercat avatar thehackercat commented on July 21, 2024

@DennyWeinberg That would be awesome 👍

from spiffworkflow.

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024

Will release it in some days/weeks (A BPMN (based on SpiffWorkflow)+DMN library!)
:)

Currently I am working on a BPMN workflow that includes a DMN decision table.

from spiffworkflow.

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024

https://github.com/labsolutionlu/bpmn_dmn

from spiffworkflow.

knipknap avatar knipknap commented on July 21, 2024

@DennyWeinberg Awesome!

I was only to glance over most of your implementation, but does it make sense to merge it into SpiffWorkflow master?
Even if it doesn't, there are parts that make me wonder: For example, does the CamundaExclusiveGatewayParser do anything on top of the BPMN standard, or should we just improve our ExclusiveGatewayParser to make the life of Camunda users easier?

I also see some other classes that seem to be pretty much "general purpose", e.g. you Packager, possibly BPMNXMLWorkflowRunner?

from spiffworkflow.

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024

Hello,

CamundaExclusiveGatewayParser adds the possibility to use "External Resource" Script condition types, that are executed exactly like a normal "Expression" condition:
image

See exclusive_gateway_complex.bpmn

You are right, the InMemoryPackager and the BPMNXMLWorkflowRunner are generic classes that can be re used.

You are free to copy what you want. But you can also let your library be independent, and the users who want to have specific helper functions and features of bpmn and dmn will switch to my library. Should be ok since I use your library in background so you have still full control over the workflow engine.

You have the coice... As you said, InMemoryPackager and the BPMNXMLWorkflowRunner are generic classes that may be also very useful in your library.

from spiffworkflow.

danfunk avatar danfunk commented on July 21, 2024

I've got a pull request out that merges the DMN portion of this effort into SpiffWorkflow. Denny, I've tried to give you credit in the commit.
#94

from spiffworkflow.

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024

Nice. I see you also took over the unit tests. I remember there was an important todo in DMN. I think I mentioned it inside my readme file.

from spiffworkflow.

danfunk avatar danfunk commented on July 21, 2024

You added "Implement all different modes that are available for DMN tables", but that isn't much to go on, can you elaborate?

from spiffworkflow.

DennyWeinberg avatar DennyWeinberg commented on July 21, 2024

Screenshot_20200123-215451_Chrome
I think it was that value. You can switch between and/or/... I think. I'm not sure and I'm not on my PC right now...

Unfortunately we are still not using that library... but I think one time we will.

from spiffworkflow.

danfunk avatar danfunk commented on July 21, 2024

We have some basic support of DMN, we are still missing some core pieces, most notably the DMN evaluation mode described here. Closing this ticket, but will potentially open new tickets for broader support of DMN in the future.

from spiffworkflow.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.