projects()

Gibt die Workflow-Projektliste einer Organisation zurück über wfm.GetProjectList.

Die Antwort ist eine flache Liste: Sie trägt kein Eltern-Attribut und keine Verschachtelung. Kein Serverjob liefert die Projekt-Hierarchie als Ausgangsparameter — sie ist nur über die Tabelle oswfprojectsrel oder über die Exportdatei von export() zu bekommen. Beide Wege samt Voraussetzungen und Fallstricken stehen unter Projekt-Hierarchie nachbilden.

1. Signatur

  • Sync

  • Async

ecm.workflow.projects(
    organisation: str | ECMOrganisation | None = None,
) -> list[ECMWorkflowProject]
await ecm.workflow.projects(
    organisation: str | ECMOrganisation | None = None,
) -> list[ECMWorkflowProject]

2. Parameter

Parameter Typ Beschreibung

organisation

str | ECMOrganisation | None

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

3. Rückgabewert

Eine Liste von ECMWorkflowProject-Instanzen in Server-Reihenfolge. Gibt eine leere Liste zurück, wenn die Organisation keine Projekte enthält.

3.1. Felder von ECMWorkflowProject

Feld Typ Beschreibung

id

str

GUID des Projekts.

name

str

Anzeigename. Das Systemprojekt heisst %SYSTEMPROJECT% und trägt die feste ID FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF.

type

ECMWorkflowProjectType

ROOT (Wurzelprojekt), PROJECT (Workflowprojekt) oder FAMILY (Workflowfamilie).

creator_id

str

GUID des Erstellers, leer bei Einträgen, die der Server selbst angelegt hat.

created_at

datetime | None

Erstellzeit als UTC-datetime. None, wo der Server 0 gemeldet hat.

description

str

Beschreibung, leer wenn nicht gesetzt.

3.2. Werte von ECMWorkflowProjectType

Member Wert Bedeutung

ROOT

1

Das einzige Wurzelprojekt der Organisation; alle anderen Projekte hängen darunter.

PROJECT

2

Ein gewöhnliches Workflowprojekt, also ein Ordner für Projekte und Familien.

FAMILY

3

Eine Workflowfamilie. Ihre Modelle listet der Serverjob wfm.GetWorkflowListByFamily.

4. Beispiele

4.1. Projekte auflisten

  • Sync

  • Async

for project in ecm.workflow.projects():
    print(f"{project.type.name:8} {project.name} ({project.id})")
for project in await ecm.workflow.projects():
    print(f"{project.type.name:8} {project.name} ({project.id})")

4.2. Wurzelprojekt ermitteln

Das Wurzelprojekt ist der Einstiegspunkt für einen Voll-Export, und sein Name eignet sich als Name des Zielordners.

  • Sync

  • Async

from ecmind_blue_client.ecm import ECMWorkflowProjectType

root = next(p for p in ecm.workflow.projects() if p.type is ECMWorkflowProjectType.ROOT)
print(root.name)
from ecmind_blue_client.ecm import ECMWorkflowProjectType

projects = await ecm.workflow.projects()
root = next(p for p in projects if p.type is ECMWorkflowProjectType.ROOT)
print(root.name)

5. Projekt-Hierarchie nachbilden

Kein Serverjob liefert die Kanten des Baums als Ausgangsparameter. Das gilt für alle dokumentierten wfm-Jobs und für jeden anderen Namespace. Ableitbar sind über die Job-Ausgaben nur die Knoten samt Typ (dieser Job) und die Kante Modell → Familie (wfm.GetWorkflowList über FamilyId).

Die Doku ist an dieser Stelle asymmetrisch: wfm.Export und wfm.Import verlangen die Hierarchie als Eingabe (WFProjectTree verschachtelt, DestPrjId, OldParent), aber kein Job gibt sie zurück.

Zwei naheliegende Fehlschlüsse:

  • wfm.GetOrganisationObjects liefert zwar <ParentObjects>, das ist aber die Organisations-Hierarchie — Rollen, Abteilungen, Personen. Nicht die Projektstruktur.

  • wfm.DBCommands ist kein SQL-Job. Er führt ausschliesslich per GUID bereits gecachte Kommandos aus.

5.1. Weg 1: Kanten aus oswfprojectsrel

rows = ecm.db.select(
    "SELECT parent_id, project_id FROM oswfprojectsrel WHERE org_id = %s",
    organisation_id,
)
parent_of = {row.typed("project_id", str): row.typed("parent_id", str) for row in rows}

Voraussetzungen und Fallstricke:

  • Systemrolle 88 ("Server: Execute Ado jobs") ist nötig, sonst schlägt ado.ExecuteSQL fehl — mit einer untypisierten BlueException, weil es für ADO-Resultcodes keine Fehlerübersetzung gibt.

  • ado.ExecuteSQL umgeht die Objektrechteprüfung des DMS, und impersonate() wirkt darauf nicht: der Kontextwechsel ist ein Job-Parameter, kein Datenbankkontext.

  • Der Filter auf org_id ist Pflicht — die Tabelle hält alle Organisationen.

  • Die Schema-Seite der Referenz trägt den Vermerk, sie sei automatisch generiert und könne unvollständig oder falsch sein. Ein Serverupgrade kann die Tabelle ändern, ohne dass die API sich ändert.

5.2. Weg 2: aus der Exportdatei lesen

wfm.Export liefert die Projekte nativ verschachtelt, die Hierarchie steckt also bereits in der Datei, die ein Export ohnehin erzeugt — ohne SQL und ohne Sonderrechte. Aufbau und Beispielcode stehen in export().

Das Format der Exportdatei ist in keiner Fassung der Server-Referenz beschrieben, und es gibt kein XSD dafür. Wer sie auswertet, betreibt Reverse Engineering ohne Kompatibilitätszusage. Gemessen gegen enaio 12.0 stimmen die daraus gelesenen Kanten exakt mit oswfprojectsrel überein, auf zwei unabhängigen Installationen und über vier Exportvarianten.

5.3. Invarianten für den Zusammenbau

Auf enaio 12.0 gemessen, auf beiden geprüften Installationen gültig:

  • Genau ein Projekt hat type is ECMWorkflowProjectType.ROOT, und nur dieses hat keine Elternzeile.

  • Die Kanten bilden einen Baum: bei n Projekten gibt es n-1 Kanten, und jedes Projekt ist höchstens einmal Ziel einer Kante.

  • Workflowmodelle hängen ausschliesslich unter FAMILY, und Familien haben nie Unterprojekte. Der Baum ist damit Wurzel → Projekte → Familie → Modelle.

  • Projektnamen sind nicht eindeutig. Auf einem der Testsysteme heissen ein Projekt und eine Familie beide test. Wer daraus Verzeichnisse baut, muss Kollisionen selbst behandeln.

5.4. Warum die Bibliothek das nicht übernimmt

ecm.workflow bildet die Server-API ab und ergänzt sie nicht um Abfragen gegen das Datenbankschema. Eine öffentliche Operation mit internem SQL bräuchte stillschweigend Systemrolle 88, umginge die Rechteprüfung und die Wirkung von impersonate(), und koppelte die Bibliothek an ein Schema ohne Kompatibilitätszusage. Der Aufruf bleibt darum beim Aufrufer, der die Kosten kennt und entscheidet.

6. Siehe auch