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 |
|---|---|---|
|
|
The organisation as a GUID or as an |
|
|
The projects and workflow models to export. |
|
|
Include the organisation structure (server parameter |
|
|
Include the organisation’s report configurations. |
|
|
Report configurations to export. An empty iterable with |
|
|
Include the organisation’s adhoc routing list templates. |
|
|
Routing list templates to export. An empty iterable with
|
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 |
|---|---|---|
|
|
The project to export, as a GUID or as an |
|
|
Export everything below the project. |
|
|
Nested project selections, used to address single branches. |
|
|
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")
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 ofcomplete=True, taking the model IDs from the<Workflow>elements.
|
|
|
|
7. See also
-
projects()— project list and root project -
organisations()— list the organisations