Metadata-Version: 2.4
Name: urban-workflow
Version: 0.2.1
Summary: Workflow engine driven by a business DSL — Python port of Black-Beard-Sdk/Workflow, with JSON outputs normalized by urban-json (JSLT).
Author-email: Gael Beard <gaelgael5@gmail.com>
License: MIT License
        
        Copyright (c) 2026 black beard
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/gaelgael5/urban-workflow
Project-URL: Issues, https://github.com/gaelgael5/urban-workflow/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: urban-json>=0.2.1
Provides-Extra: sql
Requires-Dist: sqlalchemy>=2.0; extra == "sql"
Provides-Extra: schemas
Requires-Dist: PyYAML>=6.0; extra == "schemas"
Requires-Dist: jsonschema>=4.18; extra == "schemas"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: sqlalchemy>=2.0; extra == "dev"
Requires-Dist: fastapi>=0.110; extra == "dev"
Requires-Dist: uvicorn>=0.27; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: PyYAML>=6.0; extra == "dev"
Requires-Dist: jsonschema>=4.18; extra == "dev"
Dynamic: license-file

# urban-workflow

*English · [Français](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/README.md)*

Workflow engine driven by a **domain-specific language** (DSL) — a Python
port of [Black-Beard-Sdk/Workflow](https://github.com/Black-Beard-Sdk/Workflow)
(.NET), whose JSON output normalization system is replaced by
[urban-json](https://pypi.org/project/urban-json/) (a JSLT engine): the
messages produced by the engine are described by declarative `.jslt`
templates that can be changed without redeploying.

## A workflow in a few lines

```sql
NAME ValidationAchat VERSION 1
MATCHING (Type = 'achat')

DEFINE EVENT  DemandeSoumise 'une demande est déposée';
DEFINE RULE   MontantInferieur (DECIMAL seuil) 'montant sous le seuil';
DEFINE ACTION NotifierValideur (TEXT dossier)  'demande d''approbation';
DEFINE CONST  seuil_auto 500 'approbation automatique sous ce montant';

INITIALIZE WORKFLOW
    ON EVENT DemandeSoumise RECURSIVE SWITCH Tri

DEFINE STATE Tri 'aiguillage'
    ON EVENT DemandeSoumise
        WHEN MontantInferieur(seuil = seuil_auto)
            SWITCH Approuvee
        EXECUTE NotifierValideur(dossier = @Workflow.ExternalId)
            SWITCH EnValidation
;
DEFINE STATE Approuvee    'approbation automatique' ;
DEFINE STATE EnValidation 'en attente d''un valideur'
    EXPIRE AFTER 3 DAY SWITCH Expiree
;
DEFINE STATE Expiree      'sans réponse' ;
```

```python
from urban_workflow import (
    FunctionRegistry, MemoryStorage, Workflow,
    WorkflowEngine, WorkflowFactory, WorkflowProcessor, WorkflowsConfig,
)
from urban_workflow.dsl import parse_config
from urban_workflow.outputs import default_memory_output

rules = FunctionRegistry("rule")
rules.add("MontantInferieur", lambda ctx, seuil: float(
    ctx.incoming_event.extended_datas.get("Montant", 0)) < seuil)

storage = MemoryStorage()
configs = WorkflowsConfig().add_document(parse_config(SCRIPT, rules=rules))
processor = WorkflowProcessor(configs, WorkflowFactory())
processor.load_existing_workflows_by_external_id = (
    lambda key: storage.get_by(Workflow, key, "external_id"))
processor.output_actions = default_memory_output(storage)
engine = WorkflowEngine(processor)

engine.evaluate_event({"Name": "DemandeSoumise", "ExternalId": "DA-42",
                       "Type": "achat", "Montant": 120})
```

## Documentation

| Guide | Contents |
|---|---|
| [docs/en/dsl-guide.md](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/docs/en/dsl-guide.md) | **Writing a workflow**: complete language reference — instance creation, `SWITCH` transitions, `WHEN` conditions, actions, expiration, fragments |
| [docs/en/rules-guide.md](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/docs/en/rules-guide.md) | **Rules and scripts**: `DEFINE RULE … AS @script`, multi-language `ScriptRegistry`, the `expression` language (operators, functions, `NULL` semantics), checks |
| [docs/en/artifacts-guide.md](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/docs/en/artifacts-guide.md) | **JSON artifacts**: `DEFINE ARTIFACT … AS @schema USING @script`, `jslt` scripts (urban-json), `@artifact.Name` in actions, schema validation |
| [docs/en/integration-guide.md](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/docs/en/integration-guide.md) | **Using it in code**: business rules, engine assembly, JSLT templates, output chain, technical events |
| [docs/en/testing-guide.md](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/docs/en/testing-guide.md) | **Testing it**: pure rules, configuration, templates, end-to-end scenarios |
| [docs/analyse-portage.md](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/docs/analyse-portage.md) (French) | Analysis of the source project and the key design choices of the port |

## Playground

**Standalone web components** (editor with intellisense, diagram,
schemas, scripts, simulation on the real engine…) that an application
mounts as needed, and a page that assembles them — a separate
distribution, [`urban-workflow-playground`](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/playground/README.en.md):

```bash
pip install urban-workflow-playground
urban-workflow-playground     # http://127.0.0.1:8777
```

## Installation and development

```bash
pip install -e .[dev]
python3 -m pytest -q
```

Single core dependency: `urban-json` (which brings the ANTLR runtime used
by the DSL parser). Python ≥ 3.10.

## Porting status

Foundation, DSL (ANTLR grammars taken from the source), engine, urban-json
templating and SQL persistence (`[sql]` extra, SQLAlchemy Core) are
functional and tested (end-to-end scenarios *DSL script → events → states
→ messages*, lifecycle persisted in SQL), with GitHub Actions CI (Python
3.10 → 3.13 tests, ruff lint). Known bugs of the source engine are not
reproduced — each one is covered by a dedicated test.

## License

See [LICENSE](https://github.com/gaelgael5/urban-workflow/blob/v0.2.1/LICENSE).
