ecm-generate-workflow-models
ecm-generate-workflow-models is a command-line tool that generates typed Python model classes for a workflow’s input variables — the static counterpart to workflow_model_by_name().
The generated classes map each input variable to a typed attribute (scalars to native Python types, lists to list[…], records to their own fully typed nested classes). This gives IDE completion and type checking, and a model instance can be passed straight to start_process() — it serialises itself into the DataFields format the server expects.
1. Installation
The command ships with the main installation — no extra required:
-
uv
-
pip
uv add ecmind-blue-client
pip install ecmind-blue-client
2. Prerequisites
The tool reads workflows live from the server (wfm.GetWorkflowList to enumerate, wfm.GetWorkflow per workflow). Unlike ecm-generate-models, there is no --file source.
To enumerate the startable workflows the server requires two inputs:
-
a workflow-organisation user (not the security login). When
--useris omitted, the tool resolves it from the login name (--username) via itsLoginattribute. -
a client type (
--client-type), as a GUID or a name such asOSECM_Client. The server requires exactly one client type; there is no "all clients" value.
3. Arguments
3.1. Server connection
| Argument | Default | Description |
|---|---|---|
|
— |
enaio server hostname or IP. Required. |
|
|
TCP port of the enaio server. |
|
|
Login username. Falls back to the |
|
|
Login password. Falls back to the |
|
SSL on |
Disable SSL/TLS. Enabled by default. |
|
— |
Application name shown in Enterprise Manager (optional). |
3.2. Workflow selection
| Argument | Default | Description |
|---|---|---|
|
from |
Workflow-organisation user GUID whose startable workflows are read. When omitted, resolved from the login name ( |
|
— |
Client type as a GUID or name (e.g. |
|
active org |
Organisation GUID. Defaults to the active organisation. |
|
all |
Only generate the workflow with this model name. |
4. Examples
4.1. Generate all startable workflows
The common case — --user is resolved from --username:
ecm-generate-workflow-models \
--host enaio.example.com \
--username admin \
--password secret \
--client-type "#OSECM_Client#" \
--output-dir ./wf_models
Terminal output:
wrote wf_models/test.py
4.2. Credentials from the environment
export ECM_USERNAME=admin ECM_PASSWORD=secret
ecm-generate-workflow-models \
--host enaio.example.com \
--client-type "#OSECM_Client#" \
--output-dir ./wf_models
5. Output format
One .py file is written per workflow. It defines an ECMWorkflowModel subclass with the input variables as typed attributes. Record variables are generated as their own fully typed ECMWorkflowRecordModel subclasses (recursively, including record-in-record and lists of records).
Example of a generated file:
# DO NOT EDIT — regenerate with generate_workflow_models.py (ecm-generate-workflow-models).
"""Generated typed workflow-variable model."""
from __future__ import annotations
from datetime import date, datetime, time
from ecmind_blue_client.ecm import ECMWorkflowModel, ECMWorkflowRecordModel # base classes for typed access
class TestRecordVarRecord(ECMWorkflowRecordModel):
"""Generated record model."""
StringField: str | None = None
IntegerField: int | None = None
TimeField: time | None = None
DateTimeField: datetime | None = None
FloatField: float | None = None
StringListField: list[str] | None = None
class TestWorkflowModel(ECMWorkflowModel):
"""Input variables for the 'test' workflow."""
test: str | None = None
StringVar: str | None = None
DateVar: date | None = None
DatetimeVar: datetime | None = None
StringListVar: list[str] | None = None
FloatVar: float | None = None
IntegerVar: int | None = None
TimeVar: time | None = None
TestRecordVar: TestRecordVarRecord | None = None
6. Starting a workflow with generated models
Import the generated file and pass the model instance to start_process() as variables. The model serialises its typed values into the DataFields format itself (scalars as <String>, datetime as a Unix epoch, lists/records in the server format).
-
Sync
-
Async
import datetime
from ecmind_blue_client.ecm import ECM
from ecmind_blue_client.pool import SyncPoolClient
from wf_models.test import TestWorkflowModel, TestRecordVarRecord
ecm = ECM(SyncPoolClient(servers="enaio.example.com:4000:1", username="admin", password="secret"))
# Resolve the workflow-organisation user from the login
user = ecm.workflow.organisation_user_by_login("admin")
# Fill the record variable type-safely
record = TestRecordVarRecord(
StringField="x",
IntegerField=1,
TimeField="08:00:00",
DateTimeField="1779365400", # datetime member: Unix epoch
FloatField="2.5",
StringListField=["inner"],
)
# Model instance with all typed variables
variables = TestWorkflowModel(
StringVar="Hello",
IntegerVar=42,
FloatVar=3.14,
DateVar=datetime.date(2026, 6, 11),
DatetimeVar=datetime.datetime(2026, 6, 11, 10, 30, 0), # serialised as a Unix epoch
TimeVar=datetime.time(10, 30, 0),
StringListVar=["a", "b"],
TestRecordVar=record,
)
# Start (organisation defaults to the active one)
process_id = ecm.workflow.start_process(
user=user,
workflow="test",
client_type="#OSECM_Client#",
variables=variables,
)
print("started process:", process_id)
import datetime
from ecmind_blue_client.ecm import ECM
from ecmind_blue_client.pool import AsyncPoolClient
from wf_models.test import TestWorkflowModel, TestRecordVarRecord
ecm = ECM(AsyncPoolClient(servers="enaio.example.com:4000:1", username="admin", password="secret"))
user = await ecm.workflow.organisation_user_by_login("admin")
record = TestRecordVarRecord(
StringField="x", IntegerField=1, TimeField="08:00:00",
DateTimeField="1779365400", FloatField="2.5", StringListField=["inner"],
)
variables = TestWorkflowModel(
StringVar="Hello",
IntegerVar=42,
DatetimeVar=datetime.datetime(2026, 6, 11, 10, 30, 0),
StringListVar=["a", "b"],
TestRecordVar=record,
)
process_id = await ecm.workflow.start_process(
user=user,
workflow="test",
client_type="#OSECM_Client#",
variables=variables,
)
print("started process:", process_id)
6.1. Attaching files to the workflow
Documents are attached via the documents parameter. wfm.StartProcess references documents already archived in enaio by id/type and places them into the workflow file’s workspace — it does not upload new files. Create the DMS document first, then pass its id/type:
from ecmind_blue_client.ecm import ECMWorkspaceDocument
from ecmind_blue_client.rpc._job_request_file import JobRequestFileFromPath
# 1) Create a DMS document with a file (or use an existing one)
doc = ecm.dms.insert_and_get(
MyDocumentModel(Name="Contract"),
files=[JobRequestFileFromPath("contract.pdf")],
folder_id=folder_id,
register_id=register_id,
)
# 2) Pass its id/type when starting
process_id = ecm.workflow.start_process(
user=user,
workflow="test",
client_type="#OSECM_Client#",
variables=variables,
documents=[ECMWorkspaceDocument(id=doc.system.id, type=doc.system.type_id)],
)
7. Without generation: runtime models
If static typing is not needed, the model can also be built at runtime without generation —
workflow_model_by_name() returns the same class:
Model = ecm.workflow.workflow_model_by_name("test", user, "#OSECM_Client#")
record = Model.record_model("TestRecordVar")(StringField="x", IntegerField=1, ...)
variables = Model(StringVar="Hello", IntegerVar=42, TestRecordVar=record)
ecm.workflow.start_process(user=user, workflow="test",
client_type="#OSECM_Client#", variables=variables)
8. When to regenerate
Regenerate the files when:
-
a workflow’s input variables are added, removed, or retyped
-
record structures change
|
The files carry a |
9. See also
-
ecm-generate-models — the DMS counterpart (folder / register / document models)
-
workflow_model_by_name() — runtime variant of model creation
-
start_process() — create and start a workflow
-
ecm.workflow — overview of the workflow operations