Job('wfm.StartProcess') auf Ansi System

Hallo zusammen,

ich hatte vor einiger Zeit einen Job erstellt, der Workflows auf einem UTF-8-enaio-System startet. Die -Werte werden dabei aus Tabellen in enaio ermittelt.

„DataFields“ erwartet hierbei eine Base64 codierung.

Seite:218

Nun wollte ich diesen Job auf einem ANSI-System wiederverwenden, stoße jedoch auf Probleme.

Screenshot 2024-11-13 150143

Die Funktion encode() ohne Parameter soll automatisch in UTF-8 kodieren. Daher war meine Schlussfolgerung, dass der Aufruf mit den Parametern ‚windows-1252‘, ‚cp1252‘ oder ‚iso-8859-1‘ erfolgen sollte.

Sowohl mit diesen Parameterisierungen als auch ohne Parameter erhalte ich jedoch folgenden Fehler:

Hoi @Globisch, hättest Du allenfalls etwas Code-Kontext, wie Du den workflow_job instanziierst?

Ich sehe sonst noch Potenzial, falls der DataField-Parameter ein XML ist, dieses nicht von Hand umzuwandeln, sondern als XmlElement zu erzeugen, vgl. etwa diesen Test, auch wenn das XML in diesem Fall für eine Anfrage vorliegt, sollte sich das Beispiel adaptieren lassen.

Hey @rk,

Ich kann aus dem System nichts direkt rauskopieren, weshalb ich es per KI aus einem Screenshot extrahieren musste, ich habe es gegen gelesen sollte also passen.

Das mit dem XmlElement werde ich ausprobieren. Das Skript ist schon etwas älter und stammt aus meinen ersten Gehversuchen. Besonders bei der Methode def convert_base64 habe ich beim einiges von Tests auskommentiert.

Process-Start:

def start_workflow(self, reminderObject: ReminderObject):

    process_job = Job("wfm.CreateProcessInstance", None, None, False)
    user_id = Param("UserId", ParamTypes.STRING, self.constantController.WF_USER_ID)
    process_job.append(user_id)

    organisation_id = Param("OrganisationId", ParamTypes.STRING, self.constantController.WF_ORG_ID)
    process_job.append(organisation_id)

    workflow_id = Param("WorkflowId", ParamTypes.STRING, self.constantController.WF_WF_ID)
    process_job.append(workflow_id)

    clientType_id = Param("ClientTypeId", ParamTypes.STRING, self.constantController.WF_CLIENT_TYP_ID)
    process_job.append(clientType_id)

    response = self.client.execute(process_job)
    process_id = response.values["ProcessId"]

    if process_id != "":
        workflow_job = Job("wfm.StartProcess", None, None, False)
        
        workflow_job.append(user_id)

        string_workspace = "<Workspace></Workspace>"
        workspace = Param("Workspace", ParamTypes.BASE64, self.convert_base64(string_workspace))
        workflow_job.append(workspace)

        processId = Param("ProcessId", ParamTypes.STRING, process_id)
        workflow_job.append(processId)

        data_fields = Param("DataFields", ParamTypes.BASE64, self.convert_base64(reminderObject.get_data_field_xml()))
        workflow_job.append(data_fields)

        self.client.execute(workflow_job)

Data_Field:

def get_data_field_xml(self):
    data_fields = f"""<DataFields>
    <DataField Id='dTermin'><![CDATA[<WFVar><String>{self.appointment_date}</String></WFVar>]]></DataField>
    <DataField Id='sFolderID'><![CDATA[<WFVar><String>{self.folder_osid}</String></WFVar>]]></DataField>
    <DataField Id='sBetreff'><![CDATA[<WFVar><String>{self.subject}</String></WFVar>]]></DataField>
    <DataField Id='sReceiver'><![CDATA[<WFVar><String>{self.recipient}</String></WFVar>]]></DataField>
    <DataField Id='sType'><![CDATA[<WFVar><String>{self.object_type}</String></WFVar>]]></DataField>
    <DataField Id='sTerminnummer'><![CDATA[<WFVar><String>{self.appointment_number}</String></WFVar>]]></DataField>
    </DataFields>"""
    return data_fields
def convert_base64(self, s):
    # text_bytes = s.encode('windows-1252') #("iso-8859-1")
    # base64_bytes = base64.b64encode(text_bytes)
    # base64_text = base64_bytes.decode('windows-1252')
    # return base64_text
    s = bytearray(s.encode('windows-1252'))
    # s_text = s.decode('windows-1252')
    print(s)
    return s

Hallo @globisch, vielen Dank für das Update.

Ein Hinweis nebenbei: Die Instanziierung des Jobs sollte viel kompakter möglich sein:

    process_job = Job(
        jobname="wfm.CreateProcessInstance",
        UserId=self.constantController.WF_USER_ID,
        OrganisationId=self.constantController.WF_ORG_ID,
        WorkflowId=self.constantController.WF_WF_ID,
        ClientTypeId=self.constantController.WF_CLIENT_TYP_ID,
    )

Wir haben mit ecmind_blue_client_workflow auch schon ein paar Workflow-Helper, aber Deinen Anwendungsfall haben wir bisher nicht umgesetzt - ggf. könnten wir uns da zusammentun? Der Helper ist wiederum Open-Source und wir freuen uns über Pull-Requests oder Hilfe aller Art.

Wie ich sehe, verwendet das DataFields-XML ![DATA...]-Elemente. Diese werden leider von xml.etree.ElementTree und damit auch von XmlElement nicht unterstützt. Falls diese nicht nötig sind/wären, wäre die XML-Erzeugung viel einfacher sauber möglich:

from XmlElement import XmlElement
class TestClass():
    def __init__(self):
        self.appointment_date = "12.08.2024"
        self.folder_osid = 12345
        self.subject = "Test 1234"
        self.recipient = "ROOT"
        self.object_type = 232160
        self.appointment_number = "12345"

    def create_data_fields_xml(self) -> XmlElement:
        return XmlElement.from_object("DataFields", {"DataField": [
            {"@Id": "dTermin", "#": self.appointment_date},
            {"@Id": "sFolderID", "#": self.folder_osid},
            {"@Id": "sBetreff", "#": self.subject},
            {"@Id": "sReceiver", "#": self.recipient},
            {"@Id": "sType", "#": self.object_type},
            {"@Id": "sTerminnummer", "#": self.appointment_number},
        ]})

@Globisch Ich gehe nicht davon aus, dass wir auf das CDATA-Zeug verzichten dürfen?

Eventuell sollten wir den String dann zumindest so erzeugen:

from lxml import etree
from XmlElement import XmlElement

class TestClass():
    def __init__(self):
        self.appointment_date = "12.08.2024"
        self.folder_osid = 12345
        self.subject = "Test 1234"
        self.recipient = "ROOT"
        self.object_type = 232160
        self.appointment_number = "12345"

    def create_data_fields_xml_with_lxml(self) -> str:
        data_fields = etree.Element("DataFields")
        for field_id, value in [
            ("dTermin", self.appointment_date),
            ("sFolderID", self.folder_osid),
            ("sBetreff", self.subject),
            ("sReceiver", self.recipient),
            ("sType", self.object_type),
            ("sTerminnummer", self.appointment_number)
        ]:
            data_field = etree.SubElement(data_fields, "DataField", Id=field_id)
            cdata_content = etree.CDATA(f"<WFVar><String>{value}</String></WFVar>")
            data_field.text = cdata_content
        return etree.tostring(data_fields, pretty_print=True, encoding="unicode")

Hey @rk,

Ein Hinweis nebenbei: Die Instanziierung des Jobs sollte viel kompakter möglich sein

Da hast du vollkommen Recht.

Wir haben mit ecmind_blue_client_workflow auch schon ein paar Workflow-Helper, aber Deinen Anwendungsfall haben wir bisher nicht umgesetzt - ggf. könnten wir uns da zusammentun?

Ich hatte den Helper schon im Git gesehen aber mich noch nicht mit auseinander gesetzt. Wir können uns da gerne mal zusammen setzen.

Wie ich sehe, verwendet das DataFields -XML ![DATA...] -Elemente. Diese werden leider von xml.etree.ElementTree und damit auch von XmlElement nicht unterstützt. Falls diese nicht nötig sind/wären, wäre die XML-Erzeugung viel einfacher sauber möglich:

Ich kann es mal ausprobieren, bei Talend mit euren Talend-Komponenten konnte man soweit ich das noch weiß nicht drauf verzichten.

Ich werde mit deine Ansätzen etwas rumprobieren und ein Feedback geben. Weiß aber noch nicht wann ich es schaffe.

Hey @rk,

ich habe inzwischen diverse Lösungsansätze ausprobiert, darunter auch deinen sowie einen Ansatz mit ElementTree (damit hat der Jobaufruf dms.GetResultList einwandfrei funktioniert).

Aus meiner Sicht habe ich alle Optionen hinsichtlich Codierung und Übergabeformat (String, ByteArray etc.) ausgeschöpft.

Das Problem scheint ausschließlich im Zusammenhang mit dem ANSI-System aufzutreten. Auf UNICODE-Systemen konnte ich dieses Verhalten nicht feststellen.

Stehe ich auf dem Schlauch oder gibt es hier noch ein Problem im Zusammenhang mit ANSI ?

def get_data_field_xml(self):
    data_fields = ET.Element("DataFields")
    fields = [
        ('dTermin', self.appointment_date),
        ('sFolderID', str(self.folder_osid)),
        ('sBetreff', self.subject),
        ('sReceiver', self.recipient),
        ('sType', str(self.object_type)),
        ('sTerminnummer', self.appointment_number)
    ]
    
    for field_id, field_value in fields:
        data_field = ET.SubElement(data_fields, "DataField", Id=field_id)
        data_field.text = field_value
    xml_byte = ET.tostring(data_fields, encoding='utf-8')
    return xml_byte.decode('utf-8')

def get_workspace_xml(self):
    workspace = ET.Element("Workspace")
    xml_byte = ET.tostring(workspace, encoding='utf-8')
    return xml_byte.decode('utf-8')
def get_data_field_xml(self) -> XmlElement:
    return XmlElement.from_object("DataFields", {"DataField": [
        {"@Id": "dTermin", "#": self.appointment_date},
        {"@Id": "sFolderID", "#": self.folder_osid},
        {"@Id": "sBetreff", "#": self.subject},
        {"@Id": "sReceiver", "#": self.recipient},
        {"@Id": "sType", "#": self.object_type},
        {"@Id": "sTerminnummer", "#": self.appointment_number}
    ]}).to_string(encoding="utf-8").decode('utf-8')

def get_workspace_xml(self) -> XmlElement:
    return XmlElement.from_object("Workspace", {}).to_string(encoding="unicode").decode('utf-8')

1 Like

Hoi @Globisch, ich glaube, ich stehe dann auch auf dem Schlauch und hatte eigentlich auch für ANSI-Systeme keine Encoding-Sorgen auf dem Schirm. Eventuell würde hier die Möglichkeit bestehen, dass wir uns dies gemeinsam anschauen?

Mein Interesse gilt dabei insbesondere einem möglichen Bug im ecmind_blue_client, welchen ich natürlich gerne beheben würde, falls dieser mit ANSI in dieser Anfrage ein Problem hat.

1 Like

Das Problem konnte gelöst werden.

Ursache für den Fehler war ein Feld, das im Data_Fields des WF-Prozesses übergeben wurde, jedoch im Workflow in enaio nicht bekannt war.

Da Workflow-Fehler nicht im Server-Log aufschlagen, haben
@rk und ich den Fehler durch Ausgaben in der Job_caller.py ermitteln können.

1 Like

Hoi @Globisch, vielen Dank, war sehr spannend! Wir versuchen auf unserer Seite auf jeden Fall noch, die Fehlermeldungen hier besser nach vorn zu reichen, damit man sie wenigstens in Python sieht, wenn sie schon nicht im Enterprise Manager/Rettungsring aufschlagen.