Implementierung

Tabellen

Information

  • In der Adaptive Card-Vorlagen-Tabelle (DT450 Adaptive Card-Vorlage) werden Adaptive Cards-Vorlagen und pro Vorlage wird eine Jython-Klasse hinterlegt, die eine API implementiert, um die Adaptive Cards zu generieren und die verschiedenen Interaktionsmöglichkeiten behandelt. Eine Modul-ID in der Tabelle referenziert ein Modul, was die Jython-Klasse bei der Generierung unterstützt.
  • In der Adaptive Card-Tabelle (DT451 Adaptive Cards) werden die erzeugten Karten gespeichert. Ein Pre-Insert Event ruft bei Erzeugung eines neuen Datensatzes die entsprechende Jython-Klasse auf, um die Karte zu generierten. Die erzeugte Karte wird in einer Spalte in der Adaptive Card-Tabelle abgelegt und kann so einfach vom Python-Code wieder ausgelesen werden, um z.B. eine E-Mail mit der Karte zu versenden.
  • Mit verschiedenen Jython-Klassen kann man verschiedene Ausbaustufen des Feature implementieren und auf individuelle Anforderungen reagieren.

Module

Information

Karten erzeugen (Python)

  • Um eine Karte von Python aus zu erzeugen, muss ein neuer Datensatz in der 451 Adaptive Cards mit dem entsprechenden Vorlage-, Parameter- und Empfängerwerten angelegt werden. Nachdem der Datensatz gespeichert wurde, kann aus der Spalte Erzeugten Karte der JSON-Code der Karte ausgelesen werden und über die Mail-API verschickt werden.
  • Über die Python-ID kann eine Vorlage abgerufen und eine neue Karte erzeugt werden:
 template = adaptivecards.get_template_from_python_id(python_id)
 template.create_new_card(recipient, parameter)
PY

Karten aktualisieren

  • Um die Karten zu aktualisieren, wenn sie vom Benutzer aufgerufen werden, muss die URL einer Web-Schnittstelle hinterlegt werden, welche das neue JSON zurückgibt. Dies wird automatisch vom Framework in jede Karte eingefügt.
  • Die Web-Schnittstelle mit der Python-ID “ac_refresh” ist für das Aktualisieren verantwortlich. Die URL für diese Web-Schnittstelle wird automatisch bei der Kartenerzeugung ausgelesen und in das JSON eingefügt. Wenn die entsprechende URL dann aufgerufen wird, wird die verantwortliche Jython-Klasse erzeugt und eine update()-Methode aufgerufen, welche die neue Karte zurückgibt. Das gespeicherte JSON der letzten Erzeugung wird geladen und mit dem Rückgabewert der update()-Methode verglichen. Wenn die Objekte gleich sind, wird keine neue Karte gesendet. Hat sich die Karte verändert wird die neue Version gesendet. Die hinterlegte Karte in der Adaptive Card-Tabelle wird aktualisiert.
  • Das Standardverhalten der update()-Methode ist, das hinterlegte Modul auf der Vorlage aufzurufen und folgende Parameter an die on_web_load()-Methode zu übergeben:
{'target': AdaptiveCardTarget.LOAD_DATA,
'card_parameter': <Parameter der im System für die Karte hinterlegt ist>,
'card': <UUID der Karte>}

JSON

Hinweis

  • Der Flag Archiviert auf der Karte kontrolliert dieses Verhalten. Wenn eine Karte archiviert wurde und ein Request für Aktualisierung eingeht, wird der letzte Stand der Karte genommen, der Autorefresh entfernt und die Karte ein letztes Mal gesendet.

Karten-Interaktionen

Information

  • Um von einer Karte aus mit einem PLANTA project-System zu interagieren, muss auf den Buttons die URL der Web-Schnittstelle mit der Python-ID “ac_action” hinterlegt sein. Ist diese aktiviert, werden die URL und der API-Schlüssel dieser Web-Schnittstelle automatisch in die erzeugten Daten der Karte eingefügt, damit diese von den Buttons referenziert werden können. Wird die URL aufgerufen, erzeugt die Web-Schnittstelle die verantwortliche Jython-Klasse und die action(body)-Methode wird aufgerufen, welche die neue Karte zurückgibt.
  • Ist die Web-Schnittstelle "ac_action” aktiviert, wird folgendes Objekt automatisch in die Daten jeder Karte eingefügt:
  "action": {
    "apikey": "<API Schlüssel der Web Schnittstelle>", 
    "url": "<URL der Web Schnittstelle>"
  }

JSON


  •   Das Standardverhalten der action()-Methode ist, das hinterlegte Modul auf der Vorlage aufzurufen und folgende Parameter an die on_web_load()-Methode zu übergeben:
{'target': AdaptiveCardTarget.EXECUTE_ACTION,
  'card_parameter': <Parameter der im System für die Karte hinterlegt ist>,
  'body': <Body des Web-Request>,
  'card': <UUID der Karte>}
JSON


  • In der Adaptive Card muss ein ActionSet-Element verwendet werden und als Inhalt ein Action.Http-Element, um die Web-Schnittstelle aufzurufen. Die Konfiguration siehe Beispiel.
  • Achtung: Im Online-Designer werden diese Elemente nicht mehr unterstützt! Kopiert man eine Vorlage ein, die solche Elemente enthält und macht man eine Änderung, werden alle ActionSet-Objekte einfach entfernt.

Beispiel JSON für einen Button auf einer Karte:

{
  "type": "ActionSet",
  "id": "${uuid}f",  
  "actions": [
    {
      "type": "Action.Http",
      "id": "${uuid}",
      "title": "${$root.strings.assignment.button}",
      "url": "${$root.action.url}",
      "method": "POST",
      "body": "{\"uuid\": \"${uuid}\"}",
      "headers": [
        {
          "name": "Apikey",
          "value": "${$root.action.apikey}"
        }
      ]
    }
  ]
}

JSON


Hinweis

  • Jedes ActionSet/Action braucht eine eigene UUID.
  • Im Body muss sich der Customizer selber überlegen, welche Daten er für seine Karte braucht und wie er diese in seiner Implementierung behandelt. Die Prognose-Karte lässt sich beispielsweise die UUID des Datensatzes schicken, für den die Prognose übernommen werden soll. Die Prozessphasen-Karte lässt sich den Kommentar und die gewünschte Aktion (Freigeben/Zurück/Abbruch) schicken.

Ressourcen Avatare anzeigen

Information

  • Der Avatar einer Ressource kann von der Web-Schnittstelle mit der Python-ID “avatar” abgerufen werden.
    • In der DT467 Ressource gibt es das DI066643 Avatar-URL, welches die URL des Avatars einer Ressource zurückgibt. Das DI wird einfach im zugehörigen Modul hinterlegt, um den Wert auszulesen und dann in der Adaptive Card-Vorlage über das Templating referenziert.

JSON-Beispiel:

{
    "type": "Image",
    "url": "${data.resource[0].avatar_url}",
    "style": "Person",
    "size": "Small"
}
JSON


Karten in PLANTA project anzeigen

Information

  • Karten können in PLANTA project angezeigt werden.
    • Man muss dafür ein Button-DI (DI004336) einfügen, das DF-Verhalten = “ac” auswählen und folgende Datenfeld-Konfiguration hinterlegen:
{
    "CardTemplate": "<Python ID wo das Template hinterlegt ist>",
    "CardData": "<Python ID wo die Daten hinterlegt sind>"

}
CODE
  • Für bereits formatierte Karten können beide Parameter einfach auf das Feld gesetzt werden, was die erzeugte Karte enthält.

Adaptive Card Designer

Information

  • Der Adaptive Card Designer kann in PLANTA project angezeigt werden. Er ist bereits für Outlook und Adaptive Card-Version 1.3 vorkonfiguriert. Über die Konfiguration kann eine bestimmte Karte im System im Designer geladen werden.
    • Man muss dafür ein Button-DI (DI004336) einfügen, das DF-Verhalten = “ad” auswählen und folgende Datenfeld-Konfiguration hinterlegen:
{
    "TemplateDf": "<Python ID wo das Template hinterlegt ist>",
    "SampleDataDf": "<Python ID wo die Beispieldaten hinterlegt sind>",
    "StretchToFill": true/false
}
CODE

Details

  • Parameter:
    • TemplateDf: Python-ID des Felds, das das Template enthält, das im Editor geladen werden soll. Änderungen am Template werden auch zurück in dieses Datenfeld geschrieben.
    • SampleDataDf: Python-ID des Datenfelds, das die Beispieldaten enthält, die im Editor geladen werden sollen. Änderungen werden auch zurück in dieses Datenfeld geschrieben.
    • StretchToFill: Wenn aktiviert, füllt das Designer Control das ganze Modul aus und anderer Inhalt wird ignoriert.
    • Sind die Template- und Beispieldatenfelder Eingabe-Datenfelder und das Modul erlaubt Speichern, so werden die Änderungen aus dem Designer auch in die Felder zurückgeschrieben.
  • Die Datenfelder müssen sich im selben Datenbereich wie der Designer befinden.
  • Über das Kontextmenü können weitere Features des Designers genutzt werden.


Jython-Klasse

Information

  • Die Jython-Klasse wird beim Erzeugen eines 451er Datensatzes instanziiert. Sie bekommt den Datensatz und damit auch die Referenz auf den Datensatz in der DT450 mit der Kartenvorlage.
  • Im Standard verwenden wir aktuell für alle Adaptive Cards die gleiche Jython-Klasse: customizing.adaptivecards.BaseAdaptiveCardHandler.
  • Der Kundencustomizer kann eine Subklasse vom BaseAdaptiveCardHandler erstellen, um individuelle Businesslogik auf dieser Schicht zu implementieren, falls nötig.
  • Wichtige Methoden:
    • new() wird vom Pre-Insert Event auf der Adaptive Card-Tabelle aufgerufen.
    • update() wird von der Refresh-Web-Schnittstelle aufgerufen.
    • action() wird von der Aktion-Web-Schnittstelle aufgerufen.
  • In allen Fällen wird das hinterlegte PLANTA project-Modul in einer clientless-Session geöffnet und erwartet, dass ein Dictionary an Daten zurückgegeben wird, welches verwendet wird, um die Kartenvorlage zu befüllen und eine neue Karte zu erzeugen.


Datenaustausch über clientless-Sessions

Information

  • Auf der Adaptive Card-Vorlage wird eine Modul-ID hinterlegt. Dieses Modul wird dann von der Jython-Klasse in einer clientless-Session geöffnet, um die Daten für die Adaptive Card auszulesen, bzw. um eine Aktion zu triggern.
  • Über die Python-IDs der Datenbereiche und Datenfelder kann eine JSON-kompatible Struktur ausgelesen werden. 
  • Im Modul können dann sämtliche Möglichkeiten genutzt werden, die das Customizing so bietet, um die Daten, die man für die Adaptive Card benötigt, zu filtern und aufzubereiten, wie beispielsweise Gruppierungen, Erfüllungen sowie virtuelle Felder.
  • Die Texte, die für die Adaptive Card benötigt werden, können auf den Datenfeldern als Überschriften hinterlegt werden und sind somit übersetzbar (PLANTA-Standard). Wenn die Karten nur einsprachig benötigt werden (kundenindividuell), kann mit festen Strings in der Kartenvorlage gearbeitet werden.
  • Die Module müssen natürlich einem übersetzungsrelevanten Arbeitsgebiet zugeordnet werden, damit sie auch übersetzt werden.

Aufruf der Modulsubklasse

Wenn das hinterlegte Modul einer Adaptive Card aufgerufen wird, so wird folgendes Dictionary übergeben:

{
    'target': <AdaptiveCardTarget>,
    'card_parameter': <Inhalt von DI 066535 “Parameter”>,
    'card': <UUID der Karte als String>,
    ‘body’: <Enthält den Body des Request>
}
JSON


  • Das "target" ist entweder “load_data” oder “execute_action”, je nachdem ob die Karte erzeugt/aktualisiert wird, oder ob eine Aktion ausgelöst wird.
  • Der “card_parameter” enthält den konfigurierten Parameter für die Karte.
  • Die “card” beinhaltet die UUID der Karte als String.
  • Das “body” Attribut wird nur bei “execute_action” übergeben und enthält den übergebenen Body des Web-Request. Was genau im Body steht, wird in der Adaptive Card-Vorlage definiert.

AdaptiveCardExtractorModule

  • Die Modulsubklasse ppms.office.adaptivecards.AdaptiveCardExtractorModule implementiert das Default-Auslesen der Daten.
  • In der on_web_load() wird die get_json_data()-Methode aufgerufen, welche die Daten aus dem Modul ausliest. In einer individuellen Subklasse kann hier entweder vor oder nach dem Laden der Daten Businesslogik eingefügt werden.
  • Beim Durchlaufen des Modulcustomizing wird ein Dictionary mit 2 Schlüsseln gebaut:
    • strings: Enthält die DF-Überschriften aller Felder, die nicht in Fenster 9 sind
    • data: Enthält die Daten aller Datenbereiche, die eine Python-ID haben und Nie Anzeig. = N.
      • Leere Datenbereiche werden ignoriert.
  • Ein Kind-Datenbereich wird in seinen Eltern-Datenbereich eingefügt. Der Schlüssel ist hierbei die Python-ID des Datenbereich. Alle Daten werden in Form von Listen übergeben.
  • In der Datenfeldkonfiguration können einzelne Felder noch wie folgt konfiguriert werden:
    • use-df-heading: Es wird die DF-Überschrift statt der Python-ID als Schlüssel verwendet
    • get-tech-value: Es wird der technische Wert statt des Text-Wertes ausgelesen

Beispiel (mit gekürztem JSON):

{
  "data": {
    "project": [
      {
        "pr_type_title": "Projekt",
        "pr_id": "000004",
        "project_name": "Adaptive Cards Testprojekt",
        "pr_functional": "KARTENTRICKS",
        "task": [
          {
            "task_name": "VG 1",
            "assignment": [
              {
                "task_technical": "1",
                "effort_rem": "10 h",
                "calc_end": "28.06.23",
                "date_forecasting": "",
                "uuid": "0f23f5be-fd95-004f-8178-132474e8386e",
                "prognose_tech": 4,
                "button": "Prognose übernehmen",
                "avatar_url": "https://3f10-2-211-106-158.ngrok-free.app/api/bed1666d-1d0b-6b4c-80c9-a1391b8519c7/resource/d277145f-028b-6546-be3b-b2c5b2671fb0/avatar",
                "effort_rem_tech": 10,
                "inc_res_466": "Marcel Carl",
                "task_completed": "N",
                "prognose": "4 h"
              }
            ],
            "pr_id": "000004",
            "task_id": "1",
            "vg_special": "1"
          }
        ]
      }
    ]
  }
}
JSON


Parameter einer Karte ändern

Information

  • Unter ppms.adaptivecards gibt es die Funktion set_card_parameter(card_id, parameter), um den Parameter einer Karte zu verändern. Im Parameter können zusätzliche Informationen gespeichert werden, um die UX der Karte zu verbessern.

Karte archivieren

Information

  • Unter ppms.adaptivecards gibt es die Funktion set_card_archive_status(card_id, archived), um eine Karte zu archivieren oder ggf. zu dearchivieren.
  • Wird beispielsweise eine Aktion in der Subklasse behandelt, welche die Karte “erledigt”, dann muss in der Subklasse diese Funktion aufgerufen werden, um die Karte zu archivieren.

Beispiel:

   def on_web_load(self, parameters):
        card_id = parameters.get('card', '')
        # … busines logic goes here
        adaptivecards.set_card_archive_status(card_id=card_id, archived=True)
PY

Templating

Information

  • Nachdem die Vorlage und die Daten ausgelesen wurden, werden diese an den Templating-Service des Webclients geschickt, der mit der generierten Karte antwortet. 
  • Mit dem Templating können die Karten flexibel gebaut werden. Statt alle Werte hart zu hinterlegen, kann in der Vorlage auf ein Datenobjekt referenziert werden und Möglichkeiten wie if/else-Klauseln zum kontextabhängigen Darstellen von Daten/Texten genutzt werden.

Templating-Beispiel:

  • Die Prognosekarte kann eine flexible Anzahl an Prognose darstellen.
  • In den Daten gibt es im “project”-Objekt eine Liste an “task”-Objekten, die jeweils eine Prognose abbilden.
  • Die Prognosewerte finden sich im “assignment”-Objekt, was eine Liste mit Länge 1 unter jedem “task”-Objekt ist.
  • In der Vorlage gibt es einen Container für die Prognosen, der mit folgendem Attribut für jedes “task”-Objekt wiederholt wird: “$data”: “${data.project[0].task}”
  • Das Templating-SDK wiederholt dann die Elemente für jedes einzelne “task”-Objekt und befüllt es mit den Werten aus der jeweiligen Iteration.
  • Mit ${$root.<pfad>} kann aus dem $data Kontext ausgebrochen werden.

(Gekürzter Auszug aus dem Vorlage-JSON)

Kopfzeile

Information

  • Auf der Vorlage einer Adaptive Card kann über DI066646 Kopfzeile konfiguriert werden, ob bei der Generierung der Karte automatisch eine Kopfzeile eingefügt werden soll.
  • Die Kopfzeile ist in der globalen Einstellung “adaptive_card_header” hinterlegt. Hier muss ein gültiges JSON-Objekt hinterlegt werden, was als erstes Element in den Body der Karte eingefügt wird.

Fußzeile

Information

  • Auf der Vorlage einer Adaptive Card kann über DI066647 Fußzeile konfiguriert werden, ob bei der Generierung der Karte automatisch eine Fußzeile eingefügt werden soll.
  • Die Fußzeile wird im Jython-Code erzeugt. Die Standardklasse erzeugt dabei folgende Fußzeile:
  • Es wird die aktuelle Uhrzeit geholt.
  1. Ist die Karte archiviert wird Textkonstante 002465 verwendet, bei offenen Karten 002464.
    1. Gibt es die Textkonstante nicht in der Sprache des Empfängers, wird Deutsch verwendet.
  2. Die Formatparameter in der Textkonstante werden mit time.strftime() aufgelöst.
    1. Beispiel: %d.%m.%Y - %H:%M = 29.06.2023 - 15:33
  3. Ein in der Klasse hinterlegtes TextBlock-Element wird mit dem Text befüllt und als letztes Element in den Body der Karte eingefügt

Achtung: Dadurch, dass sich die Fußzeile jede Minute ändert, wird auch jedes Mal eine neue Karte gesendet! Die Aktualisierung auf der Outlook-Oberfläche ist sichtbar, für eine geschmeidige UX sollte daher die Fußzeile deaktiviert werden!

Adaptive Cards im PLANTA-Standard

Prognose versenden

Information

  • Im Standard ist die Funktion in der Zeiterfassung hinter dem Button mit dem Brief-Icon zu finden. Im Modul kann für ein Projekt eine Prognose versendet werden, was alle zugehörigen Datensätze mit einem eingetragenen Prognose-Wert in ein Template in das Modul Info versenden einfügt.
  • Wenn Adaptive Cards aktiviert sind, wird statt Info versenden zu öffnen eine E-Mail mit einer Adaptive Card an den Projektleiter gesendet. In der Karte können die Datensätze übernommen werden.
  • Die Karte zeigt die Felder Aufwand-Rest, Prognose, Kalk. Ende und vsl. Erledigt am an.
  • Beim Erzeugen der Karte werden die UUIDs der zu dem Zeitpunkt relevanten 466er Datensätze auf der Karte als Parameter hinterlegt. Das zugehörige Modul filtert auf den UUIDs, um die Daten für die Karte zu ermitteln. Sind keine offenen Prognosen übrig, wird die Karte archiviert. Wenn der Benutzer in der E-Mail auf den Knopf Übernehmen drückt, wird der zugehörige Button im Modul ausgelöst und die Prognose übernommen.

Die Karte speichert im Parameter folgendes:

  • Die UUID der Ressourcenzuordnungen, die zum Zeitpunkt der Erstellung einen Prognosewert hatten
  • Den Zustand der Prognose (Neu / Übernommen / Zurückgezogen)
  • Den alten/neuen Wert (wird nur aktualisiert, wenn der Anwender auf die Schaltfläche Übernehmen drückt)

Das dazugehörige Modul, welches in einer Clientless-Session aufgerufen wird, um die Werte abzurufen und den Button auszulösen, sieht wie folgt aus:

API

  • adaptivecards.cards.forecast.create_forecast_card(recipient, assignment_uuids):  Legt eine neue Karte “Prognose übernehmen” an. Gibt die erzeugte Karte als JSON-String zurück oder None, wenn die Karte nicht angelegt werden konnte.

Achtung

  • Im Modul Prognosen versenden ist DI065078 als Rest gecustomized. Dieses Feld ist ein Wertebereich, der nicht neu berechnet wird, wenn über die Adaptive Card der Aufwand-Rest geändert wird.

Abgegebene Prozessphase bearbeiten

Information

  • Wird bei Verwendung eines Prozessmodells in PLANTA project eine Prozessphase abgegeben, so erhält der Benutzer, der für die Freigabe verantwortlich ist, im Outlook eine Adaptive Card.
  • Der Verantwortliche kann auf der Karte einen Kommentar hinterlegen und die Prozessphase entweder freigeben, an den Ersteller zurückgeben oder das Projekt abbrechen. Wird einer der Buttons betätigt, öffnet sich auf dem PLANTA-Server eine Clientless-Session und führt genau diese Aktion für den Benutzer aus. 
    • Auf der Karte können Prozessschritte der Phase durch Aufklappen eingesehen werden.

API

  • adaptivecards.cards.processmodel.create_release_process_phase_card(recipient, project_id, phase_id): Legt eine neue “Abgegebene Prozessphase bearbeiten” Karte an. Gibt die erzeugte Karte als JSON-String zurück oder None, wenn die Karte nicht angelegt werden konnte.

Beispiel-Workflow: Ressource mit Avatar in Adaptive Card anzeigen

Ziel

  • Es soll eine Adaptive Card entstehen, die den Namen einer Ressource und deren Avatar darstellt.

Vorgehensweise

  1. Modul customizen
    1. Modul-Subklasse: ppms.office.adaptivecards.AdaptiveCardExtractorModule
    2. Datenbereich einfügen mit DI001275 Ressource und DI066643 Avatar-URL und DDI001218 Ressourcen-ID.
    3. Python-ID “resource” für den Datenbereich vergeben.

  1. Adaptive Card-Vorlage anlegen und die ID des zuvor erstellten Moduls hinterlegen.

  1. Vorlage bearbeiten. Entweder die Karte selber basteln, oder das folgende Beispiel einkopieren.
    1. Wichtig ist, dass als URL für den Avatar “${data.resource[0].avatar_url}” und für den Namen “${data.resource[0].title}” hinterlegt wird.
{
  "type": "AdaptiveCard",
  "hideOriginalBody": true,
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.1",
  "verticalContentAlignment": "Center",
  "body": [
    {
      "type": "ColumnSet",
      "columns": [
        {
          "type": "Column",
          "items": [
            {
              "type": "Image",
              "style": "Person",
              "url": "${data.resource[0].avatar_url}",
              "size": "Small"
            }
          ],
          "width": "auto"
        },
        {
          "type": "Column",
          "items": [
            {
              "type": "TextBlock",
              "text": "${data.resource[0].title}",
              "weight": "Bolder",
              "wrap": true,
              "size": "Default"
            }
          ],
          "width": "auto"
        }
      ]
    }
  ]
}
JSON


  1. Karte erzeugen drücken und die neue Karte speichern.
  2. Die Daten können zurück in die Vorlage als Beispieldaten einkopiert werden, damit die Vorlage in Zukunft auch eine gültige Vorschau bekommt.