export()

Exports organisation data, workflow projects, and workflow models via wfm.Export.

The server answers with a single .exp file: UTF-16 encoded XML with a <WorkflowImport> root, exactly what wfm.Import takes back.

The whole organisation fits into one call: select the root project with complete=True, which the server documents as "all projects or workflows hanging under the project", so no tree has to be spelled out.

1. Signature

  • Sync

  • Async

ecm.workflow.export(
    organisation: str | ECMOrganisation | None = None,
    *,
    projects: Iterable[ECMWorkflowProjectSelection] | None = None,
    include_organisation: bool = True,
    include_report_configs: bool = False,
    report_config_ids: Iterable[str] = (),
    include_routing_list_templates: bool = False,
    routing_list_template_ids: Iterable[str] = (),
) -> list[JobResponseFile]
await ecm.workflow.export(
    organisation: str | ECMOrganisation | None = None,
    *,
    projects: Iterable[ECMWorkflowProjectSelection] | None = None,
    include_organisation: bool = True,
    include_report_configs: bool = False,
    report_config_ids: Iterable[str] = (),
    include_routing_list_templates: bool = False,
    routing_list_template_ids: Iterable[str] = (),
) -> list[JobResponseFile]

2. Parameters

Parameter Type Description

organisation

str | ECMOrganisation | None

The organisation as a GUID or as an ECMOrganisation. Defaults to the active organisation when omitted.

projects

Iterable[ECMWorkflowProjectSelection] | None

The projects and workflow models to export. None or an empty sequence exports no project — useful together with include_organisation.

include_organisation

bool

Include the organisation structure (server parameter OrgDoExport). Defaults to True.

include_report_configs

bool

Include the organisation’s report configurations.

report_config_ids

Iterable[str]

Report configurations to export. An empty iterable with include_report_configs=True exports all of them.

include_routing_list_templates

bool

Include the organisation’s adhoc routing list templates.

routing_list_template_ids

Iterable[str]

Routing list templates to export. An empty iterable with include_routing_list_templates=True exports all of them.

3. Return value

The export files as returned by the server. Measured against enaio 12.0 it is one .exp file in every case. Use file.store(path) to write it to disk.

4. ECMWorkflowProjectSelection

One node of the WFProjectTree parameter.

Field Type Description

project

str | ECMWorkflowProject

The project to export, as a GUID or as an ECMWorkflowProject.

complete

bool

Export everything below the project. True writes CompleteExport="1" and makes children and workflows unnecessary. False (the default) writes "0" and exports only what children and workflows name — which is what the ancestors of a selected branch need.

children

Sequence[ECMWorkflowProjectSelection]

Nested project selections, used to address single branches.

workflows

Sequence[str]

GUIDs of individual workflow models below this project.

5. Examples

5.1. Export the whole organisation

  • Sync

  • Async

from ecmind_blue_client.ecm import ECMWorkflowProjectSelection, ECMWorkflowProjectType

root = next(p for p in ecm.workflow.projects() if p.type is ECMWorkflowProjectType.ROOT)
files = ecm.workflow.export(projects=[ECMWorkflowProjectSelection(root, complete=True)])
files[0].store(f"{root.name}.exp")
from ecmind_blue_client.ecm import ECMWorkflowProjectSelection, ECMWorkflowProjectType

projects = await ecm.workflow.projects()
root = next(p for p in projects if p.type is ECMWorkflowProjectType.ROOT)
files = await ecm.workflow.export(projects=[ECMWorkflowProjectSelection(root, complete=True)])
files[0].store(f"{root.name}.exp")

5.2. Organisation structure only

files = ecm.workflow.export()

5.3. Single projects and workflows

files = ecm.workflow.export(
    projects=[
        ECMWorkflowProjectSelection(
            "AAA...",
            children=[ECMWorkflowProjectSelection("BBB...", workflows=["W1...", "W2..."])],
        )
    ],
    include_organisation=False,
)

5.4. With report configurations and routing list templates

Empty ID lists mean "all of them":

files = ecm.workflow.export(
    projects=[ECMWorkflowProjectSelection(root, complete=True)],
    include_report_configs=True,
    include_routing_list_templates=True,
)

6. Reading the export file

import xml.etree.ElementTree as ET

document = ET.fromstring(files[0].bytes().decode("utf-16"))
print(document.tag)  # WorkflowImport

6.1. Structure

<WorkflowImport EngineVersion ExportSubVersion ExportEncoding>
  <Organisation Id Name>
    <OrgStructure/>                                   <!-- classes, attributes, objects -->
    <Eventtypes/>
    <WorkflowProject Id Name Type Description CreatorId CreationTime
                     Priority ProcCount VersionCount>  <!-- nested recursively -->
      <Workflow Id Name Description State Version PackageId IconId
                CreatorId CreationTime/>               <!-- below Type=3 only -->
    </WorkflowProject>
  </Organisation>
  <ClientTypes/>
  <ResourceLanguages/>
</WorkflowImport>

<WorkflowProject> nests to any depth and therefore carries the project hierarchy that projects() does not return. Priority, ProcCount, and VersionCount appear here only.

6.2. A folder path per workflow model

from pathlib import Path
import xml.etree.ElementTree as ET

document = ET.fromstring(files[0].bytes().decode("utf-16"))

def walk(node, path):
    for project in node.findall("WorkflowProject"):
        here = path + [project.get("Name", "")]
        for model in project.findall("Workflow"):
            yield Path(*here) / f"{model.get('Name')}.xml"
        yield from walk(project, here)

for target in walk(document.find("Organisation"), []):
    print(target)   # Workflowprojekte/test1/Test/WF.xml

Project names are not unique — handle collisions yourself, see Reconstructing the project hierarchy.

6.3. Exporting the organisation and the workflows separately

Taking the big file apart afterwards is delicate: a fragment can only be imported again when it carries the organisation-wide sections with it — <ClientTypes> and <ResourceLanguages> at the WorkflowImport level, <Eventtypes> at the Organisation level, plus the chain of <WorkflowProject> ancestors. On top of that, events reference client-type IDs, among them the sentinel FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, which is not defined in <ClientTypes> at all.

Simpler and without that risk: let the server produce the parts. The organisation structure once without any project, then one call per workflow family with include_organisation=False — otherwise <OrgStructure> ends up in every single file.

from pathlib import Path
import xml.etree.ElementTree as ET

document = ET.fromstring(files[0].bytes().decode("utf-16"))

# the organisation structure once, without any project
ecm.workflow.export()[0].store("organisation.xml")

def families(node, folder, chain):
    """Folders for projects only, one file per family; the chain leads to the family."""
    for project in node.findall("WorkflowProject"):
        name, project_id, kind = project.get("Name", ""), project.get("Id"), project.get("Type")
        if kind == "1":                                  # root project
            yield from families(project, folder / name, chain)
        elif kind == "2":                                # workflow project
            yield from families(project, folder / name, chain + [project_id])
        else:                                            # workflow family
            yield folder, name, chain, project_id

for folder, name, chain, family_id in families(document.find("Organisation"), Path("."), []):
    selection = ECMWorkflowProjectSelection(family_id, complete=True)
    for ancestor in reversed(chain):
        selection = ECMWorkflowProjectSelection(ancestor, children=[selection])

    part = ecm.workflow.export(projects=[selection], include_organisation=False)[0]
    folder.mkdir(parents=True, exist_ok=True)
    part.store(folder / f"{name}.xml")

Result:

organisation.xml                       63 940 B
Workflowprojekte/test1/Adhoc.xml     1154 822 B
Workflowprojekte/test1/Taskflow.xml   726 460 B
Workflowprojekte/test1/Test.xml        87 824 B   (holds the models Test and WF)
Workflowprojekte/test2/Test2.xml       26 758 B

Two details:

  • The ancestor chain may start at the root project but does not have to — above it is left out, and the server resolves by ID.

  • A family can hold several models, which then share one file. For one file per model, select the leaf with workflows=[model_id] instead of complete=True, taking the model IDs from the <Workflow> elements.

wfm.Import separates organisation and workflows through its own DoImportOrganisation flag, so this split matches the intended import model. Whether such a set reads back completely can only be confirmed by an actual import.

CompleteExport has to be present on every <WorkflowProject>. The server reference calls the attribute optional; in practice a node without it exports nothing below itself — silently, with return code 0 and an export file that is merely smaller. Measured on two enaio 12.0 installations.

ECMWorkflowProjectSelection therefore always writes the attribute, defaulting to False. Anyone building the WFProjectTree by hand has to remember it.

7. See also