export()

Exportiert Organisationsdaten, Workflowprojekte und Workflowmodelle über wfm.Export.

Der Server antwortet mit einer einzelnen .exp-Datei: UTF-16-kodiertes XML mit dem Wurzelelement <WorkflowImport>, also genau das, was wfm.Import wieder entgegennimmt.

Die ganze Organisation passt in einen Aufruf: das Wurzelprojekt mit complete=True auswählen. Der Server dokumentiert das als „all projects or workflows hanging under the project", der Baum muss also nicht ausgeschrieben werden.

1. Signatur

  • 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. Parameter

Parameter Typ Beschreibung

organisation

str | ECMOrganisation | None

Die Organisation als GUID oder als ECMOrganisation. Ohne Angabe wird die aktive Organisation verwendet.

projects

Iterable[ECMWorkflowProjectSelection] | None

Die zu exportierenden Projekte und Workflowmodelle. None oder eine leere Sequenz exportiert kein Projekt — sinnvoll zusammen mit include_organisation.

include_organisation

bool

Organisationsstruktur mit exportieren (Serverparameter OrgDoExport). Voreingestellt True.

include_report_configs

bool

Report-Konfigurationen der Organisation mit exportieren.

report_config_ids

Iterable[str]

Die zu exportierenden Report-Konfigurationen. Leer zusammen mit include_report_configs=True exportiert alle.

include_routing_list_templates

bool

Adhoc-Verteilerlisten-Vorlagen der Organisation mit exportieren.

routing_list_template_ids

Iterable[str]

Die zu exportierenden Vorlagen. Leer zusammen mit include_routing_list_templates=True exportiert alle.

3. Rückgabewert

Die Exportdateien, so wie der Server sie liefert. Gegen enaio 12.0 gemessen ist es in jedem Fall genau eine .exp-Datei. Mit file.store(pfad) wird sie auf die Platte geschrieben.

4. ECMWorkflowProjectSelection

Ein Knoten des WFProjectTree-Parameters.

Feld Typ Beschreibung

project

str | ECMWorkflowProject

Das zu exportierende Projekt als GUID oder als ECMWorkflowProject.

complete

bool

Alles unterhalb des Projekts exportieren. True schreibt CompleteExport="1" und macht children und workflows überflüssig. False (Vorgabe) schreibt "0" und exportiert nur, was children und workflows benennen — das brauchen die Vorfahren eines ausgewählten Zweigs.

children

Sequence[ECMWorkflowProjectSelection]

Verschachtelte Projektauswahl, um einzelne Zweige zu adressieren.

workflows

Sequence[str]

GUIDs einzelner Workflowmodelle unterhalb dieses Projekts.

5. Beispiele

5.1. Ganze Organisation exportieren

  • 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. Nur die Organisationsstruktur

files = ecm.workflow.export()

5.3. Einzelne Projekte und Workflows

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

5.4. Mit Report-Konfigurationen und Verteilerlisten-Vorlagen

Leere ID-Listen bedeuten „alle":

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

6. Exportdatei lesen

import xml.etree.ElementTree as ET

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

6.1. Aufbau

<WorkflowImport EngineVersion ExportSubVersion ExportEncoding>
  <Organisation Id Name>
    <OrgStructure/>                                   <!-- Klassen, Attribute, Objekte -->
    <Eventtypes/>
    <WorkflowProject Id Name Type Description CreatorId CreationTime
                     Priority ProcCount VersionCount>  <!-- rekursiv verschachtelt -->
      <Workflow Id Name Description State Version PackageId IconId
                CreatorId CreationTime/>               <!-- nur unter Type=3 -->
    </WorkflowProject>
  </Organisation>
  <ClientTypes/>
  <ResourceLanguages/>
</WorkflowImport>

<WorkflowProject> ist beliebig tief verschachtelt und bildet damit die Projekt-Hierarchie ab, die projects() nicht liefert. Priority, ProcCount und VersionCount stehen nur hier.

6.2. Ordnerstruktur je Workflowmodell

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

Projektnamen sind nicht eindeutig — Kollisionen sind selbst zu behandeln, siehe Projekt-Hierarchie nachbilden.

6.3. Organisation und Workflows getrennt exportieren

Die grosse Datei nachträglich zu zerlegen ist heikel: ein Teilstück ist nur dann wieder importierbar, wenn es die organisationsweiten Abschnitte mitträgt — <ClientTypes> und <ResourceLanguages> auf WorkflowImport-Ebene, <Eventtypes> auf Organisation-Ebene, dazu die Kette der <WorkflowProject>-Vorfahren. Hinzu kommt, dass Events auf Client-Type-IDs verweisen, darunter die Sentinel-ID FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, die in <ClientTypes> gar nicht definiert ist.

Einfacher und ohne dieses Risiko: den Server die Teile selbst erzeugen lassen. Die Organisationsstruktur einmal ohne Projekte, dann je Workflowfamilie einen Aufruf mit include_organisation=False — sonst steckt <OrgStructure> in jeder einzelnen Datei.

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

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

# Organisationsstruktur einmal, ohne Projekte
ecm.workflow.export()[0].store("organisation.xml")

def familien(node, ordner, kette):
    """Ordner nur für Projekte, Dateien je Familie; die Kette führt zur Familie."""
    for project in node.findall("WorkflowProject"):
        name, project_id, typ = project.get("Name", ""), project.get("Id"), project.get("Type")
        if typ == "1":                                   # Wurzelprojekt
            yield from familien(project, ordner / name, kette)
        elif typ == "2":                                 # Workflowprojekt
            yield from familien(project, ordner / name, kette + [project_id])
        else:                                            # Workflowfamilie
            yield ordner, name, kette, project_id

for ordner, name, kette, family_id in familien(document.find("Organisation"), Path("."), []):
    auswahl = ECMWorkflowProjectSelection(family_id, complete=True)
    for vorfahre in reversed(kette):
        auswahl = ECMWorkflowProjectSelection(vorfahre, children=[auswahl])

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

Ergebnis:

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   (enthält die Modelle Test und WF)
Workflowprojekte/test2/Test2.xml       26 758 B

Zwei Feinheiten:

  • Die Vorfahrenkette darf beim Wurzelprojekt beginnen, muss aber nicht — oben ist es weggelassen, der Server löst über die IDs auf.

  • Eine Familie kann mehrere Modelle enthalten, die dann in derselben Datei landen. Wer je Modell eine Datei will, wählt das Blatt statt mit complete=True mit workflows=[model_id] und nimmt die Modell-IDs aus den <Workflow>-Elementen.

wfm.Import trennt Organisation und Workflows über den eigenen Schalter DoImportOrganisation, die Aufteilung entspricht also dem vorgesehenen Importmodell. Ob ein so erzeugter Satz vollständig zurückgelesen wird, lässt sich nur durch einen echten Import bestätigen.

CompleteExport muss auf jedem <WorkflowProject> stehen. Der Serverreferenz zufolge ist das Attribut optional; tatsächlich exportiert ein Knoten ohne es nichts unterhalb seiner selbst — und zwar stillschweigend, mit Rückgabecode 0 und einer Datei, die einfach kleiner ist. Gemessen auf zwei enaio-12.0-Installationen.

ECMWorkflowProjectSelection schreibt das Attribut deshalb immer, mit False als Vorgabe. Wer den WFProjectTree von Hand baut, muss selbst daran denken.

7. Siehe auch