Der Blog von fm-ProductNode

Einen BMEcat-Produktkatalog an der Konsole analysieren

Lupe
Verfasst von franz-mue am 11. August 2017

Im neuen Release 2.0 bietet fm-ProductNode viele neue Möglichkeiten. In diesem Beitrag zeige ich, wie ein BMEcat-Katalog auf der Kommandozeile in allen Einzelheiten untersucht werden kann. Im Mittelpunkt steht dabei eine in fm-ProductNode eingebettete und wesentlich ausgebaute Twig Template Engine. Das Vorgehen ist zwar nicht so komfortabel wie an einer grafischen Bedienoberfläche,aber dem, der sich auf diese Ebene begibt, stehen alle Möglichkeiten der Automatisierung unmittelbar offen.

Update

Der nachfolgende Beitrag bezieht sich auf das inzwischen nicht mehr aktuelle Release 2.0 von fm-ProductNode! Manche Aufrufe und Anweisungen haben sich in neueren Releases geändert.

Übersicht

Zuerst wird der BMEcat-2005-Katalog eingelesen.

Einen Einblick in die Eigenschaften des Katalogs erhält man durch die Verwendung der eingebetteten Template Engine. Die Verarbeitung per Template Engine wird direkt aus der Konsole angestoßen. Das zugehörige Template wird am einfachsten beim Konsolenaufruf gleich mit angegeben.

Einige Template-Kommandos erlauben einen schnellen ersten Überblick über die Inhalte des Produktkatalogs.

Ein automatisch mit angelegter Such-Index ermöglichst das schnelle Auffinden von Produkten mit bestimmten Attribut-Werten.

Es können ebenso übergreifende Kataloginformationen abgerufen werden wie auch die Attribute von bestimmten Produkten oder auch seltenerer Katalogbestandteile.

Auch benutzerdefinierte Erweiterungen in den verschiedenen Bereichen des Katalogs sind abfragbar.

Ausgangssituation

Als Grundlage für den vorliegenden Beitrag dient ein BMEcat-Produktkatalog aus der Praxis. Den Katalog habe ich aus dem Internet heruntergeladen, und die Inhalte etwas verfremdet. Es handelt sich um einen kleineren Katalog mit 60 Produkten, der etwa ein halbes Megabyte Speicherplatz einnimmt.

Für die Untersuchung öffne ich auf einem Linux-Rechner ein Terminal-Fenster. Die Konsolenkommandos werden von einer Shell (bash) ausgeführt. Als aktuelles Verzeichnis ist das Wurzelverzeichnis von fm-ProductNode eingestellt.

Einen BMEcat-Katalog einlesen

Vorweg wird der Katalog in die Datenhaltung von fm-ProductNode eingelesen.

$ php trans.php catalog.read --alias source1 workplace/catalog/source1.xml

Das PHP-Skript trans.php ruft den Befehl catalog.read von fm-ProductNode auf. Als Argument ist am Ende der Pfad zum BMEcat-Katalog angegeben. Die Katalog-Datei heißt source1.xml; sie liegt in dem dafür angelegten Verzeichnis workplace/catalog/. Um in der Folge nicht mit der automatisch vergebenen ID hantieren zu müssen, vergebe ich mit Hilfe der --alias-Option einen besser zu merkenden Namen, nämlich hier source1.

Einen fehlerhaften Katalog beim Einlesen korrigieren

Leider halten sich nach meiner Erfahrung die meisten BMEcat-2005-Kataloge vor allem an einer Stelle nicht an die Spezifikation: Es wird zu Beginn keine Vorgabesprache eingestellt. Auch bei einsprachigen Produktkatalogen - wie es hier der Fall ist - schreibt das die Spezifikation vor, sofern nicht zu jedem sprachabhängigen Element ergänzend die verwendete Sprache eingestellt ist. Um das Problem gleich beim Einlesen aus der Welt zu schaffen, stellt ein eingabeseitiges Template diese Anforderung während des Einlesens sicher:

$ php trans.php catalog.read --alias source1 --template_string "item_paths:
  BMECAT/HEADER/CATALOG/LANGUAGE#default:
    default_value: 'true'" workplace/catalog/source1.xml

Das Kommando ist ähnlich dem vorausgehenden Kommando zum Einlesen eines BMEcat-Katalogs aufgebaut, erweitert es jedoch um die Option --template_string.

Ähnlich wie auf der Ausgangsseite, bietet fm-ProductNode auch auf der Eingangsseite zwei Alternativen zur Angabe eines Templates an. Einerseits kann ein Template in Form einer Datei angegeben werden, andererseits kann der Inhalt des Templates gleich als Zeichenkette an das Kommando angegeben werden. Das ist bequemer und weniger aufwendig, als erst eine Template-Datei anzulegen, und das Template in einem nächsten Schritt aufzurufen. Gerade, wenn das Template später nicht mehr gebraucht wird, bietet sich diese Alternative an.

Der Inhalt des Templates ist im Yaml-Format abgefasst - so wie es fm-ProductNode bei allen eingangsseitigen Templates hält. Die Direktive im Template bewirkt, dass beim Einlesen, die LANGUAGE-BMEcat-Elemente im Kopfbereich des Katalogs geprüft werden. Falls bei dem Element kein default-Attribut gesetzt ist, wird das beim Einlesen nachgeholt. Der Fehler wird also mit Hilfe der Direktive automatisch, also ohne weiteren Eingriff in die BMEcat-Datei, beseitigt.

Bei der Angabe des Wertes für die Direktive default_value ist in diesem Fall Vorsicht geboten: Der Wert true soll nicht als boolscher Wert, sondern als Zeichenkette in den Katalog eingefügt werden. Deshalb muss der Wert in Anführungszeichen angegeben werden: 'true'.

Der Vollständigkeit halber erwähne ich, dass das Template so nur in einsprachigen BMEcat-Katalogen verwendbar ist: In mehrsprachigen Katalogen würde das Attribut bei jedem LANGUAGE-Element gesetzt. Das Attribut darf jedoch nur bei einem einzigen LANGUAGE-Element gesetzt sein.

Nun ist es soweit: Der Katalog wurde in fm-ProductNode eingelesen. Ab diesem Zeitpunkt sind die Inhalte des Katalogs abfragbar.

Einbeziehung von Templates

Seit Release 2.0.0 stellt fm-ProductNode einen weit mächtigeren Mechanismus zur Inhaltsabfrage zur Verfügung als bisher: fm-ProductNode verwendet zum Zugriff auf Katalogbestandteile eine modifizierte Twig Template Engine. Diese Template Engine kann auch über die Konsole aufgerufen werden. Entweder wird der Name oder der Dateiname eines Templates angezogen, das zur Abarbeitung des Konsolenkommandos herangezogen werden soll, oder - vor allem in einfachen Fällen - es wird gleich der Inhalt des Templates beim Aufruf des Konsolenkommandos mit angegeben.

Template Engines werden häufig als flexible Schablonen zur Erstellung von Web-Seiten eingesetzt. Vor diesem Hintergrund dürfte auch die Twig Template Engine entwickelt worden sein. Bei fm-ProductNode wird die Template Engine dagegen umfunktioniert und für Datentransformationen und Workflows verwendet. Voraussetzung für Datentransformationen ist der Zugriff auf die Daten. Und gerade das ermöglicht die Template Engine auch. Daher liegt die Verwendung einer Template Engine für Katalog-Inhaltsabfragen auch nicht so fern.

Die Katalog-Inhaltsabfrage per Template-Abarbeitung sieht im Prinzip von der Kommandozeile wie folgt aus:

$ php trans.php twig.render --alias source1 --template_string "
{# ... hier steht der Inhalt des Templates ... #}
"

Die folgenden Beispiele veranschaulichen, wie dieser Mechanismus eingesetzt werden kann.

Ein erster Überblick über den Katalog

Wie lautet der Name des Katalogs:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.metadata.name }}
"

Die Konsolen-Ausgabe zum Katalognamen könnte zum Beispiel lauten:

Huber Elektronen Preisliste 2017 Gebrauchtheiten 08/2017

Welche Transaktion enthält der Katalog:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.metadata.transaction }}
"

In den allermeisten Fällen wird die Ausgabe lauten:

T_NEW_CATALOG

Welche Sprachen verwendet der Katalog:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.metadata.languages }}
"

Hier haben wir es nur mit einem einsprachigem Katalog zu tun. Daher lautet das Ergebnis:

deu

deu steht dabei für die deutsche Sprache nach dem ISO-Standard 639.2. Der ISO-Standard ist international akzeptiert, und erleichtert die maschinelle Verarbeitbarkeit.

Mit einem einfachen Template wird die Anzahl der Produkte des Katalogs abgefragt:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.productCount }}
"

Ergebnis ist beim aktuellen Katalog die Zahl 60.

Als nächstes ein umfangreicheres Beispiel, das in einem Rutsch prüft, ob im Katalog nicht nur Produkte vorhanden sind:

$ php trans.php twig.render --alias source1 --template_string "
{% set viewer = repository.currentCatalog.viewer %}
{% if viewer.hasClassifications %}
  {{ viewer.classificationCount }} Klassifikationssysteme vorhanden
{% else %}
  keine Klassifikationssysteme vorhanden
{% endif %}
{% if viewer.hasCatalogGroup %}
  Kataloggruppensystem vorhanden
{% else %}
  kein Kataloggruppensystem vorhanden
{% endif %}
{% if viewer.hasProductToCatalogGroupMaps %}
  {{ viewer.productToCatalogGroupMapCount }} Abbildungen von Produkten zu Kataloggruppen vorhanden
{% else %}
  keine Abbildungen von Produkten zu Kataloggruppen vorhanden
{% endif %}
{% if viewer.hasFormulas %}
  {{ viewer.formulaCount }} Formeln vorhanden
{% else %}
  keine Formeln vorhanden
{% endif %}
{% if viewer.hasIppDefinitions %}
  {{ viewer.ippDefinitionCount }} IPP-Definitionen vorhanden
{% else %}
  keine IPP-Definitionen vorhanden
{% endif %}
"

Das Template ist schon eher zu lange für eine Eingabe auf der Kommandozeile. In der Praxis dürfte in diesem Fall die Auslagerung in eine Template-Datei der bessere Weg sein. Natürlich können die Abfragen auch der Reihe nach in separaten Aufrufen erfolgen. Mir geht es im Beispiel jedoch nur darum, zu zeigen, was möglich ist.

Das Ergebnis des Konsolenaufrufs:

  keine Klassifikationssysteme vorhanden
  kein Kataloggruppensystem vorhanden
  keine Abbildungen von Produkten zu Kataloggruppen vorhanden
  keine Formeln vorhanden
  keine IPP-Definitionen vorhanden

Die Ausgabe zeigt, dass sich der BMEcat-Katalog auf Produkte beschränkt. Andere denkbare Bestandteile, wie etwa Klassifikationssysteme oder Preisformeln werden im Katalog nicht mit übertragen.

Suchen

Sofern für fm-ProductNode die entsprechende Einstellung nicht abgeschaltet wird, werden beim Einlesen eines Katalogs automatisch Such-Indexes für einige Produkteigenschaften angelegt. Die Such-Indexes beschleunigen die spätere Suche nach diesen Kriterien. So ist zum Beispiel das Suchen von internationalen Artikelnummern im Katalog möglich. Unter internationale Artikelnummern versteht BMEcat-2005 zum Beispiel GTIN- bzw. EAN-Nummern.

Das folgende Kommando liefert eine Liste im Katalog vorhandener internationaler Artikelnummern zurück; pro Zeile wird eine Artikelnummer ausgegeben:

$ php trans.php twig.render --alias source1 --template_string "
{% set values = repository.currentCatalog.viewer('deu').search.internationalPidValues %}
{% for value in values %}
{{ value }}
{% endfor %}
"

Das Ergebnis könnte etwa wie folgt aussehen:

3606480504839
3606480504846
...
3606480769238
3606480769245
3606480769252

Wie Sie nun wissen möchten, um welche Produkte es sich bei den letzten beiden angegebenen EAN-Nummern handelt, würde die nächste Abfrage weiterhelfen:

$ php trans.php twig.render --alias source1 --template_string "
{% set values = repository.currentCatalog.viewer('deu').search.internationalPidIndexes('36064807692') %}
{% for value in values %}
{{ value }}
{% endfor %}
"

Im Template wurde als Argument für die Methode internationalPidIndexes des search-Objektes eine Zeichenkette übergeben, die die gemeinsame Teilzeichenkette der beiden letzten EAN-Nummern darstellt. Das Kommando sucht alle Produkte aus dem Katalog heraus, die in der EAN-Nummer die übergebene Zeichenkette enthalten. Die Produkte werden dabei durch ihren Index angegeben; mit anderen Worten, die Produkte werden durch ihre Reihenfolge im Katalog bestimmt. Das erste Produkt besitzt den Index 0, das zweite den Index 1, und so weiter. Das Ergebnis lautet zum Beispiel:

3
2
5
4

Anhand dieser Indexes können die Produkte bestimmt und deren Eigenschaften genauer unter die Lupe genommen werden. Weiter unten folgt dazu noch ein Beispiel mit dem Aufruf der productByIndex-Methode des viewer-Objekts.

Beachtenswert bei den beiden vorausgehenden Aufrufen ist, dass das viewer-Objekt mit dem Argument 'deu' instantiiert wurde. Das Argument steht für die Sprache deutsch. Hier ist die Beschränkung des viewer-Objektes auf eine Sprache nicht notwendig: Ein GTIN- oder EAN-Identifikator ist nicht sprachabhängig. Bei der Suche nach einem Schlüsselwort oder Produktsegment liegt die Situation anders; diese Produkt-Attribute sind sprachabhängig. Daher wäre eine Beschränkung des viewer-Objekts auf eine geeignete Sprache - hier deutsch - erforderlich gewesen.

Einen Eindruck vorhandener Meta- oder Produktinformationen erhalten

BMEcat bietet viele Spielräume, um Informationen in den übergreifenden Metadaten oder zu den einzelnen Produkten darzustellen. fm-ProductNode erlaubt es auf unterschiedliche Weise, diese Informationen zu betrachten. Eine brachiale Alternative ist es, die Rohdaten ähnlich wie sie in der internen Datenhaltung aufbewahrt werden, auszugeben. Im Falle der Metadaten lautet der Aufruf:

$ php trans.php twig.render --alias source1 --template_string "
{% set content = repository.currentCatalog.viewer.metadata.content %}
{% for key, value in content %}
  {{ key }}:{{ value }}
{% endfor %}
"

Je nach der Menge der enthaltenen Daten kann die Ausgabe auf der Konsole unterschiedlich umfangreich ausfallen, zum Beispiel:

  buyer.id:BUYER 315533703
  buyer.name.deprecated:Huber Reutlingen
  buyer.type:duns
  default.language:deu
  default.price.currency:EUR
  default.price.factor:1
  default.territories:DE
  { ... und so weiter ... }
  identification.version:15.08
  languages:deu
  party.0.address.city:Reutlingen
  party.0.address.country:D
  party.0.address.emails.0.email:info@de.huber-electronen.net
  party.0.address.name:Huber - Elektronen GmbH
  party.0.address.name2:Filiale
  { ... und so weiter ... }
  party.0.roles:buyer
  { ... und so weiter ... }
  supplier.id:SUPPLIER 4012106000002
  supplier.name.deprecated:Huber Elektronen
  supplier.type:iln
  transaction.name:T_NEW_CATALOG

Allerdings lässt sich die Ausgabemenge einschränken, wenn beim Aufruf des Kommandos der Methode content ein einschränkender Filter mitgegeben wird. Im Falle des ersten Produkts lautet der analoge Aufruf, diesmal allerdings mit einer eingeschränkten Suche zum Beispiel:

$ php trans.php twig.render --alias source1 --template_string "
{% set content = repository.currentCatalog.viewer.productByIndex.content('manufacturer', true) %}
{% for key, value in content %}
  {{ key }}:{{ value }}
{% endfor %}
"

Ausgegeben werden nur die Produktbestandteile, die in der Attributbezeichnung die Zeichenkette "manufacturer" enthalten. Das Ergebnis könnte zum Beispiel lauten:

  identification.manufacturer.name.deprecated:Huber Elektronen GmbH
  identification.manufacturer.product_id:LSS100300
  identification.manufacturer.type_description:LSS100300

Die gezeigte Methode dürfte nicht für jeden Geschmack geeignet sein. Daher bietet fm-ProductNode, wie in der Folge gezeigt,  viele weitere Zugriffsmöglichkeiten an.

Übergreifende Katalogdaten gezielt abfragen

fm-ProductNode ist in der Lage, alle Informationen im Kopfbereich des Kataloges gezielt abzufragen.

Zum Beispiel lautet der Aufruf zur Abfrage der voreingestellten Währung:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.metadata.defaults.currency }}
"

Im Ergebnis könnte EUR an der Konsole ausgegeben werden - das Kürzel für den Euro.

Den Namen des zweiten im Katalog aufgeführten Geschäftspartners könnten Sie mit folgendem Kommando in Erfahrung bringen:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.metadata.partyByIndex(1).address.name }}
"

Eigenschaften zu einzelnen Produkten gezielt abfragen

Um Produktdaten abzufragen, muss als erstes das Produkt ausgewählt werden. Eine einfache Option ist die Angabe eines Index (wobei das erste Produkt die Nummer 0 besitzt). Um zum Beispiel die Hersteller-Artikelnummer des 10. Produkts zu erhalten, könnte der Aufruf lauten:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.productByIndex(9).properties.manufacturerPid }}
"

Benutzerdefinierte Erweiterungen einsehen

Eine Besonderheit von BMEcat-2005 sind benutzerdefinierte Erweiterungen, die an verschiedenen Stellen des Produktkataloges eingebaut werden dürfen. Beispielsweise sind solche Erweiterungen in den Metadaten des Katalogs wie folgt abzurufen:

$ php trans.php twig.render --alias source1 --template_string "
{{ repository.currentCatalog.viewer.metadata.userDefinedContent|raw }}
"

Falls derartige Erweiterungen im Katalog vorliegen, werden sie als XML-Zeichenkette ausgegeben, zum Beispiel:

<UDX.EDXF.VERSION>2.2</UDX.EDXF.VERSION>

Zu dem Kommandoaufruf sei auf eine Besonderheit hingewiesen: Die ermittelten benutzerdefinierten Daten werden vor der Ausgabe durch den raw-Filter von Twig geschickt. Das ist erforderlich, da Twig von Hause aus davon ausgeht, dass HTML-Code aus den Angaben im Template generiert wird; ohne den raw-Filter würden die spitzen Klammern in der ausgegebenen Zeichenkette umgeändert, so dass die Zeichenkette kein Chaos in umgebendem HTML-Code erzeugen kann.

Hinweise

Mit dem Beitrag wollte ich einen ersten Einblick in die Möglichkeiten der Analyse von BMEcat-Katalogen auf der Kommandozeile mittels fm-ProductNode geben. Tatsächlich fokussiert der Beitrag nur auf einen kleinen Ausschnitt der Schnittstellen von fm-ProductNode; insgesamt stehende dutzende von Objekten und hunderte von Methoden zum feingranularen Zugriff auf BMEcat-Inhalte zur Verfügung. Für weitere Einzelheiten sei auf die Schnittstellen-Dokumentation verwiesen.

Abschließend sei noch vermerkt, dass analoge Zugriffsmethoden auf BMEcat-Inhalte auch im Rahmen des PHP-APIs von fm-ProductNode vorhanden sind. Ebenso bietet auch das PHP-API Aufrufe zur Ausführung der Template Engine an.

Ausblick

In den nächsten Blog-Beiträgen geht es um die Weiterverarbeitung von BMEcat-Kataloginhalten. fm-ProductNode bietet umfangreiche Funktionalität an, Konvertierungen in verschiedenste Richtungen anzubieten.

Tags: BMEcat, CLI, Release2.0, Template, Twig
Foto: joerg-design / pixabay.com

« Das Release 2.0 von fm-ProductNode - Einen Produktdaten-Feed aus einem BMEcat-Katalog erzeugen »